mirror of
				https://github.com/chylex/TweetDuck.git
				synced 2025-10-31 18:17:15 +01:00 
			
		
		
		
	Compare commits
	
		
			465 Commits
		
	
	
		
			1.14.1
			...
			1c1aa5ea44
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1c1aa5ea44 | |||
| da54af221c | |||
| 6c8d518e0d | |||
| 697f4f1569 | |||
| 15d4ec3228 | |||
| c303346bc3 | |||
| b9af966849 | |||
| 0a7459b72e | |||
| 9953f06ab1 | |||
| 0c8159aa79 | |||
| c785a7ed8c | |||
| b1328e5b1f | |||
| cb94f0c81e | |||
| 8de2989f12 | |||
| 1cf7d13873 | |||
| 35c2ee3673 | |||
| a1b4c31450 | |||
| ea95e5cbac | |||
| 2927097e8e | |||
| b5bffdb95b | |||
| bee894bfbb | |||
| 96d2e7cc7c | |||
| b58c8f65fe | |||
| 2c69289785 | |||
| dc0fc06673 | |||
| 3114b489b6 | |||
| 8e5934bd84 | |||
| a2129b957e | |||
| 61cd632df6 | |||
| 712bcd5a6f | |||
| dd47201d7b | |||
| 2af864f337 | |||
| acafbc3706 | |||
| b815ae4b11 | |||
| 45a3a7499f | |||
| 09fac63ffc | |||
| dd6776fef4 | |||
| cd02a03e8a | |||
| 933e0e54df | |||
| c4aa62fc3a | |||
| ad30021d6d | |||
| 7c8b43adfe | |||
| 3aace0b399 | |||
| 0a9c84feec | |||
| d5ae698855 | |||
| 26e6a09d5a | |||
| 57fcff3824 | |||
| 2a7aec199f | |||
| 1b01c38fda | |||
| c9fd4634ab | |||
| 51d2ec92ca | |||
| 12ec8baf5c | |||
| 6040337bb4 | |||
| 1ced72388b | |||
| 4751a948e7 | |||
| 3939c2263a | |||
| b0ba4595ae | |||
| 38b1057a4c | |||
| af5d785ff2 | |||
| 655d334714 | |||
| eee72959e6 | |||
| 89b8977f7d | |||
| 9ede2e1ccc | |||
| 03d1bc0f4c | |||
| cde9f66111 | |||
| 8149ed50e1 | |||
| 24f5075116 | |||
| 2a636245b4 | |||
| 3f4844f6f6 | |||
| 29308de3ee | |||
| 0d3d744d94 | |||
| d38e525fed | |||
| e2ac38ed0b | |||
| fa534f9eb3 | |||
| ec7827df24 | |||
| b915488651 | |||
| bf9a0226be | |||
| 68582f6973 | |||
| 03f3d4d450 | |||
| 115428ec50 | |||
| 5f60852fbb | |||
| a7a5723c4b | |||
| 17e42df42d | |||
| 7e692460d8 | |||
| f41a5946e4 | |||
| 29fee155d7 | |||
| 32728fc20a | |||
| 394cb80022 | |||
| 21e49505d1 | |||
| e0025e02d1 | |||
| 4a9590c1d9 | |||
| 7de0c50387 | |||
| ccb87351c1 | |||
| 273e7266eb | |||
| 427975e5ce | |||
| adb304b6a2 | |||
| 3a89a28f8b | |||
| cff93dcc97 | |||
| dfde38ea3b | |||
| a8e7f065cf | |||
| 5ebfc67e48 | |||
| 57b03baad9 | |||
| c91f1d0e5e | |||
| 6a421292b3 | |||
| ceae748503 | |||
| 008de87e55 | |||
| e7479ef9e3 | |||
| 13f8f12ac8 | |||
| fd634379d7 | |||
| e854315a81 | |||
| 86136d7692 | |||
| db251bfdfd | |||
| 9d39f26d54 | |||
| 9cd813c02c | |||
| 8e1f87e062 | |||
| dda954285c | |||
| ed4f7b6b72 | |||
| 7239dcf4d2 | |||
| 901cca268e | |||
| b909341988 | |||
| 3e607ae0fe | |||
| 587060f73c | |||
| 3ea6f6ac18 | |||
| c162761464 | |||
| 994f9635ef | |||
| ab8752845d | |||
| a94ee2fe4b | |||
| 763c999b09 | |||
| b18cd2658c | |||
| d9782f554f | |||
| f1d9663709 | |||
| bcf77052a5 | |||
| 85d15b32e9 | |||
| 3d3b695c85 | |||
| 88b7cac298 | |||
| c7efa631e1 | |||
| aeb90cbb1a | |||
| ae1c59847f | |||
| 651d9be57c | |||
| eeb32db6fb | |||
| daa0780644 | |||
| 8502f9105f | |||
| 16ced3d827 | |||
| 1c16187346 | |||
| 2fe058d9cb | |||
| cefdadd53a | |||
| c21c10df63 | |||
| b4d359d30c | |||
| 1e8c62ac25 | |||
| c578f36644 | |||
| c973d0cff4 | |||
| 0c3d9ae46a | |||
| a834e8b6a2 | |||
| 9f5580d983 | |||
| e94e3cecf8 | |||
| 1991f7f50f | |||
| 9eb4e623e7 | |||
| ad28d2279f | |||
| 1e3de31fc3 | |||
| f85bd41b96 | |||
| 563124b68c | |||
| 63de08c635 | |||
| 8a0a215443 | |||
| f1b7cd633e | |||
| 458eeeccda | |||
| 464e758b94 | |||
| 4c61047e9b | |||
| 1bf9e7fb56 | |||
| ca69554f37 | |||
| cf7029037e | |||
| 418388b0ab | |||
| a0f8689d4f | |||
| 95007bdd26 | |||
| ab91540deb | |||
| b2ebb984f8 | |||
| f7e9ad74d1 | |||
| d48da3d51c | |||
| 76d22554c5 | |||
| 6eaafd883b | |||
| 5961a80b23 | |||
| f41c6fe533 | |||
| 65b8efe13c | |||
| 89529f9c96 | |||
| e90f6ebc63 | |||
| 5888d540a6 | |||
| ae8b740600 | |||
| ab4e2f5bda | |||
| 1091b6d232 | |||
| fc89744238 | |||
| 34e049a002 | |||
| 980bf2c307 | |||
| 762aea1e20 | |||
| c1aefc7163 | |||
| 9480ba26e0 | |||
| c2c9160ed9 | |||
| 175b067a17 | |||
| 9d8656ca20 | |||
| 0863001c80 | |||
| 0ee22a30ad | |||
| 447697ba45 | |||
| aea77ff909 | |||
| af5da76f72 | |||
| a369c65451 | |||
| 318f65f187 | |||
| 1cd60e831c | |||
| b988959eaa | |||
| 1eb1f9848a | |||
| 7f6cc0da01 | |||
| 19fcb69525 | |||
| 22cef0a44c | |||
| 2459d31bff | |||
| 19f104239a | |||
| bd0be65038 | |||
| bbb7907e54 | |||
| a6963a18d4 | |||
| 92716ea3e0 | |||
| aec4c1feea | |||
| d505b3305b | |||
| a34a02e14d | |||
| 26d2d7a51e | |||
| c2f7e52d13 | |||
| de68d8934d | |||
| 4fdf7fc958 | |||
| 42a5e72f19 | |||
| f7359ebc8a | |||
| f395ac53dc | |||
| 1113e0b559 | |||
| 5e3bd31862 | |||
| 11d978dad1 | |||
| f7961024d7 | |||
| 72973a8707 | |||
| 68254f48d5 | |||
| eac4f30c50 | |||
| 25680fa980 | |||
| ff5e1da14d | |||
| 95afff7879 | |||
| 50bd526025 | |||
| 108a0fefc3 | |||
| dd8c5d27be | |||
| b2937bc776 | |||
| 4d8e764211 | |||
| 544b8664fd | |||
| d0610865bd | |||
| ebc0b51590 | |||
| 4487f1169e | |||
| 85559b6083 | |||
| 1056273c57 | |||
| 61af2ebc8b | |||
| 9121c86656 | |||
| 1ccefe853a | |||
| aca438b837 | |||
| 7210c29cd8 | |||
| 26d90c0c9b | |||
| a03b222a95 | |||
| 7944a24d3c | |||
| cc8459c759 | |||
| 10074ff92c | |||
| 173f25bebc | |||
| 31680fc4ae | |||
| e937d43614 | |||
| 20e29a7975 | |||
| ef815dabce | |||
| 1fb133e6b8 | |||
| 50b58cd6a6 | |||
| 01485d7ef9 | |||
| b17c6a5ac7 | |||
| d2ed2b4a00 | |||
| 710a7524a1 | |||
| 2be46464d6 | |||
| 8d536a6734 | |||
| 250d502238 | |||
| e8de7266d0 | |||
| 9414f372d7 | |||
| b0f9de67cf | |||
| 9b082e114e | |||
| 816a5334ac | |||
| 15a4e10da9 | |||
| 01b9302b0c | |||
| 442126a11a | |||
| a9c140c0fc | |||
| 97ad7a3e68 | |||
| 7d737eefb6 | |||
| 4ac05b38d3 | |||
| 651bbbb672 | |||
| 52da4d8687 | |||
| 36063ae76a | |||
| 2fcec2d2cd | |||
| 762a7fdfb7 | |||
| cd7aeaeed2 | |||
| 6f414d312c | |||
| 1b5304efb7 | |||
| d59375308f | |||
| 8c9509a906 | |||
| fb86d8f3a8 | |||
| 50e909cb3d | |||
| 2f54edf7e7 | |||
| c251603e1e | |||
| 4476edb6c3 | |||
| 28fc67660f | |||
| 6e8b5a5ce5 | |||
| e53681416f | |||
| acb5e184e8 | |||
| bdbafb3e5c | |||
| ac70cf87c6 | |||
| 93de835ab4 | |||
| 2ea38b88ce | |||
| 2c4f2be57d | |||
| fa4beea425 | |||
| 7a976edc82 | |||
| bb22c35221 | |||
| ff3dc59016 | |||
| 2e4dd3df3e | |||
| b82e5d33f9 | |||
| 65d56b336b | |||
| 7836d61173 | |||
| 898437720b | |||
| d9a80d1085 | |||
| ab3d8b0ae2 | |||
| b865074c32 | |||
| 2b8ca77c0d | |||
| fa8b4e1e7f | |||
| a310c5bcc1 | |||
| b5dccd6b91 | |||
| c2bcb39b38 | |||
| 4515add0a0 | |||
| 5b6aaec48c | |||
| 810e56ca31 | |||
| 5bcc8ac2e0 | |||
| f5bfb35867 | |||
| 9088b8cd07 | |||
| a7d90dc708 | |||
| 5968b57a01 | |||
| 94946a9ed6 | |||
| 561aec5ef0 | |||
| e31696d843 | |||
| 8b33fd2002 | |||
| 064e43750f | |||
| 29d2f4f681 | |||
| c11b40b94a | |||
| e874e1d798 | |||
| 01244ec632 | |||
| 671657e2b0 | |||
| dff7278e2e | |||
| 0881328636 | |||
| a951c3a7c9 | |||
| b25fae54fe | |||
| a87bc4609e | |||
| 3e68026949 | |||
| 78d6d285cd | |||
| 72e3596a3c | |||
| 643ebcaab4 | |||
| 52ef6cd95a | |||
| 4aec2f3260 | |||
| 73549515eb | |||
| d83d2660cf | |||
| 8de913172c | |||
| 4acfd64cff | |||
| b56f90ed52 | |||
| 1375630727 | |||
| 668825bc01 | |||
| 90414ae579 | |||
| 41c8caa2a4 | |||
| 15305ecabe | |||
| 50090effd4 | |||
| 6dffdcd1ed | |||
| db9daf2714 | |||
| 502ac4ebc1 | |||
| 3f44f3bab4 | |||
| 746eba185b | |||
| 82dec92510 | |||
| 2edc93df47 | |||
| c5a6738199 | |||
| dfd578165b | |||
| 285d400f69 | |||
| 47a3444ace | |||
| 0ec227da52 | |||
| f9cf582306 | |||
| b0883229bf | |||
| 31b5d9a4c0 | |||
| 6841a78556 | |||
| 9d2089a8ce | |||
| 6d6bb79199 | |||
| 937c8e22c4 | |||
| 34c8d44dfe | |||
| 2dbf778e56 | |||
| 873fe2b393 | |||
| 0c53bc6f32 | |||
| fa407e150b | |||
| 4e52102c5c | |||
| 0355a5c646 | |||
| 377d9c3554 | |||
| 20b1b3c895 | |||
| 41bbe7c51b | |||
| 27fa6aefd3 | |||
| a63c3232da | |||
| 146908a115 | |||
| 48b0f35fee | |||
| 6c435ebe26 | |||
| b8d0b721a2 | |||
| a6d5957f46 | |||
| 9e5f676e23 | |||
| 414f3a1f9d | |||
| fedf9c60ee | |||
| b6385d9622 | |||
| 76d25a712d | |||
| 2c6d935273 | |||
| 830d98a101 | |||
| d7378bd75a | |||
| 0f41cb9dbc | |||
| 77bc922d93 | |||
| c91b635132 | |||
| e5521de34a | |||
| 7c0f8d0f24 | |||
| f60d5f650f | |||
| ca67f2fe0a | |||
| 16cce8be1d | |||
| 770619d948 | |||
| cfedb7d6b1 | |||
| 738557b3a2 | |||
| 38b01deec1 | |||
| 1a31e69ec9 | |||
| e065983c95 | |||
| 30a169171a | |||
| 8d1900362e | |||
| e154189de1 | |||
| b0f147de24 | |||
| 979b3548db | |||
| 05d6c578b3 | |||
| a117559063 | |||
| f87c649b09 | |||
| 6504dc9184 | |||
| 25a8ddffd4 | |||
| fa0f9b89cf | |||
| 4d00a67891 | |||
| bd2c43e1f4 | |||
| c7279eaa34 | |||
| fd523e552c | |||
| cb877b8b23 | |||
| ed1bee8b89 | |||
| a8e1492056 | |||
| 5587216c01 | |||
| 86569261ad | |||
| 4a9049c7aa | |||
| 75d60a8182 | |||
| 14d4dc2ed9 | |||
| fd0e1740a5 | |||
| 70ca890bef | |||
| b9318dfd8e | |||
| 995642a719 | |||
| d14de4ac9e | |||
| b7f325a241 | |||
| 27c55718c2 | |||
| 421ff0654b | |||
| ed947458f9 | |||
| 9cdb20ba84 | |||
| d8774b735f | |||
| adcb42695f | |||
| dd77b5bcbb | |||
| d2445be155 | |||
| 10254c8af7 | |||
| d7e830badf | |||
| b445a3a9b8 | |||
| 97f42ead66 | |||
| 03730fafb9 | |||
| 0be9465dca | 
							
								
								
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,3 @@ | |||||||
| # Auto detect text files and perform LF normalization | # Auto detect text files and perform LF normalization | ||||||
| * text=auto | * text=auto eof=lf | ||||||
|  |  | ||||||
| *.txt text eof=lf |  | ||||||
| *.cs  diff=csharp | *.cs  diff=csharp | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | github: chylex | ||||||
|  | patreon: chylex | ||||||
|  | ko_fi: chylex | ||||||
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,8 +5,15 @@ | |||||||
| bld/* | bld/* | ||||||
| !bld/*.iss | !bld/*.iss | ||||||
| !bld/*.bat | !bld/*.bat | ||||||
|  | !bld/*.ps1 | ||||||
|  | !bld/Redist | ||||||
| !bld/Resources | !bld/Resources | ||||||
|  |  | ||||||
|  | # Rider | ||||||
|  | **/.idea/dictionaries | ||||||
|  | **/.idea/misc.xml | ||||||
|  | **/.idea/riderMarkupCache.xml | ||||||
|  |  | ||||||
| # User-specific files | # User-specific files | ||||||
| *.suo | *.suo | ||||||
| *.user | *.user | ||||||
| @@ -140,4 +147,3 @@ _UpgradeReport_Files/ | |||||||
| Backup*/ | Backup*/ | ||||||
| UpgradeLog*.XML | UpgradeLog*.XML | ||||||
| UpgradeLog*.htm | UpgradeLog*.htm | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.idea/.idea.TweetDuck/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/.idea.TweetDuck/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | # Default ignored files | ||||||
|  | /shelf/ | ||||||
|  | /workspace.xml | ||||||
|  | # Rider ignored files | ||||||
|  | /projectSettingsUpdater.xml | ||||||
|  | /modules.xml | ||||||
|  | /contentModel.xml | ||||||
|  | /.idea.TweetDuck.iml | ||||||
							
								
								
									
										450
									
								
								.idea/.idea.TweetDuck/.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										450
									
								
								.idea/.idea.TweetDuck/.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,450 @@ | |||||||
|  | <component name="ProjectCodeStyleConfiguration"> | ||||||
|  |   <code_scheme name="Project" version="173"> | ||||||
|  |     <option name="AUTODETECT_INDENTS" value="false" /> | ||||||
|  |     <option name="OTHER_INDENT_OPTIONS"> | ||||||
|  |       <value> | ||||||
|  |         <option name="INDENT_SIZE" value="2" /> | ||||||
|  |         <option name="TAB_SIZE" value="2" /> | ||||||
|  |       </value> | ||||||
|  |     </option> | ||||||
|  |     <option name="LINE_SEPARATOR" value="
" /> | ||||||
|  |     <option name="RIGHT_MARGIN" value="999" /> | ||||||
|  |     <option name="FORMATTER_TAGS_ENABLED" value="true" /> | ||||||
|  |     <CssCodeStyleSettings> | ||||||
|  |       <option name="HEX_COLOR_LOWER_CASE" value="true" /> | ||||||
|  |     </CssCodeStyleSettings> | ||||||
|  |     <DB2CodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </DB2CodeStyleSettings> | ||||||
|  |     <DerbyCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </DerbyCodeStyleSettings> | ||||||
|  |     <GoCodeStyleSettings> | ||||||
|  |       <option name="MOVE_ALL_STDLIB_IMPORTS_IN_ONE_GROUP" value="true" /> | ||||||
|  |       <option name="GROUP_STDLIB_IMPORTS" value="true" /> | ||||||
|  |       <option name="WRAP_COMP_LIT" value="5" /> | ||||||
|  |       <option name="WRAP_FUNC_PARAMS" value="5" /> | ||||||
|  |       <option name="WRAP_FUNC_RESULT" value="5" /> | ||||||
|  |     </GoCodeStyleSettings> | ||||||
|  |     <H2CodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </H2CodeStyleSettings> | ||||||
|  |     <H2CodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </H2CodeStyleSettings> | ||||||
|  |     <HSQLCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </HSQLCodeStyleSettings> | ||||||
|  |     <HTMLCodeStyleSettings> | ||||||
|  |       <option name="HTML_ALIGN_TEXT" value="true" /> | ||||||
|  |       <option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" /> | ||||||
|  |       <option name="HTML_ELEMENTS_TO_REMOVE_NEW_LINE_BEFORE" value="" /> | ||||||
|  |       <option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="" /> | ||||||
|  |     </HTMLCodeStyleSettings> | ||||||
|  |     <JSCodeStyleSettings version="0"> | ||||||
|  |       <option name="FORCE_SEMICOLON_STYLE" value="true" /> | ||||||
|  |       <option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACKETS" value="true" /> | ||||||
|  |       <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> | ||||||
|  |       <option name="FORCE_QUOTE_STYlE" value="true" /> | ||||||
|  |       <option name="USE_EXPLICIT_JS_EXTENSION" value="TRUE" /> | ||||||
|  |       <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" /> | ||||||
|  |       <option name="SPACES_WITHIN_IMPORTS" value="true" /> | ||||||
|  |       <option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" /> | ||||||
|  |       <option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" /> | ||||||
|  |       <option name="IMPORT_SORT_MODULE_NAME" value="true" /> | ||||||
|  |     </JSCodeStyleSettings> | ||||||
|  |     <JSON> | ||||||
|  |       <option name="OBJECT_WRAPPING" value="5" /> | ||||||
|  |       <option name="ARRAY_WRAPPING" value="5" /> | ||||||
|  |     </JSON> | ||||||
|  |     <JavaCodeStyleSettings> | ||||||
|  |       <option name="INSERT_INNER_CLASS_IMPORTS" value="true" /> | ||||||
|  |       <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> | ||||||
|  |       <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" /> | ||||||
|  |       <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND"> | ||||||
|  |         <value /> | ||||||
|  |       </option> | ||||||
|  |       <option name="IMPORT_LAYOUT_TABLE"> | ||||||
|  |         <value> | ||||||
|  |           <package name="" withSubpackages="true" static="false" /> | ||||||
|  |           <package name="javax" withSubpackages="true" static="false" /> | ||||||
|  |           <package name="java" withSubpackages="true" static="false" /> | ||||||
|  |           <package name="" withSubpackages="true" static="true" /> | ||||||
|  |         </value> | ||||||
|  |       </option> | ||||||
|  |     </JavaCodeStyleSettings> | ||||||
|  |     <JetCodeStyleSettings> | ||||||
|  |       <option name="PACKAGES_TO_USE_STAR_IMPORTS"> | ||||||
|  |         <value> | ||||||
|  |           <package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" /> | ||||||
|  |         </value> | ||||||
|  |       </option> | ||||||
|  |       <option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" /> | ||||||
|  |       <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> | ||||||
|  |       <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> | ||||||
|  |       <option name="IMPORT_NESTED_CLASSES" value="true" /> | ||||||
|  |       <option name="WRAP_ELVIS_EXPRESSIONS" value="0" /> | ||||||
|  |       <option name="ALLOW_TRAILING_COMMA" value="true" /> | ||||||
|  |       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> | ||||||
|  |     </JetCodeStyleSettings> | ||||||
|  |     <LessCodeStyleSettings> | ||||||
|  |       <option name="HEX_COLOR_LOWER_CASE" value="true" /> | ||||||
|  |     </LessCodeStyleSettings> | ||||||
|  |     <MSSQLCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </MSSQLCodeStyleSettings> | ||||||
|  |     <MySQLCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </MySQLCodeStyleSettings> | ||||||
|  |     <Objective-C> | ||||||
|  |       <option name="INDENT_DIRECTIVE_AS_CODE" value="true" /> | ||||||
|  |       <option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" /> | ||||||
|  |       <option name="KEEP_CASE_EXPRESSIONS_IN_ONE_LINE" value="true" /> | ||||||
|  |       <option name="SPACE_BEFORE_INIT_LIST" value="true" /> | ||||||
|  |       <option name="SPACE_AFTER_DICTIONARY_LITERAL_COLON" value="false" /> | ||||||
|  |     </Objective-C> | ||||||
|  |     <OracleCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </OracleCodeStyleSettings> | ||||||
|  |     <PHPCodeStyleSettings> | ||||||
|  |       <option name="ALIGN_KEY_VALUE_PAIRS" value="true" /> | ||||||
|  |       <option name="CONCAT_SPACES" value="false" /> | ||||||
|  |       <option name="COMMA_AFTER_LAST_ARRAY_ELEMENT" value="true" /> | ||||||
|  |       <option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" /> | ||||||
|  |       <option name="LOWER_CASE_BOOLEAN_CONST" value="true" /> | ||||||
|  |       <option name="LOWER_CASE_NULL_CONST" value="true" /> | ||||||
|  |       <option name="ELSE_IF_STYLE" value="COMBINE" /> | ||||||
|  |       <option name="VARIABLE_NAMING_STYLE" value="SNAKE_CASE" /> | ||||||
|  |       <option name="KEEP_BLANK_LINES_AFTER_LBRACE" value="0" /> | ||||||
|  |       <option name="SPACE_BEFORE_CLOSURE_LEFT_PARENTHESIS" value="false" /> | ||||||
|  |       <option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" /> | ||||||
|  |       <option name="NEW_LINE_AFTER_PHP_OPENING_TAG" value="true" /> | ||||||
|  |       <option name="SPACE_AROUND_ASSIGNMENT_IN_DECLARE" value="true" /> | ||||||
|  |     </PHPCodeStyleSettings> | ||||||
|  |     <PostgresCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </PostgresCodeStyleSettings> | ||||||
|  |     <Properties> | ||||||
|  |       <option name="KEEP_BLANK_LINES" value="true" /> | ||||||
|  |     </Properties> | ||||||
|  |     <Python> | ||||||
|  |       <option name="SPACE_AROUND_EQ_IN_NAMED_PARAMETER" value="true" /> | ||||||
|  |       <option name="SPACE_AROUND_EQ_IN_KEYWORD_ARGUMENT" value="true" /> | ||||||
|  |       <option name="NEW_LINE_AFTER_COLON" value="true" /> | ||||||
|  |       <option name="DICT_WRAPPING" value="5" /> | ||||||
|  |       <option name="DICT_NEW_LINE_AFTER_LEFT_BRACE" value="true" /> | ||||||
|  |       <option name="DICT_NEW_LINE_BEFORE_RIGHT_BRACE" value="true" /> | ||||||
|  |     </Python> | ||||||
|  |     <RsCodeStyleSettings> | ||||||
|  |       <option name="ALIGN_RET_TYPE" value="false" /> | ||||||
|  |       <option name="ALIGN_TYPE_PARAMS" value="true" /> | ||||||
|  |       <option name="ALLOW_ONE_LINE_MATCH" value="true" /> | ||||||
|  |       <option name="SPACE_AROUND_ASSOC_TYPE_BINDING" value="true" /> | ||||||
|  |     </RsCodeStyleSettings> | ||||||
|  |     <Ruby> | ||||||
|  |       <option name="INDENT_PRIVATE_METHODS" value="true" /> | ||||||
|  |       <option name="INDENT_PROTECTED_METHODS" value="true" /> | ||||||
|  |       <option name="INDENT_PUBLIC_METHODS" value="true" /> | ||||||
|  |       <option name="INDENT_WHEN_CASES" value="true" /> | ||||||
|  |       <option name="CHAIN_CALLS_ALIGNMENT" value="2" /> | ||||||
|  |     </Ruby> | ||||||
|  |     <SQLiteCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </SQLiteCodeStyleSettings> | ||||||
|  |     <ScssCodeStyleSettings> | ||||||
|  |       <option name="HEX_COLOR_LOWER_CASE" value="true" /> | ||||||
|  |     </ScssCodeStyleSettings> | ||||||
|  |     <SqlCodeStyleSettings version="6"> | ||||||
|  |       <option name="KEYWORD_CASE" value="2" /> | ||||||
|  |       <option name="TYPE_CASE" value="2" /> | ||||||
|  |       <option name="CUSTOM_TYPE_CASE" value="2" /> | ||||||
|  |       <option name="SUBQUERY_CONTENT" value="1" /> | ||||||
|  |       <option name="SUBQUERY_CLOSING" value="1" /> | ||||||
|  |       <option name="INSERT_TABLE_EL_LINE" value="0" /> | ||||||
|  |       <option name="INSERT_EL_WRAP" value="2" /> | ||||||
|  |       <option name="SET_EL_WRAP" value="2" /> | ||||||
|  |       <option name="SET_ALIGN_EQUAL_SIGN" value="false" /> | ||||||
|  |       <option name="FROM_EL_WRAP" value="2" /> | ||||||
|  |       <option name="FROM_ALIGN_JOIN_TABLES" value="true" /> | ||||||
|  |       <option name="FROM_INDENT_JOIN" value="false" /> | ||||||
|  |       <option name="FROM_ONLY_JOIN_INDENT" value="2" /> | ||||||
|  |       <option name="WHERE_EL_WRAP" value="2" /> | ||||||
|  |       <option name="TABLE_OPENING" value="1" /> | ||||||
|  |       <option name="TABLE_CONTENT" value="2" /> | ||||||
|  |       <option name="TABLE_CLOSING" value="3" /> | ||||||
|  |       <option name="TABLE_DEFAULTS_ALIGN" value="false" /> | ||||||
|  |       <option name="TABLE_NULLABILITIES_ALIGN" value="false" /> | ||||||
|  |       <option name="CONSTRAINT_WRAP_1" value="false" /> | ||||||
|  |       <option name="CONSTRAINT_WRAP_3" value="true" /> | ||||||
|  |       <option name="CONSTRAINT_WRAP_4" value="true" /> | ||||||
|  |       <option name="VIEW_INDENT_QUERY" value="true" /> | ||||||
|  |       <option name="EXPR_CASE_WHEN_WRAP" value="false" /> | ||||||
|  |     </SqlCodeStyleSettings> | ||||||
|  |     <SybaseCodeStyleSettings version="6"> | ||||||
|  |       <option name="USE_GENERIC_STYLE" value="true" /> | ||||||
|  |     </SybaseCodeStyleSettings> | ||||||
|  |     <TypeScriptCodeStyleSettings version="0"> | ||||||
|  |       <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> | ||||||
|  |       <option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" /> | ||||||
|  |       <option name="SPACES_WITHIN_IMPORTS" value="true" /> | ||||||
|  |       <option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" /> | ||||||
|  |       <option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" /> | ||||||
|  |     </TypeScriptCodeStyleSettings> | ||||||
|  |     <XML> | ||||||
|  |       <option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" /> | ||||||
|  |     </XML> | ||||||
|  |     <codeStyleSettings language="CMake"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="CSS"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="INDENT_SIZE" value="2" /> | ||||||
|  |         <option name="TAB_SIZE" value="2" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="Groovy"> | ||||||
|  |       <option name="ELSE_ON_NEW_LINE" value="true" /> | ||||||
|  |       <option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" /> | ||||||
|  |       <option name="IF_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="DOWHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="WHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="FOR_BRACE_FORCE" value="3" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="HTML"> | ||||||
|  |       <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" /> | ||||||
|  |       <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="INDENT_SIZE" value="2" /> | ||||||
|  |         <option name="TAB_SIZE" value="2" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="JAVA"> | ||||||
|  |       <option name="RIGHT_MARGIN" value="999" /> | ||||||
|  |       <option name="BLANK_LINES_AFTER_PACKAGE" value="0" /> | ||||||
|  |       <option name="BLANK_LINES_BEFORE_IMPORTS" value="0" /> | ||||||
|  |       <option name="ELSE_ON_NEW_LINE" value="true" /> | ||||||
|  |       <option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" /> | ||||||
|  |       <option name="SPACE_AFTER_TYPE_CAST" value="false" /> | ||||||
|  |       <option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" /> | ||||||
|  |       <option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" /> | ||||||
|  |       <option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" /> | ||||||
|  |       <option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" /> | ||||||
|  |       <option name="IF_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="DOWHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="WHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="FOR_BRACE_FORCE" value="3" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="JSON"> | ||||||
|  |       <option name="KEEP_BLANK_LINES_IN_CODE" value="1" /> | ||||||
|  |       <option name="SPACE_WITHIN_BRACKETS" value="true" /> | ||||||
|  |       <option name="SPACE_WITHIN_BRACES" value="true" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="JavaScript"> | ||||||
|  |       <option name="ELSE_ON_NEW_LINE" value="true" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" /> | ||||||
|  |       <option name="IF_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="DOWHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="WHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="FOR_BRACE_FORCE" value="3" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="LESS"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="Lua"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="INDENT_SIZE" value="2" /> | ||||||
|  |         <option name="TAB_SIZE" value="2" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="ObjectiveC"> | ||||||
|  |       <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" /> | ||||||
|  |       <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" /> | ||||||
|  |       <option name="LINE_COMMENT_ADD_SPACE" value="true" /> | ||||||
|  |       <option name="ELSE_ON_NEW_LINE" value="true" /> | ||||||
|  |       <option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" /> | ||||||
|  |       <option name="IF_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="DOWHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="WHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="FOR_BRACE_FORCE" value="3" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="PHP"> | ||||||
|  |       <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" /> | ||||||
|  |       <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" /> | ||||||
|  |       <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" /> | ||||||
|  |       <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" /> | ||||||
|  |       <option name="KEEP_BLANK_LINES_IN_CODE" value="1" /> | ||||||
|  |       <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" /> | ||||||
|  |       <option name="CLASS_BRACE_STYLE" value="1" /> | ||||||
|  |       <option name="METHOD_BRACE_STYLE" value="1" /> | ||||||
|  |       <option name="ELSE_ON_NEW_LINE" value="true" /> | ||||||
|  |       <option name="SPECIAL_ELSE_IF_TREATMENT" value="true" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" /> | ||||||
|  |       <option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" /> | ||||||
|  |       <option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" /> | ||||||
|  |       <option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" /> | ||||||
|  |       <option name="IF_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="DOWHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="WHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="FOR_BRACE_FORCE" value="3" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="INDENT_SIZE" value="2" /> | ||||||
|  |         <option name="TAB_SIZE" value="2" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="Puppet"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="Python"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="RHTML"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="Rust"> | ||||||
|  |       <option name="RIGHT_MARGIN" value="140" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="SASS"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="SCSS"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="SQL"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="Shell Script"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="INDENT_SIZE" value="4" /> | ||||||
|  |         <option name="TAB_SIZE" value="4" /> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="TOML"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="TypeScript"> | ||||||
|  |       <option name="ELSE_ON_NEW_LINE" value="true" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" /> | ||||||
|  |       <option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" /> | ||||||
|  |       <option name="IF_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="DOWHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="WHILE_BRACE_FORCE" value="3" /> | ||||||
|  |       <option name="FOR_BRACE_FORCE" value="3" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="XML"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="INDENT_SIZE" value="2" /> | ||||||
|  |         <option name="TAB_SIZE" value="2" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="go"> | ||||||
|  |       <option name="CALL_PARAMETERS_WRAP" value="5" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="kotlin"> | ||||||
|  |       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> | ||||||
|  |       <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" /> | ||||||
|  |       <option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" /> | ||||||
|  |       <option name="LINE_COMMENT_ADD_SPACE" value="true" /> | ||||||
|  |       <option name="ELSE_ON_NEW_LINE" value="true" /> | ||||||
|  |       <option name="METHOD_ANNOTATION_WRAP" value="0" /> | ||||||
|  |       <option name="CLASS_ANNOTATION_WRAP" value="0" /> | ||||||
|  |       <option name="FIELD_ANNOTATION_WRAP" value="0" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="CONTINUATION_INDENT_SIZE" value="4" /> | ||||||
|  |         <option name="USE_TAB_CHARACTER" value="true" /> | ||||||
|  |         <option name="SMART_TABS" value="true" /> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="liquid"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="ruby"> | ||||||
|  |       <option name="SPACE_WITHIN_BRACES" value="true" /> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |     <codeStyleSettings language="yaml"> | ||||||
|  |       <indentOptions> | ||||||
|  |         <option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" /> | ||||||
|  |       </indentOptions> | ||||||
|  |     </codeStyleSettings> | ||||||
|  |   </code_scheme> | ||||||
|  | </component> | ||||||
							
								
								
									
										5
									
								
								.idea/.idea.TweetDuck/.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.idea/.idea.TweetDuck/.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | <component name="ProjectCodeStyleConfiguration"> | ||||||
|  |   <state> | ||||||
|  |     <option name="USE_PER_PROJECT_SETTINGS" value="true" /> | ||||||
|  |   </state> | ||||||
|  | </component> | ||||||
							
								
								
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8"> | ||||||
|  |     <file url="PROJECT" charset="UTF-8" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										8
									
								
								.idea/.idea.TweetDuck/.idea/indexLayout.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/.idea.TweetDuck/.idea/indexLayout.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="UserContentModel"> | ||||||
|  |     <attachedFolders /> | ||||||
|  |     <explicitIncludes /> | ||||||
|  |     <explicitExcludes /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										770
									
								
								.idea/.idea.TweetDuck/.idea/inspectionProfiles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										770
									
								
								.idea/.idea.TweetDuck/.idea/inspectionProfiles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,770 @@ | |||||||
|  | <component name="InspectionProjectProfileManager"> | ||||||
|  |   <profile version="1.0"> | ||||||
|  |     <option name="myName" value="Project" /> | ||||||
|  |     <inspection_tool class="AccessToNonThreadSafeStaticFieldFromInstance" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="nonThreadSafeClasses"> | ||||||
|  |         <value /> | ||||||
|  |       </option> | ||||||
|  |       <option name="nonThreadSafeTypes" value="" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="AccessToStaticFieldLockedOnInstance" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AddOperatorModifier" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AmbiguousFieldAccess" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AmbiguousMethodCall" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AnonymousInnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ArrayEquality" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AssertEqualsCalledOnArray" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AssertsWithoutMessagesTestNG" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AssignmentOrReturnOfFieldWithMutableType" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AssignmentToCatchBlockParameter" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AssignmentToLambdaParameter" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AssignmentToMethodParameter" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreTransformationOfOriginalParameter" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="AssignmentToStaticFieldFromInstanceMethod" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AssignmentUsedAsCondition" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AutoBoxing" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreAddedToCollection" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoredTypes" value="java.util.stream.Stream,java.util.stream.IntStream,java.util.stream.LongStream,java.util.stream.DoubleStream,net.minecraft.client.Minecraft,net.minecraft.client.MainWindow" /> | ||||||
|  |       <option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,net.minecraft.client.MinecraftClient,getInstance|getWindow" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="AutoUnboxing" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AwaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="AwaitWithoutCorrespondingSignal" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="BadOddness" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="BigDecimalEquals" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="BigDecimalLegacyMethod" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="BooleanExpressionMayBeConditional" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CallToNativeMethodWhileLocked" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CallableParameterUseCaseInTypeContextInspection" enabled="false" level="WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="CascadeStringReplacementInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="USE_SHORT_ARRAYS_SYNTAX" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="CastConflictsWithInstanceof" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CastToIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ChainedEquality" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ChannelResource" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="insideTryAllowed" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ClassIndependentOfModule" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ClassLoaderInstantiation" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ClassMayBeInterface" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="reportClassesWithNonAbstractMethods" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ClassMethodNameMatchesFieldNameInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="ClassNameDiffersFromFileName" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ClassNewInstance" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ClassOnlyUsedInOneModule" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ClassOnlyUsedInOnePackage" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ClassOverridesFieldOfSuperClassInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="REPORT_PRIVATE_REDEFINITION" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ClassUnconnectedToPackage" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CloneableClassInSecureContext" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CollectionContainsUrl" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CollectionsFieldAccessReplaceableByMethodCall" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ComparableImplementedButEqualsNotOverridden" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ComparatorNotSerializable" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CompareToUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ComposeMissingKeys" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConditionSignal" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConditionalExpression" enabled="true" level="INFORMATION" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreSimpleAssignmentsAndReturns" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ConditionalExpressionWithIdenticalBranchesJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConfusingElse" enabled="false" level="WEAK WARNING" enabled_by_default="false"> | ||||||
|  |       <option name="reportWhenNoStatementFollow" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ConfusingMainMethod" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConfusingOctalEscape" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConfusingPlusesOrMinusesJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConstantJUnitAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConstantMathCall" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConstantTestNGAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ControlFlowStatementWithoutBraces" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConvertJavadoc" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConvertLambdaToReference" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ConvertOldAnnotations" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CssConvertColorToHexInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CssConvertColorToRgbInspection" enabled="true" level="INFORMATION" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CssMissingSemicolon" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CssReplaceWithShorthandUnsafely" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CustomClassloader" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CustomSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CyclicClassDependency" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="CyclicPackageDependency" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DateToString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DisallowWritingIntoStaticPropertiesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DisconnectedForeachInstructionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="DisjointPackage" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DivideByZeroJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DocumentWriteJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DoubleBraceInitialization" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DoubleCheckedLocking" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreOnVolatileVariables" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="DriverManagerGetConnection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DuplicateBooleanBranch" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DuplicateConditionJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="DynamicallyGeneratedCodeJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ES6ConvertIndexedForToForOf" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ES6ConvertLetToConst" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ES6ConvertToForOf" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ES6ShorthandObjectProperty" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ES6TopLevelAwaitExpression" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="EmptyDirectory" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="EmptyStatementBody" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_reportEmptyBlocks" value="true" /> | ||||||
|  |       <option name="commentsAreContent" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="EmptySynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="EnumSwitchStatementWhichMissesCases" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreSwitchStatementsWithDefault" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="EqualsCalledOnEnumConstant" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="EqualsHashCodeCalledOnUrl" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="EqualsUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ErrorRethrown" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ExceptionNameDoesntEndWithException" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ExceptionPackage" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ExpectedExceptionNeverThrownTestNG" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ExplicitArgumentCanBeLambda" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ExtendsThread" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ExtendsThrowable" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ExternalizableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="FallthruInSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="FieldAccessedSynchronizedAndUnsynchronized" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="countGettersAndSetters" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="FieldDeclarationSideOnly" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="FieldHidesSuperclassField" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreInvisibleFields" value="false" /> | ||||||
|  |       <option name="ignoreStaticFields" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="FieldMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="Finalize" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreTrivialFinalizers" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="FinalizeNotProtected" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="FixedTimeStartWithInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="FloatingPointEquality" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="FoldInitializerAndIfToElvis" enabled="false" level="INFO" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="ForLoopThatDoesntUseLoopVariableJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ForgottenDebugOutputInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="configuration"> | ||||||
|  |         <list> | ||||||
|  |           <option value="\Codeception\Util\Debug::debug" /> | ||||||
|  |           <option value="\Codeception\Util\Debug::pause" /> | ||||||
|  |           <option value="\Doctrine\Common\Util\Debug::dump" /> | ||||||
|  |           <option value="\Doctrine\Common\Util\Debug::export" /> | ||||||
|  |           <option value="\Illuminate\Support\Debug\Dumper::dump" /> | ||||||
|  |           <option value="\Symfony\Component\Debug\Debug::enable" /> | ||||||
|  |           <option value="\Symfony\Component\Debug\DebugClassLoader::enable" /> | ||||||
|  |           <option value="\Symfony\Component\Debug\ErrorHandler::register" /> | ||||||
|  |           <option value="\Symfony\Component\Debug\ExceptionHandler::register" /> | ||||||
|  |           <option value="\TYPO3\CMS\Core\Utility\DebugUtility::debug" /> | ||||||
|  |           <option value="\Zend\Debug\Debug::dump" /> | ||||||
|  |           <option value="\Zend\Di\Display\Console::export" /> | ||||||
|  |           <option value="dd" /> | ||||||
|  |           <option value="debug_print_backtrace" /> | ||||||
|  |           <option value="debug_zval_dump" /> | ||||||
|  |           <option value="dpm" /> | ||||||
|  |           <option value="dpq" /> | ||||||
|  |           <option value="dsm" /> | ||||||
|  |           <option value="dump" /> | ||||||
|  |           <option value="dvm" /> | ||||||
|  |           <option value="error_log" /> | ||||||
|  |           <option value="kpr" /> | ||||||
|  |           <option value="phpinfo" /> | ||||||
|  |           <option value="print_r" /> | ||||||
|  |           <option value="var_dump" /> | ||||||
|  |           <option value="var_export" /> | ||||||
|  |           <option value="wp_die" /> | ||||||
|  |           <option value="xdebug_break" /> | ||||||
|  |           <option value="xdebug_call_class" /> | ||||||
|  |           <option value="xdebug_call_file" /> | ||||||
|  |           <option value="xdebug_call_function" /> | ||||||
|  |           <option value="xdebug_call_line" /> | ||||||
|  |           <option value="xdebug_code_coverage_started" /> | ||||||
|  |           <option value="xdebug_debug_zval" /> | ||||||
|  |           <option value="xdebug_debug_zval_stdout" /> | ||||||
|  |           <option value="xdebug_dump_superglobals" /> | ||||||
|  |           <option value="xdebug_enable" /> | ||||||
|  |           <option value="xdebug_get_code_coverage" /> | ||||||
|  |           <option value="xdebug_get_collected_errors" /> | ||||||
|  |           <option value="xdebug_get_declared_vars" /> | ||||||
|  |           <option value="xdebug_get_function_stack" /> | ||||||
|  |           <option value="xdebug_get_headers" /> | ||||||
|  |           <option value="xdebug_get_monitored_functions" /> | ||||||
|  |           <option value="xdebug_get_profiler_filename" /> | ||||||
|  |           <option value="xdebug_get_stack_depth" /> | ||||||
|  |           <option value="xdebug_get_tracefile_name" /> | ||||||
|  |           <option value="xdebug_is_enabled" /> | ||||||
|  |           <option value="xdebug_memory_usage" /> | ||||||
|  |           <option value="xdebug_peak_memory_usage" /> | ||||||
|  |           <option value="xdebug_print_function_stack" /> | ||||||
|  |           <option value="xdebug_start_code_coverage" /> | ||||||
|  |           <option value="xdebug_start_error_collection" /> | ||||||
|  |           <option value="xdebug_start_function_monitor" /> | ||||||
|  |           <option value="xdebug_start_trace" /> | ||||||
|  |           <option value="xdebug_stop_code_coverage" /> | ||||||
|  |           <option value="xdebug_stop_error_collection" /> | ||||||
|  |           <option value="xdebug_stop_function_monitor" /> | ||||||
|  |           <option value="xdebug_stop_trace" /> | ||||||
|  |           <option value="xdebug_time_index" /> | ||||||
|  |           <option value="xdebug_var_dump" /> | ||||||
|  |         </list> | ||||||
|  |       </option> | ||||||
|  |       <option name="migratedIntoUserSpace" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="FunctionNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_regex" value="[a-z][A-Za-z]*" /> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="99" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="FunctionWithInconsistentReturnsJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="HashCodeUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="HibernateResource" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="insideTryAllowed" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="HtmlMissingClosingTag" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="HtmlPresentationalElement" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="HtmlRequiredTitleAttribute" enabled="true" level="INFORMATION" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="IOResource" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoredTypesString" value="java.io.ByteArrayOutputStream,java.io.ByteArrayInputStream,java.io.StringBufferInputStream,java.io.CharArrayWriter,java.io.CharArrayReader,java.io.StringWriter,java.io.StringReader" /> | ||||||
|  |       <option name="insideTryAllowed" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="IfStatementWithIdenticalBranchesJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ImplicitDefaultCharsetUsage" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="InconsistentLanguageLevel" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="IncrementDecrementOperationEquivalentInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="InnerClassOnInterface" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreInnerInterfaces" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="InnerClassReferencedViaSubclass" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="InnerClassVariableHidesOuterClassVariable" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreInvisibleFields" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="InsertLiteralUnderscores" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="InstanceofCatchParameter" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="InstanceofIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="InstanceofThis" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="InterfaceMayBeAnnotatedFunctional" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="IsEmptyFunctionUsageInspection" enabled="false" level="WARNING" enabled_by_default="false"> | ||||||
|  |       <option name="SUGGEST_TO_USE_COUNT_CHECK" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="IsNullFunctionUsageInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="IteratorNextDoesNotThrowNoSuchElementException" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JDBCExecuteWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JDBCPrepareStatementWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JDBCResource" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="insideTryAllowed" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JNDIResource" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="insideTryAllowed" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JSArrowFunctionBracesCanBeRemoved" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JSClassNamingConvention" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="99" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JSConstructorReturnsPrimitive" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JSEqualityComparisonWithCoercion.TS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="mySeverity" value="Always" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JSJoinVariableDeclarationAndAssignment" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JSMissingSwitchBranches" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JSNonASCIINames" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="myAllowOnlyAscii" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JSNonStrictModeUsed" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JSOctalInteger" enabled="true" level="ERROR" enabled_by_default="true"> | ||||||
|  |       <option name="myReportNonStrictEs5" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JSUndeclaredVariable" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="myCheckGlobalDefinitions" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JSUnusedGlobalSymbols" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="myReportUnusedDefinitions" value="true" /> | ||||||
|  |       <option name="myReportUnusedProperties" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="JUnitDatapoint" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JUnitRule" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JUnitTestNG" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JavadocHtmlLint" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="JoinDeclarationAndAssignmentJava" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="LambdaCanBeMethodCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="LengthOneStringInIndexOf" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="LengthOneStringsInConcatenation" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="LoadLibraryWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="REPORT_VARIABLES" value="true" /> | ||||||
|  |       <option name="REPORT_PARAMETERS" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="LocalVariableDeclarationSideOnly" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="LocalVariableNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_regex" value="[a-z][A-Za-z]*" /> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="99" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="MalformedSetUpTearDown" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MapReplaceableByEnumMap" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MethodCallSideOnly" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MethodMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_onlyPrivateOrFinal" value="false" /> | ||||||
|  |       <option name="m_ignoreEmptyMethods" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="MethodMayBeSynchronized" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MethodOverloadsParentMethod" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="reportIncompatibleParameters" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="MethodOverridesInaccessibleMethodOfSuper" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MethodOverridesStaticMethod" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MethodSideOnly" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MisorderedAssertEqualsArgumentsTestNG" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MisorderedAssertEqualsParameters" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="MisorderedModifiersInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="MissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreObjectMethods" value="false" /> | ||||||
|  |       <option name="ignoreAnonymousClassMethods" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_requireAnnotationsFirst" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="MultipleTopLevelClassesInFile" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NakedNotify" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NegatedConditional" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreNegatedNullComparison" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="NestedAssignment" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NestedClassSideOnly" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NestedConditionalExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NestedSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NestedSynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NestedTernaryOperatorInspection" enabled="false" level="WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="NewExpressionSideOnly" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonBlockStatementBodyJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonExceptionNameEndsWithException" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonFinalClone" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonFinalFieldInEnum" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonFinalFieldOfException" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonFinalUtilityClass" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonReproducibleMathCall" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonSerializableFieldInSerializableClass" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignorableAnnotations"> | ||||||
|  |         <value /> | ||||||
|  |       </option> | ||||||
|  |       <option name="ignoreAnonymousInnerClasses" value="false" /> | ||||||
|  |       <option name="superClassString" value="java.awt.Component" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="NonSerializableObjectBoundToHttpSession" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonSerializableObjectPassedToObjectStream" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonSerializableWithSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonSerializableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonShortCircuitBoolean" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonSynchronizedMethodOverridesSynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NonThreadSafeLazyInitialization" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NotOptimalIfConditionsInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="REPORT_DUPLICATE_CONDITIONS" value="false" /> | ||||||
|  |       <option name="SUGGEST_OPTIMIZING_CONDITIONS" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="NotifyCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NullThrown" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="OCInconsistentNaming" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ObjectAllocationIgnoredJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ObjectInstantiationInEqualsHashCode" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ObjectNotify" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ObjectToString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ObsoleteCollection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreRequiredObsoleteCollectionTypes" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="OctalAndDecimalIntegersMixed" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="OffsetOperationsInspection" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="OneTimeUseVariablesInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ALLOW_LONG_STATEMENTS" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="OverloadedVarargsMethod" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PackageInMultipleModules" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreInvisibleFields" value="true" /> | ||||||
|  |       <option name="m_ignoreStaticMethodParametersHidingInstanceFields" value="true" /> | ||||||
|  |       <option name="m_ignoreForConstructors" value="true" /> | ||||||
|  |       <option name="m_ignoreForPropertySetters" value="true" /> | ||||||
|  |       <option name="m_ignoreForAbstractMethods" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ParameterNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_regex" value="[a-z][A-Za-z]*" /> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="99" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ParameterizedParametersStaticCollection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpAssignmentInConditionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpAssignmentReplaceableWithOperatorAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpAssignmentReplaceableWithPrefixExpressionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpCSValidationInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false"> | ||||||
|  |       <option name="EXTENSIONS" value="php,js,css,inc" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpClassNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="0" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpClosureCanBeConvertedToShortArrowFunctionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpCompoundNamespaceDepthInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpConstantNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="0" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpConstantReassignmentInspection" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpDivisionByZeroInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpDocMissingThrowsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="SKIP_ON_EMPTY_PHPDOC" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpFunctionNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="0" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpInconsistentReturnPointsInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ALLOW_RETURN_NULL_IN_VOID" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpLongTypeFormInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpLoopCanBeConvertedToArrayFillInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpLoopCanBeConvertedToArrayFilterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpLoopCanBeConvertedToArrayMapInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpMethodNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="0" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpMethodOrClassCallIsNotCaseSensitiveInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpMissingParentCallMagicInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpMissingReturnTypeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpMissingStrictTypesDeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpMissingVisibilityInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpNewClassMissingParameterListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpNonCanonicalElementsOrderInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpOverridingMethodVisibilityInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpPropertyNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_regex" value="[a-z][_a-z\d]*" /> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="0" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpRedundantClosingTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="PhpSeparateElseIfInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpShortOpenTagInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpSingleStatementWithBracesInspection" enabled="false" level="INFORMATION" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="PhpStatementHasEmptyBodyInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="myCommentsCountAsContent" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpStatementWithoutBracesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpStaticAsDynamicMethodCallInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="SHOW_FOR_MAGIC" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpTraditionalSyntaxArrayLiteralInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpTraitsUseListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpUndefinedCallbackInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpUndefinedClassConstantInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="DOWNGRADE_SEVERITY" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpUndefinedMethodInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="DOWNGRADE_SEVERITY" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpUnused" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="SHOW_UNUSED_BY_ENTRIES" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="DONT_REPORT_ANONYMOUS" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpVarUsageInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PhpVariableNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_minLength" value="0" /> | ||||||
|  |       <option name="m_maxLength" value="0" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="PhpVariableVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PointlessBitwiseExpressionJS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreExpressionsContainingConstants" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PropertyCanBeStaticInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PublicStaticArrayField" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PublicStaticCollectionField" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PyAugmentAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_onlyWhenTypesAreKnown" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="RawTypeCanBeGeneric" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReadObjectAndWriteObjectPrivate" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReadObjectInitialization" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReadResolveAndWriteReplaceProtected" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="RedundantElseClauseInspection" enabled="false" level="WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreSerializable" value="true" /> | ||||||
|  |       <option name="ignoreCloneable" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="RedundantObjectTypeCheck" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="RedundantSuppression" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="IGNORE_ALL" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ReferencingObjectsInspection" enabled="false" level="WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="RegExpOctalEscape" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceAssignmentWithOperatorAssignment" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreLazyOperators" value="true" /> | ||||||
|  |       <option name="ignoreObscureOperators" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ReplaceAssignmentWithOperatorAssignmentJS" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceCollectionCountWithSize" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceGuardClauseWithFunctionCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceStringFormatWithLiteral" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceSubstringWithDropLast" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceSubstringWithIndexingOperation" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceSubstringWithSubstringAfter" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceSubstringWithSubstringBefore" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReplaceSubstringWithTake" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ResultOfObjectAllocationIgnored" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ResultSetIndexZero" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ReturnOfInnerClass" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="RsSimplifyBooleanExpression" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="RuntimeExec" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="RuntimeExecWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SafeLock" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SecurityAdvisoriesInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="optionConfiguration"> | ||||||
|  |         <list> | ||||||
|  |           <option value="barryvdh/laravel-debugbar" /> | ||||||
|  |           <option value="behat/behat" /> | ||||||
|  |           <option value="brianium/paratest" /> | ||||||
|  |           <option value="codeception/codeception" /> | ||||||
|  |           <option value="codedungeon/phpunit-result-printer" /> | ||||||
|  |           <option value="composer/composer" /> | ||||||
|  |           <option value="doctrine/coding-standard" /> | ||||||
|  |           <option value="filp/whoops" /> | ||||||
|  |           <option value="friendsofphp/php-cs-fixer" /> | ||||||
|  |           <option value="humbug/humbug" /> | ||||||
|  |           <option value="infection/infection" /> | ||||||
|  |           <option value="jakub-onderka/php-parallel-lint" /> | ||||||
|  |           <option value="johnkary/phpunit-speedtrap" /> | ||||||
|  |           <option value="kalessil/production-dependencies-guard" /> | ||||||
|  |           <option value="mikey179/vfsStream" /> | ||||||
|  |           <option value="mockery/mockery" /> | ||||||
|  |           <option value="mybuilder/phpunit-accelerator" /> | ||||||
|  |           <option value="orchestra/testbench" /> | ||||||
|  |           <option value="pdepend/pdepend" /> | ||||||
|  |           <option value="phan/phan" /> | ||||||
|  |           <option value="phing/phing" /> | ||||||
|  |           <option value="phpcompatibility/php-compatibility" /> | ||||||
|  |           <option value="phpmd/phpmd" /> | ||||||
|  |           <option value="phpro/grumphp" /> | ||||||
|  |           <option value="phpspec/phpspec" /> | ||||||
|  |           <option value="phpspec/prophecy" /> | ||||||
|  |           <option value="phpstan/phpstan" /> | ||||||
|  |           <option value="phpunit/phpunit" /> | ||||||
|  |           <option value="povils/phpmnd" /> | ||||||
|  |           <option value="roave/security-advisories" /> | ||||||
|  |           <option value="satooshi/php-coveralls" /> | ||||||
|  |           <option value="sebastian/phpcpd" /> | ||||||
|  |           <option value="slevomat/coding-standard" /> | ||||||
|  |           <option value="spatie/phpunit-watcher" /> | ||||||
|  |           <option value="squizlabs/php_codesniffer" /> | ||||||
|  |           <option value="sstalle/php7cc" /> | ||||||
|  |           <option value="symfony/debug" /> | ||||||
|  |           <option value="symfony/maker-bundle" /> | ||||||
|  |           <option value="symfony/phpunit-bridge" /> | ||||||
|  |           <option value="symfony/var-dumper" /> | ||||||
|  |           <option value="vimeo/psalm" /> | ||||||
|  |           <option value="wimg/php-compatibility" /> | ||||||
|  |           <option value="wp-coding-standards/wpcs" /> | ||||||
|  |           <option value="yiisoft/yii2-coding-standards" /> | ||||||
|  |           <option value="yiisoft/yii2-debug" /> | ||||||
|  |           <option value="yiisoft/yii2-gii" /> | ||||||
|  |           <option value="zendframework/zend-coding-standard" /> | ||||||
|  |           <option value="zendframework/zend-debug" /> | ||||||
|  |           <option value="zendframework/zend-test" /> | ||||||
|  |         </list> | ||||||
|  |       </option> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SerialPersistentFieldsWithWrongSignature" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SerialVersionUIDNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreAnonymousInnerClasses" value="false" /> | ||||||
|  |       <option name="superClassString" value="java.awt.Component" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SerializableHasSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreAnonymousInnerClasses" value="false" /> | ||||||
|  |       <option name="superClassString" value="java.awt.Component" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SerializableInnerClassHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreAnonymousInnerClasses" value="false" /> | ||||||
|  |       <option name="superClassString" value="java.awt.Component" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SerializableInnerClassWithNonSerializableOuterClass" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreAnonymousInnerClasses" value="false" /> | ||||||
|  |       <option name="superClassString" value="java.awt.Component" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SerializableStoresNonSerializable" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SerializableWithUnconstructableAncestor" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SetReplaceableByEnumSet" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SeveralTargetsMessage" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SharedThreadLocalRandom" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ShortEchoTagCanBeUsedInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SignalWithoutCorrespondingAwait" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SimpleDateFormatWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SimplifiableAnnotation" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SimplifiableIfStatement" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SingleStatementInBlock" enabled="false" level="INFORMATION" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SleepWhileHoldingLock" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SocketResource" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="insideTryAllowed" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> | ||||||
|  |       <option name="processCode" value="true" /> | ||||||
|  |       <option name="processLiterals" value="true" /> | ||||||
|  |       <option name="processComments" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SqlGotoInspection" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SqlRedundantOrderingDirectionInspection" enabled="false" level="WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="SqlWithoutWhereInspection" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="myDontWarnForLimit" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="StrTrUsageAsStrReplaceInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="StringBufferToStringInConcatenation" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="StringConcatenationInFormatCall" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="StringConcatenationInMessageFormatCall" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="StringConcatenationMissingWhitespace" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="StringEqualsEmptyString" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="SUPPRESS_FOR_VALUES_WHICH_COULD_BE_NULL" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="StringReplaceableByStringBuffer" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="onlyWarnOnLoop" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="StringToUpperWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SubStrUsedAsStrPosInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="SubtractionInCompareTo" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SuspiciousArrayCast" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SuspiciousIndentAfterControlStatement" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SuspiciousLiteralUnderscore" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SwitchStatementWithConfusingDeclaration" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SynchronizationOnStaticField" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SynchronizeOnLock" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SynchronizeOnThis" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_includeNativeMethods" value="true" /> | ||||||
|  |       <option name="ignoreSynchronizedSuperMethods" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="SynchronizedOnLiteralObject" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SystemGC" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SystemGetenv" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SystemProperties" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SystemRunFinalizersOnExit" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="SystemSetSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TestCaseInProductCode" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TestCaseWithConstructor" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TestCaseWithNoTestMethods" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreSupers" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="TestMethodInProductCode" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TestMethodWithoutAssertion" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TextLabelInSwitchStatementJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ThreadLocalNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ThreadPriority" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ThreadStartInConstruction" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ThreadStopSuspendResume" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ThreadYield" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="ThrowCaughtLocally" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreRethrownExceptions" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ThrowRawExceptionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="TimeToString" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TooBroadScope" enabled="true" level="WEAK WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_allowConstructorAsInitializer" value="false" /> | ||||||
|  |       <option name="m_onlyLookAtBlocks" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="TransientFieldInNonSerializableClass" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TransientFieldNotInitialized" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="TrivialStringConcatenation" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnNecessaryDoubleQuotesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnaryPlus" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnclearBinaryExpression" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="UnconditionalWait" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnconstrainedVariableType" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UndeclaredTests" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnknownInspectionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreStaticFieldAccesses" value="false" /> | ||||||
|  |       <option name="m_ignoreStaticMethodCalls" value="false" /> | ||||||
|  |       <option name="m_ignoreStaticAccessFromStaticContext" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnnecessarilyQualifiedStaticallyImportedElement" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnnecessaryBoxing" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="onlyReportSuperfluouslyBoxed" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnnecessaryConstantArrayCreationExpression" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnnecessaryConstructor" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreAnnotations" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnnecessaryLocalVariable" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreImmediatelyReturnedVariables" value="true" /> | ||||||
|  |       <option name="m_ignoreAnnotatedVariables" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnnecessaryLocalVariableJS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreImmediatelyReturnedVariables" value="true" /> | ||||||
|  |       <option name="m_ignoreAnnotatedVariables" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnnecessaryParentheses" enabled="true" level="INFORMATION" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreClarifyingParentheses" value="true" /> | ||||||
|  |       <option name="ignoreParenthesesOnConditionals" value="false" /> | ||||||
|  |       <option name="ignoreParenthesesOnLambdaParameter" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnnecessaryToStringCall" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnnecessaryUnaryMinus" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UnnecessaryUnboxing" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="onlyReportSuperfluouslyUnboxed" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnsetConstructsCanBeMergedInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="UnterminatedStatementJS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreSemicolonAtEndOfBlock" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnusedCatchParameterJS" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="m_ignoreCatchBlocksWithComments" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="UnusedProperty" enabled="false" level="WARNING" enabled_by_default="false" /> | ||||||
|  |     <inspection_tool class="UseOfAWTPeerClass" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UseOfJDBCDriverClass" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UseOfObsoleteAssert" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UseOfObsoleteDateTimeApi" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UseOfProcessBuilder" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UseOfPropertiesAsHashtable" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UseOfSunClasses" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UsingInclusionReturnValueInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UtilityClassWithPublicConstructor" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="UtilityClassWithoutPrivateConstructor" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignorableAnnotations"> | ||||||
|  |         <value /> | ||||||
|  |       </option> | ||||||
|  |       <option name="ignoreClassesWithOnlyMain" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="VariableNotUsedInsideIf" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="VariableUseSideOnly" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="VoidExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="VolatileArrayField" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="WaitCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="WaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="WaitNotifyNotInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="WaitOrAwaitWithoutTimeout" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="WaitWhileHoldingTwoLocks" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="WaitWithoutCorrespondingNotify" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |     <inspection_tool class="WhileLoopSpinsOnField" enabled="true" level="WARNING" enabled_by_default="true"> | ||||||
|  |       <option name="ignoreNonEmtpyLoops" value="false" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |     <inspection_tool class="ZeroLengthArrayInitialization" enabled="true" level="WARNING" enabled_by_default="true" /> | ||||||
|  |   </profile> | ||||||
|  | </component> | ||||||
							
								
								
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/inspectionProfiles/profiles_settings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/inspectionProfiles/profiles_settings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | <component name="InspectionProjectProfileManager"> | ||||||
|  |   <settings> | ||||||
|  |     <option name="PROJECT_PROFILE" value="Project" /> | ||||||
|  |     <version value="1.0" /> | ||||||
|  |   </settings> | ||||||
|  | </component> | ||||||
							
								
								
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/jsLibraryMappings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/jsLibraryMappings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="JavaScriptLibraryMappings"> | ||||||
|  |     <file url="PROJECT" libraries="{@types/jquery}" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										20
									
								
								.idea/.idea.TweetDuck/.idea/runConfigurations/TweetDuck.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.idea/.idea.TweetDuck/.idea/runConfigurations/TweetDuck.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | <component name="ProjectRunConfigurationManager"> | ||||||
|  |   <configuration default="false" name="TweetDuck" type="DotNetProject" factoryName=".NET Project"> | ||||||
|  |     <option name="EXE_PATH" value="$PROJECT_DIR$/windows/TweetDuck/bin/x86/Debug/TweetDuck.exe" /> | ||||||
|  |     <option name="PROGRAM_PARAMETERS" value="-datafolder TweetDuckDebug -nogdpr" /> | ||||||
|  |     <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/windows/TweetDuck/bin/x86/Debug" /> | ||||||
|  |     <option name="PASS_PARENT_ENVS" value="1" /> | ||||||
|  |     <option name="USE_EXTERNAL_CONSOLE" value="0" /> | ||||||
|  |     <option name="USE_MONO" value="0" /> | ||||||
|  |     <option name="RUNTIME_ARGUMENTS" value="" /> | ||||||
|  |     <option name="PROJECT_PATH" value="$PROJECT_DIR$/windows/TweetDuck/TweetDuck.csproj" /> | ||||||
|  |     <option name="PROJECT_EXE_PATH_TRACKING" value="1" /> | ||||||
|  |     <option name="PROJECT_ARGUMENTS_TRACKING" value="1" /> | ||||||
|  |     <option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" /> | ||||||
|  |     <option name="PROJECT_KIND" value="DotNetCore" /> | ||||||
|  |     <option name="PROJECT_TFM" value="net7.0-windows" /> | ||||||
|  |     <method v="2"> | ||||||
|  |       <option name="Build" /> | ||||||
|  |     </method> | ||||||
|  |   </configuration> | ||||||
|  | </component> | ||||||
							
								
								
									
										11
									
								
								.idea/.idea.TweetDuck/.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.idea/.idea.TweetDuck/.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="GitSharedSettings"> | ||||||
|  |     <option name="FORCE_PUSH_PROHIBITED_PATTERNS"> | ||||||
|  |       <list /> | ||||||
|  |     </option> | ||||||
|  |   </component> | ||||||
|  |   <component name="VcsDirectoryMappings"> | ||||||
|  |     <mapping directory="$PROJECT_DIR$" vcs="Git" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| using System; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Configuration{ |  | ||||||
|     static class Arguments{ |  | ||||||
|         // public args |  | ||||||
|         public const string ArgDataFolder = "-datafolder"; |  | ||||||
|         public const string ArgLogging = "-log"; |  | ||||||
| 
 |  | ||||||
|         // internal args |  | ||||||
|         public const string ArgRestart = "-restart"; |  | ||||||
|         public const string ArgImportCookies = "-importcookies"; |  | ||||||
|         public const string ArgDeleteCookies = "-deletecookies"; |  | ||||||
|         public const string ArgUpdated = "-updated"; |  | ||||||
| 
 |  | ||||||
|         // class data and methods |  | ||||||
|         private static readonly CommandLineArgs Current = CommandLineArgs.FromStringArray('-', Environment.GetCommandLineArgs()); |  | ||||||
| 
 |  | ||||||
|         public static bool HasFlag(string flag){ |  | ||||||
|             return Current.HasFlag(flag); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GetValue(string key, string defaultValue){ |  | ||||||
|             return Current.GetValue(key, defaultValue); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static CommandLineArgs GetCurrentClean(){ |  | ||||||
|             CommandLineArgs args = Current.Clone(); |  | ||||||
|             args.RemoveFlag(ArgRestart); |  | ||||||
|             args.RemoveFlag(ArgImportCookies); |  | ||||||
|             args.RemoveFlag(ArgDeleteCookies); |  | ||||||
|             args.RemoveFlag(ArgUpdated); |  | ||||||
|             return args; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static CommandLineArgs GetCurrentForInstaller(){ |  | ||||||
|             CommandLineArgs args = GetCurrentClean(); |  | ||||||
|             args.AddFlag(ArgUpdated); |  | ||||||
|             return args; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GetCurrentForInstallerCmd(){ |  | ||||||
|             return GetCurrentForInstaller().ToString().Replace("\"", "::"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,183 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.ComponentModel; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.IO; |  | ||||||
| using System.Threading; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Configuration{ |  | ||||||
|     sealed class LockManager{ |  | ||||||
|         private const int RetryDelay = 250; |  | ||||||
| 
 |  | ||||||
|         public enum Result{ |  | ||||||
|             Success, HasProcess, Fail |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly string file; |  | ||||||
|         private FileStream lockStream; |  | ||||||
|         private Process lockingProcess; |  | ||||||
| 
 |  | ||||||
|         public LockManager(string file){ |  | ||||||
|             this.file = file; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Lock file |  | ||||||
| 
 |  | ||||||
|         private bool ReleaseLockFileStream(){ |  | ||||||
|             if (lockStream != null){ |  | ||||||
|                 lockStream.Dispose(); |  | ||||||
|                 lockStream = null; |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private Result TryCreateLockFile(){ |  | ||||||
|             void CreateLockFileStream(){ |  | ||||||
|                 lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read); |  | ||||||
|                 lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int)); |  | ||||||
|                 lockStream.Flush(true); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 CreateLockFileStream(); |  | ||||||
|                 return Result.Success; |  | ||||||
|             }catch(DirectoryNotFoundException){ |  | ||||||
|                 try{ |  | ||||||
|                     CreateLockFileStream(); |  | ||||||
|                     return Result.Success; |  | ||||||
|                 }catch{ |  | ||||||
|                     ReleaseLockFileStream(); |  | ||||||
|                     return Result.Fail; |  | ||||||
|                 } |  | ||||||
|             }catch(IOException){ |  | ||||||
|                 return Result.HasProcess; |  | ||||||
|             }catch{ |  | ||||||
|                 ReleaseLockFileStream(); |  | ||||||
|                 return Result.Fail; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Lock management |  | ||||||
| 
 |  | ||||||
|         public Result Lock(){ |  | ||||||
|             if (lockStream != null){ |  | ||||||
|                 return Result.Success; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Result initialResult = TryCreateLockFile(); |  | ||||||
| 
 |  | ||||||
|             if (initialResult == Result.HasProcess){ |  | ||||||
|                 try{ |  | ||||||
|                     int pid; |  | ||||||
| 
 |  | ||||||
|                     using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ |  | ||||||
|                         byte[] bytes = new byte[sizeof(int)]; |  | ||||||
|                         fileStream.Read(bytes, 0, bytes.Length); |  | ||||||
|                         pid = BitConverter.ToInt32(bytes, 0); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     try{ |  | ||||||
|                         Process foundProcess = Process.GetProcessById(pid); |  | ||||||
| 
 |  | ||||||
|                         using(Process currentProcess = Process.GetCurrentProcess()){ |  | ||||||
|                             if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){ |  | ||||||
|                                 lockingProcess = foundProcess; |  | ||||||
|                             } |  | ||||||
|                             else{ |  | ||||||
|                                 foundProcess.Close(); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     }catch{ |  | ||||||
|                         // GetProcessById throws ArgumentException if the process is missing |  | ||||||
|                         // Process.MainModule can throw exceptions in some cases |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     return lockingProcess == null ? Result.Fail : Result.HasProcess; |  | ||||||
|                 }catch{ |  | ||||||
|                     return Result.Fail; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return initialResult; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public Result LockWait(int timeout){ |  | ||||||
|             for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){ |  | ||||||
|                 Result result = Lock(); |  | ||||||
| 
 |  | ||||||
|                 if (result == Result.HasProcess){ |  | ||||||
|                     Thread.Sleep(RetryDelay); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     return result; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return Lock(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool Unlock(){ |  | ||||||
|             if (ReleaseLockFileStream()){ |  | ||||||
|                 try{ |  | ||||||
|                     File.Delete(file); |  | ||||||
|                 }catch(Exception e){ |  | ||||||
|                     Program.Reporter.Log(e.ToString()); |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Locking process |  | ||||||
| 
 |  | ||||||
|         public bool RestoreLockingProcess(int failTimeout){ |  | ||||||
|             if (lockingProcess != null && lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray |  | ||||||
|                 NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0); |  | ||||||
| 
 |  | ||||||
|                 if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){ |  | ||||||
|                     return true; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool CloseLockingProcess(int closeTimeout, int killTimeout){ |  | ||||||
|             if (lockingProcess != null){ |  | ||||||
|                 try{ |  | ||||||
|                     if (lockingProcess.CloseMainWindow()){ |  | ||||||
|                         WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (!lockingProcess.HasExited){ |  | ||||||
|                         lockingProcess.Kill(); |  | ||||||
|                         WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (lockingProcess.HasExited){ |  | ||||||
|                         lockingProcess.Dispose(); |  | ||||||
|                         lockingProcess = null; |  | ||||||
|                         return true; |  | ||||||
|                     } |  | ||||||
|                 }catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){ |  | ||||||
|                     if (lockingProcess != null){ |  | ||||||
|                         bool hasExited = CheckLockingProcessExited(); |  | ||||||
|                         lockingProcess.Dispose(); |  | ||||||
|                         return hasExited; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private bool CheckLockingProcessExited(){ |  | ||||||
|             lockingProcess.Refresh(); |  | ||||||
|             return lockingProcess.HasExited; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.IO; |  | ||||||
| using TweetDuck.Data.Serialization; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Configuration{ |  | ||||||
|     sealed class SystemConfig{ |  | ||||||
|         private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>(); |  | ||||||
| 
 |  | ||||||
|         public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) && |  | ||||||
|                                                                       File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll")); |  | ||||||
| 
 |  | ||||||
|         // CONFIGURATION DATA |  | ||||||
| 
 |  | ||||||
|         private bool _hardwareAcceleration = true; |  | ||||||
| 
 |  | ||||||
|         public bool ClearCacheAutomatically { get; set; } = true; |  | ||||||
|         public int ClearCacheThreshold      { get; set; } = 250; |  | ||||||
| 
 |  | ||||||
|         // SPECIAL PROPERTIES |  | ||||||
|          |  | ||||||
|         public bool HardwareAcceleration{ |  | ||||||
|             get => _hardwareAcceleration && IsHardwareAccelerationSupported; |  | ||||||
|             set => _hardwareAcceleration = value; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // END OF CONFIG |  | ||||||
| 
 |  | ||||||
|         private readonly string file; |  | ||||||
|          |  | ||||||
|         private SystemConfig(string file){ |  | ||||||
|             this.file = file; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Save(){ |  | ||||||
|             try{ |  | ||||||
|                 Serializer.Write(file, this); |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public static SystemConfig Load(string file){ |  | ||||||
|             SystemConfig config = new SystemConfig(file); |  | ||||||
|              |  | ||||||
|             try{ |  | ||||||
|                 Serializer.ReadIfExists(file, config); |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Configuration Error", "Could not open the system configuration file. If you continue, you will lose system specific configuration such as Hardware Acceleration.", true, e); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return config; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,238 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.IO; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| using TweetDuck.Data.Serialization; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Configuration{ |  | ||||||
|     sealed class UserConfig{ |  | ||||||
|         private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>(); |  | ||||||
| 
 |  | ||||||
|         static UserConfig(){ |  | ||||||
|             Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter); |  | ||||||
| 
 |  | ||||||
|             Serializer.RegisterTypeConverter(typeof(Point), new SingleTypeConverter<Point>{ |  | ||||||
|                 ConvertToString = value => $"{value.X} {value.Y}", |  | ||||||
|                 ConvertToObject = value => { |  | ||||||
|                     int[] elements = StringUtils.ParseInts(value, ' '); |  | ||||||
|                     return new Point(elements[0], elements[1]); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|              |  | ||||||
|             Serializer.RegisterTypeConverter(typeof(Size), new SingleTypeConverter<Size>{ |  | ||||||
|                 ConvertToString = value => $"{value.Width} {value.Height}", |  | ||||||
|                 ConvertToObject = value => { |  | ||||||
|                     int[] elements = StringUtils.ParseInts(value, ' '); |  | ||||||
|                     return new Size(elements[0], elements[1]); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         // CONFIGURATION DATA |  | ||||||
| 
 |  | ||||||
|         public bool FirstRun            { get; set; } = true; |  | ||||||
|         public bool AllowDataCollection { get; set; } = false; |  | ||||||
| 
 |  | ||||||
|         public WindowState BrowserWindow { get; set; } = new WindowState(); |  | ||||||
|         public Size PluginsWindowSize    { get; set; } = Size.Empty; |  | ||||||
| 
 |  | ||||||
|         public bool ExpandLinksOnHover        { get; set; } = true; |  | ||||||
|         public bool OpenSearchInFirstColumn   { get; set; } = true; |  | ||||||
|         public bool KeepLikeFollowDialogsOpen { get; set; } = true; |  | ||||||
|         public bool BestImageQuality          { get; set; } = true; |  | ||||||
|         public bool EnableAnimatedImages      { get; set; } = true; |  | ||||||
| 
 |  | ||||||
|         public bool IgnoreTrackingUrlWarning { get; set; } = false; |  | ||||||
|         public bool EnableSmoothScrolling    { get; set; } = true; |  | ||||||
|         public string BrowserPath            { get; set; } = null; |  | ||||||
|         public string SearchEngineUrl        { get; set; } = null; |  | ||||||
|         private int _zoomLevel                             = 100; |  | ||||||
|         private bool _muteNotifications; |  | ||||||
| 
 |  | ||||||
|         public int VideoPlayerVolume { get; set; } = 50; |  | ||||||
|          |  | ||||||
|         public bool EnableSpellCheck     { get; set; } = false; |  | ||||||
|         public string SpellCheckLanguage { get; set; } = "en-US"; |  | ||||||
|         public string TranslationTarget  { get; set; } = "en"; |  | ||||||
|          |  | ||||||
|         private TrayIcon.Behavior _trayBehavior       = TrayIcon.Behavior.Disabled; |  | ||||||
|         public bool EnableTrayHighlight { get; set; } = true; |  | ||||||
| 
 |  | ||||||
|         public bool EnableUpdateCheck { get; set; } = true; |  | ||||||
|         public string DismissedUpdate { get; set; } = null; |  | ||||||
| 
 |  | ||||||
|         public bool DisplayNotificationColumn    { get; set; } = false; |  | ||||||
|         public bool NotificationMediaPreviews    { get; set; } = true; |  | ||||||
|         public bool NotificationSkipOnLinkClick  { get; set; } = false; |  | ||||||
|         public bool NotificationNonIntrusiveMode { get; set; } = true; |  | ||||||
|         public int NotificationIdlePauseSeconds  { get; set; } = 0; |  | ||||||
| 
 |  | ||||||
|         public bool DisplayNotificationTimer     { get; set; } = true; |  | ||||||
|         public bool NotificationTimerCountDown   { get; set; } = false; |  | ||||||
|         public int NotificationDurationValue     { get; set; } = 25; |  | ||||||
| 
 |  | ||||||
|         public TweetNotification.Position NotificationPosition { get; set; } = TweetNotification.Position.TopRight; |  | ||||||
|         public Point CustomNotificationPosition                { get; set; } = ControlExtensions.InvisibleLocation; |  | ||||||
|         public int NotificationDisplay                         { get; set; } = 0; |  | ||||||
|         public int NotificationEdgeDistance                    { get; set; } = 8; |  | ||||||
| 
 |  | ||||||
|         public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto; |  | ||||||
|         public Size CustomNotificationSize             { get; set; } = Size.Empty; |  | ||||||
|         public int NotificationScrollSpeed             { get; set; } = 100; |  | ||||||
| 
 |  | ||||||
|         private string _notificationSoundPath; |  | ||||||
|         private int _notificationSoundVolume = 100; |  | ||||||
| 
 |  | ||||||
|         public string CustomCefArgs         { get; set; } = null; |  | ||||||
|         public string CustomBrowserCSS      { get; set; } = null; |  | ||||||
|         public string CustomNotificationCSS { get; set; } = null; |  | ||||||
|          |  | ||||||
|         // SPECIAL PROPERTIES |  | ||||||
| 
 |  | ||||||
|         public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation; |  | ||||||
|         public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty; |  | ||||||
|         public bool IsCustomSoundNotificationSet => NotificationSoundPath != string.Empty; |  | ||||||
| 
 |  | ||||||
|         public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default; |  | ||||||
|          |  | ||||||
|         public string NotificationSoundPath{ |  | ||||||
|             get => _notificationSoundPath ?? string.Empty; |  | ||||||
|             set => UpdatePropertyWithEvent(ref _notificationSoundPath, value, SoundNotificationChanged); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public int NotificationSoundVolume{ |  | ||||||
|             get => _notificationSoundVolume; |  | ||||||
|             set => UpdatePropertyWithEvent(ref _notificationSoundVolume, value, SoundNotificationChanged); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool MuteNotifications{ |  | ||||||
|             get => _muteNotifications; |  | ||||||
|             set => UpdatePropertyWithEvent(ref _muteNotifications, value, MuteToggled); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public int ZoomLevel{ |  | ||||||
|             get => _zoomLevel; |  | ||||||
|             set => UpdatePropertyWithEvent(ref _zoomLevel, value, ZoomLevelChanged); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public TrayIcon.Behavior TrayBehavior{ |  | ||||||
|             get => _trayBehavior; |  | ||||||
|             set => UpdatePropertyWithEvent(ref _trayBehavior, value, TrayBehaviorChanged); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // EVENTS |  | ||||||
|          |  | ||||||
|         public event EventHandler MuteToggled; |  | ||||||
|         public event EventHandler ZoomLevelChanged; |  | ||||||
|         public event EventHandler TrayBehaviorChanged; |  | ||||||
|         public event EventHandler SoundNotificationChanged; |  | ||||||
| 
 |  | ||||||
|         // END OF CONFIG |  | ||||||
|          |  | ||||||
|         private readonly string file; |  | ||||||
| 
 |  | ||||||
|         private UserConfig(string file){ |  | ||||||
|             this.file = file; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void UpdatePropertyWithEvent<T>(ref T field, T value, EventHandler eventHandler){ |  | ||||||
|             if (!EqualityComparer<T>.Default.Equals(field, value)){ |  | ||||||
|                 field = value; |  | ||||||
|                 eventHandler?.Invoke(this, EventArgs.Empty); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Save(){ |  | ||||||
|             try{ |  | ||||||
|                 if (File.Exists(file)){ |  | ||||||
|                     string backupFile = GetBackupFile(file); |  | ||||||
|                     File.Delete(backupFile); |  | ||||||
|                     File.Move(file, backupFile); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 Serializer.Write(file, this); |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Reload(){ |  | ||||||
|             try{ |  | ||||||
|                 LoadInternal(false); |  | ||||||
|             }catch(FileNotFoundException){ |  | ||||||
|                 try{ |  | ||||||
|                     Serializer.Write(file, new UserConfig(file)); |  | ||||||
|                     LoadInternal(false); |  | ||||||
|                 }catch(Exception e){ |  | ||||||
|                     Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e); |  | ||||||
|                 } |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Reset(){ |  | ||||||
|             try{ |  | ||||||
|                 File.Delete(file); |  | ||||||
|                 File.Delete(GetBackupFile(file)); |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Configuration Error", "Could not delete configuration files to reset the options.", true, e); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             Reload(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void LoadInternal(bool backup){ |  | ||||||
|             Serializer.Read(backup ? GetBackupFile(file) : file, this); |  | ||||||
| 
 |  | ||||||
|             if (NotificationScrollSpeed == 10){ // incorrect initial value |  | ||||||
|                 NotificationScrollSpeed = 100; |  | ||||||
|                 Save(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public static UserConfig Load(string file){ |  | ||||||
|             Exception firstException = null; |  | ||||||
| 
 |  | ||||||
|             for(int attempt = 0; attempt < 2; attempt++){ |  | ||||||
|                 try{ |  | ||||||
|                     UserConfig config = new UserConfig(file); |  | ||||||
|                     config.LoadInternal(attempt > 0); |  | ||||||
|                     return config; |  | ||||||
|                 }catch(FileNotFoundException){ |  | ||||||
|                 }catch(DirectoryNotFoundException){ |  | ||||||
|                     break; |  | ||||||
|                 }catch(Exception e){ |  | ||||||
|                     if (attempt == 0){ |  | ||||||
|                         firstException = e; |  | ||||||
|                         Program.Reporter.Log(e.ToString()); |  | ||||||
|                     } |  | ||||||
|                     else if (firstException is FormatException){ |  | ||||||
|                         Program.Reporter.HandleException("Configuration Error", "The configuration file is outdated or corrupted. If you continue, your program options will be reset.", true, e); |  | ||||||
|                         return new UserConfig(file); |  | ||||||
|                     } |  | ||||||
|                     else if (firstException != null){ |  | ||||||
|                         Program.Reporter.HandleException("Configuration Error", "Could not open the backup configuration file. If you continue, your program options will be reset.", true, e); |  | ||||||
|                         return new UserConfig(file); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (firstException != null){ |  | ||||||
|                 Program.Reporter.HandleException("Configuration Error", "Could not open the configuration file.", true, firstException); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return new UserConfig(file); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GetBackupFile(string file){ |  | ||||||
|             return file+".bak"; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| using System.Text; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Bridge{ |  | ||||||
|     static class PropertyBridge{ |  | ||||||
|         public enum Environment{ |  | ||||||
|             Browser, Notification |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GenerateScript(Environment environment){ |  | ||||||
|             string Bool(bool value) => value ? "true;" : "false;"; |  | ||||||
|             string Str(string value) => '"'+value+"\";"; |  | ||||||
| 
 |  | ||||||
|             StringBuilder build = new StringBuilder().Append("(function(x){"); |  | ||||||
| 
 |  | ||||||
|             build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover)); |  | ||||||
|              |  | ||||||
|             if (environment == Environment.Browser){ |  | ||||||
|                 build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn)); |  | ||||||
|                 build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(Program.UserConfig.KeepLikeFollowDialogsOpen)); |  | ||||||
|                 build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications)); |  | ||||||
|                 build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews)); |  | ||||||
|                 build.Append("x.translationTarget=").Append(Str(Program.UserConfig.TranslationTarget)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (environment == Environment.Notification){ |  | ||||||
|                 build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick)); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             return build.Append("})(window.$TDX=window.$TDX||{})").ToString(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,160 +0,0 @@ | |||||||
| using System.Collections.Generic; |  | ||||||
| using System.Text; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Management; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Bridge{ |  | ||||||
|     class TweetDeckBridge{ |  | ||||||
|         public static string FontSize { get; private set; } |  | ||||||
|         public static string NotificationHeadLayout { get; private set; } |  | ||||||
|         public static readonly ContextInfo ContextInfo = new ContextInfo(); |  | ||||||
| 
 |  | ||||||
|         private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2); |  | ||||||
| 
 |  | ||||||
|         public static void ResetStaticProperties(){ |  | ||||||
|             FontSize = NotificationHeadLayout = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void RestoreSessionData(IFrame frame){ |  | ||||||
|             if (SessionData.Count > 0){ |  | ||||||
|                 StringBuilder build = new StringBuilder().Append("window.TD_SESSION={"); |  | ||||||
|                  |  | ||||||
|                 foreach(KeyValuePair<string, string> kvp in SessionData){ |  | ||||||
|                     build.Append(kvp.Key).Append(":'").Append(kvp.Value.Replace("'", "\\'")).Append("',"); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 ScriptLoader.ExecuteScript(frame, build.Append("}").ToString(), "gen:session"); |  | ||||||
|                 SessionData.Clear(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly FormBrowser form; |  | ||||||
|         private readonly FormNotificationMain notification; |  | ||||||
| 
 |  | ||||||
|         protected TweetDeckBridge(FormBrowser form, FormNotificationMain notification){ |  | ||||||
|             this.form = form; |  | ||||||
|             this.notification = notification; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Browser only |  | ||||||
| 
 |  | ||||||
|         public sealed class Browser : TweetDeckBridge{ |  | ||||||
|             public Browser(FormBrowser form, FormNotificationMain notification) : base(form, notification){} |  | ||||||
| 
 |  | ||||||
|             public void OpenContextMenu(){ |  | ||||||
|                 form.InvokeAsyncSafe(form.OpenContextMenu); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){ |  | ||||||
|                 form.InvokeAsyncSafe(() => { |  | ||||||
|                     form.OnIntroductionClosed(showGuide, allowDataCollection); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void LoadNotificationLayout(string fontSize, string headLayout){ |  | ||||||
|                 form.InvokeAsyncSafe(() => { |  | ||||||
|                     FontSize = fontSize; |  | ||||||
|                     NotificationHeadLayout = headLayout; |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ |  | ||||||
|                 ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void DisplayTooltip(string text){ |  | ||||||
|                 form.InvokeAsyncSafe(() => form.DisplayTooltip(text)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void SetSessionData(string key, string value){ |  | ||||||
|                 form.InvokeSafe(() => { // do not use InvokeAsyncSafe, return only after invocation |  | ||||||
|                     SessionData.Add(key, value); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Notification only |  | ||||||
| 
 |  | ||||||
|         public sealed class Notification : TweetDeckBridge{ |  | ||||||
|             public Notification(FormBrowser form, FormNotificationMain notification) : base(form, notification){} |  | ||||||
| 
 |  | ||||||
|             public void DisplayTooltip(string text){ |  | ||||||
|                 notification.InvokeAsyncSafe(() => notification.DisplayTooltip(text)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void LoadNextNotification(){ |  | ||||||
|                 notification.InvokeAsyncSafe(notification.FinishCurrentNotification); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void ShowTweetDetail(){ |  | ||||||
|                 notification.InvokeAsyncSafe(notification.ShowTweetDetail); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Global |  | ||||||
| 
 |  | ||||||
|         public void SetLastRightClickInfo(string type, string url){ |  | ||||||
|             ContextInfo.SetLink(type, url); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){ |  | ||||||
|             notification.InvokeAsyncSafe(() => { |  | ||||||
|                 form.OnTweetNotification(); |  | ||||||
|                 notification.ShowNotification(new TweetNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl)); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnTweetSound(){ |  | ||||||
|             form.InvokeAsyncSafe(() => { |  | ||||||
|                 form.OnTweetNotification(); |  | ||||||
|                 form.OnTweetSound(); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ScreenshotTweet(string html, int width){ |  | ||||||
|             form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void PlayVideo(string url, string username){ |  | ||||||
|             form.InvokeAsyncSafe(() => form.PlayVideo(url, username)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void FixClipboard(){ |  | ||||||
|             form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OpenBrowser(string url){ |  | ||||||
|             form.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public int GetIdleSeconds(){ |  | ||||||
|             return NativeMethods.GetIdleSeconds(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Alert(string type, string contents){ |  | ||||||
|             MessageBoxIcon icon; |  | ||||||
| 
 |  | ||||||
|             switch(type){ |  | ||||||
|                 case "error": icon = MessageBoxIcon.Error; break; |  | ||||||
|                 case "warning": icon = MessageBoxIcon.Warning; break; |  | ||||||
|                 case "info": icon = MessageBoxIcon.Information; break; |  | ||||||
|                 default: icon = MessageBoxIcon.None; break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             FormMessage.Show("TweetDuck Browser Message", contents, icon, FormMessage.OK); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void CrashDebug(string message){ |  | ||||||
|             #if DEBUG |  | ||||||
|             System.Diagnostics.Debug.WriteLine(message); |  | ||||||
|             System.Diagnostics.Debugger.Break(); |  | ||||||
|             #endif |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,79 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Controls{ |  | ||||||
|     static class ControlExtensions{ |  | ||||||
|         public static readonly Point InvisibleLocation = new Point(-32000, -32000); |  | ||||||
| 
 |  | ||||||
|         public static void InvokeSafe(this Control control, Action func){ |  | ||||||
|             if (control.InvokeRequired){ |  | ||||||
|                 control.Invoke(func); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 func(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void InvokeAsyncSafe(this Control control, Action func){ |  | ||||||
|             control.BeginInvoke(func); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static float GetDPIScale(this Control control){ |  | ||||||
|             using(Graphics graphics = control.CreateGraphics()){ |  | ||||||
|                 return graphics.DpiY/96F; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool IsFullyOutsideView(this Form form){ |  | ||||||
|             return !Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(form.Bounds)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void MoveToCenter(this Form targetForm, Form parentForm){ |  | ||||||
|             targetForm.Location = new Point(parentForm.Location.X+parentForm.Width/2-targetForm.Width/2, parentForm.Location.Y+parentForm.Height/2-targetForm.Height/2); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetValueInstant(this ProgressBar bar, int value){ |  | ||||||
|             if (value == bar.Maximum){ |  | ||||||
|                 bar.Value = value; |  | ||||||
|                 bar.Value = value-1; |  | ||||||
|                 bar.Value = value; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 bar.Value = value+1; |  | ||||||
|                 bar.Value = value; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetValueSafe(this NumericUpDown numUpDown, int value){ |  | ||||||
|             if (value >= numUpDown.Minimum && value <= numUpDown.Maximum){ |  | ||||||
|                 numUpDown.Value = value; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetValueSafe(this TrackBar trackBar, int value){ |  | ||||||
|             if (value >= trackBar.Minimum && value <= trackBar.Maximum){ |  | ||||||
|                 trackBar.Value = value; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool AlignValueToTick(this TrackBar trackBar){ |  | ||||||
|             if (trackBar.Value % trackBar.SmallChange != 0){ |  | ||||||
|                 trackBar.Value = trackBar.SmallChange*(int)Math.Floor(((double)trackBar.Value/trackBar.SmallChange)+0.5); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|             else return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void EnableMultilineShortcuts(this TextBox textBox){ |  | ||||||
|             textBox.KeyDown += (sender, args) => { |  | ||||||
|                 if (args.Control && args.KeyCode == Keys.A){ |  | ||||||
|                     ((TextBox)sender).SelectAll(); |  | ||||||
|                     args.SuppressKeyPress = true; |  | ||||||
|                     args.Handled = true; |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Controls{ |  | ||||||
|     sealed class FlatButton : Button{ |  | ||||||
|         protected override bool ShowFocusCues => false; |  | ||||||
| 
 |  | ||||||
|         public FlatButton(){ |  | ||||||
|             GotFocus += FlatButton_GotFocus; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FlatButton_GotFocus(object sender, EventArgs e){ // removes extra border when focused |  | ||||||
|             NotifyDefault(false); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Controls{ |  | ||||||
|     sealed class FlatProgressBar : ProgressBar{ |  | ||||||
|         private readonly SolidBrush brush; |  | ||||||
| 
 |  | ||||||
|         public FlatProgressBar(){ |  | ||||||
|             brush = new SolidBrush(Color.White); |  | ||||||
| 
 |  | ||||||
|             SetStyle(ControlStyles.UserPaint, true); |  | ||||||
|             SetStyle(ControlStyles.OptimizedDoubleBuffer, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void SetValueInstant(int value){ |  | ||||||
|             ControlExtensions.SetValueInstant(this, Math.Max(Minimum, Math.Min(Maximum, value))); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void OnPaint(PaintEventArgs e){ |  | ||||||
|             if (brush.Color != ForeColor){ |  | ||||||
|                 brush.Color = ForeColor; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Rectangle rect = e.ClipRectangle; |  | ||||||
|             rect.Width = (int)(rect.Width*((double)Value/Maximum)); |  | ||||||
|             e.Graphics.FillRectangle(brush, rect); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void Dispose(bool disposing){ |  | ||||||
|             base.Dispose(disposing); |  | ||||||
| 
 |  | ||||||
|             if (disposing){ |  | ||||||
|                 brush.Dispose(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Controls{ |  | ||||||
|     sealed class LabelVertical : Label{ |  | ||||||
|         public int LineHeight { get; set; } |  | ||||||
| 
 |  | ||||||
|         protected override void OnPaint(PaintEventArgs e){ |  | ||||||
|             int y = (int)Math.Floor((ClientRectangle.Height-Text.Length*LineHeight)/2F)-1; |  | ||||||
| 
 |  | ||||||
|             using(Brush brush = new SolidBrush(ForeColor)){ |  | ||||||
|                 foreach(char chr in Text){ |  | ||||||
|                     string str = chr.ToString(); |  | ||||||
|                     float x = (ClientRectangle.Width-e.Graphics.MeasureString(str, Font).Width)/2F; |  | ||||||
| 
 |  | ||||||
|                     e.Graphics.DrawString(str, Font, brush, x, y); |  | ||||||
|                     y += LineHeight; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| using System.ComponentModel; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Controls{ |  | ||||||
|     sealed class NumericUpDownEx : NumericUpDown{ |  | ||||||
|         public string TextSuffix { get; set ; } |  | ||||||
| 
 |  | ||||||
|         protected override void UpdateEditText(){ |  | ||||||
|             base.UpdateEditText(); |  | ||||||
| 
 |  | ||||||
|             if (LicenseManager.UsageMode != LicenseUsageMode.Designtime){ |  | ||||||
|                 ChangingText = true; |  | ||||||
|                 Text += TextSuffix; |  | ||||||
|                 ChangingText = false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,520 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Configuration; |  | ||||||
| using TweetDuck.Core.Bridge; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Handling; |  | ||||||
| using TweetDuck.Core.Management; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| using TweetDuck.Core.Notification.Screenshot; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Other.Analytics; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Plugins.Enums; |  | ||||||
| using TweetDuck.Plugins.Events; |  | ||||||
| using TweetDuck.Updates; |  | ||||||
| using TweetDuck.Updates.Events; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core{ |  | ||||||
|     sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{ |  | ||||||
|         private static UserConfig Config => Program.UserConfig; |  | ||||||
| 
 |  | ||||||
|         public bool IsWaiting{ |  | ||||||
|             set{ |  | ||||||
|                 if (value){ |  | ||||||
|                     browser.Enabled = false; |  | ||||||
|                     Cursor = Cursors.WaitCursor; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     browser.Enabled = true; |  | ||||||
|                     Cursor = Cursors.Default; |  | ||||||
| 
 |  | ||||||
|                     if (Focused){ // re-focus browser only if the window or a child is activated |  | ||||||
|                         browser.Focus(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public string UpdateInstallerPath { get; private set; } |  | ||||||
|         private bool ignoreUpdateCheckError; |  | ||||||
|          |  | ||||||
|         public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy; |  | ||||||
| 
 |  | ||||||
|         private readonly TweetDeckBrowser browser; |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
|         private readonly UpdateHandler updates; |  | ||||||
|         private readonly FormNotificationTweet notification; |  | ||||||
|         private readonly ContextMenu contextMenu; |  | ||||||
| 
 |  | ||||||
|         private bool isLoaded; |  | ||||||
|         private FormWindowState prevState; |  | ||||||
|          |  | ||||||
|         private TweetScreenshotManager notificationScreenshotManager; |  | ||||||
|         private VideoPlayer videoPlayer; |  | ||||||
|         private AnalyticsManager analytics; |  | ||||||
| 
 |  | ||||||
|         public FormBrowser(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             Text = Program.BrandName; |  | ||||||
| 
 |  | ||||||
|             this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath); |  | ||||||
|             this.plugins.Reloaded += plugins_Reloaded; |  | ||||||
|             this.plugins.Executed += plugins_Executed; |  | ||||||
|             this.plugins.Reload(); |  | ||||||
| 
 |  | ||||||
|             this.notification = new FormNotificationTweet(this, plugins); |  | ||||||
|             this.notification.Show(); |  | ||||||
|              |  | ||||||
|             this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification)); |  | ||||||
|             this.contextMenu = ContextMenuBrowser.CreateMenu(this); |  | ||||||
| 
 |  | ||||||
|             this.plugins.Register(browser, PluginEnvironment.Browser, true); |  | ||||||
| 
 |  | ||||||
|             Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update |  | ||||||
| 
 |  | ||||||
|             Disposed += (sender, args) => { |  | ||||||
|                 Config.MuteToggled -= Config_MuteToggled; |  | ||||||
|                 Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged; |  | ||||||
|                  |  | ||||||
|                 browser.Dispose(); |  | ||||||
|                 updates.Dispose(); |  | ||||||
|                 contextMenu.Dispose(); |  | ||||||
| 
 |  | ||||||
|                 notificationScreenshotManager?.Dispose(); |  | ||||||
|                 videoPlayer?.Dispose(); |  | ||||||
|                 analytics?.Dispose(); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             Config.MuteToggled += Config_MuteToggled; |  | ||||||
| 
 |  | ||||||
|             this.trayIcon.ClickRestore += trayIcon_ClickRestore; |  | ||||||
|             this.trayIcon.ClickClose += trayIcon_ClickClose; |  | ||||||
|             Config.TrayBehaviorChanged += Config_TrayBehaviorChanged; |  | ||||||
| 
 |  | ||||||
|             UpdateTrayIcon(); |  | ||||||
| 
 |  | ||||||
|             this.updates = new UpdateHandler(browser, Program.InstallerPath); |  | ||||||
|             this.updates.CheckFinished += updates_CheckFinished; |  | ||||||
|             this.updates.UpdateAccepted += updates_UpdateAccepted; |  | ||||||
|             this.updates.UpdateDismissed += updates_UpdateDismissed; |  | ||||||
| 
 |  | ||||||
|             if (Config.AllowDataCollection){ |  | ||||||
|                 analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             RestoreWindow(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void ShowChildForm(Form form){ |  | ||||||
|             form.VisibleChanged += (sender, args) => form.MoveToCenter(this); |  | ||||||
|             form.Show(this); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ForceClose(){ |  | ||||||
|             trayIcon.Visible = false; // checked in FormClosing event |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // window setup |  | ||||||
| 
 |  | ||||||
|         private void RestoreWindow(){ |  | ||||||
|             Config.BrowserWindow.Restore(this, true); |  | ||||||
|             prevState = WindowState; |  | ||||||
|             isLoaded = true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void UpdateTrayIcon(){ |  | ||||||
|             trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // event handlers |  | ||||||
| 
 |  | ||||||
|         private void timerResize_Tick(object sender, EventArgs e){ |  | ||||||
|             FormBrowser_ResizeEnd(this, e); // also stops timer |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormBrowser_Activated(object sender, EventArgs e){ |  | ||||||
|             if (!isLoaded)return; |  | ||||||
| 
 |  | ||||||
|             trayIcon.HasNotifications = false; |  | ||||||
| 
 |  | ||||||
|             if (!browser.Enabled){      // when taking a screenshot, the window is unfocused and |  | ||||||
|                 browser.Enabled = true; // the browser is disabled; if the user clicks back into |  | ||||||
|             }                           // the window, enable the browser again |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormBrowser_LocationChanged(object sender, EventArgs e){ |  | ||||||
|             if (!isLoaded)return; |  | ||||||
|              |  | ||||||
|             timerResize.Stop(); |  | ||||||
|             timerResize.Start(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormBrowser_Resize(object sender, EventArgs e){ |  | ||||||
|             if (!isLoaded)return; |  | ||||||
| 
 |  | ||||||
|             if (WindowState != prevState){ |  | ||||||
|                 prevState = WindowState; |  | ||||||
| 
 |  | ||||||
|                 if (WindowState == FormWindowState.Minimized){ |  | ||||||
|                     if (Config.TrayBehavior.ShouldHideOnMinimize()){ |  | ||||||
|                         Hide(); // hides taskbar too?! welp that works I guess |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     FormBrowser_ResizeEnd(sender, e); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 timerResize.Stop(); |  | ||||||
|                 timerResize.Start(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves |  | ||||||
|             if (!isLoaded)return; |  | ||||||
| 
 |  | ||||||
|             timerResize.Stop(); |  | ||||||
|              |  | ||||||
|             if (Location != ControlExtensions.InvisibleLocation){ |  | ||||||
|                 Config.BrowserWindow.Save(this); |  | ||||||
|                 Config.Save(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormBrowser_FormClosing(object sender, FormClosingEventArgs e){ |  | ||||||
|             if (!isLoaded)return; |  | ||||||
| 
 |  | ||||||
|             if (Config.TrayBehavior.ShouldHideOnClose() && trayIcon.Visible && e.CloseReason == CloseReason.UserClosing){ |  | ||||||
|                 Hide(); // hides taskbar too?! welp that works I guess |  | ||||||
|                 e.Cancel = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormBrowser_FormClosed(object sender, FormClosedEventArgs e){ |  | ||||||
|             if (isLoaded && UpdateInstallerPath == null){ |  | ||||||
|                 updates.CleanupDownload(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Config_MuteToggled(object sender, EventArgs e){ |  | ||||||
|             AnalyticsFile.NotificationMutes.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Config_TrayBehaviorChanged(object sender, EventArgs e){ |  | ||||||
|             UpdateTrayIcon(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void trayIcon_ClickRestore(object sender, EventArgs e){ |  | ||||||
|             Show(); |  | ||||||
|             RestoreWindow(); |  | ||||||
|             Activate(); |  | ||||||
|             UpdateTrayIcon(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void trayIcon_ClickClose(object sender, EventArgs e){ |  | ||||||
|             ForceClose(); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         private void plugins_Reloaded(object sender, PluginErrorEventArgs e){ |  | ||||||
|             if (e.HasErrors){ |  | ||||||
|                 FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (isLoaded){ |  | ||||||
|                 browser.ReloadToTweetDeck(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         private static void plugins_Executed(object sender, PluginErrorEventArgs e){ |  | ||||||
|             if (e.HasErrors){ |  | ||||||
|                 FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ |  | ||||||
|             e.Result.Handle(update => { |  | ||||||
|                 string tag = update.VersionTag; |  | ||||||
| 
 |  | ||||||
|                 if (tag != Program.VersionTag && tag != Config.DismissedUpdate){ |  | ||||||
|                     updates.PrepareUpdate(update); |  | ||||||
|                     browser.ShowUpdateNotification(tag, update.ReleaseNotes); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     updates.StartTimer(); |  | ||||||
|                 } |  | ||||||
|             }, ex => { |  | ||||||
|                 if (!ignoreUpdateCheckError){ |  | ||||||
|                     Program.Reporter.HandleException("Update Check Error", "An error occurred while checking for updates.", true, ex); |  | ||||||
|                     updates.StartTimer(); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             ignoreUpdateCheckError = true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void updates_UpdateAccepted(object sender, UpdateEventArgs e){ |  | ||||||
|             this.InvokeAsyncSafe(() => { |  | ||||||
|                 FormManager.CloseAllDialogs(); |  | ||||||
| 
 |  | ||||||
|                 if (!string.IsNullOrEmpty(Config.DismissedUpdate)){ |  | ||||||
|                     Config.DismissedUpdate = null; |  | ||||||
|                     Config.Save(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 updates.BeginUpdateDownload(this, e.UpdateInfo, update => { |  | ||||||
|                     UpdateDownloadStatus status = update.DownloadStatus; |  | ||||||
| 
 |  | ||||||
|                     if (status == UpdateDownloadStatus.Done){ |  | ||||||
|                         UpdateInstallerPath = update.InstallerPath; |  | ||||||
|                         ForceClose(); |  | ||||||
|                     } |  | ||||||
|                     else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: "+(update.DownloadError?.Message ?? "unknown error")+"\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                         BrowserUtils.OpenExternalBrowser(Program.Website); |  | ||||||
|                         ForceClose(); |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         Show(); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void updates_UpdateDismissed(object sender, UpdateEventArgs e){ |  | ||||||
|             this.InvokeAsyncSafe(() => { |  | ||||||
|                 Config.DismissedUpdate = e.UpdateInfo.VersionTag; |  | ||||||
|                 Config.Save(); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void WndProc(ref Message m){ |  | ||||||
|             if (isLoaded && m.Msg == Program.WindowRestoreMessage){ |  | ||||||
|                 if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){ |  | ||||||
|                     trayIcon_ClickRestore(trayIcon, EventArgs.Empty); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             if (browser.Ready && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){ |  | ||||||
|                 if (videoPlayer != null && videoPlayer.Running){ |  | ||||||
|                     videoPlayer.Close(); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     browser.OnMouseClickExtra(m.WParam); |  | ||||||
|                     AnalyticsFile.BrowserExtraMouseButtons.Trigger(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             base.WndProc(ref m); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // bridge methods |  | ||||||
| 
 |  | ||||||
|         public void PauseNotification(){ |  | ||||||
|             notification.PauseNotification(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ResumeNotification(){ |  | ||||||
|             notification.ResumeNotification(); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public void ReinjectCustomCSS(string css){ |  | ||||||
|             browser.ReinjectCustomCSS(css); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ReloadToTweetDeck(){ |  | ||||||
|             #if DEBUG |  | ||||||
|             Resources.ScriptLoader.HotSwap(); |  | ||||||
|             #endif |  | ||||||
| 
 |  | ||||||
|             ignoreUpdateCheckError = false; |  | ||||||
|             browser.ReloadToTweetDeck(); |  | ||||||
|             AnalyticsFile.BrowserReloads.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void AddSearchColumn(string query){ |  | ||||||
|             browser.AddSearchColumn(query); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void TriggerTweetScreenshot(){ |  | ||||||
|             browser.TriggerTweetScreenshot(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ReloadColumns(){ |  | ||||||
|             browser.ReloadColumns(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void PlaySoundNotification(){ |  | ||||||
|             browser.PlaySoundNotification(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ApplyROT13(){ |  | ||||||
|             browser.ApplyROT13(); |  | ||||||
|             AnalyticsFile.UsedROT13.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // callback handlers |  | ||||||
|          |  | ||||||
|         public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){ |  | ||||||
|             if (Config.FirstRun){ |  | ||||||
|                 Config.FirstRun = false; |  | ||||||
|                 Config.AllowDataCollection = allowDataCollection; |  | ||||||
|                 Config.Save(); |  | ||||||
| 
 |  | ||||||
|                 if (allowDataCollection && analytics == null){ |  | ||||||
|                     analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (showGuide){ |  | ||||||
|                 FormGuide.Show(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OpenContextMenu(){ |  | ||||||
|             contextMenu.Show(this, PointToClient(Cursor.Position)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OpenSettings(){ |  | ||||||
|             OpenSettings(null); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OpenSettings(Type startTab){ |  | ||||||
|             if (!FormManager.TryBringToFront<FormSettings>()){ |  | ||||||
|                 bool prevEnableUpdateCheck = Config.EnableUpdateCheck; |  | ||||||
| 
 |  | ||||||
|                 FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab); |  | ||||||
| 
 |  | ||||||
|                 form.FormClosed += (sender, args) => { |  | ||||||
|                     if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){ |  | ||||||
|                         Config.DismissedUpdate = null; |  | ||||||
|                         Config.Save(); |  | ||||||
|                          |  | ||||||
|                         updates.Check(true); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (!Config.EnableTrayHighlight){ |  | ||||||
|                         trayIcon.HasNotifications = false; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (Config.AllowDataCollection){ |  | ||||||
|                         if (analytics == null){ |  | ||||||
|                             analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     else if (analytics != null){ |  | ||||||
|                         analytics.Dispose(); |  | ||||||
|                         analytics = null; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     BrowserCache.RefreshTimer(); |  | ||||||
|                      |  | ||||||
|                     if (form.ShouldReloadBrowser){ |  | ||||||
|                         FormManager.TryFind<FormPlugins>()?.Close(); |  | ||||||
|                         plugins.Reload(); // also reloads the browser |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         browser.UpdateProperties(); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     notification.RequiresResize = true; |  | ||||||
|                     form.Dispose(); |  | ||||||
|                 }; |  | ||||||
|                  |  | ||||||
|                 AnalyticsFile.OpenOptions.Trigger(); |  | ||||||
|                 ShowChildForm(form); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OpenAbout(){ |  | ||||||
|             if (!FormManager.TryBringToFront<FormAbout>()){ |  | ||||||
|                 AnalyticsFile.OpenAbout.Trigger(); |  | ||||||
|                 ShowChildForm(new FormAbout()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OpenPlugins(){ |  | ||||||
|             if (!FormManager.TryBringToFront<FormPlugins>()){ |  | ||||||
|                 AnalyticsFile.OpenPlugins.Trigger(); |  | ||||||
|                 ShowChildForm(new FormPlugins(plugins)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnTweetNotification(){ // may be called multiple times, once for each type of notification |  | ||||||
|             if (Config.EnableTrayHighlight && !ContainsFocus){ |  | ||||||
|                 trayIcon.HasNotifications = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnTweetSound(){ |  | ||||||
|             AnalyticsFile.SoundNotifications.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void PlayVideo(string url, string username){ |  | ||||||
|             if (string.IsNullOrEmpty(url)){ |  | ||||||
|                 videoPlayer?.Close(); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (videoPlayer == null){ |  | ||||||
|                 videoPlayer = new VideoPlayer(this); |  | ||||||
| 
 |  | ||||||
|                 videoPlayer.ProcessExited += (sender, args) => { |  | ||||||
|                     browser.HideVideoOverlay(true); |  | ||||||
|                 }; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             videoPlayer.Launch(url, username); |  | ||||||
|             AnalyticsFile.VideoPlays.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool ProcessBrowserKey(Keys key){ |  | ||||||
|             if (videoPlayer != null && videoPlayer.Running){ |  | ||||||
|                 videoPlayer.SendKeyEvent(key); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){ |  | ||||||
|             Activate(); |  | ||||||
| 
 |  | ||||||
|             if (!browser.IsTweetDeckWebsite){ |  | ||||||
|                 FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             notification.FinishCurrentNotification(); |  | ||||||
|             browser.ShowTweetDetail(columnId, chirpId, fallbackUrl); |  | ||||||
|             AnalyticsFile.TweetDetails.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnTweetScreenshotReady(string html, int width){ |  | ||||||
|             if (notificationScreenshotManager == null){ |  | ||||||
|                 notificationScreenshotManager = new TweetScreenshotManager(this, plugins); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             notificationScreenshotManager.Trigger(html, width); |  | ||||||
|             AnalyticsFile.TweetScreenshots.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void DisplayTooltip(string text){ |  | ||||||
|             if (string.IsNullOrEmpty(text)){ |  | ||||||
|                 toolTip.Hide(this); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 Point position = PointToClient(Cursor.Position); |  | ||||||
|                 position.Offset(20, 10); |  | ||||||
|                 toolTip.Show(text, this, position); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| using System.Linq; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core{ |  | ||||||
|     static class FormManager{ |  | ||||||
|         public static T TryFind<T>() where T : Form{ |  | ||||||
|             return Application.OpenForms.OfType<T>().FirstOrDefault(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool TryBringToFront<T>() where T : Form{ |  | ||||||
|             T form = TryFind<T>(); |  | ||||||
| 
 |  | ||||||
|             if (form != null){ |  | ||||||
|                 form.BringToFront(); |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             else return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void CloseAllDialogs(){ |  | ||||||
|             foreach(Form form in Application.OpenForms.Cast<Form>().Reverse()){ |  | ||||||
|                 if (form is FormSettings || form is FormPlugins || form is FormAbout || form is FormGuide){ |  | ||||||
|                     form.Close(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,255 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.IO; |  | ||||||
| using System.Text.RegularExpressions; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using System.Linq; |  | ||||||
| using TweetDuck.Core.Bridge; |  | ||||||
| using TweetDuck.Core.Management; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Other.Analytics; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     abstract class ContextMenuBase : IContextMenuHandler{ |  | ||||||
|         public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak")); |  | ||||||
| 
 |  | ||||||
|         private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality; |  | ||||||
|          |  | ||||||
|         private const CefMenuCommand MenuOpenLinkUrl     = (CefMenuCommand)26500; |  | ||||||
|         private const CefMenuCommand MenuCopyLinkUrl     = (CefMenuCommand)26501; |  | ||||||
|         private const CefMenuCommand MenuCopyUsername    = (CefMenuCommand)26502; |  | ||||||
|         private const CefMenuCommand MenuViewImage       = (CefMenuCommand)26503; |  | ||||||
|         private const CefMenuCommand MenuOpenMediaUrl    = (CefMenuCommand)26504; |  | ||||||
|         private const CefMenuCommand MenuCopyMediaUrl    = (CefMenuCommand)26505; |  | ||||||
|         private const CefMenuCommand MenuSaveMedia       = (CefMenuCommand)26506; |  | ||||||
|         private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507; |  | ||||||
|         private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508; |  | ||||||
|         private const CefMenuCommand MenuReadApplyROT13  = (CefMenuCommand)26509; |  | ||||||
|         private const CefMenuCommand MenuOpenDevTools    = (CefMenuCommand)26599; |  | ||||||
|          |  | ||||||
|         protected ContextInfo.LinkInfo LastLink { get; private set; } |  | ||||||
|         protected ContextInfo.ChirpInfo LastChirp { get; private set; } |  | ||||||
| 
 |  | ||||||
|         private readonly AnalyticsFile.IProvider analytics; |  | ||||||
| 
 |  | ||||||
|         protected ContextMenuBase(AnalyticsFile.IProvider analytics){ |  | ||||||
|             this.analytics = analytics; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void ResetContextInfo(){ |  | ||||||
|             LastLink = default(ContextInfo.LinkInfo); |  | ||||||
|             LastChirp = default(ContextInfo.ChirpInfo); |  | ||||||
|             TweetDeckBridge.ContextInfo.Reset(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ |  | ||||||
|             if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ |  | ||||||
|                 ResetContextInfo(); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 LastLink = TweetDeckBridge.ContextInfo.Link; |  | ||||||
|                 LastChirp = TweetDeckBridge.ContextInfo.Chirp; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){ |  | ||||||
|                 model.AddItem(MenuSearchInBrowser, "Search in browser"); |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|                 model.AddItem(MenuReadApplyROT13, "Apply ROT13"); |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bool hasTweetImage = LastLink.IsImage; |  | ||||||
|             bool hasTweetVideo = LastLink.IsVideo; |  | ||||||
| 
 |  | ||||||
|             string TextOpen(string name) => "Open "+name+" in browser"; |  | ||||||
|             string TextCopy(string name) => "Copy "+name+" address"; |  | ||||||
|             string TextSave(string name) => "Save "+name+" as..."; |  | ||||||
|              |  | ||||||
|             if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage && !hasTweetVideo){ |  | ||||||
|                 if (TwitterUtils.RegexAccount.IsMatch(parameters.UnfilteredLinkUrl)){ |  | ||||||
|                     model.AddItem(MenuOpenLinkUrl, TextOpen("account")); |  | ||||||
|                     model.AddItem(MenuCopyLinkUrl, TextCopy("account")); |  | ||||||
|                     model.AddItem(MenuCopyUsername, "Copy account username"); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     model.AddItem(MenuOpenLinkUrl, TextOpen("link")); |  | ||||||
|                     model.AddItem(MenuCopyLinkUrl, TextCopy("link")); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (hasTweetVideo){ |  | ||||||
|                 model.AddItem(MenuOpenMediaUrl, TextOpen("video")); |  | ||||||
|                 model.AddItem(MenuCopyMediaUrl, TextCopy("video")); |  | ||||||
|                 model.AddItem(MenuSaveMedia, TextSave("video")); |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
|             else if (((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage) && parameters.SourceUrl != TweetNotification.AppLogo.Url){ |  | ||||||
|                 model.AddItem(MenuViewImage, "View image in photo viewer"); |  | ||||||
|                 model.AddItem(MenuOpenMediaUrl, TextOpen("image")); |  | ||||||
|                 model.AddItem(MenuCopyMediaUrl, TextCopy("image")); |  | ||||||
|                 model.AddItem(MenuSaveMedia, TextSave("image")); |  | ||||||
| 
 |  | ||||||
|                 if (LastChirp.Images.Length > 1){ |  | ||||||
|                     model.AddItem(MenuSaveTweetImages, TextSave("all images")); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ |  | ||||||
|             Control control = browserControl.AsControl(); |  | ||||||
| 
 |  | ||||||
|             switch(commandId){ |  | ||||||
|                 case MenuOpenLinkUrl: |  | ||||||
|                     OpenBrowser(control, LastLink.GetUrl(parameters, true)); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case MenuCopyLinkUrl: |  | ||||||
|                     SetClipboardText(control, LastLink.GetUrl(parameters, false)); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case MenuCopyUsername: |  | ||||||
|                     Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl); |  | ||||||
|                     SetClipboardText(control, match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl); |  | ||||||
|                     control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case MenuOpenMediaUrl: |  | ||||||
|                     OpenBrowser(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality)); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case MenuCopyMediaUrl: |  | ||||||
|                     SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality)); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case MenuViewImage: { |  | ||||||
|                     void ViewImage(string path){ |  | ||||||
|                         string ext = Path.GetExtension(path); |  | ||||||
| 
 |  | ||||||
|                         if (TwitterUtils.ValidImageExtensions.Contains(ext)){ |  | ||||||
|                             WindowsUtils.OpenAssociatedProgram(path); |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     string url = LastLink.GetMediaSource(parameters); |  | ||||||
|                     string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName()); |  | ||||||
| 
 |  | ||||||
|                     control.InvokeAsyncSafe(() => { |  | ||||||
|                         if (File.Exists(file)){ |  | ||||||
|                             ViewImage(file); |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             analytics.AnalyticsFile.ViewedImages.Trigger(); |  | ||||||
| 
 |  | ||||||
|                             BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => { |  | ||||||
|                                 ViewImage(file); |  | ||||||
|                             }, ex => { |  | ||||||
|                                 FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK); |  | ||||||
|                             }); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                      |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 case MenuSaveMedia: { |  | ||||||
|                     bool isVideo = LastLink.IsVideo; |  | ||||||
|                     string url = LastLink.GetMediaSource(parameters); |  | ||||||
|                     string username = LastChirp.Authors.LastOrDefault(); |  | ||||||
|                      |  | ||||||
|                     control.InvokeAsyncSafe(() => { |  | ||||||
|                         if (isVideo){ |  | ||||||
|                             TwitterUtils.DownloadVideo(url, username); |  | ||||||
|                             analytics.AnalyticsFile.DownloadedVideos.Trigger(); |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             TwitterUtils.DownloadImage(url, username, ImageQuality); |  | ||||||
|                             analytics.AnalyticsFile.DownloadedImages.Trigger(); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 case MenuSaveTweetImages: { |  | ||||||
|                     string[] urls = LastChirp.Images; |  | ||||||
|                     string username = LastChirp.Authors.LastOrDefault(); |  | ||||||
|                      |  | ||||||
|                     control.InvokeAsyncSafe(() => { |  | ||||||
|                         TwitterUtils.DownloadImages(urls, username, ImageQuality); |  | ||||||
|                         analytics.AnalyticsFile.DownloadedImages.Trigger(); |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 case MenuReadApplyROT13: |  | ||||||
|                     string selection = parameters.SelectionText; |  | ||||||
|                     control.InvokeAsyncSafe(() => FormMessage.Information("ROT13", StringUtils.ConvertRot13(selection), FormMessage.OK)); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuSearchInBrowser: |  | ||||||
|                     string query = parameters.SelectionText; |  | ||||||
|                     control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalSearch(query)); |  | ||||||
|                     DeselectAll(frame); |  | ||||||
|                     break; |  | ||||||
|                      |  | ||||||
|                 case MenuOpenDevTools: |  | ||||||
|                     browserControl.ShowDevTools(); |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){ |  | ||||||
|             ResetContextInfo(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){ |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected static void DeselectAll(IFrame frame){ |  | ||||||
|             ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected static void OpenBrowser(Control control, string url){ |  | ||||||
|             control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected static void SetClipboardText(Control control, string text){ |  | ||||||
|             control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected static void InsertSelectionSearchItem(IMenuModel model, CefMenuCommand insertCommand, string insertLabel){ |  | ||||||
|             model.InsertItemAt(model.GetIndexOf(MenuSearchInBrowser)+1, insertCommand, insertLabel); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         protected static void AddDebugMenuItems(IMenuModel model){ |  | ||||||
|             model.AddItem(MenuOpenDevTools, "Open dev tools"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected static void RemoveSeparatorIfLast(IMenuModel model){ |  | ||||||
|             if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){ |  | ||||||
|                 model.RemoveAt(model.Count-1); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected static void AddSeparator(IMenuModel model){ |  | ||||||
|             if (model.Count > 0 && model.GetTypeAt(model.Count-1) != MenuItemType.Separator){ // do not add separators if there is nothing to separate |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,181 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     sealed class ContextMenuBrowser : ContextMenuBase{ |  | ||||||
|         private const CefMenuCommand MenuGlobal   = (CefMenuCommand)26600; |  | ||||||
|         private const CefMenuCommand MenuMute     = (CefMenuCommand)26601; |  | ||||||
|         private const CefMenuCommand MenuSettings = (CefMenuCommand)26602; |  | ||||||
|         private const CefMenuCommand MenuPlugins  = (CefMenuCommand)26003; |  | ||||||
|         private const CefMenuCommand MenuAbout    = (CefMenuCommand)26604; |  | ||||||
|          |  | ||||||
|         private const CefMenuCommand MenuOpenTweetUrl       = (CefMenuCommand)26610; |  | ||||||
|         private const CefMenuCommand MenuCopyTweetUrl       = (CefMenuCommand)26611; |  | ||||||
|         private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612; |  | ||||||
|         private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613; |  | ||||||
|         private const CefMenuCommand MenuScreenshotTweet    = (CefMenuCommand)26614; |  | ||||||
|         private const CefMenuCommand MenuWriteApplyROT13    = (CefMenuCommand)26615; |  | ||||||
|         private const CefMenuCommand MenuSearchInColumn     = (CefMenuCommand)26616; |  | ||||||
| 
 |  | ||||||
|         private const string TitleReloadBrowser = "Reload browser"; |  | ||||||
|         private const string TitleMuteNotifications = "Mute notifications"; |  | ||||||
|         private const string TitleSettings = "Options"; |  | ||||||
|         private const string TitlePlugins = "Plugins"; |  | ||||||
|         private const string TitleAboutProgram = "About "+Program.BrandName; |  | ||||||
| 
 |  | ||||||
|         private readonly FormBrowser form; |  | ||||||
|          |  | ||||||
|         public ContextMenuBrowser(FormBrowser form) : base(form){ |  | ||||||
|             this.form = form; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ |  | ||||||
|             bool isSelecting = parameters.TypeFlags.HasFlag(ContextMenuType.Selection); |  | ||||||
|             bool isEditing = parameters.TypeFlags.HasFlag(ContextMenuType.Editable); |  | ||||||
| 
 |  | ||||||
|             model.Remove(CefMenuCommand.Back); |  | ||||||
|             model.Remove(CefMenuCommand.Forward); |  | ||||||
|             model.Remove(CefMenuCommand.Print); |  | ||||||
|             model.Remove(CefMenuCommand.ViewSource); |  | ||||||
|             RemoveSeparatorIfLast(model); |  | ||||||
| 
 |  | ||||||
|             if (isSelecting){ |  | ||||||
|                 if (isEditing){ |  | ||||||
|                     model.AddSeparator(); |  | ||||||
|                     model.AddItem(MenuWriteApplyROT13, "Apply ROT13"); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); |  | ||||||
| 
 |  | ||||||
|             if (isSelecting && !isEditing && TwitterUtils.IsTweetDeckWebsite(frame)){ |  | ||||||
|                 InsertSelectionSearchItem(model, MenuSearchInColumn, "Search in a column"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrEmpty(LastChirp.TweetUrl) && !isSelecting && !isEditing){ |  | ||||||
|                 model.AddItem(MenuOpenTweetUrl, "Open tweet in browser"); |  | ||||||
|                 model.AddItem(MenuCopyTweetUrl, "Copy tweet address"); |  | ||||||
|                 model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard"); |  | ||||||
| 
 |  | ||||||
|                 if (!string.IsNullOrEmpty(LastChirp.QuoteUrl)){ |  | ||||||
|                     model.AddSeparator(); |  | ||||||
|                     model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser"); |  | ||||||
|                     model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!isSelecting && !isEditing){ |  | ||||||
|                 AddSeparator(model); |  | ||||||
| 
 |  | ||||||
|                 IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName); |  | ||||||
|              |  | ||||||
|                 globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser); |  | ||||||
|                 globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications); |  | ||||||
|                 globalMenu.SetChecked(MenuMute, Program.UserConfig.MuteNotifications); |  | ||||||
|                 globalMenu.AddSeparator(); |  | ||||||
| 
 |  | ||||||
|                 globalMenu.AddItem(MenuSettings, TitleSettings); |  | ||||||
|                 globalMenu.AddItem(MenuPlugins, TitlePlugins); |  | ||||||
|                 globalMenu.AddItem(MenuAbout, TitleAboutProgram); |  | ||||||
| 
 |  | ||||||
|                 if (HasDevTools){ |  | ||||||
|                     globalMenu.AddSeparator(); |  | ||||||
|                     AddDebugMenuItems(globalMenu); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             RemoveSeparatorIfLast(model); |  | ||||||
|              |  | ||||||
|             form.InvokeAsyncSafe(form.AnalyticsFile.BrowserContextMenus.Trigger); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ |  | ||||||
|             if (base.OnContextMenuCommand(browserControl, browser, frame, parameters, commandId, eventFlags)){ |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             switch(commandId){ |  | ||||||
|                 case CefMenuCommand.Reload: |  | ||||||
|                     form.InvokeAsyncSafe(form.ReloadToTweetDeck); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuSettings: |  | ||||||
|                     form.InvokeAsyncSafe(form.OpenSettings); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuAbout: |  | ||||||
|                     form.InvokeAsyncSafe(form.OpenAbout); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuPlugins: |  | ||||||
|                     form.InvokeAsyncSafe(form.OpenPlugins); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuMute: |  | ||||||
|                     form.InvokeAsyncSafe(ToggleMuteNotifications); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuOpenTweetUrl: |  | ||||||
|                     OpenBrowser(form, LastChirp.TweetUrl); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuCopyTweetUrl: |  | ||||||
|                     SetClipboardText(form, LastChirp.TweetUrl); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuScreenshotTweet: |  | ||||||
|                     form.InvokeAsyncSafe(form.TriggerTweetScreenshot); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuOpenQuotedTweetUrl: |  | ||||||
|                     OpenBrowser(form, LastChirp.QuoteUrl); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuCopyQuotedTweetUrl: |  | ||||||
|                     SetClipboardText(form, LastChirp.QuoteUrl); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuWriteApplyROT13: |  | ||||||
|                     form.InvokeAsyncSafe(form.ApplyROT13); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuSearchInColumn: |  | ||||||
|                     string query = parameters.SelectionText; |  | ||||||
|                     form.InvokeAsyncSafe(() => form.AddSearchColumn(query)); |  | ||||||
|                     DeselectAll(frame); |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static ContextMenu CreateMenu(FormBrowser form){ |  | ||||||
|             ContextMenu menu = new ContextMenu(); |  | ||||||
| 
 |  | ||||||
|             menu.MenuItems.Add(TitleReloadBrowser, (sender, args) => form.ReloadToTweetDeck()); |  | ||||||
|             menu.MenuItems.Add(TitleMuteNotifications, (sender, args) => ToggleMuteNotifications()); |  | ||||||
|             menu.MenuItems.Add("-"); |  | ||||||
|             menu.MenuItems.Add(TitleSettings, (sender, args) => form.OpenSettings()); |  | ||||||
|             menu.MenuItems.Add(TitlePlugins, (sender, args) => form.OpenPlugins()); |  | ||||||
|             menu.MenuItems.Add(TitleAboutProgram,  (sender, args) => form.OpenAbout()); |  | ||||||
| 
 |  | ||||||
|             menu.Popup += (sender, args) => { |  | ||||||
|                 menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications; |  | ||||||
|                 form.AnalyticsFile.BrowserContextMenus.Trigger(); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             return menu; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static void ToggleMuteNotifications(){ |  | ||||||
|             Program.UserConfig.MuteNotifications = !Program.UserConfig.MuteNotifications; |  | ||||||
|             Program.UserConfig.Save(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Other.Analytics; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     sealed class ContextMenuGuide : ContextMenuBase{ |  | ||||||
|         public ContextMenuGuide(AnalyticsFile.IProvider analytics) : base(analytics){} |  | ||||||
| 
 |  | ||||||
|         public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ |  | ||||||
|             model.Clear(); |  | ||||||
|             base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); |  | ||||||
| 
 |  | ||||||
|             if (HasDevTools){ |  | ||||||
|                 AddSeparator(model); |  | ||||||
|                 AddDebugMenuItems(model); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,98 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     sealed class ContextMenuNotification : ContextMenuBase{ |  | ||||||
|         private const CefMenuCommand MenuViewDetail         = (CefMenuCommand)26600; |  | ||||||
|         private const CefMenuCommand MenuSkipTweet          = (CefMenuCommand)26601; |  | ||||||
|         private const CefMenuCommand MenuFreeze             = (CefMenuCommand)26602; |  | ||||||
|         private const CefMenuCommand MenuCopyTweetUrl       = (CefMenuCommand)26603; |  | ||||||
|         private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26604; |  | ||||||
| 
 |  | ||||||
|         private readonly FormNotificationBase form; |  | ||||||
|         private readonly bool enableCustomMenu; |  | ||||||
| 
 |  | ||||||
|         public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){ |  | ||||||
|             this.form = form; |  | ||||||
|             this.enableCustomMenu = enableCustomMenu; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ |  | ||||||
|             model.Clear(); |  | ||||||
| 
 |  | ||||||
|             if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){ |  | ||||||
|                 model.AddItem(CefMenuCommand.Copy, "Copy"); |  | ||||||
|                 model.AddSeparator(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); |  | ||||||
| 
 |  | ||||||
|             if (enableCustomMenu){ |  | ||||||
|                 if (form.CanViewDetail){ |  | ||||||
|                     model.AddItem(MenuViewDetail, "View detail"); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 model.AddItem(MenuSkipTweet, "Skip tweet"); |  | ||||||
|                 model.AddCheckItem(MenuFreeze, "Freeze"); |  | ||||||
|                 model.SetChecked(MenuFreeze, form.FreezeTimer); |  | ||||||
| 
 |  | ||||||
|                 if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){ |  | ||||||
|                     model.AddSeparator(); |  | ||||||
|                     model.AddItem(MenuCopyTweetUrl, "Copy tweet address"); |  | ||||||
| 
 |  | ||||||
|                     if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){ |  | ||||||
|                         model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             if (HasDevTools){ |  | ||||||
|                 AddSeparator(model); |  | ||||||
|                 AddDebugMenuItems(model); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             RemoveSeparatorIfLast(model); |  | ||||||
| 
 |  | ||||||
|             form.InvokeAsyncSafe(() => { |  | ||||||
|                 form.ContextMenuOpen = true; |  | ||||||
|                 form.AnalyticsFile.NotificationContextMenus.Trigger(); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ |  | ||||||
|             if (base.OnContextMenuCommand(browserControl, browser, frame, parameters, commandId, eventFlags)){ |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             switch(commandId){ |  | ||||||
|                 case MenuSkipTweet: |  | ||||||
|                     form.InvokeAsyncSafe(form.FinishCurrentNotification); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuFreeze: |  | ||||||
|                     form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuViewDetail: |  | ||||||
|                     form.InvokeSafe(form.ShowTweetDetail); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuCopyTweetUrl: |  | ||||||
|                     SetClipboardText(form, form.CurrentTweetUrl); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 case MenuCopyQuotedTweetUrl: |  | ||||||
|                     SetClipboardText(form, form.CurrentQuoteUrl); |  | ||||||
|                     return true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){ |  | ||||||
|             base.OnContextMenuDismissed(browserControl, browser, frame); |  | ||||||
|             form.InvokeAsyncSafe(() => form.ContextMenuOpen = false); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| using System.Collections.Generic; |  | ||||||
| using CefSharp; |  | ||||||
| using CefSharp.Enums; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     sealed class DragHandlerBrowser : IDragHandler{ |  | ||||||
|         public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){ |  | ||||||
|             void TriggerDragStart(string type, string data = null){ |  | ||||||
|                 browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (dragData.IsLink){ |  | ||||||
|                 TriggerDragStart("link", dragData.LinkUrl); |  | ||||||
|             } |  | ||||||
|             else if (dragData.IsFragment){ |  | ||||||
|                 TriggerDragStart("text", dragData.FragmentText.Trim()); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 TriggerDragStart("unknown"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions){} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Threading.Tasks; |  | ||||||
| using CefSharp; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling.General{ |  | ||||||
|     sealed class BrowserProcessHandler : IBrowserProcessHandler{ |  | ||||||
|         public static Task UpdatePrefs(){ |  | ||||||
|             return Cef.UIThreadTaskFactory.StartNew(UpdatePrefsInternal); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static void UpdatePrefsInternal(){ |  | ||||||
|             using(IRequestContext ctx = Cef.GetGlobalRequestContext()){ |  | ||||||
|                 ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _); |  | ||||||
|                 ctx.SetPreference("spellcheck.dictionary", Program.UserConfig.SpellCheckLanguage, out string _); |  | ||||||
|                 ctx.SetPreference("settings.a11y.animation_policy", Program.UserConfig.EnableAnimatedImages ? "allowed" : "none", out string _); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void IBrowserProcessHandler.OnContextInitialized(){ |  | ||||||
|             UpdatePrefsInternal(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){} |  | ||||||
|         void IDisposable.Dispose(){} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.IO; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling.General{ |  | ||||||
|     sealed class FileDialogHandler : IDialogHandler{ |  | ||||||
|         public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){ |  | ||||||
|             CefFileDialogMode dialogType = mode & CefFileDialogMode.TypeMask; |  | ||||||
| 
 |  | ||||||
|             if (dialogType == CefFileDialogMode.Open || dialogType == CefFileDialogMode.OpenMultiple){ |  | ||||||
|                 string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter)); |  | ||||||
| 
 |  | ||||||
|                 using(OpenFileDialog dialog = new OpenFileDialog{ |  | ||||||
|                     AutoUpgradeEnabled = true, |  | ||||||
|                     DereferenceLinks = true, |  | ||||||
|                     Multiselect = dialogType == CefFileDialogMode.OpenMultiple, |  | ||||||
|                     Title = "Open Files", |  | ||||||
|                     Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*" |  | ||||||
|                 }){ |  | ||||||
|                     if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                         string ext = Path.GetExtension(dialog.FileName); |  | ||||||
|                         callback.Continue(acceptFilters.FindIndex(filter => filter.Equals(ext, StringComparison.OrdinalIgnoreCase)), dialog.FileNames.ToList()); |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         callback.Cancel(); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     callback.Dispose(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 callback.Dispose(); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,90 +0,0 @@ | |||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling.General{ |  | ||||||
|     sealed class JavaScriptDialogHandler : IJsDialogHandler{ |  | ||||||
|         private static FormMessage CreateMessageForm(string caption, string text){ |  | ||||||
|             MessageBoxIcon icon = MessageBoxIcon.None; |  | ||||||
|             int pipe = text.IndexOf('|'); |  | ||||||
| 
 |  | ||||||
|             if (pipe != -1){ |  | ||||||
|                 switch(text.Substring(0, pipe)){ |  | ||||||
|                     case "error": icon = MessageBoxIcon.Error; break; |  | ||||||
|                     case "warning": icon = MessageBoxIcon.Warning; break; |  | ||||||
|                     case "info": icon = MessageBoxIcon.Information; break; |  | ||||||
|                     case "question": icon = MessageBoxIcon.Question; break; |  | ||||||
|                     default: return new FormMessage(caption, text, icon); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 text = text.Substring(pipe+1); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return new FormMessage(caption, text, icon); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){ |  | ||||||
|             browserControl.AsControl().InvokeSafe(() => { |  | ||||||
|                 FormMessage form; |  | ||||||
|                 TextBox input = null; |  | ||||||
| 
 |  | ||||||
|                 if (dialogType == CefJsDialogType.Alert){ |  | ||||||
|                     form = CreateMessageForm("Browser Message", messageText); |  | ||||||
|                     form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused); |  | ||||||
|                 } |  | ||||||
|                 else if (dialogType == CefJsDialogType.Confirm){ |  | ||||||
|                     form = CreateMessageForm("Browser Confirmation", messageText); |  | ||||||
|                     form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel); |  | ||||||
|                     form.AddButton(FormMessage.Yes, ControlType.Focused); |  | ||||||
|                 } |  | ||||||
|                 else if (dialogType == CefJsDialogType.Prompt){ |  | ||||||
|                     form = CreateMessageForm("Browser Prompt", messageText); |  | ||||||
|                     form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel); |  | ||||||
|                     form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused); |  | ||||||
| 
 |  | ||||||
|                     float dpiScale = form.GetDPIScale(); |  | ||||||
|                     int inputPad = form.HasIcon ? 43 : 0; |  | ||||||
| 
 |  | ||||||
|                     input = new TextBox{ |  | ||||||
|                         Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom, |  | ||||||
|                         Font = SystemFonts.MessageBoxFont, |  | ||||||
|                         Location = new Point(BrowserUtils.Scale(22+inputPad, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)), |  | ||||||
|                         Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44+inputPad, dpiScale), BrowserUtils.Scale(23, dpiScale)) |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     form.Controls.Add(input); |  | ||||||
|                     form.ActiveControl = input; |  | ||||||
|                     form.Height += input.Size.Height+input.Margin.Vertical; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     callback.Continue(false); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 bool success = form.ShowDialog() == DialogResult.OK; |  | ||||||
| 
 |  | ||||||
|                 if (input == null){ |  | ||||||
|                     callback.Continue(success); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     callback.Continue(success, input.Text); |  | ||||||
|                     input.Dispose(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 form.Dispose(); |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){ |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void IJsDialogHandler.OnResetDialogState(IWebBrowser browserControl, IBrowser browser){} |  | ||||||
|         void IJsDialogHandler.OnDialogClosed(IWebBrowser browserControl, IBrowser browser){} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling.General{ |  | ||||||
|     sealed class LifeSpanHandler : ILifeSpanHandler{ |  | ||||||
|         public static bool HandleLinkClick(IWebBrowser browserControl, WindowOpenDisposition targetDisposition, string targetUrl){ |  | ||||||
|             switch(targetDisposition){ |  | ||||||
|                 case WindowOpenDisposition.NewBackgroundTab: |  | ||||||
|                 case WindowOpenDisposition.NewForegroundTab: |  | ||||||
|                 case WindowOpenDisposition.NewPopup: |  | ||||||
|                 case WindowOpenDisposition.NewWindow: |  | ||||||
|                     browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl)); |  | ||||||
|                     return true; |  | ||||||
| 
 |  | ||||||
|                 default: |  | ||||||
|                     return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){ |  | ||||||
|             newBrowser = null; |  | ||||||
|             return HandleLinkClick(browserControl, targetDisposition, targetUrl); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser){} |  | ||||||
| 
 |  | ||||||
|         public bool DoClose(IWebBrowser browserControl, IBrowser browser){ |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser){} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     sealed class KeyboardHandlerBrowser : IKeyboardHandler{ |  | ||||||
|         private readonly FormBrowser form; |  | ||||||
| 
 |  | ||||||
|         public KeyboardHandlerBrowser(FormBrowser form){ |  | ||||||
|             this.form = form; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){ |  | ||||||
|             return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){ |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,45 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling { |  | ||||||
|     sealed class KeyboardHandlerNotification : IKeyboardHandler{ |  | ||||||
|         private readonly FormNotificationBase notification; |  | ||||||
| 
 |  | ||||||
|         public KeyboardHandlerNotification(FormNotificationBase notification){ |  | ||||||
|             this.notification = notification; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void TriggerKeyboardShortcutAnalytics(){ |  | ||||||
|             notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){ |  | ||||||
|             if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){ |  | ||||||
|                 switch((Keys)windowsKeyCode){ |  | ||||||
|                     case Keys.Enter: |  | ||||||
|                         notification.InvokeAsyncSafe(notification.FinishCurrentNotification); |  | ||||||
|                         TriggerKeyboardShortcutAnalytics(); |  | ||||||
|                         return true; |  | ||||||
| 
 |  | ||||||
|                     case Keys.Escape: |  | ||||||
|                         notification.InvokeAsyncSafe(notification.HideNotification); |  | ||||||
|                         TriggerKeyboardShortcutAnalytics(); |  | ||||||
|                         return true; |  | ||||||
| 
 |  | ||||||
|                     case Keys.Space: |  | ||||||
|                         notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer); |  | ||||||
|                         TriggerKeyboardShortcutAnalytics(); |  | ||||||
|                         return true; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){ |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| using System.Collections.Specialized; |  | ||||||
| using CefSharp; |  | ||||||
| using CefSharp.Handler; |  | ||||||
| using TweetDuck.Core.Handling.General; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     class RequestHandlerBase : DefaultRequestHandler{ |  | ||||||
|         public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){ |  | ||||||
|             return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){ |  | ||||||
|             if (ContextMenuBase.HasDevTools){ |  | ||||||
|                 NameValueCollection headers = request.Headers; |  | ||||||
|                 headers.Remove("x-devtools-emulate-network-conditions-client-id"); |  | ||||||
|                 request.Headers = headers; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     sealed class RequestHandlerBrowser : RequestHandlerBase{ |  | ||||||
|         public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){ |  | ||||||
|             browser.Reload(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){ |  | ||||||
|             if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){ |  | ||||||
|                 callback.Dispose(); |  | ||||||
|                 return CefReturnValue.Cancel; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){ |  | ||||||
|             if (request.ResourceType == ResourceType.Image && request.Url.Contains("backgrounds/spinner_blue")){ |  | ||||||
|                 request.Url = TwitterUtils.LoadingSpinner.Url; |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return base.OnResourceResponse(browserControl, browser, frame, request, response); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,63 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using System.Collections.Specialized; |  | ||||||
| using System.IO; |  | ||||||
| using System.Text; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Handling{ |  | ||||||
|     sealed class ResourceHandlerNotification : IResourceHandler{ |  | ||||||
|         private readonly NameValueCollection headers = new NameValueCollection(0); |  | ||||||
|         private MemoryStream dataIn; |  | ||||||
| 
 |  | ||||||
|         public void SetHTML(string html){ |  | ||||||
|             dataIn?.Dispose(); |  | ||||||
|             dataIn = ResourceHandler.GetMemoryStream(html, Encoding.UTF8); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Dispose(){ |  | ||||||
|             if (dataIn != null){ |  | ||||||
|                 dataIn.Dispose(); |  | ||||||
|                 dataIn = null; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IResourceHandler.ProcessRequest(IRequest request, ICallback callback){ |  | ||||||
|             callback.Continue(); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void IResourceHandler.GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl){ |  | ||||||
|             redirectUrl = null; |  | ||||||
| 
 |  | ||||||
|             response.MimeType = "text/html"; |  | ||||||
|             response.StatusCode = 200; |  | ||||||
|             response.StatusText = "OK"; |  | ||||||
|             response.ResponseHeaders = headers; |  | ||||||
|             responseLength = dataIn?.Length ?? -1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IResourceHandler.ReadResponse(Stream dataOut, out int bytesRead, ICallback callback){ |  | ||||||
|             callback.Dispose(); |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 int length = (int)dataIn.Length; |  | ||||||
| 
 |  | ||||||
|                 dataIn.CopyTo(dataOut, length); |  | ||||||
|                 bytesRead = length; |  | ||||||
|                 return true; |  | ||||||
|             }catch{ // catch IOException, possibly NullReferenceException if dataIn is null |  | ||||||
|                 bytesRead = 0; |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IResourceHandler.CanGetCookie(Cookie cookie){ |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool IResourceHandler.CanSetCookie(Cookie cookie){ |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void IResourceHandler.Cancel(){} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,72 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.IO; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Threading; |  | ||||||
| using System.Threading.Tasks; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Management{ |  | ||||||
|     static class BrowserCache{ |  | ||||||
|         public static string CacheFolder => Path.Combine(Program.StoragePath, "Cache"); |  | ||||||
|          |  | ||||||
|         private static bool ClearOnExit; |  | ||||||
|         private static Timer AutoClearTimer; |  | ||||||
| 
 |  | ||||||
|         private static long CalculateCacheSize(){ |  | ||||||
|             return new DirectoryInfo(CacheFolder).EnumerateFiles().Select(file => { |  | ||||||
|                 try{ |  | ||||||
|                     return file.Length; |  | ||||||
|                 }catch{ |  | ||||||
|                     return 0L; |  | ||||||
|                 } |  | ||||||
|             }).Sum(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void GetCacheSize(Action<Task<long>> callbackBytes){ |  | ||||||
|             Task<long> task = new Task<long>(CalculateCacheSize); |  | ||||||
|             task.ContinueWith(callbackBytes); |  | ||||||
|             task.Start(); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public static void RefreshTimer(){ |  | ||||||
|             bool shouldRun = Program.SystemConfig.ClearCacheAutomatically && !ClearOnExit; |  | ||||||
| 
 |  | ||||||
|             if (!shouldRun && AutoClearTimer != null){ |  | ||||||
|                 AutoClearTimer.Dispose(); |  | ||||||
|                 AutoClearTimer = null; |  | ||||||
|             } |  | ||||||
|             else if (shouldRun && AutoClearTimer == null){ |  | ||||||
|                 AutoClearTimer = new Timer(state => { |  | ||||||
|                     if (AutoClearTimer != null){ |  | ||||||
|                         try{ |  | ||||||
|                             if (CalculateCacheSize() >= Program.SystemConfig.ClearCacheThreshold*1024L*1024L){ |  | ||||||
|                                 SetClearOnExit(); |  | ||||||
|                             } |  | ||||||
|                         }catch(Exception){ |  | ||||||
|                             // TODO should probably log errors and report them at some point |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }, null, TimeSpan.FromSeconds(30), TimeSpan.FromHours(4)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetClearOnExit(){ |  | ||||||
|             ClearOnExit = true; |  | ||||||
|             RefreshTimer(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void Exit(){ |  | ||||||
|             if (AutoClearTimer != null){ |  | ||||||
|                 AutoClearTimer.Dispose(); |  | ||||||
|                 AutoClearTimer = null; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (ClearOnExit){ |  | ||||||
|                 try{ |  | ||||||
|                     Directory.Delete(CacheFolder, true); |  | ||||||
|                 }catch{ |  | ||||||
|                     // welp, too bad |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,68 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Management{ |  | ||||||
|     sealed class ContextInfo{ |  | ||||||
|         public LinkInfo Link { get; private set; } |  | ||||||
|         public ChirpInfo Chirp { get; private set; } |  | ||||||
|          |  | ||||||
|         public ContextInfo(){ |  | ||||||
|             Reset(); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public void SetLink(string type, string url){ |  | ||||||
|             Link = string.IsNullOrEmpty(url) ? new LinkInfo() : new LinkInfo(type, url); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void SetChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ |  | ||||||
|             Chirp = new ChirpInfo(tweetUrl, quoteUrl, chirpAuthors, chirpImages); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Reset(){ |  | ||||||
|             Link = new LinkInfo(); |  | ||||||
|             Chirp = new ChirpInfo(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Data structures |  | ||||||
| 
 |  | ||||||
|         public struct LinkInfo{ |  | ||||||
|             public bool IsLink => type == "link"; |  | ||||||
|             public bool IsImage => type == "image"; |  | ||||||
|             public bool IsVideo => type == "video"; |  | ||||||
| 
 |  | ||||||
|             public string GetUrl(IContextMenuParams parameters, bool safe){ |  | ||||||
|                 return IsLink ? url : (safe ? parameters.LinkUrl : parameters.UnfilteredLinkUrl); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public string GetMediaSource(IContextMenuParams parameters){ |  | ||||||
|                 return IsImage || IsVideo ? url : parameters.SourceUrl; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             private readonly string type; |  | ||||||
|             private readonly string url; |  | ||||||
| 
 |  | ||||||
|             public LinkInfo(string type, string url){ |  | ||||||
|                 this.type = type; |  | ||||||
|                 this.url = url; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public struct ChirpInfo{ |  | ||||||
|             public string TweetUrl { get; } |  | ||||||
|             public string QuoteUrl { get; } |  | ||||||
| 
 |  | ||||||
|             public string[] Authors => chirpAuthors?.Split(';') ?? StringUtils.EmptyArray; |  | ||||||
|             public string[] Images => chirpImages?.Split(';') ?? StringUtils.EmptyArray; |  | ||||||
|              |  | ||||||
|             private readonly string chirpAuthors; |  | ||||||
|             private readonly string chirpImages; |  | ||||||
| 
 |  | ||||||
|             public ChirpInfo(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ |  | ||||||
|                 this.TweetUrl = tweetUrl; |  | ||||||
|                 this.QuoteUrl = quoteUrl; |  | ||||||
|                 this.chirpAuthors = chirpAuthors; |  | ||||||
|                 this.chirpImages = chirpImages; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,225 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.IO; |  | ||||||
| using System.Linq; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Plugins.Enums; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Management{ |  | ||||||
|     sealed class ProfileManager{ |  | ||||||
|         private static readonly string CookiesPath = Path.Combine(Program.StoragePath, "Cookies"); |  | ||||||
|         private static readonly string TempCookiesPath = Path.Combine(Program.StoragePath, "CookiesTmp"); |  | ||||||
| 
 |  | ||||||
|         [Flags] |  | ||||||
|         public enum Items{ |  | ||||||
|             None = 0, |  | ||||||
|             UserConfig = 1, |  | ||||||
|             SystemConfig = 2, |  | ||||||
|             Session = 4, |  | ||||||
|             PluginData = 8, |  | ||||||
|             All = UserConfig|SystemConfig|Session|PluginData |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool IsRestarting { get; private set; } |  | ||||||
| 
 |  | ||||||
|         private readonly string file; |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
| 
 |  | ||||||
|         public ProfileManager(string file, PluginManager plugins){ |  | ||||||
|             this.file = file; |  | ||||||
|             this.plugins = plugins; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool Export(Items items){ |  | ||||||
|             try{ |  | ||||||
|                 using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){ |  | ||||||
|                     if (items.HasFlag(Items.UserConfig)){ |  | ||||||
|                         stream.WriteFile("config", Program.UserConfigFilePath); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (items.HasFlag(Items.SystemConfig)){ |  | ||||||
|                         stream.WriteFile("system", Program.SystemConfigFilePath); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (items.HasFlag(Items.PluginData)){ |  | ||||||
|                         stream.WriteFile("plugin.config", Program.PluginConfigFilePath); |  | ||||||
| 
 |  | ||||||
|                         foreach(Plugin plugin in plugins.Plugins){ |  | ||||||
|                             foreach(PathInfo path in EnumerateFilesRelative(plugin.GetPluginFolder(PluginFolder.Data))){ |  | ||||||
|                                 try{ |  | ||||||
|                                     stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full); |  | ||||||
|                                 }catch(ArgumentOutOfRangeException e){ |  | ||||||
|                                     FormMessage.Warning("Export Profile", "Could not include a plugin file in the export. "+e.Message, FormMessage.OK); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (items.HasFlag(Items.Session)){ |  | ||||||
|                         stream.WriteFile("cookies", CookiesPath); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     stream.Flush(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return true; |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, e); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public Items FindImportItems(){ |  | ||||||
|             Items items = Items.None; |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){ |  | ||||||
|                     string key; |  | ||||||
| 
 |  | ||||||
|                     while((key = stream.SkipFile()) != null){ |  | ||||||
|                         switch(key){ |  | ||||||
|                             case "config": |  | ||||||
|                                 items |= Items.UserConfig; |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case "system": |  | ||||||
|                                 items |= Items.SystemConfig; |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case "plugin.config": |  | ||||||
|                             case "plugin.data": |  | ||||||
|                                 items |= Items.PluginData; |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case "cookies": |  | ||||||
|                                 items |= Items.Session; |  | ||||||
|                                 break; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             }catch(Exception){ |  | ||||||
|                 items = Items.None; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return items; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool Import(Items items){ |  | ||||||
|             try{ |  | ||||||
|                 HashSet<string> missingPlugins = new HashSet<string>(); |  | ||||||
| 
 |  | ||||||
|                 using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){ |  | ||||||
|                     CombinedFileStream.Entry entry; |  | ||||||
| 
 |  | ||||||
|                     while((entry = stream.ReadFile()) != null){ |  | ||||||
|                         switch(entry.KeyName){ |  | ||||||
|                             case "config": |  | ||||||
|                                 if (items.HasFlag(Items.UserConfig)){ |  | ||||||
|                                     entry.WriteToFile(Program.UserConfigFilePath); |  | ||||||
|                                 } |  | ||||||
| 
 |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case "system": |  | ||||||
|                                 if (items.HasFlag(Items.SystemConfig)){ |  | ||||||
|                                     entry.WriteToFile(Program.SystemConfigFilePath); |  | ||||||
|                                     IsRestarting = true; |  | ||||||
|                                 } |  | ||||||
| 
 |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case "plugin.config": |  | ||||||
|                                 if (items.HasFlag(Items.PluginData)){ |  | ||||||
|                                     entry.WriteToFile(Program.PluginConfigFilePath); |  | ||||||
|                                 } |  | ||||||
| 
 |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case "plugin.data": |  | ||||||
|                                 if (items.HasFlag(Items.PluginData)){ |  | ||||||
|                                     string[] value = entry.KeyValue; |  | ||||||
| 
 |  | ||||||
|                                     entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true); |  | ||||||
| 
 |  | ||||||
|                                     if (!plugins.IsPluginInstalled(value[0])){ |  | ||||||
|                                         missingPlugins.Add(value[0]); |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
| 
 |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case "cookies": |  | ||||||
|                                 if (items.HasFlag(Items.Session)){ |  | ||||||
|                                     entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath)); |  | ||||||
|                                     IsRestarting = true; |  | ||||||
|                                 } |  | ||||||
| 
 |  | ||||||
|                                 break; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (missingPlugins.Count > 0){ |  | ||||||
|                     FormMessage.Information("Profile Import", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return true; |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing TweetDuck profile.", true, e); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void ImportCookies(){ |  | ||||||
|             if (File.Exists(TempCookiesPath)){ |  | ||||||
|                 try{ |  | ||||||
|                     if (File.Exists(CookiesPath)){ |  | ||||||
|                         File.Delete(CookiesPath); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     File.Move(TempCookiesPath, CookiesPath); |  | ||||||
|                 }catch(Exception e){ |  | ||||||
|                     Program.Reporter.HandleException("Profile Import Error", "Could not import the cookie file to restore login session.", true, e); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void DeleteCookies(){ |  | ||||||
|             try{ |  | ||||||
|                 if (File.Exists(CookiesPath)){ |  | ||||||
|                     File.Delete(CookiesPath); |  | ||||||
|                 } |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){ |  | ||||||
|             if (Directory.Exists(root)){ |  | ||||||
|                 int rootLength = root.Length; |  | ||||||
|                 return Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo(fullPath, rootLength)); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 return Enumerable.Empty<PathInfo>(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private sealed class PathInfo{ |  | ||||||
|             public string Full { get; } |  | ||||||
|             public string Relative { get; } |  | ||||||
| 
 |  | ||||||
|             public PathInfo(string fullPath, int rootLength){ |  | ||||||
|                 this.Full = fullPath; |  | ||||||
|                 this.Relative = fullPath.Substring(rootLength).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); // strip leading separator character |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     static class ProfileManagerExtensions{ |  | ||||||
|         public static bool NeedsRestart(this ProfileManager.Items items){ |  | ||||||
|             return items.HasFlag(ProfileManager.Items.SystemConfig) || items.HasFlag(ProfileManager.Items.Session); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,210 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.IO; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetLib.Communication; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Management{ |  | ||||||
|     sealed class VideoPlayer : IDisposable{ |  | ||||||
|         public bool Running => currentInstance != null && currentInstance.Running; |  | ||||||
| 
 |  | ||||||
|         public event EventHandler ProcessExited; |  | ||||||
| 
 |  | ||||||
|         private readonly FormBrowser owner; |  | ||||||
| 
 |  | ||||||
|         private Instance currentInstance; |  | ||||||
|         private bool isClosing; |  | ||||||
| 
 |  | ||||||
|         public VideoPlayer(FormBrowser owner){ |  | ||||||
|             this.owner = owner; |  | ||||||
|             this.owner.FormClosing += owner_FormClosing; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Launch(string url, string username){ |  | ||||||
|             if (Running){ |  | ||||||
|                 Destroy(); |  | ||||||
|                 isClosing = false; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             try{ |  | ||||||
|                 DuplexPipe.Server pipe = DuplexPipe.CreateServer(); |  | ||||||
|                 pipe.DataIn += pipe_DataIn; |  | ||||||
| 
 |  | ||||||
|                 Process process; |  | ||||||
| 
 |  | ||||||
|                 if ((process = Process.Start(new ProcessStartInfo{ |  | ||||||
|                     FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"), |  | ||||||
|                     Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"", |  | ||||||
|                     UseShellExecute = false, |  | ||||||
|                     RedirectStandardOutput = true |  | ||||||
|                 })) != null){ |  | ||||||
|                     currentInstance = new Instance(process, pipe, url, username); |  | ||||||
| 
 |  | ||||||
|                     process.EnableRaisingEvents = true; |  | ||||||
|                     process.Exited += process_Exited; |  | ||||||
|                      |  | ||||||
|                     process.BeginOutputReadLine(); |  | ||||||
|                     process.OutputDataReceived += process_OutputDataReceived; |  | ||||||
| 
 |  | ||||||
|                     pipe.DisposeToken(); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     pipe.DataIn -= pipe_DataIn; |  | ||||||
|                     pipe.Dispose(); |  | ||||||
|                 } |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void SendKeyEvent(Keys key){ |  | ||||||
|             currentInstance?.Pipe.Write("key", ((int)key).ToString()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){ |  | ||||||
|             owner.InvokeSafe(() => { |  | ||||||
|                 switch(e.Key){ |  | ||||||
|                     case "vol": |  | ||||||
|                         if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){ |  | ||||||
|                             Program.UserConfig.VideoPlayerVolume = volume; |  | ||||||
|                             Program.UserConfig.Save(); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         break; |  | ||||||
| 
 |  | ||||||
|                     case "download": |  | ||||||
|                         if (currentInstance != null){ |  | ||||||
|                             owner.AnalyticsFile.DownloadedVideos.Trigger(); |  | ||||||
|                             TwitterUtils.DownloadVideo(currentInstance.Url, currentInstance.Username); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         break; |  | ||||||
| 
 |  | ||||||
|                     case "rip": |  | ||||||
|                         currentInstance?.Dispose(); |  | ||||||
|                         currentInstance = null; |  | ||||||
| 
 |  | ||||||
|                         isClosing = false; |  | ||||||
|                         TriggerProcessExitEventUnsafe(); |  | ||||||
|                         break; |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Close(){ |  | ||||||
|             if (currentInstance != null){ |  | ||||||
|                 if (isClosing){ |  | ||||||
|                     Destroy(); |  | ||||||
|                     isClosing = false; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     isClosing = true; |  | ||||||
|                     currentInstance.Process.Exited -= process_Exited; |  | ||||||
|                     currentInstance.Pipe.Write("die"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Dispose(){ |  | ||||||
|             ProcessExited = null; |  | ||||||
| 
 |  | ||||||
|             isClosing = true; |  | ||||||
|             Destroy(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Destroy(){ |  | ||||||
|             if (currentInstance != null){ |  | ||||||
|                 currentInstance.KillAndDispose(); |  | ||||||
|                 currentInstance = null; |  | ||||||
| 
 |  | ||||||
|                 TriggerProcessExitEventUnsafe(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void owner_FormClosing(object sender, FormClosingEventArgs e){ |  | ||||||
|             if (currentInstance != null){ |  | ||||||
|                 currentInstance.Process.Exited -= process_Exited; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){ |  | ||||||
|             if (!string.IsNullOrEmpty(e.Data)){ |  | ||||||
|                 Program.Reporter.Log("[VideoPlayer] "+e.Data); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void process_Exited(object sender, EventArgs e){ |  | ||||||
|             if (currentInstance == null){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             int exitCode = currentInstance.Process.ExitCode; |  | ||||||
|             string url = currentInstance.Url; |  | ||||||
| 
 |  | ||||||
|             currentInstance.Dispose(); |  | ||||||
|             currentInstance = null; |  | ||||||
| 
 |  | ||||||
|             switch(exitCode){ |  | ||||||
|                 case 3: // CODE_LAUNCH_FAIL |  | ||||||
|                     if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                         BrowserUtils.OpenExternalBrowser(url); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case 4: // CODE_MEDIA_ERROR |  | ||||||
|                     if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                         BrowserUtils.OpenExternalBrowser(url); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void TriggerProcessExitEventUnsafe(){ |  | ||||||
|             ProcessExited?.Invoke(this, EventArgs.Empty); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private sealed class Instance : IDisposable{ |  | ||||||
|             public bool Running{ |  | ||||||
|                 get{ |  | ||||||
|                     Process.Refresh(); |  | ||||||
|                     return !Process.HasExited; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public Process Process { get; } |  | ||||||
|             public DuplexPipe.Server Pipe { get; } |  | ||||||
| 
 |  | ||||||
|             public string Url { get; } |  | ||||||
|             public string Username { get; } |  | ||||||
| 
 |  | ||||||
|             public Instance(Process process, DuplexPipe.Server pipe, string url, string username){ |  | ||||||
|                 this.Process = process; |  | ||||||
|                 this.Pipe = pipe; |  | ||||||
|                 this.Url = url; |  | ||||||
|                 this.Username = username; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void KillAndDispose(){ |  | ||||||
|                 try{ |  | ||||||
|                     Process.Kill(); |  | ||||||
|                 }catch{ |  | ||||||
|                     // kill me instead then |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 Dispose(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void Dispose(){ |  | ||||||
|                 Process.Dispose(); |  | ||||||
|                 Pipe.Dispose(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,66 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification.Example{ |  | ||||||
|     sealed class FormNotificationExample : FormNotificationMain{ |  | ||||||
|         public override bool RequiresResize => true; |  | ||||||
|         protected override bool CanDragWindow => Program.UserConfig.NotificationPosition == TweetNotification.Position.Custom; |  | ||||||
|          |  | ||||||
|         protected override FormBorderStyle NotificationBorderStyle{ |  | ||||||
|             get{ |  | ||||||
|                 if (Program.UserConfig.NotificationSize == TweetNotification.Size.Custom){ |  | ||||||
|                     switch(base.NotificationBorderStyle){ |  | ||||||
|                         case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable; |  | ||||||
|                         case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return base.NotificationBorderStyle; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public event EventHandler Ready; |  | ||||||
| 
 |  | ||||||
|         private readonly TweetNotification exampleNotification; |  | ||||||
| 
 |  | ||||||
|         public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){ |  | ||||||
|             browser.LoadingStateChanged += browser_LoadingStateChanged; |  | ||||||
| 
 |  | ||||||
|             string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true)?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty; |  | ||||||
| 
 |  | ||||||
|             #if DEBUG |  | ||||||
|             exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>"); |  | ||||||
|             #endif |  | ||||||
| 
 |  | ||||||
|             exampleNotification = TweetNotification.Example(exampleTweetHTML, 176); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ |  | ||||||
|             if (!e.IsLoading){ |  | ||||||
|                 Ready?.Invoke(this, EventArgs.Empty); |  | ||||||
|                 browser.LoadingStateChanged -= browser_LoadingStateChanged; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void HideNotification(){ |  | ||||||
|             Location = ControlExtensions.InvisibleLocation; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void FinishCurrentNotification(){} |  | ||||||
| 
 |  | ||||||
|         public void ShowExampleNotification(bool reset){ |  | ||||||
|             if (reset){ |  | ||||||
|                 LoadTweet(exampleNotification); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 PrepareAndDisplayWindow(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             UpdateTitle(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,234 +0,0 @@ | |||||||
| using CefSharp.WinForms; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Configuration; |  | ||||||
| using TweetDuck.Core.Bridge; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Handling; |  | ||||||
| using TweetDuck.Core.Handling.General; |  | ||||||
| using TweetDuck.Core.Other.Analytics; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification{ |  | ||||||
|     partial class FormNotificationBase : Form, AnalyticsFile.IProvider{ |  | ||||||
|         protected static int FontSizeLevel{ |  | ||||||
|             get{ |  | ||||||
|                 switch(TweetDeckBridge.FontSize){ |  | ||||||
|                     case "largest": return 4; |  | ||||||
|                     case "large": return 3; |  | ||||||
|                     case "small": return 1; |  | ||||||
|                     case "smallest": return 0; |  | ||||||
|                     default: return 2; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected virtual Point PrimaryLocation{ |  | ||||||
|             get{ |  | ||||||
|                 UserConfig config = Program.UserConfig; |  | ||||||
|                 Screen screen; |  | ||||||
| 
 |  | ||||||
|                 if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){ |  | ||||||
|                     screen = Screen.AllScreens[config.NotificationDisplay-1]; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     screen = Screen.FromControl(owner); |  | ||||||
|                 } |  | ||||||
|              |  | ||||||
|                 int edgeDist = config.NotificationEdgeDistance; |  | ||||||
| 
 |  | ||||||
|                 switch(config.NotificationPosition){ |  | ||||||
|                     case TweetNotification.Position.TopLeft: |  | ||||||
|                         return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+edgeDist); |  | ||||||
| 
 |  | ||||||
|                     case TweetNotification.Position.TopRight: |  | ||||||
|                         return new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist); |  | ||||||
| 
 |  | ||||||
|                     case TweetNotification.Position.BottomLeft: |  | ||||||
|                         return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height); |  | ||||||
| 
 |  | ||||||
|                     case TweetNotification.Position.BottomRight: |  | ||||||
|                         return new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height); |  | ||||||
| 
 |  | ||||||
|                     case TweetNotification.Position.Custom: |  | ||||||
|                         if (!config.IsCustomNotificationPositionSet){ |  | ||||||
|                             config.CustomNotificationPosition = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist); |  | ||||||
|                             config.Save(); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         return config.CustomNotificationPosition; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return Location; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation; |  | ||||||
|         protected virtual bool CanDragWindow => true; |  | ||||||
| 
 |  | ||||||
|         public new Point Location{ |  | ||||||
|             get{ |  | ||||||
|                 return base.Location; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             set{ |  | ||||||
|                 Visible = (base.Location = value) != ControlExtensions.InvisibleLocation; |  | ||||||
|                 FormBorderStyle = NotificationBorderStyle; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected virtual FormBorderStyle NotificationBorderStyle{ |  | ||||||
|             get{ |  | ||||||
|                 if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab |  | ||||||
|                     return FormBorderStyle.FixedSingle; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     return FormBorderStyle.FixedToolWindow; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public AnalyticsFile AnalyticsFile => owner.AnalyticsFile; |  | ||||||
|          |  | ||||||
|         protected override bool ShowWithoutActivation => true; |  | ||||||
|          |  | ||||||
|         protected float DpiScale { get; } |  | ||||||
|         protected double SizeScale => DpiScale*Program.UserConfig.ZoomLevel/100.0; |  | ||||||
| 
 |  | ||||||
|         protected readonly FormBrowser owner; |  | ||||||
|         protected readonly ChromiumWebBrowser browser; |  | ||||||
|          |  | ||||||
|         private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification(); |  | ||||||
| 
 |  | ||||||
|         private TweetNotification currentNotification; |  | ||||||
|         private int pauseCounter; |  | ||||||
|          |  | ||||||
|         public string CurrentTweetUrl => currentNotification?.TweetUrl; |  | ||||||
|         public string CurrentQuoteUrl => currentNotification?.QuoteUrl; |  | ||||||
| 
 |  | ||||||
|         public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId); |  | ||||||
| 
 |  | ||||||
|         protected bool IsPaused => pauseCounter > 0; |  | ||||||
|         protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position)); |  | ||||||
|          |  | ||||||
|         public bool FreezeTimer { get; set; } |  | ||||||
|         public bool ContextMenuOpen { get; set; } |  | ||||||
| 
 |  | ||||||
|         protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.owner = owner; |  | ||||||
|             this.owner.FormClosed += owner_FormClosed; |  | ||||||
| 
 |  | ||||||
|             this.browser = new ChromiumWebBrowser("about:blank"){ |  | ||||||
|                 MenuHandler = new ContextMenuNotification(this, enableContextMenu), |  | ||||||
|                 JsDialogHandler = new JavaScriptDialogHandler(), |  | ||||||
|                 LifeSpanHandler = new LifeSpanHandler(), |  | ||||||
|                 RequestHandler = new RequestHandlerBase() |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             this.browser.Dock = DockStyle.None; |  | ||||||
|             this.browser.ClientSize = ClientSize; |  | ||||||
|              |  | ||||||
|             browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler); |  | ||||||
|             browser.SetupResourceHandler(TweetNotification.AppLogo); |  | ||||||
| 
 |  | ||||||
|             Controls.Add(browser); |  | ||||||
| 
 |  | ||||||
|             Disposed += (sender, args) => { |  | ||||||
|                 this.browser.Dispose(); |  | ||||||
|                 this.owner.FormClosed -= owner_FormClosed; |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             DpiScale = this.GetDPIScale(); |  | ||||||
| 
 |  | ||||||
|             // ReSharper disable once VirtualMemberCallInContructor |  | ||||||
|             UpdateTitle(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void WndProc(ref Message m){ |  | ||||||
|             if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanDragWindow){ // WM_SYSCOMMAND, SC_MOVE |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             base.WndProc(ref m); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // event handlers |  | ||||||
| 
 |  | ||||||
|         private void owner_FormClosed(object sender, FormClosedEventArgs e){ |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // notification methods |  | ||||||
| 
 |  | ||||||
|         public virtual void HideNotification(){ |  | ||||||
|             browser.Load("about:blank"); |  | ||||||
|             DisplayTooltip(null); |  | ||||||
| 
 |  | ||||||
|             Location = ControlExtensions.InvisibleLocation; |  | ||||||
|             currentNotification = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public virtual void FinishCurrentNotification(){} |  | ||||||
| 
 |  | ||||||
|         public virtual void PauseNotification(){ |  | ||||||
|             if (pauseCounter++ == 0 && IsNotificationVisible){ |  | ||||||
|                 Location = ControlExtensions.InvisibleLocation; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public virtual void ResumeNotification(){ |  | ||||||
|             if (pauseCounter > 0){ |  | ||||||
|                 --pauseCounter; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected virtual string GetTweetHTML(TweetNotification tweet){ |  | ||||||
|             return tweet.GenerateHtml(IsCursorOverBrowser ? "td-notification td-hover" : "td-notification"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected virtual void LoadTweet(TweetNotification tweet){ |  | ||||||
|             currentNotification = tweet; |  | ||||||
|             resourceHandler.SetHTML(GetTweetHTML(tweet)); |  | ||||||
| 
 |  | ||||||
|             browser.Load(TwitterUtils.TweetDeckURL); |  | ||||||
|             DisplayTooltip(null); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected virtual void SetNotificationSize(int width, int height){ |  | ||||||
|             browser.ClientSize = ClientSize = new Size(BrowserUtils.Scale(width, SizeScale), BrowserUtils.Scale(height, SizeScale)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected virtual void UpdateTitle(){ |  | ||||||
|             string title = currentNotification?.ColumnTitle; |  | ||||||
|             Text = string.IsNullOrEmpty(title) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+title; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ShowTweetDetail(){ |  | ||||||
|             if (currentNotification != null){ |  | ||||||
|                 owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void MoveToVisibleLocation(){ |  | ||||||
|             bool needsReactivating = Location == ControlExtensions.InvisibleLocation; |  | ||||||
|             Location = PrimaryLocation; |  | ||||||
| 
 |  | ||||||
|             if (needsReactivating){ |  | ||||||
|                 NativeMethods.SetFormPos(this, NativeMethods.HWND_TOPMOST, NativeMethods.SWP_NOACTIVATE); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void DisplayTooltip(string text){ |  | ||||||
|             if (string.IsNullOrEmpty(text)){ |  | ||||||
|                 toolTip.Hide(this); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 Point position = PointToClient(Cursor.Position); |  | ||||||
|                 position.Offset(20, 5); |  | ||||||
|                 toolTip.Show(text, this, position); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,300 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Bridge; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Handling; |  | ||||||
| using TweetDuck.Core.Other.Interfaces; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Plugins.Enums; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification{ |  | ||||||
|     abstract partial class FormNotificationMain : FormNotificationBase, ITweetDeckBrowser{ |  | ||||||
|         private const string NotificationScriptFile = "notification.js"; |  | ||||||
| 
 |  | ||||||
|         private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile); |  | ||||||
|         private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile); |  | ||||||
|          |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
|         private readonly int timerBarHeight; |  | ||||||
| 
 |  | ||||||
|         protected int timeLeft, totalTime; |  | ||||||
|         protected bool pausedDuringNotification; |  | ||||||
|          |  | ||||||
|         private readonly NativeMethods.HookProc mouseHookDelegate; |  | ||||||
|         private IntPtr mouseHook; |  | ||||||
|         private bool blockXButtonUp; |  | ||||||
| 
 |  | ||||||
|         private bool? prevDisplayTimer; |  | ||||||
|         private int? prevFontSize; |  | ||||||
| 
 |  | ||||||
|         public virtual bool RequiresResize{ |  | ||||||
|             get{ |  | ||||||
|                 return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             set{ |  | ||||||
|                 if (value){ |  | ||||||
|                     prevDisplayTimer = null; |  | ||||||
|                     prevFontSize = null; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer; |  | ||||||
|                     prevFontSize = FontSizeLevel; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private int BaseClientWidth{ |  | ||||||
|             get{ |  | ||||||
|                 switch(Program.UserConfig.NotificationSize){ |  | ||||||
|                     default: |  | ||||||
|                         return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel)); |  | ||||||
| 
 |  | ||||||
|                     case TweetNotification.Size.Custom: |  | ||||||
|                         return Program.UserConfig.CustomNotificationSize.Width; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private int BaseClientHeight{ |  | ||||||
|             get{ |  | ||||||
|                 switch(Program.UserConfig.NotificationSize){ |  | ||||||
|                     default: |  | ||||||
|                         return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*FontSizeLevel)); |  | ||||||
| 
 |  | ||||||
|                     case TweetNotification.Size.Custom: |  | ||||||
|                         return Program.UserConfig.CustomNotificationSize.Height; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public Size BrowserSize => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize; |  | ||||||
| 
 |  | ||||||
|         protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.plugins = pluginManager; |  | ||||||
|             this.timerBarHeight = BrowserUtils.Scale(4, DpiScale); |  | ||||||
|              |  | ||||||
|             browser.KeyboardHandler = new KeyboardHandlerNotification(this); |  | ||||||
|             browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge.Notification(owner, this)); |  | ||||||
| 
 |  | ||||||
|             browser.LoadingStateChanged += Browser_LoadingStateChanged; |  | ||||||
|             browser.FrameLoadEnd += Browser_FrameLoadEnd; |  | ||||||
| 
 |  | ||||||
|             plugins.Register(this, PluginEnvironment.Notification); |  | ||||||
| 
 |  | ||||||
|             mouseHookDelegate = MouseHookProc; |  | ||||||
|             Disposed += (sender, args) => StopMouseHook(true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // implementation of ITweetDeckBrowser |  | ||||||
| 
 |  | ||||||
|         bool ITweetDeckBrowser.IsTweetDeckWebsite => IsNotificationVisible; |  | ||||||
| 
 |  | ||||||
|         void ITweetDeckBrowser.RegisterBridge(string name, object obj){ |  | ||||||
|             browser.RegisterAsyncJsObject(name, obj); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void ITweetDeckBrowser.OnFrameLoaded(Action<IFrame> callback){ |  | ||||||
|             browser.FrameLoadEnd += (sender, args) => { |  | ||||||
|                 IFrame frame = args.Frame; |  | ||||||
| 
 |  | ||||||
|                 if (frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){ |  | ||||||
|                     callback(frame); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void ITweetDeckBrowser.ExecuteFunction(string name, params object[] args){ |  | ||||||
|             browser.ExecuteScriptAsync(name, args); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // mouse wheel hook |  | ||||||
| 
 |  | ||||||
|         private void StartMouseHook(){ |  | ||||||
|             if (mouseHook == IntPtr.Zero){ |  | ||||||
|                 mouseHook = NativeMethods.SetWindowsHookEx(NativeMethods.WM_MOUSE_LL, mouseHookDelegate, IntPtr.Zero, 0); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void StopMouseHook(bool force){ |  | ||||||
|             if (mouseHook != IntPtr.Zero && (force || !blockXButtonUp)){ |  | ||||||
|                 NativeMethods.UnhookWindowsHookEx(mouseHook); |  | ||||||
|                 mouseHook = IntPtr.Zero; |  | ||||||
|                 blockXButtonUp = false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam){ |  | ||||||
|             if (nCode == 0){ |  | ||||||
|                 int eventType = wParam.ToInt32(); |  | ||||||
| 
 |  | ||||||
|                 if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){ |  | ||||||
|                     browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None); |  | ||||||
|                     return NativeMethods.HOOK_HANDLED; |  | ||||||
|                 } |  | ||||||
|                 else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){ |  | ||||||
|                     int extraButton = NativeMethods.GetMouseHookData(lParam); |  | ||||||
| 
 |  | ||||||
|                     if (extraButton == 2){ // forward button |  | ||||||
|                         this.InvokeAsyncSafe(FinishCurrentNotification); |  | ||||||
|                     } |  | ||||||
|                     else if (extraButton == 1){ // back button |  | ||||||
|                         this.InvokeAsyncSafe(Close); |  | ||||||
|                     } |  | ||||||
|                      |  | ||||||
|                     blockXButtonUp = true; |  | ||||||
|                     this.InvokeAsyncSafe(AnalyticsFile.NotificationExtraMouseButtons.Trigger); |  | ||||||
|                     return NativeMethods.HOOK_HANDLED; |  | ||||||
|                 } |  | ||||||
|                 else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){ |  | ||||||
|                     blockXButtonUp = false; |  | ||||||
| 
 |  | ||||||
|                     if (!Visible){ |  | ||||||
|                         StopMouseHook(false); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     return NativeMethods.HOOK_HANDLED; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return NativeMethods.CallNextHookEx(mouseHook, nCode, wParam, lParam); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // event handlers |  | ||||||
| 
 |  | ||||||
|         private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){ |  | ||||||
|             if (e.CloseReason == CloseReason.UserClosing){ |  | ||||||
|                 HideNotification(); |  | ||||||
|                 e.Cancel = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ |  | ||||||
|             if (!e.IsLoading && browser.Address != "about:blank"){ |  | ||||||
|                 this.InvokeSafe(() => { |  | ||||||
|                     Visible = true; // ensures repaint before moving the window to a visible location |  | ||||||
|                     timerDisplayDelay.Start(); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ |  | ||||||
|             if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){ |  | ||||||
|                 e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification)); |  | ||||||
|                 ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void timerDisplayDelay_Tick(object sender, EventArgs e){ |  | ||||||
|             OnNotificationReady(); |  | ||||||
|             timerDisplayDelay.Stop(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void timerHideProgress_Tick(object sender, EventArgs e){ |  | ||||||
|             if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             timeLeft -= timerProgress.Interval; |  | ||||||
| 
 |  | ||||||
|             int value = BrowserUtils.Scale(progressBarTimer.Maximum+25, (totalTime-timeLeft)/(double)totalTime); |  | ||||||
|             progressBarTimer.SetValueInstant(Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum-value : value); |  | ||||||
| 
 |  | ||||||
|             if (timeLeft <= 0){ |  | ||||||
|                 FinishCurrentNotification(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // notification methods |  | ||||||
| 
 |  | ||||||
|         public virtual void ShowNotification(TweetNotification notification){ |  | ||||||
|             LoadTweet(notification); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void HideNotification(){ |  | ||||||
|             base.HideNotification(); |  | ||||||
|              |  | ||||||
|             progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum; |  | ||||||
|             timerProgress.Stop(); |  | ||||||
|             totalTime = 0; |  | ||||||
| 
 |  | ||||||
|             StopMouseHook(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void FinishCurrentNotification(){ |  | ||||||
|             timerProgress.Stop(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void PauseNotification(){ |  | ||||||
|             if (!IsPaused){ |  | ||||||
|                 pausedDuringNotification = IsNotificationVisible; |  | ||||||
|                 timerProgress.Stop(); |  | ||||||
|                 StopMouseHook(true); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             base.PauseNotification(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void ResumeNotification(){ |  | ||||||
|             bool wasPaused = IsPaused; |  | ||||||
|             base.ResumeNotification(); |  | ||||||
| 
 |  | ||||||
|             if (wasPaused && !IsPaused && pausedDuringNotification){ |  | ||||||
|                 OnNotificationReady(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override string GetTweetHTML(TweetNotification tweet){ |  | ||||||
|             string html = base.GetTweetHTML(tweet); |  | ||||||
| 
 |  | ||||||
|             foreach(InjectedHTML injection in plugins.NotificationInjections){ |  | ||||||
|                 html = injection.Inject(html); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return html; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void LoadTweet(TweetNotification tweet){ |  | ||||||
|             timerProgress.Stop(); |  | ||||||
|             totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue); |  | ||||||
|             progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum; |  | ||||||
| 
 |  | ||||||
|             base.LoadTweet(tweet); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void SetNotificationSize(int width, int height){ |  | ||||||
|             if (Program.UserConfig.DisplayNotificationTimer){ |  | ||||||
|                 ClientSize = new Size(width, height+timerBarHeight); |  | ||||||
|                 progressBarTimer.Visible = true; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 ClientSize = new Size(width, height); |  | ||||||
|                 progressBarTimer.Visible = false; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             browser.ClientSize = new Size(width, height); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         protected void PrepareAndDisplayWindow(){ |  | ||||||
|             if (RequiresResize){ |  | ||||||
|                 RequiresResize = false; |  | ||||||
|                 SetNotificationSize(BaseClientWidth, BaseClientHeight); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             MoveToVisibleLocation(); |  | ||||||
|             StartMouseHook(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected virtual void OnNotificationReady(){ |  | ||||||
|             PrepareAndDisplayWindow(); |  | ||||||
|             timerProgress.Start(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,165 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Drawing; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification{ |  | ||||||
|     sealed partial class FormNotificationTweet : FormNotificationMain{ |  | ||||||
|         private const int NonIntrusiveIdleLimit = 30; |  | ||||||
|         private const int TrimMinimum = 32; |  | ||||||
| 
 |  | ||||||
|         protected override Point PrimaryLocation => hasTemporarilyMoved && IsNotificationVisible ? Location : base.PrimaryLocation; |  | ||||||
|         private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position); |  | ||||||
| 
 |  | ||||||
|         protected override bool CanDragWindow{ |  | ||||||
|             get{ |  | ||||||
|                 if (ModifierKeys.HasFlag(Keys.Alt)){ |  | ||||||
|                     hasTemporarilyMoved = true; |  | ||||||
|                     return true; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4); |  | ||||||
|         private bool needsTrim; |  | ||||||
|         private bool hasTemporarilyMoved; |  | ||||||
| 
 |  | ||||||
|         public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             Program.UserConfig.MuteToggled += Config_MuteToggled; |  | ||||||
|             Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled; |  | ||||||
| 
 |  | ||||||
|             if (Program.UserConfig.MuteNotifications){ |  | ||||||
|                 PauseNotification(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void WndProc(ref Message m){ |  | ||||||
|             if (m.Msg == 0x00A7){ // WM_NCMBUTTONDOWN |  | ||||||
|                 int hitTest = m.WParam.ToInt32(); |  | ||||||
| 
 |  | ||||||
|                 if (hitTest == 2 || hitTest == 20){ // HTCAPTION, HTCLOSE |  | ||||||
|                     hasTemporarilyMoved = false; |  | ||||||
|                     MoveToVisibleLocation(); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             base.WndProc(ref m); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // event handlers |  | ||||||
| 
 |  | ||||||
|         private void Config_MuteToggled(object sender, EventArgs e){ |  | ||||||
|             if (Program.UserConfig.MuteNotifications){ |  | ||||||
|                 PauseNotification(); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 ResumeNotification(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void timerCursorCheck_Tick(object sender, EventArgs e){ |  | ||||||
|             if (!IsCursorOverNotificationArea){ |  | ||||||
|                 ResumeNotification(); |  | ||||||
|                 timerCursorCheck.Stop(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void timerIdlePauseCheck_Tick(object sender, EventArgs e){ |  | ||||||
|             if (NativeMethods.GetIdleSeconds() < Program.UserConfig.NotificationIdlePauseSeconds){ |  | ||||||
|                 ResumeNotification(); |  | ||||||
|                 timerIdlePauseCheck.Stop(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // notification methods |  | ||||||
| 
 |  | ||||||
|         public override void ShowNotification(TweetNotification notification){ |  | ||||||
|             tweetQueue.Enqueue(notification); |  | ||||||
|              |  | ||||||
|             if (!IsPaused){ |  | ||||||
|                 UpdateTitle(); |  | ||||||
| 
 |  | ||||||
|                 if (totalTime == 0){ |  | ||||||
|                     LoadNextNotification(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             needsTrim |= tweetQueue.Count >= TrimMinimum; |  | ||||||
|             AnalyticsFile.DesktopNotifications.Trigger(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void HideNotification(){ |  | ||||||
|             base.HideNotification(); |  | ||||||
|             tweetQueue.Clear(); |  | ||||||
| 
 |  | ||||||
|             if (needsTrim){ |  | ||||||
|                 tweetQueue.TrimExcess(); |  | ||||||
|                 needsTrim = false; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             hasTemporarilyMoved = false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void FinishCurrentNotification(){ |  | ||||||
|             if (tweetQueue.Count > 0){ |  | ||||||
|                 LoadNextNotification(); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 HideNotification(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void ResumeNotification(){ |  | ||||||
|             bool wasPaused = IsPaused; |  | ||||||
|             base.ResumeNotification(); |  | ||||||
| 
 |  | ||||||
|             if (wasPaused && !IsPaused && !pausedDuringNotification && tweetQueue.Count > 0){ |  | ||||||
|                 LoadNextNotification(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void LoadNextNotification(){ |  | ||||||
|             if (!IsNotificationVisible){ |  | ||||||
|                 if (Program.UserConfig.NotificationNonIntrusiveMode && IsCursorOverNotificationArea && NativeMethods.GetIdleSeconds() < NonIntrusiveIdleLimit){ |  | ||||||
|                     if (!timerCursorCheck.Enabled){ |  | ||||||
|                         PauseNotification(); |  | ||||||
|                         timerCursorCheck.Start(); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                 else if (Program.UserConfig.NotificationIdlePauseSeconds > 0 && NativeMethods.GetIdleSeconds() >= Program.UserConfig.NotificationIdlePauseSeconds){ |  | ||||||
|                     if (!timerIdlePauseCheck.Enabled){ |  | ||||||
|                         PauseNotification(); |  | ||||||
|                         timerIdlePauseCheck.Start(); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             LoadTweet(tweetQueue.Dequeue()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void UpdateTitle(){ |  | ||||||
|             base.UpdateTitle(); |  | ||||||
| 
 |  | ||||||
|             if (tweetQueue.Count > 0){ |  | ||||||
|                 Text = Text+" ("+tweetQueue.Count+" more left)"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void OnNotificationReady(){ |  | ||||||
|             UpdateTitle(); |  | ||||||
|             base.OnNotificationReady(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,92 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Drawing.Imaging; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification.Screenshot{ |  | ||||||
|     sealed class FormNotificationScreenshotable : FormNotificationBase{ |  | ||||||
|         protected override bool CanDragWindow => false; |  | ||||||
| 
 |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
|         private int height; |  | ||||||
| 
 |  | ||||||
|         public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager, string html, int width) : base(owner, false){ |  | ||||||
|             this.plugins = pluginManager; |  | ||||||
| 
 |  | ||||||
|             browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback)); |  | ||||||
| 
 |  | ||||||
|             browser.LoadingStateChanged += (sender, args) => { |  | ||||||
|                 if (args.IsLoading){ |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 string script = ScriptLoader.LoadResource("screenshot.js", true); |  | ||||||
|                          |  | ||||||
|                 if (script == null){ |  | ||||||
|                     this.InvokeAsyncSafe(callback); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 using(IFrame frame = args.Browser.MainFrame){ |  | ||||||
|                     ScriptLoader.ExecuteScript(frame, script.Replace("{width}", BrowserUtils.Scale(width, DpiScale).ToString()), "screenshot"); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|              |  | ||||||
|             SetNotificationSize(width, 1024); |  | ||||||
|             LoadTweet(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override string GetTweetHTML(TweetNotification tweet){ |  | ||||||
|             string html = tweet.GenerateHtml("td-screenshot"); |  | ||||||
| 
 |  | ||||||
|             foreach(InjectedHTML injection in plugins.NotificationInjections){ |  | ||||||
|                 html = injection.Inject(html); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return html; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void SetScreenshotHeight(int browserHeight){ |  | ||||||
|             this.height = BrowserUtils.Scale(browserHeight, SizeScale); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool TakeScreenshot(bool ignoreHeightError = false){ |  | ||||||
|             if (!ignoreHeightError){ |  | ||||||
|                 if (height == 0){ |  | ||||||
|                     FormMessage.Error("Screenshot Failed", "Could not detect screenshot size.", FormMessage.OK); |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|                 else if (height > ClientSize.Height){ |  | ||||||
|                     FormMessage.Error("Screenshot Failed", $"Screenshot is too large: {height}px > {ClientSize.Height}px", FormMessage.OK); |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             IntPtr context = NativeMethods.GetDC(this.Handle); |  | ||||||
| 
 |  | ||||||
|             if (context == IntPtr.Zero){ |  | ||||||
|                 FormMessage.Error("Screenshot Failed", "Could not retrieve a graphics context handle for the notification window to take the screenshot.", FormMessage.OK); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 using(Bitmap bmp = new Bitmap(ClientSize.Width, Math.Max(1, height), PixelFormat.Format32bppRgb)){ |  | ||||||
|                     try{ |  | ||||||
|                         NativeMethods.RenderSourceIntoBitmap(context, bmp); |  | ||||||
|                     }finally{ |  | ||||||
|                         NativeMethods.ReleaseDC(this.Handle, context); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     Clipboard.SetImage(bmp); |  | ||||||
|                     return true; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification.Screenshot{ |  | ||||||
|     sealed class ScreenshotBridge{ |  | ||||||
|         private readonly Control owner; |  | ||||||
| 
 |  | ||||||
|         private readonly Action<int> safeSetHeight; |  | ||||||
|         private readonly Action safeTriggerScreenshot; |  | ||||||
| 
 |  | ||||||
|         public ScreenshotBridge(Control owner, Action<int> safeSetHeight, Action safeTriggerScreenshot){ |  | ||||||
|             this.owner = owner; |  | ||||||
|             this.safeSetHeight = safeSetHeight; |  | ||||||
|             this.safeTriggerScreenshot = safeTriggerScreenshot; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void SetHeight(int tweetHeight){ |  | ||||||
|             owner.InvokeSafe(() => safeSetHeight(tweetHeight)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void TriggerScreenshot(){ |  | ||||||
|             owner.InvokeSafe(safeTriggerScreenshot); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,144 +0,0 @@ | |||||||
| #if DEBUG |  | ||||||
| // Uncomment to keep screenshot windows visible for debugging |  | ||||||
| // #define NO_HIDE_SCREENSHOTS |  | ||||||
| 
 |  | ||||||
| // Uncomment to generate screenshots of individual frames for at most 1 second |  | ||||||
| // #define GEN_SCREENSHOT_FRAMES |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| 
 |  | ||||||
| #if GEN_SCREENSHOT_FRAMES |  | ||||||
| using System.Drawing.Imaging; |  | ||||||
| using System.IO; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification.Screenshot{ |  | ||||||
|     sealed class TweetScreenshotManager : IDisposable{ |  | ||||||
|         private readonly FormBrowser owner; |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
|         private readonly Timer timeout; |  | ||||||
|         private readonly Timer disposer; |  | ||||||
| 
 |  | ||||||
|         #if GEN_SCREENSHOT_FRAMES |  | ||||||
|         private readonly Timer debugger; |  | ||||||
|         private int frameCounter; |  | ||||||
|         #endif |  | ||||||
|          |  | ||||||
|         private FormNotificationScreenshotable screenshot; |  | ||||||
| 
 |  | ||||||
|         public TweetScreenshotManager(FormBrowser owner, PluginManager pluginManager){ |  | ||||||
|             this.owner = owner; |  | ||||||
|             this.plugins = pluginManager; |  | ||||||
| 
 |  | ||||||
|             this.timeout = new Timer{ Interval = 8000 }; |  | ||||||
|             this.timeout.Tick += timeout_Tick; |  | ||||||
| 
 |  | ||||||
|             this.disposer = new Timer{ Interval = 1 }; |  | ||||||
|             this.disposer.Tick += disposer_Tick; |  | ||||||
| 
 |  | ||||||
|             #if GEN_SCREENSHOT_FRAMES |  | ||||||
|             this.debugger = new Timer{ Interval = 16 }; |  | ||||||
|             this.debugger.Tick += debugger_Tick; |  | ||||||
|             #endif |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void timeout_Tick(object sender, EventArgs e){ |  | ||||||
|             timeout.Stop(); |  | ||||||
|             OnFinished(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void disposer_Tick(object sender, EventArgs e){ |  | ||||||
|             disposer.Stop(); |  | ||||||
|             screenshot.Dispose(); |  | ||||||
|             screenshot = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Trigger(string html, int width){ |  | ||||||
|             if (screenshot != null){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             screenshot = new FormNotificationScreenshotable(Callback, owner, plugins, html, width); |  | ||||||
|             screenshot.Show(); |  | ||||||
|             timeout.Start(); |  | ||||||
| 
 |  | ||||||
|             #if GEN_SCREENSHOT_FRAMES |  | ||||||
|             StartDebugger(); |  | ||||||
|             #endif |  | ||||||
| 
 |  | ||||||
|             #if !NO_HIDE_SCREENSHOTS |  | ||||||
|             owner.IsWaiting = true; |  | ||||||
|             #endif |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Callback(){ |  | ||||||
|             if (!timeout.Enabled){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             timeout.Stop(); |  | ||||||
|             screenshot.TakeScreenshot(); |  | ||||||
|              |  | ||||||
|             #if !NO_HIDE_SCREENSHOTS |  | ||||||
|             OnFinished(); |  | ||||||
|             #else |  | ||||||
|             screenshot.MoveToVisibleLocation(); |  | ||||||
|             screenshot.FormClosed += (sender, args) => disposer.Start(); |  | ||||||
|             #endif |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void OnFinished(){ |  | ||||||
|             #if GEN_SCREENSHOT_FRAMES |  | ||||||
|             debugger.Stop(); |  | ||||||
|             #endif |  | ||||||
| 
 |  | ||||||
|             screenshot.Location = ControlExtensions.InvisibleLocation; |  | ||||||
|             owner.IsWaiting = false; |  | ||||||
|             disposer.Start(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Dispose(){ |  | ||||||
|             #if GEN_SCREENSHOT_FRAMES |  | ||||||
|             debugger.Dispose(); |  | ||||||
|             #endif |  | ||||||
| 
 |  | ||||||
|             timeout.Dispose(); |  | ||||||
|             disposer.Dispose(); |  | ||||||
|             screenshot?.Dispose(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #if GEN_SCREENSHOT_FRAMES |  | ||||||
|         private static readonly string DebugScreenshotPath = Path.Combine(Program.StoragePath, "TD_Screenshots"); |  | ||||||
| 
 |  | ||||||
|         private void StartDebugger(){ |  | ||||||
|             frameCounter = 0; |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 Directory.Delete(DebugScreenshotPath, true); |  | ||||||
|                 WindowsUtils.TrySleepUntil(() => !Directory.Exists(DebugScreenshotPath), 1000, 10); |  | ||||||
|             }catch(DirectoryNotFoundException){} |  | ||||||
|              |  | ||||||
|             Directory.CreateDirectory(DebugScreenshotPath); |  | ||||||
|             debugger.Start(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void debugger_Tick(object sender, EventArgs e){ |  | ||||||
|             if (frameCounter < 63 && screenshot.TakeScreenshot(true)){ |  | ||||||
|                 try{ |  | ||||||
|                     Clipboard.GetImage()?.Save(Path.Combine(DebugScreenshotPath, "frame_"+(++frameCounter)+".png"), ImageFormat.Png); |  | ||||||
|                 }catch{ |  | ||||||
|                     System.Diagnostics.Debug.WriteLine("Failed generating frame "+frameCounter); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 debugger.Stop(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         #endif |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,50 +0,0 @@ | |||||||
| using System.Drawing; |  | ||||||
| using System.IO; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Core.Other.Settings; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification{ |  | ||||||
|     static class SoundNotification{ |  | ||||||
|         public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm"; |  | ||||||
|          |  | ||||||
|         public static IResourceHandler CreateFileHandler(string path){ |  | ||||||
|             string mimeType; |  | ||||||
| 
 |  | ||||||
|             switch(Path.GetExtension(path)){ |  | ||||||
|                 case ".weba": |  | ||||||
|                 case ".webm": mimeType = "audio/webm"; break; |  | ||||||
|                 case ".wav": mimeType = "audio/wav"; break; |  | ||||||
|                 case ".ogg": mimeType = "audio/ogg"; break; |  | ||||||
|                 case ".mp3": mimeType = "audio/mp3"; break; |  | ||||||
|                 case ".flac": mimeType = "audio/flac"; break; |  | ||||||
|                 case ".opus": mimeType = "audio/ogg; codecs=opus"; break; |  | ||||||
|                 default: mimeType = null; break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 return ResourceHandler.FromFilePath(path, mimeType); |  | ||||||
|             }catch{ |  | ||||||
|                 FormBrowser browser = FormManager.TryFind<FormBrowser>(); |  | ||||||
| 
 |  | ||||||
|                 browser?.InvokeAsyncSafe(() => { |  | ||||||
|                     using(FormMessage form = new FormMessage("Sound Notification Error", "Could not find custom notification sound file:\n"+path, MessageBoxIcon.Error)){ |  | ||||||
|                         form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused); |  | ||||||
|                          |  | ||||||
|                         Button btnViewOptions = form.AddButton("View Options"); |  | ||||||
|                         btnViewOptions.Width += 16; |  | ||||||
|                         btnViewOptions.Location = new Point(btnViewOptions.Location.X-16, btnViewOptions.Location.Y); |  | ||||||
| 
 |  | ||||||
|                         if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnViewOptions){ |  | ||||||
|                             browser.OpenSettings(typeof(TabSettingsSounds)); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                  |  | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Text; |  | ||||||
| using CefSharp; |  | ||||||
| using TweetDuck.Core.Bridge; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Notification{ |  | ||||||
|     sealed class TweetNotification{ |  | ||||||
|         private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>"; |  | ||||||
|         private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css") ?? string.Empty; |  | ||||||
|         public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png")); |  | ||||||
| 
 |  | ||||||
|         public static TweetNotification Example(string html, int characters){ |  | ||||||
|             return new TweetNotification(string.Empty, string.Empty, "Home", html, characters, string.Empty, string.Empty, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public enum Position{ |  | ||||||
|             TopLeft, TopRight, BottomLeft, BottomRight, Custom |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public enum Size{ |  | ||||||
|             Auto, Custom |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public string ColumnId { get; } |  | ||||||
|         public string ChirpId { get; } |  | ||||||
| 
 |  | ||||||
|         public string ColumnTitle { get; } |  | ||||||
|         public string TweetUrl { get; } |  | ||||||
|         public string QuoteUrl { get; } |  | ||||||
|          |  | ||||||
|         private readonly string html; |  | ||||||
|         private readonly int characters; |  | ||||||
|         private readonly bool isExample; |  | ||||||
| 
 |  | ||||||
|         public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl) : this(columnId, chirpId, title, html, characters, tweetUrl, quoteUrl, false){} |  | ||||||
| 
 |  | ||||||
|         private TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){ |  | ||||||
|             this.ColumnId = columnId; |  | ||||||
|             this.ChirpId = chirpId; |  | ||||||
| 
 |  | ||||||
|             this.ColumnTitle = title; |  | ||||||
|             this.TweetUrl = tweetUrl; |  | ||||||
|             this.QuoteUrl = quoteUrl; |  | ||||||
| 
 |  | ||||||
|             this.html = html; |  | ||||||
|             this.characters = characters; |  | ||||||
|             this.isExample = isExample; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public int GetDisplayDuration(int value){ |  | ||||||
|             return 2000+Math.Max(1000, value*characters); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public string GenerateHtml(string bodyClasses){ |  | ||||||
|             StringBuilder build = new StringBuilder(); |  | ||||||
|             build.Append("<!DOCTYPE html>"); |  | ||||||
|             build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout); |  | ||||||
|             build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>"); |  | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){ |  | ||||||
|                 build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>"); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             build.Append("</head>"); |  | ||||||
|             build.Append("<body class='scroll-styled-v"); |  | ||||||
| 
 |  | ||||||
|             if (!string.IsNullOrEmpty(bodyClasses)){ |  | ||||||
|                 build.Append(' ').Append(bodyClasses); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;min-height:100vh!important;height:auto!important;overflow:initial!important;'>"); |  | ||||||
|             build.Append(html); |  | ||||||
|             build.Append("</div></body>"); |  | ||||||
|             build.Append("</html>"); |  | ||||||
|             return build.ToString(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,135 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Diagnostics.CodeAnalysis; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Reflection; |  | ||||||
| using TweetDuck.Data.Serialization; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Analytics{ |  | ||||||
|     [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")] |  | ||||||
|     sealed class AnalyticsFile{ |  | ||||||
|         private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>(); |  | ||||||
| 
 |  | ||||||
|         static AnalyticsFile(){ |  | ||||||
|             Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{ |  | ||||||
|                 ConvertToString = value => value.ToBinary().ToString(), |  | ||||||
|                 ConvertToObject = value => DateTime.FromBinary(long.Parse(value)) |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             Serializer.RegisterTypeConverter(typeof(Counter), new SingleTypeConverter<Counter>{ |  | ||||||
|                 ConvertToString = value => value.Value.ToString(), |  | ||||||
|                 ConvertToObject = value => int.Parse(value) |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static readonly AnalyticsFile Dummy = new AnalyticsFile(); |  | ||||||
| 
 |  | ||||||
|         // STATE PROPERTIES |  | ||||||
|          |  | ||||||
|         public DateTime LastDataCollection  { get; set; } = DateTime.MinValue; |  | ||||||
|         public string LastCollectionVersion { get; set; } = null; |  | ||||||
|         public string LastCollectionMessage { get; set; } = null; |  | ||||||
| 
 |  | ||||||
|         // USAGE DATA |  | ||||||
| 
 |  | ||||||
|         public Counter OpenOptions { get; private set; } = 0; |  | ||||||
|         public Counter OpenPlugins { get; private set; } = 0; |  | ||||||
|         public Counter OpenAbout   { get; private set; } = 0; |  | ||||||
|         public Counter OpenGuide   { get; private set; } = 0; |  | ||||||
| 
 |  | ||||||
|         public Counter DesktopNotifications { get; private set; } = 0; |  | ||||||
|         public Counter SoundNotifications   { get; private set; } = 0; |  | ||||||
|         public Counter NotificationMutes    { get; private set; } = 0; |  | ||||||
|          |  | ||||||
|         public Counter BrowserContextMenus           { get; private set; } = 0; |  | ||||||
|         public Counter BrowserExtraMouseButtons      { get; private set; } = 0; |  | ||||||
|         public Counter NotificationContextMenus      { get; private set; } = 0; |  | ||||||
|         public Counter NotificationExtraMouseButtons { get; private set; } = 0; |  | ||||||
|         public Counter NotificationKeyboardShortcuts { get; private set; } = 0; |  | ||||||
| 
 |  | ||||||
|         public Counter BrowserReloads   { get; private set; } = 0; |  | ||||||
|         public Counter CopiedUsernames  { get; private set; } = 0; |  | ||||||
|         public Counter ViewedImages     { get; private set; } = 0; |  | ||||||
|         public Counter DownloadedImages { get; private set; } = 0; |  | ||||||
|         public Counter DownloadedVideos { get; private set; } = 0; |  | ||||||
|         public Counter UsedROT13        { get; private set; } = 0; |  | ||||||
| 
 |  | ||||||
|         public Counter TweetScreenshots { get; private set; } = 0; |  | ||||||
|         public Counter TweetDetails     { get; private set; } = 0; |  | ||||||
|         public Counter VideoPlays       { get; private set; } = 0; |  | ||||||
| 
 |  | ||||||
|         // END OF DATA |  | ||||||
| 
 |  | ||||||
|         public event EventHandler PropertyChanged; |  | ||||||
|          |  | ||||||
|         private readonly string file; |  | ||||||
|          |  | ||||||
|         private AnalyticsFile(string file){ |  | ||||||
|             this.file = file; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private AnalyticsFile(){ |  | ||||||
|             this.file = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void SetupProperties(){ |  | ||||||
|             foreach(Counter counter in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(Counter)).Select(prop => (Counter)prop.GetValue(this))){ |  | ||||||
|                 counter.SetOwner(this); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnPropertyChanged(){ |  | ||||||
|             PropertyChanged?.Invoke(this, EventArgs.Empty); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Save(){ |  | ||||||
|             if (file == null){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 Serializer.Write(file, this); |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Analytics File Error", "Could not save the analytics file.", true, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public static AnalyticsFile Load(string file){ |  | ||||||
|             AnalyticsFile config = new AnalyticsFile(file); |  | ||||||
|              |  | ||||||
|             try{ |  | ||||||
|                 Serializer.ReadIfExists(file, config); |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             config.SetupProperties(); |  | ||||||
|             return config; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public interface IProvider{ |  | ||||||
|             AnalyticsFile AnalyticsFile { get; } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public sealed class Counter{ |  | ||||||
|             public int Value { get; private set; } |  | ||||||
| 
 |  | ||||||
|             private AnalyticsFile owner; |  | ||||||
| 
 |  | ||||||
|             public Counter(int value){ |  | ||||||
|                 this.Value = value; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void SetOwner(AnalyticsFile owner){ |  | ||||||
|                 this.owner = owner; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void Trigger(){ |  | ||||||
|                 ++Value; |  | ||||||
|                 owner?.OnPropertyChanged(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public static implicit operator int(Counter counter) => counter.Value; |  | ||||||
|             public static implicit operator Counter(int value) => new Counter(value); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,158 +0,0 @@ | |||||||
| // Uncomment to debug reports locally |  | ||||||
| // #define ANALYTICS_LOCALHOST |  | ||||||
| // #define ANALYTICS_INSTANT |  | ||||||
| 
 |  | ||||||
| using System; |  | ||||||
| using System.Net; |  | ||||||
| using System.Threading.Tasks; |  | ||||||
| using System.Timers; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Analytics{ |  | ||||||
|     sealed class AnalyticsManager : IDisposable{ |  | ||||||
|         private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(14); |  | ||||||
| 
 |  | ||||||
|         private static readonly Uri CollectionUrl = new Uri( |  | ||||||
|             #if (DEBUG && ANALYTICS_LOCALHOST) |  | ||||||
|             "http://localhost/newhome/tweetduck/~breadcrumb/request.php?type=report" |  | ||||||
|             #else |  | ||||||
|             "https://tweetduck.chylex.com/breadcrumb/report" |  | ||||||
|             #endif |  | ||||||
|             ); |  | ||||||
|          |  | ||||||
|         public AnalyticsFile File { get; } |  | ||||||
| 
 |  | ||||||
|         private readonly FormBrowser browser; |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
|         private readonly Timer currentTimer, saveTimer; |  | ||||||
| 
 |  | ||||||
|         public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){ |  | ||||||
|             this.browser = browser; |  | ||||||
|             this.plugins = plugins; |  | ||||||
| 
 |  | ||||||
|             this.File = AnalyticsFile.Load(file); |  | ||||||
|             this.File.PropertyChanged += File_PropertyChanged; |  | ||||||
| 
 |  | ||||||
|             this.currentTimer = new Timer{ SynchronizingObject = browser }; |  | ||||||
|             this.currentTimer.Elapsed += currentTimer_Elapsed; |  | ||||||
| 
 |  | ||||||
|             this.saveTimer = new Timer{ SynchronizingObject = browser, Interval = 60000 }; |  | ||||||
|             this.saveTimer.Elapsed += saveTimer_Elapsed; |  | ||||||
| 
 |  | ||||||
|             if (this.File.LastCollectionVersion != Program.VersionTag){ |  | ||||||
|                 ScheduleReportIn(TimeSpan.FromHours(8), string.Empty); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 RestartTimer(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #if (DEBUG && ANALYTICS_INSTANT) |  | ||||||
|             SendReport(); |  | ||||||
|             #endif |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Dispose(){ |  | ||||||
|             File.PropertyChanged -= File_PropertyChanged; |  | ||||||
| 
 |  | ||||||
|             if (saveTimer.Enabled){ |  | ||||||
|                 File.Save(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             currentTimer.Dispose(); |  | ||||||
|             saveTimer.Dispose(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void File_PropertyChanged(object sender, EventArgs e){ |  | ||||||
|             saveTimer.Enabled = true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void saveTimer_Elapsed(object sender, ElapsedEventArgs e){ |  | ||||||
|             saveTimer.Stop(); |  | ||||||
|             File.Save(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void ScheduleReportIn(TimeSpan delay, string message = null){ |  | ||||||
|             SetLastDataCollectionTime(DateTime.Now.Subtract(CollectionInterval).Add(delay), message); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void SetLastDataCollectionTime(DateTime dt, string message = null){ |  | ||||||
|             File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind); |  | ||||||
|             File.LastCollectionVersion = Program.VersionTag; |  | ||||||
|             File.LastCollectionMessage = message ?? dt.ToString("g", Program.Culture); |  | ||||||
| 
 |  | ||||||
|             File.Save(); |  | ||||||
|             RestartTimer(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void RestartTimer(){ |  | ||||||
|             TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection); |  | ||||||
|             int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes)); |  | ||||||
|              |  | ||||||
|             currentTimer.Interval = Math.Max(minutesTillNext, 2)*60000; |  | ||||||
|             currentTimer.Start(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void currentTimer_Elapsed(object sender, ElapsedEventArgs e){ |  | ||||||
|             currentTimer.Stop(); |  | ||||||
| 
 |  | ||||||
|             TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection); |  | ||||||
|              |  | ||||||
|             if (Math.Floor(diff.TotalMinutes) >= CollectionInterval.TotalMinutes){ |  | ||||||
|                 SendReport(); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 RestartTimer(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         private void SendReport(){ |  | ||||||
|             AnalyticsReportGenerator.ExternalInfo info = AnalyticsReportGenerator.ExternalInfo.From(browser); |  | ||||||
| 
 |  | ||||||
|             Task.Factory.StartNew(() => { |  | ||||||
|                 AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins); |  | ||||||
| 
 |  | ||||||
|                 #if (DEBUG && !ANALYTICS_INSTANT) |  | ||||||
|                 System.Diagnostics.Debugger.Break(); |  | ||||||
|                 #endif |  | ||||||
| 
 |  | ||||||
|                 BrowserUtils.CreateWebClient().UploadValues(CollectionUrl, "POST", report.ToNameValueCollection()); |  | ||||||
|             }).ContinueWith(task => browser.InvokeAsyncSafe(() => { |  | ||||||
|                 if (task.Status == TaskStatus.RanToCompletion){ |  | ||||||
|                     SetLastDataCollectionTime(DateTime.Now); |  | ||||||
|                 } |  | ||||||
|                 else if (task.Exception != null){ |  | ||||||
|                     string message = null; |  | ||||||
| 
 |  | ||||||
|                     if (task.Exception.InnerException is WebException e){ |  | ||||||
|                         switch(e.Status){ |  | ||||||
|                             case WebExceptionStatus.ConnectFailure: |  | ||||||
|                                 message = "Connection Error"; |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case WebExceptionStatus.NameResolutionFailure: |  | ||||||
|                                 message = "DNS Error"; |  | ||||||
|                                 break; |  | ||||||
| 
 |  | ||||||
|                             case WebExceptionStatus.ProtocolError: |  | ||||||
|                                 HttpWebResponse response = e.Response as HttpWebResponse; |  | ||||||
|                                 message = "HTTP Error "+(response != null ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)"); |  | ||||||
|                                 break; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         #if DEBUG |  | ||||||
|                         System.IO.Stream responseStream = e.Response.GetResponseStream(); |  | ||||||
| 
 |  | ||||||
|                         if (responseStream != null){ |  | ||||||
|                             System.Diagnostics.Debug.WriteLine(new System.IO.StreamReader(responseStream).ReadToEnd()); |  | ||||||
|                         } |  | ||||||
|                         #endif |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: "+(task.Exception.InnerException?.Message ?? task.Exception.Message)); |  | ||||||
|                 } |  | ||||||
|             })); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,57 +0,0 @@ | |||||||
| using System.Collections; |  | ||||||
| using System.Collections.Specialized; |  | ||||||
| using System.Text; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Analytics{ |  | ||||||
|     sealed class AnalyticsReport : IEnumerable{ |  | ||||||
|         private OrderedDictionary data = new OrderedDictionary(32); |  | ||||||
|         private int separators; |  | ||||||
| 
 |  | ||||||
|         public void Add(int ignored){ // adding separators to pretty print |  | ||||||
|             data.Add((++separators).ToString(), null); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Add(string key, string value){ |  | ||||||
|             data.Add(key, value); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public AnalyticsReport FinalizeReport(){ |  | ||||||
|             if (!data.IsReadOnly){ |  | ||||||
|                 data = data.AsReadOnly(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return this; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public IEnumerator GetEnumerator(){ |  | ||||||
|             return data.GetEnumerator(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public NameValueCollection ToNameValueCollection(){ |  | ||||||
|             NameValueCollection collection = new NameValueCollection(); |  | ||||||
| 
 |  | ||||||
|             foreach(DictionaryEntry entry in data){ |  | ||||||
|                 if (entry.Value != null){ |  | ||||||
|                     collection.Add(((string)entry.Key).ToLower().Replace(' ', '_'), (string)entry.Value); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return collection; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override string ToString(){ |  | ||||||
|             StringBuilder build = new StringBuilder(); |  | ||||||
| 
 |  | ||||||
|             foreach(DictionaryEntry entry in data){ |  | ||||||
|                 if (entry.Value == null){ |  | ||||||
|                     build.AppendLine(); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     build.AppendLine(entry.Key+": "+entry.Value); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return build.ToString(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,326 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.IO; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using Microsoft.Win32; |  | ||||||
| using TweetDuck.Configuration; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Management; |  | ||||||
| using System.Text.RegularExpressions; |  | ||||||
| using TweetDuck.Core.Handling; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Plugins.Enums; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Analytics{ |  | ||||||
|     static class AnalyticsReportGenerator{ |  | ||||||
|         public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins){ |  | ||||||
|             Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData; |  | ||||||
| 
 |  | ||||||
|             return new AnalyticsReport{ |  | ||||||
|                 { "App Version"   , Program.VersionTag }, |  | ||||||
|                 { "App Type"      , Program.IsPortable ? "portable" : "installed" }, |  | ||||||
|                 { "App Dev Tools" , Bool(ContextMenuBase.HasDevTools) }, |  | ||||||
|                 0, |  | ||||||
|                 { "System Name"        , SystemName }, |  | ||||||
|                 { "System Edition"     , SystemEdition }, |  | ||||||
|                 { "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" }, |  | ||||||
|                 { "System Build"       , SystemBuild }, |  | ||||||
|                 { "System Locale"      , Program.Culture.Name.ToLower() }, |  | ||||||
|                 0, |  | ||||||
|                 { "RAM" , Exact(RamSize) }, |  | ||||||
|                 { "GPU" , GpuVendor }, |  | ||||||
|                 0, |  | ||||||
|                 { "Screen Count"      , Exact(Screen.AllScreens.Length) }, |  | ||||||
|                 { "Screen Resolution" , info.Resolution ?? "(unknown)" }, |  | ||||||
|                 { "Screen DPI"        , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" }, |  | ||||||
|                 0, |  | ||||||
|                 { "Hardware Acceleration"     , Bool(SysConfig.HardwareAcceleration) }, |  | ||||||
|                 { "Clear Cache Automatically" , Bool(SysConfig.ClearCacheAutomatically) }, |  | ||||||
|                 { "Clear Cache Threshold"     , Exact(SysConfig.ClearCacheThreshold) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Expand Links"                  , Bool(UserConfig.ExpandLinksOnHover) }, |  | ||||||
|                 { "Search In First Column"        , Bool(UserConfig.OpenSearchInFirstColumn) }, |  | ||||||
|                 { "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) }, |  | ||||||
|                 { "Best Image Quality"            , Bool(UserConfig.BestImageQuality) }, |  | ||||||
|                 { "Animated Images"               , Bool(UserConfig.EnableAnimatedImages) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Smooth Scrolling" , Bool(UserConfig.EnableSmoothScrolling) }, |  | ||||||
|                 { "Custom Browser"   , CustomBrowser }, |  | ||||||
|                 { "Zoom"             , Exact(UserConfig.ZoomLevel) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Spell Check"                 , Bool(UserConfig.EnableSpellCheck) }, |  | ||||||
|                 { "Spell Check Language"        , UserConfig.SpellCheckLanguage.ToLower() }, |  | ||||||
|                 { "Translation Target Language" , UserConfig.TranslationTarget }, |  | ||||||
|                 0, |  | ||||||
|                 { "Updates"          , Bool(UserConfig.EnableUpdateCheck) }, |  | ||||||
|                 { "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Tray"           , TrayMode }, |  | ||||||
|                 { "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Notification Position"            , NotificationPosition }, |  | ||||||
|                 { "Notification Size"                , NotificationSize }, |  | ||||||
|                 { "Notification Timer"               , NotificationTimer }, |  | ||||||
|                 { "Notification Timer Speed"         , RoundUp(UserConfig.NotificationDurationValue, 5) }, |  | ||||||
|                 { "Notification Scroll Speed"        , Exact(UserConfig.NotificationScrollSpeed) }, |  | ||||||
|                 { "Notification Column Title"        , Bool(UserConfig.DisplayNotificationColumn) }, |  | ||||||
|                 { "Notification Media Previews"      , Bool(UserConfig.NotificationMediaPreviews) }, |  | ||||||
|                 { "Notification Link Skip"           , Bool(UserConfig.NotificationSkipOnLinkClick) }, |  | ||||||
|                 { "Notification Non-Intrusive"       , Bool(UserConfig.NotificationNonIntrusiveMode) }, |  | ||||||
|                 { "Notification Idle Pause"          , Exact(UserConfig.NotificationIdlePauseSeconds) }, |  | ||||||
|                 { "Custom Sound Notification"        , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) }, |  | ||||||
|                 { "Custom Sound Notification Volume" , RoundUp(UserConfig.NotificationSoundVolume, 5) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Program Arguments"       , List(ProgramArguments) }, |  | ||||||
|                 { "Custom CEF Arguments"    , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) }, |  | ||||||
|                 { "Custom Browser CSS"      , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) }, |  | ||||||
|                 { "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Plugins All"     , List(plugins.Plugins.Select(Plugin)) }, |  | ||||||
|                 { "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(Plugin)) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Theme"               , Dict(editLayoutDesign, "_theme",                  "light/def") }, |  | ||||||
|                 { "Column Width"        , Dict(editLayoutDesign, "columnWidth",             "310px/def") }, |  | ||||||
|                 { "Font Size"           , Dict(editLayoutDesign, "fontSize",                "12px/def") }, |  | ||||||
|                 { "Large Quote Font"    , Dict(editLayoutDesign, "increaseQuoteTextSize",   "false/def") }, |  | ||||||
|                 { "Small Compose Font"  , Dict(editLayoutDesign, "smallComposeTextSize",    "false/def") }, |  | ||||||
|                 { "Avatar Radius"       , Dict(editLayoutDesign, "avatarRadius",            "2/def") }, |  | ||||||
|                 { "Hide Tweet Actions"  , Dict(editLayoutDesign, "hideTweetActions",        "true/def") }, |  | ||||||
|                 { "Move Tweet Actions"  , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") }, |  | ||||||
|                 { "Theme Color Tweaks"  , Dict(editLayoutDesign, "themeColorTweaks",        "true/def") }, |  | ||||||
|                 { "Revert Icons"        , Dict(editLayoutDesign, "revertIcons",             "true/def") }, |  | ||||||
|                 { "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations",      "true/def") }, |  | ||||||
|                 { "Reply Account Mode"  , ReplyAccountConfigFromPlugin }, |  | ||||||
|                 { "Template Count"      , Exact(TemplateCountFromPlugin) }, |  | ||||||
|                 0, |  | ||||||
|                 { "Opened Options"                   , LogRound(file.OpenOptions, 4) }, |  | ||||||
|                 { "Opened Plugins"                   , LogRound(file.OpenPlugins, 4) }, |  | ||||||
|                 { "Opened About"                     , LogRound(file.OpenAbout, 4) }, |  | ||||||
|                 { "Opened Guide"                     , LogRound(file.OpenGuide, 4) }, |  | ||||||
|                 { "Desktop Notifications"            , LogRound(file.DesktopNotifications, 5) }, |  | ||||||
|                 { "Sound Notifications"              , LogRound(file.SoundNotifications, 5) }, |  | ||||||
|                 { "Notification Mutes"               , LogRound(file.NotificationMutes, 2) }, |  | ||||||
|                 { "Browser Context Menus"            , LogRound(file.BrowserContextMenus, 2) }, |  | ||||||
|                 { "Browser Extra Mouse Buttons"      , LogRound(file.BrowserExtraMouseButtons, 2) }, |  | ||||||
|                 { "Notification Context Menus"       , LogRound(file.NotificationContextMenus, 2) }, |  | ||||||
|                 { "Notification Extra Mouse Buttons" , LogRound(file.NotificationExtraMouseButtons, 2) }, |  | ||||||
|                 { "Notification Keyboard Shortcuts"  , LogRound(file.NotificationKeyboardShortcuts, 2) }, |  | ||||||
|                 { "Browser Reloads"                  , LogRound(file.BrowserReloads, 2) }, |  | ||||||
|                 { "Copied Usernames"                 , LogRound(file.CopiedUsernames, 2) }, |  | ||||||
|                 { "Viewed Images"                    , LogRound(file.ViewedImages, 2) }, |  | ||||||
|                 { "Downloaded Images"                , LogRound(file.DownloadedImages, 2) }, |  | ||||||
|                 { "Downloaded Videos"                , LogRound(file.DownloadedVideos, 2) }, |  | ||||||
|                 { "Used ROT13"                       , LogRound(file.UsedROT13, 2) }, |  | ||||||
|                 { "Tweet Screenshots"                , LogRound(file.TweetScreenshots, 2) }, |  | ||||||
|                 { "Tweet Details"                    , LogRound(file.TweetDetails, 2) }, |  | ||||||
|                 { "Video Plays"                      , LogRound(file.VideoPlays, 4) } |  | ||||||
|             }.FinalizeReport(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static UserConfig UserConfig => Program.UserConfig; |  | ||||||
|         private static SystemConfig SysConfig => Program.SystemConfig; |  | ||||||
| 
 |  | ||||||
|         private static string Bool(bool value) => value ? "on" : "off"; |  | ||||||
|         private static string Exact(int value) => value.ToString(); |  | ||||||
|         private static string RoundUp(int value, int multiple) => (multiple*(int)Math.Ceiling((double)value/multiple)).ToString(); |  | ||||||
|         private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString(); |  | ||||||
|         private static string Plugin(Plugin plugin) => plugin.Group.GetIdentifierPrefixShort()+plugin.Identifier.Substring(plugin.Group.GetIdentifierPrefix().Length); |  | ||||||
|         private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def; |  | ||||||
|         private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)")); |  | ||||||
| 
 |  | ||||||
|         private static string SystemName { get; } |  | ||||||
|         private static string SystemEdition { get; } |  | ||||||
|         private static string SystemBuild { get; } |  | ||||||
|         private static int RamSize { get; } |  | ||||||
|         private static string GpuVendor { get; } |  | ||||||
|         private static string[] ProgramArguments { get; } |  | ||||||
| 
 |  | ||||||
|         static AnalyticsReportGenerator(){ |  | ||||||
|             string osName, osEdition, osBuild; |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 using(RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false)){ |  | ||||||
|                     // ReSharper disable once PossibleNullReferenceException |  | ||||||
|                     osName = key.GetValue("ProductName") as string; |  | ||||||
|                     osBuild = key.GetValue("CurrentBuild") as string; |  | ||||||
|                     osEdition = null; |  | ||||||
|                      |  | ||||||
|                     if (osName != null){ |  | ||||||
|                         Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$"); |  | ||||||
| 
 |  | ||||||
|                         if (match.Success){ |  | ||||||
|                             osName = match.Groups[1].Value; |  | ||||||
|                             osEdition = match.Groups[2].Value; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             }catch{ |  | ||||||
|                 osName = osEdition = osBuild = null; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             SystemName = osName ?? "Windows (unknown)"; |  | ||||||
|             SystemEdition = osEdition ?? "(unknown)"; |  | ||||||
|             SystemBuild = osBuild ?? "(unknown)"; |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory")){ |  | ||||||
|                     foreach(ManagementBaseObject obj in searcher.Get()){ |  | ||||||
|                         RamSize += (int)((ulong)obj["Capacity"]/(1024L*1024L)); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             }catch{ |  | ||||||
|                 RamSize = 0; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             string gpu = null; |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController")){ |  | ||||||
|                     foreach(ManagementBaseObject obj in searcher.Get()){ |  | ||||||
|                         string vendor = obj["Caption"] as string; |  | ||||||
| 
 |  | ||||||
|                         if (!string.IsNullOrEmpty(vendor)){ |  | ||||||
|                             gpu = vendor; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             }catch{ |  | ||||||
|                 // rip |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             GpuVendor = gpu ?? "(unknown)"; |  | ||||||
| 
 |  | ||||||
|             Dictionary<string, string> args = new Dictionary<string, string>(); |  | ||||||
|             Arguments.GetCurrentClean().ToDictionary(args); |  | ||||||
|             ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static string CustomBrowser{ |  | ||||||
|             get{ |  | ||||||
|                 return Path.GetFileName(UserConfig.BrowserPath) ?? string.Empty; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static string TrayMode{ |  | ||||||
|             get{ |  | ||||||
|                 switch(UserConfig.TrayBehavior){ |  | ||||||
|                     case TrayIcon.Behavior.DisplayOnly: return "icon"; |  | ||||||
|                     case TrayIcon.Behavior.MinimizeToTray: return "minimize"; |  | ||||||
|                     case TrayIcon.Behavior.CloseToTray: return "close"; |  | ||||||
|                     case TrayIcon.Behavior.Combined: return "combined"; |  | ||||||
|                     default: return "off"; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static string NotificationPosition{ |  | ||||||
|             get{ |  | ||||||
|                 switch(UserConfig.NotificationPosition){ |  | ||||||
|                     case TweetNotification.Position.TopLeft: return "top left"; |  | ||||||
|                     case TweetNotification.Position.TopRight: return "top right"; |  | ||||||
|                     case TweetNotification.Position.BottomLeft: return "bottom left"; |  | ||||||
|                     case TweetNotification.Position.BottomRight: return "bottom right"; |  | ||||||
|                     default: return "custom"; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static string NotificationSize{ |  | ||||||
|             get{ |  | ||||||
|                 switch(UserConfig.NotificationSize){ |  | ||||||
|                     case TweetNotification.Size.Auto: return "auto"; |  | ||||||
|                     default: return RoundUp(UserConfig.CustomNotificationSize.Width, 20)+"x"+RoundUp(UserConfig.CustomNotificationSize.Height, 20); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static string NotificationTimer{ |  | ||||||
|             get{ |  | ||||||
|                 if (!UserConfig.DisplayNotificationTimer){ |  | ||||||
|                     return "off"; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     return UserConfig.NotificationTimerCountDown ? "count down" : "count up"; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static Dictionary<string, string> EditLayoutDesignPluginData{ |  | ||||||
|             get{ |  | ||||||
|                 Dictionary<string, string> dict = new Dictionary<string, string>(); |  | ||||||
|                  |  | ||||||
|                 try{ |  | ||||||
|                     string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json")); |  | ||||||
| 
 |  | ||||||
|                     foreach(Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")){ |  | ||||||
|                         dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"'); |  | ||||||
|                     } |  | ||||||
|                 }catch{ |  | ||||||
|                     // rip |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return dict; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static int TemplateCountFromPlugin{ |  | ||||||
|             get{ |  | ||||||
|                 try{ |  | ||||||
|                     string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json")); |  | ||||||
|                     return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":")); |  | ||||||
|                 }catch{ |  | ||||||
|                     return 0; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static string ReplyAccountConfigFromPlugin{ |  | ||||||
|             get{ |  | ||||||
|                 try{ |  | ||||||
|                     string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", ""); |  | ||||||
| 
 |  | ||||||
|                     Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)"); |  | ||||||
|                     Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline); |  | ||||||
| 
 |  | ||||||
|                     if (!matchType.Success){ |  | ||||||
|                         return data.Contains("defaultAccount:\"\"") ? "(legacy)" : "(unknown)"; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account"; |  | ||||||
|                     return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/"+accType : accType; |  | ||||||
|                 }catch{ |  | ||||||
|                     return "(unknown)"; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public class ExternalInfo{ |  | ||||||
|             public static ExternalInfo From(Form form){ |  | ||||||
|                 if (form == null){ |  | ||||||
|                     return new ExternalInfo(); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     Screen screen = Screen.FromControl(form); // works on multi-monitor setups even in tray |  | ||||||
|                     int dpi; |  | ||||||
| 
 |  | ||||||
|                     using(Graphics graphics = form.CreateGraphics()){ |  | ||||||
|                         dpi = (int)graphics.DpiY; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     return new ExternalInfo{ |  | ||||||
|                         Resolution = screen.Bounds.Width+"x"+screen.Bounds.Height, |  | ||||||
|                         DPI = dpi |  | ||||||
|                     }; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public string Resolution { get; private set; } |  | ||||||
|             public int? DPI { get; private set; } |  | ||||||
| 
 |  | ||||||
|             private ExternalInfo(){} |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| using System.ComponentModel; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.IO; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other{ |  | ||||||
|     sealed partial class FormAbout : Form{ |  | ||||||
|         private const string TipsLink = "https://github.com/chylex/TweetDuck/wiki"; |  | ||||||
|         private const string IssuesLink = "https://github.com/chylex/TweetDuck/issues"; |  | ||||||
| 
 |  | ||||||
|         public FormAbout(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             Text = "About "+Program.BrandName+" "+Program.VersionTag; |  | ||||||
| 
 |  | ||||||
|             labelDescription.Text = "TweetDuck was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license."; |  | ||||||
|              |  | ||||||
|             labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website)); |  | ||||||
|             labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink)); |  | ||||||
|             labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, IssuesLink)); |  | ||||||
| 
 |  | ||||||
|             MemoryStream logoStream = new MemoryStream(Properties.Resources.avatar); |  | ||||||
|             pictureLogo.Image = Image.FromStream(logoStream); |  | ||||||
|             Disposed += (sender, args) => logoStream.Dispose(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){ |  | ||||||
|             BrowserUtils.OpenExternalBrowser(e.Link.LinkData as string); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){ |  | ||||||
|             ShowGuide(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormAbout_HelpButtonClicked(object sender, CancelEventArgs e){ |  | ||||||
|             e.Cancel = true; |  | ||||||
|             ShowGuide(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void ShowGuide(){ |  | ||||||
|             FormGuide.Show(); |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,131 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using CefSharp.WinForms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Handling; |  | ||||||
| using TweetDuck.Core.Handling.General; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using System.Text.RegularExpressions; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other{ |  | ||||||
|     sealed partial class FormGuide : Form{ |  | ||||||
|         private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/"; |  | ||||||
|         private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?"; |  | ||||||
| 
 |  | ||||||
|         private static readonly ResourceLink DummyPage = new ResourceLink("http://td/dummy", ResourceHandler.FromString("")); |  | ||||||
| 
 |  | ||||||
|         public static bool CheckGuideUrl(string url, out string hash){ |  | ||||||
|             if (!url.Contains("//tweetduck.chylex.com/guide")){ |  | ||||||
|                 hash = null; |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             string path = url.Substring(url.IndexOf("/guide")+1); |  | ||||||
|             Match match = Regex.Match(path, GuidePathRegex); |  | ||||||
| 
 |  | ||||||
|             if (match.Success){ |  | ||||||
|                 hash = match.Groups[1].Value; |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 hash = null; |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void Show(string hash = null){ |  | ||||||
|             string url = GuideUrl+(hash ?? string.Empty); |  | ||||||
|             FormGuide guide = FormManager.TryFind<FormGuide>(); |  | ||||||
|              |  | ||||||
|             if (guide == null){ |  | ||||||
|                 FormBrowser owner = FormManager.TryFind<FormBrowser>(); |  | ||||||
| 
 |  | ||||||
|                 if (owner != null){ |  | ||||||
|                     owner.AnalyticsFile.OpenGuide.Trigger(); |  | ||||||
|                     new FormGuide(url, owner).Show(owner); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 guide.Reload(url); |  | ||||||
|                 guide.Activate(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly ChromiumWebBrowser browser; |  | ||||||
|         private string nextUrl; |  | ||||||
| 
 |  | ||||||
|         private FormGuide(string url, FormBrowser owner){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             Text = Program.BrandName+" Guide"; |  | ||||||
|             Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4); |  | ||||||
|             VisibleChanged += (sender, args) => this.MoveToCenter(owner); |  | ||||||
|              |  | ||||||
|             this.browser = new ChromiumWebBrowser(url){ |  | ||||||
|                 MenuHandler = new ContextMenuGuide(owner), |  | ||||||
|                 JsDialogHandler = new JavaScriptDialogHandler(), |  | ||||||
|                 LifeSpanHandler = new LifeSpanHandler(), |  | ||||||
|                 RequestHandler = new RequestHandlerBrowser() |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             browser.LoadingStateChanged += browser_LoadingStateChanged; |  | ||||||
|             browser.FrameLoadStart += browser_FrameLoadStart; |  | ||||||
|             browser.FrameLoadEnd += browser_FrameLoadEnd; |  | ||||||
|              |  | ||||||
|             browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb(); |  | ||||||
|             browser.Dock = DockStyle.None; |  | ||||||
|             browser.Location = ControlExtensions.InvisibleLocation; |  | ||||||
| 
 |  | ||||||
|             browser.SetupResourceHandler(DummyPage); |  | ||||||
| 
 |  | ||||||
|             Controls.Add(browser); |  | ||||||
| 
 |  | ||||||
|             Disposed += (sender, args) => { |  | ||||||
|                 Program.UserConfig.ZoomLevelChanged -= Config_ZoomLevelChanged; |  | ||||||
|                 browser.Dispose(); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Reload(string url){ |  | ||||||
|             nextUrl = url; |  | ||||||
|             browser.LoadingStateChanged += browser_LoadingStateChanged; |  | ||||||
|             browser.Dock = DockStyle.None; |  | ||||||
|             browser.Location = ControlExtensions.InvisibleLocation; |  | ||||||
|             browser.Load(DummyPage.Url); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ |  | ||||||
|             if (!e.IsLoading){ |  | ||||||
|                 if (browser.Address == DummyPage.Url){ |  | ||||||
|                     browser.Load(nextUrl); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     this.InvokeAsyncSafe(() => { |  | ||||||
|                         browser.Location = Point.Empty; |  | ||||||
|                         browser.Dock = DockStyle.Fill; |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     browser.LoadingStateChanged -= browser_LoadingStateChanged; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){ |  | ||||||
|             BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ |  | ||||||
|             ScriptLoader.ExecuteScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void Config_ZoomLevelChanged(object sender, EventArgs e){ |  | ||||||
|             BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,222 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other{ |  | ||||||
|     [Flags] |  | ||||||
|     enum ControlType{ |  | ||||||
|         None = 0, |  | ||||||
|         Accept = 1, // triggered by pressing enter when a non-button is focused |  | ||||||
|         Cancel = 2, // triggered by closing the dialog without pressing a button |  | ||||||
|         Focused = 4 // active control after the dialog is showed |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     sealed partial class FormMessage : Form{ |  | ||||||
|         public const string OK = "OK"; |  | ||||||
|         public const string Yes = "Yes"; |  | ||||||
|         public const string No = "No"; |  | ||||||
|         public const string Cancel = "Cancel"; |  | ||||||
|         public const string Retry = "Retry"; |  | ||||||
|         public const string Ignore = "Ignore"; |  | ||||||
|         public const string Exit = "Exit"; |  | ||||||
| 
 |  | ||||||
|         public static bool Information(string caption, string text, string buttonAccept, string buttonCancel = null){ |  | ||||||
|             return Show(caption, text, MessageBoxIcon.Information, buttonAccept, buttonCancel); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool Warning(string caption, string text, string buttonAccept, string buttonCancel = null){ |  | ||||||
|             return Show(caption, text, MessageBoxIcon.Warning, buttonAccept, buttonCancel); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool Error(string caption, string text, string buttonAccept, string buttonCancel = null){ |  | ||||||
|             return Show(caption, text, MessageBoxIcon.Error, buttonAccept, buttonCancel); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool Question(string caption, string text, string buttonAccept, string buttonCancel = null){ |  | ||||||
|             return Show(caption, text, MessageBoxIcon.Question, buttonAccept, buttonCancel); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool Show(string caption, string text, MessageBoxIcon icon, string button){ |  | ||||||
|             return Show(caption, text, icon, button, null); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool Show(string caption, string text, MessageBoxIcon icon, string buttonAccept, string buttonCancel){ |  | ||||||
|             using(FormMessage message = new FormMessage(caption, text, icon)){ |  | ||||||
|                 if (buttonCancel == null){ |  | ||||||
|                     message.AddButton(buttonAccept, DialogResult.OK, ControlType.Cancel | ControlType.Focused); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     message.AddButton(buttonCancel, DialogResult.Cancel, ControlType.Cancel); |  | ||||||
|                     message.AddButton(buttonAccept, DialogResult.OK, ControlType.Accept | ControlType.Focused); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return message.ShowDialog() == DialogResult.OK; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Instance |  | ||||||
| 
 |  | ||||||
|         public Button ClickedButton { get; private set; } |  | ||||||
| 
 |  | ||||||
|         public bool HasIcon => icon != null; |  | ||||||
|         public int ActionPanelY => panelActions.Location.Y; |  | ||||||
| 
 |  | ||||||
|         private int ClientWidth{ |  | ||||||
|             get => ClientSize.Width; |  | ||||||
|             set => ClientSize = new Size(value, ClientSize.Height); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private int ButtonDistance{ |  | ||||||
|             get => BrowserUtils.Scale(96, dpiScale); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly Icon icon; |  | ||||||
|         private readonly bool isReady; |  | ||||||
|         private readonly float dpiScale; |  | ||||||
| 
 |  | ||||||
|         private int realFormWidth, minFormWidth; |  | ||||||
|         private int buttonCount; |  | ||||||
|         private int prevLabelWidth, prevLabelHeight; |  | ||||||
|         private bool wasLabelMultiline; |  | ||||||
| 
 |  | ||||||
|         public FormMessage(string caption, string text, MessageBoxIcon messageIcon){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.dpiScale = this.GetDPIScale(); |  | ||||||
| 
 |  | ||||||
|             this.prevLabelWidth = labelMessage.Width; |  | ||||||
|             this.prevLabelHeight = labelMessage.Height; |  | ||||||
|             this.minFormWidth = BrowserUtils.Scale(42, dpiScale); |  | ||||||
| 
 |  | ||||||
|             switch(messageIcon){ |  | ||||||
|                 case MessageBoxIcon.Information: |  | ||||||
|                     icon = SystemIcons.Information; |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case MessageBoxIcon.Warning: |  | ||||||
|                     icon = SystemIcons.Warning; |  | ||||||
|                     break; |  | ||||||
|                      |  | ||||||
|                 case MessageBoxIcon.Error: |  | ||||||
|                     icon = SystemIcons.Error; |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case MessageBoxIcon.Question: |  | ||||||
|                     icon = SystemIcons.Question; |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 default: |  | ||||||
|                     icon = null; |  | ||||||
|                     labelMessage.Location = new Point(BrowserUtils.Scale(19, dpiScale), labelMessage.Location.Y); // 19 instead of 9 due to larger height |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             this.isReady = true; |  | ||||||
| 
 |  | ||||||
|             this.Text = caption; |  | ||||||
|             this.labelMessage.Text = text.Replace("\r", "").Replace("\n", Environment.NewLine); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormMessage_SizeChanged(object sender, EventArgs e){ |  | ||||||
|             RecalculateButtonLocation(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public Button AddButton(string title, ControlType type){ |  | ||||||
|             return AddButton(title, DialogResult.OK, type); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public Button AddButton(string title, DialogResult result = DialogResult.OK, ControlType type = ControlType.None){ |  | ||||||
|             Button button = new Button{ |  | ||||||
|                 Anchor = AnchorStyles.Bottom, |  | ||||||
|                 Font = SystemFonts.MessageBoxFont, |  | ||||||
|                 Location = new Point(0, 12), |  | ||||||
|                 Size = new Size(BrowserUtils.Scale(88, dpiScale), BrowserUtils.Scale(26, dpiScale)), |  | ||||||
|                 TabIndex = 256-buttonCount, |  | ||||||
|                 Text = title, |  | ||||||
|                 UseVisualStyleBackColor = true |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             button.Click += (sender, args) => { |  | ||||||
|                 ClickedButton = (Button)sender; |  | ||||||
|                 DialogResult = result; |  | ||||||
|                 Close(); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             panelActions.Controls.Add(button); |  | ||||||
|             ++buttonCount; |  | ||||||
| 
 |  | ||||||
|             minFormWidth += ButtonDistance; |  | ||||||
|             ClientWidth = Math.Max(realFormWidth, minFormWidth); |  | ||||||
|             RecalculateButtonLocation(); |  | ||||||
| 
 |  | ||||||
|             if (type.HasFlag(ControlType.Accept)){ |  | ||||||
|                 AcceptButton = button; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (type.HasFlag(ControlType.Cancel)){ |  | ||||||
|                 CancelButton = button; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (type.HasFlag(ControlType.Focused)){ |  | ||||||
|                 ActiveControl = button; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return button; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void AddActionControl(Control control){ |  | ||||||
|             panelActions.Controls.Add(control); |  | ||||||
|              |  | ||||||
|             control.Size = new Size(BrowserUtils.Scale(control.Width, dpiScale), BrowserUtils.Scale(control.Height, dpiScale)); |  | ||||||
| 
 |  | ||||||
|             minFormWidth += control.Width+control.Margin.Horizontal; |  | ||||||
|             ClientWidth = Math.Max(realFormWidth, minFormWidth); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         private void RecalculateButtonLocation(){ |  | ||||||
|             int dist = ButtonDistance; |  | ||||||
|             int start = ClientWidth-dist; |  | ||||||
| 
 |  | ||||||
|             for(int index = 0; index < buttonCount; index++){ |  | ||||||
|                 Control control = panelActions.Controls[index]; |  | ||||||
|                 control.Location = new Point(start-index*dist, control.Location.Y); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void labelMessage_SizeChanged(object sender, EventArgs e){ |  | ||||||
|             if (!isReady){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bool isMultiline = labelMessage.Height > labelMessage.MinimumSize.Height; |  | ||||||
|             int labelOffset = BrowserUtils.Scale(8, dpiScale); |  | ||||||
| 
 |  | ||||||
|             if (isMultiline && !wasLabelMultiline){ |  | ||||||
|                 labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-labelOffset); |  | ||||||
|                 prevLabelHeight += labelOffset; |  | ||||||
|             } |  | ||||||
|             else if (!isMultiline && wasLabelMultiline){ |  | ||||||
|                 labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+labelOffset); |  | ||||||
|                 prevLabelHeight -= labelOffset; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             realFormWidth = ClientWidth-(icon == null ? BrowserUtils.Scale(50, dpiScale) : 0)+labelMessage.Width-prevLabelWidth; |  | ||||||
|             ClientWidth = Math.Max(realFormWidth, minFormWidth); |  | ||||||
|             Height += labelMessage.Height-prevLabelHeight; |  | ||||||
| 
 |  | ||||||
|             prevLabelWidth = labelMessage.Width; |  | ||||||
|             prevLabelHeight = labelMessage.Height; |  | ||||||
|             wasLabelMultiline = isMultiline; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected override void OnPaint(PaintEventArgs e){ |  | ||||||
|             if (icon != null){ |  | ||||||
|                 e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1+BrowserUtils.Scale(25, dpiScale)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             base.OnPaint(e); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,109 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Plugins.Controls; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other{ |  | ||||||
|     sealed partial class FormPlugins : Form{ |  | ||||||
|         private readonly PluginManager pluginManager; |  | ||||||
|          |  | ||||||
|         public FormPlugins(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             Text = Program.BrandName+" Plugins"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public FormPlugins(PluginManager pluginManager) : this(){ |  | ||||||
|             this.pluginManager = pluginManager; |  | ||||||
| 
 |  | ||||||
|             if (!Program.UserConfig.PluginsWindowSize.IsEmpty){ |  | ||||||
|                 Size targetSize = Program.UserConfig.PluginsWindowSize; |  | ||||||
|                 Size = new Size(Math.Max(MinimumSize.Width, targetSize.Width), Math.Max(MinimumSize.Height, targetSize.Height)); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             Shown += (sender, args) => { |  | ||||||
|                 ReloadPluginList(); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             FormClosed += (sender, args) => { |  | ||||||
|                 Program.UserConfig.PluginsWindowSize = Size; |  | ||||||
|                 Program.UserConfig.Save(); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             ResizeEnd += (sender, args) => { |  | ||||||
|                 timerLayout.Start(); |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private int GetPluginOrderIndex(Plugin plugin){ |  | ||||||
|             return !plugin.CanRun ? 0 : pluginManager.Config.IsEnabled(plugin) ? 1 : 2; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void ReloadPluginList(){ |  | ||||||
|             flowLayoutPlugins.Controls.Clear(); |  | ||||||
|             flowLayoutPlugins.SuspendLayout(); |  | ||||||
| 
 |  | ||||||
|             foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){ |  | ||||||
|                 flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin)); |  | ||||||
| 
 |  | ||||||
|                 flowLayoutPlugins.Controls.Add(new Panel{ |  | ||||||
|                     BackColor = Color.DimGray, |  | ||||||
|                     Margin = new Padding(0), |  | ||||||
|                     Size = new Size(1, 1) |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             flowLayoutPlugins.ResumeLayout(true); |  | ||||||
|              |  | ||||||
|             timerLayout_Tick(null, EventArgs.Empty); |  | ||||||
|             timerLayout.Start(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void timerLayout_Tick(object sender, EventArgs e){ |  | ||||||
|             timerLayout.Stop(); |  | ||||||
|              |  | ||||||
|             // stupid WinForms scrollbars and panels |  | ||||||
|             Padding = new Padding(Padding.Left, Padding.Top, Padding.Right+1, Padding.Bottom+1); |  | ||||||
|             Padding = new Padding(Padding.Left, Padding.Top, Padding.Right-1, Padding.Bottom-1); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void flowLayoutPlugins_Resize(object sender, EventArgs e){ |  | ||||||
|             Control lastPlugin = flowLayoutPlugins.Controls.OfType<PluginControl>().LastOrDefault(); |  | ||||||
|              |  | ||||||
|             if (lastPlugin == null){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bool showScrollBar = lastPlugin.Location.Y+lastPlugin.Height+1 >= flowLayoutPlugins.Height; |  | ||||||
|             int horizontalOffset = showScrollBar ? SystemInformation.VerticalScrollBarWidth : 0; |  | ||||||
|              |  | ||||||
|             flowLayoutPlugins.AutoScroll = showScrollBar; |  | ||||||
|             flowLayoutPlugins.VerticalScroll.Visible = showScrollBar; |  | ||||||
| 
 |  | ||||||
|             foreach(Control control in flowLayoutPlugins.Controls){ |  | ||||||
|                 control.Width = flowLayoutPlugins.Width-control.Margin.Horizontal-horizontalOffset; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             flowLayoutPlugins.Controls[flowLayoutPlugins.Controls.Count-1].Visible = !showScrollBar; |  | ||||||
|             flowLayoutPlugins.Focus(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnOpenFolder_Click(object sender, EventArgs e){ |  | ||||||
|             using(Process.Start("explorer.exe", '"'+pluginManager.PathCustomPlugins+'"')){} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnReload_Click(object sender, EventArgs e){ |  | ||||||
|             if (FormMessage.Warning("Reloading Plugins", "This will also reload the browser window. Do you want to proceed?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                 pluginManager.Reload(); |  | ||||||
|                 ReloadPluginList(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnClose_Click(object sender, EventArgs e){ |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,187 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Handling.General; |  | ||||||
| using TweetDuck.Core.Notification.Example; |  | ||||||
| using TweetDuck.Core.Other.Analytics; |  | ||||||
| using TweetDuck.Core.Other.Settings; |  | ||||||
| using TweetDuck.Core.Other.Settings.Dialogs; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| using TweetDuck.Updates; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other{ |  | ||||||
|     sealed partial class FormSettings : Form{ |  | ||||||
|         private readonly FormBrowser browser; |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
| 
 |  | ||||||
|         private readonly int buttonHeight; |  | ||||||
| 
 |  | ||||||
|         private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(4); |  | ||||||
|         private SettingsTab currentTab; |  | ||||||
| 
 |  | ||||||
|         public bool ShouldReloadBrowser { get; private set; } |  | ||||||
| 
 |  | ||||||
|         public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             Text = Program.BrandName+" Options"; |  | ||||||
| 
 |  | ||||||
|             this.browser = browser; |  | ||||||
|             this.browser.PauseNotification(); |  | ||||||
| 
 |  | ||||||
|             this.plugins = plugins; |  | ||||||
|              |  | ||||||
|             this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1; |  | ||||||
| 
 |  | ||||||
|             AddButton("General", () => new TabSettingsGeneral(this.browser.ReloadColumns, updates)); |  | ||||||
|             AddButton("Locales", () => new TabSettingsLocales()); |  | ||||||
|             AddButton("System Tray", () => new TabSettingsTray()); |  | ||||||
|             AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins))); |  | ||||||
|             AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification)); |  | ||||||
|             AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins)); |  | ||||||
|             AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS)); |  | ||||||
| 
 |  | ||||||
|             SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){ |  | ||||||
|             currentTab.Control.OnClosing(); |  | ||||||
| 
 |  | ||||||
|             foreach(SettingsTab tab in tabs.Values){ |  | ||||||
|                 if (tab.IsInitialized){ |  | ||||||
|                     tab.Control.Dispose(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             Program.UserConfig.Save(); |  | ||||||
|             browser.ResumeNotification(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnManageOptions_Click(object sender, EventArgs e){ |  | ||||||
|             currentTab.Control.OnClosing(); |  | ||||||
| 
 |  | ||||||
|             using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){ |  | ||||||
|                 FormClosing -= FormSettings_FormClosing; |  | ||||||
| 
 |  | ||||||
|                 if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                     if (!dialog.IsRestarting){ |  | ||||||
|                         browser.ResumeNotification(); |  | ||||||
|                      |  | ||||||
|                         BrowserProcessHandler.UpdatePrefs(); |  | ||||||
|                         ShouldReloadBrowser = dialog.ShouldReloadBrowser; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     Close(); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     FormClosing += FormSettings_FormClosing; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnClose_Click(object sender, EventArgs e){ |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void AddButton<T>(string title, Func<T> constructor) where T : BaseTabSettings{ |  | ||||||
|             FlatButton btn = new FlatButton{ |  | ||||||
|                 BackColor = SystemColors.Control, |  | ||||||
|                 FlatStyle = FlatStyle.Flat, |  | ||||||
|                 Font = SystemFonts.MessageBoxFont, |  | ||||||
|                 Location = new Point(0, (buttonHeight+1)*(panelButtons.Controls.Count/2)), |  | ||||||
|                 Margin = new Padding(0), |  | ||||||
|                 Size = new Size(panelButtons.Width, buttonHeight), |  | ||||||
|                 Text = title, |  | ||||||
|                 UseVisualStyleBackColor = true |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             btn.FlatAppearance.BorderSize = 0; |  | ||||||
|             btn.FlatAppearance.MouseDownBackColor = Color.FromArgb(179, 213, 232); |  | ||||||
|             btn.FlatAppearance.MouseOverBackColor = Color.FromArgb(216, 230, 237); |  | ||||||
|              |  | ||||||
|             panelButtons.Controls.Add(btn); |  | ||||||
| 
 |  | ||||||
|             panelButtons.Controls.Add(new Panel{ |  | ||||||
|                 BackColor = Color.DimGray, |  | ||||||
|                 Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count-1].Location.Y+buttonHeight), |  | ||||||
|                 Margin = new Padding(0), |  | ||||||
|                 Size = new Size(panelButtons.Width, 1) |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             tabs.Add(typeof(T), new SettingsTab(btn, constructor)); |  | ||||||
| 
 |  | ||||||
|             btn.Click += (sender, args) => SelectTab<T>(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void SelectTab<T>() where T : BaseTabSettings{ |  | ||||||
|             SelectTab(tabs[typeof(T)]); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void SelectTab(SettingsTab tab){ |  | ||||||
|             if (currentTab != null){ |  | ||||||
|                 currentTab.Button.BackColor = SystemColors.Control; |  | ||||||
|                 currentTab.Control.OnClosing(); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor; |  | ||||||
| 
 |  | ||||||
|             if (!tab.IsInitialized){ |  | ||||||
|                 foreach(Control control in tab.Control.InteractiveControls){ |  | ||||||
|                     if (control is ComboBox){ |  | ||||||
|                         control.MouseLeave += control_MouseLeave; |  | ||||||
|                     } |  | ||||||
|                     else if (control is TrackBar){ |  | ||||||
|                         control.MouseWheel += control_MouseWheel; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 tab.Control.OnReady(); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             panelContents.VerticalScroll.Enabled = false; // required to stop animation that would otherwise break everything |  | ||||||
|             panelContents.PerformLayout(); |  | ||||||
| 
 |  | ||||||
|             panelContents.SuspendLayout(); |  | ||||||
|             panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte |  | ||||||
|             panelContents.Controls.Clear(); |  | ||||||
|             panelContents.Controls.Add(tab.Control); |  | ||||||
|             panelContents.ResumeLayout(true); |  | ||||||
| 
 |  | ||||||
|             panelContents.VerticalScroll.Enabled = true; |  | ||||||
|             panelContents.Focus(); |  | ||||||
| 
 |  | ||||||
|             currentTab = tab; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void control_MouseLeave(object sender, EventArgs e){ |  | ||||||
|             if (sender is ComboBox cb && cb.DroppedDown){ |  | ||||||
|                 return; // prevents comboboxes from closing when MouseLeave event triggers during opening animation |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             panelContents.Focus(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void control_MouseWheel(object sender, MouseEventArgs e){ |  | ||||||
|             ((HandledMouseEventArgs)e).Handled = true; |  | ||||||
|             panelContents.Focus(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private sealed class SettingsTab{ |  | ||||||
|             public Button Button { get; } |  | ||||||
| 
 |  | ||||||
|             public BaseTabSettings Control => control ?? (control = constructor()); |  | ||||||
|             public bool IsInitialized => control != null; |  | ||||||
| 
 |  | ||||||
|             private readonly Func<BaseTabSettings> constructor; |  | ||||||
|             private BaseTabSettings control; |  | ||||||
| 
 |  | ||||||
|             public SettingsTab(Button button, Func<BaseTabSettings> constructor){ |  | ||||||
|                 this.Button = button; |  | ||||||
|                 this.constructor = constructor; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,12 +0,0 @@ | |||||||
| using System; |  | ||||||
| using CefSharp; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Interfaces{ |  | ||||||
|     interface ITweetDeckBrowser{ |  | ||||||
|         bool IsTweetDeckWebsite { get; } |  | ||||||
| 
 |  | ||||||
|         void RegisterBridge(string name, object obj); |  | ||||||
|         void OnFrameLoaded(Action<IFrame> callback); |  | ||||||
|         void ExecuteFunction(string name, params object[] args); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| using System.Collections.Generic; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Configuration; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     class BaseTabSettings : UserControl{ |  | ||||||
|         protected static UserConfig Config => Program.UserConfig; |  | ||||||
| 
 |  | ||||||
|         public IEnumerable<Control> InteractiveControls{ |  | ||||||
|             get{ |  | ||||||
|                 IEnumerable<Control> FindInteractiveControls(Control parent){ |  | ||||||
|                     foreach(Control control in parent.Controls){ |  | ||||||
|                         if (control is Panel subPanel){ |  | ||||||
|                             foreach(Control subControl in FindInteractiveControls(subPanel)){ |  | ||||||
|                                 yield return subControl; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             yield return control; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return FindInteractiveControls(this); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         protected BaseTabSettings(){ |  | ||||||
|             Padding = new Padding(6); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public virtual void OnReady(){} |  | ||||||
|         public virtual void OnClosing(){} |  | ||||||
| 
 |  | ||||||
|         protected static void PromptRestart(){ |  | ||||||
|             if (FormMessage.Information("TweetDuck Options", "The application must restart for the option to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                 Program.Restart(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,94 +0,0 @@ | |||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs { |  | ||||||
|     partial class DialogSettingsAnalytics { |  | ||||||
|         /// <summary> |  | ||||||
|         /// Required designer variable. |  | ||||||
|         /// </summary> |  | ||||||
|         private System.ComponentModel.IContainer components = null; |  | ||||||
| 
 |  | ||||||
|         /// <summary> |  | ||||||
|         /// Clean up any resources being used. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |  | ||||||
|         protected override void Dispose(bool disposing) { |  | ||||||
|             if (disposing && (components != null)) { |  | ||||||
|                 components.Dispose(); |  | ||||||
|             } |  | ||||||
|             base.Dispose(disposing); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #region Windows Form Designer generated code |  | ||||||
| 
 |  | ||||||
|         /// <summary> |  | ||||||
|         /// Required method for Designer support - do not modify |  | ||||||
|         /// the contents of this method with the code editor. |  | ||||||
|         /// </summary> |  | ||||||
|         private void InitializeComponent() { |  | ||||||
|             this.textBoxReport = new System.Windows.Forms.TextBox(); |  | ||||||
|             this.btnClose = new System.Windows.Forms.Button(); |  | ||||||
|             this.labelInfo = new System.Windows.Forms.Label(); |  | ||||||
|             this.SuspendLayout(); |  | ||||||
|             //  |  | ||||||
|             // textBoxReport |  | ||||||
|             //  |  | ||||||
|             this.textBoxReport.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.textBoxReport.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.textBoxReport.Location = new System.Drawing.Point(12, 45); |  | ||||||
|             this.textBoxReport.Multiline = true; |  | ||||||
|             this.textBoxReport.Name = "textBoxReport"; |  | ||||||
|             this.textBoxReport.ReadOnly = true; |  | ||||||
|             this.textBoxReport.ScrollBars = System.Windows.Forms.ScrollBars.Both; |  | ||||||
|             this.textBoxReport.Size = new System.Drawing.Size(435, 474); |  | ||||||
|             this.textBoxReport.TabIndex = 1; |  | ||||||
|             //  |  | ||||||
|             // btnClose |  | ||||||
|             //  |  | ||||||
|             this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.btnClose.AutoSize = true; |  | ||||||
|             this.btnClose.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnClose.Location = new System.Drawing.Point(397, 525); |  | ||||||
|             this.btnClose.Name = "btnClose"; |  | ||||||
|             this.btnClose.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnClose.Size = new System.Drawing.Size(50, 25); |  | ||||||
|             this.btnClose.TabIndex = 2; |  | ||||||
|             this.btnClose.Text = "Close"; |  | ||||||
|             this.btnClose.UseVisualStyleBackColor = true; |  | ||||||
|             this.btnClose.Click += new System.EventHandler(this.btnClose_Click); |  | ||||||
|             //  |  | ||||||
|             // labelInfo |  | ||||||
|             //  |  | ||||||
|             this.labelInfo.AutoSize = true; |  | ||||||
|             this.labelInfo.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelInfo.Location = new System.Drawing.Point(12, 9); |  | ||||||
|             this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); |  | ||||||
|             this.labelInfo.Name = "labelInfo"; |  | ||||||
|             this.labelInfo.Size = new System.Drawing.Size(434, 30); |  | ||||||
|             this.labelInfo.TabIndex = 0; |  | ||||||
|             this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly every 14 days." + |  | ||||||
|     "\r\nSome numbers in the report were made imprecise on purpose."; |  | ||||||
|             //  |  | ||||||
|             // DialogSettingsAnalytics |  | ||||||
|             //  |  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |  | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |  | ||||||
|             this.ClientSize = new System.Drawing.Size(459, 562); |  | ||||||
|             this.Controls.Add(this.labelInfo); |  | ||||||
|             this.Controls.Add(this.btnClose); |  | ||||||
|             this.Controls.Add(this.textBoxReport); |  | ||||||
|             this.MinimumSize = new System.Drawing.Size(475, 340); |  | ||||||
|             this.Name = "DialogSettingsAnalytics"; |  | ||||||
|             this.ShowIcon = false; |  | ||||||
|             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; |  | ||||||
|             this.ResumeLayout(false); |  | ||||||
|             this.PerformLayout(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #endregion |  | ||||||
| 
 |  | ||||||
|         private System.Windows.Forms.TextBox textBoxReport; |  | ||||||
|         private System.Windows.Forms.Button btnClose; |  | ||||||
|         private System.Windows.Forms.Label labelInfo; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,24 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Other.Analytics; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs{ |  | ||||||
|     sealed partial class DialogSettingsAnalytics : Form{ |  | ||||||
|         public string CefArgs => textBoxReport.Text; |  | ||||||
| 
 |  | ||||||
|         public DialogSettingsAnalytics(AnalyticsReport report){ |  | ||||||
|             InitializeComponent(); |  | ||||||
|              |  | ||||||
|             Text = Program.BrandName+" Options - Analytics Report"; |  | ||||||
|              |  | ||||||
|             textBoxReport.EnableMultilineShortcuts(); |  | ||||||
|             textBoxReport.Text = report.ToString().TrimEnd(); |  | ||||||
|             textBoxReport.Select(0, 0); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnClose_Click(object sender, EventArgs e){ |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,216 +0,0 @@ | |||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs { |  | ||||||
|     partial class DialogSettingsCSS { |  | ||||||
|         /// <summary> |  | ||||||
|         /// Required designer variable. |  | ||||||
|         /// </summary> |  | ||||||
|         private System.ComponentModel.IContainer components = null; |  | ||||||
| 
 |  | ||||||
|         /// <summary> |  | ||||||
|         /// Clean up any resources being used. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |  | ||||||
|         protected override void Dispose(bool disposing) { |  | ||||||
|             if (disposing && (components != null)) { |  | ||||||
|                 components.Dispose(); |  | ||||||
|             } |  | ||||||
|             base.Dispose(disposing); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #region Windows Form Designer generated code |  | ||||||
| 
 |  | ||||||
|         /// <summary> |  | ||||||
|         /// Required method for Designer support - do not modify |  | ||||||
|         /// the contents of this method with the code editor. |  | ||||||
|         /// </summary> |  | ||||||
|         private void InitializeComponent() { |  | ||||||
|             this.components = new System.ComponentModel.Container(); |  | ||||||
|             this.textBoxBrowserCSS = new System.Windows.Forms.TextBox(); |  | ||||||
|             this.btnCancel = new System.Windows.Forms.Button(); |  | ||||||
|             this.btnApply = new System.Windows.Forms.Button(); |  | ||||||
|             this.splitContainer = new System.Windows.Forms.SplitContainer(); |  | ||||||
|             this.labelBrowser = new System.Windows.Forms.Label(); |  | ||||||
|             this.labelNotification = new System.Windows.Forms.Label(); |  | ||||||
|             this.textBoxNotificationCSS = new System.Windows.Forms.TextBox(); |  | ||||||
|             this.labelWarning = new System.Windows.Forms.Label(); |  | ||||||
|             this.btnOpenWiki = new System.Windows.Forms.Button(); |  | ||||||
|             this.timerTestBrowser = new System.Windows.Forms.Timer(this.components); |  | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); |  | ||||||
|             this.splitContainer.Panel1.SuspendLayout(); |  | ||||||
|             this.splitContainer.Panel2.SuspendLayout(); |  | ||||||
|             this.splitContainer.SuspendLayout(); |  | ||||||
|             this.SuspendLayout(); |  | ||||||
|             //  |  | ||||||
|             // textBoxBrowserCSS |  | ||||||
|             //  |  | ||||||
|             this.textBoxBrowserCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16); |  | ||||||
|             this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0); |  | ||||||
|             this.textBoxBrowserCSS.Multiline = true; |  | ||||||
|             this.textBoxBrowserCSS.Name = "textBoxBrowserCSS"; |  | ||||||
|             this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both; |  | ||||||
|             this.textBoxBrowserCSS.Size = new System.Drawing.Size(378, 251); |  | ||||||
|             this.textBoxBrowserCSS.TabIndex = 1; |  | ||||||
|             this.textBoxBrowserCSS.WordWrap = false; |  | ||||||
|             this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp); |  | ||||||
|             //  |  | ||||||
|             // btnCancel |  | ||||||
|             //  |  | ||||||
|             this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.btnCancel.AutoSize = true; |  | ||||||
|             this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); |  | ||||||
|             this.btnCancel.Location = new System.Drawing.Point(657, 285); |  | ||||||
|             this.btnCancel.Name = "btnCancel"; |  | ||||||
|             this.btnCancel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnCancel.Size = new System.Drawing.Size(57, 25); |  | ||||||
|             this.btnCancel.TabIndex = 2; |  | ||||||
|             this.btnCancel.Text = "Cancel"; |  | ||||||
|             this.btnCancel.UseVisualStyleBackColor = true; |  | ||||||
|             this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); |  | ||||||
|             //  |  | ||||||
|             // btnApply |  | ||||||
|             //  |  | ||||||
|             this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.btnApply.AutoSize = true; |  | ||||||
|             this.btnApply.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); |  | ||||||
|             this.btnApply.Location = new System.Drawing.Point(720, 285); |  | ||||||
|             this.btnApply.Name = "btnApply"; |  | ||||||
|             this.btnApply.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnApply.Size = new System.Drawing.Size(52, 25); |  | ||||||
|             this.btnApply.TabIndex = 1; |  | ||||||
|             this.btnApply.Text = "Apply"; |  | ||||||
|             this.btnApply.UseVisualStyleBackColor = true; |  | ||||||
|             this.btnApply.Click += new System.EventHandler(this.btnApply_Click); |  | ||||||
|             //  |  | ||||||
|             // splitContainer |  | ||||||
|             //  |  | ||||||
|             this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.splitContainer.Location = new System.Drawing.Point(12, 12); |  | ||||||
|             this.splitContainer.Name = "splitContainer"; |  | ||||||
|             //  |  | ||||||
|             // splitContainer.Panel1 |  | ||||||
|             //  |  | ||||||
|             this.splitContainer.Panel1.Controls.Add(this.labelBrowser); |  | ||||||
|             this.splitContainer.Panel1.Controls.Add(this.textBoxBrowserCSS); |  | ||||||
|             this.splitContainer.Panel1MinSize = 64; |  | ||||||
|             //  |  | ||||||
|             // splitContainer.Panel2 |  | ||||||
|             //  |  | ||||||
|             this.splitContainer.Panel2.Controls.Add(this.labelNotification); |  | ||||||
|             this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS); |  | ||||||
|             this.splitContainer.Panel2MinSize = 64; |  | ||||||
|             this.splitContainer.Size = new System.Drawing.Size(760, 267); |  | ||||||
|             this.splitContainer.SplitterDistance = 378; |  | ||||||
|             this.splitContainer.SplitterWidth = 5; |  | ||||||
|             this.splitContainer.TabIndex = 0; |  | ||||||
|             //  |  | ||||||
|             // labelBrowser |  | ||||||
|             //  |  | ||||||
|             this.labelBrowser.AutoSize = true; |  | ||||||
|             this.labelBrowser.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); |  | ||||||
|             this.labelBrowser.Location = new System.Drawing.Point(-3, 0); |  | ||||||
|             this.labelBrowser.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); |  | ||||||
|             this.labelBrowser.Name = "labelBrowser"; |  | ||||||
|             this.labelBrowser.Size = new System.Drawing.Size(49, 15); |  | ||||||
|             this.labelBrowser.TabIndex = 0; |  | ||||||
|             this.labelBrowser.Text = "Browser"; |  | ||||||
|             //  |  | ||||||
|             // labelNotification |  | ||||||
|             //  |  | ||||||
|             this.labelNotification.AutoSize = true; |  | ||||||
|             this.labelNotification.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); |  | ||||||
|             this.labelNotification.Location = new System.Drawing.Point(-3, 0); |  | ||||||
|             this.labelNotification.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); |  | ||||||
|             this.labelNotification.Name = "labelNotification"; |  | ||||||
|             this.labelNotification.Size = new System.Drawing.Size(70, 15); |  | ||||||
|             this.labelNotification.TabIndex = 0; |  | ||||||
|             this.labelNotification.Text = "Notification"; |  | ||||||
|             //  |  | ||||||
|             // textBoxNotificationCSS |  | ||||||
|             //  |  | ||||||
|             this.textBoxNotificationCSS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16); |  | ||||||
|             this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0); |  | ||||||
|             this.textBoxNotificationCSS.Multiline = true; |  | ||||||
|             this.textBoxNotificationCSS.Name = "textBoxNotificationCSS"; |  | ||||||
|             this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both; |  | ||||||
|             this.textBoxNotificationCSS.Size = new System.Drawing.Size(372, 251); |  | ||||||
|             this.textBoxNotificationCSS.TabIndex = 1; |  | ||||||
|             this.textBoxNotificationCSS.WordWrap = false; |  | ||||||
|             //  |  | ||||||
|             // labelWarning |  | ||||||
|             //  |  | ||||||
|             this.labelWarning.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); |  | ||||||
|             this.labelWarning.AutoSize = true; |  | ||||||
|             this.labelWarning.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelWarning.Location = new System.Drawing.Point(94, 290); |  | ||||||
|             this.labelWarning.Name = "labelWarning"; |  | ||||||
|             this.labelWarning.Size = new System.Drawing.Size(373, 15); |  | ||||||
|             this.labelWarning.TabIndex = 3; |  | ||||||
|             this.labelWarning.Text = "The code is not validated, please make sure there are no syntax errors."; |  | ||||||
|             //  |  | ||||||
|             // btnOpenWiki |  | ||||||
|             //  |  | ||||||
|             this.btnOpenWiki.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); |  | ||||||
|             this.btnOpenWiki.AutoSize = true; |  | ||||||
|             this.btnOpenWiki.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); |  | ||||||
|             this.btnOpenWiki.Location = new System.Drawing.Point(12, 285); |  | ||||||
|             this.btnOpenWiki.Name = "btnOpenWiki"; |  | ||||||
|             this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnOpenWiki.Size = new System.Drawing.Size(76, 25); |  | ||||||
|             this.btnOpenWiki.TabIndex = 4; |  | ||||||
|             this.btnOpenWiki.Text = "Open Wiki"; |  | ||||||
|             this.btnOpenWiki.UseVisualStyleBackColor = true; |  | ||||||
|             this.btnOpenWiki.Click += new System.EventHandler(this.btnOpenWiki_Click); |  | ||||||
|             //  |  | ||||||
|             // timerTestBrowser |  | ||||||
|             //  |  | ||||||
|             this.timerTestBrowser.Interval = 500; |  | ||||||
|             this.timerTestBrowser.Tick += new System.EventHandler(this.timerTestBrowser_Tick); |  | ||||||
|             //  |  | ||||||
|             // DialogSettingsCSS |  | ||||||
|             //  |  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |  | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |  | ||||||
|             this.ClientSize = new System.Drawing.Size(784, 322); |  | ||||||
|             this.Controls.Add(this.btnOpenWiki); |  | ||||||
|             this.Controls.Add(this.labelWarning); |  | ||||||
|             this.Controls.Add(this.splitContainer); |  | ||||||
|             this.Controls.Add(this.btnApply); |  | ||||||
|             this.Controls.Add(this.btnCancel); |  | ||||||
|             this.MinimumSize = new System.Drawing.Size(620, 160); |  | ||||||
|             this.Name = "DialogSettingsCSS"; |  | ||||||
|             this.ShowIcon = false; |  | ||||||
|             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; |  | ||||||
|             this.splitContainer.Panel1.ResumeLayout(false); |  | ||||||
|             this.splitContainer.Panel1.PerformLayout(); |  | ||||||
|             this.splitContainer.Panel2.ResumeLayout(false); |  | ||||||
|             this.splitContainer.Panel2.PerformLayout(); |  | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); |  | ||||||
|             this.splitContainer.ResumeLayout(false); |  | ||||||
|             this.ResumeLayout(false); |  | ||||||
|             this.PerformLayout(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #endregion |  | ||||||
| 
 |  | ||||||
|         private System.Windows.Forms.TextBox textBoxBrowserCSS; |  | ||||||
|         private System.Windows.Forms.Button btnCancel; |  | ||||||
|         private System.Windows.Forms.Button btnApply; |  | ||||||
|         private System.Windows.Forms.SplitContainer splitContainer; |  | ||||||
|         private System.Windows.Forms.TextBox textBoxNotificationCSS; |  | ||||||
|         private System.Windows.Forms.Label labelBrowser; |  | ||||||
|         private System.Windows.Forms.Label labelNotification; |  | ||||||
|         private System.Windows.Forms.Label labelWarning; |  | ||||||
|         private System.Windows.Forms.Button btnOpenWiki; |  | ||||||
|         private System.Windows.Forms.Timer timerTestBrowser; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,51 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs{ |  | ||||||
|     sealed partial class DialogSettingsCSS : Form{ |  | ||||||
|         public string BrowserCSS => textBoxBrowserCSS.Text; |  | ||||||
|         public string NotificationCSS => textBoxNotificationCSS.Text; |  | ||||||
| 
 |  | ||||||
|         private readonly Action<string> reinjectBrowserCSS; |  | ||||||
| 
 |  | ||||||
|         public DialogSettingsCSS(Action<string> reinjectBrowserCSS){ // TODO high dpi breaks scaling of things inside the panel... |  | ||||||
|             InitializeComponent(); |  | ||||||
|              |  | ||||||
|             Text = Program.BrandName+" Options - CSS"; |  | ||||||
| 
 |  | ||||||
|             this.reinjectBrowserCSS = reinjectBrowserCSS; |  | ||||||
|              |  | ||||||
|             textBoxBrowserCSS.EnableMultilineShortcuts(); |  | ||||||
|             textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? ""; |  | ||||||
| 
 |  | ||||||
|             textBoxNotificationCSS.EnableMultilineShortcuts(); |  | ||||||
|             textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? ""; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void textBoxBrowserCSS_KeyUp(object sender, KeyEventArgs e){ |  | ||||||
|             timerTestBrowser.Stop(); |  | ||||||
|             timerTestBrowser.Start(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void timerTestBrowser_Tick(object sender, EventArgs e){ |  | ||||||
|             reinjectBrowserCSS(textBoxBrowserCSS.Text); |  | ||||||
|             timerTestBrowser.Stop(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnOpenWiki_Click(object sender, EventArgs e){ |  | ||||||
|             BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnApply_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.OK; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnCancel_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.Cancel; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,48 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs{ |  | ||||||
|     sealed partial class DialogSettingsCefArgs : Form{ |  | ||||||
|         public string CefArgs => textBoxArgs.Text; |  | ||||||
| 
 |  | ||||||
|         public DialogSettingsCefArgs(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
|              |  | ||||||
|             Text = Program.BrandName+" Options - CEF Arguments"; |  | ||||||
|              |  | ||||||
|             textBoxArgs.EnableMultilineShortcuts(); |  | ||||||
|             textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? ""; |  | ||||||
|             textBoxArgs.Select(textBoxArgs.Text.Length, 0); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnHelp_Click(object sender, EventArgs e){ |  | ||||||
|             BrowserUtils.OpenExternalBrowser("http://peter.sh/experiments/chromium-command-line-switches/"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnApply_Click(object sender, EventArgs e){ |  | ||||||
|             string prevArgs = Program.UserConfig.CustomCefArgs; |  | ||||||
| 
 |  | ||||||
|             if (CefArgs == prevArgs){ |  | ||||||
|                 DialogResult = DialogResult.Cancel; |  | ||||||
|                 Close(); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             int count = CommandLineArgs.ReadCefArguments(CefArgs).Count; |  | ||||||
|             string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?"; |  | ||||||
| 
 |  | ||||||
|             if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){ |  | ||||||
|                 DialogResult = DialogResult.OK; |  | ||||||
|                 Close(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnCancel_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.Cancel; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,222 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.IO; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Configuration; |  | ||||||
| using TweetDuck.Core.Management; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs{ |  | ||||||
|     sealed partial class DialogSettingsManage : Form{ |  | ||||||
|         private enum State{ |  | ||||||
|             Deciding, Reset, Import, Export |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private ProfileManager.Items SelectedItems{ |  | ||||||
|             get => _selectedItems; |  | ||||||
| 
 |  | ||||||
|             set{ |  | ||||||
|                 // this will call events and SetFlag, which also updates the UI |  | ||||||
|                 foreach(KeyValuePair<CheckBox, ProfileManager.Items> kvp in checkBoxMap){ |  | ||||||
|                     kvp.Key.Checked = value.HasFlag(kvp.Value); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public bool IsRestarting { get; private set; } |  | ||||||
|         public bool ShouldReloadBrowser { get; private set; } |  | ||||||
|          |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
|         private readonly Dictionary<CheckBox, ProfileManager.Items> checkBoxMap = new Dictionary<CheckBox, ProfileManager.Items>(4); |  | ||||||
| 
 |  | ||||||
|         private State currentState; |  | ||||||
|         private ProfileManager importManager; |  | ||||||
| 
 |  | ||||||
|         private ProfileManager.Items _selectedItems = ProfileManager.Items.None; |  | ||||||
| 
 |  | ||||||
|         public DialogSettingsManage(PluginManager plugins){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.plugins = plugins; |  | ||||||
|             this.currentState = State.Deciding; |  | ||||||
| 
 |  | ||||||
|             this.checkBoxMap[cbProgramConfig] = ProfileManager.Items.UserConfig; |  | ||||||
|             this.checkBoxMap[cbSystemConfig] = ProfileManager.Items.SystemConfig; |  | ||||||
|             this.checkBoxMap[cbSession] = ProfileManager.Items.Session; |  | ||||||
|             this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void radioDecision_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             btnContinue.Enabled = true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkBoxSelection_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             CheckBox cb = (CheckBox)sender; |  | ||||||
|             SetFlag(checkBoxMap[cb], cb.Checked); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnContinue_Click(object sender, EventArgs e){ |  | ||||||
|             string file; |  | ||||||
| 
 |  | ||||||
|             switch(currentState){ |  | ||||||
|                 case State.Deciding: |  | ||||||
|                     // Reset |  | ||||||
|                     if (radioReset.Checked){ |  | ||||||
|                         currentState = State.Reset; |  | ||||||
| 
 |  | ||||||
|                         Text = "Restore Defaults"; |  | ||||||
|                         SelectedItems = ProfileManager.Items.UserConfig; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     // Import |  | ||||||
|                     else if (radioImport.Checked){ |  | ||||||
|                         using(OpenFileDialog dialog = new OpenFileDialog{ |  | ||||||
|                             AutoUpgradeEnabled = true, |  | ||||||
|                             DereferenceLinks = true, |  | ||||||
|                             Title = "Import TweetDuck Profile", |  | ||||||
|                             Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings" |  | ||||||
|                         }){ |  | ||||||
|                             if (dialog.ShowDialog() != DialogResult.OK){ |  | ||||||
|                                 return; |  | ||||||
|                             } |  | ||||||
| 
 |  | ||||||
|                             file = dialog.FileName; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         importManager = new ProfileManager(file, plugins); |  | ||||||
|                         currentState = State.Import; |  | ||||||
| 
 |  | ||||||
|                         Text = "Import Profile"; |  | ||||||
|                         SelectedItems = importManager.FindImportItems(); |  | ||||||
| 
 |  | ||||||
|                         foreach(CheckBox cb in checkBoxMap.Keys){ |  | ||||||
|                             cb.Enabled = cb.Checked; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     // Export |  | ||||||
|                     else if (radioExport.Checked){ |  | ||||||
|                         currentState = State.Export; |  | ||||||
| 
 |  | ||||||
|                         Text = "Export Profile"; |  | ||||||
|                         btnContinue.Text = "Export Profile"; |  | ||||||
|                         SelectedItems = ProfileManager.Items.UserConfig | ProfileManager.Items.PluginData; |  | ||||||
|                     } |  | ||||||
|                      |  | ||||||
|                     // Continue... |  | ||||||
|                     panelDecision.Visible = false; |  | ||||||
|                     panelSelection.Visible = true; |  | ||||||
|                     Height += panelSelection.Height-panelDecision.Height; |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case State.Reset: |  | ||||||
|                     if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                         if (SelectedItems.HasFlag(ProfileManager.Items.UserConfig)){ |  | ||||||
|                             Program.UserConfig.Reset(); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){ |  | ||||||
|                             try{ |  | ||||||
|                                 File.Delete(Program.SystemConfigFilePath); |  | ||||||
|                             }catch(Exception ex){ |  | ||||||
|                                 Program.Reporter.HandleException("System Config Reset Error", "Could not delete system config.", true, ex); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)){ |  | ||||||
|                             try{ |  | ||||||
|                                 File.Delete(Program.PluginConfigFilePath); |  | ||||||
|                                 Directory.Delete(Program.PluginDataPath, true); |  | ||||||
|                             }catch(Exception ex){ |  | ||||||
|                                 Program.Reporter.HandleException("Plugin Data Reset Error", "Could not delete plugin data.", true, ex); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (SelectedItems.HasFlag(ProfileManager.Items.Session)){ |  | ||||||
|                             RestartProgram(Arguments.ArgDeleteCookies); |  | ||||||
|                         } |  | ||||||
|                         else if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){ |  | ||||||
|                             RestartProgram(); |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             ShouldReloadBrowser = true; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         DialogResult = DialogResult.OK; |  | ||||||
|                         Close(); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case State.Import: |  | ||||||
|                     if (importManager.Import(SelectedItems)){ |  | ||||||
|                         Program.UserConfig.Reload(); |  | ||||||
| 
 |  | ||||||
|                         if (importManager.IsRestarting){ |  | ||||||
|                             if (SelectedItems.HasFlag(ProfileManager.Items.Session)){ |  | ||||||
|                                 RestartProgram(Arguments.ArgImportCookies); |  | ||||||
|                             } |  | ||||||
|                             else if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){ |  | ||||||
|                                 RestartProgram(); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             ShouldReloadBrowser = true; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                      |  | ||||||
|                     DialogResult = DialogResult.OK; |  | ||||||
|                     Close(); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case State.Export: |  | ||||||
|                     using(SaveFileDialog dialog = new SaveFileDialog{ |  | ||||||
|                         AddExtension = true, |  | ||||||
|                         AutoUpgradeEnabled = true, |  | ||||||
|                         OverwritePrompt = true, |  | ||||||
|                         DefaultExt = "tdsettings", |  | ||||||
|                         FileName = "TweetDuck.tdsettings", |  | ||||||
|                         Title = "Export TweetDuck Profile", |  | ||||||
|                         Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings" |  | ||||||
|                     }){ |  | ||||||
|                         if (dialog.ShowDialog() != DialogResult.OK){ |  | ||||||
|                             return; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         file = dialog.FileName; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     Program.UserConfig.Save(); |  | ||||||
|                     Program.SystemConfig.Save(); |  | ||||||
|                      |  | ||||||
|                     new ProfileManager(file, plugins).Export(SelectedItems); |  | ||||||
| 
 |  | ||||||
|                     DialogResult = DialogResult.OK; |  | ||||||
|                     Close(); |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnCancel_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.Cancel; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void SetFlag(ProfileManager.Items flag, bool enable){ |  | ||||||
|             _selectedItems = enable ? _selectedItems | flag : _selectedItems & ~flag; |  | ||||||
|             btnContinue.Enabled = _selectedItems != ProfileManager.Items.None; |  | ||||||
|              |  | ||||||
|             if (currentState == State.Import){ |  | ||||||
|                 btnContinue.Text = _selectedItems.NeedsRestart() ? "Import && Restart" : "Import Profile"; |  | ||||||
|             } |  | ||||||
|             else if (currentState == State.Reset){ |  | ||||||
|                 btnContinue.Text = _selectedItems.NeedsRestart() ? "Restore && Restart" : "Restore Defaults"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void RestartProgram(params string[] extraArgs){ |  | ||||||
|             IsRestarting = true; |  | ||||||
|             Program.Restart(extraArgs); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,61 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Configuration; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs{ |  | ||||||
|     sealed partial class DialogSettingsRestart : Form{ |  | ||||||
|         public CommandLineArgs Args { get; private set; } |  | ||||||
| 
 |  | ||||||
|         public DialogSettingsRestart(CommandLineArgs currentArgs){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging); |  | ||||||
|             cbLogging.CheckedChanged += control_Change; |  | ||||||
|              |  | ||||||
|             if (Program.IsPortable){ |  | ||||||
|                 tbDataFolder.Text = "Not available in portable version"; |  | ||||||
|                 tbDataFolder.Enabled = false; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty); |  | ||||||
|                 tbDataFolder.TextChanged += control_Change; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             control_Change(this, EventArgs.Empty); |  | ||||||
| 
 |  | ||||||
|             Text = Program.BrandName+" Arguments"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void control_Change(object sender, EventArgs e){ |  | ||||||
|             Args = new CommandLineArgs(); |  | ||||||
|              |  | ||||||
|             if (cbLogging.Checked){ |  | ||||||
|                 Args.AddFlag(Arguments.ArgLogging); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){ |  | ||||||
|                 Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             tbShortcutTarget.Text = $@"""{Application.ExecutablePath}""{(Args.Count > 0 ? " " : "")}{Args}"; |  | ||||||
|             tbShortcutTarget.Select(tbShortcutTarget.Text.Length, 0); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void tbShortcutTarget_Click(object sender, EventArgs e){ |  | ||||||
|             if (tbShortcutTarget.SelectionLength == 0){ |  | ||||||
|                 tbShortcutTarget.SelectAll(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnRestart_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.OK; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnCancel_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.Cancel; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings.Dialogs{ |  | ||||||
|     sealed partial class DialogSettingsSearchEngine : Form{ |  | ||||||
|         public string Url => textBoxUrl.Text; |  | ||||||
| 
 |  | ||||||
|         public DialogSettingsSearchEngine(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
|              |  | ||||||
|             Text = Program.BrandName+" Options - Custom Search Engine"; |  | ||||||
|              |  | ||||||
|             textBoxUrl.Text = Program.UserConfig.SearchEngineUrl ?? ""; |  | ||||||
|             textBoxUrl.Select(textBoxUrl.Text.Length, 0); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         private void btnApply_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.OK; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnCancel_Click(object sender, EventArgs e){ |  | ||||||
|             DialogResult = DialogResult.Cancel; |  | ||||||
|             Close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										321
									
								
								Core/Other/Settings/TabSettingsAdvanced.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										321
									
								
								Core/Other/Settings/TabSettingsAdvanced.Designer.cs
									
									
									
										generated
									
									
									
								
							| @@ -1,321 +0,0 @@ | |||||||
| namespace TweetDuck.Core.Other.Settings { |  | ||||||
|     partial class TabSettingsAdvanced { |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required designer variable. |  | ||||||
|         /// </summary> |  | ||||||
|         private System.ComponentModel.IContainer components = null; |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Clean up any resources being used. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |  | ||||||
|         protected override void Dispose(bool disposing) { |  | ||||||
|             if (disposing && (components != null)) { |  | ||||||
|                 components.Dispose(); |  | ||||||
|             } |  | ||||||
|             base.Dispose(disposing); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #region Component Designer generated code |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required method for Designer support - do not modify  |  | ||||||
|         /// the contents of this method with the code editor. |  | ||||||
|         /// </summary> |  | ||||||
|         private void InitializeComponent() { |  | ||||||
|             this.components = new System.ComponentModel.Container(); |  | ||||||
|             this.btnClearCache = new System.Windows.Forms.Button(); |  | ||||||
|             this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.toolTip = new System.Windows.Forms.ToolTip(this.components); |  | ||||||
|             this.btnEditCefArgs = new System.Windows.Forms.Button(); |  | ||||||
|             this.btnEditCSS = new System.Windows.Forms.Button(); |  | ||||||
|             this.btnRestartArgs = new System.Windows.Forms.Button(); |  | ||||||
|             this.btnRestart = new System.Windows.Forms.Button(); |  | ||||||
|             this.btnOpenAppFolder = new System.Windows.Forms.Button(); |  | ||||||
|             this.btnOpenDataFolder = new System.Windows.Forms.Button(); |  | ||||||
|             this.numClearCacheThreshold = new TweetDuck.Core.Controls.NumericUpDownEx(); |  | ||||||
|             this.checkClearCacheAuto = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelApp = new System.Windows.Forms.Label(); |  | ||||||
|             this.panelAppButtons = new System.Windows.Forms.Panel(); |  | ||||||
|             this.labelPerformance = new System.Windows.Forms.Label(); |  | ||||||
|             this.panelClearCacheAuto = new System.Windows.Forms.Panel(); |  | ||||||
|             this.labelCache = new System.Windows.Forms.Label(); |  | ||||||
|             this.panelConfiguration = new System.Windows.Forms.Panel(); |  | ||||||
|             this.labelConfiguration = new System.Windows.Forms.Label(); |  | ||||||
|             this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); |  | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit(); |  | ||||||
|             this.panelAppButtons.SuspendLayout(); |  | ||||||
|             this.panelClearCacheAuto.SuspendLayout(); |  | ||||||
|             this.panelConfiguration.SuspendLayout(); |  | ||||||
|             this.flowPanel.SuspendLayout(); |  | ||||||
|             this.SuspendLayout(); |  | ||||||
|             //  |  | ||||||
|             // btnClearCache |  | ||||||
|             //  |  | ||||||
|             this.btnClearCache.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnClearCache.Location = new System.Drawing.Point(5, 179); |  | ||||||
|             this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.btnClearCache.Name = "btnClearCache"; |  | ||||||
|             this.btnClearCache.Size = new System.Drawing.Size(144, 25); |  | ||||||
|             this.btnClearCache.TabIndex = 5; |  | ||||||
|             this.btnClearCache.Text = "Clear Cache (...)"; |  | ||||||
|             this.btnClearCache.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // checkHardwareAcceleration |  | ||||||
|             //  |  | ||||||
|             this.checkHardwareAcceleration.AutoSize = true; |  | ||||||
|             this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 128); |  | ||||||
|             this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |  | ||||||
|             this.checkHardwareAcceleration.Name = "checkHardwareAcceleration"; |  | ||||||
|             this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19); |  | ||||||
|             this.checkHardwareAcceleration.TabIndex = 3; |  | ||||||
|             this.checkHardwareAcceleration.Text = "Hardware Acceleration"; |  | ||||||
|             this.checkHardwareAcceleration.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnEditCefArgs |  | ||||||
|             //  |  | ||||||
|             this.btnEditCefArgs.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnEditCefArgs.Location = new System.Drawing.Point(5, 3); |  | ||||||
|             this.btnEditCefArgs.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.btnEditCefArgs.Name = "btnEditCefArgs"; |  | ||||||
|             this.btnEditCefArgs.Size = new System.Drawing.Size(144, 25); |  | ||||||
|             this.btnEditCefArgs.TabIndex = 0; |  | ||||||
|             this.btnEditCefArgs.Text = "Edit CEF Arguments"; |  | ||||||
|             this.btnEditCefArgs.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnEditCSS |  | ||||||
|             //  |  | ||||||
|             this.btnEditCSS.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnEditCSS.Location = new System.Drawing.Point(155, 3); |  | ||||||
|             this.btnEditCSS.Name = "btnEditCSS"; |  | ||||||
|             this.btnEditCSS.Size = new System.Drawing.Size(144, 25); |  | ||||||
|             this.btnEditCSS.TabIndex = 1; |  | ||||||
|             this.btnEditCSS.Text = "Edit CSS"; |  | ||||||
|             this.btnEditCSS.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnRestartArgs |  | ||||||
|             //  |  | ||||||
|             this.btnRestartArgs.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnRestartArgs.Location = new System.Drawing.Point(155, 32); |  | ||||||
|             this.btnRestartArgs.Name = "btnRestartArgs"; |  | ||||||
|             this.btnRestartArgs.Size = new System.Drawing.Size(144, 25); |  | ||||||
|             this.btnRestartArgs.TabIndex = 3; |  | ||||||
|             this.btnRestartArgs.Text = "Restart with Arguments"; |  | ||||||
|             this.btnRestartArgs.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnRestart |  | ||||||
|             //  |  | ||||||
|             this.btnRestart.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnRestart.Location = new System.Drawing.Point(155, 3); |  | ||||||
|             this.btnRestart.Name = "btnRestart"; |  | ||||||
|             this.btnRestart.Size = new System.Drawing.Size(144, 25); |  | ||||||
|             this.btnRestart.TabIndex = 2; |  | ||||||
|             this.btnRestart.Text = "Restart the Program"; |  | ||||||
|             this.btnRestart.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnOpenAppFolder |  | ||||||
|             //  |  | ||||||
|             this.btnOpenAppFolder.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnOpenAppFolder.Location = new System.Drawing.Point(5, 3); |  | ||||||
|             this.btnOpenAppFolder.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.btnOpenAppFolder.Name = "btnOpenAppFolder"; |  | ||||||
|             this.btnOpenAppFolder.Size = new System.Drawing.Size(144, 25); |  | ||||||
|             this.btnOpenAppFolder.TabIndex = 0; |  | ||||||
|             this.btnOpenAppFolder.Text = "Open Program Folder"; |  | ||||||
|             this.btnOpenAppFolder.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnOpenDataFolder |  | ||||||
|             //  |  | ||||||
|             this.btnOpenDataFolder.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnOpenDataFolder.Location = new System.Drawing.Point(5, 32); |  | ||||||
|             this.btnOpenDataFolder.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.btnOpenDataFolder.Name = "btnOpenDataFolder"; |  | ||||||
|             this.btnOpenDataFolder.Size = new System.Drawing.Size(144, 25); |  | ||||||
|             this.btnOpenDataFolder.TabIndex = 1; |  | ||||||
|             this.btnOpenDataFolder.Text = "Open Data Folder"; |  | ||||||
|             this.btnOpenDataFolder.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // numClearCacheThreshold |  | ||||||
|             //  |  | ||||||
|             this.numClearCacheThreshold.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.numClearCacheThreshold.Increment = new decimal(new int[] { |  | ||||||
|             50, |  | ||||||
|             0, |  | ||||||
|             0, |  | ||||||
|             0}); |  | ||||||
|             this.numClearCacheThreshold.Location = new System.Drawing.Point(246, 5); |  | ||||||
|             this.numClearCacheThreshold.Maximum = new decimal(new int[] { |  | ||||||
|             1000, |  | ||||||
|             0, |  | ||||||
|             0, |  | ||||||
|             0}); |  | ||||||
|             this.numClearCacheThreshold.Minimum = new decimal(new int[] { |  | ||||||
|             100, |  | ||||||
|             0, |  | ||||||
|             0, |  | ||||||
|             0}); |  | ||||||
|             this.numClearCacheThreshold.Name = "numClearCacheThreshold"; |  | ||||||
|             this.numClearCacheThreshold.Size = new System.Drawing.Size(68, 23); |  | ||||||
|             this.numClearCacheThreshold.TabIndex = 1; |  | ||||||
|             this.numClearCacheThreshold.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; |  | ||||||
|             this.numClearCacheThreshold.TextSuffix = " MB"; |  | ||||||
|             this.numClearCacheThreshold.Value = new decimal(new int[] { |  | ||||||
|             250, |  | ||||||
|             0, |  | ||||||
|             0, |  | ||||||
|             0}); |  | ||||||
|             //  |  | ||||||
|             // checkClearCacheAuto |  | ||||||
|             //  |  | ||||||
|             this.checkClearCacheAuto.AutoSize = true; |  | ||||||
|             this.checkClearCacheAuto.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkClearCacheAuto.Location = new System.Drawing.Point(6, 6); |  | ||||||
|             this.checkClearCacheAuto.Margin = new System.Windows.Forms.Padding(6, 6, 0, 2); |  | ||||||
|             this.checkClearCacheAuto.Name = "checkClearCacheAuto"; |  | ||||||
|             this.checkClearCacheAuto.Size = new System.Drawing.Size(237, 19); |  | ||||||
|             this.checkClearCacheAuto.TabIndex = 0; |  | ||||||
|             this.checkClearCacheAuto.Text = "Clear Cache Automatically When Above"; |  | ||||||
|             this.checkClearCacheAuto.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelApp |  | ||||||
|             //  |  | ||||||
|             this.labelApp.AutoSize = true; |  | ||||||
|             this.labelApp.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelApp.Location = new System.Drawing.Point(0, 0); |  | ||||||
|             this.labelApp.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.labelApp.Name = "labelApp"; |  | ||||||
|             this.labelApp.Size = new System.Drawing.Size(37, 20); |  | ||||||
|             this.labelApp.TabIndex = 0; |  | ||||||
|             this.labelApp.Text = "App"; |  | ||||||
|             //  |  | ||||||
|             // panelAppButtons |  | ||||||
|             //  |  | ||||||
|             this.panelAppButtons.Anchor = System.Windows.Forms.AnchorStyles.Top; |  | ||||||
|             this.panelAppButtons.Controls.Add(this.btnOpenDataFolder); |  | ||||||
|             this.panelAppButtons.Controls.Add(this.btnOpenAppFolder); |  | ||||||
|             this.panelAppButtons.Controls.Add(this.btnRestart); |  | ||||||
|             this.panelAppButtons.Controls.Add(this.btnRestartArgs); |  | ||||||
|             this.panelAppButtons.Location = new System.Drawing.Point(0, 20); |  | ||||||
|             this.panelAppButtons.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.panelAppButtons.Name = "panelAppButtons"; |  | ||||||
|             this.panelAppButtons.Size = new System.Drawing.Size(322, 62); |  | ||||||
|             this.panelAppButtons.TabIndex = 1; |  | ||||||
|             //  |  | ||||||
|             // labelPerformance |  | ||||||
|             //  |  | ||||||
|             this.labelPerformance.AutoSize = true; |  | ||||||
|             this.labelPerformance.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelPerformance.Location = new System.Drawing.Point(0, 102); |  | ||||||
|             this.labelPerformance.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); |  | ||||||
|             this.labelPerformance.Name = "labelPerformance"; |  | ||||||
|             this.labelPerformance.Size = new System.Drawing.Size(93, 20); |  | ||||||
|             this.labelPerformance.TabIndex = 2; |  | ||||||
|             this.labelPerformance.Text = "Performance"; |  | ||||||
|             //  |  | ||||||
|             // panelClearCacheAuto |  | ||||||
|             //  |  | ||||||
|             this.panelClearCacheAuto.Anchor = System.Windows.Forms.AnchorStyles.Top; |  | ||||||
|             this.panelClearCacheAuto.Controls.Add(this.checkClearCacheAuto); |  | ||||||
|             this.panelClearCacheAuto.Controls.Add(this.numClearCacheThreshold); |  | ||||||
|             this.panelClearCacheAuto.Location = new System.Drawing.Point(0, 207); |  | ||||||
|             this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.panelClearCacheAuto.Name = "panelClearCacheAuto"; |  | ||||||
|             this.panelClearCacheAuto.Size = new System.Drawing.Size(322, 28); |  | ||||||
|             this.panelClearCacheAuto.TabIndex = 6; |  | ||||||
|             //  |  | ||||||
|             // labelCache |  | ||||||
|             //  |  | ||||||
|             this.labelCache.AutoSize = true; |  | ||||||
|             this.labelCache.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelCache.Location = new System.Drawing.Point(3, 161); |  | ||||||
|             this.labelCache.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelCache.Name = "labelCache"; |  | ||||||
|             this.labelCache.Size = new System.Drawing.Size(40, 15); |  | ||||||
|             this.labelCache.TabIndex = 4; |  | ||||||
|             this.labelCache.Text = "Cache"; |  | ||||||
|             //  |  | ||||||
|             // panelConfiguration |  | ||||||
|             //  |  | ||||||
|             this.panelConfiguration.Anchor = System.Windows.Forms.AnchorStyles.Top; |  | ||||||
|             this.panelConfiguration.Controls.Add(this.btnEditCSS); |  | ||||||
|             this.panelConfiguration.Controls.Add(this.btnEditCefArgs); |  | ||||||
|             this.panelConfiguration.Location = new System.Drawing.Point(0, 275); |  | ||||||
|             this.panelConfiguration.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.panelConfiguration.Name = "panelConfiguration"; |  | ||||||
|             this.panelConfiguration.Size = new System.Drawing.Size(322, 31); |  | ||||||
|             this.panelConfiguration.TabIndex = 8; |  | ||||||
|             //  |  | ||||||
|             // labelConfiguration |  | ||||||
|             //  |  | ||||||
|             this.labelConfiguration.AutoSize = true; |  | ||||||
|             this.labelConfiguration.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelConfiguration.Location = new System.Drawing.Point(0, 255); |  | ||||||
|             this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); |  | ||||||
|             this.labelConfiguration.Name = "labelConfiguration"; |  | ||||||
|             this.labelConfiguration.Size = new System.Drawing.Size(100, 20); |  | ||||||
|             this.labelConfiguration.TabIndex = 7; |  | ||||||
|             this.labelConfiguration.Text = "Configuration"; |  | ||||||
|             //  |  | ||||||
|             // flowPanel |  | ||||||
|             //  |  | ||||||
|             this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelApp); |  | ||||||
|             this.flowPanel.Controls.Add(this.panelAppButtons); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelPerformance); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkHardwareAcceleration); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelCache); |  | ||||||
|             this.flowPanel.Controls.Add(this.btnClearCache); |  | ||||||
|             this.flowPanel.Controls.Add(this.panelClearCacheAuto); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelConfiguration); |  | ||||||
|             this.flowPanel.Controls.Add(this.panelConfiguration); |  | ||||||
|             this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |  | ||||||
|             this.flowPanel.Location = new System.Drawing.Point(9, 9); |  | ||||||
|             this.flowPanel.Name = "flowPanel"; |  | ||||||
|             this.flowPanel.Size = new System.Drawing.Size(322, 307); |  | ||||||
|             this.flowPanel.TabIndex = 0; |  | ||||||
|             this.flowPanel.WrapContents = false; |  | ||||||
|             //  |  | ||||||
|             // TabSettingsAdvanced |  | ||||||
|             //  |  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |  | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |  | ||||||
|             this.Controls.Add(this.flowPanel); |  | ||||||
|             this.Name = "TabSettingsAdvanced"; |  | ||||||
|             this.Size = new System.Drawing.Size(340, 325); |  | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).EndInit(); |  | ||||||
|             this.panelAppButtons.ResumeLayout(false); |  | ||||||
|             this.panelClearCacheAuto.ResumeLayout(false); |  | ||||||
|             this.panelClearCacheAuto.PerformLayout(); |  | ||||||
|             this.panelConfiguration.ResumeLayout(false); |  | ||||||
|             this.flowPanel.ResumeLayout(false); |  | ||||||
|             this.flowPanel.PerformLayout(); |  | ||||||
|             this.ResumeLayout(false); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #endregion |  | ||||||
| 
 |  | ||||||
|         private System.Windows.Forms.Button btnClearCache; |  | ||||||
|         private System.Windows.Forms.CheckBox checkHardwareAcceleration; |  | ||||||
|         private System.Windows.Forms.ToolTip toolTip; |  | ||||||
|         private System.Windows.Forms.Button btnEditCefArgs; |  | ||||||
|         private System.Windows.Forms.Button btnEditCSS; |  | ||||||
|         private System.Windows.Forms.Button btnRestartArgs; |  | ||||||
|         private System.Windows.Forms.Button btnRestart; |  | ||||||
|         private System.Windows.Forms.Button btnOpenAppFolder; |  | ||||||
|         private System.Windows.Forms.Button btnOpenDataFolder; |  | ||||||
|         private System.Windows.Forms.Label labelApp; |  | ||||||
|         private System.Windows.Forms.Panel panelAppButtons; |  | ||||||
|         private System.Windows.Forms.Label labelPerformance; |  | ||||||
|         private System.Windows.Forms.Panel panelClearCacheAuto; |  | ||||||
|         private System.Windows.Forms.Panel panelConfiguration; |  | ||||||
|         private System.Windows.Forms.Label labelConfiguration; |  | ||||||
|         private System.Windows.Forms.Label labelCache; |  | ||||||
|         private Controls.NumericUpDownEx numClearCacheThreshold; |  | ||||||
|         private System.Windows.Forms.CheckBox checkClearCacheAuto; |  | ||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanel; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,160 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.Threading.Tasks; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Configuration; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Management; |  | ||||||
| using TweetDuck.Core.Other.Settings.Dialogs; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     sealed partial class TabSettingsAdvanced : BaseTabSettings{ |  | ||||||
|         private static SystemConfig SysConfig => Program.SystemConfig; |  | ||||||
| 
 |  | ||||||
|         private readonly Action<string> reinjectBrowserCSS; |  | ||||||
| 
 |  | ||||||
|         public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.reinjectBrowserCSS = reinjectBrowserCSS; |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(btnOpenAppFolder, "Opens the folder where the app is located."); |  | ||||||
|             toolTip.SetToolTip(btnOpenDataFolder, "Opens the folder where your profile data is located."); |  | ||||||
|             toolTip.SetToolTip(btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch."); |  | ||||||
|             toolTip.SetToolTip(btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments."); |  | ||||||
| 
 |  | ||||||
|             toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile."); |  | ||||||
| 
 |  | ||||||
|             toolTip.SetToolTip(btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources."); |  | ||||||
|             toolTip.SetToolTip(checkClearCacheAuto, "Automatically clears cache when its size exceeds the set threshold. Note that cache can only be cleared when closing TweetDuck."); |  | ||||||
| 
 |  | ||||||
|             toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework."); |  | ||||||
|             toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows."); |  | ||||||
| 
 |  | ||||||
|             if (SystemConfig.IsHardwareAccelerationSupported){ |  | ||||||
|                 checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 checkHardwareAcceleration.Enabled = false; |  | ||||||
|                 checkHardwareAcceleration.Checked = false; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             checkClearCacheAuto.Checked = SysConfig.ClearCacheAutomatically; |  | ||||||
|             numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked; |  | ||||||
|             numClearCacheThreshold.SetValueSafe(SysConfig.ClearCacheThreshold); |  | ||||||
|              |  | ||||||
|             BrowserCache.GetCacheSize(task => { |  | ||||||
|                 string text = task.Status == TaskStatus.RanToCompletion ? (int)Math.Ceiling(task.Result/(1024.0*1024.0))+" MB" : "unknown"; |  | ||||||
|                 this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})"); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnReady(){ |  | ||||||
|             btnOpenAppFolder.Click += btnOpenAppFolder_Click; |  | ||||||
|             btnOpenDataFolder.Click += btnOpenDataFolder_Click; |  | ||||||
|             btnRestart.Click += btnRestart_Click; |  | ||||||
|             btnRestartArgs.Click += btnRestartArgs_Click; |  | ||||||
| 
 |  | ||||||
|             checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged; |  | ||||||
| 
 |  | ||||||
|             btnClearCache.Click += btnClearCache_Click; |  | ||||||
|             checkClearCacheAuto.CheckedChanged += checkClearCacheAuto_CheckedChanged; |  | ||||||
|              |  | ||||||
|             btnEditCefArgs.Click += btnEditCefArgs_Click; |  | ||||||
|             btnEditCSS.Click += btnEditCSS_Click; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnClosing(){ |  | ||||||
|             SysConfig.ClearCacheAutomatically = checkClearCacheAuto.Checked; |  | ||||||
|             SysConfig.ClearCacheThreshold = (int)numClearCacheThreshold.Value; |  | ||||||
|             SysConfig.Save(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnClearCache_Click(object sender, EventArgs e){ |  | ||||||
|             btnClearCache.Enabled = false; |  | ||||||
|             BrowserCache.SetClearOnExit(); |  | ||||||
|             FormMessage.Information("Clear Cache", "Cache will be automatically cleared when TweetDuck exits.", FormMessage.OK); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkClearCacheAuto_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked; |  | ||||||
|             PromptRestart(); // calls OnClosing |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnEditCefArgs_Click(object sender, EventArgs e){ |  | ||||||
|             DialogSettingsCefArgs form = new DialogSettingsCefArgs(); |  | ||||||
| 
 |  | ||||||
|             form.VisibleChanged += (sender2, args2) => { |  | ||||||
|                 form.MoveToCenter(ParentForm); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             form.FormClosed += (sender2, args2) => { |  | ||||||
|                 RestoreParentForm(); |  | ||||||
| 
 |  | ||||||
|                 if (form.DialogResult == DialogResult.OK){ |  | ||||||
|                     Config.CustomCefArgs = form.CefArgs; |  | ||||||
|                     PromptRestart(); |  | ||||||
|                     form.Dispose(); |  | ||||||
|                 } |  | ||||||
|                 else form.Dispose(); |  | ||||||
|             }; |  | ||||||
|              |  | ||||||
|             form.Show(ParentForm); |  | ||||||
|             NativeMethods.SetFormDisabled(ParentForm, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnEditCSS_Click(object sender, EventArgs e){ |  | ||||||
|             DialogSettingsCSS form = new DialogSettingsCSS(reinjectBrowserCSS); |  | ||||||
| 
 |  | ||||||
|             form.VisibleChanged += (sender2, args2) => { |  | ||||||
|                 form.MoveToCenter(ParentForm); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             form.FormClosed += (sender2, args2) => { |  | ||||||
|                 RestoreParentForm(); |  | ||||||
| 
 |  | ||||||
|                 if (form.DialogResult == DialogResult.OK){ |  | ||||||
|                     Config.CustomBrowserCSS = form.BrowserCSS; |  | ||||||
|                     Config.CustomNotificationCSS = form.NotificationCSS; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 reinjectBrowserCSS(Config.CustomBrowserCSS); // reinject on cancel too, because the CSS is updated while typing |  | ||||||
|                 form.Dispose(); |  | ||||||
|             }; |  | ||||||
|              |  | ||||||
|             form.Show(ParentForm); |  | ||||||
|             NativeMethods.SetFormDisabled(ParentForm, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnOpenAppFolder_Click(object sender, EventArgs e){ |  | ||||||
|             using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnOpenDataFolder_Click(object sender, EventArgs e){ |  | ||||||
|             using(Process.Start("explorer.exe", "\""+Program.StoragePath+"\"")){} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnRestart_Click(object sender, EventArgs e){ |  | ||||||
|             Program.Restart(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnRestartArgs_Click(object sender, EventArgs e){ |  | ||||||
|             using(DialogSettingsRestart dialog = new DialogSettingsRestart(Arguments.GetCurrentClean())){ |  | ||||||
|                 if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                     Program.RestartWithArgs(dialog.Args); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void RestoreParentForm(){ |  | ||||||
|             if (ParentForm != null){ // when the parent is closed first, ParentForm is null in FormClosed event |  | ||||||
|                 NativeMethods.SetFormDisabled(ParentForm, false); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										183
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										183
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
								
							| @@ -1,183 +0,0 @@ | |||||||
| namespace TweetDuck.Core.Other.Settings { |  | ||||||
|     partial class TabSettingsFeedback { |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required designer variable. |  | ||||||
|         /// </summary> |  | ||||||
|         private System.ComponentModel.IContainer components = null; |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Clean up any resources being used. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |  | ||||||
|         protected override void Dispose(bool disposing) { |  | ||||||
|             if (disposing && (components != null)) { |  | ||||||
|                 components.Dispose(); |  | ||||||
|             } |  | ||||||
|             base.Dispose(disposing); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #region Component Designer generated code |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required method for Designer support - do not modify  |  | ||||||
|         /// the contents of this method with the code editor. |  | ||||||
|         /// </summary> |  | ||||||
|         private void InitializeComponent() { |  | ||||||
|             this.components = new System.ComponentModel.Container(); |  | ||||||
|             this.panelDataCollection = new System.Windows.Forms.Panel(); |  | ||||||
|             this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel(); |  | ||||||
|             this.checkDataCollection = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelDataCollectionMessage = new System.Windows.Forms.Label(); |  | ||||||
|             this.btnViewReport = new System.Windows.Forms.Button(); |  | ||||||
|             this.btnSendFeedback = new System.Windows.Forms.Button(); |  | ||||||
|             this.labelDataCollection = new System.Windows.Forms.Label(); |  | ||||||
|             this.labelFeedback = new System.Windows.Forms.Label(); |  | ||||||
|             this.toolTip = new System.Windows.Forms.ToolTip(this.components); |  | ||||||
|             this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); |  | ||||||
|             this.panelDataCollection.SuspendLayout(); |  | ||||||
|             this.flowPanel.SuspendLayout(); |  | ||||||
|             this.SuspendLayout(); |  | ||||||
|             //  |  | ||||||
|             // panelDataCollection |  | ||||||
|             //  |  | ||||||
|             this.panelDataCollection.Anchor = System.Windows.Forms.AnchorStyles.Top; |  | ||||||
|             this.panelDataCollection.Controls.Add(this.labelDataCollectionLink); |  | ||||||
|             this.panelDataCollection.Controls.Add(this.checkDataCollection); |  | ||||||
|             this.panelDataCollection.Location = new System.Drawing.Point(0, 78); |  | ||||||
|             this.panelDataCollection.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.panelDataCollection.Name = "panelDataCollection"; |  | ||||||
|             this.panelDataCollection.Size = new System.Drawing.Size(322, 28); |  | ||||||
|             this.panelDataCollection.TabIndex = 3; |  | ||||||
|             //  |  | ||||||
|             // labelDataCollectionLink |  | ||||||
|             //  |  | ||||||
|             this.labelDataCollectionLink.AutoSize = true; |  | ||||||
|             this.labelDataCollectionLink.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10); |  | ||||||
|             this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; |  | ||||||
|             this.labelDataCollectionLink.Location = new System.Drawing.Point(153, 4); |  | ||||||
|             this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.labelDataCollectionLink.Name = "labelDataCollectionLink"; |  | ||||||
|             this.labelDataCollectionLink.Size = new System.Drawing.Size(71, 21); |  | ||||||
|             this.labelDataCollectionLink.TabIndex = 1; |  | ||||||
|             this.labelDataCollectionLink.TabStop = true; |  | ||||||
|             this.labelDataCollectionLink.Text = "(learn more)"; |  | ||||||
|             this.labelDataCollectionLink.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; |  | ||||||
|             this.labelDataCollectionLink.UseCompatibleTextRendering = true; |  | ||||||
|             //  |  | ||||||
|             // checkDataCollection |  | ||||||
|             //  |  | ||||||
|             this.checkDataCollection.AutoSize = true; |  | ||||||
|             this.checkDataCollection.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkDataCollection.Location = new System.Drawing.Point(6, 6); |  | ||||||
|             this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 6, 0, 2); |  | ||||||
|             this.checkDataCollection.Name = "checkDataCollection"; |  | ||||||
|             this.checkDataCollection.Size = new System.Drawing.Size(147, 19); |  | ||||||
|             this.checkDataCollection.TabIndex = 0; |  | ||||||
|             this.checkDataCollection.Text = "Send Anonymous Data"; |  | ||||||
|             this.checkDataCollection.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelDataCollectionMessage |  | ||||||
|             //  |  | ||||||
|             this.labelDataCollectionMessage.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 143); |  | ||||||
|             this.labelDataCollectionMessage.Margin = new System.Windows.Forms.Padding(6); |  | ||||||
|             this.labelDataCollectionMessage.Name = "labelDataCollectionMessage"; |  | ||||||
|             this.labelDataCollectionMessage.Size = new System.Drawing.Size(310, 67); |  | ||||||
|             this.labelDataCollectionMessage.TabIndex = 5; |  | ||||||
|             //  |  | ||||||
|             // btnViewReport |  | ||||||
|             //  |  | ||||||
|             this.btnViewReport.AutoSize = true; |  | ||||||
|             this.btnViewReport.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnViewReport.Location = new System.Drawing.Point(5, 109); |  | ||||||
|             this.btnViewReport.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.btnViewReport.Name = "btnViewReport"; |  | ||||||
|             this.btnViewReport.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnViewReport.Size = new System.Drawing.Size(155, 25); |  | ||||||
|             this.btnViewReport.TabIndex = 4; |  | ||||||
|             this.btnViewReport.Text = "View My Analytics Report"; |  | ||||||
|             this.btnViewReport.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnSendFeedback |  | ||||||
|             //  |  | ||||||
|             this.btnSendFeedback.AutoSize = true; |  | ||||||
|             this.btnSendFeedback.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnSendFeedback.Location = new System.Drawing.Point(5, 23); |  | ||||||
|             this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.btnSendFeedback.Name = "btnSendFeedback"; |  | ||||||
|             this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnSendFeedback.Size = new System.Drawing.Size(170, 25); |  | ||||||
|             this.btnSendFeedback.TabIndex = 1; |  | ||||||
|             this.btnSendFeedback.Text = "Send Feedback / Bug Report"; |  | ||||||
|             this.btnSendFeedback.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelDataCollection |  | ||||||
|             //  |  | ||||||
|             this.labelDataCollection.AutoSize = true; |  | ||||||
|             this.labelDataCollection.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelDataCollection.Location = new System.Drawing.Point(3, 63); |  | ||||||
|             this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelDataCollection.Name = "labelDataCollection"; |  | ||||||
|             this.labelDataCollection.Size = new System.Drawing.Size(88, 15); |  | ||||||
|             this.labelDataCollection.TabIndex = 2; |  | ||||||
|             this.labelDataCollection.Text = "Data Collection"; |  | ||||||
|             //  |  | ||||||
|             // labelFeedback |  | ||||||
|             //  |  | ||||||
|             this.labelFeedback.AutoSize = true; |  | ||||||
|             this.labelFeedback.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelFeedback.Location = new System.Drawing.Point(0, 0); |  | ||||||
|             this.labelFeedback.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.labelFeedback.Name = "labelFeedback"; |  | ||||||
|             this.labelFeedback.Size = new System.Drawing.Size(72, 20); |  | ||||||
|             this.labelFeedback.TabIndex = 0; |  | ||||||
|             this.labelFeedback.Text = "Feedback"; |  | ||||||
|             //  |  | ||||||
|             // flowPanel |  | ||||||
|             //  |  | ||||||
|             this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelFeedback); |  | ||||||
|             this.flowPanel.Controls.Add(this.btnSendFeedback); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelDataCollection); |  | ||||||
|             this.flowPanel.Controls.Add(this.panelDataCollection); |  | ||||||
|             this.flowPanel.Controls.Add(this.btnViewReport); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelDataCollectionMessage); |  | ||||||
|             this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |  | ||||||
|             this.flowPanel.Location = new System.Drawing.Point(9, 9); |  | ||||||
|             this.flowPanel.Name = "flowPanel"; |  | ||||||
|             this.flowPanel.Size = new System.Drawing.Size(322, 212); |  | ||||||
|             this.flowPanel.TabIndex = 0; |  | ||||||
|             this.flowPanel.WrapContents = false; |  | ||||||
|             //  |  | ||||||
|             // TabSettingsFeedback |  | ||||||
|             //  |  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |  | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |  | ||||||
|             this.Controls.Add(this.flowPanel); |  | ||||||
|             this.Name = "TabSettingsFeedback"; |  | ||||||
|             this.Size = new System.Drawing.Size(340, 230); |  | ||||||
|             this.panelDataCollection.ResumeLayout(false); |  | ||||||
|             this.panelDataCollection.PerformLayout(); |  | ||||||
|             this.flowPanel.ResumeLayout(false); |  | ||||||
|             this.flowPanel.PerformLayout(); |  | ||||||
|             this.ResumeLayout(false); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #endregion |  | ||||||
| 
 |  | ||||||
|         private System.Windows.Forms.Panel panelDataCollection; |  | ||||||
|         private System.Windows.Forms.CheckBox checkDataCollection; |  | ||||||
|         private System.Windows.Forms.Label labelDataCollection; |  | ||||||
|         private System.Windows.Forms.Label labelFeedback; |  | ||||||
|         private System.Windows.Forms.ToolTip toolTip; |  | ||||||
|         private System.Windows.Forms.LinkLabel labelDataCollectionLink; |  | ||||||
|         private System.Windows.Forms.Button btnSendFeedback; |  | ||||||
|         private System.Windows.Forms.Button btnViewReport; |  | ||||||
|         private System.Windows.Forms.Label labelDataCollectionMessage; |  | ||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanel; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Other.Analytics; |  | ||||||
| using TweetDuck.Core.Other.Settings.Dialogs; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Plugins; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     sealed partial class TabSettingsFeedback : BaseTabSettings{ |  | ||||||
|         private readonly AnalyticsFile analyticsFile; |  | ||||||
|         private readonly AnalyticsReportGenerator.ExternalInfo analyticsInfo; |  | ||||||
|         private readonly PluginManager plugins; |  | ||||||
| 
 |  | ||||||
|         public TabSettingsFeedback(AnalyticsManager analytics, AnalyticsReportGenerator.ExternalInfo analyticsInfo, PluginManager plugins){ |  | ||||||
|             InitializeComponent(); |  | ||||||
|              |  | ||||||
|             this.analyticsFile = analytics?.File ?? AnalyticsFile.Load(Program.AnalyticsFilePath); |  | ||||||
|             this.analyticsInfo = analyticsInfo; |  | ||||||
|             this.plugins = plugins; |  | ||||||
| 
 |  | ||||||
|             checkDataCollection.Checked = Config.AllowDataCollection; |  | ||||||
| 
 |  | ||||||
|             if (analytics != null){ |  | ||||||
|                 string collectionTime = analyticsFile.LastCollectionMessage; |  | ||||||
|                 labelDataCollectionMessage.Text = string.IsNullOrEmpty(collectionTime) ? "No collection yet" : "Last collection: "+collectionTime; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnReady(){ |  | ||||||
|             btnSendFeedback.Click += btnSendFeedback_Click; |  | ||||||
|             checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged; |  | ||||||
|             labelDataCollectionLink.LinkClicked += labelDataCollectionLink_LinkClicked; |  | ||||||
|             btnViewReport.Click += btnViewReport_Click; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnSendFeedback_Click(object sender, EventArgs e){ |  | ||||||
|             BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/issues/new"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkDataCollection_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.AllowDataCollection = checkDataCollection.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){ |  | ||||||
|             BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnViewReport_Click(object sender, EventArgs e){ |  | ||||||
|             using(DialogSettingsAnalytics dialog = new DialogSettingsAnalytics(AnalyticsReportGenerator.Create(analyticsFile, analyticsInfo, plugins))){ |  | ||||||
|                 dialog.ShowDialog(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										351
									
								
								Core/Other/Settings/TabSettingsGeneral.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										351
									
								
								Core/Other/Settings/TabSettingsGeneral.Designer.cs
									
									
									
										generated
									
									
									
								
							| @@ -1,351 +0,0 @@ | |||||||
| namespace TweetDuck.Core.Other.Settings { |  | ||||||
|     partial class TabSettingsGeneral { |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required designer variable. |  | ||||||
|         /// </summary> |  | ||||||
|         private System.ComponentModel.IContainer components = null; |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Clean up any resources being used. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |  | ||||||
|         protected override void Dispose(bool disposing) { |  | ||||||
|             if (disposing && (components != null)) { |  | ||||||
|                 components.Dispose(); |  | ||||||
|             } |  | ||||||
|             base.Dispose(disposing); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #region Component Designer generated code |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required method for Designer support - do not modify  |  | ||||||
|         /// the contents of this method with the code editor. |  | ||||||
|         /// </summary> |  | ||||||
|         private void InitializeComponent() { |  | ||||||
|             this.components = new System.ComponentModel.Container(); |  | ||||||
|             this.checkExpandLinks = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.toolTip = new System.Windows.Forms.ToolTip(this.components); |  | ||||||
|             this.checkUpdateNotifications = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.btnCheckUpdates = new System.Windows.Forms.Button(); |  | ||||||
|             this.labelZoomValue = new System.Windows.Forms.Label(); |  | ||||||
|             this.checkBestImageQuality = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.checkOpenSearchInFirstColumn = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.trackBarZoom = new System.Windows.Forms.TrackBar(); |  | ||||||
|             this.labelZoom = new System.Windows.Forms.Label(); |  | ||||||
|             this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components); |  | ||||||
|             this.labelUI = new System.Windows.Forms.Label(); |  | ||||||
|             this.panelZoom = new System.Windows.Forms.Panel(); |  | ||||||
|             this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelUpdates = new System.Windows.Forms.Label(); |  | ||||||
|             this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelBrowserSettings = new System.Windows.Forms.Label(); |  | ||||||
|             this.checkSmoothScrolling = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelBrowserPath = new System.Windows.Forms.Label(); |  | ||||||
|             this.comboBoxBrowserPath = new System.Windows.Forms.ComboBox(); |  | ||||||
|             this.labelSearchEngine = new System.Windows.Forms.Label(); |  | ||||||
|             this.comboBoxSearchEngine = new System.Windows.Forms.ComboBox(); |  | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); |  | ||||||
|             this.panelZoom.SuspendLayout(); |  | ||||||
|             this.flowPanel.SuspendLayout(); |  | ||||||
|             this.SuspendLayout(); |  | ||||||
|             //  |  | ||||||
|             // checkExpandLinks |  | ||||||
|             //  |  | ||||||
|             this.checkExpandLinks.AutoSize = true; |  | ||||||
|             this.checkExpandLinks.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkExpandLinks.Location = new System.Drawing.Point(6, 26); |  | ||||||
|             this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |  | ||||||
|             this.checkExpandLinks.Name = "checkExpandLinks"; |  | ||||||
|             this.checkExpandLinks.Size = new System.Drawing.Size(176, 19); |  | ||||||
|             this.checkExpandLinks.TabIndex = 1; |  | ||||||
|             this.checkExpandLinks.Text = "Expand Links When Hovered"; |  | ||||||
|             this.checkExpandLinks.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // checkUpdateNotifications |  | ||||||
|             //  |  | ||||||
|             this.checkUpdateNotifications.AutoSize = true; |  | ||||||
|             this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 427); |  | ||||||
|             this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |  | ||||||
|             this.checkUpdateNotifications.Name = "checkUpdateNotifications"; |  | ||||||
|             this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19); |  | ||||||
|             this.checkUpdateNotifications.TabIndex = 15; |  | ||||||
|             this.checkUpdateNotifications.Text = "Check Updates Automatically"; |  | ||||||
|             this.checkUpdateNotifications.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // btnCheckUpdates |  | ||||||
|             //  |  | ||||||
|             this.btnCheckUpdates.AutoSize = true; |  | ||||||
|             this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.btnCheckUpdates.Location = new System.Drawing.Point(5, 451); |  | ||||||
|             this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.btnCheckUpdates.Name = "btnCheckUpdates"; |  | ||||||
|             this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25); |  | ||||||
|             this.btnCheckUpdates.TabIndex = 16; |  | ||||||
|             this.btnCheckUpdates.Text = "Check Updates Now"; |  | ||||||
|             this.btnCheckUpdates.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelZoomValue |  | ||||||
|             //  |  | ||||||
|             this.labelZoomValue.BackColor = System.Drawing.Color.Transparent; |  | ||||||
|             this.labelZoomValue.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelZoomValue.Location = new System.Drawing.Point(147, 4); |  | ||||||
|             this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); |  | ||||||
|             this.labelZoomValue.Name = "labelZoomValue"; |  | ||||||
|             this.labelZoomValue.Size = new System.Drawing.Size(38, 13); |  | ||||||
|             this.labelZoomValue.TabIndex = 1; |  | ||||||
|             this.labelZoomValue.Text = "100%"; |  | ||||||
|             this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight; |  | ||||||
|             //  |  | ||||||
|             // checkBestImageQuality |  | ||||||
|             //  |  | ||||||
|             this.checkBestImageQuality.AutoSize = true; |  | ||||||
|             this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkBestImageQuality.Location = new System.Drawing.Point(6, 98); |  | ||||||
|             this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkBestImageQuality.Name = "checkBestImageQuality"; |  | ||||||
|             this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19); |  | ||||||
|             this.checkBestImageQuality.TabIndex = 4; |  | ||||||
|             this.checkBestImageQuality.Text = "Best Image Quality"; |  | ||||||
|             this.checkBestImageQuality.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // checkOpenSearchInFirstColumn |  | ||||||
|             //  |  | ||||||
|             this.checkOpenSearchInFirstColumn.AutoSize = true; |  | ||||||
|             this.checkOpenSearchInFirstColumn.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 50); |  | ||||||
|             this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn"; |  | ||||||
|             this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(245, 19); |  | ||||||
|             this.checkOpenSearchInFirstColumn.TabIndex = 2; |  | ||||||
|             this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column"; |  | ||||||
|             this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // trackBarZoom |  | ||||||
|             //  |  | ||||||
|             this.trackBarZoom.AutoSize = false; |  | ||||||
|             this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control; |  | ||||||
|             this.trackBarZoom.LargeChange = 25; |  | ||||||
|             this.trackBarZoom.Location = new System.Drawing.Point(3, 3); |  | ||||||
|             this.trackBarZoom.Maximum = 200; |  | ||||||
|             this.trackBarZoom.Minimum = 50; |  | ||||||
|             this.trackBarZoom.Name = "trackBarZoom"; |  | ||||||
|             this.trackBarZoom.Size = new System.Drawing.Size(148, 30); |  | ||||||
|             this.trackBarZoom.SmallChange = 5; |  | ||||||
|             this.trackBarZoom.TabIndex = 0; |  | ||||||
|             this.trackBarZoom.TickFrequency = 25; |  | ||||||
|             this.trackBarZoom.Value = 100; |  | ||||||
|             //  |  | ||||||
|             // labelZoom |  | ||||||
|             //  |  | ||||||
|             this.labelZoom.AutoSize = true; |  | ||||||
|             this.labelZoom.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelZoom.Location = new System.Drawing.Point(3, 330); |  | ||||||
|             this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelZoom.Name = "labelZoom"; |  | ||||||
|             this.labelZoom.Size = new System.Drawing.Size(39, 15); |  | ||||||
|             this.labelZoom.TabIndex = 12; |  | ||||||
|             this.labelZoom.Text = "Zoom"; |  | ||||||
|             //  |  | ||||||
|             // zoomUpdateTimer |  | ||||||
|             //  |  | ||||||
|             this.zoomUpdateTimer.Interval = 250; |  | ||||||
|             this.zoomUpdateTimer.Tick += new System.EventHandler(this.zoomUpdateTimer_Tick); |  | ||||||
|             //  |  | ||||||
|             // labelUI |  | ||||||
|             //  |  | ||||||
|             this.labelUI.AutoSize = true; |  | ||||||
|             this.labelUI.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelUI.Location = new System.Drawing.Point(0, 0); |  | ||||||
|             this.labelUI.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.labelUI.Name = "labelUI"; |  | ||||||
|             this.labelUI.Size = new System.Drawing.Size(100, 20); |  | ||||||
|             this.labelUI.TabIndex = 0; |  | ||||||
|             this.labelUI.Text = "User Interface"; |  | ||||||
|             //  |  | ||||||
|             // panelZoom |  | ||||||
|             //  |  | ||||||
|             this.panelZoom.Anchor = System.Windows.Forms.AnchorStyles.Top; |  | ||||||
|             this.panelZoom.Controls.Add(this.trackBarZoom); |  | ||||||
|             this.panelZoom.Controls.Add(this.labelZoomValue); |  | ||||||
|             this.panelZoom.Location = new System.Drawing.Point(0, 345); |  | ||||||
|             this.panelZoom.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.panelZoom.Name = "panelZoom"; |  | ||||||
|             this.panelZoom.Size = new System.Drawing.Size(322, 36); |  | ||||||
|             this.panelZoom.TabIndex = 13; |  | ||||||
|             //  |  | ||||||
|             // checkAnimatedAvatars |  | ||||||
|             //  |  | ||||||
|             this.checkAnimatedAvatars.AutoSize = true; |  | ||||||
|             this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 122); |  | ||||||
|             this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkAnimatedAvatars.Name = "checkAnimatedAvatars"; |  | ||||||
|             this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19); |  | ||||||
|             this.checkAnimatedAvatars.TabIndex = 5; |  | ||||||
|             this.checkAnimatedAvatars.Text = "Enable Animated Avatars"; |  | ||||||
|             this.checkAnimatedAvatars.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelUpdates |  | ||||||
|             //  |  | ||||||
|             this.labelUpdates.AutoSize = true; |  | ||||||
|             this.labelUpdates.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelUpdates.Location = new System.Drawing.Point(0, 401); |  | ||||||
|             this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); |  | ||||||
|             this.labelUpdates.Name = "labelUpdates"; |  | ||||||
|             this.labelUpdates.Size = new System.Drawing.Size(64, 20); |  | ||||||
|             this.labelUpdates.TabIndex = 14; |  | ||||||
|             this.labelUpdates.Text = "Updates"; |  | ||||||
|             //  |  | ||||||
|             // flowPanel |  | ||||||
|             //  |  | ||||||
|             this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelUI); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkExpandLinks); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkOpenSearchInFirstColumn); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkKeepLikeFollowDialogsOpen); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkBestImageQuality); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkAnimatedAvatars); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelBrowserSettings); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkSmoothScrolling); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelBrowserPath); |  | ||||||
|             this.flowPanel.Controls.Add(this.comboBoxBrowserPath); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelSearchEngine); |  | ||||||
|             this.flowPanel.Controls.Add(this.comboBoxSearchEngine); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelZoom); |  | ||||||
|             this.flowPanel.Controls.Add(this.panelZoom); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelUpdates); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkUpdateNotifications); |  | ||||||
|             this.flowPanel.Controls.Add(this.btnCheckUpdates); |  | ||||||
|             this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |  | ||||||
|             this.flowPanel.Location = new System.Drawing.Point(9, 9); |  | ||||||
|             this.flowPanel.Name = "flowPanel"; |  | ||||||
|             this.flowPanel.Size = new System.Drawing.Size(322, 486); |  | ||||||
|             this.flowPanel.TabIndex = 0; |  | ||||||
|             this.flowPanel.WrapContents = false; |  | ||||||
|             //  |  | ||||||
|             // checkKeepLikeFollowDialogsOpen |  | ||||||
|             //  |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.AutoSize = true; |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 74); |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen"; |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(190, 19); |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.TabIndex = 3; |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open"; |  | ||||||
|             this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelBrowserSettings |  | ||||||
|             //  |  | ||||||
|             this.labelBrowserSettings.AutoSize = true; |  | ||||||
|             this.labelBrowserSettings.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelBrowserSettings.Location = new System.Drawing.Point(0, 163); |  | ||||||
|             this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); |  | ||||||
|             this.labelBrowserSettings.Name = "labelBrowserSettings"; |  | ||||||
|             this.labelBrowserSettings.Size = new System.Drawing.Size(119, 20); |  | ||||||
|             this.labelBrowserSettings.TabIndex = 6; |  | ||||||
|             this.labelBrowserSettings.Text = "Browser Settings"; |  | ||||||
|             //  |  | ||||||
|             // checkSmoothScrolling |  | ||||||
|             //  |  | ||||||
|             this.checkSmoothScrolling.AutoSize = true; |  | ||||||
|             this.checkSmoothScrolling.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 189); |  | ||||||
|             this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |  | ||||||
|             this.checkSmoothScrolling.Name = "checkSmoothScrolling"; |  | ||||||
|             this.checkSmoothScrolling.Size = new System.Drawing.Size(117, 19); |  | ||||||
|             this.checkSmoothScrolling.TabIndex = 7; |  | ||||||
|             this.checkSmoothScrolling.Text = "Smooth Scrolling"; |  | ||||||
|             this.checkSmoothScrolling.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelBrowserPath |  | ||||||
|             //  |  | ||||||
|             this.labelBrowserPath.AutoSize = true; |  | ||||||
|             this.labelBrowserPath.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelBrowserPath.Location = new System.Drawing.Point(3, 222); |  | ||||||
|             this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelBrowserPath.Name = "labelBrowserPath"; |  | ||||||
|             this.labelBrowserPath.Size = new System.Drawing.Size(103, 15); |  | ||||||
|             this.labelBrowserPath.TabIndex = 8; |  | ||||||
|             this.labelBrowserPath.Text = "Open Links With..."; |  | ||||||
|             //  |  | ||||||
|             // comboBoxBrowserPath |  | ||||||
|             //  |  | ||||||
|             this.comboBoxBrowserPath.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |  | ||||||
|             this.comboBoxBrowserPath.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.comboBoxBrowserPath.FormattingEnabled = true; |  | ||||||
|             this.comboBoxBrowserPath.Location = new System.Drawing.Point(5, 240); |  | ||||||
|             this.comboBoxBrowserPath.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.comboBoxBrowserPath.Name = "comboBoxBrowserPath"; |  | ||||||
|             this.comboBoxBrowserPath.Size = new System.Drawing.Size(173, 23); |  | ||||||
|             this.comboBoxBrowserPath.TabIndex = 9; |  | ||||||
|             //  |  | ||||||
|             // labelSearchEngine |  | ||||||
|             //  |  | ||||||
|             this.labelSearchEngine.AutoSize = true; |  | ||||||
|             this.labelSearchEngine.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelSearchEngine.Location = new System.Drawing.Point(3, 276); |  | ||||||
|             this.labelSearchEngine.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelSearchEngine.Name = "labelSearchEngine"; |  | ||||||
|             this.labelSearchEngine.Size = new System.Drawing.Size(81, 15); |  | ||||||
|             this.labelSearchEngine.TabIndex = 10; |  | ||||||
|             this.labelSearchEngine.Text = "Search Engine"; |  | ||||||
|             //  |  | ||||||
|             // comboBoxSearchEngine |  | ||||||
|             //  |  | ||||||
|             this.comboBoxSearchEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |  | ||||||
|             this.comboBoxSearchEngine.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.comboBoxSearchEngine.FormattingEnabled = true; |  | ||||||
|             this.comboBoxSearchEngine.Location = new System.Drawing.Point(5, 294); |  | ||||||
|             this.comboBoxSearchEngine.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.comboBoxSearchEngine.Name = "comboBoxSearchEngine"; |  | ||||||
|             this.comboBoxSearchEngine.Size = new System.Drawing.Size(173, 23); |  | ||||||
|             this.comboBoxSearchEngine.TabIndex = 11; |  | ||||||
|             //  |  | ||||||
|             // TabSettingsGeneral |  | ||||||
|             //  |  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |  | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |  | ||||||
|             this.Controls.Add(this.flowPanel); |  | ||||||
|             this.Name = "TabSettingsGeneral"; |  | ||||||
|             this.Size = new System.Drawing.Size(340, 504); |  | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); |  | ||||||
|             this.panelZoom.ResumeLayout(false); |  | ||||||
|             this.flowPanel.ResumeLayout(false); |  | ||||||
|             this.flowPanel.PerformLayout(); |  | ||||||
|             this.ResumeLayout(false); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #endregion |  | ||||||
| 
 |  | ||||||
|         private System.Windows.Forms.CheckBox checkExpandLinks; |  | ||||||
|         private System.Windows.Forms.ToolTip toolTip; |  | ||||||
|         private System.Windows.Forms.CheckBox checkUpdateNotifications; |  | ||||||
|         private System.Windows.Forms.Button btnCheckUpdates; |  | ||||||
|         private System.Windows.Forms.Label labelZoom; |  | ||||||
|         private System.Windows.Forms.Label labelZoomValue; |  | ||||||
|         private System.Windows.Forms.TrackBar trackBarZoom; |  | ||||||
|         private System.Windows.Forms.Timer zoomUpdateTimer; |  | ||||||
|         private System.Windows.Forms.Label labelUI; |  | ||||||
|         private System.Windows.Forms.Panel panelZoom; |  | ||||||
|         private System.Windows.Forms.Label labelUpdates; |  | ||||||
|         private System.Windows.Forms.CheckBox checkBestImageQuality; |  | ||||||
|         private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn; |  | ||||||
|         private System.Windows.Forms.CheckBox checkAnimatedAvatars; |  | ||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanel; |  | ||||||
|         private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen; |  | ||||||
|         private System.Windows.Forms.Label labelBrowserPath; |  | ||||||
|         private System.Windows.Forms.ComboBox comboBoxBrowserPath; |  | ||||||
|         private System.Windows.Forms.Label labelBrowserSettings; |  | ||||||
|         private System.Windows.Forms.CheckBox checkSmoothScrolling; |  | ||||||
|         private System.Windows.Forms.Label labelSearchEngine; |  | ||||||
|         private System.Windows.Forms.ComboBox comboBoxSearchEngine; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,253 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.IO; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Handling.General; |  | ||||||
| using TweetDuck.Core.Other.Settings.Dialogs; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Updates; |  | ||||||
| using TweetDuck.Updates.Events; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     sealed partial class TabSettingsGeneral : BaseTabSettings{ |  | ||||||
|         private readonly Action reloadColumns; |  | ||||||
| 
 |  | ||||||
|         private readonly UpdateHandler updates; |  | ||||||
|         private int updateCheckEventId = -1; |  | ||||||
| 
 |  | ||||||
|         private readonly int browserListIndexDefault; |  | ||||||
|         private readonly int browserListIndexCustom; |  | ||||||
| 
 |  | ||||||
|         private readonly int searchEngineIndexDefault; |  | ||||||
|         private readonly int searchEngineIndexCustom; |  | ||||||
| 
 |  | ||||||
|         public TabSettingsGeneral(Action reloadColumns, UpdateHandler updates){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.reloadColumns = reloadColumns; |  | ||||||
|              |  | ||||||
|             this.updates = updates; |  | ||||||
|             this.updates.CheckFinished += updates_CheckFinished; |  | ||||||
|             Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished; |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead."); |  | ||||||
|             toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead."); |  | ||||||
|             toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action."); |  | ||||||
|             toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL)."); |  | ||||||
|             toolTip.SetToolTip(checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs."); |  | ||||||
| 
 |  | ||||||
|             toolTip.SetToolTip(checkSmoothScrolling, "Toggles smooth mouse wheel scrolling."); |  | ||||||
|             toolTip.SetToolTip(comboBoxBrowserPath, "Sets the default browser for opening links."); |  | ||||||
|             toolTip.SetToolTip(labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots."); |  | ||||||
|             toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue)); |  | ||||||
| 
 |  | ||||||
|             toolTip.SetToolTip(checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again."); |  | ||||||
|             toolTip.SetToolTip(btnCheckUpdates, "Forces an update check, even for updates that had been dismissed."); |  | ||||||
| 
 |  | ||||||
|             checkExpandLinks.Checked = Config.ExpandLinksOnHover; |  | ||||||
|             checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn; |  | ||||||
|             checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen; |  | ||||||
|             checkBestImageQuality.Checked = Config.BestImageQuality; |  | ||||||
|             checkAnimatedAvatars.Checked = Config.EnableAnimatedImages; |  | ||||||
| 
 |  | ||||||
|             checkSmoothScrolling.Checked = Config.EnableSmoothScrolling; |  | ||||||
| 
 |  | ||||||
|             foreach(WindowsUtils.Browser browserInfo in WindowsUtils.FindInstalledBrowsers()){ |  | ||||||
|                 comboBoxBrowserPath.Items.Add(browserInfo); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             browserListIndexDefault = comboBoxBrowserPath.Items.Add("(default browser)"); |  | ||||||
|             browserListIndexCustom = comboBoxBrowserPath.Items.Add("(custom program...)"); |  | ||||||
|             UpdateBrowserPathSelection(); |  | ||||||
| 
 |  | ||||||
|             comboBoxSearchEngine.Items.Add(new SearchEngine("DuckDuckGo", "https://duckduckgo.com/?q=")); |  | ||||||
|             comboBoxSearchEngine.Items.Add(new SearchEngine("Google", "https://www.google.com/search?q=")); |  | ||||||
|             comboBoxSearchEngine.Items.Add(new SearchEngine("Bing", "https://www.bing.com/search?q=")); |  | ||||||
|             comboBoxSearchEngine.Items.Add(new SearchEngine("Yahoo", "https://search.yahoo.com/search?p=")); |  | ||||||
|             searchEngineIndexDefault = comboBoxSearchEngine.Items.Add("(no engine set)"); |  | ||||||
|             searchEngineIndexCustom = comboBoxSearchEngine.Items.Add("(custom url...)"); |  | ||||||
|             UpdateSearchEngineSelection(); |  | ||||||
|              |  | ||||||
|             trackBarZoom.SetValueSafe(Config.ZoomLevel); |  | ||||||
|             labelZoomValue.Text = trackBarZoom.Value+"%"; |  | ||||||
| 
 |  | ||||||
|             checkUpdateNotifications.Checked = Config.EnableUpdateCheck; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnReady(){ |  | ||||||
|             checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged; |  | ||||||
|             checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged; |  | ||||||
|             checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged; |  | ||||||
|             checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged; |  | ||||||
|             checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged; |  | ||||||
| 
 |  | ||||||
|             checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged; |  | ||||||
|             comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged; |  | ||||||
|             comboBoxSearchEngine.SelectedIndexChanged += comboBoxSearchEngine_SelectedIndexChanged; |  | ||||||
|             trackBarZoom.ValueChanged += trackBarZoom_ValueChanged; |  | ||||||
| 
 |  | ||||||
|             checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged; |  | ||||||
|             btnCheckUpdates.Click += btnCheckUpdates_Click; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnClosing(){ |  | ||||||
|             Config.ZoomLevel = trackBarZoom.Value; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkExpandLinks_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.ExpandLinksOnHover = checkExpandLinks.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkKeepLikeFollowDialogsOpen_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.KeepLikeFollowDialogsOpen = checkKeepLikeFollowDialogsOpen.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.BestImageQuality = checkBestImageQuality.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.EnableAnimatedImages = checkAnimatedAvatars.Checked; |  | ||||||
|             BrowserProcessHandler.UpdatePrefs().ContinueWith(task => reloadColumns()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkSmoothScrolling_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.EnableSmoothScrolling = checkSmoothScrolling.Checked; |  | ||||||
|             PromptRestart(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void UpdateBrowserPathSelection(){ |  | ||||||
|             if (string.IsNullOrEmpty(Config.BrowserPath) || !File.Exists(Config.BrowserPath)){ |  | ||||||
|                 comboBoxBrowserPath.SelectedIndex = browserListIndexDefault; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 WindowsUtils.Browser browserInfo = comboBoxBrowserPath.Items.OfType<WindowsUtils.Browser>().FirstOrDefault(browser => browser.Path == Config.BrowserPath); |  | ||||||
| 
 |  | ||||||
|                 if (browserInfo == null){ |  | ||||||
|                     comboBoxBrowserPath.SelectedIndex = browserListIndexCustom; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     comboBoxBrowserPath.SelectedItem = browserInfo; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void comboBoxBrowserPath_SelectedIndexChanged(object sender, EventArgs e){ |  | ||||||
|             if (comboBoxBrowserPath.SelectedIndex == browserListIndexCustom){ |  | ||||||
|                 using(OpenFileDialog dialog = new OpenFileDialog{ |  | ||||||
|                     AutoUpgradeEnabled = true, |  | ||||||
|                     DereferenceLinks = true, |  | ||||||
|                     InitialDirectory = Path.GetDirectoryName(Config.BrowserPath), // returns null if argument is null |  | ||||||
|                     Title = "Open Links With...", |  | ||||||
|                     Filter = "Executables (*.exe;*.bat;*.cmd)|*.exe;*.bat;*.cmd|All Files (*.*)|*.*" |  | ||||||
|                 }){ |  | ||||||
|                     if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                         Config.BrowserPath = dialog.FileName; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 comboBoxBrowserPath.SelectedIndexChanged -= comboBoxBrowserPath_SelectedIndexChanged; |  | ||||||
|                 UpdateBrowserPathSelection(); |  | ||||||
|                 comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 Config.BrowserPath = (comboBoxBrowserPath.SelectedItem as WindowsUtils.Browser)?.Path; // default browser item is a string and casts to null |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void comboBoxSearchEngine_SelectedIndexChanged(object sender, EventArgs e){ |  | ||||||
|             if (comboBoxSearchEngine.SelectedIndex == searchEngineIndexCustom){ |  | ||||||
|                 using(DialogSettingsSearchEngine dialog = new DialogSettingsSearchEngine()){ |  | ||||||
|                     if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                         Config.SearchEngineUrl = dialog.Url.Trim(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 comboBoxSearchEngine.SelectedIndexChanged -= comboBoxSearchEngine_SelectedIndexChanged; |  | ||||||
|                 UpdateSearchEngineSelection(); |  | ||||||
|                 comboBoxSearchEngine.SelectedIndexChanged += comboBoxSearchEngine_SelectedIndexChanged; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 Config.SearchEngineUrl = (comboBoxSearchEngine.SelectedItem as SearchEngine)?.Url; // default search engine item is a string and casts to null |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void UpdateSearchEngineSelection(){ |  | ||||||
|             if (string.IsNullOrEmpty(Config.SearchEngineUrl)){ |  | ||||||
|                 comboBoxSearchEngine.SelectedIndex = searchEngineIndexDefault; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 SearchEngine engineInfo = comboBoxSearchEngine.Items.OfType<SearchEngine>().FirstOrDefault(engine => engine.Url == Config.SearchEngineUrl); |  | ||||||
|                  |  | ||||||
|                 if (engineInfo == null){ |  | ||||||
|                     comboBoxSearchEngine.SelectedIndex = searchEngineIndexCustom; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     comboBoxSearchEngine.SelectedItem = engineInfo; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void trackBarZoom_ValueChanged(object sender, EventArgs e){ |  | ||||||
|             if (trackBarZoom.AlignValueToTick()){ |  | ||||||
|                 zoomUpdateTimer.Stop(); |  | ||||||
|                 zoomUpdateTimer.Start(); |  | ||||||
|                 labelZoomValue.Text = trackBarZoom.Value+"%"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.EnableUpdateCheck = checkUpdateNotifications.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnCheckUpdates_Click(object sender, EventArgs e){ |  | ||||||
|             Config.DismissedUpdate = null; |  | ||||||
| 
 |  | ||||||
|             btnCheckUpdates.Enabled = false; |  | ||||||
|             updateCheckEventId = updates.Check(true); |  | ||||||
| 
 |  | ||||||
|             if (updateCheckEventId == UpdateHandler.CheckCodeNotOnTweetDeck){ |  | ||||||
|                 FormMessage.Error("Update Check", "Updates can only be checked once TweetDeck is fully loaded.", FormMessage.OK); |  | ||||||
|                 btnCheckUpdates.Enabled = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ |  | ||||||
|             if (e.EventId == updateCheckEventId){ |  | ||||||
|                 btnCheckUpdates.Enabled = true; |  | ||||||
| 
 |  | ||||||
|                 e.Result.Handle(update => { |  | ||||||
|                     if (update.VersionTag == Program.VersionTag){ |  | ||||||
|                         FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK); |  | ||||||
|                     } |  | ||||||
|                 }, ex => { |  | ||||||
|                     Program.Reporter.HandleException("Update Check Error", "An error occurred while checking for updates.", true, ex); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void zoomUpdateTimer_Tick(object sender, EventArgs e){ |  | ||||||
|             Config.ZoomLevel = trackBarZoom.Value; |  | ||||||
|             zoomUpdateTimer.Stop(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private sealed class SearchEngine{ |  | ||||||
|             private string Name { get; } |  | ||||||
|             public string Url { get; } |  | ||||||
|              |  | ||||||
|             public SearchEngine(string name, string url){ |  | ||||||
|                 Name = name; |  | ||||||
|                 Url = url; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             public override int GetHashCode() => Name.GetHashCode(); |  | ||||||
|             public override bool Equals(object obj) => obj is SearchEngine other && Name == other.Name; |  | ||||||
|             public override string ToString() => Name; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										160
									
								
								Core/Other/Settings/TabSettingsLocales.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										160
									
								
								Core/Other/Settings/TabSettingsLocales.Designer.cs
									
									
									
										generated
									
									
									
								
							| @@ -1,160 +0,0 @@ | |||||||
| namespace TweetDuck.Core.Other.Settings { |  | ||||||
|     partial class TabSettingsLocales { |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required designer variable. |  | ||||||
|         /// </summary> |  | ||||||
|         private System.ComponentModel.IContainer components = null; |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Clean up any resources being used. |  | ||||||
|         /// </summary> |  | ||||||
|         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |  | ||||||
|         protected override void Dispose(bool disposing) { |  | ||||||
|             if (disposing && (components != null)) { |  | ||||||
|                 components.Dispose(); |  | ||||||
|             } |  | ||||||
|             base.Dispose(disposing); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #region Component Designer generated code |  | ||||||
| 
 |  | ||||||
|         /// <summary>  |  | ||||||
|         /// Required method for Designer support - do not modify  |  | ||||||
|         /// the contents of this method with the code editor. |  | ||||||
|         /// </summary> |  | ||||||
|         private void InitializeComponent() { |  | ||||||
|             this.components = new System.ComponentModel.Container(); |  | ||||||
|             this.toolTip = new System.Windows.Forms.ToolTip(this.components); |  | ||||||
|             this.checkSpellCheck = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelLocales = new System.Windows.Forms.Label(); |  | ||||||
|             this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); |  | ||||||
|             this.labelSpellCheckLanguage = new System.Windows.Forms.Label(); |  | ||||||
|             this.comboBoxSpellCheckLanguage = new System.Windows.Forms.ComboBox(); |  | ||||||
|             this.labelTranslations = new System.Windows.Forms.Label(); |  | ||||||
|             this.labelTranslationTarget = new System.Windows.Forms.Label(); |  | ||||||
|             this.comboBoxTranslationTarget = new System.Windows.Forms.ComboBox(); |  | ||||||
|             this.flowPanel.SuspendLayout(); |  | ||||||
|             this.SuspendLayout(); |  | ||||||
|             //  |  | ||||||
|             // checkSpellCheck |  | ||||||
|             //  |  | ||||||
|             this.checkSpellCheck.AutoSize = true; |  | ||||||
|             this.checkSpellCheck.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.checkSpellCheck.Location = new System.Drawing.Point(6, 26); |  | ||||||
|             this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |  | ||||||
|             this.checkSpellCheck.Name = "checkSpellCheck"; |  | ||||||
|             this.checkSpellCheck.Size = new System.Drawing.Size(125, 19); |  | ||||||
|             this.checkSpellCheck.TabIndex = 1; |  | ||||||
|             this.checkSpellCheck.Text = "Enable Spell Check"; |  | ||||||
|             this.checkSpellCheck.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelLocales |  | ||||||
|             //  |  | ||||||
|             this.labelLocales.AutoSize = true; |  | ||||||
|             this.labelLocales.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelLocales.Location = new System.Drawing.Point(0, 0); |  | ||||||
|             this.labelLocales.Margin = new System.Windows.Forms.Padding(0); |  | ||||||
|             this.labelLocales.Name = "labelLocales"; |  | ||||||
|             this.labelLocales.Size = new System.Drawing.Size(58, 20); |  | ||||||
|             this.labelLocales.TabIndex = 0; |  | ||||||
|             this.labelLocales.Text = "Locales"; |  | ||||||
|             //  |  | ||||||
|             // flowPanel |  | ||||||
|             //  |  | ||||||
|             this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Right))); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelLocales); |  | ||||||
|             this.flowPanel.Controls.Add(this.checkSpellCheck); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelSpellCheckLanguage); |  | ||||||
|             this.flowPanel.Controls.Add(this.comboBoxSpellCheckLanguage); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelTranslations); |  | ||||||
|             this.flowPanel.Controls.Add(this.labelTranslationTarget); |  | ||||||
|             this.flowPanel.Controls.Add(this.comboBoxTranslationTarget); |  | ||||||
|             this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |  | ||||||
|             this.flowPanel.Location = new System.Drawing.Point(9, 9); |  | ||||||
|             this.flowPanel.Name = "flowPanel"; |  | ||||||
|             this.flowPanel.Size = new System.Drawing.Size(322, 201); |  | ||||||
|             this.flowPanel.TabIndex = 0; |  | ||||||
|             this.flowPanel.WrapContents = false; |  | ||||||
|             //  |  | ||||||
|             // labelSpellCheckLanguage |  | ||||||
|             //  |  | ||||||
|             this.labelSpellCheckLanguage.AutoSize = true; |  | ||||||
|             this.labelSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelSpellCheckLanguage.Location = new System.Drawing.Point(3, 59); |  | ||||||
|             this.labelSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelSpellCheckLanguage.Name = "labelSpellCheckLanguage"; |  | ||||||
|             this.labelSpellCheckLanguage.Size = new System.Drawing.Size(123, 15); |  | ||||||
|             this.labelSpellCheckLanguage.TabIndex = 2; |  | ||||||
|             this.labelSpellCheckLanguage.Text = "Spell Check Language"; |  | ||||||
|             //  |  | ||||||
|             // comboBoxSpellCheckLanguage |  | ||||||
|             //  |  | ||||||
|             this.comboBoxSpellCheckLanguage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |  | ||||||
|             this.comboBoxSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.comboBoxSpellCheckLanguage.FormattingEnabled = true; |  | ||||||
|             this.comboBoxSpellCheckLanguage.Location = new System.Drawing.Point(5, 77); |  | ||||||
|             this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage"; |  | ||||||
|             this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(311, 23); |  | ||||||
|             this.comboBoxSpellCheckLanguage.TabIndex = 3; |  | ||||||
|             //  |  | ||||||
|             // labelTranslations |  | ||||||
|             //  |  | ||||||
|             this.labelTranslations.AutoSize = true; |  | ||||||
|             this.labelTranslations.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelTranslations.Location = new System.Drawing.Point(0, 123); |  | ||||||
|             this.labelTranslations.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); |  | ||||||
|             this.labelTranslations.Name = "labelTranslations"; |  | ||||||
|             this.labelTranslations.Size = new System.Drawing.Size(109, 20); |  | ||||||
|             this.labelTranslations.TabIndex = 4; |  | ||||||
|             this.labelTranslations.Text = "Bing Translator"; |  | ||||||
|             //  |  | ||||||
|             // labelTranslationTarget |  | ||||||
|             //  |  | ||||||
|             this.labelTranslationTarget.AutoSize = true; |  | ||||||
|             this.labelTranslationTarget.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.labelTranslationTarget.Location = new System.Drawing.Point(3, 155); |  | ||||||
|             this.labelTranslationTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelTranslationTarget.Name = "labelTranslationTarget"; |  | ||||||
|             this.labelTranslationTarget.Size = new System.Drawing.Size(96, 15); |  | ||||||
|             this.labelTranslationTarget.TabIndex = 5; |  | ||||||
|             this.labelTranslationTarget.Text = "Target Language"; |  | ||||||
|             //  |  | ||||||
|             // comboBoxTranslationTarget |  | ||||||
|             //  |  | ||||||
|             this.comboBoxTranslationTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |  | ||||||
|             this.comboBoxTranslationTarget.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); |  | ||||||
|             this.comboBoxTranslationTarget.FormattingEnabled = true; |  | ||||||
|             this.comboBoxTranslationTarget.Location = new System.Drawing.Point(5, 173); |  | ||||||
|             this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |  | ||||||
|             this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget"; |  | ||||||
|             this.comboBoxTranslationTarget.Size = new System.Drawing.Size(311, 23); |  | ||||||
|             this.comboBoxTranslationTarget.TabIndex = 6; |  | ||||||
|             //  |  | ||||||
|             // TabSettingsLocales |  | ||||||
|             //  |  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |  | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |  | ||||||
|             this.Controls.Add(this.flowPanel); |  | ||||||
|             this.Name = "TabSettingsLocales"; |  | ||||||
|             this.Size = new System.Drawing.Size(340, 219); |  | ||||||
|             this.flowPanel.ResumeLayout(false); |  | ||||||
|             this.flowPanel.PerformLayout(); |  | ||||||
|             this.ResumeLayout(false); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         #endregion |  | ||||||
|         private System.Windows.Forms.ToolTip toolTip; |  | ||||||
|         private System.Windows.Forms.CheckBox checkSpellCheck; |  | ||||||
|         private System.Windows.Forms.Label labelLocales; |  | ||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanel; |  | ||||||
|         private System.Windows.Forms.ComboBox comboBoxTranslationTarget; |  | ||||||
|         private System.Windows.Forms.Label labelTranslationTarget; |  | ||||||
|         private System.Windows.Forms.ComboBox comboBoxSpellCheckLanguage; |  | ||||||
|         private System.Windows.Forms.Label labelTranslations; |  | ||||||
|         private System.Windows.Forms.Label labelSpellCheckLanguage; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,53 +0,0 @@ | |||||||
| using System; |  | ||||||
| using TweetDuck.Core.Handling.General; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     sealed partial class TabSettingsLocales : BaseTabSettings{ |  | ||||||
|         public TabSettingsLocales(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(checkSpellCheck, "Underlines words that are spelled incorrectly."); |  | ||||||
|             toolTip.SetToolTip(comboBoxSpellCheckLanguage, "Language used for spell check."); |  | ||||||
|             toolTip.SetToolTip(comboBoxTranslationTarget, "Language tweets are translated into."); |  | ||||||
|              |  | ||||||
|             checkSpellCheck.Checked = Config.EnableSpellCheck; |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 foreach(LocaleUtils.Item item in LocaleUtils.SpellCheckLanguages){ |  | ||||||
|                     comboBoxSpellCheckLanguage.Items.Add(item); |  | ||||||
|                 } |  | ||||||
|             }catch{ |  | ||||||
|                 comboBoxSpellCheckLanguage.Items.Add(new LocaleUtils.Item("en-US")); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             comboBoxSpellCheckLanguage.SelectedItem = new LocaleUtils.Item(Config.SpellCheckLanguage); |  | ||||||
| 
 |  | ||||||
|             foreach(LocaleUtils.Item item in LocaleUtils.TweetDeckTranslationLocales){ |  | ||||||
|                 comboBoxTranslationTarget.Items.Add(item); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             comboBoxTranslationTarget.SelectedItem = new LocaleUtils.Item(Config.TranslationTarget); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnReady(){ |  | ||||||
|             checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged; |  | ||||||
|             comboBoxSpellCheckLanguage.SelectedValueChanged += comboBoxSpellCheckLanguage_SelectedValueChanged; |  | ||||||
|             comboBoxTranslationTarget.SelectedValueChanged += comboBoxTranslationTarget_SelectedValueChanged; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.EnableSpellCheck = checkSpellCheck.Checked; |  | ||||||
|             BrowserProcessHandler.UpdatePrefs(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void comboBoxSpellCheckLanguage_SelectedValueChanged(object sender, EventArgs e){ |  | ||||||
|             Config.SpellCheckLanguage = (comboBoxSpellCheckLanguage.SelectedItem as LocaleUtils.Item)?.Code ?? "en-US"; |  | ||||||
|             PromptRestart(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void comboBoxTranslationTarget_SelectedValueChanged(object sender, EventArgs e){ |  | ||||||
|             Config.TranslationTarget = (comboBoxTranslationTarget.SelectedItem as LocaleUtils.Item)?.Code ?? "en"; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,276 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| using TweetDuck.Core.Notification.Example; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     sealed partial class TabSettingsNotifications : BaseTabSettings{ |  | ||||||
|         private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 }; |  | ||||||
| 
 |  | ||||||
|         private readonly FormNotificationExample notification; |  | ||||||
| 
 |  | ||||||
|         public TabSettingsNotifications(FormNotificationExample notification){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.notification = notification; |  | ||||||
|              |  | ||||||
|             this.notification.Ready += (sender, args) => { |  | ||||||
|                 this.InvokeAsyncSafe(() => { |  | ||||||
|                     this.notification.ShowExampleNotification(true); |  | ||||||
|                     this.notification.Move += notification_Move; |  | ||||||
|                     this.notification.ResizeEnd += notification_ResizeEnd; |  | ||||||
|                 }); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             this.notification.Activated += notification_Activated; |  | ||||||
|             this.notification.Show(); |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(checkColumnName, "Shows column name each notification originated\r\nfrom in the notification window title."); |  | ||||||
|             toolTip.SetToolTip(checkMediaPreviews, "Shows image and video thumbnails in the notification window."); |  | ||||||
|             toolTip.SetToolTip(checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked."); |  | ||||||
|             toolTip.SetToolTip(checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be delayed until the cursor moves away to prevent accidental clicks."); |  | ||||||
| 
 |  | ||||||
|             toolTip.SetToolTip(comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time."); |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(checkTimerCountDown, "The notification timer counts down instead of up."); |  | ||||||
|             toolTip.SetToolTip(labelDurationValue, "Milliseconds per character."); |  | ||||||
|             toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue)); |  | ||||||
| 
 |  | ||||||
|             toolTip.SetToolTip(radioLocCustom, "Drag the example notification window to the desired location."); |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(radioSizeAuto, "Notification size is based on the font size and browser zoom level."); |  | ||||||
|             toolTip.SetToolTip(radioSizeCustom, "Resize the example notification window to the desired size."); |  | ||||||
|              |  | ||||||
|             checkColumnName.Checked = Config.DisplayNotificationColumn; |  | ||||||
|             checkMediaPreviews.Checked = Config.NotificationMediaPreviews; |  | ||||||
|             checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick; |  | ||||||
|             checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode; |  | ||||||
| 
 |  | ||||||
|             comboBoxIdlePause.Items.Add("Disabled"); |  | ||||||
|             comboBoxIdlePause.Items.Add("30 seconds"); |  | ||||||
|             comboBoxIdlePause.Items.Add("1 minute"); |  | ||||||
|             comboBoxIdlePause.Items.Add("2 minutes"); |  | ||||||
|             comboBoxIdlePause.Items.Add("5 minutes"); |  | ||||||
|             comboBoxIdlePause.SelectedIndex = Math.Max(0, Array.FindIndex(IdlePauseSeconds, val => val == Config.NotificationIdlePauseSeconds)); |  | ||||||
| 
 |  | ||||||
|             checkNotificationTimer.Checked = Config.DisplayNotificationTimer; |  | ||||||
|             checkTimerCountDown.Enabled = checkNotificationTimer.Checked; |  | ||||||
|             checkTimerCountDown.Checked = Config.NotificationTimerCountDown; |  | ||||||
|              |  | ||||||
|             trackBarDuration.SetValueSafe(Config.NotificationDurationValue); |  | ||||||
|             labelDurationValue.Text = Config.NotificationDurationValue+" ms/c"; |  | ||||||
| 
 |  | ||||||
|             switch(Config.NotificationPosition){ |  | ||||||
|                 case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break; |  | ||||||
|                 case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break; |  | ||||||
|                 case TweetNotification.Position.BottomLeft: radioLocBL.Checked = true; break; |  | ||||||
|                 case TweetNotification.Position.BottomRight: radioLocBR.Checked = true; break; |  | ||||||
|                 case TweetNotification.Position.Custom: radioLocCustom.Checked = true; break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked; |  | ||||||
|             comboBoxDisplay.Items.Add("(Same as TweetDuck)"); |  | ||||||
| 
 |  | ||||||
|             foreach(Screen screen in Screen.AllScreens){ |  | ||||||
|                 comboBoxDisplay.Items.Add(screen.DeviceName.TrimStart('\\', '.')+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1, Config.NotificationDisplay); |  | ||||||
| 
 |  | ||||||
|             trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance); |  | ||||||
|             labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px"; |  | ||||||
| 
 |  | ||||||
|             switch(Config.NotificationSize){ |  | ||||||
|                 case TweetNotification.Size.Auto: radioSizeAuto.Checked = true; break; |  | ||||||
|                 case TweetNotification.Size.Custom: radioSizeCustom.Checked = true; break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed); |  | ||||||
|             labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%"; |  | ||||||
|              |  | ||||||
|             Disposed += (sender, args) => this.notification.Dispose(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnReady(){ |  | ||||||
|             checkColumnName.CheckedChanged += checkColumnName_CheckedChanged; |  | ||||||
|             checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged; |  | ||||||
|             checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged; |  | ||||||
|             checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged; |  | ||||||
| 
 |  | ||||||
|             comboBoxIdlePause.SelectedValueChanged += comboBoxIdlePause_SelectedValueChanged; |  | ||||||
| 
 |  | ||||||
|             checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged; |  | ||||||
|             checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged; |  | ||||||
| 
 |  | ||||||
|             trackBarDuration.ValueChanged += trackBarDuration_ValueChanged; |  | ||||||
|             btnDurationShort.Click += btnDurationShort_Click; |  | ||||||
|             btnDurationMedium.Click += btnDurationMedium_Click; |  | ||||||
|             btnDurationLong.Click += btnDurationLong_Click; |  | ||||||
| 
 |  | ||||||
|             radioLocTL.CheckedChanged += radioLoc_CheckedChanged; |  | ||||||
|             radioLocTR.CheckedChanged += radioLoc_CheckedChanged; |  | ||||||
|             radioLocBL.CheckedChanged += radioLoc_CheckedChanged; |  | ||||||
|             radioLocBR.CheckedChanged += radioLoc_CheckedChanged; |  | ||||||
|             radioLocCustom.Click += radioLocCustom_Click; |  | ||||||
| 
 |  | ||||||
|             comboBoxDisplay.SelectedValueChanged += comboBoxDisplay_SelectedValueChanged; |  | ||||||
|             trackBarEdgeDistance.ValueChanged += trackBarEdgeDistance_ValueChanged; |  | ||||||
| 
 |  | ||||||
|             radioSizeAuto.CheckedChanged += radioSize_CheckedChanged; |  | ||||||
|             radioSizeCustom.Click += radioSizeCustom_Click; |  | ||||||
| 
 |  | ||||||
|             trackBarScrollSpeed.ValueChanged += trackBarScrollSpeed_ValueChanged; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){ |  | ||||||
|             if (Parent == null){ |  | ||||||
|                 notification.HideNotification(); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 notification.ShowExampleNotification(true); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void notification_Activated(object sender, EventArgs e){ |  | ||||||
|             notification.Hide(); |  | ||||||
|             notification.Activated -= notification_Activated; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void notification_Move(object sender, EventArgs e){ |  | ||||||
|             if (radioLocCustom.Checked && notification.Location != ControlExtensions.InvisibleLocation){ |  | ||||||
|                 Config.CustomNotificationPosition = notification.Location; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void notification_ResizeEnd(object sender, EventArgs e){ |  | ||||||
|             if (radioSizeCustom.Checked){ |  | ||||||
|                 Config.CustomNotificationSize = notification.BrowserSize; |  | ||||||
|                 notification.ShowExampleNotification(false); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void radioLoc_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft; |  | ||||||
|             else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight; |  | ||||||
|             else if (radioLocBL.Checked)Config.NotificationPosition = TweetNotification.Position.BottomLeft; |  | ||||||
|             else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight; |  | ||||||
| 
 |  | ||||||
|             comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true; |  | ||||||
|             notification.ShowExampleNotification(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void radioLocCustom_Click(object sender, EventArgs e){ |  | ||||||
|             if (!Config.IsCustomNotificationPositionSet){ |  | ||||||
|                 Config.CustomNotificationPosition = notification.Location; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Config.NotificationPosition = TweetNotification.Position.Custom; |  | ||||||
| 
 |  | ||||||
|             comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false; |  | ||||||
|             notification.ShowExampleNotification(false); |  | ||||||
| 
 |  | ||||||
|             if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is Outside View", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                 Config.NotificationPosition = TweetNotification.Position.TopRight; |  | ||||||
|                 notification.MoveToVisibleLocation(); |  | ||||||
| 
 |  | ||||||
|                 Config.CustomNotificationPosition = notification.Location; |  | ||||||
| 
 |  | ||||||
|                 Config.NotificationPosition = TweetNotification.Position.Custom; |  | ||||||
|                 notification.MoveToVisibleLocation(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void radioSize_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             if (radioSizeAuto.Checked){ |  | ||||||
|                 Config.NotificationSize = TweetNotification.Size.Auto; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             notification.ShowExampleNotification(false); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         private void radioSizeCustom_Click(object sender, EventArgs e){ |  | ||||||
|             if (!Config.IsCustomNotificationSizeSet){ |  | ||||||
|                 Config.CustomNotificationSize = notification.BrowserSize; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Config.NotificationSize = TweetNotification.Size.Custom; |  | ||||||
|             notification.ShowExampleNotification(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void trackBarDuration_ValueChanged(object sender, EventArgs e){ |  | ||||||
|             durationUpdateTimer.Stop(); |  | ||||||
|             durationUpdateTimer.Start(); |  | ||||||
| 
 |  | ||||||
|             Config.NotificationDurationValue = trackBarDuration.Value; |  | ||||||
|             labelDurationValue.Text = Config.NotificationDurationValue+" ms/c"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnDurationShort_Click(object sender, EventArgs e){ |  | ||||||
|             trackBarDuration.Value = 15; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnDurationMedium_Click(object sender, EventArgs e){ |  | ||||||
|             trackBarDuration.Value = 25; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnDurationLong_Click(object sender, EventArgs e){ |  | ||||||
|             trackBarDuration.Value = 35; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkColumnName_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.DisplayNotificationColumn = checkColumnName.Checked; |  | ||||||
|             notification.ShowExampleNotification(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.DisplayNotificationTimer = checkNotificationTimer.Checked; |  | ||||||
|             checkTimerCountDown.Enabled = checkNotificationTimer.Checked; |  | ||||||
|             notification.ShowExampleNotification(true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.NotificationTimerCountDown = checkTimerCountDown.Checked; |  | ||||||
|             notification.ShowExampleNotification(true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.NotificationMediaPreviews = checkMediaPreviews.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkNonIntrusive_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.NotificationNonIntrusiveMode = checkNonIntrusive.Checked; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void comboBoxIdlePause_SelectedValueChanged(object sender, EventArgs e){ |  | ||||||
|             Config.NotificationIdlePauseSeconds = IdlePauseSeconds[comboBoxIdlePause.SelectedIndex]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void trackBarScrollSpeed_ValueChanged(object sender, EventArgs e){ |  | ||||||
|             if (trackBarScrollSpeed.AlignValueToTick()){ |  | ||||||
|                 labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%"; |  | ||||||
|                 Config.NotificationScrollSpeed = trackBarScrollSpeed.Value; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){ |  | ||||||
|             Config.NotificationDisplay = comboBoxDisplay.SelectedIndex; |  | ||||||
|             notification.ShowExampleNotification(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){ |  | ||||||
|             labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px"; |  | ||||||
|             Config.NotificationEdgeDistance = trackBarEdgeDistance.Value; |  | ||||||
|             notification.ShowExampleNotification(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void durationUpdateTimer_Tick(object sender, EventArgs e){ |  | ||||||
|             notification.ShowExampleNotification(true); |  | ||||||
|             durationUpdateTimer.Stop(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,88 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.IO; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     sealed partial class TabSettingsSounds : BaseTabSettings{ |  | ||||||
|         private readonly Action playSoundNotification; |  | ||||||
| 
 |  | ||||||
|         public TabSettingsSounds(Action playSoundNotification){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.playSoundNotification = playSoundNotification; |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(tbCustomSound, "When empty, the default TweetDeck sound notification is used."); |  | ||||||
|              |  | ||||||
|             trackBarVolume.SetValueSafe(Config.NotificationSoundVolume); |  | ||||||
|             labelVolumeValue.Text = trackBarVolume.Value+"%"; |  | ||||||
| 
 |  | ||||||
|             tbCustomSound.Text = Config.NotificationSoundPath; |  | ||||||
|             tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnReady(){ |  | ||||||
|             tbCustomSound.TextChanged += tbCustomSound_TextChanged; |  | ||||||
|             btnPlaySound.Click += btnPlaySound_Click; |  | ||||||
|             btnBrowseSound.Click += btnBrowseSound_Click; |  | ||||||
|             btnResetSound.Click += btnResetSound_Click; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnClosing(){ |  | ||||||
|             Config.NotificationSoundPath = tbCustomSound.Text; |  | ||||||
|             Config.NotificationSoundVolume = trackBarVolume.Value; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private bool RefreshCanPlay(){ |  | ||||||
|             bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text); |  | ||||||
|             bool canPlay = isEmpty || File.Exists(tbCustomSound.Text); |  | ||||||
| 
 |  | ||||||
|             tbCustomSound.ForeColor = canPlay ? SystemColors.WindowText : Color.Red; |  | ||||||
|             btnPlaySound.Enabled = canPlay; |  | ||||||
|             btnResetSound.Enabled = !isEmpty; |  | ||||||
|             return canPlay; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void tbCustomSound_TextChanged(object sender, EventArgs e){ |  | ||||||
|             RefreshCanPlay(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnPlaySound_Click(object sender, EventArgs e){ |  | ||||||
|             if (RefreshCanPlay()){ |  | ||||||
|                 Config.NotificationSoundPath = tbCustomSound.Text; |  | ||||||
|                 Config.NotificationSoundVolume = trackBarVolume.Value; |  | ||||||
|                 playSoundNotification(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnBrowseSound_Click(object sender, EventArgs e){ |  | ||||||
|             using(OpenFileDialog dialog = new OpenFileDialog{ |  | ||||||
|                 AutoUpgradeEnabled = true, |  | ||||||
|                 DereferenceLinks = true, |  | ||||||
|                 Title = "Custom Notification Sound", |  | ||||||
|                 Filter = $"Sound file ({SoundNotification.SupportedFormats})|{SoundNotification.SupportedFormats}|All files (*.*)|*.*" |  | ||||||
|             }){ |  | ||||||
|                 if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                     tbCustomSound.Text = dialog.FileName; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void btnResetSound_Click(object sender, EventArgs e){ |  | ||||||
|             tbCustomSound.Text = string.Empty; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void trackBarVolume_ValueChanged(object sender, EventArgs e){ |  | ||||||
|             volumeUpdateTimer.Stop(); |  | ||||||
|             volumeUpdateTimer.Start(); |  | ||||||
|             labelVolumeValue.Text = trackBarVolume.Value+"%"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void volumeUpdateTimer_Tick(object sender, EventArgs e){ |  | ||||||
|             Config.NotificationSoundVolume = trackBarVolume.Value; |  | ||||||
|             volumeUpdateTimer.Stop(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,36 +0,0 @@ | |||||||
| using System; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other.Settings{ |  | ||||||
|     sealed partial class TabSettingsTray : BaseTabSettings{ |  | ||||||
|         public TabSettingsTray(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
|              |  | ||||||
|             toolTip.SetToolTip(comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu."); |  | ||||||
|             toolTip.SetToolTip(checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored."); |  | ||||||
|              |  | ||||||
|             comboBoxTrayType.Items.Add("Disabled"); |  | ||||||
|             comboBoxTrayType.Items.Add("Display Icon Only"); |  | ||||||
|             comboBoxTrayType.Items.Add("Minimize to Tray"); |  | ||||||
|             comboBoxTrayType.Items.Add("Close to Tray"); |  | ||||||
|             comboBoxTrayType.Items.Add("Combined"); |  | ||||||
|             comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1); |  | ||||||
| 
 |  | ||||||
|             checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon(); |  | ||||||
|             checkTrayHighlight.Checked = Config.EnableTrayHighlight; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override void OnReady(){ |  | ||||||
|             comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged; |  | ||||||
|             checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){ |  | ||||||
|             Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex; |  | ||||||
|             checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){ |  | ||||||
|             Config.EnableTrayHighlight = checkTrayHighlight.Checked; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,101 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.ComponentModel; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Other{ |  | ||||||
|     sealed partial class TrayIcon : Component{ |  | ||||||
|         public enum Behavior{ // keep order |  | ||||||
|             Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public event EventHandler ClickRestore; |  | ||||||
|         public event EventHandler ClickClose; |  | ||||||
| 
 |  | ||||||
|         public bool Visible{ |  | ||||||
|             get{ |  | ||||||
|                 return notifyIcon.Visible; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             set{ |  | ||||||
|                 if (value){ |  | ||||||
|                     notifyIcon.Icon = Properties.Resources.icon_tray; |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 notifyIcon.Visible = value; |  | ||||||
|                 hasNotifications = false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool HasNotifications{ |  | ||||||
|             get{ |  | ||||||
|                 return hasNotifications; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             set{ |  | ||||||
|                 if (hasNotifications != value && Visible){ |  | ||||||
|                     notifyIcon.Icon = value ? Properties.Resources.icon_tray_new : Properties.Resources.icon_tray; |  | ||||||
|                     hasNotifications = value; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly ContextMenu contextMenu; |  | ||||||
|         private bool hasNotifications; |  | ||||||
| 
 |  | ||||||
|         public TrayIcon(){ |  | ||||||
|             InitializeComponent(); |  | ||||||
| 
 |  | ||||||
|             this.contextMenu = new ContextMenu(); |  | ||||||
|             this.contextMenu.MenuItems.Add("Restore", menuItemRestore_Click); |  | ||||||
|             this.contextMenu.MenuItems.Add("Mute notifications", menuItemMuteNotifications_Click); |  | ||||||
|             this.contextMenu.MenuItems.Add("Close", menuItemClose_Click); |  | ||||||
|             this.contextMenu.Popup += contextMenu_Popup; |  | ||||||
|                  |  | ||||||
|             this.notifyIcon.ContextMenu = contextMenu; |  | ||||||
|             this.notifyIcon.Text = Program.BrandName; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public TrayIcon(IContainer container) : this(){ |  | ||||||
|             container.Add(this); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // event handlers |  | ||||||
| 
 |  | ||||||
|         private void trayIcon_MouseClick(object sender, MouseEventArgs e){ |  | ||||||
|             if (e.Button == MouseButtons.Left){ |  | ||||||
|                 menuItemRestore_Click(sender, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void contextMenu_Popup(object sender, EventArgs e){ |  | ||||||
|             contextMenu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void menuItemRestore_Click(object sender, EventArgs e){ |  | ||||||
|             ClickRestore?.Invoke(this, e); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void menuItemMuteNotifications_Click(object sender, EventArgs e){ |  | ||||||
|             Program.UserConfig.MuteNotifications = !contextMenu.MenuItems[1].Checked; |  | ||||||
|             Program.UserConfig.Save(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void menuItemClose_Click(object sender, EventArgs e){ |  | ||||||
|             ClickClose?.Invoke(this, e); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     static class BehaviorExtensions{ |  | ||||||
|         public static bool ShouldDisplayIcon(this TrayIcon.Behavior behavior){ |  | ||||||
|             return behavior != TrayIcon.Behavior.Disabled; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool ShouldHideOnMinimize(this TrayIcon.Behavior behavior){ |  | ||||||
|             return behavior == TrayIcon.Behavior.MinimizeToTray || behavior == TrayIcon.Behavior.Combined; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool ShouldHideOnClose(this TrayIcon.Behavior behavior){ |  | ||||||
|             return behavior == TrayIcon.Behavior.CloseToTray || behavior == TrayIcon.Behavior.Combined; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,257 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Text; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp; |  | ||||||
| using CefSharp.WinForms; |  | ||||||
| using TweetDuck.Core.Bridge; |  | ||||||
| using TweetDuck.Core.Controls; |  | ||||||
| using TweetDuck.Core.Handling; |  | ||||||
| using TweetDuck.Core.Handling.General; |  | ||||||
| using TweetDuck.Core.Notification; |  | ||||||
| using TweetDuck.Core.Other.Interfaces; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| using TweetDuck.Resources; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core{ |  | ||||||
|     sealed class TweetDeckBrowser : ITweetDeckBrowser, IDisposable{ |  | ||||||
|         public bool Ready { get; private set; } |  | ||||||
| 
 |  | ||||||
|         public bool Enabled{ |  | ||||||
|             get => browser.Enabled; |  | ||||||
|             set => browser.Enabled = value; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool IsTweetDeckWebsite{ |  | ||||||
|             get{ |  | ||||||
|                 if (!Ready){ |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 using(IFrame frame = browser.GetBrowser().MainFrame){ |  | ||||||
|                     return TwitterUtils.IsTweetDeckWebsite(frame); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         private readonly ChromiumWebBrowser browser; |  | ||||||
| 
 |  | ||||||
|         private string prevSoundNotificationPath = null; |  | ||||||
| 
 |  | ||||||
|         public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){ |  | ||||||
|             this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){ |  | ||||||
|                 DialogHandler = new FileDialogHandler(), |  | ||||||
|                 DragHandler = new DragHandlerBrowser(), |  | ||||||
|                 MenuHandler = new ContextMenuBrowser(owner), |  | ||||||
|                 JsDialogHandler = new JavaScriptDialogHandler(), |  | ||||||
|                 KeyboardHandler = new KeyboardHandlerBrowser(owner), |  | ||||||
|                 LifeSpanHandler = new LifeSpanHandler(), |  | ||||||
|                 RequestHandler = new RequestHandlerBrowser() |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             this.browser.LoadingStateChanged += browser_LoadingStateChanged; |  | ||||||
|             this.browser.FrameLoadStart += browser_FrameLoadStart; |  | ||||||
|             this.browser.FrameLoadEnd += browser_FrameLoadEnd; |  | ||||||
|             this.browser.LoadError += browser_LoadError; |  | ||||||
| 
 |  | ||||||
|             this.browser.RegisterAsyncJsObject("$TD", bridge); |  | ||||||
|              |  | ||||||
|             this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb(); |  | ||||||
|             this.browser.Dock = DockStyle.None; |  | ||||||
|             this.browser.Location = ControlExtensions.InvisibleLocation; |  | ||||||
| 
 |  | ||||||
|             this.browser.SetupResourceHandler(TweetNotification.AppLogo); |  | ||||||
|             this.browser.SetupResourceHandler(TwitterUtils.LoadingSpinner); |  | ||||||
| 
 |  | ||||||
|             owner.Controls.Add(browser); |  | ||||||
|              |  | ||||||
|             Program.UserConfig.MuteToggled += UserConfig_MuteToggled; |  | ||||||
|             Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged; |  | ||||||
|             Program.UserConfig.SoundNotificationChanged += UserConfig_SoundNotificationInfoChanged; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // setup and management |  | ||||||
| 
 |  | ||||||
|         private void OnBrowserReady(){ |  | ||||||
|             if (!Ready){ |  | ||||||
|                 browser.Location = Point.Empty; |  | ||||||
|                 browser.Dock = DockStyle.Fill; |  | ||||||
|                 Ready = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Focus(){ |  | ||||||
|             browser.Focus(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Dispose(){ |  | ||||||
|             Program.UserConfig.MuteToggled -= UserConfig_MuteToggled; |  | ||||||
|             Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged; |  | ||||||
|             Program.UserConfig.SoundNotificationChanged -= UserConfig_SoundNotificationInfoChanged; |  | ||||||
|              |  | ||||||
|             browser.Dispose(); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         void ITweetDeckBrowser.RegisterBridge(string name, object obj){ |  | ||||||
|             browser.RegisterAsyncJsObject(name, obj); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void ITweetDeckBrowser.OnFrameLoaded(Action<IFrame> callback){ |  | ||||||
|             browser.FrameLoadEnd += (sender, args) => { |  | ||||||
|                 IFrame frame = args.Frame; |  | ||||||
| 
 |  | ||||||
|                 if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){ |  | ||||||
|                     callback(frame); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void ITweetDeckBrowser.ExecuteFunction(string name, params object[] args){ |  | ||||||
|             browser.ExecuteScriptAsync(name, args); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // event handlers |  | ||||||
| 
 |  | ||||||
|         private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ |  | ||||||
|             if (!e.IsLoading){ |  | ||||||
|                 foreach(string word in TwitterUtils.DictionaryWords){ |  | ||||||
|                     browser.AddWordToDictionary(word); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 browser.BeginInvoke(new Action(OnBrowserReady)); |  | ||||||
|                 browser.LoadingStateChanged -= browser_LoadingStateChanged; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){ |  | ||||||
|             IFrame frame = e.Frame; |  | ||||||
| 
 |  | ||||||
|             if (frame.IsMain){ |  | ||||||
|                 if (Program.UserConfig.ZoomLevel != 100){ |  | ||||||
|                     BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (TwitterUtils.IsTwitterWebsite(frame)){ |  | ||||||
|                     ScriptLoader.ExecuteFile(frame, "twitter.js", browser); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ |  | ||||||
|             IFrame frame = e.Frame; |  | ||||||
| 
 |  | ||||||
|             if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){ |  | ||||||
|                 UpdateProperties(); |  | ||||||
|                 TweetDeckBridge.RestoreSessionData(frame); |  | ||||||
|                 ScriptLoader.ExecuteFile(frame, "code.js", browser); |  | ||||||
|                 ScriptLoader.ExecuteFile(frame, "update.js", browser); |  | ||||||
| 
 |  | ||||||
|                 InjectBrowserCSS(); |  | ||||||
|                 ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS); |  | ||||||
|                 UserConfig_SoundNotificationInfoChanged(null, EventArgs.Empty); |  | ||||||
| 
 |  | ||||||
|                 TweetDeckBridge.ResetStaticProperties(); |  | ||||||
| 
 |  | ||||||
|                 if (Program.UserConfig.FirstRun){ |  | ||||||
|                     ScriptLoader.ExecuteFile(frame, "introduction.js", browser); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void browser_LoadError(object sender, LoadErrorEventArgs e){ |  | ||||||
|             if (e.ErrorCode == CefErrorCode.Aborted){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){ |  | ||||||
|                 string errorPage = ScriptLoader.LoadResource("pages/error.html", true); |  | ||||||
| 
 |  | ||||||
|                 if (errorPage != null){ |  | ||||||
|                     browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void UserConfig_MuteToggled(object sender, EventArgs e){ |  | ||||||
|             UpdateProperties(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void UserConfig_ZoomLevelChanged(object sender, EventArgs e){ |  | ||||||
|             BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void UserConfig_SoundNotificationInfoChanged(object sender, EventArgs e){ |  | ||||||
|             const string soundUrl = "https://ton.twimg.com/tduck/updatesnd"; |  | ||||||
|             bool hasCustomSound = Program.UserConfig.IsCustomSoundNotificationSet; |  | ||||||
| 
 |  | ||||||
|             if (prevSoundNotificationPath != Program.UserConfig.NotificationSoundPath){ |  | ||||||
|                 browser.SetupResourceHandler(soundUrl, hasCustomSound ? SoundNotification.CreateFileHandler(Program.UserConfig.NotificationSoundPath) : null); |  | ||||||
|                 prevSoundNotificationPath = Program.UserConfig.NotificationSoundPath; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_setSoundNotificationData", hasCustomSound, Program.UserConfig.NotificationSoundVolume); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // external handling |  | ||||||
| 
 |  | ||||||
|         public void HideVideoOverlay(bool focus){ |  | ||||||
|             if (focus){ |  | ||||||
|                 browser.GetBrowser().GetHost().SendFocusEvent(true); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // javascript calls |  | ||||||
| 
 |  | ||||||
|         public void ReloadToTweetDeck(){ |  | ||||||
|             browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void UpdateProperties(){ |  | ||||||
|             browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void InjectBrowserCSS(){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css", false, browser)?.TrimEnd() ?? string.Empty); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ReinjectCustomCSS(string css){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void OnMouseClickExtra(IntPtr param){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void AddSearchColumn(string query){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_performSearch", query); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void TriggerTweetScreenshot(){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_triggerScreenshot()"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ReloadColumns(){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_reloadColumns()"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void PlaySoundNotification(){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_playSoundNotification()"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ApplyROT13(){ |  | ||||||
|             browser.ExecuteScriptAsync("TDGF_applyROT13()"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ShowUpdateNotification(string versionTag, string releaseNotes){ |  | ||||||
|             browser.ExecuteScriptAsync("TDUF_displayNotification", versionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(releaseNotes))); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,206 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.IO; |  | ||||||
| using System.Net; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using CefSharp.WinForms; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Utils{ |  | ||||||
|     static class BrowserUtils{ |  | ||||||
|         public static string UserAgentVanilla => Program.BrandName+" "+Application.ProductVersion; |  | ||||||
|         public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"+Cef.ChromiumVersion+" Safari/537.36"; |  | ||||||
|          |  | ||||||
|         public static void SetupCefArgs(IDictionary<string, string> args){ |  | ||||||
|             if (!Program.SystemConfig.HardwareAcceleration){ |  | ||||||
|                 args["disable-gpu"] = "1"; |  | ||||||
|                 args["disable-gpu-vsync"] = "1"; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!Program.UserConfig.EnableSmoothScrolling){ |  | ||||||
|                 args["disable-smooth-scrolling"] = "1"; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             args["disable-pdf-extension"] = "1"; |  | ||||||
|             args["disable-plugins-discovery"] = "1"; |  | ||||||
|             args["enable-system-flash"] = "0"; |  | ||||||
| 
 |  | ||||||
|             if (args.TryGetValue("js-flags", out string jsFlags)){ |  | ||||||
|                 args["js-flags"] = "--expose-gc "+jsFlags; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 args["js-flags"] = "--expose-gc"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static ChromiumWebBrowser AsControl(this IWebBrowser browserControl){ |  | ||||||
|             return (ChromiumWebBrowser)browserControl; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetupResourceHandler(this ChromiumWebBrowser browser, string url, IResourceHandler handler){ |  | ||||||
|             DefaultResourceHandlerFactory factory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory; |  | ||||||
| 
 |  | ||||||
|             if (handler == null){ |  | ||||||
|                 factory.UnregisterHandler(url); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 factory.RegisterHandler(url, handler); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetupResourceHandler(this ChromiumWebBrowser browser, ResourceLink resource){ |  | ||||||
|             browser.SetupResourceHandler(resource.Url, resource.Handler); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private const string TwitterTrackingUrl = "t.co"; |  | ||||||
| 
 |  | ||||||
|         public enum UrlCheckResult{ |  | ||||||
|             Invalid, Tracking, Fine |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static UrlCheckResult CheckUrl(string url){ |  | ||||||
|             if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){ |  | ||||||
|                 string scheme = uri.Scheme; |  | ||||||
| 
 |  | ||||||
|                 if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){ |  | ||||||
|                     return uri.Host == TwitterTrackingUrl ? UrlCheckResult.Tracking : UrlCheckResult.Fine; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return UrlCheckResult.Invalid; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void OpenExternalBrowser(string url){ |  | ||||||
|             if (string.IsNullOrWhiteSpace(url))return; |  | ||||||
| 
 |  | ||||||
|             switch(CheckUrl(url)){ |  | ||||||
|                 case UrlCheckResult.Fine: |  | ||||||
|                     if (FormGuide.CheckGuideUrl(url, out string hash)){ |  | ||||||
|                         FormGuide.Show(hash); |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         string browserPath = Program.UserConfig.BrowserPath; |  | ||||||
| 
 |  | ||||||
|                         if (browserPath == null || !File.Exists(browserPath)){ |  | ||||||
|                             WindowsUtils.OpenAssociatedProgram(url); |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             try{ |  | ||||||
|                                 using(Process.Start(browserPath, url)){} |  | ||||||
|                             }catch(Exception e){ |  | ||||||
|                                 Program.Reporter.HandleException("Error Opening Browser", "Could not open the browser.", true, e); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case UrlCheckResult.Tracking: |  | ||||||
|                     if (Program.UserConfig.IgnoreTrackingUrlWarning){ |  | ||||||
|                         goto case UrlCheckResult.Fine; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     using(FormMessage form = new FormMessage("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, MessageBoxIcon.Warning)){ |  | ||||||
|                         form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel | ControlType.Focused); |  | ||||||
|                         form.AddButton(FormMessage.Yes, DialogResult.Yes, ControlType.Accept); |  | ||||||
|                         form.AddButton("Always Visit", DialogResult.Ignore); |  | ||||||
| 
 |  | ||||||
|                         DialogResult result = form.ShowDialog(); |  | ||||||
| 
 |  | ||||||
|                         if (result == DialogResult.Ignore){ |  | ||||||
|                             Program.UserConfig.IgnoreTrackingUrlWarning = true; |  | ||||||
|                             Program.UserConfig.Save(); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (result == DialogResult.Ignore || result == DialogResult.Yes){ |  | ||||||
|                             goto case UrlCheckResult.Fine; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case UrlCheckResult.Invalid: |  | ||||||
|                     FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK); |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void OpenExternalSearch(string query){ |  | ||||||
|             if (string.IsNullOrWhiteSpace(query))return; |  | ||||||
|              |  | ||||||
|             string searchUrl = Program.UserConfig.SearchEngineUrl; |  | ||||||
|              |  | ||||||
|             if (string.IsNullOrEmpty(searchUrl)){ |  | ||||||
|                 if (FormMessage.Question("Search Options", "You have not configured a default search engine yet, would you like to do it now?", FormMessage.Yes, FormMessage.No)){ |  | ||||||
|                     bool wereSettingsOpen = FormManager.TryFind<FormSettings>() != null; |  | ||||||
| 
 |  | ||||||
|                     FormManager.TryFind<FormBrowser>()?.OpenSettings(); |  | ||||||
|                     if (wereSettingsOpen)return; |  | ||||||
| 
 |  | ||||||
|                     FormSettings settings = FormManager.TryFind<FormSettings>(); |  | ||||||
|                     if (settings == null)return; |  | ||||||
| 
 |  | ||||||
|                     settings.FormClosed += (sender, args) => { |  | ||||||
|                         if (args.CloseReason == CloseReason.UserClosing && Program.UserConfig.SearchEngineUrl != searchUrl){ |  | ||||||
|                             OpenExternalSearch(query); |  | ||||||
|                         } |  | ||||||
|                     }; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 OpenExternalBrowser(searchUrl+Uri.EscapeUriString(query)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GetFileNameFromUrl(string url){ |  | ||||||
|             string file = Path.GetFileName(new Uri(url).AbsolutePath); |  | ||||||
|             return string.IsNullOrEmpty(file) ? null : file; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GetErrorName(CefErrorCode code){ |  | ||||||
|             return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static WebClient CreateWebClient(){ |  | ||||||
|             WindowsUtils.EnsureTLS12(); |  | ||||||
| 
 |  | ||||||
|             WebClient client = new WebClient{ Proxy = null }; |  | ||||||
|             client.Headers[HttpRequestHeader.UserAgent] = UserAgentVanilla; |  | ||||||
|             return client; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){ |  | ||||||
|             WebClient client = CreateWebClient(); |  | ||||||
| 
 |  | ||||||
|             client.DownloadFileCompleted += (sender, args) => { |  | ||||||
|                 if (args.Cancelled){ |  | ||||||
|                     try{ |  | ||||||
|                         File.Delete(target); |  | ||||||
|                     }catch{ |  | ||||||
|                         // didn't want it deleted anyways |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else if (args.Error != null){ |  | ||||||
|                     onFailure?.Invoke(args.Error); |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     onSuccess?.Invoke(); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             client.DownloadFileAsync(new Uri(url), target); |  | ||||||
|             return client; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static int Scale(int baseValue, double scaleFactor){ |  | ||||||
|             return (int)Math.Round(baseValue*scaleFactor); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetZoomLevel(IBrowser browser, int percentage){ |  | ||||||
|             browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,58 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Globalization; |  | ||||||
| using System.Linq; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Utils{ |  | ||||||
|     static class LocaleUtils{ |  | ||||||
|         // https://cs.chromium.org/chromium/src/third_party/hunspell_dictionaries/ |  | ||||||
|         public static IEnumerable<Item> SpellCheckLanguages { get; } = new List<string>{ |  | ||||||
|             "af-ZA", "bg-BG", "ca-ES", "cs-CZ", "da-DK", "de-DE", |  | ||||||
|             "el-GR", "en-AU", "en-CA", "en-GB", "en-US", "es-ES", |  | ||||||
|             "et-EE", "fa-IR", "fo-FO", "fr-FR", "he-IL", "hi-IN", |  | ||||||
|             "hr-HR", "hu-HU", "id-ID", "it-IT", "ko"   , "lt-LT", |  | ||||||
|             "lv-LV", "nb-NO", "nl-NL", "pl-PL", "pt-BR", "pt-PT", |  | ||||||
|             "ro-RO", "ru-RU", "sk-SK", "sl-SI", "sq"   , "sr", |  | ||||||
|             "sv-SE", "ta-IN", "tg-TG", "tr"   , "uk-UA", "vi-VN" |  | ||||||
|         }.Select(code => { |  | ||||||
|             string lang = StringUtils.ExtractBefore(code, '-', 2); |  | ||||||
|             return lang == "en" || lang == "pt" ? new Item(code) : new Item(code, lang); |  | ||||||
|         }).OrderBy(code => code).ToList(); |  | ||||||
|          |  | ||||||
|         // TD.languages.getSupportedTranslationDestinationLanguages() except for "ht", "in", "iw" which are missing/duplicates |  | ||||||
|         public static IEnumerable<Item> TweetDeckTranslationLocales { get; } = new List<string>{ |  | ||||||
|             "bg", "ca", "zh-cn", "zh-tw", "cs", "da", "nl", |  | ||||||
|             "en", "et", "fi", "fr", "de", "el", "he", "hi", |  | ||||||
|             "hu", "id", "it", "ja", "ko", "lv", "lt", "no", |  | ||||||
|             "pl", "pt", "ro", "ru", "sk", "sl", "es", "sv", |  | ||||||
|             "th", "tr", "uk", "vi", "ar", "fa" |  | ||||||
|         }.Select(code => new Item(code)).OrderBy(code => code).ToList(); |  | ||||||
| 
 |  | ||||||
|         public sealed class Item : IComparable<Item>{ |  | ||||||
|             public string Code { get; } |  | ||||||
|             public CultureInfo Info { get; } |  | ||||||
| 
 |  | ||||||
|             public Item(string code, string alt = null){ |  | ||||||
|                 this.Code = code; |  | ||||||
|                 this.Info = CultureInfo.GetCultureInfo(alt ?? code); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public override bool Equals(object obj){ |  | ||||||
|                 return obj is Item other && Code.Equals(other.Code, StringComparison.OrdinalIgnoreCase); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public override int GetHashCode(){ |  | ||||||
|                 return Code.GetHashCode(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public override string ToString(){ |  | ||||||
|                 string capitalizedName = Info.TextInfo.ToTitleCase(Info.NativeName); |  | ||||||
|                 return Info.DisplayName == Info.NativeName ? capitalizedName : $"{capitalizedName}, {Info.DisplayName}"; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public int CompareTo(Item other){ |  | ||||||
|                 return string.Compare(Info.NativeName, other.Info.NativeName, false, CultureInfo.InvariantCulture); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,145 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Diagnostics.CodeAnalysis; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Utils{ |  | ||||||
|     [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] |  | ||||||
|     [SuppressMessage("ReSharper", "MemberCanBePrivate.Local")] |  | ||||||
|     static class NativeMethods{ |  | ||||||
|         public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF); |  | ||||||
|         public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1); |  | ||||||
| 
 |  | ||||||
|         public const int HWND_TOPMOST = -1; |  | ||||||
|         public const uint SWP_NOACTIVATE = 0x0010; |  | ||||||
|         public const int WS_DISABLED = 0x08000000; |  | ||||||
|         public const int GWL_STYLE = -16; |  | ||||||
| 
 |  | ||||||
|         public const int SB_HORZ = 0; |  | ||||||
| 
 |  | ||||||
|         public const int WM_MOUSE_LL = 14; |  | ||||||
|         public const int WM_MOUSEWHEEL = 0x020A; |  | ||||||
|         public const int WM_XBUTTONDOWN = 0x020B; |  | ||||||
|         public const int WM_XBUTTONUP = 0x020C; |  | ||||||
|         public const int WM_PARENTNOTIFY = 0x0210; |  | ||||||
|          |  | ||||||
|         [StructLayout(LayoutKind.Sequential)] |  | ||||||
|         private struct LASTINPUTINFO{ |  | ||||||
|             public static readonly uint Size = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO)); |  | ||||||
| 
 |  | ||||||
|             public uint cbSize; |  | ||||||
|             public uint dwTime; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         [StructLayout(LayoutKind.Sequential)] |  | ||||||
|         private struct POINT{ |  | ||||||
|             public int X; |  | ||||||
|             public int Y; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         [StructLayout(LayoutKind.Sequential)] |  | ||||||
|         private struct MSLLHOOKSTRUCT{ |  | ||||||
|             public POINT pt; |  | ||||||
|             public int mouseData; |  | ||||||
|             public int flags; |  | ||||||
|             public int time; |  | ||||||
|             public UIntPtr dwExtraInfo; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll", EntryPoint = "SetWindowPos")] |  | ||||||
|         private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         public static extern uint RegisterWindowMessage(string messageName); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         private static extern bool GetLastInputInfo(ref LASTINPUTINFO info); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         public static extern IntPtr GetDC(IntPtr hWnd); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); |  | ||||||
| 
 |  | ||||||
|         [DllImport("gdi32.dll")] |  | ||||||
|         [return: MarshalAs(UnmanagedType.Bool)] |  | ||||||
|         private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop); |  | ||||||
|          |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         [return: MarshalAs(UnmanagedType.Bool)] |  | ||||||
|         public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         public static extern bool UnhookWindowsHookEx(IntPtr idHook); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         private static extern int GetWindowLong(IntPtr hWnd, int nIndex); |  | ||||||
| 
 |  | ||||||
|         [DllImport("user32.dll")] |  | ||||||
|         private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); |  | ||||||
| 
 |  | ||||||
|         public static void SetFormPos(Form form, int hWndOrder, uint flags){ |  | ||||||
|             SetWindowPos(form.Handle.ToInt32(), hWndOrder, form.Left, form.Top, form.Width, form.Height, flags); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetFormDisabled(Form form, bool disabled){ |  | ||||||
|             if (disabled){ |  | ||||||
|                 SetWindowLong(form.Handle, GWL_STYLE, GetWindowLong(form.Handle, GWL_STYLE) | WS_DISABLED); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 SetWindowLong(form.Handle, GWL_STYLE, GetWindowLong(form.Handle, GWL_STYLE) & ~WS_DISABLED); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void BroadcastMessage(uint msg, uint wParam, int lParam){ |  | ||||||
|             PostMessage(HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static int GetMouseHookData(IntPtr ptr){ |  | ||||||
|             return Marshal.PtrToStructure<MSLLHOOKSTRUCT>(ptr).mouseData >> 16; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static int GetIdleSeconds(){ |  | ||||||
|             LASTINPUTINFO info = new LASTINPUTINFO{ |  | ||||||
|                 cbSize = LASTINPUTINFO.Size |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             if (!GetLastInputInfo(ref info)){ |  | ||||||
|                 return 0; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             uint ticks; |  | ||||||
| 
 |  | ||||||
|             unchecked{ |  | ||||||
|                 ticks = (uint)Environment.TickCount; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             int seconds = (int)Math.Floor(TimeSpan.FromMilliseconds(ticks-info.dwTime).TotalSeconds); |  | ||||||
|             return Math.Max(0, seconds); // ignore rollover after several weeks of uptime |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void RenderSourceIntoBitmap(IntPtr source, Bitmap target){ |  | ||||||
|             using(Graphics graphics = Graphics.FromImage(target)){ |  | ||||||
|                 IntPtr graphicsHandle = graphics.GetHdc(); |  | ||||||
| 
 |  | ||||||
|                 try{ |  | ||||||
|                     BitBlt(graphicsHandle, 0, 0, target.Width, target.Height, source, 0, 0, 0x00CC0020); |  | ||||||
|                 }finally{ |  | ||||||
|                     graphics.ReleaseHdc(graphicsHandle); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Text.RegularExpressions; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Utils{ |  | ||||||
|     static class StringUtils{ |  | ||||||
|         public static readonly string[] EmptyArray = new string[0]; |  | ||||||
| 
 |  | ||||||
|         public static string ExtractBefore(string str, char search, int startIndex = 0){ |  | ||||||
|             int index = str.IndexOf(search, startIndex); |  | ||||||
|             return index == -1 ? str : str.Substring(0, index); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static int[] ParseInts(string str, char separator){ |  | ||||||
|             return str.Split(new char[]{ separator }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray(); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public static string ConvertPascalCaseToScreamingSnakeCase(string str){ |  | ||||||
|             return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string ConvertRot13(string str){ |  | ||||||
|             return Regex.Replace(str, @"[a-zA-Z]", match => { |  | ||||||
|                 int code = match.Value[0]; |  | ||||||
|                 int start = code <= 90 ? 65 : 97; |  | ||||||
|                 return ((char)(start+(code-start+13)%26)).ToString(); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static int CountOccurrences(string source, string substring){ |  | ||||||
|             int count = 0, index = 0; |  | ||||||
| 
 |  | ||||||
|             while((index = source.IndexOf(substring, index)) != -1){ |  | ||||||
|                 index += substring.Length; |  | ||||||
|                 ++count; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return count; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,136 +0,0 @@ | |||||||
| using System; |  | ||||||
| using CefSharp; |  | ||||||
| using System.Drawing; |  | ||||||
| using System.IO; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Text.RegularExpressions; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using TweetDuck.Core.Other; |  | ||||||
| using TweetDuck.Data; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Utils{ |  | ||||||
|     static class TwitterUtils{ |  | ||||||
|         public const string TweetDeckURL = "https://tweetdeck.twitter.com"; |  | ||||||
| 
 |  | ||||||
|         public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153); |  | ||||||
|         public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important}';h.appendChild(e);},1)"; |  | ||||||
| 
 |  | ||||||
|         public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandler.FromByteArray(Properties.Resources.spinner, "image/apng")); |  | ||||||
|          |  | ||||||
|         private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$|search$|search-)([^/?]+)/?$", RegexOptions.Compiled), false); |  | ||||||
|         public static Regex RegexAccount => RegexAccountLazy.Value; |  | ||||||
| 
 |  | ||||||
|         public static readonly string[] DictionaryWords = { |  | ||||||
|             "tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD" |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         public static readonly string[] ValidImageExtensions = { |  | ||||||
|             ".jpg", ".jpeg", ".png", ".gif" |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         public enum ImageQuality{ |  | ||||||
|             Default, Orig |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool IsTweetDeckWebsite(IFrame frame){ |  | ||||||
|             return frame.Url.Contains("//tweetdeck.twitter.com/"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool IsTwitterWebsite(IFrame frame){ |  | ||||||
|             return frame.Url.Contains("//twitter.com/"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static string ExtractMediaBaseLink(string url){ |  | ||||||
|             int slash = url.LastIndexOf('/'); |  | ||||||
|             return slash == -1 ? url : StringUtils.ExtractBefore(url, ':', slash); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GetMediaLink(string url, ImageQuality quality){ |  | ||||||
|             if (quality == ImageQuality.Orig){ |  | ||||||
|                 string result = ExtractMediaBaseLink(url); |  | ||||||
| 
 |  | ||||||
|                 if (result != url || url.Contains("//pbs.twimg.com/media/")){ |  | ||||||
|                     result += ":orig"; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return result; |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 return url; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static string GetImageFileName(string url){ |  | ||||||
|             return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url)); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public static void DownloadImage(string url, string username, ImageQuality quality){ |  | ||||||
|             DownloadImages(new string[]{ url }, username, quality); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void DownloadImages(string[] urls, string username, ImageQuality quality){ |  | ||||||
|             if (urls.Length == 0){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             string firstImageLink = GetMediaLink(urls[0], quality); |  | ||||||
|             int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/')); |  | ||||||
| 
 |  | ||||||
|             string file = GetImageFileName(firstImageLink); |  | ||||||
|             string ext = Path.GetExtension(file); // includes dot |  | ||||||
| 
 |  | ||||||
|             string[] fileNameParts = qualityIndex == -1 ? new string[]{ |  | ||||||
|                 Path.ChangeExtension(file, null) |  | ||||||
|             } : new string[]{ |  | ||||||
|                 username, |  | ||||||
|                 Path.ChangeExtension(file, null), |  | ||||||
|                 firstImageLink.Substring(qualityIndex+1) |  | ||||||
|             }; |  | ||||||
|              |  | ||||||
|             using(SaveFileDialog dialog = new SaveFileDialog{ |  | ||||||
|                 AutoUpgradeEnabled = true, |  | ||||||
|                 OverwritePrompt = urls.Length == 1, |  | ||||||
|                 Title = "Save Image", |  | ||||||
|                 FileName = $"{string.Join(" ", fileNameParts.Where(part => !string.IsNullOrEmpty(part)))}{ext}", |  | ||||||
|                 Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}") |  | ||||||
|             }){ |  | ||||||
|                 if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                     void OnFailure(Exception ex){ |  | ||||||
|                         FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (urls.Length == 1){ |  | ||||||
|                         BrowserUtils.DownloadFileAsync(firstImageLink, dialog.FileName, null, OnFailure); |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         string pathBase = Path.ChangeExtension(dialog.FileName, null); |  | ||||||
|                         string pathExt = Path.GetExtension(dialog.FileName); |  | ||||||
| 
 |  | ||||||
|                         for(int index = 0; index < urls.Length; index++){ |  | ||||||
|                             BrowserUtils.DownloadFileAsync(GetMediaLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void DownloadVideo(string url, string username){ |  | ||||||
|             string filename = BrowserUtils.GetFileNameFromUrl(url); |  | ||||||
|             string ext = Path.GetExtension(filename); |  | ||||||
| 
 |  | ||||||
|             using(SaveFileDialog dialog = new SaveFileDialog{ |  | ||||||
|                 AutoUpgradeEnabled = true, |  | ||||||
|                 OverwritePrompt = true, |  | ||||||
|                 Title = "Save Video", |  | ||||||
|                 FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {filename}", |  | ||||||
|                 Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}") |  | ||||||
|             }){ |  | ||||||
|                 if (dialog.ShowDialog() == DialogResult.OK){ |  | ||||||
|                     BrowserUtils.DownloadFileAsync(url, dialog.FileName, null, ex => { |  | ||||||
|                         FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK); |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,205 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.ComponentModel; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.IO; |  | ||||||
| using System.Net; |  | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| using System.Text.RegularExpressions; |  | ||||||
| using System.Threading; |  | ||||||
| using System.Windows.Forms; |  | ||||||
| using Microsoft.Win32; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Core.Utils{ |  | ||||||
|     static class WindowsUtils{ |  | ||||||
|         private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false); |  | ||||||
|         private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false); |  | ||||||
| 
 |  | ||||||
|         public static int CurrentProcessID { get; } |  | ||||||
|         public static bool ShouldAvoidToolWindow { get; } |  | ||||||
|          |  | ||||||
|         private static bool HasMicrosoftBeenBroughtTo2008Yet; |  | ||||||
| 
 |  | ||||||
|         static WindowsUtils(){ |  | ||||||
|             using(Process me = Process.GetCurrentProcess()){ |  | ||||||
|                 CurrentProcessID = me.Id; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Version ver = Environment.OSVersion.Version; |  | ||||||
|             ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10 |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public static void EnsureTLS12(){ |  | ||||||
|             if (!HasMicrosoftBeenBroughtTo2008Yet){ |  | ||||||
|                 ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; |  | ||||||
|                 ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11); |  | ||||||
|                 HasMicrosoftBeenBroughtTo2008Yet = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void CreateDirectoryForFile(string file){ |  | ||||||
|             string dir = Path.GetDirectoryName(file); |  | ||||||
| 
 |  | ||||||
|             if (dir == null){ |  | ||||||
|                 throw new ArgumentException("Invalid file path: "+file); |  | ||||||
|             } |  | ||||||
|             else if (dir.Length > 0){ |  | ||||||
|                 Directory.CreateDirectory(dir); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool CheckFolderWritePermission(string path){ |  | ||||||
|             string testFile = Path.Combine(path, ".test"); |  | ||||||
| 
 |  | ||||||
|             try{ |  | ||||||
|                 Directory.CreateDirectory(path); |  | ||||||
| 
 |  | ||||||
|                 using(File.Create(testFile)){} |  | ||||||
|                 File.Delete(testFile); |  | ||||||
|                 return true; |  | ||||||
|             }catch{ |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){ |  | ||||||
|             try{ |  | ||||||
|                 using(Process.Start(new ProcessStartInfo{ |  | ||||||
|                     FileName = file, |  | ||||||
|                     Arguments = arguments, |  | ||||||
|                     Verb = runElevated ? "runas" : string.Empty, |  | ||||||
|                     ErrorDialog = true |  | ||||||
|                 })){ |  | ||||||
|                     return true; |  | ||||||
|                 } |  | ||||||
|             }catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user |  | ||||||
|                 return false; |  | ||||||
|             }catch(Exception e){ |  | ||||||
|                 Program.Reporter.HandleException("Error Opening Program", "Could not open the associated program for "+file, true, e); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){ |  | ||||||
|             for(int waited = 0; waited < timeoutMillis; waited += timeStepMillis){ |  | ||||||
|                 if (test()){ |  | ||||||
|                     return true; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 Thread.Sleep(timeStepMillis); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void TryDeleteFolderWhenAble(string path, int timeout){ |  | ||||||
|             new Thread(() => { |  | ||||||
|                 TrySleepUntil(() => { |  | ||||||
|                     try{ |  | ||||||
|                         Directory.Delete(path, true); |  | ||||||
|                         return true; |  | ||||||
|                     }catch(DirectoryNotFoundException){ |  | ||||||
|                         return true; |  | ||||||
|                     }catch{ |  | ||||||
|                         return false; |  | ||||||
|                     } |  | ||||||
|                 }, timeout, 500); |  | ||||||
|             }).Start(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void ClipboardStripHtmlStyles(){ |  | ||||||
|             if (!Clipboard.ContainsText(TextDataFormat.Html) || !Clipboard.ContainsText(TextDataFormat.UnicodeText)){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             string originalText = Clipboard.GetText(TextDataFormat.UnicodeText); |  | ||||||
|             string originalHtml = Clipboard.GetText(TextDataFormat.Html); |  | ||||||
| 
 |  | ||||||
|             string updatedHtml = RegexStripHtmlStyles.Value.Replace(originalHtml, string.Empty); |  | ||||||
| 
 |  | ||||||
|             int removed = originalHtml.Length-updatedHtml.Length; |  | ||||||
|             updatedHtml = RegexOffsetClipboardHtml.Value.Replace(updatedHtml, match => (int.Parse(match.Value)-removed).ToString().PadLeft(match.Value.Length, '0')); |  | ||||||
|              |  | ||||||
|             DataObject obj = new DataObject(); |  | ||||||
|             obj.SetText(originalText, TextDataFormat.UnicodeText); |  | ||||||
|             obj.SetText(updatedHtml, TextDataFormat.Html); |  | ||||||
|             SetClipboardData(obj); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void SetClipboard(string text, TextDataFormat format){ |  | ||||||
|             if (string.IsNullOrEmpty(text)){ |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             DataObject obj = new DataObject(); |  | ||||||
|             obj.SetText(text, format); |  | ||||||
|             SetClipboardData(obj); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static void SetClipboardData(DataObject obj){ |  | ||||||
|             try{ |  | ||||||
|                 Clipboard.SetDataObject(obj); |  | ||||||
|             }catch(ExternalException e){ |  | ||||||
|                 Program.Reporter.HandleException("Clipboard Error", "TweetDuck could not access the clipboard as it is currently used by another process.", true, e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static IEnumerable<Browser> FindInstalledBrowsers(){ |  | ||||||
|             IEnumerable<Browser> ReadBrowsersFromKey(RegistryHive hive){ |  | ||||||
|                 using(RegistryKey root = RegistryKey.OpenBaseKey(hive, RegistryView.Default)) |  | ||||||
|                 using(RegistryKey browserList = root.OpenSubKey(@"SOFTWARE\Clients\StartMenuInternet", false)){ |  | ||||||
|                     if (browserList == null){ |  | ||||||
|                         yield break; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     foreach(string sub in browserList.GetSubKeyNames()){ |  | ||||||
|                         using(RegistryKey browserKey = browserList.OpenSubKey(sub, false)) |  | ||||||
|                         using(RegistryKey shellKey = browserKey?.OpenSubKey(@"shell\open\command")){ |  | ||||||
|                             if (shellKey == null){ |  | ||||||
|                                 continue; |  | ||||||
|                             } |  | ||||||
| 
 |  | ||||||
|                             string browserName = browserKey.GetValue(null) as string; |  | ||||||
|                             string browserPath = shellKey.GetValue(null) as string; |  | ||||||
| 
 |  | ||||||
|                             if (string.IsNullOrEmpty(browserName) || string.IsNullOrEmpty(browserPath)){ |  | ||||||
|                                 continue; |  | ||||||
|                             } |  | ||||||
| 
 |  | ||||||
|                             if (browserPath[0] == '"' && browserPath[browserPath.Length-1] == '"'){ |  | ||||||
|                                 browserPath = browserPath.Substring(1, browserPath.Length-2); |  | ||||||
|                             } |  | ||||||
| 
 |  | ||||||
|                             yield return new Browser(browserName, browserPath); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             HashSet<Browser> browsers = new HashSet<Browser>(); |  | ||||||
|              |  | ||||||
|             try{ |  | ||||||
|                 browsers.UnionWith(ReadBrowsersFromKey(RegistryHive.CurrentUser)); |  | ||||||
|                 browsers.UnionWith(ReadBrowsersFromKey(RegistryHive.LocalMachine)); |  | ||||||
|             }catch{ |  | ||||||
|                 // oops I guess |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return browsers; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public sealed class Browser{ |  | ||||||
|             public string Name { get; } |  | ||||||
|             public string Path { get; } |  | ||||||
| 
 |  | ||||||
|             public Browser(string name, string path){ |  | ||||||
|                 this.Name = name; |  | ||||||
|                 this.Path = path; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public override int GetHashCode() => Name.GetHashCode(); |  | ||||||
|             public override bool Equals(object obj) => obj is Browser other && Name == other.Name; |  | ||||||
|             public override string ToString() => Name; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,130 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.IO; |  | ||||||
| using System.Text; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data{ |  | ||||||
|     sealed class CombinedFileStream : IDisposable{ |  | ||||||
|         public const char KeySeparator = '|'; |  | ||||||
| 
 |  | ||||||
|         private readonly Stream stream; |  | ||||||
| 
 |  | ||||||
|         public CombinedFileStream(Stream stream){ |  | ||||||
|             this.stream = stream; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void WriteFile(string[] identifier, string path){ |  | ||||||
|             WriteFile(string.Join(KeySeparator.ToString(), identifier), path); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void WriteFile(string identifier, string path){ |  | ||||||
|             byte[] name = Encoding.UTF8.GetBytes(identifier); |  | ||||||
| 
 |  | ||||||
|             if (name.Length > 255){ |  | ||||||
|                 throw new ArgumentOutOfRangeException("Identifier cannot be 256 or more characters long: "+identifier); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             byte[] contents; |  | ||||||
| 
 |  | ||||||
|             using(FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ |  | ||||||
|                 int index = 0; |  | ||||||
|                 int left = (int)fileStream.Length; |  | ||||||
| 
 |  | ||||||
|                 contents = new byte[left]; |  | ||||||
| 
 |  | ||||||
|                 while(left > 0){ |  | ||||||
|                     int read = fileStream.Read(contents, index, left); |  | ||||||
|                     index += read; |  | ||||||
|                     left -= read; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             stream.WriteByte((byte)name.Length); |  | ||||||
|             stream.Write(name, 0, name.Length); |  | ||||||
|             stream.Write(BitConverter.GetBytes(contents.Length), 0, 4); |  | ||||||
|             stream.Write(contents, 0, contents.Length); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public Entry ReadFile(){ |  | ||||||
|             int nameLength = stream.ReadByte(); |  | ||||||
| 
 |  | ||||||
|             if (nameLength == -1){ |  | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             byte[] name = new byte[nameLength]; |  | ||||||
|             stream.Read(name, 0, nameLength); |  | ||||||
| 
 |  | ||||||
|             byte[] contentLength = new byte[4]; |  | ||||||
|             stream.Read(contentLength, 0, 4); |  | ||||||
| 
 |  | ||||||
|             byte[] contents = new byte[BitConverter.ToInt32(contentLength, 0)]; |  | ||||||
|             stream.Read(contents, 0, contents.Length); |  | ||||||
| 
 |  | ||||||
|             return new Entry(Encoding.UTF8.GetString(name), contents); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public string SkipFile(){ |  | ||||||
|             int nameLength = stream.ReadByte(); |  | ||||||
| 
 |  | ||||||
|             if (nameLength == -1){ |  | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             byte[] name = new byte[nameLength]; |  | ||||||
|             stream.Read(name, 0, nameLength); |  | ||||||
| 
 |  | ||||||
|             byte[] contentLength = new byte[4]; |  | ||||||
|             stream.Read(contentLength, 0, 4); |  | ||||||
| 
 |  | ||||||
|             stream.Position += BitConverter.ToInt32(contentLength, 0); |  | ||||||
| 
 |  | ||||||
|             string keyName = Encoding.UTF8.GetString(name); |  | ||||||
|             return StringUtils.ExtractBefore(keyName, KeySeparator); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Flush(){ |  | ||||||
|             stream.Flush(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void IDisposable.Dispose(){ |  | ||||||
|             stream.Dispose(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public sealed class Entry{ |  | ||||||
|             public string Identifier { get; } |  | ||||||
| 
 |  | ||||||
|             public string KeyName{ |  | ||||||
|                 get{ |  | ||||||
|                     return StringUtils.ExtractBefore(Identifier, KeySeparator); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public string[] KeyValue{ |  | ||||||
|                 get{ |  | ||||||
|                     int index = Identifier.IndexOf(KeySeparator); |  | ||||||
|                     return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index+1).Split(KeySeparator); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             private readonly byte[] contents; |  | ||||||
| 
 |  | ||||||
|             public Entry(string identifier, byte[] contents){ |  | ||||||
|                 this.Identifier = identifier; |  | ||||||
|                 this.contents = contents; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void WriteToFile(string path){ |  | ||||||
|                 File.WriteAllBytes(path, contents); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public void WriteToFile(string path, bool createDirectory){ |  | ||||||
|                 if (createDirectory){ |  | ||||||
|                     WindowsUtils.CreateDirectoryForFile(path); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 File.WriteAllBytes(path, contents); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,137 +0,0 @@ | |||||||
| using System.Collections.Generic; |  | ||||||
| using System.Text; |  | ||||||
| using System.Text.RegularExpressions; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data{ |  | ||||||
|     sealed class CommandLineArgs{ |  | ||||||
|         public static CommandLineArgs FromStringArray(char entryChar, string[] array){ |  | ||||||
|             CommandLineArgs args = new CommandLineArgs(); |  | ||||||
|             ReadStringArray(entryChar, array, args); |  | ||||||
|             return args; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static void ReadStringArray(char entryChar, string[] array, CommandLineArgs targetArgs){ |  | ||||||
|             for(int index = 0; index < array.Length; index++){ |  | ||||||
|                 string entry = array[index]; |  | ||||||
| 
 |  | ||||||
|                 if (entry.Length > 0 && entry[0] == entryChar){ |  | ||||||
|                     if (index < array.Length-1){ |  | ||||||
|                         string potentialValue = array[index+1]; |  | ||||||
| 
 |  | ||||||
|                         if (potentialValue.Length > 0 && potentialValue[0] == entryChar){ |  | ||||||
|                             targetArgs.AddFlag(entry); |  | ||||||
|                         } |  | ||||||
|                         else{ |  | ||||||
|                             targetArgs.SetValue(entry, potentialValue); |  | ||||||
|                             ++index; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         targetArgs.AddFlag(entry); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static CommandLineArgs ReadCefArguments(string argumentString){ |  | ||||||
|             CommandLineArgs args = new CommandLineArgs(); |  | ||||||
| 
 |  | ||||||
|             if (string.IsNullOrWhiteSpace(argumentString)){ |  | ||||||
|                 return args; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             foreach(Match match in Regex.Matches(argumentString, @"([^=\s]+(?:=(?:\S*""[^""]*?""\S*|\S*))?)")){ |  | ||||||
|                 string matchValue = match.Value; |  | ||||||
| 
 |  | ||||||
|                 int indexEquals = matchValue.IndexOf('='); |  | ||||||
|                 string key, value; |  | ||||||
| 
 |  | ||||||
|                 if (indexEquals == -1){ |  | ||||||
|                     key = matchValue.TrimStart('-'); |  | ||||||
|                     value = "1"; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     key = matchValue.Substring(0, indexEquals).TrimStart('-'); |  | ||||||
|                     value = matchValue.Substring(indexEquals+1).Trim('"'); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (key.Length != 0){ |  | ||||||
|                     args.SetValue(key, value); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return args; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly HashSet<string> flags = new HashSet<string>(); |  | ||||||
|         private readonly Dictionary<string, string> values = new Dictionary<string, string>(); |  | ||||||
| 
 |  | ||||||
|         public int Count => flags.Count+values.Count; |  | ||||||
| 
 |  | ||||||
|         public void AddFlag(string flag){ |  | ||||||
|             flags.Add(flag.ToLower()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool HasFlag(string flag){ |  | ||||||
|             return flags.Contains(flag.ToLower()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void RemoveFlag(string flag){ |  | ||||||
|             flags.Remove(flag.ToLower()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void SetValue(string key, string value){ |  | ||||||
|             values[key.ToLower()] = value; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool HasValue(string key){ |  | ||||||
|             return values.ContainsKey(key.ToLower()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public string GetValue(string key, string defaultValue){ |  | ||||||
|             return values.TryGetValue(key.ToLower(), out string val) ? val : defaultValue; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void RemoveValue(string key){ |  | ||||||
|             values.Remove(key.ToLower()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public CommandLineArgs Clone(){ |  | ||||||
|             CommandLineArgs copy = new CommandLineArgs(); |  | ||||||
| 
 |  | ||||||
|             foreach(string flag in flags){ |  | ||||||
|                 copy.AddFlag(flag); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             foreach(KeyValuePair<string, string> kvp in values){ |  | ||||||
|                 copy.SetValue(kvp.Key, kvp.Value); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return copy; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ToDictionary(IDictionary<string, string> target){ |  | ||||||
|             foreach(string flag in flags){ |  | ||||||
|                 target[flag] = "1"; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             foreach(KeyValuePair<string, string> kvp in values){ |  | ||||||
|                 target[kvp.Key] = kvp.Value; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public override string ToString(){ |  | ||||||
|             StringBuilder build = new StringBuilder(); |  | ||||||
| 
 |  | ||||||
|             foreach(string flag in flags){ |  | ||||||
|                 build.Append(flag).Append(' '); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             foreach(KeyValuePair<string, string> kvp in values){ |  | ||||||
|                 build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" "); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return build.Length == 0 ? string.Empty : build.Remove(build.Length-1, 1).ToString(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| using System; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data{ |  | ||||||
|     sealed class InjectedHTML{ |  | ||||||
|         public enum Position{ |  | ||||||
|             Before, After |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private readonly Position position; |  | ||||||
|         private readonly string search; |  | ||||||
|         private readonly string html; |  | ||||||
| 
 |  | ||||||
|         public InjectedHTML(Position position, string search, string html){ |  | ||||||
|             this.position = position; |  | ||||||
|             this.search = search; |  | ||||||
|             this.html = html; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public string Inject(string targetHTML){ |  | ||||||
|             int index = targetHTML.IndexOf(search, StringComparison.Ordinal); |  | ||||||
| 
 |  | ||||||
|             if (index == -1){ |  | ||||||
|                 return targetHTML; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             int cutIndex; |  | ||||||
| 
 |  | ||||||
|             switch(position){ |  | ||||||
|                 case Position.Before: cutIndex = index; break; |  | ||||||
|                 case Position.After: cutIndex = index+search.Length; break; |  | ||||||
|                 default: return targetHTML; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return targetHTML.Insert(cutIndex, html); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| using CefSharp; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data{ |  | ||||||
|     sealed class ResourceLink{ |  | ||||||
|         public string Url { get; } |  | ||||||
|         public IResourceHandler Handler { get; } |  | ||||||
| 
 |  | ||||||
|         public ResourceLink(string url, IResourceHandler handler){ |  | ||||||
|             this.Url = url; |  | ||||||
|             this.Handler = handler; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,36 +0,0 @@ | |||||||
| using System; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data{ |  | ||||||
|     sealed class Result<T>{ |  | ||||||
|         public bool HasValue => exception == null; |  | ||||||
| 
 |  | ||||||
|         public T Value => HasValue ? value : throw new InvalidOperationException("Requested value from a failed result."); |  | ||||||
|         public Exception Exception => exception ?? throw new InvalidOperationException("Requested exception from a successful result."); |  | ||||||
| 
 |  | ||||||
|         private readonly T value; |  | ||||||
|         private readonly Exception exception; |  | ||||||
| 
 |  | ||||||
|         public Result(T value){ |  | ||||||
|             this.value = value; |  | ||||||
|             this.exception = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public Result(Exception exception){ |  | ||||||
|             this.value = default(T); |  | ||||||
|             this.exception = exception ?? throw new ArgumentNullException(nameof(exception)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Handle(Action<T> onSuccess, Action<Exception> onException){ |  | ||||||
|             if (HasValue){ |  | ||||||
|                 onSuccess(value); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 onException(exception); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public Result<R> Select<R>(Func<T, R> map){ |  | ||||||
|             return HasValue ? new Result<R>(map(value)) : new Result<R>(exception); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,204 +0,0 @@ | |||||||
| using System; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.IO; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Reflection; |  | ||||||
| using System.Runtime.Serialization; |  | ||||||
| using System.Text; |  | ||||||
| using TweetDuck.Core.Utils; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data.Serialization{ |  | ||||||
|     sealed class FileSerializer<T>{ |  | ||||||
|         private const string NewLineReal = "\r\n"; |  | ||||||
|         private const string NewLineCustom = "\r~\n"; |  | ||||||
| 
 |  | ||||||
|         private static string EscapeLine(string input) => input.Replace("\\", "\\\\").Replace(Environment.NewLine, "\\\r\n"); |  | ||||||
|         private static string UnescapeLine(string input) => input.Replace(NewLineCustom, Environment.NewLine); |  | ||||||
| 
 |  | ||||||
|         private static string UnescapeStream(StreamReader reader){ |  | ||||||
|             string data = reader.ReadToEnd(); |  | ||||||
| 
 |  | ||||||
|             StringBuilder build = new StringBuilder(data.Length); |  | ||||||
|             int index = 0; |  | ||||||
| 
 |  | ||||||
|             while(true){ |  | ||||||
|                 int nextIndex = data.IndexOf('\\', index); |  | ||||||
| 
 |  | ||||||
|                 if (nextIndex == -1 || nextIndex+1 >= data.Length){ |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     build.Append(data.Substring(index, nextIndex-index)); |  | ||||||
| 
 |  | ||||||
|                     char next = data[nextIndex+1]; |  | ||||||
| 
 |  | ||||||
|                     if (next == '\\'){ // convert double backslash to single backslash |  | ||||||
|                         build.Append('\\'); |  | ||||||
|                         index = nextIndex+2; |  | ||||||
|                     } |  | ||||||
|                     else if (next == '\r' && nextIndex+2 < data.Length && data[nextIndex+2] == '\n'){ // convert backslash followed by CRLF to custom new line |  | ||||||
|                         build.Append(NewLineCustom); |  | ||||||
|                         index = nextIndex+3; |  | ||||||
|                     } |  | ||||||
|                     else{ // single backslash |  | ||||||
|                         build.Append('\\'); |  | ||||||
|                         index = nextIndex+1; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             return build.Append(data.Substring(index)).ToString(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter(); |  | ||||||
|          |  | ||||||
|         private readonly Dictionary<string, PropertyInfo> props; |  | ||||||
|         private readonly Dictionary<Type, ITypeConverter> converters; |  | ||||||
| 
 |  | ||||||
|         public FileSerializer(){ |  | ||||||
|             this.props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.CanWrite).ToDictionary(prop => prop.Name); |  | ||||||
|             this.converters = new Dictionary<Type, ITypeConverter>(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void RegisterTypeConverter(Type type, ITypeConverter converter){ |  | ||||||
|             converters[type] = converter; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Write(string file, T obj){ |  | ||||||
|             WindowsUtils.CreateDirectoryForFile(file); |  | ||||||
| 
 |  | ||||||
|             using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){ |  | ||||||
|                 foreach(KeyValuePair<string, PropertyInfo> prop in props){ |  | ||||||
|                     Type type = prop.Value.PropertyType; |  | ||||||
|                     object value = prop.Value.GetValue(obj); |  | ||||||
|                      |  | ||||||
|                     if (!converters.TryGetValue(type, out ITypeConverter serializer)){ |  | ||||||
|                         serializer = BasicSerializerObj; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (serializer.TryWriteType(type, value, out string converted)){ |  | ||||||
|                         if (converted != null){ |  | ||||||
|                             writer.Write($"{prop.Key} {EscapeLine(converted)}"); |  | ||||||
|                             writer.Write(NewLineReal); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         throw new SerializationException($"Invalid serialization type, conversion failed for: {type}"); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Read(string file, T obj){ |  | ||||||
|             string contents; |  | ||||||
| 
 |  | ||||||
|             using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){ |  | ||||||
|                 contents = UnescapeStream(reader); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (string.IsNullOrWhiteSpace(contents)){ |  | ||||||
|                 throw new FormatException("File is empty."); |  | ||||||
|             } |  | ||||||
|             else if (contents[0] <= (char)1){ |  | ||||||
|                 throw new FormatException("Input appears to be a binary file."); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             int currentPos = 0; |  | ||||||
|                  |  | ||||||
|             do{ |  | ||||||
|                 string line; |  | ||||||
|                 int nextPos = contents.IndexOf(NewLineReal, currentPos); |  | ||||||
| 
 |  | ||||||
|                 if (nextPos == -1){ |  | ||||||
|                     line = contents.Substring(currentPos); |  | ||||||
|                     currentPos = -1; |  | ||||||
| 
 |  | ||||||
|                     if (string.IsNullOrEmpty(line)){ |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else{ |  | ||||||
|                     line = contents.Substring(currentPos, nextPos-currentPos); |  | ||||||
|                     currentPos = nextPos+NewLineReal.Length; |  | ||||||
|                 } |  | ||||||
|                      |  | ||||||
|                 int space = line.IndexOf(' '); |  | ||||||
| 
 |  | ||||||
|                 if (space == -1){ |  | ||||||
|                     throw new SerializationException($"Invalid file format, missing separator: {line}"); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 string property = line.Substring(0, space); |  | ||||||
|                 string value = UnescapeLine(line.Substring(space+1)); |  | ||||||
| 
 |  | ||||||
|                 if (props.TryGetValue(property, out PropertyInfo info)){ |  | ||||||
|                     if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)){ |  | ||||||
|                         serializer = BasicSerializerObj; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (serializer.TryReadType(info.PropertyType, value, out object converted)){ |  | ||||||
|                         info.SetValue(obj, converted); |  | ||||||
|                     } |  | ||||||
|                     else{ |  | ||||||
|                         throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})"); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             }while(currentPos != -1); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void ReadIfExists(string file, T obj){ |  | ||||||
|             try{ |  | ||||||
|                 Read(file, obj); |  | ||||||
|             }catch(FileNotFoundException){ |  | ||||||
|             }catch(DirectoryNotFoundException){} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private sealed class BasicTypeConverter : ITypeConverter{ |  | ||||||
|             bool ITypeConverter.TryWriteType(Type type, object value, out string converted){ |  | ||||||
|                 switch(Type.GetTypeCode(type)){ |  | ||||||
|                     case TypeCode.Boolean: |  | ||||||
|                         converted = value.ToString(); |  | ||||||
|                         return true; |  | ||||||
| 
 |  | ||||||
|                     case TypeCode.Int32: |  | ||||||
|                         converted = ((int)value).ToString(); // cast required for enums |  | ||||||
|                         return true; |  | ||||||
| 
 |  | ||||||
|                     case TypeCode.String: |  | ||||||
|                         converted = value?.ToString(); |  | ||||||
|                         return true; |  | ||||||
| 
 |  | ||||||
|                     default: |  | ||||||
|                         converted = null; |  | ||||||
|                         return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             bool ITypeConverter.TryReadType(Type type, string value, out object converted){ |  | ||||||
|                 switch(Type.GetTypeCode(type)){ |  | ||||||
|                     case TypeCode.Boolean: |  | ||||||
|                         if (bool.TryParse(value, out bool b)){ |  | ||||||
|                             converted = b; |  | ||||||
|                             return true; |  | ||||||
|                         } |  | ||||||
|                         else goto default; |  | ||||||
| 
 |  | ||||||
|                     case TypeCode.Int32: |  | ||||||
|                         if (int.TryParse(value, out int i)){ |  | ||||||
|                             converted = i; |  | ||||||
|                             return true; |  | ||||||
|                         } |  | ||||||
|                         else goto default; |  | ||||||
| 
 |  | ||||||
|                     case TypeCode.String: |  | ||||||
|                         converted = value; |  | ||||||
|                         return true; |  | ||||||
| 
 |  | ||||||
|                     default: |  | ||||||
|                         converted = null; |  | ||||||
|                         return false; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| using System; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data.Serialization{ |  | ||||||
|     interface ITypeConverter{ |  | ||||||
|         bool TryWriteType(Type type, object value, out string converted); |  | ||||||
|         bool TryReadType(Type type, string value, out object converted); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| using System; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data.Serialization{ |  | ||||||
|     sealed class SingleTypeConverter<T> : ITypeConverter{ |  | ||||||
|         public Func<T, string> ConvertToString { get; set; } |  | ||||||
|         public Func<string, T> ConvertToObject { get; set; } |  | ||||||
| 
 |  | ||||||
|         bool ITypeConverter.TryWriteType(Type type, object value, out string converted){ |  | ||||||
|             try{ |  | ||||||
|                 converted = ConvertToString((T)value); |  | ||||||
|                 return true; |  | ||||||
|             }catch{ |  | ||||||
|                 converted = null; |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool ITypeConverter.TryReadType(Type type, string value, out object converted){ |  | ||||||
|             try{ |  | ||||||
|                 converted = ConvertToObject(value); |  | ||||||
|                 return true; |  | ||||||
|             }catch{ |  | ||||||
|                 converted = null; |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,101 +0,0 @@ | |||||||
| using System.Collections.Generic; |  | ||||||
| using System.Linq; |  | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Data{ |  | ||||||
|     sealed class TwoKeyDictionary<K1, K2, V>{ |  | ||||||
|         private readonly Dictionary<K1, Dictionary<K2, V>> dict; |  | ||||||
|         private readonly int innerCapacity; |  | ||||||
| 
 |  | ||||||
|         public TwoKeyDictionary() : this(16, 16){} |  | ||||||
| 
 |  | ||||||
|         public TwoKeyDictionary(int outerCapacity, int innerCapacity){ |  | ||||||
|             this.dict = new Dictionary<K1, Dictionary<K2, V>>(outerCapacity); |  | ||||||
|             this.innerCapacity = innerCapacity; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Properties |  | ||||||
| 
 |  | ||||||
|         public V this[K1 outerKey, K2 innerKey]{ |  | ||||||
|             get{ // throws on missing key |  | ||||||
|                 return dict[outerKey][innerKey]; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             set{ |  | ||||||
|                 if (!dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict)){ |  | ||||||
|                     dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity)); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 innerDict[innerKey] = value; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public IEnumerable<V> InnerValues{ |  | ||||||
|             get{ |  | ||||||
|                 foreach(Dictionary<K2, V> innerDict in dict.Values){ |  | ||||||
|                     foreach(V value in innerDict.Values){ |  | ||||||
|                         yield return value; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Members |  | ||||||
| 
 |  | ||||||
|         public void Add(K1 outerKey, K2 innerKey, V value){ // throws on duplicate |  | ||||||
|             if (!dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict)){ |  | ||||||
|                 dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             innerDict.Add(innerKey, value); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public void Clear(){ |  | ||||||
|             dict.Clear(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void Clear(K1 outerKey){ // throws on missing key, but keeps the key unlike Remove(K1) |  | ||||||
|             dict[outerKey].Clear(); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public bool Contains(K1 outerKey){ |  | ||||||
|             return dict.ContainsKey(outerKey); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public bool Contains(K1 outerKey, K2 innerKey){ |  | ||||||
|             return dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict) && innerDict.ContainsKey(innerKey); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public int Count(){ |  | ||||||
|             return dict.Values.Sum(d => d.Count); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public int Count(K1 outerKey){ // throws on missing key |  | ||||||
|             return dict[outerKey].Count; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool Remove(K1 outerKey){ |  | ||||||
|             return dict.Remove(outerKey); |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         public bool Remove(K1 outerKey, K2 innerKey){ |  | ||||||
|             if (dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict) && innerDict.Remove(innerKey)){ |  | ||||||
|                 if (innerDict.Count == 0) { |  | ||||||
|                     dict.Remove(outerKey); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             else return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public bool TryGetValue(K1 outerKey, K2 innerKey, out V value){ |  | ||||||
|             if (dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict)){ |  | ||||||
|                 return innerDict.TryGetValue(innerKey, out value); |  | ||||||
|             } |  | ||||||
|             else{ |  | ||||||
|                 value = default(V); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user