mirror of
				https://github.com/chylex/TweetDuck.git
				synced 2025-11-04 01:40:16 +01:00 
			
		
		
		
	Compare commits
	
		
			758 Commits
		
	
	
		
			1.11.2
			...
			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 | |||
| 1d78bd2655 | |||
| 9250f1907c | |||
| a63e210b88 | |||
| 06bd65b7f8 | |||
| b6c17eb05e | |||
| a3d40fdc2b | |||
| c064ef7a30 | |||
| 762717da1e | |||
| b7d3758bea | |||
| d20541fd24 | |||
| 2c2f860f26 | |||
| d1db3aa673 | |||
| cedc52cdf5 | |||
| 33f8eafbcf | |||
| ad45cf8c72 | |||
| f99d035621 | |||
| f3072caea8 | |||
| 1410974292 | |||
| 44413fa96c | |||
| 342a4b4067 | |||
| 4356dde92d | |||
| 21e64a18d8 | |||
| 5a305a6740 | |||
| 44595bad40 | |||
| 7fc9edc9cb | |||
| 93e191f522 | |||
| 8d8355e792 | |||
| a5379d290c | |||
| caea8d4315 | |||
| 24224ab4c6 | |||
| 4dbc02360c | |||
| aa7a29af0c | |||
| 296d0c6199 | |||
| 812a034e8d | |||
| e9de789b79 | |||
| cfbc1b9575 | |||
| e39e85e4dd | |||
| 3f0b161cd0 | |||
| ebe3868720 | |||
| ffd0f5e986 | |||
| 217535a3ba | |||
| 7abfbea2da | |||
| 86ffeaac9a | |||
| ab915b7115 | |||
| 705b5d38cf | |||
| fc2acb00b3 | |||
| 5add8a1d0e | |||
| 063d3a2637 | |||
| f1f90a2ee3 | |||
| ed317a4e46 | |||
| cca16f3bb1 | |||
| aba156cb3b | |||
| cd4e4d7095 | |||
| 8fbb639430 | |||
| d5bf8ec558 | |||
| b6cff40f1e | |||
| 833e42f455 | |||
| 8134843dad | |||
| 1f92d5e633 | |||
| dc51c0ae85 | |||
| 45c79643d6 | |||
| 9041bfc627 | |||
| 0b3b3dd0be | |||
| 89e92dab59 | |||
| 8c168c9ad7 | |||
| 9f63357a92 | |||
| d91b4bd1f3 | |||
| c0c64f6d62 | |||
| 1a5d2af779 | |||
| f40a33192b | |||
| ca4900aff0 | |||
| 56fc9e2d40 | |||
| d2174c0b69 | |||
| 9f76754ad3 | |||
| 118ceaec35 | |||
| 5a57d28a7d | |||
| 07af99f862 | |||
| 59fba7fba0 | |||
| dd4edc4249 | |||
| 856226473a | |||
| 8d1c07d6b2 | |||
| c32462cc9e | |||
| ec94ea3273 | |||
| 41acd8c15b | |||
| 155a79f2ec | |||
| 9197cb9be6 | |||
| 03d50c847b | |||
| bf45c40365 | |||
| 679e126194 | |||
| 50e39164bd | |||
| cb9f75e968 | |||
| aa7f6cc3b1 | |||
| fe601aed41 | |||
| 2282a9df28 | |||
| 2b54627750 | |||
| 16051a0d25 | |||
| 66d5f0d790 | |||
| 07d29207f0 | |||
| a60be2afcc | |||
| 027f3ee253 | |||
| 04774815e4 | |||
| 61a73c055b | |||
| 7731534ffc | |||
| ed7bf99610 | |||
| cbe4272556 | |||
| 8f5e3dfdcc | |||
| 35500c51f1 | |||
| 629f873bb2 | |||
| a44cb884c4 | |||
| d5ad1d0daa | |||
| 61ae7e3b6a | |||
| 01583e424f | |||
| 5c0aa1b3da | |||
| 07391efa70 | |||
| b80f1bfc7c | |||
| ad310db86c | |||
| 4ce0122a29 | |||
| a8894f7054 | |||
| 1d1515351b | |||
| 2a9ddd4468 | |||
| 0f9a944775 | |||
| 34ee9ebd66 | |||
| 43f632b555 | |||
| 7cf3f1d32c | |||
| e51e87647e | |||
| b8aae88b11 | |||
| d06e29db15 | |||
| 62449450f3 | |||
| b290c94635 | |||
| f909b887d9 | |||
| 5cf4843212 | |||
| b3d1e1bfac | |||
| df47499a28 | |||
| 421475ec87 | |||
| 29d999b8eb | |||
| acacd9a5e5 | |||
| b81c26f93f | |||
| 00b212944c | |||
| 70ba006e4d | |||
| 118e0cae62 | |||
| c003bb4e71 | |||
| e9b2fa7603 | |||
| 35afaa105d | |||
| 2e300a7b8f | |||
| f3f5b88550 | |||
| 22f491d98a | |||
| 7908c8ebd9 | |||
| e114a93714 | |||
| 931761600f | |||
| e5b4b03e1a | |||
| f1e8b3fbf0 | |||
| 4d64243a07 | |||
| 3422b4d4d6 | |||
| b170d529fd | |||
| 83741db5aa | |||
| c4b2b3ab25 | |||
| 676df44985 | |||
| 037adc6b5c | |||
| 186d17dd98 | |||
| ab9ff980ef | |||
| f297cb2623 | |||
| b53c672768 | |||
| 1a2b967749 | |||
| 6ba30c48cf | |||
| 1af9ee9ced | |||
| e50480aa35 | |||
| 6943c7813f | |||
| 7c9b4382ca | |||
| 3187f97592 | |||
| b71a367052 | |||
| 2d4bbf2a6f | |||
| 6e59dfddcc | |||
| bd92fc6ee0 | |||
| 2f61de7025 | |||
| 8fcec7ec7c | |||
| 33d9ba3871 | |||
| 4f8c778ba0 | |||
| 804c739038 | |||
| a0445fbb12 | |||
| 7ab5d7b796 | |||
| 7f83a7773b | |||
| fc9e8a808f | |||
| 5ab8976bc7 | |||
| e2a28f2811 | |||
| 137a20ed0e | |||
| f956f696f4 | |||
| bb7cbde38f | |||
| 8c452d3fa2 | |||
| f65c33c432 | |||
| da2758ccb1 | |||
| de10112b7f | |||
| 301d4fb171 | |||
| f0a79add14 | |||
| d33bc9fe25 | |||
| a2a5dfd435 | |||
| f3d7c8d4c3 | |||
| 67f60dd787 | |||
| 62310ce4a4 | |||
| 3a27089364 | |||
| a05460f562 | |||
| 390872c305 | |||
| 594d12df79 | |||
| c42c12c72b | |||
| c37f4fe365 | |||
| 8d37d68770 | |||
| 1b3d1fb36a | |||
| 2f352ef9bb | |||
| 527f3cab4c | |||
| f67bf27db2 | |||
| a8bb3ba349 | |||
| 321ab12213 | |||
| 4cab18e557 | |||
| c15ea97a36 | |||
| a0cc4109df | |||
| f66ff1000a | |||
| 51a9bb6d3c | |||
| 07017bd29b | |||
| 45b6f49a08 | |||
| 103ad72788 | |||
| 543259f29f | 
							
								
								
									
										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,48 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using TweetDuck.Data;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Configuration{
 | 
			
		||||
    static class Arguments{
 | 
			
		||||
        // public args
 | 
			
		||||
        public const string ArgDataFolder = "-datafolder";
 | 
			
		||||
        public const string ArgLocale = "-locale";
 | 
			
		||||
        public const string ArgLogging = "-log";
 | 
			
		||||
        public const string ArgDebugUpdates = "-debugupdates";
 | 
			
		||||
 | 
			
		||||
        // 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 EnableBrowserGCReload { get; set; } = true;
 | 
			
		||||
        public int BrowserMemoryThreshold { get; set; } = 400;
 | 
			
		||||
 | 
			
		||||
        // 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,221 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
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 bool ShowDataCollectionNotification { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
        public WindowState BrowserWindow { get; set; } = new WindowState();
 | 
			
		||||
        public WindowState PluginsWindow { get; set; } = new WindowState();
 | 
			
		||||
 | 
			
		||||
        public bool ExpandLinksOnHover      { get; set; } = true;
 | 
			
		||||
        public bool SwitchAccountSelectors  { get; set; } = true;
 | 
			
		||||
        public bool OpenSearchInFirstColumn { get; set; } = true;
 | 
			
		||||
        public bool BestImageQuality        { get; set; } = true;
 | 
			
		||||
        public bool EnableAnimatedImages    { get; set; } = true;
 | 
			
		||||
        public bool EnableSpellCheck        { get; set; } = false;
 | 
			
		||||
        public int VideoPlayerVolume        { get; set; } = 50;
 | 
			
		||||
        private int _zoomLevel                            = 100;
 | 
			
		||||
        private bool _muteNotifications;
 | 
			
		||||
        
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
        public int NotificationSoundVolume { get; set; } = 100;
 | 
			
		||||
        private string _notificationSoundPath;
 | 
			
		||||
 | 
			
		||||
        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 TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default;
 | 
			
		||||
 | 
			
		||||
        public string NotificationSoundPath{
 | 
			
		||||
            get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath;
 | 
			
		||||
            set => _notificationSoundPath = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool MuteNotifications{
 | 
			
		||||
            get => _muteNotifications;
 | 
			
		||||
 | 
			
		||||
            set{
 | 
			
		||||
                if (_muteNotifications != value){
 | 
			
		||||
                    _muteNotifications = value;
 | 
			
		||||
                    MuteToggled?.Invoke(this, EventArgs.Empty);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int ZoomLevel{
 | 
			
		||||
            get => _zoomLevel;
 | 
			
		||||
 | 
			
		||||
            set{
 | 
			
		||||
                if (_zoomLevel != value){
 | 
			
		||||
                    _zoomLevel = value;
 | 
			
		||||
                    ZoomLevelChanged?.Invoke(this, EventArgs.Empty);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        public TrayIcon.Behavior TrayBehavior{
 | 
			
		||||
            get => _trayBehavior;
 | 
			
		||||
 | 
			
		||||
            set{
 | 
			
		||||
                if (_trayBehavior != value){
 | 
			
		||||
                    _trayBehavior = value;
 | 
			
		||||
                    TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // EVENTS
 | 
			
		||||
        
 | 
			
		||||
        public event EventHandler MuteToggled;
 | 
			
		||||
        public event EventHandler ZoomLevelChanged;
 | 
			
		||||
        public event EventHandler TrayBehaviorChanged;
 | 
			
		||||
 | 
			
		||||
        // END OF CONFIG
 | 
			
		||||
        
 | 
			
		||||
        private readonly string file;
 | 
			
		||||
 | 
			
		||||
        private UserConfig(string file){
 | 
			
		||||
            this.file = file;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,19 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Bridge{
 | 
			
		||||
    sealed class CallbackBridge{
 | 
			
		||||
        private readonly Control owner;
 | 
			
		||||
        private readonly Action safeCallback;
 | 
			
		||||
 | 
			
		||||
        public CallbackBridge(Control owner, Action safeCallback){
 | 
			
		||||
            this.owner = owner;
 | 
			
		||||
            this.safeCallback = safeCallback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Trigger(){
 | 
			
		||||
            owner.InvokeSafe(safeCallback);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +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){
 | 
			
		||||
                return value ? "true;" : "false;";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            StringBuilder build = new StringBuilder().Append("(function(x){");
 | 
			
		||||
 | 
			
		||||
            build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
 | 
			
		||||
            
 | 
			
		||||
            if (environment == Environment.Browser){
 | 
			
		||||
                build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
 | 
			
		||||
                build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
 | 
			
		||||
                build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
 | 
			
		||||
                build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
 | 
			
		||||
                build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (environment == Environment.Notification){
 | 
			
		||||
                build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,156 +0,0 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Handling;
 | 
			
		||||
using TweetDuck.Core.Notification;
 | 
			
		||||
using TweetDuck.Core.Other;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using TweetDuck.Resources;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Bridge{
 | 
			
		||||
    sealed class TweetDeckBridge{
 | 
			
		||||
        public static string FontSize { get; private set; }
 | 
			
		||||
        public static string NotificationHeadLayout { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public static string LastHighlightedTweetUrl = string.Empty;
 | 
			
		||||
        public static string LastHighlightedQuoteUrl = string.Empty;
 | 
			
		||||
        private static string LastHighlightedTweetAuthors = string.Empty;
 | 
			
		||||
        private static string LastHighlightedTweetImages = string.Empty;
 | 
			
		||||
 | 
			
		||||
        public static string[] LastHighlightedTweetAuthorsArray => LastHighlightedTweetAuthors.Split(';');
 | 
			
		||||
        public static string[] LastHighlightedTweetImagesArray => LastHighlightedTweetImages.Split(';');
 | 
			
		||||
 | 
			
		||||
        private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
 | 
			
		||||
 | 
			
		||||
        public static void ResetStaticProperties(){
 | 
			
		||||
            FontSize = NotificationHeadLayout = null;
 | 
			
		||||
            LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
        public TweetDeckBridge(FormBrowser form, FormNotificationMain notification){
 | 
			
		||||
            this.form = form;
 | 
			
		||||
            this.notification = notification;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 SetLastRightClickInfo(string type, string link){
 | 
			
		||||
            form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){
 | 
			
		||||
            form.InvokeAsyncSafe(() => {
 | 
			
		||||
                LastHighlightedTweetUrl = tweetUrl;
 | 
			
		||||
                LastHighlightedQuoteUrl = quoteUrl;
 | 
			
		||||
                LastHighlightedTweetAuthors = authors;
 | 
			
		||||
                LastHighlightedTweetImages = imageList;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OpenContextMenu(){
 | 
			
		||||
            form.InvokeAsyncSafe(form.OpenContextMenu);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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.PlayNotificationSound();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void DisplayTooltip(string text, bool showInNotification){
 | 
			
		||||
            if (showInNotification){
 | 
			
		||||
                notification.InvokeAsyncSafe(() => notification.DisplayTooltip(text));
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                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);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void LoadNextNotification(){
 | 
			
		||||
            notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ScreenshotTweet(string html, int width, int height){
 | 
			
		||||
            form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,549 +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.Notification;
 | 
			
		||||
using TweetDuck.Core.Notification.Screenshot;
 | 
			
		||||
using TweetDuck.Core.Other;
 | 
			
		||||
using TweetDuck.Core.Other.Analytics;
 | 
			
		||||
using TweetDuck.Core.Other.Management;
 | 
			
		||||
using TweetDuck.Core.Other.Settings;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using TweetDuck.Plugins;
 | 
			
		||||
using TweetDuck.Plugins.Events;
 | 
			
		||||
using TweetDuck.Updates;
 | 
			
		||||
using TweetLib.Audio;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core{
 | 
			
		||||
    sealed partial class FormBrowser : Form{
 | 
			
		||||
        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 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 SoundNotification soundNotification;
 | 
			
		||||
        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, plugins, new TweetDeckBridge(this, notification));
 | 
			
		||||
            this.browser.PageLoaded += browser_PageLoaded;
 | 
			
		||||
 | 
			
		||||
            this.contextMenu = ContextMenuBrowser.CreateMenu(this);
 | 
			
		||||
 | 
			
		||||
            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();
 | 
			
		||||
                contextMenu.Dispose();
 | 
			
		||||
 | 
			
		||||
                notificationScreenshotManager?.Dispose();
 | 
			
		||||
                soundNotification?.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 = browser.CreateUpdateHandler(updaterSettings);
 | 
			
		||||
            this.updates.UpdateAccepted += updates_UpdateAccepted;
 | 
			
		||||
            this.updates.UpdateDismissed += updates_UpdateDismissed;
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
            if (Config.AllowDataCollection){
 | 
			
		||||
                analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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){
 | 
			
		||||
            TriggerAnalyticsEvent(AnalyticsFile.Event.MuteNotification);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 browser_PageLoaded(object sender, EventArgs e){
 | 
			
		||||
            if (Config.ShowDataCollectionNotification){
 | 
			
		||||
                this.InvokeAsyncSafe(() => {
 | 
			
		||||
                    if (!Config.FirstRun && Config.AllowDataCollection){
 | 
			
		||||
                        FormMessage form = new FormMessage("Anonymous Data Update", "Hi! You can now review your anonymous data report, and opt-out if you've changed your mind. Collected data will be used to focus development on most commonly used features. If you want to opt-out but still support the project, any feedback and donations are appreciated.", MessageBoxIcon.Information);
 | 
			
		||||
                        form.AddButton("OK", ControlType.Accept | ControlType.Focused);
 | 
			
		||||
 | 
			
		||||
                        Button btnReviewSettings = new Button{
 | 
			
		||||
                            Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
 | 
			
		||||
                            Font = SystemFonts.MessageBoxFont,
 | 
			
		||||
                            Location = new Point(9, 12),
 | 
			
		||||
                            Margin = new Padding(0, 0, 48, 0),
 | 
			
		||||
                            Size = new Size(160, 26),
 | 
			
		||||
                            Text = "Review Feedback Options",
 | 
			
		||||
                            UseVisualStyleBackColor = true
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                        btnReviewSettings.Click += (sender2, args2) => {
 | 
			
		||||
                            form.Close();
 | 
			
		||||
                            OpenSettings(typeof(TabSettingsFeedback));
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                        form.AddActionControl(btnReviewSettings);
 | 
			
		||||
                        ShowChildForm(form);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Config.ShowDataCollectionNotification = false;
 | 
			
		||||
                    Config.Save();
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        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){
 | 
			
		||||
                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_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 => {
 | 
			
		||||
                    if (update.DownloadStatus == UpdateDownloadStatus.Done){
 | 
			
		||||
                        UpdateInstallerPath = update.InstallerPath;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ForceClose();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void updates_UpdateDismissed(object sender, UpdateEventArgs e){
 | 
			
		||||
            this.InvokeAsyncSafe(() => {
 | 
			
		||||
                Config.DismissedUpdate = e.UpdateInfo.VersionTag;
 | 
			
		||||
                Config.Save();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void soundNotification_PlaybackError(object sender, PlaybackErrorEventArgs e){
 | 
			
		||||
            e.Ignore = true;
 | 
			
		||||
 | 
			
		||||
            using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, MessageBoxIcon.Error)){
 | 
			
		||||
                form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused);
 | 
			
		||||
 | 
			
		||||
                Button btnOpenSettings = form.AddButton("View Options");
 | 
			
		||||
                btnOpenSettings.Width += 16;
 | 
			
		||||
                btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y);
 | 
			
		||||
 | 
			
		||||
                if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){
 | 
			
		||||
                    OpenSettings(typeof(TabSettingsSounds));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void WndProc(ref Message m){
 | 
			
		||||
            if (isLoaded){
 | 
			
		||||
                if (m.Msg == Program.WindowRestoreMessage){
 | 
			
		||||
                    if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
 | 
			
		||||
                        trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                else if (m.Msg == Program.SubProcessMessage){
 | 
			
		||||
                    int processId = m.WParam.ToInt32();
 | 
			
		||||
 | 
			
		||||
                    if (WindowsUtils.IsChildProcess(processId)){ // child process is checked in two places for safety
 | 
			
		||||
                        BrowserProcesses.Link(m.LParam.ToInt32(), processId);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    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);
 | 
			
		||||
                    TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserExtraMouseButton);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                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(){
 | 
			
		||||
            browser.ReloadToTweetDeck();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerTweetScreenshot(){
 | 
			
		||||
            browser.TriggerTweetScreenshot();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ReloadColumns(){
 | 
			
		||||
            browser.ReloadColumns();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ApplyROT13(){
 | 
			
		||||
            browser.ApplyROT13();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
 | 
			
		||||
            analytics?.TriggerEvent(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // callback handlers
 | 
			
		||||
        
 | 
			
		||||
        public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){
 | 
			
		||||
            if (Config.FirstRun){
 | 
			
		||||
                Config.FirstRun = false;
 | 
			
		||||
                Config.AllowDataCollection = allowDataCollection;
 | 
			
		||||
                Config.ShowDataCollectionNotification = false;
 | 
			
		||||
                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;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    browser.RefreshMemoryTracker();
 | 
			
		||||
 | 
			
		||||
                    if (Config.AllowDataCollection){
 | 
			
		||||
                        if (analytics == null){
 | 
			
		||||
                            analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (analytics != null){
 | 
			
		||||
                        analytics.Dispose();
 | 
			
		||||
                        analytics = null;
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    if (form.ShouldReloadBrowser){
 | 
			
		||||
                        FormManager.TryFind<FormPlugins>()?.Close();
 | 
			
		||||
                        plugins.Reload(); // also reloads the browser
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        browser.UpdateProperties();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    notification.RequiresResize = true;
 | 
			
		||||
                    form.Dispose();
 | 
			
		||||
                };
 | 
			
		||||
                
 | 
			
		||||
                TriggerAnalyticsEvent(AnalyticsFile.Event.OpenOptions);
 | 
			
		||||
                ShowChildForm(form);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OpenAbout(){
 | 
			
		||||
            if (!FormManager.TryBringToFront<FormAbout>()){
 | 
			
		||||
                TriggerAnalyticsEvent(AnalyticsFile.Event.OpenAbout);
 | 
			
		||||
                ShowChildForm(new FormAbout());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OpenPlugins(){
 | 
			
		||||
            if (!FormManager.TryBringToFront<FormPlugins>()){
 | 
			
		||||
                TriggerAnalyticsEvent(AnalyticsFile.Event.OpenPlugins);
 | 
			
		||||
                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 PlayNotificationSound(){
 | 
			
		||||
            if (Config.NotificationSoundPath.Length == 0){
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (soundNotification == null){
 | 
			
		||||
                soundNotification = new SoundNotification();
 | 
			
		||||
                soundNotification.PlaybackError += soundNotification_PlaybackError;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            soundNotification.SetVolume(Config.NotificationSoundVolume);
 | 
			
		||||
            soundNotification.Play(Config.NotificationSoundPath);
 | 
			
		||||
 | 
			
		||||
            TriggerAnalyticsEvent(AnalyticsFile.Event.SoundNotification);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            TriggerAnalyticsEvent(AnalyticsFile.Event.VideoPlay);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            TriggerAnalyticsEvent(AnalyticsFile.Event.TweetDetail);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnTweetScreenshotReady(string html, int width, int height){
 | 
			
		||||
            if (notificationScreenshotManager == null){
 | 
			
		||||
                notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            notificationScreenshotManager.Trigger(html, width, height);
 | 
			
		||||
            TriggerAnalyticsEvent(AnalyticsFile.Event.TweetScreenshot);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,200 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using TweetDuck.Core.Bridge;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using TweetDuck.Core.Other;
 | 
			
		||||
 | 
			
		||||
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 static KeyValuePair<string, string> ContextInfo;
 | 
			
		||||
        private static bool IsLink => ContextInfo.Key == "link";
 | 
			
		||||
        private static bool IsImage => ContextInfo.Key == "image";
 | 
			
		||||
        private static bool IsVideo => ContextInfo.Key == "video";
 | 
			
		||||
 | 
			
		||||
        public static void SetContextInfo(string type, string link){
 | 
			
		||||
            ContextInfo = new KeyValuePair<string, string>(string.IsNullOrEmpty(link) ? null : type, link);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static string GetMediaLink(IContextMenuParams parameters){
 | 
			
		||||
            return IsImage || IsVideo ? ContextInfo.Value : parameters.SourceUrl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 MenuOpenDevTools    = (CefMenuCommand)26599;
 | 
			
		||||
        
 | 
			
		||||
        private string[] lastHighlightedTweetAuthors;
 | 
			
		||||
        private string[] lastHighlightedTweetImageList;
 | 
			
		||||
 | 
			
		||||
        public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
 | 
			
		||||
            if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
 | 
			
		||||
                lastHighlightedTweetAuthors = StringUtils.EmptyArray;
 | 
			
		||||
                lastHighlightedTweetImageList = StringUtils.EmptyArray;
 | 
			
		||||
                ContextInfo = default(KeyValuePair<string, string>);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray;
 | 
			
		||||
                lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool hasTweetImage = IsImage;
 | 
			
		||||
            bool hasTweetVideo = 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){
 | 
			
		||||
                model.AddItem(MenuViewImage, "View image in photo viewer");
 | 
			
		||||
                model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
 | 
			
		||||
                model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
 | 
			
		||||
                model.AddItem(MenuSaveMedia, TextSave("image"));
 | 
			
		||||
 | 
			
		||||
                if (lastHighlightedTweetImageList.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){
 | 
			
		||||
            switch(commandId){
 | 
			
		||||
                case MenuOpenLinkUrl:
 | 
			
		||||
                    OpenBrowser(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.LinkUrl);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuCopyLinkUrl:
 | 
			
		||||
                    SetClipboardText(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuCopyUsername:
 | 
			
		||||
                    Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
 | 
			
		||||
                    SetClipboardText(browserControl.AsControl(), match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuOpenMediaUrl:
 | 
			
		||||
                    OpenBrowser(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuCopyMediaUrl:
 | 
			
		||||
                    SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuViewImage:
 | 
			
		||||
                    string url = GetMediaLink(parameters);
 | 
			
		||||
                    string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url));
 | 
			
		||||
 | 
			
		||||
                    void ViewFile(){
 | 
			
		||||
                        string ext = Path.GetExtension(file);
 | 
			
		||||
 | 
			
		||||
                        if (TwitterUtils.ValidImageExtensions.Contains(ext)){
 | 
			
		||||
                            WindowsUtils.OpenAssociatedProgram(file);
 | 
			
		||||
                        }
 | 
			
		||||
                        else{
 | 
			
		||||
                            FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (File.Exists(file)){
 | 
			
		||||
                        ViewFile();
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, ex => {
 | 
			
		||||
                            FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuSaveMedia:
 | 
			
		||||
                    if (IsVideo){
 | 
			
		||||
                        TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MenuSaveTweetImages:
 | 
			
		||||
                    TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
 | 
			
		||||
                    break;
 | 
			
		||||
                    
 | 
			
		||||
                case MenuOpenDevTools:
 | 
			
		||||
                    browserControl.ShowDevTools();
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
 | 
			
		||||
            ContextInfo = default(KeyValuePair<string, string>);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 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,180 +0,0 @@
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Bridge;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Other.Analytics;
 | 
			
		||||
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 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;
 | 
			
		||||
 | 
			
		||||
        private string lastHighlightedTweetUrl;
 | 
			
		||||
        private string lastHighlightedQuoteUrl;
 | 
			
		||||
 | 
			
		||||
        public ContextMenuBrowser(FormBrowser form){
 | 
			
		||||
            this.form = form;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
 | 
			
		||||
            model.Remove(CefMenuCommand.Back);
 | 
			
		||||
            model.Remove(CefMenuCommand.Forward);
 | 
			
		||||
            model.Remove(CefMenuCommand.Print);
 | 
			
		||||
            model.Remove(CefMenuCommand.ViewSource);
 | 
			
		||||
            RemoveSeparatorIfLast(model);
 | 
			
		||||
 | 
			
		||||
            if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
 | 
			
		||||
                if (parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
 | 
			
		||||
                    model.AddSeparator();
 | 
			
		||||
                    model.AddItem(MenuInputApplyROT13, "Apply ROT13");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                model.AddSeparator();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
 | 
			
		||||
 | 
			
		||||
            lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl;
 | 
			
		||||
            lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
 | 
			
		||||
 | 
			
		||||
            if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
 | 
			
		||||
                lastHighlightedTweetUrl = string.Empty;
 | 
			
		||||
                lastHighlightedQuoteUrl = string.Empty;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
 | 
			
		||||
                model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
 | 
			
		||||
                model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
 | 
			
		||||
                model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
 | 
			
		||||
 | 
			
		||||
                if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){
 | 
			
		||||
                    model.AddSeparator();
 | 
			
		||||
                    model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
 | 
			
		||||
                    model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                model.AddSeparator();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
 | 
			
		||||
                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.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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, lastHighlightedTweetUrl);
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                case MenuCopyTweetUrl:
 | 
			
		||||
                    SetClipboardText(form, lastHighlightedTweetUrl);
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                case MenuScreenshotTweet:
 | 
			
		||||
                    form.InvokeAsyncSafe(form.TriggerTweetScreenshot);
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                case MenuOpenQuotedTweetUrl:
 | 
			
		||||
                    OpenBrowser(form, lastHighlightedQuoteUrl);
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                case MenuCopyQuotedTweetUrl:
 | 
			
		||||
                    SetClipboardText(form, lastHighlightedQuoteUrl);
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                case MenuInputApplyROT13:
 | 
			
		||||
                    form.InvokeAsyncSafe(form.ApplyROT13);
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return menu;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void ToggleMuteNotifications(){
 | 
			
		||||
            Program.UserConfig.MuteNotifications = !Program.UserConfig.MuteNotifications;
 | 
			
		||||
            Program.UserConfig.Save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
using CefSharp;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Handling{
 | 
			
		||||
    sealed class ContextMenuGuide : ContextMenuBase{
 | 
			
		||||
        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,99 +0,0 @@
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Notification;
 | 
			
		||||
using TweetDuck.Core.Other.Analytics;
 | 
			
		||||
 | 
			
		||||
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){
 | 
			
		||||
            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.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationContextMenu);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,25 +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("settings.a11y.animation_policy", Program.UserConfig.EnableAnimatedImages ? "allowed" : "none", out string _);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void IBrowserProcessHandler.OnContextInitialized(){
 | 
			
		||||
            UpdatePrefsInternal();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){}
 | 
			
		||||
        void IDisposable.Dispose(){}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Handling.General{
 | 
			
		||||
    sealed class FileDialogHandler : IDialogHandler{
 | 
			
		||||
        public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){
 | 
			
		||||
            CefFileDialogMode dialogType = mode & CefFileDialogMode.TypeMask;
 | 
			
		||||
 | 
			
		||||
            if (dialogType == CefFileDialogMode.Open || dialogType == CefFileDialogMode.OpenMultiple){
 | 
			
		||||
                string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter));
 | 
			
		||||
 | 
			
		||||
                using(OpenFileDialog dialog = new OpenFileDialog{
 | 
			
		||||
                    AutoUpgradeEnabled = true,
 | 
			
		||||
                    DereferenceLinks = true,
 | 
			
		||||
                    Multiselect = dialogType == CefFileDialogMode.OpenMultiple,
 | 
			
		||||
                    Title = "Open Files",
 | 
			
		||||
                    Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
 | 
			
		||||
                }){
 | 
			
		||||
                    if (dialog.ShowDialog() == DialogResult.OK){
 | 
			
		||||
                        string ext = Path.GetExtension(dialog.FileName);
 | 
			
		||||
                        callback.Continue(acceptFilters.FindIndex(filter => filter.Equals(ext, StringComparison.OrdinalIgnoreCase)), dialog.FileNames.ToList());
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        callback.Cancel();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    callback.Dispose();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                callback.Dispose();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,90 +0,0 @@
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using CefSharp.WinForms;
 | 
			
		||||
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){
 | 
			
		||||
            ((ChromiumWebBrowser)browserControl).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,31 +0,0 @@
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Handling.General{
 | 
			
		||||
    sealed class LifeSpanHandler : ILifeSpanHandler{
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
            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 void OnAfterCreated(IWebBrowser browserControl, IBrowser browser){}
 | 
			
		||||
 | 
			
		||||
        public bool DoClose(IWebBrowser browserControl, IBrowser browser){
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser){}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
using System.Security.Cryptography.X509Certificates;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Handling.General{
 | 
			
		||||
    abstract class RequestHandlerBase : IRequestHandler{
 | 
			
		||||
        // Browser
 | 
			
		||||
 | 
			
		||||
        public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){}
 | 
			
		||||
 | 
			
		||||
        public virtual void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){}
 | 
			
		||||
 | 
			
		||||
        public virtual bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect){
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url){
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Resources
 | 
			
		||||
 | 
			
		||||
        public virtual CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
 | 
			
		||||
            return CefReturnValue.Continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, ref string newUrl){}
 | 
			
		||||
 | 
			
		||||
        public virtual bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength){}
 | 
			
		||||
 | 
			
		||||
        // JavaScript & Plugins
 | 
			
		||||
 | 
			
		||||
        public virtual bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback){
 | 
			
		||||
            callback.Dispose();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath){}
 | 
			
		||||
 | 
			
		||||
        // Auth
 | 
			
		||||
 | 
			
		||||
        public virtual bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback){
 | 
			
		||||
            callback.Dispose();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual bool OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy, string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback){
 | 
			
		||||
            callback.Dispose();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public virtual bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback){
 | 
			
		||||
            callback.Dispose();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,46 +0,0 @@
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Notification;
 | 
			
		||||
using TweetDuck.Core.Other.Analytics;
 | 
			
		||||
 | 
			
		||||
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.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationKeyboardShortcut));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,18 +0,0 @@
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using TweetDuck.Core.Handling.General;
 | 
			
		||||
 | 
			
		||||
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.")){
 | 
			
		||||
                return CefReturnValue.Cancel;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return CefReturnValue.Continue;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,46 +0,0 @@
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
            #if DEBUG
 | 
			
		||||
            exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
 | 
			
		||||
            #endif
 | 
			
		||||
 | 
			
		||||
            exampleNotification = TweetNotification.Example(exampleTweetHTML, 95);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ShowExampleNotification(bool reset){
 | 
			
		||||
            if (reset){
 | 
			
		||||
                LoadTweet(exampleNotification);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                PrepareAndDisplayWindow();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            UpdateTitle();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,248 +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.Other.Management;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Notification{
 | 
			
		||||
    partial class FormNotificationBase : Form{
 | 
			
		||||
        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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        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()
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.browser.Dock = DockStyle.None;
 | 
			
		||||
            this.browser.ClientSize = ClientSize;
 | 
			
		||||
            this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
 | 
			
		||||
 | 
			
		||||
            #if DEBUG
 | 
			
		||||
            this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
 | 
			
		||||
            #endif
 | 
			
		||||
 | 
			
		||||
            DpiScale = this.GetDPIScale();
 | 
			
		||||
 | 
			
		||||
            DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
 | 
			
		||||
            handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
 | 
			
		||||
 | 
			
		||||
            Controls.Add(browser);
 | 
			
		||||
 | 
			
		||||
            Disposed += (sender, args) => {
 | 
			
		||||
                this.browser.Dispose();
 | 
			
		||||
                this.owner.FormClosed -= owner_FormClosed;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // 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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
 | 
			
		||||
            owner.TriggerAnalyticsEvent(e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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);
 | 
			
		||||
 | 
			
		||||
                int identifier = browser.GetBrowser().Identifier;
 | 
			
		||||
                Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // notification methods
 | 
			
		||||
 | 
			
		||||
        public virtual void HideNotification(){
 | 
			
		||||
            browser.Load("about:blank");
 | 
			
		||||
            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-hover" : string.Empty);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected virtual void LoadTweet(TweetNotification tweet){
 | 
			
		||||
            currentNotification = tweet;
 | 
			
		||||
            resourceHandler.SetHTML(GetTweetHTML(tweet));
 | 
			
		||||
            browser.Load(TwitterUtils.TweetDeckURL);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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(){
 | 
			
		||||
            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,279 +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.Analytics;
 | 
			
		||||
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{
 | 
			
		||||
        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(owner, this));
 | 
			
		||||
            browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
 | 
			
		||||
 | 
			
		||||
            browser.LoadingStateChanged += Browser_LoadingStateChanged;
 | 
			
		||||
            browser.FrameLoadEnd += Browser_FrameLoadEnd;
 | 
			
		||||
 | 
			
		||||
            mouseHookDelegate = MouseHookProc;
 | 
			
		||||
            Disposed += (sender, args) => StopMouseHook(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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(() => TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationExtraMouseButton));
 | 
			
		||||
                    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);
 | 
			
		||||
                plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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.Bridge.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,166 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using TweetDuck.Plugins;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Other.Analytics;
 | 
			
		||||
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;
 | 
			
		||||
            TriggerAnalyticsEvent(AnalyticsFile.Event.DesktopNotification);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,67 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Drawing.Imaging;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using TweetDuck.Core.Bridge;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
        public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
 | 
			
		||||
            this.plugins = pluginManager;
 | 
			
		||||
 | 
			
		||||
            browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
 | 
			
		||||
 | 
			
		||||
            browser.LoadingStateChanged += (sender, args) => {
 | 
			
		||||
                if (!args.IsLoading){
 | 
			
		||||
                    using(IFrame frame = args.Browser.MainFrame){
 | 
			
		||||
                        ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, document.getElementsByTagName('iframe').length ? 267 : 67)", "gen:screenshot");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        protected override string GetTweetHTML(TweetNotification tweet){
 | 
			
		||||
            string html = tweet.GenerateHtml("td-screenshot", false);
 | 
			
		||||
 | 
			
		||||
            foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
 | 
			
		||||
                html = injection.Inject(html);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return html;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
 | 
			
		||||
            LoadTweet(tweet);
 | 
			
		||||
            SetNotificationSize(width, height);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TakeScreenshot(){
 | 
			
		||||
            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,83 +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, int height){
 | 
			
		||||
            if (screenshot != null){
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            screenshot = new FormNotificationScreenshotable(Callback, owner, plugins);
 | 
			
		||||
            screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
 | 
			
		||||
            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,32 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using TweetLib.Audio;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Notification{
 | 
			
		||||
    sealed class SoundNotification : IDisposable{
 | 
			
		||||
        public string SupportedFormats => player.SupportedFormats;
 | 
			
		||||
        public event EventHandler<PlaybackErrorEventArgs> PlaybackError;
 | 
			
		||||
 | 
			
		||||
        private readonly AudioPlayer player;
 | 
			
		||||
 | 
			
		||||
        public SoundNotification(){
 | 
			
		||||
            this.player = AudioPlayer.New();
 | 
			
		||||
            this.player.PlaybackError += Player_PlaybackError;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Play(string file){
 | 
			
		||||
            player.Play(file);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool SetVolume(int volume){
 | 
			
		||||
            return player.SetVolume(volume);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){
 | 
			
		||||
            PlaybackError?.Invoke(this, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose(){
 | 
			
		||||
            player.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,80 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using TweetDuck.Core.Bridge;
 | 
			
		||||
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");
 | 
			
		||||
 | 
			
		||||
        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 = null, bool enableCustomCSS = true){
 | 
			
		||||
            StringBuilder build = new StringBuilder();
 | 
			
		||||
            build.Append("<!DOCTYPE html>");
 | 
			
		||||
            build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
 | 
			
		||||
            
 | 
			
		||||
            if (enableCustomCSS){
 | 
			
		||||
                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,103 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using TweetDuck.Data.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Analytics{
 | 
			
		||||
    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))
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public enum Event{
 | 
			
		||||
            OpenOptions, OpenPlugins, OpenAbout, OpenGuide,
 | 
			
		||||
            DesktopNotification, SoundNotification, MuteNotification,
 | 
			
		||||
            BrowserContextMenu, BrowserExtraMouseButton,
 | 
			
		||||
            NotificationContextMenu, NotificationExtraMouseButton, NotificationKeyboardShortcut,
 | 
			
		||||
            TweetScreenshot, TweetDetail, VideoPlay, GCReload
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // STATE PROPERTIES
 | 
			
		||||
        
 | 
			
		||||
        public DateTime LastDataCollection  { get; set; } = DateTime.MinValue;
 | 
			
		||||
        public string LastCollectionVersion { get; set; } = null;
 | 
			
		||||
        public string LastCollectionMessage { get; set; } = null;
 | 
			
		||||
 | 
			
		||||
        // USAGE DATA
 | 
			
		||||
 | 
			
		||||
        public int CountOpenOptions { get; private set; } = 0;
 | 
			
		||||
        public int CountOpenPlugins { get; private set; } = 0;
 | 
			
		||||
        public int CountOpenAbout   { get; private set; } = 0;
 | 
			
		||||
        public int CountOpenGuide   { get; private set; } = 0;
 | 
			
		||||
 | 
			
		||||
        public int CountDesktopNotifications { get; private set; } = 0;
 | 
			
		||||
        public int CountSoundNotifications   { get; private set; } = 0;
 | 
			
		||||
        public int CountMuteNotifications    { get; private set; } = 0;
 | 
			
		||||
        
 | 
			
		||||
        public int CountBrowserContextMenus           { get; private set; } = 0;
 | 
			
		||||
        public int CountBrowserExtraMouseButtons      { get; private set; } = 0;
 | 
			
		||||
        public int CountNotificationContextMenus      { get; private set; } = 0;
 | 
			
		||||
        public int CountNotificationExtraMouseButtons { get; private set; } = 0;
 | 
			
		||||
        public int CountNotificationKeyboardShortcuts { get; private set; } = 0;
 | 
			
		||||
 | 
			
		||||
        public int CountTweetScreenshots { get; private set; } = 0;
 | 
			
		||||
        public int CountTweetDetails     { get; private set; } = 0;
 | 
			
		||||
        public int CountVideoPlays       { get; private set; } = 0;
 | 
			
		||||
        public int CountGCReloads        { get; private set; } = 0;
 | 
			
		||||
 | 
			
		||||
        // END OF DATA
 | 
			
		||||
        
 | 
			
		||||
        private readonly string file;
 | 
			
		||||
        
 | 
			
		||||
        private AnalyticsFile(string file){
 | 
			
		||||
            this.file = file;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerEvent(Event e){
 | 
			
		||||
            switch(e){
 | 
			
		||||
                case Event.OpenOptions: ++CountOpenOptions; break;
 | 
			
		||||
                case Event.OpenPlugins: ++CountOpenPlugins; break;
 | 
			
		||||
                case Event.OpenAbout: ++CountOpenAbout; break;
 | 
			
		||||
                case Event.OpenGuide: ++CountOpenGuide; break;
 | 
			
		||||
 | 
			
		||||
                case Event.DesktopNotification: ++CountDesktopNotifications; break;
 | 
			
		||||
                case Event.SoundNotification: ++CountSoundNotifications; break;
 | 
			
		||||
                case Event.MuteNotification: ++CountMuteNotifications; break;
 | 
			
		||||
 | 
			
		||||
                case Event.BrowserContextMenu: ++CountBrowserContextMenus; break;
 | 
			
		||||
                case Event.BrowserExtraMouseButton: ++CountBrowserExtraMouseButtons; break;
 | 
			
		||||
                case Event.NotificationContextMenu: ++CountNotificationContextMenus; break;
 | 
			
		||||
                case Event.NotificationExtraMouseButton: ++CountNotificationExtraMouseButtons; break;
 | 
			
		||||
                case Event.NotificationKeyboardShortcut: ++CountNotificationKeyboardShortcuts; break;
 | 
			
		||||
 | 
			
		||||
                case Event.TweetScreenshot: ++CountTweetScreenshots; break;
 | 
			
		||||
                case Event.TweetDetail: ++CountTweetDetails; break;
 | 
			
		||||
                case Event.VideoPlay: ++CountVideoPlays; break;
 | 
			
		||||
                case Event.GCReload: ++CountGCReloads; break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Save(){
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return config;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
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(7);
 | 
			
		||||
        private static readonly Uri CollectionUrl = new Uri("https://tweetduck.chylex.com/breadcrumb/report");
 | 
			
		||||
        
 | 
			
		||||
        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.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(12), string.Empty);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                RestartTimer();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose(){
 | 
			
		||||
            if (saveTimer.Enabled){
 | 
			
		||||
                File.Save();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            currentTimer.Dispose();
 | 
			
		||||
            saveTimer.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TriggerEvent(AnalyticsFile.Event e){
 | 
			
		||||
            File.TriggerEvent(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, 1)*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);
 | 
			
		||||
                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;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    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,306 +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;
 | 
			
		||||
 | 
			
		||||
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) },
 | 
			
		||||
                { "Browser GC Reload"     , Bool(SysConfig.EnableBrowserGCReload) },
 | 
			
		||||
                { "Browser GC Threshold"  , Exact(SysConfig.BrowserMemoryThreshold) },
 | 
			
		||||
                0,
 | 
			
		||||
                { "Expand Links"             , Bool(UserConfig.ExpandLinksOnHover) },
 | 
			
		||||
                { "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) },
 | 
			
		||||
                { "Search In First Column"   , Bool(UserConfig.OpenSearchInFirstColumn) },
 | 
			
		||||
                { "Best Image Quality"       , Bool(UserConfig.BestImageQuality) },
 | 
			
		||||
                { "Spell Check"              , Bool(UserConfig.EnableSpellCheck) },
 | 
			
		||||
                { "Zoom"                     , Exact(UserConfig.ZoomLevel) },
 | 
			
		||||
                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 => plugin.Identifier)) },
 | 
			
		||||
                { "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(plugin => plugin.Identifier)) },
 | 
			
		||||
                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.CountOpenOptions, 4) },
 | 
			
		||||
                { "Opened Plugins"                   , LogRound(file.CountOpenPlugins, 4) },
 | 
			
		||||
                { "Opened About"                     , LogRound(file.CountOpenAbout, 4) },
 | 
			
		||||
                { "Opened Guide"                     , LogRound(file.CountOpenGuide, 4) },
 | 
			
		||||
                { "Desktop Notifications"            , LogRound(file.CountDesktopNotifications, 5) },
 | 
			
		||||
                { "Sound Notifications"              , LogRound(file.CountSoundNotifications, 5) },
 | 
			
		||||
                { "Mute Notifications"               , LogRound(file.CountMuteNotifications, 2) },
 | 
			
		||||
                { "Browser Context Menus"            , LogRound(file.CountBrowserContextMenus, 2) },
 | 
			
		||||
                { "Browser Extra Mouse Buttons"      , LogRound(file.CountBrowserExtraMouseButtons, 2) },
 | 
			
		||||
                { "Notification Context Menus"       , LogRound(file.CountNotificationContextMenus, 2) },
 | 
			
		||||
                { "Notification Extra Mouse Buttons" , LogRound(file.CountNotificationExtraMouseButtons, 2) },
 | 
			
		||||
                { "Notification Keyboard Shortcuts"  , LogRound(file.CountNotificationKeyboardShortcuts, 2) },
 | 
			
		||||
                { "Tweet Screenshots"                , LogRound(file.CountTweetScreenshots, 2) },
 | 
			
		||||
                { "Tweet Details"                    , LogRound(file.CountTweetDetails, 2) },
 | 
			
		||||
                { "Video Plays"                      , LogRound(file.CountVideoPlays, 4) },
 | 
			
		||||
                { "GC Reloads"                       , LogRound(file.CountGCReloads, 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 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 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);
 | 
			
		||||
                    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,40 +0,0 @@
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
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));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,246 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
  <!-- 
 | 
			
		||||
    Microsoft ResX Schema 
 | 
			
		||||
    
 | 
			
		||||
    Version 2.0
 | 
			
		||||
    
 | 
			
		||||
    The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
    that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
    various data types are done through the TypeConverter classes 
 | 
			
		||||
    associated with the data types.
 | 
			
		||||
    
 | 
			
		||||
    Example:
 | 
			
		||||
    
 | 
			
		||||
    ... ado.net/XML headers & schema ...
 | 
			
		||||
    <resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
    <resheader name="version">2.0</resheader>
 | 
			
		||||
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
 | 
			
		||||
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
        <value>[base64 mime encoded serialized .NET Framework object]</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
 | 
			
		||||
        <comment>This is a comment</comment>
 | 
			
		||||
    </data>
 | 
			
		||||
                
 | 
			
		||||
    There are any number of "resheader" rows that contain simple 
 | 
			
		||||
    name/value pairs.
 | 
			
		||||
    
 | 
			
		||||
    Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
    type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
    text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
    Classes that don't support this are serialized and stored with the 
 | 
			
		||||
    mimetype set.
 | 
			
		||||
    
 | 
			
		||||
    The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
    ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
    extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
    
 | 
			
		||||
    Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
    that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
    read any of the formats listed below.
 | 
			
		||||
    
 | 
			
		||||
    mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
    value   : The object must be serialized with 
 | 
			
		||||
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
            : and then encoded with base64 encoding.
 | 
			
		||||
    
 | 
			
		||||
    mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
    value   : The object must be serialized with 
 | 
			
		||||
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
            : and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
    mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
    value   : The object must be serialized into a byte array 
 | 
			
		||||
            : using a System.ComponentModel.TypeConverter
 | 
			
		||||
            : and then encoded with base64 encoding.
 | 
			
		||||
    -->
 | 
			
		||||
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
 | 
			
		||||
    <xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
      <xsd:complexType>
 | 
			
		||||
        <xsd:choice maxOccurs="unbounded">
 | 
			
		||||
          <xsd:element name="metadata">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:sequence>
 | 
			
		||||
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
 | 
			
		||||
              </xsd:sequence>
 | 
			
		||||
              <xsd:attribute name="name" use="required" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute name="type" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute name="mimetype" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute ref="xml:space" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
          <xsd:element name="assembly">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:attribute name="alias" type="xsd:string" />
 | 
			
		||||
              <xsd:attribute name="name" type="xsd:string" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
          <xsd:element name="data">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:sequence>
 | 
			
		||||
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
              </xsd:sequence>
 | 
			
		||||
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
 | 
			
		||||
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
              <xsd:attribute ref="xml:space" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
          <xsd:element name="resheader">
 | 
			
		||||
            <xsd:complexType>
 | 
			
		||||
              <xsd:sequence>
 | 
			
		||||
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
              </xsd:sequence>
 | 
			
		||||
              <xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
            </xsd:complexType>
 | 
			
		||||
          </xsd:element>
 | 
			
		||||
        </xsd:choice>
 | 
			
		||||
      </xsd:complexType>
 | 
			
		||||
    </xsd:element>
 | 
			
		||||
  </xsd:schema>
 | 
			
		||||
  <resheader name="resmimetype">
 | 
			
		||||
    <value>text/microsoft-resx</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <resheader name="version">
 | 
			
		||||
    <value>2.0</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <resheader name="reader">
 | 
			
		||||
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <resheader name="writer">
 | 
			
		||||
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
  </resheader>
 | 
			
		||||
  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
 | 
			
		||||
  <data name="pictureLogo.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
    <value>
 | 
			
		||||
        iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH
 | 
			
		||||
        DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp
 | 
			
		||||
        bGUAAEjHnZZ3VFTXFofPvXd6oc0wAlKG3rvAANJ7k15FYZgZYCgDDjM0sSGiAhFFRJoiSFDEgNFQJFZE
 | 
			
		||||
        sRAUVLAHJAgoMRhFVCxvRtaLrqy89/Ly++Osb+2z97n77L3PWhcAkqcvl5cGSwGQyhPwgzyc6RGRUXTs
 | 
			
		||||
        AIABHmCAKQBMVka6X7B7CBDJy82FniFyAl8EAfB6WLwCcNPQM4BOB/+fpFnpfIHomAARm7M5GSwRF4g4
 | 
			
		||||
        JUuQLrbPipgalyxmGCVmvihBEcuJOWGRDT77LLKjmNmpPLaIxTmns1PZYu4V8bZMIUfEiK+ICzO5nCwR
 | 
			
		||||
        3xKxRoowlSviN+LYVA4zAwAUSWwXcFiJIjYRMYkfEuQi4uUA4EgJX3HcVyzgZAvEl3JJS8/hcxMSBXQd
 | 
			
		||||
        li7d1NqaQffkZKVwBALDACYrmcln013SUtOZvBwAFu/8WTLi2tJFRbY0tba0NDQzMv2qUP91829K3NtF
 | 
			
		||||
        ehn4uWcQrf+L7a/80hoAYMyJarPziy2uCoDOLQDI3fti0zgAgKSobx3Xv7oPTTwviQJBuo2xcVZWlhGX
 | 
			
		||||
        wzISF/QP/U+Hv6GvvmckPu6P8tBdOfFMYYqALq4bKy0lTcinZ6QzWRy64Z+H+B8H/nUeBkGceA6fwxNF
 | 
			
		||||
        hImmjMtLELWbx+YKuGk8Opf3n5r4D8P+pMW5FonS+BFQY4yA1HUqQH7tBygKESDR+8Vd/6NvvvgwIH55
 | 
			
		||||
        4SqTi3P/7zf9Z8Gl4iWDm/A5ziUohM4S8jMX98TPEqABAUgCKpAHykAd6ABDYAasgC1wBG7AG/iDEBAJ
 | 
			
		||||
        VgMWSASpgA+yQB7YBApBMdgJ9oBqUAcaQTNoBcdBJzgFzoNL4Bq4AW6D+2AUTIBnYBa8BgsQBGEhMkSB
 | 
			
		||||
        5CEVSBPSh8wgBmQPuUG+UBAUCcVCCRAPEkJ50GaoGCqDqqF6qBn6HjoJnYeuQIPQXWgMmoZ+h97BCEyC
 | 
			
		||||
        qbASrAUbwwzYCfaBQ+BVcAK8Bs6FC+AdcCXcAB+FO+Dz8DX4NjwKP4PnEIAQERqiihgiDMQF8UeikHiE
 | 
			
		||||
        j6xHipAKpAFpRbqRPuQmMorMIG9RGBQFRUcZomxRnqhQFAu1BrUeVYKqRh1GdaB6UTdRY6hZ1Ec0Ga2I
 | 
			
		||||
        1kfboL3QEegEdBa6EF2BbkK3oy+ib6Mn0K8xGAwNo42xwnhiIjFJmLWYEsw+TBvmHGYQM46Zw2Kx8lh9
 | 
			
		||||
        rB3WH8vECrCF2CrsUexZ7BB2AvsGR8Sp4Mxw7rgoHA+Xj6vAHcGdwQ3hJnELeCm8Jt4G749n43PwpfhG
 | 
			
		||||
        fDf+On4Cv0CQJmgT7AghhCTCJkIloZVwkfCA8JJIJKoRrYmBRC5xI7GSeIx4mThGfEuSIemRXEjRJCFp
 | 
			
		||||
        B+kQ6RzpLuklmUzWIjuSo8gC8g5yM/kC+RH5jQRFwkjCS4ItsUGiRqJDYkjiuSReUlPSSXK1ZK5kheQJ
 | 
			
		||||
        yeuSM1J4KS0pFymm1HqpGqmTUiNSc9IUaVNpf+lU6RLpI9JXpKdksDJaMm4ybJkCmYMyF2TGKQhFneJC
 | 
			
		||||
        YVE2UxopFykTVAxVm+pFTaIWU7+jDlBnZWVkl8mGyWbL1sielh2lITQtmhcthVZKO04bpr1borTEaQln
 | 
			
		||||
        yfYlrUuGlszLLZVzlOPIFcm1yd2WeydPl3eTT5bfJd8p/1ABpaCnEKiQpbBf4aLCzFLqUtulrKVFS48v
 | 
			
		||||
        vacIK+opBimuVTyo2K84p6Ss5KGUrlSldEFpRpmm7KicpFyufEZ5WoWiYq/CVSlXOavylC5Ld6Kn0Cvp
 | 
			
		||||
        vfRZVUVVT1Whar3qgOqCmrZaqFq+WpvaQ3WCOkM9Xr1cvUd9VkNFw08jT6NF454mXpOhmai5V7NPc15L
 | 
			
		||||
        Wytca6tWp9aUtpy2l3audov2Ax2yjoPOGp0GnVu6GF2GbrLuPt0berCehV6iXo3edX1Y31Kfq79Pf9AA
 | 
			
		||||
        bWBtwDNoMBgxJBk6GWYathiOGdGMfI3yjTqNnhtrGEcZ7zLuM/5oYmGSYtJoct9UxtTbNN+02/R3Mz0z
 | 
			
		||||
        llmN2S1zsrm7+QbzLvMXy/SXcZbtX3bHgmLhZ7HVosfig6WVJd+y1XLaSsMq1qrWaoRBZQQwShiXrdHW
 | 
			
		||||
        ztYbrE9Zv7WxtBHYHLf5zdbQNtn2iO3Ucu3lnOWNy8ft1OyYdvV2o/Z0+1j7A/ajDqoOTIcGh8eO6o5s
 | 
			
		||||
        xybHSSddpySno07PnU2c+c7tzvMuNi7rXM65Iq4erkWuA24ybqFu1W6P3NXcE9xb3Gc9LDzWepzzRHv6
 | 
			
		||||
        eO7yHPFS8mJ5NXvNelt5r/Pu9SH5BPtU+zz21fPl+3b7wX7efrv9HqzQXMFb0ekP/L38d/s/DNAOWBPw
 | 
			
		||||
        YyAmMCCwJvBJkGlQXlBfMCU4JvhI8OsQ55DSkPuhOqHC0J4wybDosOaw+XDX8LLw0QjjiHUR1yIVIrmR
 | 
			
		||||
        XVHYqLCopqi5lW4r96yciLaILoweXqW9KnvVldUKq1NWn46RjGHGnIhFx4bHHol9z/RnNjDn4rziauNm
 | 
			
		||||
        WS6svaxnbEd2OXuaY8cp40zG28WXxU8l2CXsTphOdEisSJzhunCruS+SPJPqkuaT/ZMPJX9KCU9pS8Wl
 | 
			
		||||
        xqae5Mnwknm9acpp2WmD6frphemja2zW7Fkzy/fhN2VAGasyugRU0c9Uv1BHuEU4lmmfWZP5Jiss60S2
 | 
			
		||||
        dDYvuz9HL2d7zmSue+63a1FrWWt78lTzNuWNrXNaV78eWh+3vmeD+oaCDRMbPTYe3kTYlLzpp3yT/LL8
 | 
			
		||||
        V5vDN3cXKBVsLBjf4rGlpVCikF84stV2a9021DbutoHt5turtn8sYhddLTYprih+X8IqufqN6TeV33za
 | 
			
		||||
        Eb9joNSydP9OzE7ezuFdDrsOl0mX5ZaN7/bb3VFOLy8qf7UnZs+VimUVdXsJe4V7Ryt9K7uqNKp2Vr2v
 | 
			
		||||
        Tqy+XeNc01arWLu9dn4fe9/Qfsf9rXVKdcV17w5wD9yp96jvaNBqqDiIOZh58EljWGPft4xvm5sUmoqb
 | 
			
		||||
        PhziHRo9HHS4t9mqufmI4pHSFrhF2DJ9NProje9cv+tqNWytb6O1FR8Dx4THnn4f+/3wcZ/jPScYJ1p/
 | 
			
		||||
        0Pyhtp3SXtQBdeR0zHYmdo52RXYNnvQ+2dNt293+o9GPh06pnqo5LXu69AzhTMGZT2dzz86dSz83cz7h
 | 
			
		||||
        /HhPTM/9CxEXbvUG9g5c9Ll4+ZL7pQt9Tn1nL9tdPnXF5srJq4yrndcsr3X0W/S3/2TxU/uA5UDHdavr
 | 
			
		||||
        XTesb3QPLh88M+QwdP6m681Lt7xuXbu94vbgcOjwnZHokdE77DtTd1PuvriXeW/h/sYH6AdFD6UeVjxS
 | 
			
		||||
        fNTws+7PbaOWo6fHXMf6Hwc/vj/OGn/2S8Yv7ycKnpCfVEyqTDZPmU2dmnafvvF05dOJZ+nPFmYKf5X+
 | 
			
		||||
        tfa5zvMffnP8rX82YnbiBf/Fp99LXsq/PPRq2aueuYC5R69TXy/MF72Rf3P4LeNt37vwd5MLWe+x7ys/
 | 
			
		||||
        6H7o/ujz8cGn1E+f/gUDmPP8usTo0wAAAAlwSFlzAAAuIgAALiIBquLdkgAAEVpJREFUeF7tXQl0VNd5
 | 
			
		||||
        Thq3Tk7tc+qmiZMm7YmdJj1OT3Gd0kTMG3mJnZQEJzaSjXETx0lqNwt2jWscQGixBAJLSMhgA8YYbIyQ
 | 
			
		||||
        hHYJI4lFGMwioQWxyewgMdoXtKFd4u/337lPjDRv9pk3g+A75zsMWv577/fd+9/l3Rl9johu0Y/U/OIt
 | 
			
		||||
        6sfrLwIYT2de+SJ4F/j34LfB+8Ap4L9J8uvvgfy9b4DfBO8EPy9DBBwC3gCI93fgDHAuuA7cDVaDzWAX
 | 
			
		||||
        OAAOgcNgP9gN8vc+A4vB9eBr4M/Be8GAMiPgDIBAXwVDwbXgEbAPJC+RDToGvgfOBr8mi/UbAsIACMFp
 | 
			
		||||
        ZRaYC7aBWuL5gh3gdvBZ8C5ZHV3hVwPQaM7hS0ETqCWQnqwDE8Apsnq6wC8GoJEPgFtAzt9aYviTPJ9k
 | 
			
		||||
        gkGyuj6FrgagUd8CeVIcBLUaH0gcATeD35XV9wl0MQCN4OXjn8EroFZjA5m8qooE/1o2x6vwuQGouAEs
 | 
			
		||||
        A7UadyPxOPgj2SyvwacGoMILwRsh3ThLTksx4BdkEz2GTwxABb8C8pJSqxGTgTvAb8jmegSvG4CK/QvI
 | 
			
		||||
        O1Wtik8mXgSnyma7Da8agAo9Auq5kfI3+Shkhmy+W/CaAajI42CvrNjNRJ7jZksZXIZXDEAFWPxA3FTp
 | 
			
		||||
        xVHQLRM8NgAFPwTejD1/InkH/biUxWl4ZAAK5LP3mynnO+JV0KWJ2W0DUNCXwVOy4Fu8zhrQ6SWqJwbk
 | 
			
		||||
        WxQa2Mxop6fSWyk0rYlCUhspZEs9zdxSJ8iv+WuhW5vpqYw27d93nXvB26RUduGWAQg+36KwwCULv7VF
 | 
			
		||||
        iDx3Wx1tPNxCBy92UU17P7VdHaLWniE619JLu8900Nv7m+jFLBgCM9gs/l3NmCqlqU+nt+D/msYtk3LZ
 | 
			
		||||
        hcsGIPB/gPaPF7jyKrW+rwNZnJCUBoraUU9H63po9No1NM8++odGac/ZDno5r45C2QitESGF59gv5dVT
 | 
			
		||||
        clUHzc5gEyb8XOaVa6DDsyMU67wBCHg7WCULsEkezi9/jEptbfKLCdzrn0s30b5znWiS6xgeuUaby1so
 | 
			
		||||
        NKVubDSwGZzCOG3NzTdRQXW7+NnM4+b2atUDPAPeIeXTBEK4ZMA8i+Ca5Ir+LrOOOvtHKGkfcit6kp4m
 | 
			
		||||
        sBh/zDGRqaMfzfEMh5CuZiXXUOgWE72Ua6L1Jc10qqmXRkbNo4nT2PMw2sHcESvl0wTCOGcAAv0DyFtv
 | 
			
		||||
        rULGyAKsO9SMUERD6EnRO+t1M4F76++zTNTUNSjK9wYutvVhzujTTGGrMG/wqNCqiwX5IsA/SxmtgDBO
 | 
			
		||||
        G7DBIqhN8oR3suEqQpnRh7watcOcU31pAvfCWUgZ1Y3Xy/YlCk+Z28rlqrTTvkwpoxUQyrEBCPCvoONz
 | 
			
		||||
        fVTg1xkm6hkYQajr4Mktdpd5uecrE9jg5IpWWaJvwRN1SHKtaA/PCfMLmyiq2DxPaNUN5KMKg5RzHBDO
 | 
			
		||||
        KQNSLYLZJFfgVSz3tMBDeM0BrMNlr9H6fXfJ8V7AErK7f1iW5hsMjYxSSnkjPbnxLP1vbq2YE04399KV
 | 
			
		||||
        vmGak9/kqF0FUs5xQFj7BuAXvwM6ddDGOfiNnQ0IYxvbTrYjVWDiwkrFW6OB83DaEd/3/obOfiq71Ekt
 | 
			
		||||
        3YOkzgg8NUTxPOdgLpiV1TE6K7vzASnrGBDCoQF8b0cz6ESyqDG77BvAOI2VxJycy7bX2q4QJoYgDVxo
 | 
			
		||||
        7ZPR9QVv4OzNb7OyOll8mpnWTNPXnlwnZR0DQtg2AAHuAPnCkmbwieQREFZQjzCOwfPChlJs/3mt7cFo
 | 
			
		||||
        YAPn5NTRsFwa6on3kII05zUIjt4uXv9icx099u5pCn7rKBkSynoMC1K/LuUVQBi7BswcF9gBWYzfZ5vE
 | 
			
		||||
        RsZZnG/powjsVsXc4IYR/DtLix2POm+C9wGrPjWfK1nWl3v6MxA+FB1xxgeX6OF3TpKSdASsIiMMMK48
 | 
			
		||||
        TkpM4RwprwDC2TUgTw3uFFEZXgo2dA4glPNgu8pruyms0HxAJnaWThrBP7v2oHnfoQd48xWBelqmHe7t
 | 
			
		||||
        Is2kNNJ/rj9HD648RsoKC+FVwgBj/MGDUl4BhNQ2AIH/FuTLq1aNtkfuFYWfXUEo18FGHKvrofg9DfRs
 | 
			
		||||
        mknEcjQq2ABejeiFyEKTWXyUbU4z7fSLj0z06NpTELnKLLyl6BOZVDVgDM+8R8ps14AQy4Y6S1fmAXvg
 | 
			
		||||
        npZ/oo0iiuro2VSYkdIgVhocX0zc0hQ2YDWWt3phIdrGp5+hW1vpZxsv0SOcZrR6uy1iFAQvLvqDlNmu
 | 
			
		||||
        Ae9aCus0IQzn8/LL3QjnHbAZpZe6RE9fsL2O/jvTJFIdlzMz2URRhZflT/oer+fX0E/ePUXBttKMI3Ia
 | 
			
		||||
        enNfhpRZ2wAI+XnQ4amnLXLamPdxHQ1i4+IL8AqqHvMMp6vdZ65Q8en2sXW5r/HbjZVYzVRoi+sMYZwx
 | 
			
		||||
        saxWWbj1Swhn04B/BPkQSVNgu+TUAHKefB/LzMkE3s3PXgfxsbLRFNd5XlMWZT6AkDYN+JmVsE5QLEPz
 | 
			
		||||
        WujXGY0iZ3N60GOHqheuDgzT4++U80SqJarz5DQUXfAcQlobACE5/fAb4jRFtkdOPatLWqmrf4RSKlvo
 | 
			
		||||
        1fzL9MSGM7TuQJ04R7nR0dDRSw8mlGqL6gpXniDjkp2xyoKULyCslQH81MutCZgNWLz7+qaI83Jz9yAm
 | 
			
		||||
        0A5q6/HeGb2/cPxyBxnivWEAT8T7c4Mjc+5EWCsD/gbkt4JqimyP6rGA+sRosqHgWD0Zlh/WFtUVsgHL
 | 
			
		||||
        S44aI3O/hrBWBvCboN273cwTMJaHvEKZjEgsOkNKogcrIJViJVTeaIzefi/CWhnA7zJvGiesC+TVT85x
 | 
			
		||||
        8wPryYYXNpl3upqiukQYsKKyR1my63sIa2UAv3WU3xelKbAj8k71lXz/nE76Em09A/Tjt5B+PF0BqUw6
 | 
			
		||||
        MogN2RSEtjKAHz+6twdg8k6Yz4NOuXceFKgorm7yTv5XmXRkVIk78H2EtjLgfpBv+moL7AR5Mv5NRp1X
 | 
			
		||||
        byf4GzH5n3kn/6vkkbS8dCpCWxnAnzrikQFMPiRbWFAnbkXc6OAN2BNreAPmjfwvyQbEH9I0gD/yxfM3
 | 
			
		||||
        W/BxRFqTuBrYOzj+lsSNht0nG72bfphJVddgwL8jvJUBHk3C4yhNeCXfRJfaPL+p5i+8ln4Cq59KbSHd
 | 
			
		||||
        ZVLloLJs7/0Ib2UAL0NbxgnpCWEC75Bnp5rog7IW6ujz7dURb+NsYxdvmswpQ0tItyiWod1KTJHmMpQ3
 | 
			
		||||
        YvxhR9qCukiekGenN4tHjU9+VENPbzpLcbtq6GS9954X+BLLtp/y7uTLNG/EmozRBd9GEVYG8Gf37LEU
 | 
			
		||||
        0RNy7/+o8gqdae6jQxc76eCFTqqo7aTGzsBPSRdbeuihRG/3fpANiC85qkTkaB5F8Adr8CeaaArqKsUj
 | 
			
		||||
        yiJ9by14C2/keXnpqXIV5pTYPblB8zZ9BcVYGcDH0f9nKaJH5I1ZSgMduNCF8DcOjtS0k+L13C/JBize
 | 
			
		||||
        EWeYn3wbihpvgHwgw+/71RbUDfIo+FNuvdWl3UAFn+a+KM59vLzyUWl+IPMbFHVd97EXZgPuAb3yKSfq
 | 
			
		||||
        ZSUeBbFFNSgi8PHRwRrzut8Xvd/Ma8FR+XYfSf4FyJ+PoymqM1TvRIakNtP098/TQ5h4psWXUnzBaRQT
 | 
			
		||||
        uDhZ1yknXi/uei0JHZTlpbVBr6z7IoqzNkAlRHR9Ira4E/lEcv31O5Hq9Q2Qe9bCrJPU3T+EYgILPajT
 | 
			
		||||
        r96v8F3qYSL/G5bszuKOjiLHeP2FBETkj5G0FlmDapp5amsrzfiwhh5ZPeFOpCXxNSWxnGauKaed2OI7
 | 
			
		||||
        8eZF3RCejXqjbj5MPWYDInJfZo1RpF0D+EOX7L4nbOzqdWqTSDN8J3Kst2sVbkF+sMGj4fmNlZR2uFas
 | 
			
		||||
        uYflg3t/mLJy11lf530Q6/+3qoaw+vkOa4xibRvAgMj8oaYTRLe+eu3UnUgt8mhgIxLKKBhLvsdXltDc
 | 
			
		||||
        zRV0qUXfXfKHBy7pID4ongUfKpfyOmXAf00U3ubVa0+IOCzAM+vKqOxCG6qhHzbziodvOvhq0rUk0k9Q
 | 
			
		||||
        WPpLUl4BVMG2AbOyu+4CW1j8Jx1dvXaHLDx6/09XHabU0loaHNb32cG7n1zQT3w+fkgs75r6Pwl3S3kF
 | 
			
		||||
        UA3bBjB+vvH8CnH1WqYLzeCukEcNVhnc459YXSaGf0evvk/O2OjovGqZdnQQn/l2NQVF5G2Qso4B1bFv
 | 
			
		||||
        gCEs/X6kmhHNoCp5NHBDBPm1ShYbTKwQPZ1723T09tfTT4hnrL2D+h9N17Zdpd9uNI88UUet9viCGAFB
 | 
			
		||||
        r29WpKxjQJXsG8AwLtv3sbhOpxUYjZi+5hg9s+E4zVpfRT9ZeZh+nFRKP11ZRqFry+mFD6soKreakg/V
 | 
			
		||||
        iPzuz/V/TqVJ1E2s83UVX9yE2294baPV3y5AtRwboIRnT0OgUavAko++fZR++cFxKj7VQgPDI9Q3OCKE
 | 
			
		||||
        HhgKjLOfc03d9ErqcX1TjiVhADLJdCnnOKB6jg1gGOP2pwsntQpAbxLzw/JSCsuqprNocCCgoaOPEgrP
 | 
			
		||||
        0MOJJeZjZT17vUo++Vz6yU4poxVQTScNWJT5XfSefvNmQqMgJhuBhirxJTQ/4wRVXGqnUT9c0Drf3E3L
 | 
			
		||||
        IfyPON2Ina0fer0gVoxJVcNBr31g9QZtFaiucwYwjEt2xdqcCywpRgRWOph0f7m+QuT/y+1XfbrD5Ztr
 | 
			
		||||
        24/V05wtR8Wmzr/CS7JWscWrpHyaQNWdN0AJS7vDmFh2zmYqmkiZmnjF8WBCibhbuWbPeTqMyZiXnp4Y
 | 
			
		||||
        wgdnfF2cl7Gc3x9Db+ccr/sEa4u87l9R3hAcnvllKZ8m0BTnDWAo4VmPGldyIXZSkRalGdwzeWTwaonP
 | 
			
		||||
        gRZlV9O6vRcoq8JEu7E0LTnXSidMHSKNHK29Qofwf76Xw99/Dz8XiRXV7z48QjPeLkOqg+gwN2BEtyRv
 | 
			
		||||
        WCNyQqRsNuGyAQxlya44p1KRPaqGQDxhCnrvGFlYlRZf558TcwxP+IEmuCXFI8eda6RcduGWAcaFaX9p
 | 
			
		||||
        jD/0qdOp6GYia7K8pCp4UfqXpFx24ZYBDGVB6jeNKypMt0ywoPm8p9UYkW3zI8omwm0DGIbXN//AuKKy
 | 
			
		||||
        VxSsVaGbiWYNhrBpdenPnHhkAAMTzUwUPnxTm6C2PWrbc1IWp+GxAQxDePavxlXkpiLazO2OKfqjlMMl
 | 
			
		||||
        eMUABruPCt1kI4HbiuVm9PY/SRlchtcMYBjCMkJQMcwJN8HEzB0tqWrIGF3wvGy+W/CqAQwlLH2aknDY
 | 
			
		||||
        xGthzYpPBnIHW1HRYozMeUw222143QDGtD8n3yP2CcKESZaSeAOaUFpuDM+8TzbXI/jEAIZxYepfGaIL
 | 
			
		||||
        48yVngQpiVMO2qHEFq81Lkr32p819JkBKgyLMmeYD/Bu4NHAdU8sr1Ui85+SzfIafG4Aw7go4y5jbPEy
 | 
			
		||||
        8TzhRpobxMitGjYu3fOOMWzrV2VzvApdDFDxw3mbpyhL92aL09RATkuibhitb35agIn2h7L6PoGuBqgw
 | 
			
		||||
        hOc8Zow7UCQayyMiIPYOnONlmow7sEeJzPPoL+Q5C78YoAI76KBpMTtXK4nlLcZV6HVqz9MUyEfkMpkr
 | 
			
		||||
        KnuMSz/ZpETkPCyrpwv8aoCKH8zdcPe0yG0vYum6X8wTLIivRoZczQgmVQ2izBIluvBVZeHWb8nq6IqA
 | 
			
		||||
        MMASWDX9k+GN7X8wxO5JU5aXnjeLxoKxIVI4YYzKCQKrX7cUWpiJf/n7CWW1yrJ9WcaYwpeU8Cynj419
 | 
			
		||||
        hYAzwBJB8zbdHhyVd78xpuA5ZfHOWEzgWeixFVgSNiBldGG0DICj4uE7PyEzvx4Q6YQ/ECm+5Cgm0jxl
 | 
			
		||||
        8Y5lyhsfPw9zpyrzU3zyp8ndRUAbwFDrZViQdlvQ/NQ7kKPvNkYX3GuIKbpPWfrJFP7IFyXu4FQYM1V5
 | 
			
		||||
        c//3lWV7p/A70JWobfcq4dlfN4Zn3WmYn3y7GseSgYCxulhW7Bb1p+YXb1Ev0uf+H9A3E1Z4VJUaAAAA
 | 
			
		||||
        AElFTkSuQmCC
 | 
			
		||||
</value>
 | 
			
		||||
  </data>
 | 
			
		||||
</root>
 | 
			
		||||
@@ -1,117 +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.Other.Analytics;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
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.TriggerAnalyticsEvent(AnalyticsFile.Event.OpenGuide);
 | 
			
		||||
                    new FormGuide(url, owner).Show(owner);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                guide.Reload(url);
 | 
			
		||||
                guide.Activate();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private readonly ChromiumWebBrowser browser;
 | 
			
		||||
 | 
			
		||||
        private FormGuide(string url, Form owner){
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
 | 
			
		||||
            Text = Program.BrandName+" Guide";
 | 
			
		||||
 | 
			
		||||
            if (owner != null){
 | 
			
		||||
                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(),
 | 
			
		||||
                JsDialogHandler = new JavaScriptDialogHandler(),
 | 
			
		||||
                LifeSpanHandler = new LifeSpanHandler(),
 | 
			
		||||
                RequestHandler = new RequestHandlerBrowser()
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            browser.LoadingStateChanged += browser_LoadingStateChanged;
 | 
			
		||||
            browser.FrameLoadStart += browser_FrameLoadStart;
 | 
			
		||||
            
 | 
			
		||||
            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 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 = 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,178 +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, updates));
 | 
			
		||||
            AddButton("System Tray", () => new TabSettingsTray());
 | 
			
		||||
            AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
 | 
			
		||||
            AddButton("Sounds", () => new TabSettingsSounds());
 | 
			
		||||
            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){
 | 
			
		||||
                    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){
 | 
			
		||||
            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,27 +0,0 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Management{
 | 
			
		||||
    static class BrowserProcesses{
 | 
			
		||||
        private static readonly Dictionary<int, int> PIDs = new Dictionary<int, int>();
 | 
			
		||||
 | 
			
		||||
        public static void Link(int identifier, int pid){
 | 
			
		||||
            PIDs[identifier] = pid;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void Forget(int identifier){
 | 
			
		||||
            PIDs.Remove(identifier);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        public static Process FindProcess(IBrowser browser){
 | 
			
		||||
            if (PIDs.TryGetValue(browser.Identifier, out int pid) && WindowsUtils.IsChildProcess(pid)){ // child process is checked in two places for safety
 | 
			
		||||
                return Process.GetProcessById(pid);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,93 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Timers;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using CefSharp.WinForms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using Timer = System.Timers.Timer;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Management{
 | 
			
		||||
    sealed class MemoryUsageTracker : IDisposable{
 | 
			
		||||
        private const int IntervalMemoryCheck = 60000*30; // 30 minutes
 | 
			
		||||
        private const int IntervalCleanupAttempt = 60000*5; // 5 minutes
 | 
			
		||||
 | 
			
		||||
        private readonly string script;
 | 
			
		||||
        private readonly Timer timer;
 | 
			
		||||
        private FormBrowser owner;
 | 
			
		||||
        private IBrowser browser;
 | 
			
		||||
 | 
			
		||||
        private long threshold;
 | 
			
		||||
        private bool needsCleanup;
 | 
			
		||||
 | 
			
		||||
        public MemoryUsageTracker(string cleanupFunctionName){
 | 
			
		||||
            this.script = $"window.{cleanupFunctionName} && window.{cleanupFunctionName}()";
 | 
			
		||||
 | 
			
		||||
            this.timer = new Timer{ Interval = IntervalMemoryCheck };
 | 
			
		||||
            this.timer.Elapsed += timer_Elapsed;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Start(ChromiumWebBrowser control, int thresholdMB){
 | 
			
		||||
            Stop();
 | 
			
		||||
 | 
			
		||||
            this.owner = (FormBrowser)control.Parent; // TODO ugly
 | 
			
		||||
            this.browser = control.GetBrowser();
 | 
			
		||||
            this.threshold = thresholdMB*1024L*1024L;
 | 
			
		||||
            this.timer.SynchronizingObject = owner;
 | 
			
		||||
            this.timer.Start();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Stop(){
 | 
			
		||||
            timer.Stop();
 | 
			
		||||
            timer.SynchronizingObject = null;
 | 
			
		||||
            owner = null;
 | 
			
		||||
            browser = null;
 | 
			
		||||
            SetNeedsCleanup(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose(){
 | 
			
		||||
            timer.SynchronizingObject = null;
 | 
			
		||||
            timer.Dispose();
 | 
			
		||||
            owner = null;
 | 
			
		||||
            browser = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void SetNeedsCleanup(bool value){
 | 
			
		||||
            if (needsCleanup != value){
 | 
			
		||||
                needsCleanup = value;
 | 
			
		||||
                timer.Interval = value ? IntervalCleanupAttempt : IntervalMemoryCheck; // restarts timer
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void timer_Elapsed(object sender, ElapsedEventArgs e){
 | 
			
		||||
            if (owner == null || browser == null){
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if (needsCleanup){
 | 
			
		||||
                if (!owner.ContainsFocus){
 | 
			
		||||
                    using(IFrame frame = browser.MainFrame){
 | 
			
		||||
                        frame.EvaluateScriptAsync(script).ContinueWith(task => {
 | 
			
		||||
                            JavascriptResponse response = task.Result;
 | 
			
		||||
 | 
			
		||||
                            if (response.Success && (response.Result as bool? ?? false)){
 | 
			
		||||
                                SetNeedsCleanup(false);
 | 
			
		||||
                                owner.InvokeAsyncSafe(() => owner.TriggerAnalyticsEvent(Analytics.AnalyticsFile.Event.GCReload));
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                try{
 | 
			
		||||
                    using(Process process = BrowserProcesses.FindProcess(browser)){
 | 
			
		||||
                        if (process?.PrivateMemorySize64 > threshold){
 | 
			
		||||
                            SetNeedsCleanup(true);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }catch{
 | 
			
		||||
                    // ignore I guess?
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,181 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using TweetLib.Communication;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Management{
 | 
			
		||||
    sealed class VideoPlayer : IDisposable{
 | 
			
		||||
        public bool Running{
 | 
			
		||||
            get{
 | 
			
		||||
                if (currentProcess == null){
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                currentProcess.Refresh();
 | 
			
		||||
                return !currentProcess.HasExited;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public event EventHandler ProcessExited;
 | 
			
		||||
 | 
			
		||||
        private readonly Form owner;
 | 
			
		||||
        private string lastUrl;
 | 
			
		||||
        private string lastUsername;
 | 
			
		||||
 | 
			
		||||
        private Process currentProcess;
 | 
			
		||||
        private DuplexPipe.Server currentPipe;
 | 
			
		||||
        private bool isClosing;
 | 
			
		||||
 | 
			
		||||
        public VideoPlayer(Form owner){
 | 
			
		||||
            this.owner = owner;
 | 
			
		||||
            this.owner.FormClosing += owner_FormClosing;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Launch(string url, string username){
 | 
			
		||||
            if (Running){
 | 
			
		||||
                Destroy();
 | 
			
		||||
                isClosing = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            lastUrl = url;
 | 
			
		||||
            lastUsername = username;
 | 
			
		||||
            
 | 
			
		||||
            try{
 | 
			
		||||
                currentPipe = DuplexPipe.CreateServer();
 | 
			
		||||
                currentPipe.DataIn += currentPipe_DataIn;
 | 
			
		||||
 | 
			
		||||
                if ((currentProcess = Process.Start(new ProcessStartInfo{
 | 
			
		||||
                    FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
 | 
			
		||||
                    Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
 | 
			
		||||
                    UseShellExecute = false,
 | 
			
		||||
                    RedirectStandardOutput = true
 | 
			
		||||
                })) != null){
 | 
			
		||||
                    currentProcess.EnableRaisingEvents = true;
 | 
			
		||||
                    currentProcess.Exited += process_Exited;
 | 
			
		||||
 | 
			
		||||
                    #if DEBUG
 | 
			
		||||
                    currentProcess.BeginOutputReadLine();
 | 
			
		||||
                    currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data);
 | 
			
		||||
                    #endif
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                currentPipe.DisposeToken();
 | 
			
		||||
            }catch(Exception e){
 | 
			
		||||
                Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SendKeyEvent(Keys key){
 | 
			
		||||
            currentPipe?.Write("key", ((int)key).ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void currentPipe_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":
 | 
			
		||||
                        TwitterUtils.DownloadVideo(lastUrl, lastUsername);
 | 
			
		||||
                        break;
 | 
			
		||||
 | 
			
		||||
                    case "rip":
 | 
			
		||||
                        currentPipe.Dispose();
 | 
			
		||||
                        currentPipe = null;
 | 
			
		||||
                    
 | 
			
		||||
                        currentProcess.Dispose();
 | 
			
		||||
                        currentProcess = null;
 | 
			
		||||
 | 
			
		||||
                        isClosing = false;
 | 
			
		||||
                        TriggerProcessExitEventUnsafe();
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Close(){
 | 
			
		||||
            if (currentProcess != null){
 | 
			
		||||
                if (isClosing){
 | 
			
		||||
                    Destroy();
 | 
			
		||||
                    isClosing = false;
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
                    isClosing = true;
 | 
			
		||||
                    currentProcess.Exited -= process_Exited;
 | 
			
		||||
                    currentPipe.Write("die");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose(){
 | 
			
		||||
            ProcessExited = null;
 | 
			
		||||
 | 
			
		||||
            isClosing = true;
 | 
			
		||||
            Destroy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Destroy(){
 | 
			
		||||
            if (currentProcess != null){
 | 
			
		||||
                try{
 | 
			
		||||
                    currentProcess.Kill();
 | 
			
		||||
                }catch{
 | 
			
		||||
                    // kill me instead then
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                currentProcess.Dispose();
 | 
			
		||||
                currentProcess = null;
 | 
			
		||||
                
 | 
			
		||||
                currentPipe.Dispose();
 | 
			
		||||
                currentPipe = null;
 | 
			
		||||
 | 
			
		||||
                TriggerProcessExitEventUnsafe();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void owner_FormClosing(object sender, FormClosingEventArgs e){
 | 
			
		||||
            if (currentProcess != null){
 | 
			
		||||
                currentProcess.Exited -= process_Exited;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void process_Exited(object sender, EventArgs e){
 | 
			
		||||
            int exitCode = currentProcess.ExitCode;
 | 
			
		||||
 | 
			
		||||
            currentProcess.Dispose();
 | 
			
		||||
            currentProcess = null;
 | 
			
		||||
 | 
			
		||||
            currentPipe.Dispose();
 | 
			
		||||
            currentPipe = 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(lastUrl);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    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(lastUrl);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void TriggerProcessExitEventUnsafe(){
 | 
			
		||||
            ProcessExited?.Invoke(this, EventArgs.Empty);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
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{
 | 
			
		||||
                foreach(Panel panel in Controls.OfType<Panel>()){
 | 
			
		||||
                    foreach(Control control in panel.Controls){
 | 
			
		||||
                        yield return control;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,203 +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.Dock = System.Windows.Forms.DockStyle.Bottom;
 | 
			
		||||
            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.Dock = System.Windows.Forms.DockStyle.Bottom;
 | 
			
		||||
            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,221 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Configuration;
 | 
			
		||||
using TweetDuck.Core.Other.Settings.Export;
 | 
			
		||||
using TweetDuck.Plugins;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
 | 
			
		||||
    sealed partial class DialogSettingsManage : Form{
 | 
			
		||||
        private enum State{
 | 
			
		||||
            Deciding, Reset, Import, Export
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ExportFileFlags Flags{
 | 
			
		||||
            get => selectedFlags;
 | 
			
		||||
 | 
			
		||||
            set{
 | 
			
		||||
                // this will call events and SetFlag, which also updates the UI
 | 
			
		||||
                cbConfig.Checked = value.HasFlag(ExportFileFlags.UserConfig);
 | 
			
		||||
                cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
 | 
			
		||||
                cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool ShouldReloadBrowser { get; private set; }
 | 
			
		||||
        
 | 
			
		||||
        private readonly PluginManager plugins;
 | 
			
		||||
        private State currentState;
 | 
			
		||||
 | 
			
		||||
        private ExportManager importManager;
 | 
			
		||||
        private ExportFileFlags selectedFlags = ExportFileFlags.None;
 | 
			
		||||
 | 
			
		||||
        public DialogSettingsManage(PluginManager plugins){
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
 | 
			
		||||
            this.plugins = plugins;
 | 
			
		||||
            this.currentState = State.Deciding;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void radioDecision_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            btnContinue.Enabled = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void cbConfig_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            SetFlag(ExportFileFlags.UserConfig, cbConfig.Checked);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void cbSession_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            SetFlag(ExportFileFlags.Session, cbSession.Checked);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void cbPluginData_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            SetFlag(ExportFileFlags.PluginData, cbPluginData.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";
 | 
			
		||||
                        Flags = ExportFileFlags.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 ExportManager(file, plugins);
 | 
			
		||||
                        currentState = State.Import;
 | 
			
		||||
 | 
			
		||||
                        Text = "Import Profile";
 | 
			
		||||
                        Flags = importManager.GetImportFlags();
 | 
			
		||||
 | 
			
		||||
                        cbConfig.Enabled = cbConfig.Checked;
 | 
			
		||||
                        cbSession.Enabled = cbSession.Checked;
 | 
			
		||||
                        cbPluginData.Enabled = cbPluginData.Checked;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Export
 | 
			
		||||
                    else if (radioExport.Checked){
 | 
			
		||||
                        currentState = State.Export;
 | 
			
		||||
 | 
			
		||||
                        Text = "Export Profile";
 | 
			
		||||
                        btnContinue.Text = "Export Profile";
 | 
			
		||||
                        Flags = ExportFileFlags.All & ~ExportFileFlags.Session;
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    // Continue...
 | 
			
		||||
                    panelDecision.Visible = false;
 | 
			
		||||
                    panelExport.Visible = true;
 | 
			
		||||
                    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 (Flags.HasFlag(ExportFileFlags.UserConfig)){
 | 
			
		||||
                            Program.ResetConfig();
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
 | 
			
		||||
                            try{
 | 
			
		||||
                                File.Delete(Program.SystemConfigFilePath);
 | 
			
		||||
                            }catch(Exception ex){
 | 
			
		||||
                                Program.Reporter.HandleException("System Config Reset Error", "Could not delete system config.", true, ex);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (Flags.HasFlag(ExportFileFlags.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 (Flags.HasFlag(ExportFileFlags.Session)){
 | 
			
		||||
                            Program.Restart(Arguments.ArgDeleteCookies);
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
 | 
			
		||||
                            Program.Restart();
 | 
			
		||||
                        }
 | 
			
		||||
                        else{
 | 
			
		||||
                            ShouldReloadBrowser = true;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        DialogResult = DialogResult.OK;
 | 
			
		||||
                        Close();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case State.Import:
 | 
			
		||||
                    if (importManager.Import(Flags)){
 | 
			
		||||
                        Program.UserConfig.Reload();
 | 
			
		||||
 | 
			
		||||
                        if (importManager.IsRestarting){
 | 
			
		||||
                            if (Flags.HasFlag(ExportFileFlags.Session)){
 | 
			
		||||
                                Program.Restart(Arguments.ArgImportCookies);
 | 
			
		||||
                            }
 | 
			
		||||
                            else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
 | 
			
		||||
                                Program.Restart();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else{
 | 
			
		||||
                            ShouldReloadBrowser = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing TweetDuck profile.", true, importManager.LastException);
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    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();
 | 
			
		||||
 | 
			
		||||
                    ExportManager manager = new ExportManager(file, plugins);
 | 
			
		||||
 | 
			
		||||
                    if (!manager.Export(Flags)){
 | 
			
		||||
                        Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, manager.LastException);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    DialogResult = DialogResult.OK;
 | 
			
		||||
                    Close();
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void btnCancel_Click(object sender, EventArgs e){
 | 
			
		||||
            DialogResult = DialogResult.Cancel;
 | 
			
		||||
            Close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void SetFlag(ExportFileFlags flag, bool enable){
 | 
			
		||||
            selectedFlags = enable ? selectedFlags | flag : selectedFlags & ~flag;
 | 
			
		||||
            btnContinue.Enabled = selectedFlags != ExportFileFlags.None;
 | 
			
		||||
 | 
			
		||||
            if (currentState == State.Import){
 | 
			
		||||
                btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile";
 | 
			
		||||
            }
 | 
			
		||||
            else if (currentState == State.Reset){
 | 
			
		||||
                btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Restore && Restart" : "Restore Defaults";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,87 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Configuration;
 | 
			
		||||
using TweetDuck.Data;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
 | 
			
		||||
    sealed partial class DialogSettingsRestart : Form{
 | 
			
		||||
        private const string DefaultLocale = "en-US";
 | 
			
		||||
 | 
			
		||||
        public CommandLineArgs Args { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public DialogSettingsRestart(CommandLineArgs currentArgs){
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
 | 
			
		||||
            try{
 | 
			
		||||
                object[] locales = Directory.EnumerateFiles(Path.Combine(Program.ProgramPath, "locales"), "*.pak", SearchOption.TopDirectoryOnly).Select(Path.GetFileNameWithoutExtension).ToArray<object>();
 | 
			
		||||
                comboLocale.Items.AddRange(locales);
 | 
			
		||||
            }catch{
 | 
			
		||||
                comboLocale.Items.Add(DefaultLocale);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
 | 
			
		||||
            cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
 | 
			
		||||
            comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
 | 
			
		||||
 | 
			
		||||
            cbLogging.CheckedChanged += control_Change;
 | 
			
		||||
            cbDebugUpdates.CheckedChanged += control_Change;
 | 
			
		||||
            comboLocale.SelectedValueChanged += 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 (cbDebugUpdates.Checked){
 | 
			
		||||
                Args.AddFlag(Arguments.ArgDebugUpdates);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            string locale = comboLocale.SelectedItem as string;
 | 
			
		||||
 | 
			
		||||
            if (!string.IsNullOrWhiteSpace(locale) && locale != DefaultLocale){
 | 
			
		||||
                Args.SetValue(Arguments.ArgLocale, locale);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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,13 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Settings.Export{
 | 
			
		||||
    [Flags]
 | 
			
		||||
    enum ExportFileFlags{
 | 
			
		||||
        None = 0,
 | 
			
		||||
        UserConfig = 1,
 | 
			
		||||
        SystemConfig = 2, // TODO implement later
 | 
			
		||||
        Session = 4,
 | 
			
		||||
        PluginData = 8,
 | 
			
		||||
        All = UserConfig|SystemConfig|Session|PluginData
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,202 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using TweetDuck.Data;
 | 
			
		||||
using TweetDuck.Plugins;
 | 
			
		||||
using TweetDuck.Plugins.Enums;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Settings.Export{
 | 
			
		||||
    sealed class ExportManager{
 | 
			
		||||
        private static readonly string CookiesPath = Path.Combine(Program.StoragePath, "Cookies");
 | 
			
		||||
        private static readonly string TempCookiesPath = Path.Combine(Program.StoragePath, "CookiesTmp");
 | 
			
		||||
 | 
			
		||||
        public bool IsRestarting { get; private set; }
 | 
			
		||||
        public Exception LastException { get; private set; }
 | 
			
		||||
 | 
			
		||||
        private readonly string file;
 | 
			
		||||
        private readonly PluginManager plugins;
 | 
			
		||||
 | 
			
		||||
        public ExportManager(string file, PluginManager plugins){
 | 
			
		||||
            this.file = file;
 | 
			
		||||
            this.plugins = plugins;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Export(ExportFileFlags flags){
 | 
			
		||||
            try{
 | 
			
		||||
                using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
 | 
			
		||||
                    if (flags.HasFlag(ExportFileFlags.UserConfig)){
 | 
			
		||||
                        stream.WriteFile("config", Program.UserConfigFilePath);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (flags.HasFlag(ExportFileFlags.SystemConfig)){
 | 
			
		||||
                        stream.WriteFile("system", Program.SystemConfigFilePath);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (flags.HasFlag(ExportFileFlags.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 (flags.HasFlag(ExportFileFlags.Session)){
 | 
			
		||||
                        stream.WriteFile("cookies", CookiesPath);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    stream.Flush();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }catch(Exception e){
 | 
			
		||||
                LastException = e;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ExportFileFlags GetImportFlags(){
 | 
			
		||||
            ExportFileFlags flags = ExportFileFlags.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":
 | 
			
		||||
                                flags |= ExportFileFlags.UserConfig;
 | 
			
		||||
                                break;
 | 
			
		||||
 | 
			
		||||
                            case "system":
 | 
			
		||||
                                flags |= ExportFileFlags.SystemConfig;
 | 
			
		||||
                                break;
 | 
			
		||||
 | 
			
		||||
                            case "plugin.config":
 | 
			
		||||
                            case "plugin.data":
 | 
			
		||||
                                flags |= ExportFileFlags.PluginData;
 | 
			
		||||
                                break;
 | 
			
		||||
 | 
			
		||||
                            case "cookies":
 | 
			
		||||
                                flags |= ExportFileFlags.Session;
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }catch(Exception e){
 | 
			
		||||
                LastException = e;
 | 
			
		||||
                flags = ExportFileFlags.None;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return flags;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Import(ExportFileFlags flags){
 | 
			
		||||
            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 (flags.HasFlag(ExportFileFlags.UserConfig)){
 | 
			
		||||
                                    entry.WriteToFile(Program.UserConfigFilePath);
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                break;
 | 
			
		||||
 | 
			
		||||
                            case "system":
 | 
			
		||||
                                if (flags.HasFlag(ExportFileFlags.SystemConfig)){
 | 
			
		||||
                                    entry.WriteToFile(Program.SystemConfigFilePath);
 | 
			
		||||
                                    IsRestarting = true;
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                break;
 | 
			
		||||
 | 
			
		||||
                            case "plugin.config":
 | 
			
		||||
                                if (flags.HasFlag(ExportFileFlags.PluginData)){
 | 
			
		||||
                                    entry.WriteToFile(Program.PluginConfigFilePath);
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                break;
 | 
			
		||||
 | 
			
		||||
                            case "plugin.data":
 | 
			
		||||
                                if (flags.HasFlag(ExportFileFlags.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 (flags.HasFlag(ExportFileFlags.Session)){
 | 
			
		||||
                                    entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath));
 | 
			
		||||
                                    IsRestarting = true;
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (missingPlugins.Count > 0){
 | 
			
		||||
                    FormMessage.Information("Importing TweetDuck Profile", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }catch(Exception e){
 | 
			
		||||
                LastException = 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){
 | 
			
		||||
            return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
 | 
			
		||||
                Full = fullPath,
 | 
			
		||||
                Relative = fullPath.Substring(root.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) // strip leading separator character
 | 
			
		||||
            }) : Enumerable.Empty<PathInfo>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private sealed class PathInfo{
 | 
			
		||||
            public string Full { get; set; }
 | 
			
		||||
            public string Relative { get; set; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										290
									
								
								Core/Other/Settings/TabSettingsAdvanced.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										290
									
								
								Core/Other/Settings/TabSettingsAdvanced.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -1,290 +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.numMemoryThreshold = new TweetDuck.Core.Controls.NumericUpDownEx();
 | 
			
		||||
            this.checkBrowserGCReload = new System.Windows.Forms.CheckBox();
 | 
			
		||||
            this.labelApp = new System.Windows.Forms.Label();
 | 
			
		||||
            this.panelApp = new System.Windows.Forms.Panel();
 | 
			
		||||
            this.labelPerformance = new System.Windows.Forms.Label();
 | 
			
		||||
            this.panelPerformance = new System.Windows.Forms.Panel();
 | 
			
		||||
            this.labelMemoryUsage = new System.Windows.Forms.Label();
 | 
			
		||||
            this.panelConfiguration = new System.Windows.Forms.Panel();
 | 
			
		||||
            this.labelConfiguration = new System.Windows.Forms.Label();
 | 
			
		||||
            ((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).BeginInit();
 | 
			
		||||
            this.panelApp.SuspendLayout();
 | 
			
		||||
            this.panelPerformance.SuspendLayout();
 | 
			
		||||
            this.panelConfiguration.SuspendLayout();
 | 
			
		||||
            this.SuspendLayout();
 | 
			
		||||
            // 
 | 
			
		||||
            // btnClearCache
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnClearCache.Location = new System.Drawing.Point(5, 28);
 | 
			
		||||
            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 = 1;
 | 
			
		||||
            this.btnClearCache.Text = "Clear Cache (calculating)";
 | 
			
		||||
            this.toolTip.SetToolTip(this.btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources.");
 | 
			
		||||
            this.btnClearCache.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkHardwareAcceleration
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkHardwareAcceleration.AutoSize = true;
 | 
			
		||||
            this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 5);
 | 
			
		||||
            this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
 | 
			
		||||
            this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
 | 
			
		||||
            this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
 | 
			
		||||
            this.checkHardwareAcceleration.TabIndex = 0;
 | 
			
		||||
            this.checkHardwareAcceleration.Text = "Hardware Acceleration";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile.");
 | 
			
		||||
            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.toolTip.SetToolTip(this.btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
 | 
			
		||||
            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.toolTip.SetToolTip(this.btnEditCSS, "Set custom CSS for browser and notification windows.");
 | 
			
		||||
            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.toolTip.SetToolTip(this.btnRestartArgs, "Restarts the program with customizable\r\ncommand line 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.toolTip.SetToolTip(this.btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch.");
 | 
			
		||||
            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.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located.");
 | 
			
		||||
            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.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located.");
 | 
			
		||||
            this.btnOpenDataFolder.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // numMemoryThreshold
 | 
			
		||||
            // 
 | 
			
		||||
            this.numMemoryThreshold.Increment = new decimal(new int[] {
 | 
			
		||||
            50,
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            0});
 | 
			
		||||
            this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82);
 | 
			
		||||
            this.numMemoryThreshold.Maximum = 2000;
 | 
			
		||||
            this.numMemoryThreshold.Minimum = 200;
 | 
			
		||||
            this.numMemoryThreshold.Name = "numMemoryThreshold";
 | 
			
		||||
            this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20);
 | 
			
		||||
            this.numMemoryThreshold.TabIndex = 4;
 | 
			
		||||
            this.numMemoryThreshold.TextSuffix = " MB";
 | 
			
		||||
            this.toolTip.SetToolTip(this.numMemoryThreshold, "Minimum amount of memory usage by the browser process to trigger the cleanup.\r\nThis is not a limit, the usage is allowed to exceed this value.");
 | 
			
		||||
            this.numMemoryThreshold.Value = 400;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkBrowserGCReload
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkBrowserGCReload.AutoSize = true;
 | 
			
		||||
            this.checkBrowserGCReload.Location = new System.Drawing.Point(6, 84);
 | 
			
		||||
            this.checkBrowserGCReload.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
 | 
			
		||||
            this.checkBrowserGCReload.Name = "checkBrowserGCReload";
 | 
			
		||||
            this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17);
 | 
			
		||||
            this.checkBrowserGCReload.TabIndex = 3;
 | 
			
		||||
            this.checkBrowserGCReload.Text = "Enable Browser Memory Threshold";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkBrowserGCReload, "Automatically reloads TweetDeck to save memory. This option only works if\r\nthe browser is in a \'default state\', i.e. all modals and drawers are closed, and\r\nall columns are scrolled to top. This option will not be exported in a profile.");
 | 
			
		||||
            this.checkBrowserGCReload.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(6, 8);
 | 
			
		||||
            this.labelApp.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
 | 
			
		||||
            this.labelApp.Name = "labelApp";
 | 
			
		||||
            this.labelApp.Size = new System.Drawing.Size(38, 20);
 | 
			
		||||
            this.labelApp.TabIndex = 0;
 | 
			
		||||
            this.labelApp.Text = "App";
 | 
			
		||||
            // 
 | 
			
		||||
            // panelApp
 | 
			
		||||
            // 
 | 
			
		||||
            this.panelApp.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
 | 
			
		||||
            | System.Windows.Forms.AnchorStyles.Right)));
 | 
			
		||||
            this.panelApp.Controls.Add(this.btnOpenDataFolder);
 | 
			
		||||
            this.panelApp.Controls.Add(this.btnOpenAppFolder);
 | 
			
		||||
            this.panelApp.Controls.Add(this.btnRestart);
 | 
			
		||||
            this.panelApp.Controls.Add(this.btnRestartArgs);
 | 
			
		||||
            this.panelApp.Location = new System.Drawing.Point(9, 31);
 | 
			
		||||
            this.panelApp.Name = "panelApp";
 | 
			
		||||
            this.panelApp.Size = new System.Drawing.Size(322, 59);
 | 
			
		||||
            this.panelApp.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(6, 114);
 | 
			
		||||
            this.labelPerformance.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
 | 
			
		||||
            this.labelPerformance.Name = "labelPerformance";
 | 
			
		||||
            this.labelPerformance.Size = new System.Drawing.Size(100, 20);
 | 
			
		||||
            this.labelPerformance.TabIndex = 2;
 | 
			
		||||
            this.labelPerformance.Text = "Performance";
 | 
			
		||||
            // 
 | 
			
		||||
            // panelPerformance
 | 
			
		||||
            // 
 | 
			
		||||
            this.panelPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
 | 
			
		||||
            | System.Windows.Forms.AnchorStyles.Right)));
 | 
			
		||||
            this.panelPerformance.Controls.Add(this.checkBrowserGCReload);
 | 
			
		||||
            this.panelPerformance.Controls.Add(this.numMemoryThreshold);
 | 
			
		||||
            this.panelPerformance.Controls.Add(this.labelMemoryUsage);
 | 
			
		||||
            this.panelPerformance.Controls.Add(this.checkHardwareAcceleration);
 | 
			
		||||
            this.panelPerformance.Controls.Add(this.btnClearCache);
 | 
			
		||||
            this.panelPerformance.Location = new System.Drawing.Point(9, 137);
 | 
			
		||||
            this.panelPerformance.Name = "panelPerformance";
 | 
			
		||||
            this.panelPerformance.Size = new System.Drawing.Size(322, 105);
 | 
			
		||||
            this.panelPerformance.TabIndex = 3;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelMemoryUsage
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelMemoryUsage.AutoSize = true;
 | 
			
		||||
            this.labelMemoryUsage.Location = new System.Drawing.Point(3, 66);
 | 
			
		||||
            this.labelMemoryUsage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
 | 
			
		||||
            this.labelMemoryUsage.Name = "labelMemoryUsage";
 | 
			
		||||
            this.labelMemoryUsage.Size = new System.Drawing.Size(78, 13);
 | 
			
		||||
            this.labelMemoryUsage.TabIndex = 2;
 | 
			
		||||
            this.labelMemoryUsage.Text = "Memory Usage";
 | 
			
		||||
            // 
 | 
			
		||||
            // panelConfiguration
 | 
			
		||||
            // 
 | 
			
		||||
            this.panelConfiguration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
 | 
			
		||||
            | System.Windows.Forms.AnchorStyles.Right)));
 | 
			
		||||
            this.panelConfiguration.Controls.Add(this.btnEditCSS);
 | 
			
		||||
            this.panelConfiguration.Controls.Add(this.btnEditCefArgs);
 | 
			
		||||
            this.panelConfiguration.Location = new System.Drawing.Point(9, 289);
 | 
			
		||||
            this.panelConfiguration.Name = "panelConfiguration";
 | 
			
		||||
            this.panelConfiguration.Size = new System.Drawing.Size(322, 29);
 | 
			
		||||
            this.panelConfiguration.TabIndex = 5;
 | 
			
		||||
            // 
 | 
			
		||||
            // 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(6, 266);
 | 
			
		||||
            this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
 | 
			
		||||
            this.labelConfiguration.Name = "labelConfiguration";
 | 
			
		||||
            this.labelConfiguration.Size = new System.Drawing.Size(104, 20);
 | 
			
		||||
            this.labelConfiguration.TabIndex = 4;
 | 
			
		||||
            this.labelConfiguration.Text = "Configuration";
 | 
			
		||||
            // 
 | 
			
		||||
            // TabSettingsAdvanced
 | 
			
		||||
            // 
 | 
			
		||||
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 | 
			
		||||
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 | 
			
		||||
            this.Controls.Add(this.labelConfiguration);
 | 
			
		||||
            this.Controls.Add(this.panelConfiguration);
 | 
			
		||||
            this.Controls.Add(this.panelPerformance);
 | 
			
		||||
            this.Controls.Add(this.labelPerformance);
 | 
			
		||||
            this.Controls.Add(this.panelApp);
 | 
			
		||||
            this.Controls.Add(this.labelApp);
 | 
			
		||||
            this.Name = "TabSettingsAdvanced";
 | 
			
		||||
            this.Size = new System.Drawing.Size(340, 328);
 | 
			
		||||
            ((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).EndInit();
 | 
			
		||||
            this.panelApp.ResumeLayout(false);
 | 
			
		||||
            this.panelPerformance.ResumeLayout(false);
 | 
			
		||||
            this.panelPerformance.PerformLayout();
 | 
			
		||||
            this.panelConfiguration.ResumeLayout(false);
 | 
			
		||||
            this.ResumeLayout(false);
 | 
			
		||||
            this.PerformLayout();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #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 panelApp;
 | 
			
		||||
        private System.Windows.Forms.Label labelPerformance;
 | 
			
		||||
        private System.Windows.Forms.Panel panelPerformance;
 | 
			
		||||
        private System.Windows.Forms.Panel panelConfiguration;
 | 
			
		||||
        private System.Windows.Forms.Label labelConfiguration;
 | 
			
		||||
        private System.Windows.Forms.Label labelMemoryUsage;
 | 
			
		||||
        private Controls.NumericUpDownEx numMemoryThreshold;
 | 
			
		||||
        private System.Windows.Forms.CheckBox checkBrowserGCReload;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,149 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Configuration;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
            if (SystemConfig.IsHardwareAccelerationSupported){
 | 
			
		||||
                checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                checkHardwareAcceleration.Enabled = false;
 | 
			
		||||
                checkHardwareAcceleration.Checked = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            checkBrowserGCReload.Checked = SysConfig.EnableBrowserGCReload;
 | 
			
		||||
            numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
 | 
			
		||||
            numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
 | 
			
		||||
 | 
			
		||||
            BrowserCache.CalculateCacheSize(task => {
 | 
			
		||||
                string text = task.IsCompleted ? (int)Math.Ceiling(task.Result/(1024.0*1024.0))+" MB" : "(unknown size)";
 | 
			
		||||
                this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})");
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void OnReady(){
 | 
			
		||||
            btnClearCache.Click += btnClearCache_Click;
 | 
			
		||||
            checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
 | 
			
		||||
 | 
			
		||||
            checkBrowserGCReload.CheckedChanged += checkBrowserGCReload_CheckedChanged;
 | 
			
		||||
            numMemoryThreshold.ValueChanged += numMemoryThreshold_ValueChanged;
 | 
			
		||||
 | 
			
		||||
            btnEditCefArgs.Click += btnEditCefArgs_Click;
 | 
			
		||||
            btnEditCSS.Click += btnEditCSS_Click;
 | 
			
		||||
            
 | 
			
		||||
            btnOpenAppFolder.Click += btnOpenAppFolder_Click;
 | 
			
		||||
            btnOpenDataFolder.Click += btnOpenDataFolder_Click;
 | 
			
		||||
            btnRestart.Click += btnRestart_Click;
 | 
			
		||||
            btnRestartArgs.Click += btnRestartArgs_Click;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void OnClosing(){
 | 
			
		||||
            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 checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
 | 
			
		||||
            PromptRestart(); // calls OnClosing
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            SysConfig.EnableBrowserGCReload = checkBrowserGCReload.Checked;
 | 
			
		||||
            numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){
 | 
			
		||||
            SysConfig.BrowserMemoryThreshold = (int)numMemoryThreshold.Value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										158
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										158
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -1,158 +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.panelFeedback = new System.Windows.Forms.Panel();
 | 
			
		||||
            this.labelDataCollectionMessage = new System.Windows.Forms.Label();
 | 
			
		||||
            this.btnViewReport = new System.Windows.Forms.Button();
 | 
			
		||||
            this.btnSendFeedback = new System.Windows.Forms.Button();
 | 
			
		||||
            this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
 | 
			
		||||
            this.checkDataCollection = new System.Windows.Forms.CheckBox();
 | 
			
		||||
            this.labelDataCollection = new System.Windows.Forms.Label();
 | 
			
		||||
            this.labelFeedback = new System.Windows.Forms.Label();
 | 
			
		||||
            this.toolTip = new System.Windows.Forms.ToolTip(this.components);
 | 
			
		||||
            this.panelFeedback.SuspendLayout();
 | 
			
		||||
            this.SuspendLayout();
 | 
			
		||||
            // 
 | 
			
		||||
            // panelFeedback
 | 
			
		||||
            // 
 | 
			
		||||
            this.panelFeedback.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
 | 
			
		||||
            | System.Windows.Forms.AnchorStyles.Right)));
 | 
			
		||||
            this.panelFeedback.Controls.Add(this.labelDataCollectionMessage);
 | 
			
		||||
            this.panelFeedback.Controls.Add(this.btnViewReport);
 | 
			
		||||
            this.panelFeedback.Controls.Add(this.btnSendFeedback);
 | 
			
		||||
            this.panelFeedback.Controls.Add(this.labelDataCollectionLink);
 | 
			
		||||
            this.panelFeedback.Controls.Add(this.checkDataCollection);
 | 
			
		||||
            this.panelFeedback.Controls.Add(this.labelDataCollection);
 | 
			
		||||
            this.panelFeedback.Location = new System.Drawing.Point(9, 31);
 | 
			
		||||
            this.panelFeedback.Name = "panelFeedback";
 | 
			
		||||
            this.panelFeedback.Size = new System.Drawing.Size(322, 188);
 | 
			
		||||
            this.panelFeedback.TabIndex = 1;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelDataCollectionMessage
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 114);
 | 
			
		||||
            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(6, 82);
 | 
			
		||||
            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, 3);
 | 
			
		||||
            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 = 0;
 | 
			
		||||
            this.btnSendFeedback.Text = "Send Feedback / Bug Report";
 | 
			
		||||
            this.btnSendFeedback.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // 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, 60);
 | 
			
		||||
            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 = 3;
 | 
			
		||||
            this.labelDataCollectionLink.TabStop = true;
 | 
			
		||||
            this.labelDataCollectionLink.Text = "(learn more)";
 | 
			
		||||
            this.labelDataCollectionLink.UseCompatibleTextRendering = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkDataCollection
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkDataCollection.AutoSize = true;
 | 
			
		||||
            this.checkDataCollection.Location = new System.Drawing.Point(6, 59);
 | 
			
		||||
            this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 5, 0, 3);
 | 
			
		||||
            this.checkDataCollection.Name = "checkDataCollection";
 | 
			
		||||
            this.checkDataCollection.Size = new System.Drawing.Size(135, 17);
 | 
			
		||||
            this.checkDataCollection.TabIndex = 2;
 | 
			
		||||
            this.checkDataCollection.Text = "Send Anonymous Data";
 | 
			
		||||
            this.checkDataCollection.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelDataCollection
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelDataCollection.AutoSize = true;
 | 
			
		||||
            this.labelDataCollection.Location = new System.Drawing.Point(3, 41);
 | 
			
		||||
            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 = 1;
 | 
			
		||||
            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(6, 8);
 | 
			
		||||
            this.labelFeedback.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
 | 
			
		||||
            this.labelFeedback.Name = "labelFeedback";
 | 
			
		||||
            this.labelFeedback.Size = new System.Drawing.Size(80, 20);
 | 
			
		||||
            this.labelFeedback.TabIndex = 0;
 | 
			
		||||
            this.labelFeedback.Text = "Feedback";
 | 
			
		||||
            // 
 | 
			
		||||
            // TabSettingsFeedback
 | 
			
		||||
            // 
 | 
			
		||||
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 | 
			
		||||
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 | 
			
		||||
            this.Controls.Add(this.panelFeedback);
 | 
			
		||||
            this.Controls.Add(this.labelFeedback);
 | 
			
		||||
            this.Name = "TabSettingsFeedback";
 | 
			
		||||
            this.Size = new System.Drawing.Size(340, 230);
 | 
			
		||||
            this.panelFeedback.ResumeLayout(false);
 | 
			
		||||
            this.panelFeedback.PerformLayout();
 | 
			
		||||
            this.ResumeLayout(false);
 | 
			
		||||
            this.PerformLayout();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        private System.Windows.Forms.Panel panelFeedback;
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										277
									
								
								Core/Other/Settings/TabSettingsGeneral.Designer.cs
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										277
									
								
								Core/Other/Settings/TabSettingsGeneral.Designer.cs
									
									
									
										generated
									
									
									
								
							@@ -1,277 +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.checkSpellCheck = new System.Windows.Forms.CheckBox();
 | 
			
		||||
            this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
 | 
			
		||||
            this.btnCheckUpdates = new System.Windows.Forms.Button();
 | 
			
		||||
            this.labelZoomValue = new System.Windows.Forms.Label();
 | 
			
		||||
            this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox();
 | 
			
		||||
            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.panelUI = new System.Windows.Forms.Panel();
 | 
			
		||||
            this.panelUpdates = new System.Windows.Forms.Panel();
 | 
			
		||||
            this.labelUpdates = new System.Windows.Forms.Label();
 | 
			
		||||
            this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
 | 
			
		||||
            ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
 | 
			
		||||
            this.panelUI.SuspendLayout();
 | 
			
		||||
            this.panelUpdates.SuspendLayout();
 | 
			
		||||
            this.SuspendLayout();
 | 
			
		||||
            // 
 | 
			
		||||
            // checkExpandLinks
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkExpandLinks.AutoSize = true;
 | 
			
		||||
            this.checkExpandLinks.Location = new System.Drawing.Point(6, 5);
 | 
			
		||||
            this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
 | 
			
		||||
            this.checkExpandLinks.Name = "checkExpandLinks";
 | 
			
		||||
            this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
 | 
			
		||||
            this.checkExpandLinks.TabIndex = 0;
 | 
			
		||||
            this.checkExpandLinks.Text = "Expand Links When Hovered";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
 | 
			
		||||
            this.checkExpandLinks.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkSpellCheck
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkSpellCheck.AutoSize = true;
 | 
			
		||||
            this.checkSpellCheck.Location = new System.Drawing.Point(6, 120);
 | 
			
		||||
            this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
 | 
			
		||||
            this.checkSpellCheck.Name = "checkSpellCheck";
 | 
			
		||||
            this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
 | 
			
		||||
            this.checkSpellCheck.TabIndex = 5;
 | 
			
		||||
            this.checkSpellCheck.Text = "Enable Spell Check";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
 | 
			
		||||
            this.checkSpellCheck.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkUpdateNotifications
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkUpdateNotifications.AutoSize = true;
 | 
			
		||||
            this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 5);
 | 
			
		||||
            this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3);
 | 
			
		||||
            this.checkUpdateNotifications.Name = "checkUpdateNotifications";
 | 
			
		||||
            this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
 | 
			
		||||
            this.checkUpdateNotifications.TabIndex = 0;
 | 
			
		||||
            this.checkUpdateNotifications.Text = "Check Updates Automatically";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again.");
 | 
			
		||||
            this.checkUpdateNotifications.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // btnCheckUpdates
 | 
			
		||||
            // 
 | 
			
		||||
            this.btnCheckUpdates.Location = new System.Drawing.Point(5, 28);
 | 
			
		||||
            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 = 1;
 | 
			
		||||
            this.btnCheckUpdates.Text = "Check Updates Now";
 | 
			
		||||
            this.toolTip.SetToolTip(this.btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
 | 
			
		||||
            this.btnCheckUpdates.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelZoomValue
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
 | 
			
		||||
            this.labelZoomValue.Location = new System.Drawing.Point(147, 169);
 | 
			
		||||
            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 = 8;
 | 
			
		||||
            this.labelZoomValue.Text = "100%";
 | 
			
		||||
            this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
 | 
			
		||||
            this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
 | 
			
		||||
            // 
 | 
			
		||||
            // checkSwitchAccountSelectors
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkSwitchAccountSelectors.AutoSize = true;
 | 
			
		||||
            this.checkSwitchAccountSelectors.Location = new System.Drawing.Point(6, 28);
 | 
			
		||||
            this.checkSwitchAccountSelectors.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
 | 
			
		||||
            this.checkSwitchAccountSelectors.Name = "checkSwitchAccountSelectors";
 | 
			
		||||
            this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17);
 | 
			
		||||
            this.checkSwitchAccountSelectors.TabIndex = 1;
 | 
			
		||||
            this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
 | 
			
		||||
            this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkBestImageQuality
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkBestImageQuality.AutoSize = true;
 | 
			
		||||
            this.checkBestImageQuality.Location = new System.Drawing.Point(6, 74);
 | 
			
		||||
            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 = 3;
 | 
			
		||||
            this.checkBestImageQuality.Text = "Best Image Quality";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
 | 
			
		||||
            this.checkBestImageQuality.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // checkOpenSearchInFirstColumn
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkOpenSearchInFirstColumn.AutoSize = true;
 | 
			
		||||
            this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 51);
 | 
			
		||||
            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.toolTip.SetToolTip(this.checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
 | 
			
		||||
            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, 168);
 | 
			
		||||
            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 = 7;
 | 
			
		||||
            this.trackBarZoom.TickFrequency = 25;
 | 
			
		||||
            this.trackBarZoom.Value = 100;
 | 
			
		||||
            // 
 | 
			
		||||
            // labelZoom
 | 
			
		||||
            // 
 | 
			
		||||
            this.labelZoom.AutoSize = true;
 | 
			
		||||
            this.labelZoom.Location = new System.Drawing.Point(3, 152);
 | 
			
		||||
            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 = 6;
 | 
			
		||||
            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(6, 8);
 | 
			
		||||
            this.labelUI.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
 | 
			
		||||
            this.labelUI.Name = "labelUI";
 | 
			
		||||
            this.labelUI.Size = new System.Drawing.Size(111, 20);
 | 
			
		||||
            this.labelUI.TabIndex = 0;
 | 
			
		||||
            this.labelUI.Text = "User Interface";
 | 
			
		||||
            // 
 | 
			
		||||
            // panelUI
 | 
			
		||||
            // 
 | 
			
		||||
            this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
 | 
			
		||||
            | System.Windows.Forms.AnchorStyles.Right)));
 | 
			
		||||
            this.panelUI.Controls.Add(this.checkAnimatedAvatars);
 | 
			
		||||
            this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn);
 | 
			
		||||
            this.panelUI.Controls.Add(this.checkBestImageQuality);
 | 
			
		||||
            this.panelUI.Controls.Add(this.checkExpandLinks);
 | 
			
		||||
            this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
 | 
			
		||||
            this.panelUI.Controls.Add(this.checkSpellCheck);
 | 
			
		||||
            this.panelUI.Controls.Add(this.labelZoom);
 | 
			
		||||
            this.panelUI.Controls.Add(this.trackBarZoom);
 | 
			
		||||
            this.panelUI.Controls.Add(this.labelZoomValue);
 | 
			
		||||
            this.panelUI.Location = new System.Drawing.Point(9, 31);
 | 
			
		||||
            this.panelUI.Name = "panelUI";
 | 
			
		||||
            this.panelUI.Size = new System.Drawing.Size(322, 205);
 | 
			
		||||
            this.panelUI.TabIndex = 1;
 | 
			
		||||
            // 
 | 
			
		||||
            // panelUpdates
 | 
			
		||||
            // 
 | 
			
		||||
            this.panelUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
 | 
			
		||||
            | System.Windows.Forms.AnchorStyles.Right)));
 | 
			
		||||
            this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
 | 
			
		||||
            this.panelUpdates.Controls.Add(this.btnCheckUpdates);
 | 
			
		||||
            this.panelUpdates.Location = new System.Drawing.Point(8, 283);
 | 
			
		||||
            this.panelUpdates.Name = "panelUpdates";
 | 
			
		||||
            this.panelUpdates.Size = new System.Drawing.Size(322, 55);
 | 
			
		||||
            this.panelUpdates.TabIndex = 3;
 | 
			
		||||
            // 
 | 
			
		||||
            // 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(5, 260);
 | 
			
		||||
            this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
 | 
			
		||||
            this.labelUpdates.Name = "labelUpdates";
 | 
			
		||||
            this.labelUpdates.Size = new System.Drawing.Size(70, 20);
 | 
			
		||||
            this.labelUpdates.TabIndex = 2;
 | 
			
		||||
            this.labelUpdates.Text = "Updates";
 | 
			
		||||
            // 
 | 
			
		||||
            // checkAnimatedAvatars
 | 
			
		||||
            // 
 | 
			
		||||
            this.checkAnimatedAvatars.AutoSize = true;
 | 
			
		||||
            this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 97);
 | 
			
		||||
            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 = 4;
 | 
			
		||||
            this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
 | 
			
		||||
            this.toolTip.SetToolTip(this.checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs.");
 | 
			
		||||
            this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
 | 
			
		||||
            // 
 | 
			
		||||
            // TabSettingsGeneral
 | 
			
		||||
            // 
 | 
			
		||||
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 | 
			
		||||
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
 | 
			
		||||
            this.Controls.Add(this.labelUpdates);
 | 
			
		||||
            this.Controls.Add(this.panelUpdates);
 | 
			
		||||
            this.Controls.Add(this.panelUI);
 | 
			
		||||
            this.Controls.Add(this.labelUI);
 | 
			
		||||
            this.Name = "TabSettingsGeneral";
 | 
			
		||||
            this.Size = new System.Drawing.Size(340, 348);
 | 
			
		||||
            ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
 | 
			
		||||
            this.panelUI.ResumeLayout(false);
 | 
			
		||||
            this.panelUI.PerformLayout();
 | 
			
		||||
            this.panelUpdates.ResumeLayout(false);
 | 
			
		||||
            this.panelUpdates.PerformLayout();
 | 
			
		||||
            this.ResumeLayout(false);
 | 
			
		||||
            this.PerformLayout();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        private System.Windows.Forms.CheckBox checkExpandLinks;
 | 
			
		||||
        private System.Windows.Forms.ToolTip toolTip;
 | 
			
		||||
        private System.Windows.Forms.CheckBox checkSpellCheck;
 | 
			
		||||
        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.CheckBox checkSwitchAccountSelectors;
 | 
			
		||||
        private System.Windows.Forms.Label labelUI;
 | 
			
		||||
        private System.Windows.Forms.Panel panelUI;
 | 
			
		||||
        private System.Windows.Forms.Panel panelUpdates;
 | 
			
		||||
        private System.Windows.Forms.Label labelUpdates;
 | 
			
		||||
        private System.Windows.Forms.CheckBox checkBestImageQuality;
 | 
			
		||||
        private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
 | 
			
		||||
        private System.Windows.Forms.CheckBox checkAnimatedAvatars;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,114 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Handling.General;
 | 
			
		||||
using TweetDuck.Updates;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Settings{
 | 
			
		||||
    sealed partial class TabSettingsGeneral : BaseTabSettings{
 | 
			
		||||
        private readonly FormBrowser browser;
 | 
			
		||||
        private readonly UpdateHandler updates;
 | 
			
		||||
        private int updateCheckEventId = -1;
 | 
			
		||||
 | 
			
		||||
        public TabSettingsGeneral(FormBrowser browser, UpdateHandler updates){
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
 | 
			
		||||
            this.browser = browser;
 | 
			
		||||
 | 
			
		||||
            this.updates = updates;
 | 
			
		||||
            this.updates.CheckFinished += updates_CheckFinished;
 | 
			
		||||
            Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
 | 
			
		||||
            
 | 
			
		||||
            toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
 | 
			
		||||
            trackBarZoom.SetValueSafe(Config.ZoomLevel);
 | 
			
		||||
            labelZoomValue.Text = trackBarZoom.Value+"%";
 | 
			
		||||
 | 
			
		||||
            checkExpandLinks.Checked = Config.ExpandLinksOnHover;
 | 
			
		||||
            checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
 | 
			
		||||
            checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
 | 
			
		||||
            checkBestImageQuality.Checked = Config.BestImageQuality;
 | 
			
		||||
            checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
 | 
			
		||||
            checkSpellCheck.Checked = Config.EnableSpellCheck;
 | 
			
		||||
 | 
			
		||||
            checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void OnReady(){
 | 
			
		||||
            checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
 | 
			
		||||
            checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
 | 
			
		||||
            checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
 | 
			
		||||
            checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
 | 
			
		||||
            checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
 | 
			
		||||
            checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
 | 
			
		||||
            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 checkSwitchAccountSelectors_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.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 => browser.ReloadColumns());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
 | 
			
		||||
            Config.EnableSpellCheck = checkSpellCheck.Checked;
 | 
			
		||||
            BrowserProcessHandler.UpdatePrefs();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void updates_CheckFinished(object sender, UpdateEventArgs e){
 | 
			
		||||
            this.InvokeAsyncSafe(() => {
 | 
			
		||||
                if (e.EventId == updateCheckEventId){
 | 
			
		||||
                    btnCheckUpdates.Enabled = true;
 | 
			
		||||
 | 
			
		||||
                    if (!e.IsUpdateAvailable){
 | 
			
		||||
                        FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void zoomUpdateTimer_Tick(object sender, EventArgs e){
 | 
			
		||||
            Config.ZoomLevel = trackBarZoom.Value;
 | 
			
		||||
            zoomUpdateTimer.Stop();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,259 +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();
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
            switch(Config.NotificationSize){
 | 
			
		||||
                case TweetNotification.Size.Auto: radioSizeAuto.Checked = true; break;
 | 
			
		||||
                case TweetNotification.Size.Custom: radioSizeCustom.Checked = true; break;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue));
 | 
			
		||||
            trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
 | 
			
		||||
            labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
 | 
			
		||||
 | 
			
		||||
            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));
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
            
 | 
			
		||||
            checkColumnName.Checked = Config.DisplayNotificationColumn;
 | 
			
		||||
            checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
 | 
			
		||||
            checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
 | 
			
		||||
            checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
 | 
			
		||||
            checkMediaPreviews.Checked = Config.NotificationMediaPreviews;
 | 
			
		||||
            checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
 | 
			
		||||
            checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
 | 
			
		||||
 | 
			
		||||
            trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
 | 
			
		||||
            labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
 | 
			
		||||
 | 
			
		||||
            trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
 | 
			
		||||
            labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
 | 
			
		||||
            
 | 
			
		||||
            Disposed += (sender, args) => this.notification.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void OnReady(){
 | 
			
		||||
            radioLocTL.CheckedChanged += radioLoc_CheckedChanged;
 | 
			
		||||
            radioLocTR.CheckedChanged += radioLoc_CheckedChanged;
 | 
			
		||||
            radioLocBL.CheckedChanged += radioLoc_CheckedChanged;
 | 
			
		||||
            radioLocBR.CheckedChanged += radioLoc_CheckedChanged;
 | 
			
		||||
            radioLocCustom.Click += radioLocCustom_Click;
 | 
			
		||||
 | 
			
		||||
            radioSizeAuto.CheckedChanged += radioSize_CheckedChanged;
 | 
			
		||||
            radioSizeCustom.Click += radioSizeCustom_Click;
 | 
			
		||||
 | 
			
		||||
            trackBarDuration.ValueChanged += trackBarDuration_ValueChanged;
 | 
			
		||||
            btnDurationShort.Click += btnDurationShort_Click;
 | 
			
		||||
            btnDurationMedium.Click += btnDurationMedium_Click;
 | 
			
		||||
            btnDurationLong.Click += btnDurationLong_Click;
 | 
			
		||||
 | 
			
		||||
            checkColumnName.CheckedChanged += checkColumnName_CheckedChanged;
 | 
			
		||||
            checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged;
 | 
			
		||||
            checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged;
 | 
			
		||||
            checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged;
 | 
			
		||||
            checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged;
 | 
			
		||||
            checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
 | 
			
		||||
 | 
			
		||||
            comboBoxIdlePause.SelectedValueChanged += comboBoxIdlePause_SelectedValueChanged;
 | 
			
		||||
            trackBarScrollSpeed.ValueChanged += trackBarScrollSpeed_ValueChanged;
 | 
			
		||||
 | 
			
		||||
            comboBoxDisplay.SelectedValueChanged += comboBoxDisplay_SelectedValueChanged;
 | 
			
		||||
            trackBarEdgeDistance.ValueChanged += trackBarEdgeDistance_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,82 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Notification;
 | 
			
		||||
using TweetLib.Audio;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Settings{
 | 
			
		||||
    sealed partial class TabSettingsSounds : BaseTabSettings{
 | 
			
		||||
        private readonly SoundNotification soundNotification;
 | 
			
		||||
        private readonly bool supportsChangingVolume;
 | 
			
		||||
 | 
			
		||||
        public TabSettingsSounds(){
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
 | 
			
		||||
            soundNotification = new SoundNotification();
 | 
			
		||||
            soundNotification.PlaybackError += sound_PlaybackError;
 | 
			
		||||
            
 | 
			
		||||
            supportsChangingVolume = soundNotification.SetVolume(Config.NotificationSoundVolume);
 | 
			
		||||
 | 
			
		||||
            trackBarVolume.Enabled = supportsChangingVolume && !string.IsNullOrEmpty(Config.NotificationSoundPath);
 | 
			
		||||
            trackBarVolume.SetValueSafe(Config.NotificationSoundVolume);
 | 
			
		||||
            labelVolumeValue.Text = trackBarVolume.Value+"%";
 | 
			
		||||
 | 
			
		||||
            tbCustomSound.Text = Config.NotificationSoundPath;
 | 
			
		||||
            tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
 | 
			
		||||
 | 
			
		||||
            Disposed += (sender, args) => soundNotification.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void tbCustomSound_TextChanged(object sender, EventArgs e){
 | 
			
		||||
            bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
 | 
			
		||||
            tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Red;
 | 
			
		||||
            btnPlaySound.Enabled = !isEmpty;
 | 
			
		||||
            btnResetSound.Enabled = !isEmpty;
 | 
			
		||||
            trackBarVolume.Enabled = supportsChangingVolume && !isEmpty;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void btnPlaySound_Click(object sender, EventArgs e){
 | 
			
		||||
            soundNotification.Play(tbCustomSound.Text);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void sound_PlaybackError(object sender, PlaybackErrorEventArgs e){
 | 
			
		||||
            FormMessage.Error("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, FormMessage.OK);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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){
 | 
			
		||||
            Config.NotificationSoundVolume = trackBarVolume.Value;
 | 
			
		||||
            soundNotification.SetVolume(Config.NotificationSoundVolume);
 | 
			
		||||
            labelVolumeValue.Text = Config.NotificationSoundVolume+"%";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Other.Settings{
 | 
			
		||||
    sealed partial class TabSettingsTray : BaseTabSettings{
 | 
			
		||||
        public TabSettingsTray(){
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
            
 | 
			
		||||
            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,242 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
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.Other.Management;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using TweetDuck.Plugins;
 | 
			
		||||
using TweetDuck.Plugins.Enums;
 | 
			
		||||
using TweetDuck.Plugins.Events;
 | 
			
		||||
using TweetDuck.Resources;
 | 
			
		||||
using TweetDuck.Updates;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core{
 | 
			
		||||
    sealed class TweetDeckBrowser : IDisposable{
 | 
			
		||||
        public bool Ready { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public bool Enabled{
 | 
			
		||||
            get => browser.Enabled;
 | 
			
		||||
            set => browser.Enabled = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsTweetDeckWebsite{
 | 
			
		||||
            get{
 | 
			
		||||
                using(IFrame frame = browser.GetBrowser().MainFrame){
 | 
			
		||||
                    return TwitterUtils.IsTweetDeckWebsite(frame);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public event EventHandler PageLoaded;
 | 
			
		||||
 | 
			
		||||
        private readonly ChromiumWebBrowser browser;
 | 
			
		||||
        private readonly PluginManager plugins;
 | 
			
		||||
        private readonly MemoryUsageTracker memoryUsageTracker;
 | 
			
		||||
 | 
			
		||||
        public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, 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()
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            #if DEBUG
 | 
			
		||||
            this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
 | 
			
		||||
            #endif
 | 
			
		||||
 | 
			
		||||
            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.RegisterAsyncJsObject("$TDP", plugins.Bridge);
 | 
			
		||||
            
 | 
			
		||||
            this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
 | 
			
		||||
            this.browser.Dock = DockStyle.None;
 | 
			
		||||
            this.browser.Location = ControlExtensions.InvisibleLocation;
 | 
			
		||||
 | 
			
		||||
            owner.Controls.Add(browser);
 | 
			
		||||
 | 
			
		||||
            this.plugins = plugins;
 | 
			
		||||
            this.plugins.PluginChangedState += plugins_PluginChangedState;
 | 
			
		||||
 | 
			
		||||
            this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
 | 
			
		||||
 | 
			
		||||
            Program.UserConfig.MuteToggled += UserConfig_MuteToggled;
 | 
			
		||||
            Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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(){
 | 
			
		||||
            plugins.PluginChangedState -= plugins_PluginChangedState;
 | 
			
		||||
 | 
			
		||||
            Program.UserConfig.MuteToggled -= UserConfig_MuteToggled;
 | 
			
		||||
            Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged;
 | 
			
		||||
 | 
			
		||||
            memoryUsageTracker.Dispose();
 | 
			
		||||
            browser.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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){
 | 
			
		||||
            if (e.Frame.IsMain){
 | 
			
		||||
                memoryUsageTracker.Stop();
 | 
			
		||||
 | 
			
		||||
                if (Program.UserConfig.ZoomLevel != 100){
 | 
			
		||||
                    BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (TwitterUtils.IsTwitterWebsite(e.Frame)){
 | 
			
		||||
                    ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
 | 
			
		||||
            if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
 | 
			
		||||
                e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
 | 
			
		||||
 | 
			
		||||
                UpdateProperties();
 | 
			
		||||
                TweetDeckBridge.RestoreSessionData(e.Frame);
 | 
			
		||||
                ScriptLoader.ExecuteFile(e.Frame, "code.js");
 | 
			
		||||
                InjectBrowserCSS();
 | 
			
		||||
                ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
 | 
			
		||||
                plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
 | 
			
		||||
 | 
			
		||||
                TweetDeckBridge.ResetStaticProperties();
 | 
			
		||||
 | 
			
		||||
                if (Program.SystemConfig.EnableBrowserGCReload){
 | 
			
		||||
                    memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (Program.UserConfig.FirstRun){
 | 
			
		||||
                    ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                PageLoaded?.Invoke(this, EventArgs.Empty);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
 | 
			
		||||
            browser.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void UserConfig_MuteToggled(object sender, EventArgs e){
 | 
			
		||||
            UpdateProperties();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void UserConfig_ZoomLevelChanged(object sender, EventArgs e){
 | 
			
		||||
            BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // external handling
 | 
			
		||||
 | 
			
		||||
        public UpdateHandler CreateUpdateHandler(UpdaterSettings settings){
 | 
			
		||||
            return new UpdateHandler(browser, settings);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void RefreshMemoryTracker(){
 | 
			
		||||
            if (Program.SystemConfig.EnableBrowserGCReload){
 | 
			
		||||
                memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                memoryUsageTracker.Stop();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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").TrimEnd());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 TriggerTweetScreenshot(){
 | 
			
		||||
            browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ReloadColumns(){
 | 
			
		||||
            browser.ExecuteScriptAsync("TDGF_reloadColumns()");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void ApplyROT13(){
 | 
			
		||||
            browser.ExecuteScriptAsync("TDGF_applyROT13()");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Utils{
 | 
			
		||||
    static class BrowserCache{
 | 
			
		||||
        private static bool ClearOnExit { get; set; }
 | 
			
		||||
 | 
			
		||||
        public static readonly string CacheFolder = Path.Combine(Program.StoragePath, "Cache");
 | 
			
		||||
        private static IEnumerable<string> CacheFiles => Directory.EnumerateFiles(CacheFolder);
 | 
			
		||||
 | 
			
		||||
        public static void CalculateCacheSize(Action<Task<long>> callbackBytes){
 | 
			
		||||
            Task<long> task = new Task<long>(() => {
 | 
			
		||||
                return CacheFiles.Select(file => {
 | 
			
		||||
                    try{
 | 
			
		||||
                        return new FileInfo(file).Length;
 | 
			
		||||
                    }catch{
 | 
			
		||||
                        return 0L;
 | 
			
		||||
                    }
 | 
			
		||||
                }).Sum();
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            task.ContinueWith(callbackBytes);
 | 
			
		||||
            task.Start();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void SetClearOnExit(){
 | 
			
		||||
            ClearOnExit = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void Exit(){
 | 
			
		||||
            if (ClearOnExit){
 | 
			
		||||
                foreach(string file in CacheFiles){
 | 
			
		||||
                    try{
 | 
			
		||||
                        File.Delete(file);
 | 
			
		||||
                    }catch{
 | 
			
		||||
                        // welp, too bad
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,147 +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;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Core.Utils{
 | 
			
		||||
    static class BrowserUtils{
 | 
			
		||||
        public static string HeaderAcceptLanguage{
 | 
			
		||||
            get{
 | 
			
		||||
                string culture = Program.Culture.Name;
 | 
			
		||||
 | 
			
		||||
                if (culture == "en"){
 | 
			
		||||
                    return "en-us,en";
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
                    return culture.ToLower()+",en;q=0.9";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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";
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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{
 | 
			
		||||
                        WindowsUtils.OpenAssociatedProgram(url);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case UrlCheckResult.Tracking:
 | 
			
		||||
                    if (FormMessage.Warning("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, FormMessage.Yes, FormMessage.No)){
 | 
			
		||||
                        WindowsUtils.OpenAssociatedProgram(url);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case UrlCheckResult.Invalid:
 | 
			
		||||
                    FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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(){
 | 
			
		||||
            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));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #if DEBUG
 | 
			
		||||
        public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
 | 
			
		||||
            Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);
 | 
			
		||||
        }
 | 
			
		||||
        #endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,133 +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;
 | 
			
		||||
 | 
			
		||||
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 BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
 | 
			
		||||
        
 | 
			
		||||
        private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$)([^/]+)/?$", 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 dot = url.LastIndexOf('/');
 | 
			
		||||
            return dot == -1 ? url : StringUtils.ExtractBefore(url, ':', dot);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 => part.Length > 0))}{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,149 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Management;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
 | 
			
		||||
        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 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 file", e.Message, 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 bool IsChildProcess(int pid){
 | 
			
		||||
            try{
 | 
			
		||||
                using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = "+pid)){
 | 
			
		||||
                    foreach(ManagementBaseObject obj in searcher.Get()){
 | 
			
		||||
                        return (uint)obj["ParentProcessId"] == CurrentProcessID;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return false;
 | 
			
		||||
            }catch{
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,160 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Runtime.Serialization;
 | 
			
		||||
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 readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
 | 
			
		||||
        
 | 
			
		||||
        public delegate void HandleUnknownPropertiesHandler(T obj, Dictionary<string, string> data);
 | 
			
		||||
        public HandleUnknownPropertiesHandler HandleUnknownProperties { get; set; }
 | 
			
		||||
        
 | 
			
		||||
        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} {converted.Replace(Environment.NewLine, NewLineCustom)}");
 | 
			
		||||
                            writer.Write(NewLineReal);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        throw new SerializationException($"Invalid serialization type, conversion failed for: {type}");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Read(string file, T obj){
 | 
			
		||||
            Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
 | 
			
		||||
 | 
			
		||||
            using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
 | 
			
		||||
                switch(reader.Peek()){
 | 
			
		||||
                    case -1:
 | 
			
		||||
                        throw new FormatException("File is empty.");
 | 
			
		||||
                    case 0:
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        throw new FormatException("Input appears to be a binary file.");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                foreach(string line in reader.ReadToEnd().Split(new string[]{ NewLineReal }, StringSplitOptions.RemoveEmptyEntries)){
 | 
			
		||||
                    int space = line.IndexOf(' ');
 | 
			
		||||
 | 
			
		||||
                    if (space == -1){
 | 
			
		||||
                        throw new SerializationException($"Invalid file format, missing separator: {line}");
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    string property = line.Substring(0, space);
 | 
			
		||||
                    string value = line.Substring(space+1).Replace(NewLineCustom, Environment.NewLine);
 | 
			
		||||
 | 
			
		||||
                    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})");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else{
 | 
			
		||||
                        unknownProperties[property] = value;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (unknownProperties.Count > 0){
 | 
			
		||||
                HandleUnknownProperties?.Invoke(obj, unknownProperties);
 | 
			
		||||
 | 
			
		||||
                if (unknownProperties.Count > 0){
 | 
			
		||||
                    throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,102 +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){
 | 
			
		||||
            Dictionary<K2, V> innerDict;
 | 
			
		||||
            return dict.TryGetValue(outerKey, out 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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using TweetDuck.Data.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Data{
 | 
			
		||||
    sealed class WindowState{
 | 
			
		||||
        private Rectangle rect;
 | 
			
		||||
        private bool isMaximized;
 | 
			
		||||
 | 
			
		||||
        public void Save(Form form){
 | 
			
		||||
            rect = form.WindowState == FormWindowState.Normal ? form.DesktopBounds : form.RestoreBounds;
 | 
			
		||||
            isMaximized = form.WindowState == FormWindowState.Maximized;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Restore(Form form, bool firstTimeFullscreen){
 | 
			
		||||
            if (rect != Rectangle.Empty){
 | 
			
		||||
                form.DesktopBounds = rect;
 | 
			
		||||
                form.WindowState = isMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((rect == Rectangle.Empty && firstTimeFullscreen) || form.IsFullyOutsideView()){
 | 
			
		||||
                form.DesktopBounds = Screen.PrimaryScreen.WorkingArea;
 | 
			
		||||
                form.WindowState = FormWindowState.Maximized;
 | 
			
		||||
                Save(form);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static readonly SingleTypeConverter<WindowState> Converter = new SingleTypeConverter<WindowState>{
 | 
			
		||||
            ConvertToString = value => $"{(value.isMaximized ? 'M' : '_')}{value.rect.X} {value.rect.Y} {value.rect.Width} {value.rect.Height}",
 | 
			
		||||
            ConvertToObject = value => {
 | 
			
		||||
                int[] elements = StringUtils.ParseInts(value.Substring(1), ' ');
 | 
			
		||||
                
 | 
			
		||||
                return new WindowState{
 | 
			
		||||
                    rect = new Rectangle(elements[0], elements[1], elements[2], elements[3]),
 | 
			
		||||
                    isMaximized = value[0] == 'M'
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,101 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Controls;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
using TweetDuck.Plugins.Enums;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Plugins.Controls{
 | 
			
		||||
    sealed partial class PluginControl : UserControl{
 | 
			
		||||
        private readonly PluginManager pluginManager;
 | 
			
		||||
        private readonly Plugin plugin;
 | 
			
		||||
 | 
			
		||||
        private readonly float dpiScale;
 | 
			
		||||
 | 
			
		||||
        public PluginControl(){
 | 
			
		||||
            InitializeComponent();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public PluginControl(PluginManager pluginManager, Plugin plugin) : this(){
 | 
			
		||||
            this.pluginManager = pluginManager;
 | 
			
		||||
            this.plugin = plugin;
 | 
			
		||||
 | 
			
		||||
            this.dpiScale = this.GetDPIScale();
 | 
			
		||||
 | 
			
		||||
            this.labelName.Text = plugin.Name;
 | 
			
		||||
            this.labelDescription.Text = plugin.CanRun ? plugin.Description : "This plugin requires TweetDuck "+plugin.RequiredVersion+" or newer.";
 | 
			
		||||
            this.labelVersion.Text = plugin.Version;
 | 
			
		||||
            this.labelAuthor.Text = plugin.Author;
 | 
			
		||||
            this.labelWebsite.Text = plugin.Website;
 | 
			
		||||
            
 | 
			
		||||
            this.labelType.LineHeight = BrowserUtils.Scale(9, dpiScale);
 | 
			
		||||
 | 
			
		||||
            UpdatePluginState();
 | 
			
		||||
 | 
			
		||||
            if (labelDescription.Text.Length == 0){
 | 
			
		||||
                labelDescription.Visible = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            panelDescription_Resize(panelDescription, null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void panelDescription_Resize(object sender, EventArgs e){
 | 
			
		||||
            if (labelDescription.Text.Length == 0){
 | 
			
		||||
                Height = MinimumSize.Height;
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth, 0);
 | 
			
		||||
                Height = Math.Min(MinimumSize.Height+BrowserUtils.Scale(9, dpiScale)+labelDescription.Height, MaximumSize.Height);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void labelWebsite_Click(object sender, EventArgs e){
 | 
			
		||||
            if (labelWebsite.Text.Length > 0){
 | 
			
		||||
                BrowserUtils.OpenExternalBrowser(labelWebsite.Text);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void btnOpenConfig_Click(object sender, EventArgs e){
 | 
			
		||||
            using(Process.Start("explorer.exe", "/select,\""+plugin.ConfigPath.Replace('/', '\\')+"\"")){}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void btnToggleState_Click(object sender, EventArgs e){
 | 
			
		||||
            pluginManager.Config.ToggleEnabled(plugin);
 | 
			
		||||
            UpdatePluginState();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void UpdatePluginState(){
 | 
			
		||||
            bool isEnabled = pluginManager.Config.IsEnabled(plugin) && plugin.CanRun;
 | 
			
		||||
            Color textColor = isEnabled ? Color.Black : Color.FromArgb(90, 90, 90);
 | 
			
		||||
            
 | 
			
		||||
            labelVersion.ForeColor = textColor;
 | 
			
		||||
            labelAuthor.ForeColor = textColor;
 | 
			
		||||
            labelWebsite.ForeColor = isEnabled ? Color.Blue : Color.FromArgb(90, 90, 249);
 | 
			
		||||
 | 
			
		||||
            if (plugin.Group == PluginGroup.Official){
 | 
			
		||||
                labelType.Text = "OFFICIAL";
 | 
			
		||||
                labelType.BackColor = isEnabled ? Color.FromArgb(154, 195, 217) : Color.FromArgb(185, 185, 185);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                labelType.Text = "CUSTOM";
 | 
			
		||||
                labelType.BackColor = isEnabled ? Color.FromArgb(208, 154, 217) : Color.FromArgb(185, 185, 185);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (plugin.CanRun){
 | 
			
		||||
                labelName.ForeColor = textColor;
 | 
			
		||||
                labelDescription.ForeColor = textColor;
 | 
			
		||||
                btnToggleState.Text = isEnabled ? "Disable" : "Enable";
 | 
			
		||||
                btnOpenConfig.Visible = plugin.HasConfig;
 | 
			
		||||
                btnOpenConfig.Enabled = btnOpenConfig.Visible && File.Exists(plugin.ConfigPath);
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                labelName.ForeColor = Color.DarkRed;
 | 
			
		||||
                labelDescription.ForeColor = Color.DarkRed;
 | 
			
		||||
                btnToggleState.Visible = false;
 | 
			
		||||
                btnOpenConfig.Visible = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using TweetDuck.Core.Utils;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Plugins.Controls{
 | 
			
		||||
    sealed class PluginListFlowLayout : FlowLayoutPanel{
 | 
			
		||||
        public PluginListFlowLayout(){
 | 
			
		||||
            FlowDirection = FlowDirection.TopDown;
 | 
			
		||||
            WrapContents = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void WndProc(ref Message m){
 | 
			
		||||
            NativeMethods.ShowScrollBar(Handle, NativeMethods.SB_HORZ, false); // basically fuck the horizontal scrollbar very much
 | 
			
		||||
            base.WndProc(ref m);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace TweetDuck.Plugins.Enums{
 | 
			
		||||
    [Flags]
 | 
			
		||||
    enum PluginEnvironment{
 | 
			
		||||
        None = 0,
 | 
			
		||||
        Browser = 1,
 | 
			
		||||
        Notification = 2
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class PluginEnvironmentExtensions{
 | 
			
		||||
        public static IEnumerable<PluginEnvironment> Values{
 | 
			
		||||
            get{
 | 
			
		||||
                yield return PluginEnvironment.Browser;
 | 
			
		||||
                yield return PluginEnvironment.Notification;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
 | 
			
		||||
            return environment == PluginEnvironment.Browser;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GetScriptIdentifier(this PluginEnvironment environment){
 | 
			
		||||
            switch(environment){
 | 
			
		||||
                case PluginEnvironment.None: return "root:plugins";
 | 
			
		||||
                case PluginEnvironment.Browser: return "root:plugins.browser";
 | 
			
		||||
                case PluginEnvironment.Notification: return "root:plugins.notification";
 | 
			
		||||
                default: return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GetPluginScriptFile(this PluginEnvironment environment){
 | 
			
		||||
            switch(environment){
 | 
			
		||||
                case PluginEnvironment.Browser: return "browser.js";
 | 
			
		||||
                case PluginEnvironment.Notification: return "notification.js";
 | 
			
		||||
                default: return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GetPluginScriptVariables(this PluginEnvironment environment){
 | 
			
		||||
            switch(environment){
 | 
			
		||||
                case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
 | 
			
		||||
                case PluginEnvironment.Notification: return "$TD,$TDP";
 | 
			
		||||
                default: return string.Empty;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user