mirror of
				https://github.com/chylex/TweetDuck.git
				synced 2025-11-03 16:40:14 +01:00 
			
		
		
		
	Compare commits
	
		
			538 Commits
		
	
	
		
			1.13.4.1
			...
			c5a42e74d9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						c5a42e74d9
	
				 | 
					
					
						|||
| 
						
						
							
						
						23ebd58da0
	
				 | 
					
					
						|||
| 
						
						
							
						
						9a6394d079
	
				 | 
					
					
						|||
| 
						
						
							
						
						88a55c8795
	
				 | 
					
					
						|||
| 
						
						
							
						
						d406866a02
	
				 | 
					
					
						|||
| 
						
						
							
						
						c2094bd2c0
	
				 | 
					
					
						|||
| 
						
						
							
						
						065b5a751b
	
				 | 
					
					
						|||
| 
						
						
							
						
						0dc454e61f
	
				 | 
					
					
						|||
| 
						
						
							
						
						416a43f0b1
	
				 | 
					
					
						|||
| 
						
						
							
						
						7a338076db
	
				 | 
					
					
						|||
| 
						
						
							
						
						54bf1c2012
	
				 | 
					
					
						|||
| 
						
						
							
						
						32681259f6
	
				 | 
					
					
						|||
| 
						
						
							
						
						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 | |||
| d7f1df4995 | |||
| 3cb0f90706 | |||
| a3e9b15a8a | |||
| 00bfa68a57 | |||
| c311e24f08 | |||
| 1cdd4e4455 | |||
| 8078c0081a | |||
| a867e1fc40 | |||
| 61da36ac1c | |||
| 720ca2a018 | |||
| b39c593552 | |||
| c808952a45 | |||
| b468d7a766 | |||
| 28578f60be | |||
| 92a39e2527 | |||
| 1bce5e4342 | |||
| 68f586e104 | |||
| d27a66202e | |||
| 07de2f450c | |||
| 3c03726634 | |||
| 6fb2643063 | |||
| 5eef6c8196 | |||
| 829c332e13 | |||
| 47eec14bca | |||
| e7ee1d6be7 | |||
| e41b5e5ff7 | |||
| ba1bacd08c | |||
| 1029ea5840 | |||
| 339eaf0195 | |||
| aa1e1549d8 | |||
| 1f8ae9ef80 | |||
| 65165de060 | |||
| 485836d2ce | |||
| 64c07c14d9 | |||
| b6313c2b72 | |||
| 58124b5821 | |||
| b6a599f8a6 | |||
| 19a6bc0dbd | |||
| 8cb81d44ee | |||
| 22d0a372d8 | |||
| 988fae75c3 | |||
| a82b0e3622 | |||
| bc6cacacf9 | |||
| 03ad1b3cbc | |||
| eac300627f | |||
| 12525ac386 | |||
| 7558551859 | |||
| a9cce13eef | |||
| 5bb2c43dd0 | |||
| 5b1dcc88cc | |||
| 5c8fc1d136 | |||
| 82c2ab3448 | |||
| b05c8d180f | |||
| 87109e5d01 | |||
| be1a809098 | |||
| ba0e3f1bd4 | |||
| 27d41e6164 | |||
| 1ce5ddfd98 | |||
| 0096a1a4ef | |||
| d2a6560a90 | |||
| 4d7c048139 | 
							
								
								
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,3 @@
 | 
			
		||||
# Auto detect text files and perform LF normalization
 | 
			
		||||
* text=auto
 | 
			
		||||
 | 
			
		||||
*.txt text eof=lf
 | 
			
		||||
* text=auto eof=lf
 | 
			
		||||
*.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/*.iss
 | 
			
		||||
!bld/*.bat
 | 
			
		||||
!bld/*.ps1
 | 
			
		||||
!bld/Redist
 | 
			
		||||
!bld/Resources
 | 
			
		||||
 | 
			
		||||
# Rider
 | 
			
		||||
**/.idea/dictionaries
 | 
			
		||||
**/.idea/misc.xml
 | 
			
		||||
**/.idea/riderMarkupCache.xml
 | 
			
		||||
 | 
			
		||||
# User-specific files
 | 
			
		||||
*.suo
 | 
			
		||||
*.user
 | 
			
		||||
@@ -140,4 +147,3 @@ _UpgradeReport_Files/
 | 
			
		||||
Backup*/
 | 
			
		||||
