├── .editorconfig
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── dotnetframework.yml
├── .gitignore
├── .stylecop.json
├── Business
├── App.xaml
├── App.xaml.cs
├── IpcPipe.cs
├── KeyboardInput.cs
├── Menus.cs
├── Program.cs
└── WaitToLoadMenu.cs
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Config
├── AppColors.cs
├── Config.cs
└── MenuDefines.cs
├── DataClasses
├── MenuData.cs
└── RowData.cs
├── GlobalSuppressions.cs
├── Helpers
├── DirectoryHelpers.cs
├── DragDropHelper.cs
├── GlobalHotkeys.cs
├── ImagingHelper.cs
├── JoystickHelper.cs
├── Updater
│ ├── GitHubUpdate.cs
│ └── JsonParser.cs
├── WindowsExplorerSort.cs
└── WindowsTaskbar.cs
├── LICENSE
├── NativeDllImport
├── HotKey.cs
├── Icon.cs
├── Menu.cs
├── Screen.cs
├── ShellInterfacesCOM.cs
├── ShellWindowsApi.cs
├── TaskBar.cs
├── Themes.cs
└── Window.cs
├── Packaging
├── Images
│ ├── BadgeLogo.scale-100.png
│ ├── BadgeLogo.scale-125.png
│ ├── BadgeLogo.scale-150.png
│ ├── BadgeLogo.scale-200.png
│ ├── BadgeLogo.scale-400.png
│ ├── LargeTile.scale-100.png
│ ├── LargeTile.scale-125.png
│ ├── LargeTile.scale-150.png
│ ├── LargeTile.scale-200.png
│ ├── LargeTile.scale-400.png
│ ├── SmallTile.scale-100.png
│ ├── SmallTile.scale-125.png
│ ├── SmallTile.scale-150.png
│ ├── SmallTile.scale-200.png
│ ├── SmallTile.scale-400.png
│ ├── SplashScreen.scale-100.png
│ ├── SplashScreen.scale-125.png
│ ├── SplashScreen.scale-150.png
│ ├── SplashScreen.scale-200.png
│ ├── SplashScreen.scale-400.png
│ ├── SplashScreen.scale-400.xcf
│ ├── Square150x150Logo.scale-100.png
│ ├── Square150x150Logo.scale-125.png
│ ├── Square150x150Logo.scale-150.png
│ ├── Square150x150Logo.scale-200.png
│ ├── Square150x150Logo.scale-400.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png
│ ├── Square44x44Logo.altform-unplated_targetsize-16.png
│ ├── Square44x44Logo.altform-unplated_targetsize-24.png
│ ├── Square44x44Logo.altform-unplated_targetsize-256.png
│ ├── Square44x44Logo.altform-unplated_targetsize-32.png
│ ├── Square44x44Logo.altform-unplated_targetsize-48.png
│ ├── Square44x44Logo.scale-100.png
│ ├── Square44x44Logo.scale-125.png
│ ├── Square44x44Logo.scale-150.png
│ ├── Square44x44Logo.scale-200.png
│ ├── Square44x44Logo.scale-400.png
│ ├── Square44x44Logo.targetsize-16.png
│ ├── Square44x44Logo.targetsize-24.png
│ ├── Square44x44Logo.targetsize-256.png
│ ├── Square44x44Logo.targetsize-32.png
│ ├── Square44x44Logo.targetsize-48.png
│ ├── StoreLogo.png
│ ├── StoreLogo.scale-100.png
│ ├── StoreLogo.scale-125.png
│ ├── StoreLogo.scale-150.png
│ ├── StoreLogo.scale-200.png
│ ├── StoreLogo.scale-400.png
│ ├── Wide310x150Logo.scale-100.png
│ ├── Wide310x150Logo.scale-125.png
│ ├── Wide310x150Logo.scale-150.png
│ ├── Wide310x150Logo.scale-200.png
│ └── Wide310x150Logo.scale-400.png
├── Package.appxmanifest
└── Packaging.wapproj
├── Properties
├── AssemblyInfo.cs
├── CustomSettingsProvider.cs
├── Settings.Designer.cs
└── Settings.settings
├── README.md
├── Resources
├── HowToOpenSettings.png
├── Languages
│ ├── lang.af.resx
│ ├── lang.ar.resx
│ ├── lang.az.resx
│ ├── lang.be.resx
│ ├── lang.bg.resx
│ ├── lang.bn.resx
│ ├── lang.ca.resx
│ ├── lang.cs.resx
│ ├── lang.cy.resx
│ ├── lang.da.resx
│ ├── lang.de.resx
│ ├── lang.el.resx
│ ├── lang.en-GB.resx
│ ├── lang.eo.resx
│ ├── lang.es.resx
│ ├── lang.et.resx
│ ├── lang.eu.resx
│ ├── lang.fa.resx
│ ├── lang.fi.resx
│ ├── lang.fil.resx
│ ├── lang.fr.resx
│ ├── lang.ga.resx
│ ├── lang.gl.resx
│ ├── lang.gu.resx
│ ├── lang.he.resx
│ ├── lang.hi.resx
│ ├── lang.hr.resx
│ ├── lang.ht.resx
│ ├── lang.hu.resx
│ ├── lang.hy.resx
│ ├── lang.id.resx
│ ├── lang.is.resx
│ ├── lang.it.resx
│ ├── lang.ja.resx
│ ├── lang.ka.resx
│ ├── lang.km.resx
│ ├── lang.kn.resx
│ ├── lang.ko.resx
│ ├── lang.la.resx
│ ├── lang.lo.resx
│ ├── lang.lt.resx
│ ├── lang.lv.resx
│ ├── lang.mk.resx
│ ├── lang.ms.resx
│ ├── lang.mt.resx
│ ├── lang.nb.resx
│ ├── lang.nl.resx
│ ├── lang.pl.resx
│ ├── lang.pt-BR.resx
│ ├── lang.pt-PT.resx
│ ├── lang.resx
│ ├── lang.ro.resx
│ ├── lang.ru.resx
│ ├── lang.sk.resx
│ ├── lang.sl.resx
│ ├── lang.sq.resx
│ ├── lang.sr.resx
│ ├── lang.sv.resx
│ ├── lang.sw.resx
│ ├── lang.ta.resx
│ ├── lang.te.resx
│ ├── lang.th.resx
│ ├── lang.tr.resx
│ ├── lang.uk.resx
│ ├── lang.ur.resx
│ ├── lang.vi.resx
│ ├── lang.yi.resx
│ ├── lang.zh-CN.resx
│ └── lang.zh-TW.resx
├── LinkArrow.ico
├── Loading.ico
├── NotFound.ico
├── ScrollBarStyles.xaml
├── SystemTrayMenu.ico
├── SystemTrayMenu.png
├── app.manifest
├── ic_fluent_arrow_sync_24_regular.svg
├── ic_fluent_folder_arrow_right_48_regular.svg
├── ic_fluent_pin_48_filled.svg
├── ic_fluent_pin_48_regular.svg
├── ic_fluent_search_48_regular.svg
└── ic_fluent_settings_28_regular.svg
├── SystemTrayMenu.csproj
├── SystemTrayMenu.sln
├── ThirdParty
└── Clearcove.Logging.dll
├── UserInterface
├── AboutBox.xaml
├── AboutBox.xaml.cs
├── AppContextMenu.cs
├── AppNotifyIcon.cs
├── ColorPickerWindow.xaml
├── ColorPickerWindow.xaml.cs
├── ColorSelector.xaml
├── ColorSelector.xaml.cs
├── FolderBrowseDialog
│ ├── FolderDialog.cs
│ └── IFolderDialog.cs
├── HotkeySelector.cs
├── HowToOpenSettingsWindow.xaml
├── HowToOpenSettingsWindow.xaml.cs
├── Menu.xaml
├── Menu.xaml.cs
├── NumericUpDown.xaml
├── NumericUpDown.xaml.cs
├── SettingsWindow.xaml
├── SettingsWindow.xaml.cs
├── ShellContextMenu.cs
├── TaskbarLogo.xaml
├── TaskbarLogo.xaml.cs
├── UpdateWindow.xaml
└── UpdateWindow.xaml.cs
├── Utilities
├── ActionCommand.cs
├── AppRestart.cs
├── File
│ ├── FileLnk.cs
│ └── IconReader.cs
├── FolderOptions.cs
├── GenerateDriveShortcuts.cs
├── Log.cs
├── PrivilegeChecker.cs
├── ScaleDouble.cs
├── Scaling.cs
├── SingleAppInstance.cs
├── Translate.cs
├── Translator.cs
└── WPFExtensions.cs
├── app.config
└── azure-pipelines.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # WFAC010: Unsupported high DPI configuration
4 | dotnet_diagnostic.WFAC010.severity = silent
5 | csharp_indent_labels = one_less_than_current
6 | csharp_using_directive_placement = outside_namespace:silent
7 | csharp_prefer_simple_using_statement = true:suggestion
8 | csharp_prefer_braces = true:silent
9 | csharp_style_namespace_declarations = block_scoped:silent
10 | csharp_style_prefer_method_group_conversion = true:silent
11 | csharp_style_prefer_top_level_statements = true:silent
12 | csharp_style_expression_bodied_methods = false:silent
13 | csharp_style_expression_bodied_constructors = false:silent
14 | csharp_style_expression_bodied_operators = false:silent
15 | csharp_style_expression_bodied_properties = true:silent
16 | csharp_style_expression_bodied_indexers = true:silent
17 | csharp_style_expression_bodied_accessors = true:silent
18 | csharp_style_expression_bodied_lambdas = true:silent
19 | csharp_style_expression_bodied_local_functions = false:silent
20 | dotnet_diagnostic.SX1101.severity = warning
21 | dotnet_diagnostic.SA1101.severity = silent
22 |
23 | [*.{cs,vb}]
24 | #### Naming styles ####
25 |
26 | # Naming rules
27 |
28 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
29 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
30 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
31 |
32 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
33 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
34 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
35 |
36 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
37 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
38 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
39 |
40 | # Symbol specifications
41 |
42 | dotnet_naming_symbols.interface.applicable_kinds = interface
43 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
44 | dotnet_naming_symbols.interface.required_modifiers =
45 |
46 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
47 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
48 | dotnet_naming_symbols.types.required_modifiers =
49 |
50 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
51 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
52 | dotnet_naming_symbols.non_field_members.required_modifiers =
53 |
54 | # Naming styles
55 |
56 | dotnet_naming_style.begins_with_i.required_prefix = I
57 | dotnet_naming_style.begins_with_i.required_suffix =
58 | dotnet_naming_style.begins_with_i.word_separator =
59 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
60 |
61 | dotnet_naming_style.pascal_case.required_prefix =
62 | dotnet_naming_style.pascal_case.required_suffix =
63 | dotnet_naming_style.pascal_case.word_separator =
64 | dotnet_naming_style.pascal_case.capitalization = pascal_case
65 |
66 | dotnet_naming_style.pascal_case.required_prefix =
67 | dotnet_naming_style.pascal_case.required_suffix =
68 | dotnet_naming_style.pascal_case.word_separator =
69 | dotnet_naming_style.pascal_case.capitalization = pascal_case
70 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
71 | tab_width = 4
72 | indent_size = 4
73 | end_of_line = crlf
74 | dotnet_style_coalesce_expression = true:suggestion
75 | dotnet_style_null_propagation = true:suggestion
76 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
77 | dotnet_style_prefer_auto_properties = true:silent
78 | dotnet_style_object_initializer = true:suggestion
79 | dotnet_style_collection_initializer = true:suggestion
80 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: Hofknecht # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Description, Steps to reproduce, Expected behavior, Screenshots**
11 |
12 |
13 |
14 | - OS: [e.g. Microsoft Windows 10 Pro]:
15 | - Version [e.g. 0.9.1.233]
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[Feature]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/dotnetframework.yml:
--------------------------------------------------------------------------------
1 | name: NetFramework
2 |
3 | on: [push]
4 | jobs:
5 | build:
6 | runs-on: windows-latest
7 | steps:
8 | - uses: actions/checkout@v2
9 | - name: Setup Nuget.exe
10 | uses: nuget/setup-nuget@v1
11 | - name: Nuget Restore
12 | run: nuget restore SystemTrayMenu.sln
13 | - name: Setup MSBuild.exe
14 | uses: microsoft/setup-msbuild@v1
15 | - name: Build with MSBuild
16 | run: msbuild SystemTrayMenu.sln -p:Configuration=Release
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # SVN
7 | .svn*
8 |
9 | # User-specific files
10 | *.suo
11 | *.user
12 | *.userosscache
13 | *.sln.docstates
14 |
15 | # User-specific files (MonoDevelop/Xamarin Studio)
16 | *.userprefs
17 |
18 | # Build results
19 | [Dd]ebug/
20 | [Dd]ebugPublic/
21 | [Rr]elease/
22 | [Rr]eleases/
23 | x64/
24 | x86/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 | **/Properties/launchSettings.json
59 |
60 | # StyleCop
61 | StyleCopReport.xml
62 |
63 | # Files built by Visual Studio
64 | *_i.c
65 | *_p.c
66 | *_i.h
67 | *.ilk
68 | *.meta
69 | *.obj
70 | *.iobj
71 | *.pch
72 | *.pdb
73 | *.ipdb
74 | *.pgc
75 | *.pgd
76 | *.rsp
77 | *.sbr
78 | *.tlb
79 | *.tli
80 | *.tlh
81 | *.tmp
82 | *.tmp_proj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 |
259 | # Microsoft Fakes
260 | FakesAssemblies/
261 |
262 | # GhostDoc plugin setting file
263 | *.GhostDoc.xml
264 |
265 | # Node.js Tools for Visual Studio
266 | .ntvs_analysis.dat
267 | node_modules/
268 |
269 | # Visual Studio 6 build log
270 | *.plg
271 |
272 | # Visual Studio 6 workspace options file
273 | *.opt
274 |
275 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
276 | *.vbw
277 |
278 | # Visual Studio LightSwitch build output
279 | **/*.HTMLClient/GeneratedArtifacts
280 | **/*.DesktopClient/GeneratedArtifacts
281 | **/*.DesktopClient/ModelManifest.xml
282 | **/*.Server/GeneratedArtifacts
283 | **/*.Server/ModelManifest.xml
284 | _Pvt_Extensions
285 |
286 | # Paket dependency manager
287 | .paket/paket.exe
288 | paket-files/
289 |
290 | # FAKE - F# Make
291 | .fake/
292 |
293 | # JetBrains Rider
294 | .idea/
295 | *.sln.iml
296 |
297 | # CodeRush
298 | .cr/
299 |
300 | # Python Tools for Visual Studio (PTVS)
301 | __pycache__/
302 | *.pyc
303 |
304 | # Cake - Uncomment if you are using it
305 | # tools/**
306 | # !tools/packages.config
307 |
308 | # Tabs Studio
309 | *.tss
310 |
311 | # Telerik's JustMock configuration file
312 | *.jmconfig
313 |
314 | # BizTalk build output
315 | *.btp.cs
316 | *.btm.cs
317 | *.odx.cs
318 | *.xsd.cs
319 |
320 | # OpenCover UI analysis results
321 | OpenCover/
322 |
323 | # Azure Stream Analytics local run output
324 | ASALocalRun/
325 |
326 | # MSBuild Binary and Structured Log
327 | *.binlog
328 |
329 | # NVidia Nsight GPU debugger configuration file
330 | *.nvuser
331 |
332 | # MFractors (Xamarin productivity tool) working folder
333 | .mfractor/
334 |
--------------------------------------------------------------------------------
/.stylecop.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
3 | "settings": {
4 | "orderingRules": {
5 | "usingDirectivesPlacement": "insideNamespace"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Business/App.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
31 |
36 |
39 |
44 |
47 |
52 |
60 |
64 |
69 |
74 |
78 |
83 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/Business/App.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu
6 | {
7 | using System;
8 | using System.Drawing;
9 | using System.IO;
10 | using System.Windows;
11 | using System.Windows.Threading;
12 | using SystemTrayMenu.Business;
13 | using SystemTrayMenu.Helpers;
14 | using SystemTrayMenu.Helpers.Updater;
15 | using SystemTrayMenu.Properties;
16 | using SystemTrayMenu.Utilities;
17 |
18 | ///
19 | /// App contains the notifyicon, the taskbarform and the menus.
20 | ///
21 | public partial class App : Application, IDisposable
22 | {
23 | private Menus? menus;
24 | private JoystickHelper? joystickHelper;
25 | private bool isDisposed;
26 |
27 | public App()
28 | {
29 | AppContext.SetSwitch("Switch.System.Windows.Controls.Text.UseAdornerForTextboxSelectionRendering", false);
30 |
31 | InitializeComponent();
32 |
33 | AppRestart.BeforeRestarting += Dispose;
34 |
35 | Activated += (_, _) => IsActiveApp = true;
36 | Deactivated += (_, _) => IsActiveApp = false;
37 | Startup += (_, _) =>
38 | {
39 | IconReader.Startup();
40 |
41 | menus = new();
42 | menus.Startup();
43 |
44 | if (Settings.Default.SupportGamepad)
45 | {
46 | joystickHelper = new();
47 | joystickHelper.KeyPressed += menus.KeyPressed;
48 | }
49 |
50 | if (Settings.Default.CheckForUpdates)
51 | {
52 | _ = Dispatcher.InvokeAsync(
53 | () => GitHubUpdate.ActivateNewVersionFormOrCheckForUpdates(showWhenUpToDate: false),
54 | DispatcherPriority.ApplicationIdle);
55 | }
56 | };
57 | }
58 |
59 | internal static bool IsActiveApp { get; private set; }
60 |
61 | public void Dispose()
62 | {
63 | Dispose(true);
64 | GC.SuppressFinalize(this);
65 | }
66 |
67 | ///
68 | /// Loads an Icon from the application's Resources.
69 | /// Note: Only allowed to be called after App's Startup event.
70 | ///
71 | /// Absolute file path from root directory.
72 | /// New Icon object.
73 | internal static Icon LoadIconFromResource(string resourceName)
74 | {
75 | using (Stream stream = GetResourceStream(new("pack://application:,,,/" + resourceName, UriKind.Absolute)).Stream)
76 | {
77 | return new(stream);
78 | }
79 | }
80 |
81 | protected virtual void Dispose(bool disposing)
82 | {
83 | if (!isDisposed)
84 | {
85 | IconReader.Shutdown();
86 |
87 | if (joystickHelper != null)
88 | {
89 | if (menus != null)
90 | {
91 | joystickHelper.KeyPressed -= menus.KeyPressed;
92 | }
93 |
94 | joystickHelper.Dispose();
95 | }
96 |
97 | menus?.Dispose();
98 |
99 | isDisposed = true;
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Business/IpcPipe.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2023-2023 Peter Kirmeier
6 | // Based on example: https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication
7 |
8 | namespace SystemTrayMenu.Business
9 | {
10 | using System;
11 | using System.IO;
12 | using System.IO.Pipes;
13 | using System.Reflection;
14 | using System.Text;
15 | using System.Threading;
16 | using SystemTrayMenu.Utilities;
17 |
18 | internal class IpcPipe : IDisposable
19 | {
20 | private readonly string pipeName;
21 | private readonly string pipeHello;
22 |
23 | private NamedPipeServerStream? serverPipe;
24 | private Thread? serverThread;
25 | private Func? serverHandler;
26 | private ManualResetEvent? cancelEvent;
27 | private bool isStopped;
28 |
29 | internal IpcPipe(byte version, string name)
30 | {
31 | pipeName = version.ToString() + "." + name + "." + (Assembly.GetExecutingAssembly().GetName().Name ?? "local");
32 | pipeHello = "Hello " + nameof(IpcPipe) + " from " + pipeName + "!";
33 | }
34 |
35 | public void Dispose()
36 | {
37 | isStopped = true;
38 | cancelEvent?.Set();
39 | serverThread?.Join(250);
40 | serverPipe?.Dispose();
41 | cancelEvent?.Dispose();
42 | serverThread = null;
43 | }
44 |
45 | internal void StartServer(Func handler)
46 | {
47 | cancelEvent = new(false);
48 | serverHandler = handler;
49 |
50 | serverThread = new(ServerThread);
51 | serverThread?.Start(this);
52 | }
53 |
54 | internal string? SendToServer(string request)
55 | {
56 | string? response = null;
57 | NamedPipeClientStream pipeClient = new(pipeName);
58 |
59 | // Wait a bit in case server is restarting
60 | pipeClient.Connect(500);
61 |
62 | IpcPipeStream stream = new (pipeClient);
63 | string hello = stream.ReadString();
64 |
65 | // Check who there is, just for sanity
66 | if (hello == pipeHello)
67 | {
68 | stream.WriteString(request);
69 | response = stream.ReadString();
70 | }
71 | else
72 | {
73 | Log.Info($"IPC client pipe error: Invalid hello: \"" + hello + "\"");
74 | }
75 |
76 | pipeClient.Dispose();
77 |
78 | return response;
79 | }
80 |
81 | private static void ServerThread(object? data)
82 | {
83 | IpcPipe ipc = (IpcPipe)data!;
84 | Func handler = ipc.serverHandler ?? (_ => string.Empty);
85 |
86 | while (!ipc.isStopped)
87 | {
88 | NamedPipeServerStream pipe = ipc.serverPipe = new(ipc.pipeName);
89 | try
90 | {
91 | // Stupid workaround for WaitForConnection as it will not unblock when pipe gets closed or disposed.
92 | // See: https://stackoverflow.com/questions/607872/what-is-a-good-way-to-shutdown-threads-blocked-on-namedpipeserverwaitforconnect
93 | ManualResetEvent connectEvent = new (false);
94 | pipe.BeginWaitForConnection(ar => { connectEvent.Set(); }, null);
95 | if (WaitHandle.WaitAny(new WaitHandle[] { connectEvent, ipc.cancelEvent! }) == 1)
96 | {
97 | ipc.serverPipe = null;
98 | pipe.Dispose();
99 | return;
100 | }
101 |
102 | IpcPipeStream stream = new(pipe);
103 |
104 | // Send indicator who we are, just for sanity checking
105 | stream.WriteString(ipc.pipeHello);
106 |
107 | string request = stream.ReadString();
108 | string respone = handler.Invoke(request);
109 | stream.WriteString(respone);
110 | }
111 | catch (Exception ex)
112 | {
113 | Log.Warn($"IPC server pipe error: ", ex);
114 | }
115 |
116 | ipc.serverPipe = null;
117 | pipe.Dispose();
118 | }
119 | }
120 |
121 | // Defines the data protocol for reading and writing strings on our stream
122 | private class IpcPipeStream
123 | {
124 | private readonly Stream ioStream;
125 | private readonly UnicodeEncoding streamEncoding = new();
126 |
127 | internal IpcPipeStream(Stream ioStream)
128 | {
129 | this.ioStream = ioStream;
130 | }
131 |
132 | internal string ReadString()
133 | {
134 | int len = 0;
135 |
136 | // Receive 32 bit message size
137 | len += ioStream.ReadByte() >> 24;
138 | len += ioStream.ReadByte() >> 16;
139 | len += ioStream.ReadByte() >> 8;
140 | len += ioStream.ReadByte() >> 0;
141 |
142 | // Receive message
143 | byte[] inBuffer = new byte[len];
144 | ioStream.Read(inBuffer, 0, len);
145 |
146 | return streamEncoding.GetString(inBuffer);
147 | }
148 |
149 | internal int WriteString(string outString)
150 | {
151 | byte[] outBuffer = streamEncoding.GetBytes(outString);
152 | int len = outBuffer.Length;
153 | if (len > int.MaxValue)
154 | {
155 | throw new InternalBufferOverflowException("More data than available space withing an IPC message");
156 | }
157 |
158 | // Send 32 bit message size
159 | ioStream.WriteByte((byte)((len >> 24) & 0xFF));
160 | ioStream.WriteByte((byte)((len >> 16) & 0xFF));
161 | ioStream.WriteByte((byte)((len >> 8) & 0xFF));
162 | ioStream.WriteByte((byte)((len >> 0) & 0xFF));
163 |
164 | // Send message
165 | ioStream.Write(outBuffer, 0, len);
166 |
167 | ioStream.Flush();
168 |
169 | return outBuffer.Length + 2;
170 | }
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/Business/Program.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu
6 | {
7 | using System;
8 | using System.Reflection;
9 | using System.Windows;
10 | using SystemTrayMenu.Utilities;
11 |
12 | internal static class Program
13 | {
14 | private static bool isStartup = true;
15 |
16 | [STAThread]
17 | private static void Main(string[] args)
18 | {
19 | try
20 | {
21 | Log.Initialize();
22 | Translator.Initialize();
23 | Config.SetFolderByWindowsContextMenu(args);
24 | Config.LoadOrSetByUser();
25 | Config.Initialize();
26 | PrivilegeChecker.Initialize();
27 |
28 | // Without a valid path we cannot do anything, just close application
29 | if (string.IsNullOrEmpty(Config.Path))
30 | {
31 | MessageBox.Show(
32 | Translator.GetText("Your root directory for the app does not exist or is empty! Change the root directory or put some files, directories or shortcuts into the root directory."),
33 | "SystemTrayMenu",
34 | MessageBoxButton.OK);
35 | return;
36 | }
37 |
38 | if (SingleAppInstance.Initialize())
39 | {
40 | AppDomain currentDomain = AppDomain.CurrentDomain;
41 | currentDomain.UnhandledException += (sender, args)
42 | => AskUserSendError((Exception)args.ExceptionObject);
43 |
44 | Scaling.Initialize();
45 | FolderOptions.Initialize();
46 |
47 | using App app = new ();
48 | isStartup = false;
49 | Log.WriteApplicationRuns();
50 | app.Run();
51 | }
52 | }
53 | catch (Exception ex)
54 | {
55 | AskUserSendError(ex);
56 | }
57 | finally
58 | {
59 | SingleAppInstance.Unload();
60 | Log.Close();
61 | }
62 |
63 | static void AskUserSendError(Exception ex)
64 | {
65 | Log.Error("Application Crashed", ex);
66 |
67 | MessageBoxResult dialogResult = MessageBox.Show(
68 | "A problem has been encountered and SystemTrayMenu needs to restart. " +
69 | "Reporting this error will help us making our product better." +
70 | Environment.NewLine + Environment.NewLine +
71 | "We kindly ask you to press 'Yes' and send us the crash report before restarting the application. " +
72 | "This will open your standard email app and prepare a mail that you can directly send off. " +
73 | "Alternatively, you can also create an issue manually here: https://github.com/Hofknecht/SystemTrayMenu/issues" +
74 | Environment.NewLine + Environment.NewLine +
75 | "Pressing 'No' will only restart the application." +
76 | Environment.NewLine +
77 | "Pressing 'Cancel' will quit the application.",
78 | "SystemTrayMenu Crashed",
79 | MessageBoxButton.YesNoCancel);
80 |
81 | if (dialogResult == MessageBoxResult.Yes)
82 | {
83 | Version? version = Assembly.GetEntryAssembly()?.GetName().Version;
84 | Log.ProcessStart("mailto:" + "markus@hofknecht.eu" +
85 | "?subject=SystemTrayMenu Bug reported " +
86 | (version != null ? version.ToString() : string.Empty) +
87 | "&body=" + ex.ToString());
88 | }
89 |
90 | if (!isStartup && dialogResult != MessageBoxResult.Cancel)
91 | {
92 | AppRestart.ByThreadException();
93 | }
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Business/WaitToLoadMenu.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.Business
6 | {
7 | using System;
8 | using System.Windows.Threading;
9 | using SystemTrayMenu.DataClasses;
10 |
11 | internal class WaitToLoadMenu : IDisposable
12 | {
13 | private readonly DispatcherTimer timerStartLoad = new();
14 | private RowData? currentItemData;
15 | private bool alreadyOpened;
16 |
17 | internal WaitToLoadMenu()
18 | {
19 | timerStartLoad.Interval = TimeSpan.FromMilliseconds(Properties.Settings.Default.TimeUntilOpens);
20 | timerStartLoad.Tick += OpenSubMenuByTimer;
21 | }
22 |
23 | internal event Action? StopLoadMenu;
24 |
25 | internal event Action? MouseSelect;
26 |
27 | internal bool MouseActive { get; set; }
28 |
29 | public void Dispose() => timerStartLoad.Stop();
30 |
31 | internal void MouseEnter(RowData itemData)
32 | {
33 | if (MouseActive)
34 | {
35 | timerStartLoad.Stop();
36 | StopLoadMenu?.Invoke();
37 | SetData(itemData);
38 | timerStartLoad.Start();
39 | }
40 | }
41 |
42 | internal void MouseLeave()
43 | {
44 | if (MouseActive)
45 | {
46 | timerStartLoad.Stop();
47 | StopLoadMenu?.Invoke();
48 | currentItemData = null;
49 | }
50 | }
51 |
52 | internal void RowSelectionChanged(RowData? itemData)
53 | {
54 | // Deselect
55 | timerStartLoad.Stop();
56 | StopLoadMenu?.Invoke();
57 | currentItemData = null;
58 | MouseActive = false;
59 |
60 | // Select (if any)
61 | if (itemData != null)
62 | {
63 | SetData(itemData);
64 | timerStartLoad.Start();
65 | }
66 | }
67 |
68 | internal void OpenSubMenuByMouse(RowData itemData)
69 | {
70 | timerStartLoad.Stop();
71 | StopLoadMenu?.Invoke();
72 | SetData(itemData);
73 | MouseActive = true;
74 | MouseSelect?.Invoke(itemData);
75 | OpenSubMenu();
76 | }
77 |
78 | internal void OpenSubMenuByKey(RowData itemData)
79 | {
80 | timerStartLoad.Stop();
81 | StopLoadMenu?.Invoke();
82 | SetData(itemData);
83 | MouseActive = false;
84 | OpenSubMenu();
85 | }
86 |
87 | private void OpenSubMenuByTimer(object? sender, EventArgs e)
88 | {
89 | timerStartLoad.Stop();
90 | StopLoadMenu?.Invoke();
91 | if (MouseActive && currentItemData != null)
92 | {
93 | MouseSelect?.Invoke(currentItemData);
94 | }
95 |
96 | OpenSubMenu();
97 | }
98 |
99 | private void OpenSubMenu()
100 | {
101 | if (!alreadyOpened && currentItemData != null)
102 | {
103 | alreadyOpened = true; // TODO: Check if this bool is still needed?
104 |
105 | currentItemData.OpenSubMenu();
106 | }
107 | }
108 |
109 | private void SetData(RowData itemData)
110 | {
111 | if (currentItemData != itemData)
112 | {
113 | alreadyOpened = false;
114 |
115 | currentItemData = itemData;
116 | }
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at markus@hofknecht.eu. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11 | build.
12 | 2. Update the README.md with details of changes to the interface, this includes new environment
13 | variables, exposed ports, useful file locations and container parameters.
14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this
15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
17 | do not have permission to do that, you may request the second reviewer to merge it for you.
18 |
19 | ## Code of Conduct
20 |
21 | ### Our Pledge
22 |
23 | In the interest of fostering an open and welcoming environment, we as
24 | contributors and maintainers pledge to making participation in our project and
25 | our community a harassment-free experience for everyone, regardless of age, body
26 | size, disability, ethnicity, gender identity and expression, level of experience,
27 | nationality, personal appearance, race, religion, or sexual identity and
28 | orientation.
29 |
30 | ### Our Standards
31 |
32 | Examples of behavior that contributes to creating a positive environment
33 | include:
34 |
35 | * Using welcoming and inclusive language
36 | * Being respectful of differing viewpoints and experiences
37 | * Gracefully accepting constructive criticism
38 | * Focusing on what is best for the community
39 | * Showing empathy towards other community members
40 |
41 | Examples of unacceptable behavior by participants include:
42 |
43 | * The use of sexualized language or imagery and unwelcome sexual attention or
44 | advances
45 | * Trolling, insulting/derogatory comments, and personal or political attacks
46 | * Public or private harassment
47 | * Publishing others' private information, such as a physical or electronic
48 | address, without explicit permission
49 | * Other conduct which could reasonably be considered inappropriate in a
50 | professional setting
51 |
52 | ### Our Responsibilities
53 |
54 | Project maintainers are responsible for clarifying the standards of acceptable
55 | behavior and are expected to take appropriate and fair corrective action in
56 | response to any instances of unacceptable behavior.
57 |
58 | Project maintainers have the right and responsibility to remove, edit, or
59 | reject comments, commits, code, wiki edits, issues, and other contributions
60 | that are not aligned to this Code of Conduct, or to ban temporarily or
61 | permanently any contributor for other behaviors that they deem inappropriate,
62 | threatening, offensive, or harmful.
63 |
64 | ### Scope
65 |
66 | This Code of Conduct applies both within project spaces and in public spaces
67 | when an individual is representing the project or its community. Examples of
68 | representing a project or community include using an official project e-mail
69 | address, posting via an official social media account, or acting as an appointed
70 | representative at an online or offline event. Representation of a project may be
71 | further defined and clarified by project maintainers.
72 |
73 | ### Enforcement
74 |
75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
76 | reported by contacting the project team at Markus@Hofknecht.eu. All
77 | complaints will be reviewed and investigated and will result in a response that
78 | is deemed necessary and appropriate to the circumstances. The project team is
79 | obligated to maintain confidentiality with regard to the reporter of an incident.
80 | Further details of specific enforcement policies may be posted separately.
81 |
82 | Project maintainers who do not follow or enforce the Code of Conduct in good
83 | faith may face temporary or permanent repercussions as determined by other
84 | members of the project's leadership.
85 |
86 | ### Attribution
87 |
88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
89 | available at [http://contributor-covenant.org/version/1/4][version]
90 |
91 | [homepage]: http://contributor-covenant.org
92 | [version]: http://contributor-covenant.org/version/1/4/
93 |
--------------------------------------------------------------------------------
/Config/AppColors.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu
6 | {
7 | using System.Windows.Media;
8 |
9 | internal static class AppColors
10 | {
11 | /* -- General -- */
12 |
13 | public static SolidColorBrush SelectedItem { get; internal set; } = new(Color.FromRgb(204, 232, 255));
14 |
15 | public static SolidColorBrush DarkModeSelecetedItem { get; internal set; } = new (Color.FromRgb(51, 51, 51));
16 |
17 | public static SolidColorBrush SelectedItemBorder { get; internal set; } = new (Color.FromRgb(153, 209, 255));
18 |
19 | public static SolidColorBrush DarkModeSelectedItemBorder { get; internal set; } = new (Color.FromRgb(20, 29, 75));
20 |
21 | public static SolidColorBrush OpenFolder { get; internal set; } = new (Color.FromRgb(194, 245, 222));
22 |
23 | public static SolidColorBrush DarkModeOpenFolder { get; internal set; } = new (Color.FromRgb(20, 65, 42));
24 |
25 | public static SolidColorBrush OpenFolderBorder { get; internal set; } = new (Color.FromRgb(153, 255, 165));
26 |
27 | public static SolidColorBrush DarkModeOpenFolderBorder { get; internal set; } = new (Color.FromRgb(20, 75, 85));
28 |
29 | public static SolidColorBrush Background { get; internal set; } = new (Color.FromRgb(255, 255, 255));
30 |
31 | public static SolidColorBrush DarkModeBackground { get; internal set; } = new (Color.FromRgb(32, 32, 32));
32 |
33 | public static SolidColorBrush BackgroundBorder { get; internal set; } = new (Color.FromRgb(0, 0, 0));
34 |
35 | public static SolidColorBrush DarkModeBackgroundBorder { get; internal set; } = new (Color.FromRgb(0, 0, 0));
36 |
37 | public static SolidColorBrush SearchField { get; internal set; } = new (Color.FromRgb(255, 255, 255));
38 |
39 | public static SolidColorBrush DarkModeSearchField { get; internal set; } = new (Color.FromRgb(25, 25, 25));
40 |
41 | public static SolidColorBrush Icons { get; internal set; } = new (Color.FromRgb(149, 160, 166));
42 |
43 | public static SolidColorBrush DarkModeIcons { get; internal set; } = new (Color.FromRgb(149, 160, 166));
44 |
45 | /* -- ScrollBar -- */
46 |
47 | public static SolidColorBrush Arrow { get; internal set; } = new(Color.FromRgb(96, 96, 96));
48 |
49 | public static SolidColorBrush ArrowHoverBackground { get; internal set; } = new(Color.FromRgb(218, 218, 218));
50 |
51 | public static SolidColorBrush ArrowHover { get; internal set; } = new(Color.FromRgb(0, 0, 0));
52 |
53 | public static SolidColorBrush ArrowClick { get; internal set; } = new(Color.FromRgb(255, 255, 255));
54 |
55 | public static SolidColorBrush ArrowClickBackground { get; internal set; } = new(Color.FromRgb(96, 96, 96));
56 |
57 | public static SolidColorBrush Slider { get; internal set; } = new(Color.FromRgb(205, 205, 205));
58 |
59 | public static SolidColorBrush SliderHover { get; internal set; } = new(Color.FromRgb(166, 166, 166));
60 |
61 | public static SolidColorBrush SliderDragging { get; internal set; } = new(Color.FromRgb(96, 96, 96));
62 |
63 | public static SolidColorBrush ScrollbarBackground { get; internal set; } = new(Color.FromRgb(240, 240, 240));
64 |
65 | public static SolidColorBrush ArrowDarkMode { get; internal set; } = new(Color.FromRgb(103, 103, 103));
66 |
67 | public static SolidColorBrush ArrowHoverBackgroundDarkMode { get; internal set; } = new(Color.FromRgb(55, 55, 55));
68 |
69 | public static SolidColorBrush ArrowHoverDarkMode { get; internal set; } = new(Color.FromRgb(103, 103, 103));
70 |
71 | public static SolidColorBrush ArrowClickDarkMode { get; internal set; } = new(Color.FromRgb(23, 23, 23));
72 |
73 | public static SolidColorBrush ArrowClickBackgroundDarkMode { get; internal set; } = new(Color.FromRgb(166, 166, 166));
74 |
75 | public static SolidColorBrush SliderDarkMode { get; internal set; } = new(Color.FromRgb(77, 77, 77));
76 |
77 | public static SolidColorBrush SliderHoverDarkMode { get; internal set; } = new(Color.FromRgb(122, 122, 122));
78 |
79 | public static SolidColorBrush SliderDraggingDarkMode { get; internal set; } = new(Color.FromRgb(166, 166, 166));
80 |
81 | public static SolidColorBrush ScrollbarBackgroundDarkMode { get; internal set; } = new(Color.FromRgb(23, 23, 23));
82 | }
83 | }
--------------------------------------------------------------------------------
/Config/MenuDefines.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu
6 | {
7 | using System.Windows.Media;
8 |
9 | internal static class MenuDefines
10 | {
11 | internal const int MenusMax = 50;
12 | internal const int LengthMax = 37;
13 |
14 | /* -- General -- */
15 |
16 | public static SolidColorBrush ColorForeground =>
17 | Config.IsDarkMode() ? Brushes.White : Brushes.Black;
18 |
19 | public static SolidColorBrush ColorSelectedItem =>
20 | Config.IsDarkMode() ? AppColors.DarkModeSelecetedItem : AppColors.SelectedItem;
21 |
22 | public static SolidColorBrush ColorSelectedItemBorder =>
23 | Config.IsDarkMode() ? AppColors.DarkModeSelectedItemBorder : AppColors.SelectedItemBorder;
24 |
25 | public static SolidColorBrush ColorOpenFolder =>
26 | Config.IsDarkMode() ? AppColors.DarkModeOpenFolder : AppColors.OpenFolder;
27 |
28 | public static SolidColorBrush ColorOpenFolderBorder =>
29 | Config.IsDarkMode() ? AppColors.DarkModeOpenFolderBorder : AppColors.OpenFolderBorder;
30 |
31 | public static SolidColorBrush ColorIcons =>
32 | Config.IsDarkMode() ? AppColors.DarkModeIcons : AppColors.Icons;
33 |
34 | public static SolidColorBrush ColorBackground =>
35 | Config.IsDarkMode() ? AppColors.DarkModeBackground : AppColors.Background;
36 |
37 | public static SolidColorBrush ColorBackgroundBorder =>
38 | Config.IsDarkMode() ? AppColors.DarkModeBackgroundBorder : AppColors.BackgroundBorder;
39 |
40 | public static SolidColorBrush ColorSearchField =>
41 | Config.IsDarkMode() ? AppColors.DarkModeSearchField : AppColors.SearchField;
42 |
43 | /* -- ScrollBar -- */
44 |
45 | public static SolidColorBrush ColorArrow =>
46 | Config.IsDarkMode() ? AppColors.ArrowDarkMode : AppColors.Arrow;
47 |
48 | public static SolidColorBrush ColorArrowHoverBackground =>
49 | Config.IsDarkMode() ? AppColors.ArrowHoverBackgroundDarkMode : AppColors.ArrowHoverBackground;
50 |
51 | public static SolidColorBrush ColorArrowHover =>
52 | Config.IsDarkMode() ? AppColors.ArrowHoverDarkMode : AppColors.ArrowHover;
53 |
54 | public static SolidColorBrush ColorArrowClick =>
55 | Config.IsDarkMode() ? AppColors.ArrowClickDarkMode : AppColors.ArrowClick;
56 |
57 | public static SolidColorBrush ColorArrowClickBackground =>
58 | Config.IsDarkMode() ? AppColors.ArrowClickBackgroundDarkMode : AppColors.ArrowClickBackground;
59 |
60 | public static SolidColorBrush ColorSlider =>
61 | Config.IsDarkMode() ? AppColors.SliderDarkMode : AppColors.Slider;
62 |
63 | public static SolidColorBrush ColorSliderHover =>
64 | Config.IsDarkMode() ? AppColors.SliderHoverDarkMode : AppColors.SliderHover;
65 |
66 | public static SolidColorBrush ColorSliderDragging =>
67 | Config.IsDarkMode() ? AppColors.SliderDraggingDarkMode : AppColors.SliderDragging;
68 |
69 | public static SolidColorBrush ColorScrollbarBackground =>
70 | Config.IsDarkMode() ? AppColors.ScrollbarBackgroundDarkMode : AppColors.ScrollbarBackground;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/DataClasses/MenuData.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.DataClasses
6 | {
7 | using System.Collections.Generic;
8 |
9 | internal enum MenuDataDirectoryState
10 | {
11 | ///
12 | /// State not defined or data still loading.
13 | ///
14 | Undefined,
15 |
16 | ///
17 | /// Data is available.
18 | ///
19 | Valid,
20 |
21 | ///
22 | /// Loading finished but no data available.
23 | ///
24 | Empty,
25 |
26 | ///
27 | /// Loading failed, so no data available.
28 | ///
29 | NoAccess,
30 | }
31 |
32 | internal struct MenuData
33 | {
34 | public MenuData(RowData? rowDataParent)
35 | {
36 | RowDataParent = rowDataParent;
37 | if (rowDataParent != null)
38 | {
39 | Level = rowDataParent.Level + 1;
40 | }
41 | else
42 | {
43 | Level = 0;
44 | }
45 | }
46 |
47 | internal int Level { get; }
48 |
49 | internal RowData? RowDataParent { get; }
50 |
51 | internal List RowDatas { get; set; } = new ();
52 |
53 | internal MenuDataDirectoryState DirectoryState { get; set; } = MenuDataDirectoryState.Undefined;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | using System.Diagnostics.CodeAnalysis;
6 |
7 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA0001:XML comment analysis is disabled due to project configuration", Justification = "no idea what this is")]
8 |
9 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "we need to document")]
10 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "we need to document")]
11 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "we need to document")]
12 |
13 | [assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "new() should not be replaced by new() ")]
14 | [assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1009:Closing parenthesis should be spaced correctly", Justification = "Weird look when used by null-forgiving operator")]
15 | [assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1011:Closing square brackets should be spaced correctly", Justification = "Conflicts with SA1018:A nullable type symbol within a C# element is not spaced correctly.")]
16 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Standard codecleanup removes the this")]
17 |
18 | [assembly: SuppressMessage("Interoperability", "CA1416:Check platform compatibility", Justification = "this is a long way to get platform compatibility")]
19 |
--------------------------------------------------------------------------------
/Helpers/DragDropHelper.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.Helpers
6 | {
7 | using System;
8 | using System.IO;
9 | using System.Net.Http;
10 | using System.Text;
11 | using System.Threading;
12 | using System.Windows;
13 | using SystemTrayMenu.UserInterface;
14 | using SystemTrayMenu.Utilities;
15 |
16 | public static class DragDropHelper
17 | {
18 | public static void DragEnter(object sender, DragEventArgs e)
19 | {
20 | object data = e.Data.GetData("UniformResourceLocator");
21 |
22 | if (data is MemoryStream memoryStream)
23 | {
24 | byte[] bytes = memoryStream.ToArray();
25 | Encoding encod = Encoding.ASCII;
26 | string url = encod.GetString(bytes);
27 | if (!string.IsNullOrEmpty(url))
28 | {
29 | e.Effects = DragDropEffects.Copy;
30 | }
31 | }
32 | }
33 |
34 | public static void DragDrop(object? sender, DragEventArgs e)
35 | {
36 | string path = ((Menu?)sender)?.RowDataParent?.ResolvedPath ?? Config.Path;
37 | object data = e.Data.GetData("UniformResourceLocator");
38 | if (data is not MemoryStream ms)
39 | {
40 | return;
41 | }
42 |
43 | byte[] bytes = ms.ToArray();
44 | Encoding encod = Encoding.ASCII;
45 | string url = encod.GetString(bytes);
46 |
47 | new Thread(CreateShortcutInBackground).Start();
48 | void CreateShortcutInBackground()
49 | {
50 | CreateShortcut(url.Replace("\0", string.Empty), path);
51 | }
52 | }
53 |
54 | private static void CreateShortcut(string url, string pathToStoreFile)
55 | {
56 | string title = GetUrlShortcutTitle(url);
57 | string fileNamePathShortcut = pathToStoreFile + "\\" + title.Trim() + ".url";
58 | WriteShortcut(url, null, fileNamePathShortcut);
59 | string pathIcon = DownloadUrlIcon(url);
60 | if (!string.IsNullOrEmpty(pathIcon))
61 | {
62 | WriteShortcut(url, pathIcon, fileNamePathShortcut);
63 | }
64 | }
65 |
66 | private static string GetUrlShortcutTitle(string url)
67 | {
68 | string title = url
69 | .Replace("/", " ")
70 | .Replace("https", string.Empty)
71 | .Replace("http", string.Empty);
72 | string invalid =
73 | new string(Path.GetInvalidFileNameChars()) +
74 | new string(Path.GetInvalidPathChars());
75 | foreach (char character in invalid)
76 | {
77 | title = title.Replace(character.ToString(), string.Empty);
78 | }
79 |
80 | title = Truncate(title, 128); // max 255
81 | return title;
82 | }
83 |
84 | private static string Truncate(string value, int maxLength)
85 | {
86 | if (!string.IsNullOrEmpty(value) &&
87 | value.Length > maxLength)
88 | {
89 | value = value[..maxLength];
90 | }
91 |
92 | return value;
93 | }
94 |
95 | private static void WriteShortcut(string url, string? pathIcon, string fileNamePathShortcut)
96 | {
97 | try
98 | {
99 | if (File.Exists(fileNamePathShortcut))
100 | {
101 | File.Delete(fileNamePathShortcut);
102 | }
103 |
104 | StreamWriter writer = new(fileNamePathShortcut);
105 | writer.WriteLine("[InternetShortcut]");
106 | writer.WriteLine($"URL={url.TrimEnd('\0')}");
107 | writer.WriteLine("IconIndex=0");
108 | writer.WriteLine($"HotKey=0");
109 | writer.WriteLine($"IDList=");
110 | if (!string.IsNullOrEmpty(pathIcon))
111 | {
112 | writer.WriteLine($"IconFile={pathIcon}");
113 | }
114 |
115 | writer.Flush();
116 | writer.Close();
117 | }
118 | catch (Exception ex)
119 | {
120 | Log.Warn($"{nameof(WriteShortcut)} failed", ex);
121 | }
122 | }
123 |
124 | private static string DownloadUrlIcon(string url)
125 | {
126 | string pathIcon = string.Empty;
127 | string pathToStoreIcons = Properties.Settings.Default.PathIcoDirectory;
128 | Uri uri = new(url);
129 | string hostname = uri.Host.ToString();
130 | string pathIconPng = Path.Combine(pathToStoreIcons, $"{hostname}.png");
131 | string urlGoogleIconDownload = @"http://www.google.com/s2/favicons?sz=32&domain=" + url;
132 | HttpClient client = new();
133 |
134 | try
135 | {
136 | if (!Directory.Exists(pathToStoreIcons))
137 | {
138 | Directory.CreateDirectory(pathToStoreIcons);
139 | }
140 |
141 | Stream stream = client.GetStreamAsync(urlGoogleIconDownload).Result;
142 | using var fileStream = File.Create(pathIconPng);
143 | stream.Seek(0, SeekOrigin.Begin);
144 | stream.CopyTo(fileStream);
145 | fileStream.Close();
146 |
147 | pathIcon = Path.Combine(pathToStoreIcons, $"{hostname}.ico");
148 | if (!ImagingHelper.ConvertToIcon(pathIconPng, pathIcon, 32))
149 | {
150 | Log.Info("Failed to convert icon.");
151 | }
152 | }
153 | catch (Exception ex)
154 | {
155 | Log.Warn($"{nameof(DownloadUrlIcon)} failed", ex);
156 | }
157 |
158 | try
159 | {
160 | if (File.Exists(pathIconPng))
161 | {
162 | File.Delete(pathIconPng);
163 | }
164 | }
165 | catch (Exception ex)
166 | {
167 | Log.Warn($"{nameof(DownloadUrlIcon)} failed to delete {pathIconPng}", ex);
168 | }
169 |
170 | return pathIcon;
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/Helpers/ImagingHelper.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.Helpers
6 | {
7 | using System.Drawing;
8 | using System.Drawing.Imaging;
9 | using System.IO;
10 | using System.Windows.Media;
11 | using System.Windows.Media.Imaging;
12 |
13 | ///
14 | /// Provides helper methods for imaging.
15 | ///
16 | internal static class ImagingHelper
17 | {
18 | ///
19 | /// Converts a PNG image to a icon (ico).
20 | ///
21 | /// The input stream.
22 | /// The output stream.
23 | /// The size (16x16 px by default).
24 | /// Preserve the aspect ratio.
25 | /// Wether or not the icon was succesfully generated.
26 | internal static bool ConvertToIcon(Stream input, Stream output, int size = 16, bool preserveAspectRatio = false)
27 | {
28 | Bitmap inputBitmap = (Bitmap)Image.FromStream(input);
29 | if (inputBitmap != null)
30 | {
31 | int width, height;
32 | if (preserveAspectRatio)
33 | {
34 | width = size;
35 | height = inputBitmap.Height / inputBitmap.Width * size;
36 | }
37 | else
38 | {
39 | width = height = size;
40 | }
41 |
42 | Bitmap newBitmap = new(inputBitmap, new Size(width, height));
43 | if (newBitmap != null)
44 | {
45 | // save the resized png into a memory stream for future use
46 | using MemoryStream memoryStream = new();
47 | newBitmap.Save(memoryStream, ImageFormat.Png);
48 |
49 | BinaryWriter iconWriter = new(output);
50 | if (output != null && iconWriter != null)
51 | {
52 | // 0-1 reserved, 0
53 | iconWriter.Write((byte)0);
54 | iconWriter.Write((byte)0);
55 |
56 | // 2-3 image type, 1 = icon, 2 = cursor
57 | iconWriter.Write((short)1);
58 |
59 | // 4-5 number of images
60 | iconWriter.Write((short)1);
61 |
62 | // image entry 1
63 | // 0 image width
64 | iconWriter.Write((byte)width);
65 |
66 | // 1 image height
67 | iconWriter.Write((byte)height);
68 |
69 | // 2 number of colors
70 | iconWriter.Write((byte)0);
71 |
72 | // 3 reserved
73 | iconWriter.Write((byte)0);
74 |
75 | // 4-5 color planes
76 | iconWriter.Write((short)0);
77 |
78 | // 6-7 bits per pixel
79 | iconWriter.Write((short)32);
80 |
81 | // 8-11 size of image data
82 | iconWriter.Write((int)memoryStream.Length);
83 |
84 | // 12-15 offset of image data
85 | iconWriter.Write(6 + 16);
86 |
87 | // write image data
88 | // png data must contain the whole png data file
89 | iconWriter.Write(memoryStream.ToArray());
90 |
91 | iconWriter.Flush();
92 |
93 | return true;
94 | }
95 | }
96 |
97 | return false;
98 | }
99 |
100 | return false;
101 | }
102 |
103 | ///
104 | /// Converts a PNG image to a icon (ico).
105 | ///
106 | /// The input path.
107 | /// The output path.
108 | /// The size (16x16 px by default).
109 | /// Preserve the aspect ratio.
110 | /// Wether or not the icon was succesfully generated.
111 | internal static bool ConvertToIcon(string inputPath, string outputPath, int size = 16, bool preserveAspectRatio = false)
112 | {
113 | using FileStream inputStream = new(inputPath, FileMode.Open);
114 | using FileStream outputStream = new(outputPath, FileMode.OpenOrCreate);
115 | return ConvertToIcon(inputStream, outputStream, size, preserveAspectRatio);
116 | }
117 |
118 | ///
119 | /// Renders an image on top of an image.
120 | ///
121 | /// Base image (remains unchanged).
122 | /// Image on top (remains unchanged).
123 | /// Rendered image.
124 | internal static RenderTargetBitmap CreateIconWithOverlay(BitmapSource originalBitmap, BitmapSource overlayBitmap)
125 | {
126 | DrawingVisual dVisual = new ();
127 | using (DrawingContext dc = dVisual.RenderOpen())
128 | {
129 | dc.DrawImage(originalBitmap, new (0, 0, originalBitmap.PixelWidth, originalBitmap.PixelHeight));
130 | dc.DrawImage(overlayBitmap, new (0, 0, originalBitmap.PixelWidth, originalBitmap.PixelHeight));
131 | }
132 |
133 | RenderTargetBitmap targetBitmap = new (originalBitmap.PixelWidth, originalBitmap.PixelHeight, originalBitmap.DpiX, originalBitmap.DpiY, PixelFormats.Default);
134 | targetBitmap.Render(dVisual);
135 | return targetBitmap;
136 | }
137 |
138 | ///
139 | /// Sets a flat the alpha channel value for an image.
140 | ///
141 | /// Base image (remains unchanged).
142 | /// Opacity value.
143 | /// Rendered image.
144 | internal static RenderTargetBitmap ApplyOpactiy(BitmapSource originalBitmap, double opacity)
145 | {
146 | DrawingVisual dVisual = new ();
147 | using (DrawingContext dc = dVisual.RenderOpen())
148 | {
149 | dc.PushOpacity(opacity);
150 | dc.DrawImage(originalBitmap, new(0, 0, originalBitmap.PixelWidth, originalBitmap.PixelHeight));
151 | }
152 |
153 | RenderTargetBitmap targetBitmap = new(originalBitmap.PixelWidth, originalBitmap.PixelHeight, originalBitmap.DpiX, originalBitmap.DpiY, PixelFormats.Default);
154 | targetBitmap.Render(dVisual);
155 | return targetBitmap;
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/Helpers/Updater/GitHubUpdate.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.Helpers.Updater
6 | {
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Net.Http;
10 | using System.Reflection;
11 | using System.Windows;
12 | using SystemTrayMenu.UserInterface;
13 | using SystemTrayMenu.Utilities;
14 |
15 | public class GitHubUpdate
16 | {
17 | private static List>? releases;
18 | private static UpdateWindow? newVersionWindow;
19 |
20 | ///
21 | /// Gets the latest release version name .
22 | ///
23 | public static string LatestVersionName
24 | {
25 | get
26 | {
27 | string result = "Unknown";
28 |
29 | if (releases == null)
30 | {
31 | return result;
32 | }
33 |
34 | try
35 | {
36 | result = releases[0]["tag_name"].ToString()!.Replace("v", string.Empty); // 0 = latest
37 | }
38 | catch (Exception ex)
39 | {
40 | Log.Warn($"{nameof(LatestVersionName)} failed", ex);
41 | }
42 |
43 | return result;
44 | }
45 | }
46 |
47 | ///
48 | /// Opens the website on GitHub of the latest release version .
49 | ///
50 | public static void WebOpenLatestRelease() => Log.ProcessStart("https://github.com/Hofknecht/SystemTrayMenu/releases");
51 |
52 | public static void ActivateNewVersionFormOrCheckForUpdates(bool showWhenUpToDate)
53 | {
54 | if (newVersionWindow != null)
55 | {
56 | newVersionWindow!.HandleInvoke(() => newVersionWindow?.Activate());
57 | }
58 | else
59 | {
60 | CheckForUpdates(showWhenUpToDate);
61 | }
62 | }
63 |
64 | private static void CheckForUpdates(bool showWhenUpToDate)
65 | {
66 | string urlGithubReleases = @"http://api.github.com/repos/Hofknecht/SystemTrayMenu/releases";
67 | HttpClient client = new();
68 |
69 | // https://developer.github.com/v3/#user-agent-required
70 | client.DefaultRequestHeaders.Add("User-Agent", "SystemTrayMenu/" + Assembly.GetExecutingAssembly().GetName().Version!.ToString());
71 |
72 | // https://developer.github.com/v3/media/#request-specific-version
73 | client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3.text+json");
74 |
75 | try
76 | {
77 | string response = client.GetStringAsync(urlGithubReleases).Result;
78 | releases = response.FromJson>>();
79 | }
80 | catch (Exception ex)
81 | {
82 | Log.Warn($"{nameof(CheckForUpdates)} failed", ex);
83 | }
84 |
85 | if (releases == null)
86 | {
87 | Log.Info($"{nameof(CheckForUpdates)} failed.");
88 | }
89 | else
90 | {
91 | RemoveCurrentAndOlderVersions();
92 | ShowNewVersionOrUpToDateDialog(showWhenUpToDate);
93 | }
94 | }
95 |
96 | private static void RemoveCurrentAndOlderVersions()
97 | {
98 | if (releases != null)
99 | {
100 | int releasesCount = releases.Count;
101 | Version versionCurrent = Assembly.GetExecutingAssembly().GetName().Version!;
102 | for (int i = 0; i < releasesCount; i++)
103 | {
104 | string? tagName = releases[i]["tag_name"].ToString();
105 | if (tagName == null)
106 | {
107 | continue;
108 | }
109 |
110 | Version versionGitHub = new(tagName.Replace("v", string.Empty));
111 | if (versionGitHub.CompareTo(versionCurrent) < 1)
112 | {
113 | releases.RemoveRange(i, releasesCount - i);
114 | break;
115 | }
116 | }
117 | }
118 | }
119 |
120 | private static void ShowNewVersionOrUpToDateDialog(bool showWhenUpToDate)
121 | {
122 | if (releases != null)
123 | {
124 | if (releases.Count > 0)
125 | {
126 | newVersionWindow = new();
127 | newVersionWindow.textBox.Text = GetChangelog();
128 | newVersionWindow.Closed += (_, _) => newVersionWindow = null;
129 | newVersionWindow.ShowDialog();
130 | }
131 | else if (showWhenUpToDate)
132 | {
133 | MessageBox.Show(Translator.GetText("You have the latest version of SystemTrayMenu!"));
134 | }
135 | }
136 | }
137 |
138 | ///
139 | /// Returns the change log from current version up to the latest release version.
140 | ///
141 | /// Change log summary or error text.
142 | private static string GetChangelog()
143 | {
144 | string result = string.Empty;
145 | string errorstr = "An error occurred during update check!" + Environment.NewLine;
146 |
147 | if (releases == null)
148 | {
149 | return errorstr + "Could not receive changelog!";
150 | }
151 |
152 | try
153 | {
154 | for (int i = 0; i < releases.Count; i++)
155 | {
156 | Dictionary release = releases[i];
157 | string? bodyText = release["body_text"].ToString();
158 | if (bodyText == null)
159 | {
160 | continue;
161 | }
162 |
163 | result += release["name"].ToString()
164 | + Environment.NewLine
165 | + bodyText
166 | .Replace("\n\n", Environment.NewLine)
167 | .Replace("\n \n", Environment.NewLine)
168 | + Environment.NewLine + Environment.NewLine;
169 | if (i < releases.Count)
170 | {
171 | result += "--------------------------------------------------" +
172 | "-------------------------------------------------------"
173 | + Environment.NewLine;
174 | }
175 | }
176 |
177 | result = result.Replace("\n", Environment.NewLine);
178 | }
179 | catch (Exception ex)
180 | {
181 | Log.Warn($"{nameof(GetChangelog)}", ex);
182 | result = errorstr + ex.Message.ToString();
183 | }
184 |
185 | return result;
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/Helpers/WindowsExplorerSort.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.Helpers
6 | {
7 | using System.Collections.Generic;
8 | using SystemTrayMenu.DllImports;
9 |
10 | internal class WindowsExplorerSort : IComparer
11 | {
12 | public int Compare(string? x, string? y)
13 | {
14 | if (x != null && y != null)
15 | {
16 | return NativeMethods.ShlwapiStrCmpLogicalW(x, y);
17 | }
18 |
19 | return 0;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Helpers/WindowsTaskbar.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.Helpers
6 | {
7 | using System;
8 | using System.Drawing;
9 | using System.Runtime.InteropServices;
10 | using static SystemTrayMenu.DllImports.NativeMethods;
11 |
12 | public enum TaskbarPosition
13 | {
14 | Unknown = -1,
15 | Left,
16 | Top,
17 | Right,
18 | Bottom,
19 | }
20 |
21 | public sealed class WindowsTaskbar
22 | {
23 | private const string ClassName = "Shell_TrayWnd";
24 |
25 | public WindowsTaskbar()
26 | {
27 | IntPtr taskbarHandle = User32FindWindow(ClassName, null);
28 |
29 | APPBARDATA data = new()
30 | {
31 | cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA)),
32 | hWnd = taskbarHandle,
33 | };
34 | IntPtr result = Shell32SHAppBarMessage(ABM.GetTaskbarPos, ref data);
35 | if (result == IntPtr.Zero)
36 | {
37 | Bounds = new Rectangle(20, 20, 20, 20);
38 | }
39 | else
40 | {
41 | Position = (TaskbarPosition)data.uEdge;
42 | Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom);
43 |
44 | data.cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA));
45 | result = Shell32SHAppBarMessage(ABM.GetState, ref data);
46 | int state = result.ToInt32();
47 | AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop;
48 | AutoHide = (state & ABS.Autohide) == ABS.Autohide;
49 | }
50 | }
51 |
52 | public Rectangle Bounds
53 | {
54 | get;
55 | private set;
56 | }
57 |
58 | public TaskbarPosition Position
59 | {
60 | get;
61 | private set;
62 | }
63 |
64 | public Point Location => Bounds.Location;
65 |
66 | public Size Size => Bounds.Size;
67 |
68 | public bool AlwaysOnTop
69 | {
70 | get;
71 | private set;
72 | }
73 |
74 | public bool AutoHide
75 | {
76 | get;
77 | private set;
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/NativeDllImport/HotKey.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.DllImports
6 | {
7 | using System;
8 | using System.Runtime.InteropServices;
9 | using System.Runtime.Versioning;
10 | using System.Text;
11 |
12 | ///
13 | /// wraps the methodcalls to native windows dll's.
14 | ///
15 | public static partial class NativeMethods
16 | {
17 | internal static string GetLastErrorHint()
18 | {
19 | const int ERROR_HOTKEY_ALREADY_REGISTERED = 1409;
20 |
21 | int error = Marshal.GetLastWin32Error();
22 | return error switch
23 | {
24 | ERROR_HOTKEY_ALREADY_REGISTERED => "ERROR_HOTKEY_ALREADY_REGISTERED",
25 | _ => error.ToString(),
26 | };
27 | }
28 |
29 | [SupportedOSPlatform("windows")]
30 | [DllImport("user32.dll", EntryPoint = "RegisterHotKey", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
31 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
32 | [return: MarshalAs(UnmanagedType.Bool)]
33 | internal static extern bool User32RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint virtualKeyCode);
34 |
35 | [SupportedOSPlatform("windows")]
36 | [DllImport("user32.dll", EntryPoint = "UnregisterHotKey", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
37 | [return: MarshalAs(UnmanagedType.Bool)]
38 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
39 | internal static extern bool User32UnregisterHotKey(IntPtr hWnd, int id);
40 |
41 | [SupportedOSPlatform("windows")]
42 | [DllImport("user32.dll", EntryPoint = "MapVirtualKey", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
43 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
44 | internal static extern uint User32MapVirtualKey(uint uCode, uint uMapType);
45 |
46 | [SupportedOSPlatform("windows")]
47 | [DllImport("user32.dll", EntryPoint = "GetKeyNameText", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
48 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
49 | internal static extern int User32GetKeyNameText(uint lParam, [Out] StringBuilder lpString, int nSize);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/NativeDllImport/Icon.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.DllImports
6 | {
7 | using System;
8 | using System.Runtime.InteropServices;
9 | using System.Runtime.Versioning;
10 |
11 | ///
12 | /// wraps the methodcalls to native windows dll's.
13 | ///
14 | public static partial class NativeMethods
15 | {
16 | public const uint ShgfiIcon = 0x000000100; // get icon
17 | public const uint ShgfiSYSICONINDEX = 0x000004000; // get system icon index
18 | public const uint ShgfiLINKOVERLAY = 0x000008000; // put a link overlay on icon
19 | public const uint ShgfiLARGEICON = 0x000000000; // get large icon
20 | public const uint ShgfiSMALLICON = 0x000000001; // get small icon
21 | public const uint ShgfiOPENICON = 0x000000002; // get open icon
22 | public const uint FileAttributeDirectory = 0x00000010;
23 | public const uint FileAttributeNormal = 0x00000080;
24 | public const int IldTransparent = 0x00000001;
25 |
26 | ///
27 | /// comctl32 ImageList_GetIcon(IntPtr himl, int i, int flags).
28 | ///
29 | /// himl.
30 | /// i.
31 | /// flags.
32 | /// IntPtr.
33 | [SupportedOSPlatform("windows")]
34 | [DllImport("comctl32", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
35 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
36 | internal static extern IntPtr ImageList_GetIcon(
37 | IntPtr himl,
38 | int i,
39 | int flags);
40 |
41 | [SupportedOSPlatform("windows")]
42 | [DllImport("User32.dll", EntryPoint = "DestroyIcon", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
43 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
44 | internal static extern int User32DestroyIcon(IntPtr hIcon);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/NativeDllImport/Menu.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.DllImports
6 | {
7 | using System;
8 | using System.Runtime.InteropServices;
9 | using System.Runtime.Versioning;
10 |
11 | ///
12 | /// wraps the methodcalls to native windows dll's.
13 | ///
14 | public static partial class NativeMethods
15 | {
16 | ///
17 | /// Specifies how TrackPopupMenuEx positions the shortcut menu horizontally.
18 | ///
19 | [Flags]
20 | internal enum TPM : uint
21 | {
22 | LEFTBUTTON = 0x0000, // LEFTALIGN = 0x0000, // TOPALIGN = 0x0000, // HORIZONTAL = 0x0000,
23 | RIGHTBUTTON = 0x0002,
24 | CENTERALIGN = 0x0004,
25 | RIGHTALIGN = 0x0008,
26 | VCENTERALIGN = 0x0010,
27 | BOTTOMALIGN = 0x0020,
28 | VERTICAL = 0x0040,
29 | NONOTIFY = 0x0080,
30 | RETURNCMD = 0x0100,
31 | RECURSE = 0x0001,
32 | HORPOSANIMATION = 0x0400,
33 | HORNEGANIMATION = 0x0800,
34 | VERPOSANIMATION = 0x1000,
35 | VERNEGANIMATION = 0x2000,
36 | NOANIMATION = 0x4000,
37 | LAYOUTRTL = 0x8000,
38 | }
39 |
40 | // 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.
41 | [SupportedOSPlatform("windows")]
42 | [DllImport("user32", EntryPoint = "CreatePopupMenu", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
43 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
44 | internal static extern IntPtr User32CreatePopupMenu();
45 |
46 | // The DestroyMenu function destroys the specified menu and frees any memory that the menu occupies.
47 | [SupportedOSPlatform("windows")]
48 | [DllImport("user32", EntryPoint = "DestroyMenu", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
49 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
50 | internal static extern bool User32DestroyMenu(IntPtr hMenu);
51 |
52 | ///
53 | /// user32 TrackPopupMenuEx.
54 | /// The TrackPopupMenuEx function displays a shortcut menu at the specified location and
55 | /// tracks the selection of items on the shortcut menu. The shortcut menu can appear anywhere on the screen.
56 | ///
57 | /// hmenu.
58 | /// flags.
59 | /// x.
60 | /// y.
61 | /// hwnd.
62 | /// lptpm.
63 | /// uint.
64 | [SupportedOSPlatform("windows")]
65 | [DllImport("user32.dll", EntryPoint = "TrackPopupMenuEx", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
66 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
67 | internal static extern uint User32TrackPopupMenuEx(IntPtr hmenu, TPM flags, int x, int y, IntPtr hwnd, IntPtr lptpm);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/NativeDllImport/Screen.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2022-2023 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.DllImports
8 | {
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Runtime.InteropServices;
13 | using System.Runtime.Versioning;
14 | using System.Windows;
15 |
16 | ///
17 | /// wraps the methodcalls to native windows dll's.
18 | ///
19 | public static partial class NativeMethods
20 | {
21 | public static bool IsTouchEnabled()
22 | {
23 | const int MAXTOUCHES_INDEX = 95;
24 | int maxTouches = GetSystemMetrics(MAXTOUCHES_INDEX);
25 |
26 | return maxTouches > 0;
27 | }
28 |
29 | [SupportedOSPlatform("windows")]
30 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
31 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
32 | private static extern int GetSystemMetrics(int nIndex);
33 |
34 | public static class Screen
35 | {
36 | private static Point LastCursorPosition = new Point(0, 0);
37 |
38 | private static List? screens;
39 |
40 | public static List Screens
41 | {
42 | get
43 | {
44 | if ((screens == null) || (screens.Count == 0))
45 | {
46 | FetchScreens();
47 | }
48 |
49 | if ((screens == null) || (screens.Count == 0))
50 | {
51 | return new()
52 | {
53 | new (0, 0, 800, 600),
54 | };
55 | }
56 |
57 | return screens;
58 | }
59 | }
60 |
61 | // The primary screen will have x = 0, y = 0 coordinates
62 | public static Rect PrimaryScreen => Screens.FirstOrDefault((screen) => screen.Left == 0 && screen.Top == 0, Screens[0]);
63 |
64 | public static Point CursorPosition
65 | {
66 | get
67 | {
68 | #if TODO // Maybe use Windows.Desktop instead of Win32 API?
69 | // See: https://learn.microsoft.com/en-us/dotnet/api/system.windows.input.mouse.getposition?view=windowsdesktop-8.0
70 | if (Mouse.Capture(menu))
71 | {
72 | LastCursorPosition = Mouse.GetPosition(menu);
73 | Mouse.Capture(null);
74 | }
75 | #else
76 | NativeMethods.POINT lpPoint;
77 | if (NativeMethods.GetCursorPos(out lpPoint))
78 | {
79 | LastCursorPosition = new(lpPoint.X, lpPoint.Y);
80 | }
81 | #endif
82 | return LastCursorPosition;
83 | }
84 | }
85 |
86 | public static Rect FromPoint(Point pt)
87 | {
88 | foreach (Rect screen in Screens)
89 | {
90 | if (screen.Contains(pt))
91 | {
92 | return screen;
93 | }
94 | }
95 |
96 | // Use primary screen as fallback
97 | return Screens[0];
98 | }
99 |
100 | internal static void FetchScreens()
101 | {
102 | var backup = screens;
103 | screens = new();
104 | if (!NativeMethods.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumCallback, IntPtr.Zero))
105 | {
106 | screens = backup;
107 | }
108 | }
109 |
110 | private static bool MonitorEnumCallback(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeMethods.RECT lprcMonitor, IntPtr dwData)
111 | {
112 | try
113 | {
114 | screens!.Add(new()
115 | {
116 | X = lprcMonitor.left,
117 | Y = lprcMonitor.top,
118 | Width = lprcMonitor.right - lprcMonitor.left,
119 | Height = lprcMonitor.bottom - lprcMonitor.top,
120 | });
121 | }
122 | catch
123 | {
124 | // Catch everything as this callback runs within a native context
125 | }
126 |
127 | return true;
128 | }
129 |
130 | private class NativeMethods
131 | {
132 | public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
133 |
134 | ///
135 | /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors .
136 | ///
137 | [SupportedOSPlatform("windows")]
138 | [DllImport("user32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
139 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
140 | public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);
141 |
142 | ///
143 | /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos .
144 | ///
145 | [SupportedOSPlatform("windows")]
146 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
147 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
148 | public static extern bool GetCursorPos(out POINT lpPoint);
149 |
150 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
151 | internal struct RECT
152 | {
153 | public int left;
154 | public int top;
155 | public int right;
156 | public int bottom;
157 | }
158 |
159 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
160 | internal struct POINT
161 | {
162 | public int X;
163 | public int Y;
164 | }
165 | }
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/NativeDllImport/ShellWindowsApi.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.DllImports
6 | {
7 | using System;
8 | using System.Runtime.InteropServices;
9 | using System.Runtime.Versioning;
10 | using System.Text;
11 |
12 | ///
13 | /// wraps the methodcalls to native windows dll's.
14 | ///
15 | public static partial class NativeMethods
16 | {
17 | [SupportedOSPlatform("windows")]
18 | [DllImport("Shell32.dll", EntryPoint = "SHGetFileInfo", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
19 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
20 | internal static extern IntPtr Shell32SHGetFileInfo(
21 | string pszPath,
22 | uint dwFileAttributes,
23 | ref SHFILEINFO psfi,
24 | uint cbFileInfo,
25 | uint uFlags);
26 |
27 | // Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace.
28 | [SupportedOSPlatform("windows")]
29 | [DllImport("shell32.dll", EntryPoint = "SHGetDesktopFolder", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
30 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
31 | internal static extern int Shell32SHGetDesktopFolder(out IntPtr ppshf);
32 |
33 | [SupportedOSPlatform("windows")]
34 | [DllImport("shlwapi.dll", EntryPoint = "StrCmpLogicalW", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
35 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
36 | internal static extern int ShlwapiStrCmpLogicalW(string x, string y);
37 |
38 | // Takes a STRRET structure returned by IShellFolder::GetDisplayNameOf, converts it to a string, and places the result in a buffer.
39 | [SupportedOSPlatform("windows")]
40 | [DllImport("shlwapi.dll", EntryPoint = "StrRetToBuf", ExactSpelling = false, SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
41 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
42 | internal static extern int ShlwapiStrRetToBuf(IntPtr pstr, IntPtr pidl, StringBuilder pszBuf, int cchBuf);
43 |
44 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
45 | internal struct SHFILEINFO
46 | {
47 | public IntPtr hIcon;
48 | public int iIcon;
49 | public uint dwAttributes;
50 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
51 | public string szDisplayName;
52 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
53 | public string szTypeName;
54 |
55 | private const int NAMESIZE = 80;
56 | private const int MAX_PATH = 256;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/NativeDllImport/TaskBar.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.DllImports
6 | {
7 | using System;
8 | using System.Runtime.InteropServices;
9 | using System.Runtime.Versioning;
10 |
11 | ///
12 | /// wraps the methodcalls to native windows dll's.
13 | ///
14 | public static partial class NativeMethods
15 | {
16 | internal enum ABM : uint
17 | {
18 | New = 0x00000000,
19 | Remove = 0x00000001,
20 | QueryPos = 0x00000002,
21 | SetPos = 0x00000003,
22 | GetState = 0x00000004,
23 | GetTaskbarPos = 0x00000005,
24 | Activate = 0x00000006,
25 | GetAutoHideBar = 0x00000007,
26 | SetAutoHideBar = 0x00000008,
27 | WindowPosChanged = 0x00000009,
28 | SetState = 0x0000000A,
29 | }
30 |
31 | internal enum ABE : uint
32 | {
33 | Left = 0,
34 | Top = 1,
35 | Right = 2,
36 | Bottom = 3,
37 | }
38 |
39 | [SupportedOSPlatform("windows")]
40 | [DllImport("shell32.dll", EntryPoint = "SHAppBarMessage", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
41 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
42 | internal static extern IntPtr Shell32SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
43 |
44 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
45 | internal struct APPBARDATA
46 | {
47 | public uint cbSize;
48 | public IntPtr hWnd;
49 | public uint uCallbackMessage;
50 | public ABE uEdge;
51 | public RECT rc;
52 | public int lParam;
53 | }
54 |
55 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
56 | internal struct RECT
57 | {
58 | public int left;
59 | public int top;
60 | public int right;
61 | public int bottom;
62 | }
63 |
64 | internal static class ABS
65 | {
66 | public const int Autohide = 0x0000001;
67 | public const int AlwaysOnTop = 0x0000002;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/NativeDllImport/Themes.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2023 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.DllImports
8 | {
9 | using System.Runtime.InteropServices;
10 | using System.Runtime.Versioning;
11 |
12 | ///
13 | /// See: https://gist.github.com/rounk-ctrl/b04e5622e30e0d62956870d5c22b7017
14 | /// See: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/e36eb4c0-4370-4933-943d-b6fe22677e6c/dark-mode-apis?forum=windowssdk
15 | /// Wraps the method calls to native Windows DLLs.
16 | ///
17 | public static partial class NativeMethods
18 | {
19 | internal enum PreferredAppMode : int
20 | {
21 | Default = 0,
22 | AllowDark = 1,
23 | ForceDark = 2,
24 | ForceLight = 3,
25 | }
26 |
27 | ///
28 | /// No official documentation.
29 | /// Seems to set mode that the UI shall use for rendering UI elements.
30 | ///
31 | /// Desired mode.
32 | /// Undocumented.
33 | [SupportedOSPlatform("windows")]
34 | [DllImport("uxtheme.dll", EntryPoint = "#135", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
35 | internal static extern int SetPreferredAppMode(PreferredAppMode preferredAppMode);
36 |
37 | ///
38 | /// No official documentation.
39 | /// Seems to switch UI mode which was set by SetPreferredAppMode.
40 | ///
41 | [SupportedOSPlatform("windows")]
42 | [DllImport("uxtheme.dll", EntryPoint = "#136", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
43 | internal static extern void FlushMenuThemes();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/NativeDllImport/Window.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2022-2023 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.DllImports
8 | {
9 | using System;
10 | using System.Runtime.InteropServices;
11 | using System.Runtime.Versioning;
12 | using System.Windows;
13 | using System.Windows.Interop;
14 |
15 | ///
16 | /// wraps the methodcalls to native windows dll's.
17 | ///
18 | public static partial class NativeMethods
19 | {
20 | private const int WS_EX_TOOLWINDOW = 0x00000080;
21 | private const int GWL_EXSTYLE = -20;
22 |
23 | internal static void HideFromAltTab(Window window)
24 | {
25 | WindowInteropHelper wndHelper = new WindowInteropHelper(window);
26 |
27 | if (Environment.Is64BitProcess)
28 | {
29 | long exStyle = (long)GetWindowLongPtr(wndHelper.Handle, GWL_EXSTYLE);
30 | exStyle |= WS_EX_TOOLWINDOW; // do not show when user presses alt + tab
31 | SetWindowLongPtr(wndHelper.Handle, GWL_EXSTYLE, (IntPtr)exStyle);
32 | }
33 | else
34 | {
35 | int exStyle = (int)GetWindowLong(wndHelper.Handle, GWL_EXSTYLE);
36 | exStyle |= WS_EX_TOOLWINDOW; // do not show when user presses alt + tab
37 | SetWindowLong(wndHelper.Handle, GWL_EXSTYLE, (IntPtr)exStyle);
38 | }
39 | }
40 |
41 | ///
42 | /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-findwindoww .
43 | ///
44 | /// The class name or a class atom.
45 | /// The window name.
46 | /// Handle to the window or NULL on failure.
47 | [SupportedOSPlatform("windows")]
48 | [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
49 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
50 | internal static extern IntPtr User32FindWindow(string? lpClassName, string? lpWindowName);
51 |
52 | ///
53 | /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongw .
54 | ///
55 | [SupportedOSPlatform("windows")]
56 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
57 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
58 | private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
59 |
60 | ///
61 | /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw .
62 | ///
63 | [SupportedOSPlatform("windows")]
64 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
65 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
66 | private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
67 |
68 | ///
69 | /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongw .
70 | ///
71 | [SupportedOSPlatform("windows")]
72 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
73 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
74 | private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
75 |
76 | ///
77 | /// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptrw .
78 | ///
79 | [SupportedOSPlatform("windows")]
80 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
81 | [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
82 | private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Packaging/Images/BadgeLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/BadgeLogo.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/BadgeLogo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/BadgeLogo.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/BadgeLogo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/BadgeLogo.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/BadgeLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/BadgeLogo.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/BadgeLogo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/BadgeLogo.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Images/LargeTile.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/LargeTile.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/LargeTile.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/LargeTile.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/LargeTile.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/LargeTile.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/LargeTile.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/LargeTile.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/LargeTile.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/LargeTile.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Images/SmallTile.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SmallTile.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/SmallTile.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SmallTile.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/SmallTile.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SmallTile.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/SmallTile.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SmallTile.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/SmallTile.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SmallTile.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Images/SplashScreen.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SplashScreen.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/SplashScreen.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SplashScreen.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/SplashScreen.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SplashScreen.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/SplashScreen.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SplashScreen.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/SplashScreen.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SplashScreen.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Images/SplashScreen.scale-400.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/SplashScreen.scale-400.xcf
--------------------------------------------------------------------------------
/Packaging/Images/Square150x150Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square150x150Logo.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/Square150x150Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square150x150Logo.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/Square150x150Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square150x150Logo.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/Square150x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square150x150Logo.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/Square150x150Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square150x150Logo.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-16.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-24.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-256.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-32.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.altform-unplated_targetsize-48.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.targetsize-16.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.targetsize-24.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.targetsize-256.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.targetsize-32.png
--------------------------------------------------------------------------------
/Packaging/Images/Square44x44Logo.targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Square44x44Logo.targetsize-48.png
--------------------------------------------------------------------------------
/Packaging/Images/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/StoreLogo.png
--------------------------------------------------------------------------------
/Packaging/Images/StoreLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/StoreLogo.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/StoreLogo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/StoreLogo.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/StoreLogo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/StoreLogo.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/StoreLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/StoreLogo.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/StoreLogo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/StoreLogo.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Images/Wide310x150Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Wide310x150Logo.scale-100.png
--------------------------------------------------------------------------------
/Packaging/Images/Wide310x150Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Wide310x150Logo.scale-125.png
--------------------------------------------------------------------------------
/Packaging/Images/Wide310x150Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Wide310x150Logo.scale-150.png
--------------------------------------------------------------------------------
/Packaging/Images/Wide310x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Wide310x150Logo.scale-200.png
--------------------------------------------------------------------------------
/Packaging/Images/Wide310x150Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Packaging/Images/Wide310x150Logo.scale-400.png
--------------------------------------------------------------------------------
/Packaging/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
14 |
15 |
16 | SystemTrayMenu
17 | SystemTrayMenu
18 | Images\StoreLogo.png
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | using System.Reflection;
6 | using System.Resources;
7 | using System.Runtime.InteropServices;
8 |
9 | // General Information about an assembly is controlled through the following
10 | // set of attributes. Change these attribute values to modify the information
11 | // associated with an assembly.
12 | [assembly: AssemblyTitle("SystemTrayMenu")]
13 | [assembly: AssemblyDescription("browse and open your files easily")]
14 | [assembly: AssemblyConfiguration("")]
15 | [assembly: AssemblyCompany("TAMAHO")]
16 | [assembly: AssemblyProduct("TAMAHO SystemTrayMenu")]
17 | [assembly: AssemblyCopyright("Copyright © 2022, TAMAHO SystemTrayMenu")]
18 | [assembly: AssemblyTrademark("TAMAHO")]
19 | [assembly: AssemblyCulture("")]
20 |
21 | // Setting ComVisible to false makes the types in this assembly not visible
22 | // to COM components. If you need to access a type in this assembly from
23 | // COM, set the ComVisible attribute to true on that type.
24 | [assembly: ComVisible(false)]
25 |
26 | // The following GUID is for the ID of the typelib if this project is exposed to COM
27 | [assembly: Guid("116c8741-a9b0-4560-8e82-7cf412894340")]
28 |
29 | // https://stackoverflow.com/questions/4068360/c-sharp-warning-mark-assemblies-with-neutralresourceslanguageattribute
30 | [assembly: NeutralResourcesLanguage("en")]
31 |
32 | // Version information for an assembly consists of the following four values:
33 | //
34 | // Major Version
35 | // Minor Version
36 | // Build Number
37 | // Revision
38 | //
39 | // You can specify all the values or you can default the Build and Revision Numbers
40 | // by using the '*' as shown below:
41 | // [assembly: AssemblyVersion("1.0.*")]
42 | [assembly: AssemblyVersion("2.0.0.0")]
43 | [assembly: AssemblyFileVersion("2.0.0.0")]
44 |
--------------------------------------------------------------------------------
/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Resources/HowToOpenSettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Resources/HowToOpenSettings.png
--------------------------------------------------------------------------------
/Resources/LinkArrow.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Resources/LinkArrow.ico
--------------------------------------------------------------------------------
/Resources/Loading.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Resources/Loading.ico
--------------------------------------------------------------------------------
/Resources/NotFound.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Resources/NotFound.ico
--------------------------------------------------------------------------------
/Resources/SystemTrayMenu.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Resources/SystemTrayMenu.ico
--------------------------------------------------------------------------------
/Resources/SystemTrayMenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/Resources/SystemTrayMenu.png
--------------------------------------------------------------------------------
/Resources/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | PerMonitor
8 | true
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Resources/ic_fluent_arrow_sync_24_regular.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Resources/ic_fluent_folder_arrow_right_48_regular.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Resources/ic_fluent_pin_48_filled.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Resources/ic_fluent_pin_48_regular.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Resources/ic_fluent_search_48_regular.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Resources/ic_fluent_settings_28_regular.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/SystemTrayMenu.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | net7.0-windows10.0.22000.0
7 |
8 | win-x64
9 | linux-x64
10 | True
11 | enable
12 | true
13 | x64
14 | x64;x86;AnyCPU
15 | Debug;Release;ReleasePackage
16 | WinExe
17 | bin\$(Platform)\$(Configuration)\
18 | publish\
19 | true
20 | Disk
21 | false
22 | Foreground
23 | 7
24 | Days
25 | false
26 | false
27 | true
28 | 0
29 | 1.0.0.%2a
30 | true
31 | false
32 | taskkill /f /fi "pid gt 0" /im SystemTrayMenu.exe >nul
33 | EXIT 0
34 | https://github.com/Hofknecht/SystemTrayMenu
35 | icon.png
36 | hofknecht.eu/systemtraymenu/
37 | LICENSE
38 |
39 | SystemTrayMenu
40 | 10.0.22000.0
41 | README.md
42 | True
43 | True
44 | SystemTrayMenu.Program
45 | Resources\app.manifest
46 | Resources\SystemTrayMenu.ico
47 | MinimumRecommendedRules.ruleset
48 | 1701;1702;WFAC010;MSB3061
49 |
50 |
51 |
52 | none
53 | true
54 | True
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | True
70 | True
71 | Settings.settings
72 |
73 |
74 | True
75 |
76 |
77 |
78 | Designer
79 |
80 |
81 | SettingsSingleFileGenerator
82 | Settings.Designer.cs
83 |
84 |
85 | True
86 | \
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | all
96 | runtime; build; native; contentfiles; analyzers; buildtransitive
97 |
98 |
99 |
100 | all
101 | runtime; build; native; contentfiles; analyzers; buildtransitive
102 |
103 |
104 | False
105 | ThirdParty\Clearcove.Logging.dll
106 |
107 |
108 |
109 |
110 |
111 | {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}
112 | 1
113 | 0
114 | 0
115 | tlbimp
116 | False
117 | True
118 |
119 |
120 | {50A7E9B0-70EF-11D1-B75A-00A0C90564FE}
121 | 1
122 | 0
123 | 0
124 | tlbimp
125 | False
126 | True
127 |
128 |
129 |
--------------------------------------------------------------------------------
/SystemTrayMenu.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32112.339
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemTrayMenu", "SystemTrayMenu.csproj", "{F317AF2E-9704-4A2A-BDAE-B4662ED9483B}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SystemTrayMenuDocuments", "SystemTrayMenuDocuments", "{FA49BF01-9C05-4750-85DF-319F6ECCE506}"
9 | ProjectSection(SolutionItems) = preProject
10 | CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
11 | CONTRIBUTING.md = CONTRIBUTING.md
12 | LICENSE = LICENSE
13 | mails Antivirus.txt = mails Antivirus.txt
14 | README.md = README.md
15 | ReadMe.txt = ReadMe.txt
16 | EndProjectSection
17 | EndProject
18 | Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Packaging", "Packaging\Packaging.wapproj", "{01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}"
19 | EndProject
20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D34D9392-6592-4F44-B8D4-1781502FE7E6}"
21 | ProjectSection(SolutionItems) = preProject
22 | .editorconfig = .editorconfig
23 | EndProjectSection
24 | EndProject
25 | Global
26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
27 | Debug|Any CPU = Debug|Any CPU
28 | Debug|x64 = Debug|x64
29 | Debug|x86 = Debug|x86
30 | Release|Any CPU = Release|Any CPU
31 | Release|x64 = Release|x64
32 | Release|x86 = Release|x86
33 | ReleasePackage|Any CPU = ReleasePackage|Any CPU
34 | ReleasePackage|x64 = ReleasePackage|x64
35 | ReleasePackage|x86 = ReleasePackage|x86
36 | EndGlobalSection
37 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
38 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Debug|x64.ActiveCfg = Debug|x64
41 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Debug|x64.Build.0 = Debug|x64
42 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Debug|x86.ActiveCfg = Debug|x86
43 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Debug|x86.Build.0 = Debug|x86
44 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Release|x64.ActiveCfg = Release|x64
47 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Release|x64.Build.0 = Release|x64
48 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Release|x86.ActiveCfg = Release|x86
49 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.Release|x86.Build.0 = Release|x86
50 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.ReleasePackage|Any CPU.ActiveCfg = ReleasePackage|Any CPU
51 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.ReleasePackage|Any CPU.Build.0 = ReleasePackage|Any CPU
52 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.ReleasePackage|x64.ActiveCfg = ReleasePackage|x64
53 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.ReleasePackage|x64.Build.0 = ReleasePackage|x64
54 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.ReleasePackage|x86.ActiveCfg = ReleasePackage|x86
55 | {F317AF2E-9704-4A2A-BDAE-B4662ED9483B}.ReleasePackage|x86.Build.0 = ReleasePackage|x86
56 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.Debug|x64.ActiveCfg = Debug|x64
58 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.Debug|x86.ActiveCfg = Debug|x86
59 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
60 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.Release|x64.ActiveCfg = Release|x64
61 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.Release|x86.ActiveCfg = Release|x86
62 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|Any CPU.ActiveCfg = ReleasePackage|Any CPU
63 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|Any CPU.Build.0 = ReleasePackage|Any CPU
64 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|Any CPU.Deploy.0 = ReleasePackage|Any CPU
65 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|x64.ActiveCfg = ReleasePackage|x64
66 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|x64.Build.0 = ReleasePackage|x64
67 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|x64.Deploy.0 = ReleasePackage|x64
68 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|x86.ActiveCfg = ReleasePackage|x86
69 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|x86.Build.0 = ReleasePackage|x86
70 | {01D77F37-786A-4DC4-A1AD-BC1EEC54EAE3}.ReleasePackage|x86.Deploy.0 = ReleasePackage|x86
71 | EndGlobalSection
72 | GlobalSection(SolutionProperties) = preSolution
73 | HideSolutionNode = FALSE
74 | EndGlobalSection
75 | GlobalSection(ExtensibilityGlobals) = postSolution
76 | SolutionGuid = {ECA870FE-709A-4EC0-9F28-206977283A13}
77 | EndGlobalSection
78 | EndGlobal
79 |
--------------------------------------------------------------------------------
/ThirdParty/Clearcove.Logging.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hofknecht/SystemTrayMenu/89a6147251162325ffdb61f6286e8da4ebe65c6b/ThirdParty/Clearcove.Logging.dll
--------------------------------------------------------------------------------
/UserInterface/AboutBox.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/UserInterface/AppNotifyIcon.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.UserInterface
6 | {
7 | using System;
8 | using System.Drawing;
9 | using System.Windows.Threading;
10 | using H.NotifyIcon.Core;
11 |
12 | internal class AppNotifyIcon : IDisposable
13 | {
14 | private readonly Dispatcher dispatchter = Dispatcher.CurrentDispatcher;
15 | private readonly TrayIconWithContextMenu notifyIcon = new ();
16 | private Icon? loadingIcon;
17 |
18 | public AppNotifyIcon()
19 | {
20 | notifyIcon.ToolTip = "SystemTrayMenu";
21 | notifyIcon.Icon = Config.GetAppIcon().Handle;
22 | notifyIcon.ContextMenu = new AppContextMenu().Create();
23 | notifyIcon.MessageWindow.MouseEventReceived += (sender, e) =>
24 | {
25 | if (e.MouseEvent == MouseEvent.IconLeftMouseUp ||
26 | e.MouseEvent == MouseEvent.IconLeftDoubleClick)
27 | {
28 | dispatchter.Invoke(() => Click?.Invoke());
29 | }
30 | };
31 | notifyIcon.Create();
32 | }
33 |
34 | public event Action? Click;
35 |
36 | public void Dispose()
37 | {
38 | notifyIcon.Dispose();
39 | loadingIcon?.Dispose();
40 | }
41 |
42 | public void LoadingStart()
43 | {
44 | loadingIcon ??= App.LoadIconFromResource("Resources/Loading.ico");
45 | notifyIcon.UpdateIcon(loadingIcon.Handle);
46 | }
47 |
48 | public void LoadingStop()
49 | {
50 | notifyIcon.UpdateIcon(Config.GetAppIcon().Handle);
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/UserInterface/ColorPickerWindow.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/UserInterface/ColorPickerWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2023-2023 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.UserInterface
8 | {
9 | using System;
10 | using System.Windows;
11 | using System.Windows.Media;
12 |
13 | ///
14 | /// Logic of ColorPickerWindow.xaml .
15 | ///
16 | public partial class ColorPickerWindow : Window
17 | {
18 | internal ColorPickerWindow(string description, Color initialColor)
19 | {
20 | InitializeComponent();
21 |
22 | if (Config.IsDarkMode())
23 | {
24 | ResourceDictionary resDict = new ();
25 | resDict.Source = new Uri("pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml", UriKind.RelativeOrAbsolute);
26 | picker.Style = (Style)resDict["DefaultColorPickerStyle"];
27 | }
28 |
29 | Loaded += (_, _) =>
30 | {
31 | MinWidth = ActualWidth;
32 | MinHeight = ActualHeight;
33 |
34 | // Issue: Placement of picker child elements incorrect.
35 | // Beyond initial layout updates it requires to have a fixed size set.
36 | // But this will force us to manually update on resize events.
37 | // Workaround: Remove the fixed values and witch back to automatic size calculation afterwards.
38 | SizeChanged += UnsetSize;
39 | void UnsetSize(object sender, RoutedEventArgs e)
40 | {
41 | SizeChanged -= UnsetSize;
42 | picker.Width = double.NaN;
43 | picker.Height = double.NaN;
44 | }
45 | };
46 |
47 | picker.SelectedColor = picker.SecondaryColor = initialColor;
48 | lblDescription.Content = description;
49 | }
50 |
51 | public Color SelectedColor => picker.SelectedColor;
52 |
53 | private void ButtonOk_Click(object sender, RoutedEventArgs e)
54 | {
55 | DialogResult = true;
56 | Close();
57 | }
58 |
59 | private void ButtonCancel_Click(object sender, RoutedEventArgs e) => Close();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/UserInterface/ColorSelector.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/UserInterface/ColorSelector.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.UserInterface
6 | {
7 | using System;
8 | using System.Windows.Controls;
9 | using System.Windows.Input;
10 | using System.Windows.Media;
11 |
12 | ///
13 | /// Logic of ColorSelector .
14 | ///
15 | public partial class ColorSelector : UserControl
16 | {
17 | public ColorSelector()
18 | {
19 | InitializeComponent();
20 | label.Content = string.Empty;
21 | }
22 |
23 | public event Action? ColorChanged;
24 |
25 | public string Text
26 | {
27 | get
28 | {
29 | try
30 | {
31 | Color color = (Color)ColorConverter.ConvertFromString(txtbox.Text.Trim());
32 | return txtbox.Text.Trim();
33 | }
34 | catch
35 | {
36 | return Colors.White.ToString();
37 | }
38 | }
39 |
40 | set
41 | {
42 | txtbox.Text = value;
43 | }
44 | }
45 |
46 | public string Description
47 | {
48 | get => (string)label.Content;
49 | set => label.Content = value ?? string.Empty;
50 | }
51 |
52 | private void Txtbox_TextChanged(object sender, TextChangedEventArgs e)
53 | {
54 | try
55 | {
56 | Color color = (Color)ColorConverter.ConvertFromString(txtbox.Text.Trim());
57 | pane.Background = new SolidColorBrush(color);
58 | }
59 | catch
60 | {
61 | return;
62 | }
63 |
64 | ColorChanged?.Invoke(this);
65 | }
66 |
67 | private void Shape_MouseDown(object sender, MouseButtonEventArgs e)
68 | {
69 | if (e.LeftButton == MouseButtonState.Pressed)
70 | {
71 | ColorPickerWindow dialog = new(Description, Colors.LightYellow);
72 | if (dialog.ShowDialog() ?? false)
73 | {
74 | Text = dialog.SelectedColor.ToString();
75 | }
76 |
77 | e.Handled = true;
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/UserInterface/FolderBrowseDialog/FolderDialog.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.UserInterface.FolderBrowseDialog
6 | {
7 | using System;
8 | using System.Runtime.InteropServices;
9 | using System.Runtime.Versioning;
10 | using System.Windows;
11 | using System.Windows.Interop;
12 | using SystemTrayMenu.DllImports;
13 | using SystemTrayMenu.Utilities;
14 |
15 | public class FolderDialog : IFolderDialog, IDisposable
16 | {
17 | private bool isDisposed;
18 |
19 | ~FolderDialog() // the finalizer
20 | {
21 | Dispose(false);
22 | }
23 |
24 | ///
25 | /// Gets or sets /sets folder in which dialog will be open.
26 | ///
27 | public string? InitialFolder { get; set; }
28 |
29 | ///
30 | /// Gets or sets /sets directory in which dialog will be open
31 | /// if there is no recent directory available.
32 | ///
33 | public string? DefaultFolder { get; set; }
34 |
35 | ///
36 | /// Gets or sets selected folder.
37 | ///
38 | public string? Folder { get; set; }
39 |
40 | ///
41 | /// Shows the file dialog and requests user interaction.
42 | ///
43 | /// The window the dialog is assigned to.
44 | /// True is returned on successful user interaction and when not cancelled by the user otherwise false is returned.
45 | [SupportedOSPlatform("windows")]
46 | public bool ShowDialog(Window? owner)
47 | {
48 | NativeMethods.IFileDialog frm = (NativeMethods.IFileDialog)new NativeMethods.FileOpenDialogRCW();
49 | frm.GetOptions(out uint options);
50 | options |= NativeMethods.FOS_PICKFOLDERS |
51 | NativeMethods.FOS_FORCEFILESYSTEM |
52 | NativeMethods.FOS_NOVALIDATE |
53 | NativeMethods.FOS_NOTESTFILECREATE |
54 | NativeMethods.FOS_DONTADDTORECENT;
55 | frm.SetOptions(options);
56 | if (InitialFolder != null)
57 | {
58 | Guid riid = new("43826D1E-E718-42EE-BC55-A1E261C37BFE"); // IShellItem
59 | if (NativeMethods.SHCreateItemFromParsingName(
60 | InitialFolder,
61 | IntPtr.Zero,
62 | ref riid,
63 | out NativeMethods.IShellItem directoryShellItem) == NativeMethods.S_OK)
64 | {
65 | frm.SetFolder(directoryShellItem);
66 | }
67 | }
68 |
69 | if (DefaultFolder != null)
70 | {
71 | Guid riid = new("43826D1E-E718-42EE-BC55-A1E261C37BFE"); // IShellItem
72 | if (NativeMethods.SHCreateItemFromParsingName(
73 | DefaultFolder,
74 | IntPtr.Zero,
75 | ref riid,
76 | out NativeMethods.IShellItem directoryShellItem) == NativeMethods.S_OK)
77 | {
78 | frm.SetDefaultFolder(directoryShellItem);
79 | }
80 | }
81 |
82 | if (frm.Show(owner == null ? IntPtr.Zero : new WindowInteropHelper(owner).Handle) == NativeMethods.S_OK)
83 | {
84 | try
85 | {
86 | if (frm.GetResult(out NativeMethods.IShellItem shellItem) == NativeMethods.S_OK)
87 | {
88 | if (shellItem.GetDisplayName(
89 | NativeMethods.SIGDN_FILESYSPATH,
90 | out IntPtr pszString) == NativeMethods.S_OK)
91 | {
92 | if (pszString != IntPtr.Zero)
93 | {
94 | try
95 | {
96 | Folder = Marshal.PtrToStringAuto(pszString);
97 | return true;
98 | }
99 | finally
100 | {
101 | Marshal.FreeCoTaskMem(pszString);
102 | }
103 | }
104 | }
105 | }
106 | }
107 | catch (Exception ex)
108 | {
109 | Log.Warn("Folder Dialog failed", ex);
110 | }
111 | }
112 |
113 | return false;
114 | }
115 |
116 | public void Dispose()
117 | {
118 | Dispose(true);
119 | GC.SuppressFinalize(this);
120 | }
121 |
122 | protected virtual void Dispose(bool disposing)
123 | {
124 | if (!isDisposed)
125 | {
126 | }
127 |
128 | isDisposed = true;
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/UserInterface/FolderBrowseDialog/IFolderDialog.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.UserInterface.FolderBrowseDialog
6 | {
7 | using System.Windows;
8 |
9 | public interface IFolderDialog
10 | {
11 | string? InitialFolder { get; set; }
12 |
13 | string? DefaultFolder { get; set; }
14 |
15 | string? Folder { get; set; }
16 |
17 | bool ShowDialog(Window owner);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/UserInterface/HowToOpenSettingsWindow.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/UserInterface/HowToOpenSettingsWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2023-2023 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.UserInterface
8 | {
9 | using System.Windows;
10 | using SystemTrayMenu.Utilities;
11 |
12 | ///
13 | /// Logic of HowToOpenSettings window.
14 | ///
15 | public partial class HowToOpenSettingsWindow : Window
16 | {
17 | public HowToOpenSettingsWindow()
18 | {
19 | InitializeComponent();
20 |
21 | // TODO: Find a way to escape ' within inline single quotes markup string in XAML
22 | checkBoxDontShowThisHintAgain.Content = Translator.GetText("Don't show this hint again.");
23 | }
24 |
25 | internal bool DoNotShowAgain => checkBoxDontShowThisHintAgain.IsChecked ?? false;
26 |
27 | private void ButtonOk_Click(object sender, RoutedEventArgs e)
28 | {
29 | Close();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/UserInterface/NumericUpDown.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
43 |
46 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/UserInterface/NumericUpDown.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 |
5 | namespace SystemTrayMenu.UserInterface
6 | {
7 | using System;
8 | using System.Diagnostics;
9 | using System.Text.RegularExpressions;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | using System.Windows.Input;
13 |
14 | ///
15 | /// Logic of NumericUpDown .
16 | ///
17 | public partial class NumericUpDown : UserControl
18 | {
19 | // TODO: Not catched yet when something like "0--8" is entered
20 | private static readonly Regex RegexNonNumeric = new Regex("[^0-9-]+");
21 | private string lastTextOK;
22 | private bool withinChanged;
23 |
24 | public NumericUpDown()
25 | {
26 | InitializeComponent();
27 | lastTextOK = txtbox.Text;
28 | }
29 |
30 | public int Value
31 | {
32 | get => int.Parse(txtbox.Text);
33 | set
34 | {
35 | txtbox.Text = value.ToString();
36 | }
37 | }
38 |
39 | public int Minimum { get; set; } = int.MinValue;
40 |
41 | public int Maximum { get; set; } = int.MaxValue;
42 |
43 | public int Increment { get; set; } = 1;
44 |
45 | private static bool IsTextAllowed(string text) => !RegexNonNumeric.IsMatch(text);
46 |
47 | private void Txtbox_PreviewTextInput(object sender, TextCompositionEventArgs e)
48 | {
49 | e.Handled = !IsTextAllowed(e.Text);
50 | }
51 |
52 | private void Txtbox_Pasting(object sender, DataObjectPastingEventArgs e)
53 | {
54 | if (e.DataObject.GetDataPresent(typeof(string)))
55 | {
56 | if (!IsTextAllowed((string)e.DataObject.GetData(typeof(string))))
57 | {
58 | e.CancelCommand();
59 | }
60 | }
61 | else
62 | {
63 | e.CancelCommand();
64 | }
65 | }
66 |
67 | private void Txtbox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
68 | {
69 | if (e.Delta > 0)
70 | {
71 | Value = Math.Min(Value + Increment, Maximum);
72 | }
73 | else
74 | {
75 | Value = Math.Max(Value - Increment, Minimum);
76 | }
77 |
78 | e.Handled = true;
79 | }
80 |
81 | private void Txtbox_TextChanged(object sender, TextChangedEventArgs e)
82 | {
83 | if (int.TryParse(txtbox.Text, out int result))
84 | {
85 | if (result < Minimum)
86 | {
87 | lastTextOK = Minimum.ToString();
88 | }
89 | else if (Maximum < result)
90 | {
91 | lastTextOK = Maximum.ToString();
92 | }
93 | else
94 | {
95 | lastTextOK = txtbox.Text;
96 | return;
97 | }
98 | }
99 |
100 | if (!withinChanged)
101 | {
102 | withinChanged = true;
103 | txtbox.Text = lastTextOK;
104 | withinChanged = false;
105 | }
106 | }
107 |
108 | private void ButtonUp_Click(object sender, RoutedEventArgs e) => Value = Math.Min(Value + Increment, Maximum);
109 |
110 | private void ButtonDown_Click(object sender, RoutedEventArgs e) => Value = Math.Max(Value - Increment, Minimum);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/UserInterface/TaskbarLogo.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/UserInterface/TaskbarLogo.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2022-2023 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.UserInterface
8 | {
9 | using System;
10 | using System.Reflection;
11 | using System.Windows;
12 |
13 | ///
14 | /// Logic of Taskbar window.
15 | ///
16 | public partial class TaskbarLogo : Window
17 | {
18 | public TaskbarLogo()
19 | {
20 | InitializeComponent();
21 |
22 | Assembly myassembly = Assembly.GetExecutingAssembly();
23 | string myname = myassembly.GetName().Name ?? string.Empty;
24 | Title = myname;
25 |
26 | Closed += (_, _) => Application.Current.Shutdown();
27 |
28 | // Do final initialization after rendering has been finished for the first time.
29 | // This ensures all icons and images are properly loaded and renderd (e.g. thumbnail image of alt tab menu).
30 | ContentRendered += LateInitialize;
31 | }
32 |
33 | private void LateInitialize(object? sender, EventArgs e)
34 | {
35 | // Do this only once
36 | ContentRendered -= LateInitialize;
37 |
38 | // Move the window out of screen, just for safety
39 | Top += SystemParameters.VirtualScreenHeight;
40 |
41 | // There is nothing to see, so no need to show this window.
42 | // Therefore it shall always be in minimized state.
43 | // Further, we then can rely on every activating event.
44 | WindowState = WindowState.Minimized;
45 | StateChanged += (_, _) => WindowState = WindowState.Minimized;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/UserInterface/UpdateWindow.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/UserInterface/UpdateWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2022-2023 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.UserInterface
8 | {
9 | using System.Windows;
10 | using SystemTrayMenu.Helpers.Updater;
11 |
12 | ///
13 | /// Logic of Update window.
14 | ///
15 | public partial class UpdateWindow : Window
16 | {
17 | public UpdateWindow()
18 | {
19 | InitializeComponent();
20 |
21 | label.Content = ((string)label.Content) + " " + GitHubUpdate.LatestVersionName;
22 | }
23 |
24 | private void ButtonGoToDownloadPage_Click(object sender, RoutedEventArgs e)
25 | {
26 | GitHubUpdate.WebOpenLatestRelease();
27 | Close();
28 | }
29 |
30 | private void ButtonOk_Click(object sender, RoutedEventArgs e)
31 | {
32 | Close();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Utilities/ActionCommand.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) PlaceholderCompany. All rights reserved.
3 | //
4 | //
5 | // Copyright (c) 2022-2022 Peter Kirmeier
6 |
7 | namespace SystemTrayMenu.Utilities
8 | {
9 | using System;
10 | using System.Windows.Input;
11 |
12 | internal class ActionCommand : ICommand
13 | {
14 | private readonly Action