├── .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 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Resources/ic_fluent_folder_arrow_right_48_regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Resources/ic_fluent_pin_48_filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Resources/ic_fluent_pin_48_regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Resources/ic_fluent_search_48_regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Resources/ic_fluent_settings_28_regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 19 | 20 | 21 |