UpgradeLog*.XML
 | 
			
		||||
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 WindowState PluginsWindow { get; set; } = new WindowState();
 | 
			
		||||
 | 
			
		||||
        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)-2; // 2 = random
 | 
			
		||||
 | 
			
		||||
            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,521 +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(UpdaterSettings updaterSettings){
 | 
			
		||||
            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, updaterSettings);
 | 
			
		||||
            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){
 | 
			
		||||
            this.InvokeAsyncSafe(() => {
 | 
			
		||||
                e.Result.Handle(update => {
 | 
			
		||||
                    if (!update.IsUpdateDismissed){
 | 
			
		||||
                        if (update.IsUpdateNew){
 | 
			
		||||
                            browser.ShowUpdateNotification(update.VersionTag, 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,230 +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 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();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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());
 | 
			
		||||
 | 
			
		||||
                    if (File.Exists(file)){
 | 
			
		||||
                        ViewImage(file);
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        control.InvokeAsyncSafe(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:
 | 
			
		||||
                    if (LastLink.IsVideo){
 | 
			
		||||
                        control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger);
 | 
			
		||||
                        TwitterUtils.DownloadVideo(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault());
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
 | 
			
		||||
                        TwitterUtils.DownloadImage(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault(), ImageQuality);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuSaveTweetImages:
 | 
			
		||||
                    control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
 | 
			
		||||
                    TwitterUtils.DownloadImages(LastChirp.Images, LastChirp.Authors.LastOrDefault(), ImageQuality);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                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 void DeselectAll(IFrame frame){
 | 
			
		||||
            ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected void OpenBrowser(Control control, string url){
 | 
			
		||||
            control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected 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 MenuInputApplyROT13    = (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(MenuInputApplyROT13, "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 MenuInputApplyROT13:
 | 
			
		||||
                    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,26 +0,0 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
 | 
			
		||||
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,89 +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,
 | 
			
		||||
                        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), 20)
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    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,53 +0,0 @@
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private readonly TweetNotification exampleNotification;
 | 
			
		||||
 | 
			
		||||
        public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
 | 
			
		||||
            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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void HideNotification(){
 | 
			
		||||
            Location = ControlExtensions.InvisibleLocation;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void FinishCurrentNotification(){}
 | 
			
		||||
 | 
			
		||||
        public void ShowExampleNotification(bool reset){
 | 
			
		||||
            if (reset){
 | 
			
		||||
                LoadTweet(exampleNotification);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                PrepareAndDisplayWindow();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            UpdateTitle();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,245 +0,0 @@
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using CefSharp.WinForms;
 | 
			
		||||
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.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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public 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);
 | 
			
		||||
        public bool IsPaused => pauseCounter > 0;
 | 
			
		||||
 | 
			
		||||
        protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position));
 | 
			
		||||
        
 | 
			
		||||
        public bool FreezeTimer { get; set; }
 | 
			
		||||
        public bool ContextMenuOpen { get; set; }
 | 
			
		||||
 | 
			
		||||
        public event EventHandler Initialized;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
            this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
 | 
			
		||||
            
 | 
			
		||||
            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();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
 | 
			
		||||
            if (e.IsBrowserInitialized){
 | 
			
		||||
                Initialized?.Invoke(this, EventArgs.Empty);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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,85 +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 readonly int width;
 | 
			
		||||
 | 
			
		||||
        public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager, string html, int width) : base(owner, false){
 | 
			
		||||
            this.plugins = pluginManager;
 | 
			
		||||
            this.width = width;
 | 
			
		||||
 | 
			
		||||
            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}", ClientSize.Width.ToString()), "screenshot");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            
 | 
			
		||||
            SetScreenshotHeight(1);
 | 
			
		||||
            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 height){
 | 
			
		||||
            SetNotificationSize(width, height);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TakeScreenshot(){
 | 
			
		||||
            if (ClientSize.Height == 0){
 | 
			
		||||
                FormMessage.Error("Screenshot Failed", "Could not detect screenshot size.", FormMessage.OK);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                using(Bitmap bmp = new Bitmap(ClientSize.Width, ClientSize.Height, PixelFormat.Format32bppRgb)){
 | 
			
		||||
                    try{
 | 
			
		||||
                        NativeMethods.RenderSourceIntoBitmap(context, bmp);
 | 
			
		||||
                    }finally{
 | 
			
		||||
                        NativeMethods.ReleaseDC(this.Handle, context);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Clipboard.SetImage(bmp);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,82 +0,0 @@
 | 
			
		||||
// Uncomment to keep screenshot windows visible for debugging
 | 
			
		||||
// #define NO_HIDE_SCREENSHOTS
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Plugins;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
        
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 !(DEBUG && NO_HIDE_SCREENSHOTS)
 | 
			
		||||
            owner.IsWaiting = true;
 | 
			
		||||
            #endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Callback(){
 | 
			
		||||
            if (!timeout.Enabled){
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            timeout.Stop();
 | 
			
		||||
            screenshot.TakeScreenshot();
 | 
			
		||||
 | 
			
		||||
            #if !(DEBUG && NO_HIDE_SCREENSHOTS)
 | 
			
		||||
            OnFinished();
 | 
			
		||||
            #else
 | 
			
		||||
            screenshot.MoveToVisibleLocation();
 | 
			
		||||
            screenshot.FormClosed += (sender, args) => disposer.Start();
 | 
			
		||||
            #endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnFinished(){
 | 
			
		||||
            screenshot.Location = ControlExtensions.InvisibleLocation;
 | 
			
		||||
            owner.IsWaiting = false;
 | 
			
		||||
            disposer.Start();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose(){
 | 
			
		||||
            timeout.Dispose();
 | 
			
		||||
            disposer.Dispose();
 | 
			
		||||
            screenshot?.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;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,327 +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) },
 | 
			
		||||
                { "Switch Account Selectors"      , Bool(false) }, // TODO remove in next major update
 | 
			
		||||
                { "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,120 +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.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+)?(?:/(#.*))?";
 | 
			
		||||
 | 
			
		||||
        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 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;
 | 
			
		||||
            Controls.Add(browser);
 | 
			
		||||
 | 
			
		||||
            Disposed += (sender, args) => {
 | 
			
		||||
                Program.UserConfig.ZoomLevelChanged -= Config_ZoomLevelChanged;
 | 
			
		||||
                browser.Dispose();
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Reload(string url){
 | 
			
		||||
            browser.LoadingStateChanged += browser_LoadingStateChanged;
 | 
			
		||||
            browser.Dock = DockStyle.None;
 | 
			
		||||
            browser.Location = ControlExtensions.InvisibleLocation;
 | 
			
		||||
            browser.Load("about:blank");
 | 
			
		||||
            browser.Load(url);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
 | 
			
		||||
            if (!e.IsLoading && browser.Address != "about:blank"){
 | 
			
		||||
                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){
 | 
			
		||||
            // idiot chromium
 | 
			
		||||
            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,93 +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;
 | 
			
		||||
 | 
			
		||||
            Shown += (sender, args) => {
 | 
			
		||||
                Program.UserConfig.PluginsWindow.Restore(this, false);
 | 
			
		||||
                ReloadPluginList();
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            FormClosed += (sender, args) => {
 | 
			
		||||
                Program.UserConfig.PluginsWindow.Save(this);
 | 
			
		||||
                Program.UserConfig.Save();
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            
 | 
			
		||||
            // sorry, I guess...
 | 
			
		||||
            Padding = new Padding(Padding.Left, Padding.Top, Padding.Right+1, Padding.Bottom);
 | 
			
		||||
            Padding = new Padding(Padding.Left, Padding.Top, Padding.Right-1, Padding.Bottom);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void flowLayoutPlugins_Resize(object sender, EventArgs e){
 | 
			
		||||
            if (flowLayoutPlugins.Controls.Count == 0){
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Control lastControl = flowLayoutPlugins.Controls[flowLayoutPlugins.Controls.Count-1];
 | 
			
		||||
            bool showScrollBar = lastControl.Location.Y+lastControl.Height >= 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;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            lastControl.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,186 +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,
 | 
			
		||||
                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,90 +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, 41);
 | 
			
		||||
            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(460, 480);
 | 
			
		||||
            this.textBoxReport.TabIndex = 1;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnClose
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
 | 
			
		||||
            this.btnClose.Location = new System.Drawing.Point(416, 527);
 | 
			
		||||
            this.btnClose.Name = "btnClose";
 | 
			
		||||
            this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
 | 
			
		||||
            this.btnClose.Size = new System.Drawing.Size(56, 23);
 | 
			
		||||
            this.btnClose.TabIndex = 2;
 | 
			
		||||
            this.btnClose.Text = "Close";
 | 
			
		||||
            this.btnClose.UseVisualStyleBackColor = true;
 | 
			
		||||
            this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
 | 
			
		||||
            // 
 | 
			
		||||
            // labelInfo
 | 
			
		||||
            // 
 | 
			
		||||
            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(460, 26);
 | 
			
		||||
            this.labelInfo.TabIndex = 0;
 | 
			
		||||
            this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly once every wee" +
 | 
			
		||||
    "k.\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(484, 562);
 | 
			
		||||
            this.Controls.Add(this.labelInfo);
 | 
			
		||||
            this.Controls.Add(this.btnClose);
 | 
			
		||||
            this.Controls.Add(this.textBoxReport);
 | 
			
		||||
            this.MinimumSize = new System.Drawing.Size(450, 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,207 +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, 253);
 | 
			
		||||
            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.Location = new System.Drawing.Point(654, 287);
 | 
			
		||||
            this.btnCancel.Name = "btnCancel";
 | 
			
		||||
            this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
 | 
			
		||||
            this.btnCancel.Size = new System.Drawing.Size(56, 23);
 | 
			
		||||
            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.Location = new System.Drawing.Point(716, 287);
 | 
			
		||||
            this.btnApply.Name = "btnApply";
 | 
			
		||||
            this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
 | 
			
		||||
            this.btnApply.Size = new System.Drawing.Size(56, 23);
 | 
			
		||||
            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, 269);
 | 
			
		||||
            this.splitContainer.SplitterDistance = 378;
 | 
			
		||||
            this.splitContainer.SplitterWidth = 5;
 | 
			
		||||
            this.splitContainer.TabIndex = 0;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelBrowser
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelBrowser.AutoSize = true;
 | 
			
		||||
            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(45, 13);
 | 
			
		||||
            this.labelBrowser.TabIndex = 0;
 | 
			
		||||
            this.labelBrowser.Text = "Browser";
 | 
			
		||||
            // 
 | 
			
		||||
            // labelNotification
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelNotification.AutoSize = true;
 | 
			
		||||
            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(60, 13);
 | 
			
		||||
            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(377, 253);
 | 
			
		||||
            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.Location = new System.Drawing.Point(91, 292);
 | 
			
		||||
            this.labelWarning.Name = "labelWarning";
 | 
			
		||||
            this.labelWarning.Size = new System.Drawing.Size(341, 13);
 | 
			
		||||
            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.Location = new System.Drawing.Point(12, 287);
 | 
			
		||||
            this.btnOpenWiki.Name = "btnOpenWiki";
 | 
			
		||||
            this.btnOpenWiki.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
 | 
			
		||||
            this.btnOpenWiki.Size = new System.Drawing.Size(73, 23);
 | 
			
		||||
            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(600, 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){
 | 
			
		||||
            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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										293
									
								
								Core/Other/Settings/TabSettingsAdvanced.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										293
									
								
								Core/Other/Settings/TabSettingsAdvanced.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -1,293 +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.Location = new System.Drawing.Point(5, 172);
 | 
			
		||||
            this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
 | 
			
		||||
            this.btnClearCache.Name = "btnClearCache";
 | 
			
		||||
            this.btnClearCache.Size = new System.Drawing.Size(144, 23);
 | 
			
		||||
            this.btnClearCache.TabIndex = 5;
 | 
			
		||||
            this.btnClearCache.Text = "Clear Cache (calculating)";
 | 
			
		||||
            this.btnClearCache.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkHardwareAcceleration
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkHardwareAcceleration.AutoSize = true;
 | 
			
		||||
            this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 124);
 | 
			
		||||
            this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
 | 
			
		||||
            this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
 | 
			
		||||
            this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
 | 
			
		||||
            this.checkHardwareAcceleration.TabIndex = 3;
 | 
			
		||||
            this.checkHardwareAcceleration.Text = "Hardware Acceleration";
 | 
			
		||||
            this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnEditCefArgs
 | 
			
		||||
            // 
 | 
			
		||||
            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, 23);
 | 
			
		||||
            this.btnEditCefArgs.TabIndex = 0;
 | 
			
		||||
            this.btnEditCefArgs.Text = "Edit CEF Arguments";
 | 
			
		||||
            this.btnEditCefArgs.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnEditCSS
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnEditCSS.Location = new System.Drawing.Point(155, 3);
 | 
			
		||||
            this.btnEditCSS.Name = "btnEditCSS";
 | 
			
		||||
            this.btnEditCSS.Size = new System.Drawing.Size(144, 23);
 | 
			
		||||
            this.btnEditCSS.TabIndex = 1;
 | 
			
		||||
            this.btnEditCSS.Text = "Edit CSS";
 | 
			
		||||
            this.btnEditCSS.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnRestartArgs
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnRestartArgs.Location = new System.Drawing.Point(155, 32);
 | 
			
		||||
            this.btnRestartArgs.Name = "btnRestartArgs";
 | 
			
		||||
            this.btnRestartArgs.Size = new System.Drawing.Size(144, 23);
 | 
			
		||||
            this.btnRestartArgs.TabIndex = 3;
 | 
			
		||||
            this.btnRestartArgs.Text = "Restart with Arguments";
 | 
			
		||||
            this.btnRestartArgs.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnRestart
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnRestart.Location = new System.Drawing.Point(155, 3);
 | 
			
		||||
            this.btnRestart.Name = "btnRestart";
 | 
			
		||||
            this.btnRestart.Size = new System.Drawing.Size(144, 23);
 | 
			
		||||
            this.btnRestart.TabIndex = 2;
 | 
			
		||||
            this.btnRestart.Text = "Restart the Program";
 | 
			
		||||
            this.btnRestart.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnOpenAppFolder
 | 
			
		||||
            // 
 | 
			
		||||
            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, 23);
 | 
			
		||||
            this.btnOpenAppFolder.TabIndex = 0;
 | 
			
		||||
            this.btnOpenAppFolder.Text = "Open Program Folder";
 | 
			
		||||
            this.btnOpenAppFolder.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnOpenDataFolder
 | 
			
		||||
            // 
 | 
			
		||||
            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, 23);
 | 
			
		||||
            this.btnOpenDataFolder.TabIndex = 1;
 | 
			
		||||
            this.btnOpenDataFolder.Text = "Open Data Folder";
 | 
			
		||||
            this.btnOpenDataFolder.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // numClearCacheThreshold
 | 
			
		||||
            // 
 | 
			
		||||
            this.numClearCacheThreshold.Increment = 50;
 | 
			
		||||
            this.numClearCacheThreshold.Location = new System.Drawing.Point(227, 4);
 | 
			
		||||
            this.numClearCacheThreshold.Maximum = 1000;
 | 
			
		||||
            this.numClearCacheThreshold.Minimum = 100;
 | 
			
		||||
            this.numClearCacheThreshold.Name = "numClearCacheThreshold";
 | 
			
		||||
            this.numClearCacheThreshold.Size = new System.Drawing.Size(72, 20);
 | 
			
		||||
            this.numClearCacheThreshold.TabIndex = 1;
 | 
			
		||||
            this.numClearCacheThreshold.TextSuffix = " MB";
 | 
			
		||||
            this.numClearCacheThreshold.Value = 250;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkClearCacheAuto
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkClearCacheAuto.AutoSize = true;
 | 
			
		||||
            this.checkClearCacheAuto.Location = new System.Drawing.Point(6, 6);
 | 
			
		||||
            this.checkClearCacheAuto.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
 | 
			
		||||
            this.checkClearCacheAuto.Name = "checkClearCacheAuto";
 | 
			
		||||
            this.checkClearCacheAuto.Size = new System.Drawing.Size(215, 17);
 | 
			
		||||
            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("Microsoft Sans Serif", 12F, 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(38, 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, 58);
 | 
			
		||||
            this.panelAppButtons.TabIndex = 1;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelPerformance
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelPerformance.AutoSize = true;
 | 
			
		||||
            this.labelPerformance.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
 | 
			
		||||
            this.labelPerformance.Location = new System.Drawing.Point(0, 98);
 | 
			
		||||
            this.labelPerformance.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
 | 
			
		||||
            this.labelPerformance.Name = "labelPerformance";
 | 
			
		||||
            this.labelPerformance.Size = new System.Drawing.Size(100, 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, 198);
 | 
			
		||||
            this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0);
 | 
			
		||||
            this.panelClearCacheAuto.Name = "panelClearCacheAuto";
 | 
			
		||||
            this.panelClearCacheAuto.Size = new System.Drawing.Size(322, 26);
 | 
			
		||||
            this.panelClearCacheAuto.TabIndex = 6;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelCache
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelCache.AutoSize = true;
 | 
			
		||||
            this.labelCache.Location = new System.Drawing.Point(3, 156);
 | 
			
		||||
            this.labelCache.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelCache.Name = "labelCache";
 | 
			
		||||
            this.labelCache.Size = new System.Drawing.Size(38, 13);
 | 
			
		||||
            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, 264);
 | 
			
		||||
            this.panelConfiguration.Margin = new System.Windows.Forms.Padding(0);
 | 
			
		||||
            this.panelConfiguration.Name = "panelConfiguration";
 | 
			
		||||
            this.panelConfiguration.Size = new System.Drawing.Size(322, 29);
 | 
			
		||||
            this.panelConfiguration.TabIndex = 8;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelConfiguration
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelConfiguration.AutoSize = true;
 | 
			
		||||
            this.labelConfiguration.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
 | 
			
		||||
            this.labelConfiguration.Location = new System.Drawing.Point(0, 244);
 | 
			
		||||
            this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
 | 
			
		||||
            this.labelConfiguration.Name = "labelConfiguration";
 | 
			
		||||
            this.labelConfiguration.Size = new System.Drawing.Size(104, 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, 295);
 | 
			
		||||
            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, 313);
 | 
			
		||||
            ((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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										176
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										176
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -1,176 +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, 74);
 | 
			
		||||
            this.panelDataCollection.Margin = new System.Windows.Forms.Padding(0);
 | 
			
		||||
            this.panelDataCollection.Name = "panelDataCollection";
 | 
			
		||||
            this.panelDataCollection.Size = new System.Drawing.Size(322, 26);
 | 
			
		||||
            this.panelDataCollection.TabIndex = 3;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelDataCollectionLink
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelDataCollectionLink.AutoSize = true;
 | 
			
		||||
            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(141, 6);
 | 
			
		||||
            this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
 | 
			
		||||
            this.labelDataCollectionLink.Name = "labelDataCollectionLink";
 | 
			
		||||
            this.labelDataCollectionLink.Size = new System.Drawing.Size(66, 17);
 | 
			
		||||
            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.Location = new System.Drawing.Point(6, 6);
 | 
			
		||||
            this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 6, 0, 3);
 | 
			
		||||
            this.checkDataCollection.Name = "checkDataCollection";
 | 
			
		||||
            this.checkDataCollection.Size = new System.Drawing.Size(135, 17);
 | 
			
		||||
            this.checkDataCollection.TabIndex = 0;
 | 
			
		||||
            this.checkDataCollection.Text = "Send Anonymous Data";
 | 
			
		||||
            this.checkDataCollection.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelDataCollectionMessage
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 135);
 | 
			
		||||
            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.Location = new System.Drawing.Point(5, 103);
 | 
			
		||||
            this.btnViewReport.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
 | 
			
		||||
            this.btnViewReport.Name = "btnViewReport";
 | 
			
		||||
            this.btnViewReport.Size = new System.Drawing.Size(144, 23);
 | 
			
		||||
            this.btnViewReport.TabIndex = 4;
 | 
			
		||||
            this.btnViewReport.Text = "View My Analytics Report";
 | 
			
		||||
            this.btnViewReport.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnSendFeedback
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnSendFeedback.AutoSize = true;
 | 
			
		||||
            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(3, 0, 3, 0);
 | 
			
		||||
            this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
 | 
			
		||||
            this.btnSendFeedback.TabIndex = 1;
 | 
			
		||||
            this.btnSendFeedback.Text = "Send Feedback / Bug Report";
 | 
			
		||||
            this.btnSendFeedback.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelDataCollection
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelDataCollection.AutoSize = true;
 | 
			
		||||
            this.labelDataCollection.Location = new System.Drawing.Point(3, 61);
 | 
			
		||||
            this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelDataCollection.Name = "labelDataCollection";
 | 
			
		||||
            this.labelDataCollection.Size = new System.Drawing.Size(79, 13);
 | 
			
		||||
            this.labelDataCollection.TabIndex = 2;
 | 
			
		||||
            this.labelDataCollection.Text = "Data Collection";
 | 
			
		||||
            // 
 | 
			
		||||
            // labelFeedback
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelFeedback.AutoSize = true;
 | 
			
		||||
            this.labelFeedback.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, 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(80, 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, 209);
 | 
			
		||||
            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, 227);
 | 
			
		||||
            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();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										335
									
								
								Core/Other/Settings/TabSettingsGeneral.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										335
									
								
								Core/Other/Settings/TabSettingsGeneral.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -1,335 +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.Location = new System.Drawing.Point(6, 26);
 | 
			
		||||
            this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
 | 
			
		||||
            this.checkExpandLinks.Name = "checkExpandLinks";
 | 
			
		||||
            this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
 | 
			
		||||
            this.checkExpandLinks.TabIndex = 1;
 | 
			
		||||
            this.checkExpandLinks.Text = "Expand Links When Hovered";
 | 
			
		||||
            this.checkExpandLinks.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkUpdateNotifications
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkUpdateNotifications.AutoSize = true;
 | 
			
		||||
            this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 415);
 | 
			
		||||
            this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
 | 
			
		||||
            this.checkUpdateNotifications.Name = "checkUpdateNotifications";
 | 
			
		||||
            this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
 | 
			
		||||
            this.checkUpdateNotifications.TabIndex = 15;
 | 
			
		||||
            this.checkUpdateNotifications.Text = "Check Updates Automatically";
 | 
			
		||||
            this.checkUpdateNotifications.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnCheckUpdates
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnCheckUpdates.Location = new System.Drawing.Point(5, 438);
 | 
			
		||||
            this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
 | 
			
		||||
            this.btnCheckUpdates.Name = "btnCheckUpdates";
 | 
			
		||||
            this.btnCheckUpdates.Size = new System.Drawing.Size(144, 23);
 | 
			
		||||
            this.btnCheckUpdates.TabIndex = 16;
 | 
			
		||||
            this.btnCheckUpdates.Text = "Check Updates Now";
 | 
			
		||||
            this.btnCheckUpdates.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelZoomValue
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
 | 
			
		||||
            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.Location = new System.Drawing.Point(6, 95);
 | 
			
		||||
            this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
 | 
			
		||||
            this.checkBestImageQuality.Name = "checkBestImageQuality";
 | 
			
		||||
            this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
 | 
			
		||||
            this.checkBestImageQuality.TabIndex = 4;
 | 
			
		||||
            this.checkBestImageQuality.Text = "Best Image Quality";
 | 
			
		||||
            this.checkBestImageQuality.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkOpenSearchInFirstColumn
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkOpenSearchInFirstColumn.AutoSize = true;
 | 
			
		||||
            this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 49);
 | 
			
		||||
            this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
 | 
			
		||||
            this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
 | 
			
		||||
            this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17);
 | 
			
		||||
            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.Location = new System.Drawing.Point(3, 320);
 | 
			
		||||
            this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelZoom.Name = "labelZoom";
 | 
			
		||||
            this.labelZoom.Size = new System.Drawing.Size(34, 13);
 | 
			
		||||
            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("Microsoft Sans Serif", 12F, 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(111, 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, 333);
 | 
			
		||||
            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.Location = new System.Drawing.Point(6, 118);
 | 
			
		||||
            this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
 | 
			
		||||
            this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
 | 
			
		||||
            this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17);
 | 
			
		||||
            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("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
 | 
			
		||||
            this.labelUpdates.Location = new System.Drawing.Point(0, 389);
 | 
			
		||||
            this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
 | 
			
		||||
            this.labelUpdates.Name = "labelUpdates";
 | 
			
		||||
            this.labelUpdates.Size = new System.Drawing.Size(70, 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, 462);
 | 
			
		||||
            this.flowPanel.TabIndex = 0;
 | 
			
		||||
            this.flowPanel.WrapContents = false;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkKeepLikeFollowDialogsOpen
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
 | 
			
		||||
            this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 72);
 | 
			
		||||
            this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
 | 
			
		||||
            this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
 | 
			
		||||
            this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(176, 17);
 | 
			
		||||
            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("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
 | 
			
		||||
            this.labelBrowserSettings.Location = new System.Drawing.Point(0, 158);
 | 
			
		||||
            this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
 | 
			
		||||
            this.labelBrowserSettings.Name = "labelBrowserSettings";
 | 
			
		||||
            this.labelBrowserSettings.Size = new System.Drawing.Size(130, 20);
 | 
			
		||||
            this.labelBrowserSettings.TabIndex = 6;
 | 
			
		||||
            this.labelBrowserSettings.Text = "Browser Settings";
 | 
			
		||||
            // 
 | 
			
		||||
            // checkSmoothScrolling
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkSmoothScrolling.AutoSize = true;
 | 
			
		||||
            this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 184);
 | 
			
		||||
            this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
 | 
			
		||||
            this.checkSmoothScrolling.Name = "checkSmoothScrolling";
 | 
			
		||||
            this.checkSmoothScrolling.Size = new System.Drawing.Size(105, 17);
 | 
			
		||||
            this.checkSmoothScrolling.TabIndex = 7;
 | 
			
		||||
            this.checkSmoothScrolling.Text = "Smooth Scrolling";
 | 
			
		||||
            this.checkSmoothScrolling.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelBrowserPath
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelBrowserPath.AutoSize = true;
 | 
			
		||||
            this.labelBrowserPath.Location = new System.Drawing.Point(3, 216);
 | 
			
		||||
            this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelBrowserPath.Name = "labelBrowserPath";
 | 
			
		||||
            this.labelBrowserPath.Size = new System.Drawing.Size(95, 13);
 | 
			
		||||
            this.labelBrowserPath.TabIndex = 8;
 | 
			
		||||
            this.labelBrowserPath.Text = "Open Links With...";
 | 
			
		||||
            // 
 | 
			
		||||
            // comboBoxBrowserPath
 | 
			
		||||
            // 
 | 
			
		||||
            this.comboBoxBrowserPath.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
 | 
			
		||||
            this.comboBoxBrowserPath.FormattingEnabled = true;
 | 
			
		||||
            this.comboBoxBrowserPath.Location = new System.Drawing.Point(5, 232);
 | 
			
		||||
            this.comboBoxBrowserPath.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
 | 
			
		||||
            this.comboBoxBrowserPath.Name = "comboBoxBrowserPath";
 | 
			
		||||
            this.comboBoxBrowserPath.Size = new System.Drawing.Size(173, 21);
 | 
			
		||||
            this.comboBoxBrowserPath.TabIndex = 9;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelSearchEngine
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelSearchEngine.AutoSize = true;
 | 
			
		||||
            this.labelSearchEngine.Location = new System.Drawing.Point(3, 268);
 | 
			
		||||
            this.labelSearchEngine.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelSearchEngine.Name = "labelSearchEngine";
 | 
			
		||||
            this.labelSearchEngine.Size = new System.Drawing.Size(77, 13);
 | 
			
		||||
            this.labelSearchEngine.TabIndex = 10;
 | 
			
		||||
            this.labelSearchEngine.Text = "Search Engine";
 | 
			
		||||
            // 
 | 
			
		||||
            // comboBoxSearchEngine
 | 
			
		||||
            // 
 | 
			
		||||
            this.comboBoxSearchEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
 | 
			
		||||
            this.comboBoxSearchEngine.FormattingEnabled = true;
 | 
			
		||||
            this.comboBoxSearchEngine.Location = new System.Drawing.Point(5, 284);
 | 
			
		||||
            this.comboBoxSearchEngine.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
 | 
			
		||||
            this.comboBoxSearchEngine.Name = "comboBoxSearchEngine";
 | 
			
		||||
            this.comboBoxSearchEngine.Size = new System.Drawing.Size(173, 21);
 | 
			
		||||
            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, 480);
 | 
			
		||||
            ((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,255 +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){
 | 
			
		||||
            this.InvokeAsyncSafe(() => {
 | 
			
		||||
                if (e.EventId == updateCheckEventId){
 | 
			
		||||
                    btnCheckUpdates.Enabled = true;
 | 
			
		||||
 | 
			
		||||
                    e.Result.Handle(update => {
 | 
			
		||||
                        if (!update.IsUpdateNew){
 | 
			
		||||
                            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										155
									
								
								Core/Other/Settings/TabSettingsLocales.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										155
									
								
								Core/Other/Settings/TabSettingsLocales.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -1,155 +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.Location = new System.Drawing.Point(6, 26);
 | 
			
		||||
            this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
 | 
			
		||||
            this.checkSpellCheck.Name = "checkSpellCheck";
 | 
			
		||||
            this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
 | 
			
		||||
            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("Microsoft Sans Serif", 12F, 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(64, 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, 193);
 | 
			
		||||
            this.flowPanel.TabIndex = 0;
 | 
			
		||||
            this.flowPanel.WrapContents = false;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelSpellCheckLanguage
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelSpellCheckLanguage.AutoSize = true;
 | 
			
		||||
            this.labelSpellCheckLanguage.Location = new System.Drawing.Point(3, 58);
 | 
			
		||||
            this.labelSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelSpellCheckLanguage.Name = "labelSpellCheckLanguage";
 | 
			
		||||
            this.labelSpellCheckLanguage.Size = new System.Drawing.Size(115, 13);
 | 
			
		||||
            this.labelSpellCheckLanguage.TabIndex = 2;
 | 
			
		||||
            this.labelSpellCheckLanguage.Text = "Spell Check Language";
 | 
			
		||||
            // 
 | 
			
		||||
            // comboBoxSpellCheckLanguage
 | 
			
		||||
            // 
 | 
			
		||||
            this.comboBoxSpellCheckLanguage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
 | 
			
		||||
            this.comboBoxSpellCheckLanguage.FormattingEnabled = true;
 | 
			
		||||
            this.comboBoxSpellCheckLanguage.Location = new System.Drawing.Point(5, 74);
 | 
			
		||||
            this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
 | 
			
		||||
            this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage";
 | 
			
		||||
            this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(311, 21);
 | 
			
		||||
            this.comboBoxSpellCheckLanguage.TabIndex = 3;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelTranslations
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelTranslations.AutoSize = true;
 | 
			
		||||
            this.labelTranslations.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
 | 
			
		||||
            this.labelTranslations.Location = new System.Drawing.Point(0, 118);
 | 
			
		||||
            this.labelTranslations.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
 | 
			
		||||
            this.labelTranslations.Name = "labelTranslations";
 | 
			
		||||
            this.labelTranslations.Size = new System.Drawing.Size(116, 20);
 | 
			
		||||
            this.labelTranslations.TabIndex = 4;
 | 
			
		||||
            this.labelTranslations.Text = "Bing Translator";
 | 
			
		||||
            // 
 | 
			
		||||
            // labelTranslationTarget
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelTranslationTarget.AutoSize = true;
 | 
			
		||||
            this.labelTranslationTarget.Location = new System.Drawing.Point(3, 150);
 | 
			
		||||
            this.labelTranslationTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelTranslationTarget.Name = "labelTranslationTarget";
 | 
			
		||||
            this.labelTranslationTarget.Size = new System.Drawing.Size(89, 13);
 | 
			
		||||
            this.labelTranslationTarget.TabIndex = 5;
 | 
			
		||||
            this.labelTranslationTarget.Text = "Target Language";
 | 
			
		||||
            // 
 | 
			
		||||
            // comboBoxTranslationTarget
 | 
			
		||||
            // 
 | 
			
		||||
            this.comboBoxTranslationTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
 | 
			
		||||
            this.comboBoxTranslationTarget.FormattingEnabled = true;
 | 
			
		||||
            this.comboBoxTranslationTarget.Location = new System.Drawing.Point(5, 166);
 | 
			
		||||
            this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
 | 
			
		||||
            this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget";
 | 
			
		||||
            this.comboBoxTranslationTarget.Size = new System.Drawing.Size(311, 21);
 | 
			
		||||
            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, 211);
 | 
			
		||||
            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.Initialized += (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,205 +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 HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion;
 | 
			
		||||
 | 
			
		||||
        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] = HeaderUserAgent;
 | 
			
		||||
            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,33 +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 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