mirror of
				https://github.com/chylex/TweetDuck.git
				synced 2025-10-31 18:17:15 +01:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			c785a7ed8c
			...
			taskbar-ov
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9238410756 | 
							
								
								
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,5 @@ | |||||||
| # Auto detect text files and perform LF normalization | # Auto detect text files and perform LF normalization | ||||||
| * text=auto eof=lf | * text=auto | ||||||
|  |  | ||||||
|  | *.txt text eof=lf | ||||||
| *.cs  diff=csharp | *.cs  diff=csharp | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +0,0 @@ | |||||||
| github: chylex |  | ||||||
| patreon: chylex |  | ||||||
| ko_fi: chylex |  | ||||||
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,14 +5,8 @@ | |||||||
| bld/* | bld/* | ||||||
| !bld/*.iss | !bld/*.iss | ||||||
| !bld/*.bat | !bld/*.bat | ||||||
| !bld/*.ps1 |  | ||||||
| !bld/Redist |  | ||||||
| !bld/Resources | !bld/Resources | ||||||
|  |  | ||||||
| # Rider |  | ||||||
| **/.idea/dictionaries |  | ||||||
| **/.idea/misc.xml |  | ||||||
|  |  | ||||||
| # User-specific files | # User-specific files | ||||||
| *.suo | *.suo | ||||||
| *.user | *.user | ||||||
| @@ -146,3 +140,4 @@ _UpgradeReport_Files/ | |||||||
| Backup*/ | Backup*/ | ||||||
| UpgradeLog*.XML | UpgradeLog*.XML | ||||||
| UpgradeLog*.htm | UpgradeLog*.htm | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.idea/.idea.TweetDuck/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.idea/.idea.TweetDuck/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,8 +0,0 @@ | |||||||
| # 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
									
									
									
								
							
							
						
						
									
										450
									
								
								.idea/.idea.TweetDuck/.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,450 +0,0 @@ | |||||||
| <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> |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| <component name="ProjectCodeStyleConfiguration"> |  | ||||||
|   <state> |  | ||||||
|     <option name="USE_PER_PROJECT_SETTINGS" value="true" /> |  | ||||||
|   </state> |  | ||||||
| </component> |  | ||||||
							
								
								
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/encodings.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/.idea.TweetDuck/.idea/encodings.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +0,0 @@ | |||||||
| <?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
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/.idea.TweetDuck/.idea/indexLayout.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,8 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <project version="4"> |  | ||||||
|   <component name="UserContentModel"> |  | ||||||
|     <attachedFolders /> |  | ||||||
|     <explicitIncludes /> |  | ||||||
|     <explicitExcludes /> |  | ||||||
|   </component> |  | ||||||
| </project> |  | ||||||
| @@ -1,770 +0,0 @@ | |||||||
| <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> |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| <component name="InspectionProjectProfileManager"> |  | ||||||
|   <settings> |  | ||||||
|     <option name="PROJECT_PROFILE" value="Project" /> |  | ||||||
|     <version value="1.0" /> |  | ||||||
|   </settings> |  | ||||||
| </component> |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <project version="4"> |  | ||||||
|   <component name="JavaScriptLibraryMappings"> |  | ||||||
|     <file url="PROJECT" libraries="{@types/jquery}" /> |  | ||||||
|   </component> |  | ||||||
| </project> |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| <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="net6.0-windows7.0" /> |  | ||||||
|     <method v="2"> |  | ||||||
|       <option name="Build" /> |  | ||||||
|     </method> |  | ||||||
|   </configuration> |  | ||||||
| </component> |  | ||||||
							
								
								
									
										11
									
								
								.idea/.idea.TweetDuck/.idea/vcs.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								.idea/.idea.TweetDuck/.idea/vcs.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,11 +0,0 @@ | |||||||
| <?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> |  | ||||||
							
								
								
									
										48
									
								
								Configuration/Arguments.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Configuration/Arguments.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | using System; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Configuration{ | ||||||
|  |     static class Arguments{ | ||||||
|  |         // public args | ||||||
|  |         public const string ArgDataFolder = "-datafolder"; | ||||||
|  |         public const string ArgLogging = "-log"; | ||||||
|  |         public const string ArgIgnoreGDPR = "-nogdpr"; | ||||||
|  |         public const string ArgFreeze = "-freeze"; | ||||||
|  | 
 | ||||||
|  |         // 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("\"", "::"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										128
									
								
								Configuration/ConfigManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								Configuration/ConfigManager.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Drawing; | ||||||
|  | using TweetDuck.Configuration.Instance; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | using TweetDuck.Data.Serialization; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Configuration{ | ||||||
|  |     sealed class ConfigManager{ | ||||||
|  |         public UserConfig User { get; } | ||||||
|  |         public SystemConfig System { get; } | ||||||
|  |         public PluginConfig Plugins { get; } | ||||||
|  |          | ||||||
|  |         public event EventHandler ProgramRestartRequested; | ||||||
|  | 
 | ||||||
|  |         private readonly FileConfigInstance<UserConfig> infoUser; | ||||||
|  |         private readonly FileConfigInstance<SystemConfig> infoSystem; | ||||||
|  |         private readonly PluginConfigInstance infoPlugins; | ||||||
|  | 
 | ||||||
|  |         private readonly IConfigInstance<BaseConfig>[] infoList; | ||||||
|  | 
 | ||||||
|  |         public ConfigManager(){ | ||||||
|  |             User = new UserConfig(this); | ||||||
|  |             System = new SystemConfig(this); | ||||||
|  |             Plugins = new PluginConfig(this); | ||||||
|  |              | ||||||
|  |             infoList = new IConfigInstance<BaseConfig>[]{ | ||||||
|  |                 infoUser = new FileConfigInstance<UserConfig>(Program.UserConfigFilePath, User, "program options"), | ||||||
|  |                 infoSystem = new FileConfigInstance<SystemConfig>(Program.SystemConfigFilePath, System, "system options"), | ||||||
|  |                 infoPlugins = new PluginConfigInstance(Program.PluginConfigFilePath, Plugins) | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // TODO refactor further | ||||||
|  | 
 | ||||||
|  |             infoUser.Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter); | ||||||
|  | 
 | ||||||
|  |             infoUser.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]); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |              | ||||||
|  |             infoUser.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]); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void LoadAll(){ | ||||||
|  |             infoUser.Load(); | ||||||
|  |             infoSystem.Load(); | ||||||
|  |             infoPlugins.Load(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SaveAll(){ | ||||||
|  |             infoUser.Save(); | ||||||
|  |             infoSystem.Save(); | ||||||
|  |             infoPlugins.Save(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ReloadAll(){ | ||||||
|  |             infoUser.Reload(); | ||||||
|  |             infoSystem.Reload(); | ||||||
|  |             infoPlugins.Reload(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void TriggerProgramRestartRequested(){ | ||||||
|  |             ProgramRestartRequested?.Invoke(this, EventArgs.Empty); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private IConfigInstance<BaseConfig> GetInstanceInfo(BaseConfig instance){ | ||||||
|  |             Type instanceType = instance.GetType(); | ||||||
|  |             return Array.Find(infoList, info => info.Instance.GetType() == instanceType); // TODO handle null | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public abstract class BaseConfig{ | ||||||
|  |             private readonly ConfigManager configManager; | ||||||
|  | 
 | ||||||
|  |             protected BaseConfig(ConfigManager configManager){ | ||||||
|  |                 this.configManager = configManager; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Management | ||||||
|  | 
 | ||||||
|  |             public void Save(){ | ||||||
|  |                 configManager.GetInstanceInfo(this).Save(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void Reload(){ | ||||||
|  |                 configManager.GetInstanceInfo(this).Reload(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void Reset(){ | ||||||
|  |                 configManager.GetInstanceInfo(this).Reset(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Construction methods | ||||||
|  | 
 | ||||||
|  |             public T ConstructWithDefaults<T>() where T : BaseConfig{ | ||||||
|  |                 return ConstructWithDefaults(configManager) as T; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             protected abstract BaseConfig ConstructWithDefaults(ConfigManager configManager); | ||||||
|  | 
 | ||||||
|  |             // Utility methods | ||||||
|  | 
 | ||||||
|  |             protected void UpdatePropertyWithEvent<T>(ref T field, T value, EventHandler eventHandler){ | ||||||
|  |                 if (!EqualityComparer<T>.Default.Equals(field, value)){ | ||||||
|  |                     field = value; | ||||||
|  |                     eventHandler?.Invoke(this, EventArgs.Empty); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             protected void UpdatePropertyWithRestartRequest<T>(ref T field, T value){ | ||||||
|  |                 if (!EqualityComparer<T>.Default.Equals(field, value)){ | ||||||
|  |                     field = value; | ||||||
|  |                     configManager.TriggerProgramRestartRequested(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								Configuration/Instance/FileConfigInstance.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								Configuration/Instance/FileConfigInstance.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using TweetDuck.Data.Serialization; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Configuration.Instance{ | ||||||
|  |     sealed class FileConfigInstance<T> : IConfigInstance<T> where T : ConfigManager.BaseConfig{ | ||||||
|  |         private const string ErrorTitle = "Configuration Error"; | ||||||
|  | 
 | ||||||
|  |         public T Instance { get; } | ||||||
|  |         public FileSerializer<T> Serializer { get; } | ||||||
|  | 
 | ||||||
|  |         private readonly string filenameMain; | ||||||
|  |         private readonly string filenameBackup; | ||||||
|  |         private readonly string errorIdentifier; | ||||||
|  | 
 | ||||||
|  |         public FileConfigInstance(string filename, T instance, string errorIdentifier){ | ||||||
|  |             this.filenameMain = filename; | ||||||
|  |             this.filenameBackup = filename+".bak"; | ||||||
|  |             this.errorIdentifier = errorIdentifier; | ||||||
|  | 
 | ||||||
|  |             this.Instance = instance; | ||||||
|  |             this.Serializer = new FileSerializer<T>(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void LoadInternal(bool backup){ | ||||||
|  |             Serializer.Read(backup ? filenameBackup : filenameMain, Instance); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Load(){ | ||||||
|  |             Exception firstException = null; | ||||||
|  |              | ||||||
|  |             for(int attempt = 0; attempt < 2; attempt++){ | ||||||
|  |                 try{ | ||||||
|  |                     LoadInternal(attempt > 0); | ||||||
|  | 
 | ||||||
|  |                     if (firstException != null){ // silently log exception that caused a backup restore | ||||||
|  |                         Program.Reporter.LogImportant(firstException.ToString()); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return; | ||||||
|  |                 }catch(FileNotFoundException){ | ||||||
|  |                 }catch(DirectoryNotFoundException){ | ||||||
|  |                     break; | ||||||
|  |                 }catch(Exception e){ | ||||||
|  |                     if (firstException == null){ | ||||||
|  |                         firstException = e; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if (firstException is FormatException){ | ||||||
|  |                 Program.Reporter.HandleException(ErrorTitle, "The configuration file for "+errorIdentifier+" is outdated or corrupted. If you continue, your "+errorIdentifier+" will be reset.", true, firstException); | ||||||
|  |             } | ||||||
|  |             else if (firstException is SerializationSoftException sse){ | ||||||
|  |                 Program.Reporter.HandleException(ErrorTitle, $"{sse.Errors.Count} error{(sse.Errors.Count == 1 ? " was" : "s were")} encountered while loading the configuration file for "+errorIdentifier+". If you continue, some of your "+errorIdentifier+" will be reset.", true, firstException); | ||||||
|  |             } | ||||||
|  |             else if (firstException != null){ | ||||||
|  |                 Program.Reporter.HandleException(ErrorTitle, "Could not open the configuration file for "+errorIdentifier+". If you continue, your "+errorIdentifier+" will be reset.", true, firstException); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Save(){ | ||||||
|  |             try{ | ||||||
|  |                 if (File.Exists(filenameMain)){ | ||||||
|  |                     File.Delete(filenameBackup); | ||||||
|  |                     File.Move(filenameMain, filenameBackup); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 Serializer.Write(filenameMain, Instance); | ||||||
|  |             }catch(SerializationSoftException e){ | ||||||
|  |                 Program.Reporter.HandleException(ErrorTitle, $"{e.Errors.Count} error{(e.Errors.Count == 1 ? " was" : "s were")} encountered while saving the configuration file for "+errorIdentifier+".", true, e); | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException(ErrorTitle, "Could not save the configuration file for "+errorIdentifier+".", true, e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Reload(){ | ||||||
|  |             try{ | ||||||
|  |                 LoadInternal(false); | ||||||
|  |             }catch(FileNotFoundException){ | ||||||
|  |                 try{ | ||||||
|  |                     Serializer.Write(filenameMain, Instance.ConstructWithDefaults<T>()); | ||||||
|  |                     LoadInternal(false); | ||||||
|  |                 }catch(Exception e){ | ||||||
|  |                     Program.Reporter.HandleException(ErrorTitle, "Could not regenerate the configuration file for "+errorIdentifier+".", true, e); | ||||||
|  |                 } | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException(ErrorTitle, "Could not reload the configuration file for "+errorIdentifier+".", true, e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Reset(){ | ||||||
|  |             try{ | ||||||
|  |                 File.Delete(filenameMain); | ||||||
|  |                 File.Delete(filenameBackup); | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException(ErrorTitle, "Could not delete configuration files to reset "+errorIdentifier+".", true, e); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             Reload(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								Configuration/Instance/IConfigInstance.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Configuration/Instance/IConfigInstance.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | namespace TweetDuck.Configuration.Instance{ | ||||||
|  |     interface IConfigInstance<out T>{ | ||||||
|  |         T Instance { get; } | ||||||
|  | 
 | ||||||
|  |         void Save(); | ||||||
|  |         void Reload(); | ||||||
|  |         void Reset(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								Configuration/Instance/PluginConfigInstance.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								Configuration/Instance/PluginConfigInstance.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Text; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Configuration.Instance{ | ||||||
|  |     class PluginConfigInstance : IConfigInstance<PluginConfig>{ | ||||||
|  |         public PluginConfig Instance { get; } | ||||||
|  | 
 | ||||||
|  |         private readonly string filename; | ||||||
|  | 
 | ||||||
|  |         public PluginConfigInstance(string filename, PluginConfig instance){ | ||||||
|  |             this.filename = filename; | ||||||
|  |             this.Instance = instance; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Load(){ | ||||||
|  |             try{ | ||||||
|  |                 using(StreamReader reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){ | ||||||
|  |                     string line = reader.ReadLine(); | ||||||
|  | 
 | ||||||
|  |                     if (line == "#Disabled"){ | ||||||
|  |                         HashSet<string> newDisabled = new HashSet<string>(); | ||||||
|  | 
 | ||||||
|  |                         while((line = reader.ReadLine()) != null){ | ||||||
|  |                             newDisabled.Add(line); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         Instance.ReloadSilently(newDisabled); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }catch(FileNotFoundException){ | ||||||
|  |             }catch(DirectoryNotFoundException){ | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Save(){ | ||||||
|  |             try{ | ||||||
|  |                 using(StreamWriter writer = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){ | ||||||
|  |                     writer.WriteLine("#Disabled"); | ||||||
|  | 
 | ||||||
|  |                     foreach(string identifier in Instance.DisabledPlugins){ | ||||||
|  |                         writer.WriteLine(identifier); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Reload(){ | ||||||
|  |             Load(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Reset(){ | ||||||
|  |             try{ | ||||||
|  |                 File.Delete(filename); | ||||||
|  |                 Instance.ReloadSilently(Instance.ConstructWithDefaults<PluginConfig>().DisabledPlugins); | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Plugin Configuration Error", "Could not delete the plugin configuration file.", true, e); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             Reload(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										183
									
								
								Configuration/LockManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								Configuration/LockManager.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | |||||||
|  | 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.LogImportant(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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								Configuration/PluginConfig.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Configuration/PluginConfig.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Plugins.Events; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Configuration{ | ||||||
|  |     sealed class PluginConfig : ConfigManager.BaseConfig, IPluginConfig{ | ||||||
|  |         private static readonly string[] DefaultDisabled = { | ||||||
|  |             "official/clear-columns", | ||||||
|  |             "official/reply-account" | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // CONFIGURATION | ||||||
|  | 
 | ||||||
|  |         public IEnumerable<string> DisabledPlugins => disabled; | ||||||
|  |          | ||||||
|  |         public event EventHandler<PluginChangedStateEventArgs> PluginChangedState; | ||||||
|  | 
 | ||||||
|  |         public void SetEnabled(Plugin plugin, bool enabled){ | ||||||
|  |             if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){ | ||||||
|  |                 PluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled)); | ||||||
|  |                 Save(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool IsEnabled(Plugin plugin){ | ||||||
|  |             return !disabled.Contains(plugin.Identifier); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ReloadSilently(IEnumerable<string> newDisabled){ | ||||||
|  |             disabled.Clear(); | ||||||
|  |             disabled.UnionWith(newDisabled); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly HashSet<string> disabled = new HashSet<string>(DefaultDisabled); | ||||||
|  |          | ||||||
|  |         // END OF CONFIG | ||||||
|  | 
 | ||||||
|  |         public PluginConfig(ConfigManager configManager) : base(configManager){} | ||||||
|  | 
 | ||||||
|  |         protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){ | ||||||
|  |             return new PluginConfig(configManager); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								Configuration/SystemConfig.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Configuration/SystemConfig.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | namespace TweetDuck.Configuration{ | ||||||
|  |     sealed class SystemConfig : ConfigManager.BaseConfig{ | ||||||
|  | 
 | ||||||
|  |         // CONFIGURATION DATA | ||||||
|  |          | ||||||
|  |         public bool _hardwareAcceleration = true; | ||||||
|  |          | ||||||
|  |         public bool ClearCacheAutomatically { get; set; } = true; | ||||||
|  |         public int ClearCacheThreshold      { get; set; } = 250; | ||||||
|  | 
 | ||||||
|  |         // SPECIAL PROPERTIES | ||||||
|  |          | ||||||
|  |         public bool HardwareAcceleration{ | ||||||
|  |             get => _hardwareAcceleration; | ||||||
|  |             set => UpdatePropertyWithRestartRequest(ref _hardwareAcceleration, value); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // END OF CONFIG | ||||||
|  | 
 | ||||||
|  |         public SystemConfig(ConfigManager configManager) : base(configManager){} | ||||||
|  | 
 | ||||||
|  |         protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){ | ||||||
|  |             return new SystemConfig(configManager); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										144
									
								
								Configuration/UserConfig.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								Configuration/UserConfig.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | |||||||
|  | using System; | ||||||
|  | using System.Drawing; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Notification; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Configuration{ | ||||||
|  |     sealed class UserConfig : ConfigManager.BaseConfig{ | ||||||
|  |          | ||||||
|  |         // CONFIGURATION DATA | ||||||
|  | 
 | ||||||
|  |         public bool FirstRun            { get; set; } = true; | ||||||
|  |         public bool AllowDataCollection { get; set; } = false; | ||||||
|  | 
 | ||||||
|  |         public WindowState BrowserWindow { get; set; } = new WindowState(); | ||||||
|  |         public Size PluginsWindowSize    { get; set; } = Size.Empty; | ||||||
|  | 
 | ||||||
|  |         public bool ExpandLinksOnHover        { get; set; } = true; | ||||||
|  |         public bool FocusDmInput              { get; set; } = true; | ||||||
|  |         public bool OpenSearchInFirstColumn   { get; set; } = true; | ||||||
|  |         public bool KeepLikeFollowDialogsOpen { get; set; } = true; | ||||||
|  |         public bool BestImageQuality          { get; set; } = true; | ||||||
|  |         public bool EnableAnimatedImages      { get; set; } = true; | ||||||
|  | 
 | ||||||
|  |         private bool _enableSmoothScrolling = true; | ||||||
|  |         private bool _enableTouchAdjustment = false; | ||||||
|  |         private string _customCefArgs       = null; | ||||||
|  | 
 | ||||||
|  |         public string BrowserPath            { get; set; } = null; | ||||||
|  |         public bool IgnoreTrackingUrlWarning { get; set; } = false; | ||||||
|  |         public string SearchEngineUrl        { get; set; } = null; | ||||||
|  |         private int _zoomLevel                             = 100; | ||||||
|  | 
 | ||||||
|  |         public int VideoPlayerVolume { get; set; } = 50; | ||||||
|  |          | ||||||
|  |         public bool EnableSpellCheck { get; set; } = false; | ||||||
|  |         private string _spellCheckLanguage         = "en-US"; | ||||||
|  | 
 | ||||||
|  |         public string TranslationTarget { get; set; } = "en"; | ||||||
|  |          | ||||||
|  |         private TrayIcon.Behavior _trayBehavior       = TrayIcon.Behavior.Disabled; | ||||||
|  |         public bool EnableTrayHighlight { get; set; } = true; | ||||||
|  | 
 | ||||||
|  |         public bool EnableUpdateCheck { get; set; } = true; | ||||||
|  |         public string DismissedUpdate { get; set; } = null; | ||||||
|  | 
 | ||||||
|  |         public bool DisplayNotificationColumn    { get; set; } = false; | ||||||
|  |         public bool NotificationMediaPreviews    { get; set; } = true; | ||||||
|  |         public bool NotificationSkipOnLinkClick  { get; set; } = false; | ||||||
|  |         public bool NotificationNonIntrusiveMode { get; set; } = true; | ||||||
|  |         public int NotificationIdlePauseSeconds  { get; set; } = 0; | ||||||
|  | 
 | ||||||
|  |         public bool DisplayNotificationTimer   { get; set; } = true; | ||||||
|  |         public bool NotificationTimerCountDown { get; set; } = false; | ||||||
|  |         public int NotificationDurationValue   { get; set; } = 25; | ||||||
|  | 
 | ||||||
|  |         public TweetNotification.Position NotificationPosition { get; set; } = TweetNotification.Position.TopRight; | ||||||
|  |         public Point CustomNotificationPosition                { get; set; } = ControlExtensions.InvisibleLocation; | ||||||
|  |         public int NotificationDisplay                         { get; set; } = 0; | ||||||
|  |         public int NotificationEdgeDistance                    { get; set; } = 8; | ||||||
|  | 
 | ||||||
|  |         public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto; | ||||||
|  |         public Size CustomNotificationSize             { get; set; } = Size.Empty; | ||||||
|  |         public int NotificationScrollSpeed             { get; set; } = 100; | ||||||
|  |          | ||||||
|  |         private string _notificationSoundPath; | ||||||
|  |         private int _notificationSoundVolume = 100; | ||||||
|  | 
 | ||||||
|  |         private bool _muteNotifications; | ||||||
|  | 
 | ||||||
|  |         public string CustomBrowserCSS      { get; set; } = null; | ||||||
|  |         public string CustomNotificationCSS { get; set; } = null; | ||||||
|  |          | ||||||
|  |         // SPECIAL PROPERTIES | ||||||
|  | 
 | ||||||
|  |         public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation; | ||||||
|  |         public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty; | ||||||
|  |         public bool IsCustomSoundNotificationSet => NotificationSoundPath != string.Empty; | ||||||
|  | 
 | ||||||
|  |         public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default; | ||||||
|  |          | ||||||
|  |         public string NotificationSoundPath{ | ||||||
|  |             get => _notificationSoundPath ?? string.Empty; | ||||||
|  |             set => UpdatePropertyWithEvent(ref _notificationSoundPath, value, SoundNotificationChanged); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public int NotificationSoundVolume{ | ||||||
|  |             get => _notificationSoundVolume; | ||||||
|  |             set => UpdatePropertyWithEvent(ref _notificationSoundVolume, value, SoundNotificationChanged); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool MuteNotifications{ | ||||||
|  |             get => _muteNotifications; | ||||||
|  |             set => UpdatePropertyWithEvent(ref _muteNotifications, value, MuteToggled); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int ZoomLevel{ | ||||||
|  |             get => _zoomLevel; | ||||||
|  |             set => UpdatePropertyWithEvent(ref _zoomLevel, value, ZoomLevelChanged); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public TrayIcon.Behavior TrayBehavior{ | ||||||
|  |             get => _trayBehavior; | ||||||
|  |             set => UpdatePropertyWithEvent(ref _trayBehavior, value, TrayBehaviorChanged); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public bool EnableSmoothScrolling{ | ||||||
|  |             get => _enableSmoothScrolling; | ||||||
|  |             set => UpdatePropertyWithRestartRequest(ref _enableSmoothScrolling, value); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool EnableTouchAdjustment{ | ||||||
|  |             get => _enableTouchAdjustment; | ||||||
|  |             set => UpdatePropertyWithRestartRequest(ref _enableTouchAdjustment, value); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string CustomCefArgs{ | ||||||
|  |             get => _customCefArgs; | ||||||
|  |             set => UpdatePropertyWithRestartRequest(ref _customCefArgs, value); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string SpellCheckLanguage{ | ||||||
|  |             get => _spellCheckLanguage; | ||||||
|  |             set => UpdatePropertyWithRestartRequest(ref _spellCheckLanguage, value); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // EVENTS | ||||||
|  |          | ||||||
|  |         public event EventHandler MuteToggled; | ||||||
|  |         public event EventHandler ZoomLevelChanged; | ||||||
|  |         public event EventHandler TrayBehaviorChanged; | ||||||
|  |         public event EventHandler SoundNotificationChanged; | ||||||
|  | 
 | ||||||
|  |         // END OF CONFIG | ||||||
|  |          | ||||||
|  |         public UserConfig(ConfigManager configManager) : base(configManager){} | ||||||
|  | 
 | ||||||
|  |         protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){ | ||||||
|  |             return new UserConfig(configManager); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								Core/Bridge/PropertyBridge.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Core/Bridge/PropertyBridge.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | using System.Text; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Bridge{ | ||||||
|  |     static class PropertyBridge{ | ||||||
|  |         public enum Environment{ | ||||||
|  |             Browser, Notification | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static string GenerateScript(Environment environment){ | ||||||
|  |             string Bool(bool value) => value ? "true;" : "false;"; | ||||||
|  |             string Str(string value) => '"'+value+"\";"; | ||||||
|  | 
 | ||||||
|  |             UserConfig config = Program.Config.User; | ||||||
|  |             StringBuilder build = new StringBuilder(128).Append("(function(x){"); | ||||||
|  | 
 | ||||||
|  |             build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover)); | ||||||
|  |              | ||||||
|  |             if (environment == Environment.Browser){ | ||||||
|  |                 build.Append("x.focusDmInput=").Append(Bool(config.FocusDmInput)); | ||||||
|  |                 build.Append("x.openSearchInFirstColumn=").Append(Bool(config.OpenSearchInFirstColumn)); | ||||||
|  |                 build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(config.KeepLikeFollowDialogsOpen)); | ||||||
|  |                 build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications)); | ||||||
|  |                 build.Append("x.notificationMediaPreviews=").Append(Bool(config.NotificationMediaPreviews)); | ||||||
|  |                 build.Append("x.translationTarget=").Append(Str(config.TranslationTarget)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (environment == Environment.Notification){ | ||||||
|  |                 build.Append("x.skipOnLinkClick=").Append(Bool(config.NotificationSkipOnLinkClick)); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             return build.Append("})(window.$TDX=window.$TDX||{})").ToString(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										139
									
								
								Core/Bridge/TweetDeckBridge.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								Core/Bridge/TweetDeckBridge.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | |||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Management; | ||||||
|  | using TweetDuck.Core.Notification; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Bridge{ | ||||||
|  |     class TweetDeckBridge{ | ||||||
|  |         public static string FontSize { get; private set; } | ||||||
|  |         public static string NotificationHeadLayout { get; private set; } | ||||||
|  |         public static readonly ContextInfo ContextInfo = new ContextInfo(); | ||||||
|  | 
 | ||||||
|  |         public static void ResetStaticProperties(){ | ||||||
|  |             FontSize = NotificationHeadLayout = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly FormBrowser form; | ||||||
|  |         private readonly FormNotificationMain notification; | ||||||
|  | 
 | ||||||
|  |         protected TweetDeckBridge(FormBrowser form, FormNotificationMain notification){ | ||||||
|  |             this.form = form; | ||||||
|  |             this.notification = notification; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Browser only | ||||||
|  | 
 | ||||||
|  |         public sealed class Browser : TweetDeckBridge{ | ||||||
|  |             public Browser(FormBrowser form, FormNotificationMain notification) : base(form, notification){} | ||||||
|  | 
 | ||||||
|  |             public void OpenContextMenu(){ | ||||||
|  |                 form.InvokeAsyncSafe(form.OpenContextMenu); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void OpenProfileImport(){ | ||||||
|  |                 form.InvokeAsyncSafe(form.OpenProfileImport); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             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 SetRightClickedLink(string type, string url){ | ||||||
|  |                 ContextInfo.SetLink(type, url); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ | ||||||
|  |                 ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void DisplayTooltip(string text){ | ||||||
|  |                 form.InvokeAsyncSafe(() => form.DisplayTooltip(text)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Notification only | ||||||
|  | 
 | ||||||
|  |         public sealed class Notification : TweetDeckBridge{ | ||||||
|  |             public Notification(FormBrowser form, FormNotificationMain notification) : base(form, notification){} | ||||||
|  | 
 | ||||||
|  |             public void DisplayTooltip(string text){ | ||||||
|  |                 notification.InvokeAsyncSafe(() => notification.DisplayTooltip(text)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void LoadNextNotification(){ | ||||||
|  |                 notification.InvokeAsyncSafe(notification.FinishCurrentNotification); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void ShowTweetDetail(){ | ||||||
|  |                 notification.InvokeAsyncSafe(notification.ShowTweetDetail); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Global | ||||||
|  | 
 | ||||||
|  |         public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){ | ||||||
|  |             notification.InvokeAsyncSafe(() => { | ||||||
|  |                 form.OnTweetNotification(); | ||||||
|  |                 notification.ShowNotification(new TweetNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl)); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnTweetSound(){ | ||||||
|  |             form.InvokeAsyncSafe(() => { | ||||||
|  |                 form.OnTweetNotification(); | ||||||
|  |                 form.OnTweetSound(); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ScreenshotTweet(string html, int width){ | ||||||
|  |             form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void PlayVideo(string url, string username){ | ||||||
|  |             form.InvokeAsyncSafe(() => form.PlayVideo(url, username)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void FixClipboard(){ | ||||||
|  |             form.InvokeAsyncSafe(WindowsUtils.ClipboardStripHtmlStyles); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OpenBrowser(string url){ | ||||||
|  |             form.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int GetIdleSeconds(){ | ||||||
|  |             return NativeMethods.GetIdleSeconds(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Alert(string type, string contents){ | ||||||
|  |             MessageBoxIcon icon; | ||||||
|  | 
 | ||||||
|  |             switch(type){ | ||||||
|  |                 case "error": icon = MessageBoxIcon.Error; break; | ||||||
|  |                 case "warning": icon = MessageBoxIcon.Warning; break; | ||||||
|  |                 case "info": icon = MessageBoxIcon.Information; break; | ||||||
|  |                 default: icon = MessageBoxIcon.None; break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             FormMessage.Show("TweetDuck Browser Message", contents, icon, FormMessage.OK); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void CrashDebug(string message){ | ||||||
|  |             #if DEBUG | ||||||
|  |             System.Diagnostics.Debug.WriteLine(message); | ||||||
|  |             System.Diagnostics.Debugger.Break(); | ||||||
|  |             #endif | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								Core/Bridge/UpdateBridge.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								Core/Bridge/UpdateBridge.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  | using System; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Updates; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Bridge{ | ||||||
|  |     class UpdateBridge{ | ||||||
|  |         private readonly UpdateHandler updates; | ||||||
|  |         private readonly Control sync; | ||||||
|  | 
 | ||||||
|  |         private UpdateInfo nextUpdate = null; | ||||||
|  | 
 | ||||||
|  |         public event EventHandler<UpdateInfo> UpdateAccepted; | ||||||
|  |         public event EventHandler<UpdateInfo> UpdateDelayed; | ||||||
|  |         public event EventHandler<UpdateInfo> UpdateDismissed; | ||||||
|  | 
 | ||||||
|  |         public UpdateBridge(UpdateHandler updates, Control sync){ | ||||||
|  |             this.sync = sync; | ||||||
|  | 
 | ||||||
|  |             this.updates = updates; | ||||||
|  |             this.updates.CheckFinished += updates_CheckFinished; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         internal void Cleanup(){ | ||||||
|  |             updates.CheckFinished -= updates_CheckFinished; | ||||||
|  |             nextUpdate?.DeleteInstaller(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ | ||||||
|  |             UpdateInfo foundUpdate = e.Result.HasValue ? e.Result.Value : null; | ||||||
|  | 
 | ||||||
|  |             if (nextUpdate != null && !nextUpdate.Equals(foundUpdate)){ | ||||||
|  |                 nextUpdate.DeleteInstaller(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             nextUpdate = foundUpdate; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void HandleInteractionEvent(EventHandler<UpdateInfo> eventHandler){ | ||||||
|  |             UpdateInfo tmpInfo = nextUpdate; | ||||||
|  | 
 | ||||||
|  |             if (tmpInfo != null){ | ||||||
|  |                 sync.InvokeAsyncSafe(() => eventHandler?.Invoke(this, tmpInfo)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Bridge methods | ||||||
|  | 
 | ||||||
|  |         public void TriggerUpdateCheck(){ | ||||||
|  |             updates.Check(false); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnUpdateAccepted(){ | ||||||
|  |             HandleInteractionEvent(UpdateAccepted); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnUpdateDelayed(){ | ||||||
|  |             HandleInteractionEvent(UpdateDelayed); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnUpdateDismissed(){ | ||||||
|  |             HandleInteractionEvent(UpdateDismissed); | ||||||
|  | 
 | ||||||
|  |             nextUpdate?.DeleteInstaller(); | ||||||
|  |             nextUpdate = null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								Core/Controls/ControlExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								Core/Controls/ControlExtensions.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | 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; | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								Core/Controls/FlatButton.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Core/Controls/FlatButton.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | 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); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								Core/Controls/FlatProgressBar.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								Core/Controls/FlatProgressBar.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | 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(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								Core/Controls/LabelVertical.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Core/Controls/LabelVertical.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | using System; | ||||||
|  | using System.Drawing; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Controls{ | ||||||
|  |     sealed class LabelVertical : Label{ | ||||||
|  |         public int LineHeight { get; set; } | ||||||
|  | 
 | ||||||
|  |         protected override void OnPaint(PaintEventArgs e){ | ||||||
|  |             int y = (int)Math.Floor((ClientRectangle.Height-Text.Length*LineHeight)/2F)-1; | ||||||
|  | 
 | ||||||
|  |             using(Brush brush = new SolidBrush(ForeColor)){ | ||||||
|  |                 foreach(char chr in Text){ | ||||||
|  |                     string str = chr.ToString(); | ||||||
|  |                     float x = (ClientRectangle.Width-e.Graphics.MeasureString(str, Font).Width)/2F; | ||||||
|  | 
 | ||||||
|  |                     e.Graphics.DrawString(str, Font, brush, x, y); | ||||||
|  |                     y += LineHeight; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								Core/Controls/NumericUpDownEx.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Core/Controls/NumericUpDownEx.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | 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,10 +1,21 @@ | |||||||
| namespace TweetDuck.Browser { | namespace TweetDuck.Core { | ||||||
|     sealed partial class FormBrowser { |     sealed partial class FormBrowser { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         private System.ComponentModel.IContainer components = null; |         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 |         #region Windows Form Designer generated code | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @@ -13,7 +24,7 @@ | |||||||
|         /// </summary> |         /// </summary> | ||||||
|         private void InitializeComponent() { |         private void InitializeComponent() { | ||||||
|             this.components = new System.ComponentModel.Container(); |             this.components = new System.ComponentModel.Container(); | ||||||
|             this.trayIcon = new TrayIcon(this.components); |             this.trayIcon = new TweetDuck.Core.Other.TrayIcon(this.components); | ||||||
|             this.toolTip = new System.Windows.Forms.ToolTip(this.components); |             this.toolTip = new System.Windows.Forms.ToolTip(this.components); | ||||||
|             this.timerResize = new System.Windows.Forms.Timer(this.components); |             this.timerResize = new System.Windows.Forms.Timer(this.components); | ||||||
|             this.SuspendLayout(); |             this.SuspendLayout(); | ||||||
| @@ -27,11 +38,10 @@ | |||||||
|             //  |             //  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||||
|             this.BackColor = TweetLib.Core.Features.TweetDeck.TweetDeckBrowser.BackgroundColor; |             this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor; | ||||||
|             this.ClientSize = new System.Drawing.Size(1008, 730); |             this.ClientSize = new System.Drawing.Size(1008, 730); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.Icon = Properties.Resources.icon; |             this.Icon = Properties.Resources.icon; | ||||||
|             this.Location = TweetDuck.Controls.ControlExtensions.InvisibleLocation; |             this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation; | ||||||
|             this.MinimumSize = new System.Drawing.Size(348, 424); |             this.MinimumSize = new System.Drawing.Size(348, 424); | ||||||
|             this.Name = "FormBrowser"; |             this.Name = "FormBrowser"; | ||||||
|             this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; |             this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; | ||||||
| @@ -47,7 +57,7 @@ | |||||||
| 
 | 
 | ||||||
|         #endregion |         #endregion | ||||||
| 
 | 
 | ||||||
|         private TrayIcon trayIcon; |         private TweetDuck.Core.Other.TrayIcon trayIcon; | ||||||
|         private System.Windows.Forms.ToolTip toolTip; |         private System.Windows.Forms.ToolTip toolTip; | ||||||
|         private System.Windows.Forms.Timer timerResize; |         private System.Windows.Forms.Timer timerResize; | ||||||
|     } |     } | ||||||
							
								
								
									
										577
									
								
								Core/FormBrowser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										577
									
								
								Core/FormBrowser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,577 @@ | |||||||
|  | using System; | ||||||
|  | using System.Drawing; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using Microsoft.WindowsAPICodePack.Taskbar; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Core.Bridge; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Handling; | ||||||
|  | using TweetDuck.Core.Handling.General; | ||||||
|  | using TweetDuck.Core.Management; | ||||||
|  | using TweetDuck.Core.Notification; | ||||||
|  | using TweetDuck.Core.Notification.Screenshot; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Other.Analytics; | ||||||
|  | using TweetDuck.Core.Other.Settings.Dialogs; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Plugins.Events; | ||||||
|  | using TweetDuck.Resources; | ||||||
|  | using TweetDuck.Updates; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core{ | ||||||
|  |     sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{ | ||||||
|  |         private static UserConfig Config => Program.Config.User; | ||||||
|  | 
 | ||||||
|  |         public bool IsWaiting{ | ||||||
|  |             set{ | ||||||
|  |                 if (value){ | ||||||
|  |                     browser.Enabled = false; | ||||||
|  |                     Cursor = Cursors.WaitCursor; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     browser.Enabled = true; | ||||||
|  |                     Cursor = Cursors.Default; | ||||||
|  | 
 | ||||||
|  |                     if (Focused){ // re-focus browser only if the window or a child is activated | ||||||
|  |                         browser.Focus(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string UpdateInstallerPath { get; private set; } | ||||||
|  |         private bool ignoreUpdateCheckError; | ||||||
|  |          | ||||||
|  |         public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy; | ||||||
|  | 
 | ||||||
|  |         private readonly TweetDeckBrowser browser; | ||||||
|  |         private readonly PluginManager plugins; | ||||||
|  |         private readonly UpdateHandler updates; | ||||||
|  |         private readonly FormNotificationTweet notification; | ||||||
|  |         private readonly ContextMenu contextMenu; | ||||||
|  |         private readonly UpdateBridge updateBridge; | ||||||
|  |         private readonly TaskbarIcon taskbarIcon; | ||||||
|  | 
 | ||||||
|  |         private bool isLoaded; | ||||||
|  |         private FormWindowState prevState; | ||||||
|  |          | ||||||
|  |         private TweetScreenshotManager notificationScreenshotManager; | ||||||
|  |         private VideoPlayer videoPlayer; | ||||||
|  |         private AnalyticsManager analytics; | ||||||
|  | 
 | ||||||
|  |         public FormBrowser(){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             Text = Program.BrandName; | ||||||
|  |              | ||||||
|  |             this.plugins = new PluginManager(Program.Config.Plugins, Program.PluginPath); | ||||||
|  |             this.plugins.Reloaded += plugins_Reloaded; | ||||||
|  |             this.plugins.Executed += plugins_Executed; | ||||||
|  |             this.plugins.Reload(); | ||||||
|  | 
 | ||||||
|  |             this.notification = new FormNotificationTweet(this, plugins); | ||||||
|  |             this.notification.Show(); | ||||||
|  |              | ||||||
|  |             this.updates = new UpdateHandler(Program.InstallerPath); | ||||||
|  |             this.updates.CheckFinished += updates_CheckFinished; | ||||||
|  | 
 | ||||||
|  |             this.updateBridge = new UpdateBridge(updates, this); | ||||||
|  |             this.updateBridge.UpdateAccepted += updateBridge_UpdateAccepted; | ||||||
|  |             this.updateBridge.UpdateDelayed += updateBridge_UpdateDelayed; | ||||||
|  |             this.updateBridge.UpdateDismissed += updateBridge_UpdateDismissed; | ||||||
|  | 
 | ||||||
|  |             this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge.Browser(this, notification), updateBridge); | ||||||
|  |             this.contextMenu = ContextMenuBrowser.CreateMenu(this); | ||||||
|  | 
 | ||||||
|  |             Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update | ||||||
|  | 
 | ||||||
|  |             this.taskbarIcon = new TaskbarIcon(); | ||||||
|  |             Shown += (sender, args) => taskbarIcon.UpdateIcon(); | ||||||
|  | 
 | ||||||
|  |             Disposed += (sender, args) => { | ||||||
|  |                 Config.MuteToggled -= Config_MuteToggled; | ||||||
|  |                 Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged; | ||||||
|  |                  | ||||||
|  |                 browser.Dispose(); | ||||||
|  |                 updates.Dispose(); | ||||||
|  |                 contextMenu.Dispose(); | ||||||
|  |                 taskbarIcon.Dispose(); | ||||||
|  | 
 | ||||||
|  |                 notificationScreenshotManager?.Dispose(); | ||||||
|  |                 videoPlayer?.Dispose(); | ||||||
|  |                 analytics?.Dispose(); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             Config.MuteToggled += Config_MuteToggled; | ||||||
|  | 
 | ||||||
|  |             this.trayIcon.ClickRestore += trayIcon_ClickRestore; | ||||||
|  |             this.trayIcon.ClickClose += trayIcon_ClickClose; | ||||||
|  |             Config.TrayBehaviorChanged += Config_TrayBehaviorChanged; | ||||||
|  | 
 | ||||||
|  |             UpdateTray(); | ||||||
|  | 
 | ||||||
|  |             if (Config.AllowDataCollection){ | ||||||
|  |                 analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             RestoreWindow(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void ShowChildForm(Form form){ | ||||||
|  |             form.VisibleChanged += (sender, args) => form.MoveToCenter(this); | ||||||
|  |             form.Show(this); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ForceClose(){ | ||||||
|  |             trayIcon.Visible = false; // checked in FormClosing event | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // window setup | ||||||
|  | 
 | ||||||
|  |         private void RestoreWindow(){ | ||||||
|  |             Config.BrowserWindow.Restore(this, true); | ||||||
|  |             prevState = WindowState; | ||||||
|  |             isLoaded = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void UpdateTray(){ | ||||||
|  |             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; | ||||||
|  |             taskbarIcon.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){ | ||||||
|  |                 updateBridge.Cleanup(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Config_MuteToggled(object sender, EventArgs e){ | ||||||
|  |             AnalyticsFile.NotificationMutes.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Config_TrayBehaviorChanged(object sender, EventArgs e){ | ||||||
|  |             UpdateTray(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void trayIcon_ClickRestore(object sender, EventArgs e){ | ||||||
|  |             Show(); | ||||||
|  |             RestoreWindow(); | ||||||
|  |             Activate(); | ||||||
|  |             UpdateTray(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void trayIcon_ClickClose(object sender, EventArgs e){ | ||||||
|  |             ForceClose(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private void plugins_Reloaded(object sender, PluginErrorEventArgs e){ | ||||||
|  |             if (e.HasErrors){ | ||||||
|  |                 FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (isLoaded){ | ||||||
|  |                 browser.ReloadToTweetDeck(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private static void plugins_Executed(object sender, PluginErrorEventArgs e){ | ||||||
|  |             if (e.HasErrors){ | ||||||
|  |                 FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ | ||||||
|  |             e.Result.Handle(update => { | ||||||
|  |                 string tag = update.VersionTag; | ||||||
|  | 
 | ||||||
|  |                 if (tag != Program.VersionTag && tag != Config.DismissedUpdate){ | ||||||
|  |                     update.BeginSilentDownload(); | ||||||
|  |                     browser.ShowUpdateNotification(tag, update.ReleaseNotes); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     updates.StartTimer(); | ||||||
|  |                 } | ||||||
|  |             }, ex => { | ||||||
|  |                 if (!ignoreUpdateCheckError){ | ||||||
|  |                     Program.Reporter.HandleException("Update Check Error", "An error occurred while checking for updates.", true, ex); | ||||||
|  |                     updates.StartTimer(); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             ignoreUpdateCheckError = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void updateBridge_UpdateAccepted(object sender, UpdateInfo update){ | ||||||
|  |             FormManager.CloseAllDialogs(); | ||||||
|  | 
 | ||||||
|  |             if (!string.IsNullOrEmpty(Config.DismissedUpdate)){ | ||||||
|  |                 Config.DismissedUpdate = null; | ||||||
|  |                 Config.Save(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             void OnFinished(){ | ||||||
|  |                 UpdateDownloadStatus status = update.DownloadStatus; | ||||||
|  | 
 | ||||||
|  |                 if (status == UpdateDownloadStatus.Done){ | ||||||
|  |                     UpdateInstallerPath = update.InstallerPath; | ||||||
|  |                     ForceClose(); | ||||||
|  |                 } | ||||||
|  |                 else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: "+(update.DownloadError?.Message ?? "unknown error")+"\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){ | ||||||
|  |                     BrowserUtils.OpenExternalBrowser(Program.Website); | ||||||
|  |                     ForceClose(); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     Show(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if (update.DownloadStatus.IsFinished(true)){ | ||||||
|  |                 OnFinished(); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 FormUpdateDownload downloadForm = new FormUpdateDownload(update); | ||||||
|  | 
 | ||||||
|  |                 downloadForm.VisibleChanged += (sender2, args2) => { | ||||||
|  |                     downloadForm.MoveToCenter(this); | ||||||
|  |                     Hide(); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 downloadForm.FormClosed += (sender2, args2) => { | ||||||
|  |                     if (downloadForm.DialogResult != DialogResult.OK){ | ||||||
|  |                         update.CancelDownload(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     downloadForm.Dispose(); | ||||||
|  |                     OnFinished(); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 downloadForm.Show(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void updateBridge_UpdateDelayed(object sender, UpdateInfo update){ | ||||||
|  |             // stops the timer | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void updateBridge_UpdateDismissed(object sender, UpdateInfo update){ | ||||||
|  |             Config.DismissedUpdate = update.VersionTag; | ||||||
|  |             Config.Save(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override void WndProc(ref Message m){ | ||||||
|  |             if (isLoaded && m.Msg == Program.WindowRestoreMessage){ | ||||||
|  |                 if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){ | ||||||
|  |                     trayIcon_ClickRestore(trayIcon, EventArgs.Empty); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if (browser.Ready && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){ | ||||||
|  |                 if (videoPlayer != null && videoPlayer.Running){ | ||||||
|  |                     videoPlayer.Close(); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     browser.OnMouseClickExtra(m.WParam); | ||||||
|  |                     AnalyticsFile.BrowserExtraMouseButtons.Trigger(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             base.WndProc(ref m); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // bridge methods | ||||||
|  | 
 | ||||||
|  |         public void PauseNotification(){ | ||||||
|  |             notification.PauseNotification(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ResumeNotification(){ | ||||||
|  |             notification.ResumeNotification(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public void ReinjectCustomCSS(string css){ | ||||||
|  |             browser.ReinjectCustomCSS(css); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ReloadToTweetDeck(){ | ||||||
|  |             #if DEBUG | ||||||
|  |             ScriptLoader.HotSwap(); | ||||||
|  |             #else | ||||||
|  |             if (ModifierKeys.HasFlag(Keys.Shift)){ | ||||||
|  |                 ScriptLoader.ClearCache(); | ||||||
|  |             } | ||||||
|  |             #endif | ||||||
|  | 
 | ||||||
|  |             ignoreUpdateCheckError = false; | ||||||
|  |             browser.ReloadToTweetDeck(); | ||||||
|  |             AnalyticsFile.BrowserReloads.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void AddSearchColumn(string query){ | ||||||
|  |             browser.AddSearchColumn(query); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void TriggerTweetScreenshot(){ | ||||||
|  |             browser.TriggerTweetScreenshot(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ReloadColumns(){ | ||||||
|  |             browser.ReloadColumns(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void PlaySoundNotification(){ | ||||||
|  |             browser.PlaySoundNotification(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ApplyROT13(){ | ||||||
|  |             browser.ApplyROT13(); | ||||||
|  |             AnalyticsFile.UsedROT13.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OpenDevTools(){ | ||||||
|  |             browser.OpenDevTools(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // callback handlers | ||||||
|  |          | ||||||
|  |         public void OnIntroductionClosed(bool showGuide, bool allowDataCollection){ | ||||||
|  |             if (Config.FirstRun){ | ||||||
|  |                 Config.FirstRun = false; | ||||||
|  |                 Config.AllowDataCollection = allowDataCollection; | ||||||
|  |                 Config.Save(); | ||||||
|  | 
 | ||||||
|  |                 if (allowDataCollection && analytics == null){ | ||||||
|  |                     analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (showGuide){ | ||||||
|  |                 FormGuide.Show(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OpenContextMenu(){ | ||||||
|  |             contextMenu.Show(this, PointToClient(Cursor.Position)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OpenSettings(){ | ||||||
|  |             OpenSettings(null); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public void OpenSettings(Type startTab){ | ||||||
|  |             if (!FormManager.TryBringToFront<FormSettings>()){ | ||||||
|  |                 bool prevEnableUpdateCheck = Config.EnableUpdateCheck; | ||||||
|  | 
 | ||||||
|  |                 FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab); | ||||||
|  |                  | ||||||
|  |                 form.FormClosed += (sender, args) => { | ||||||
|  |                     if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){ | ||||||
|  |                         Config.DismissedUpdate = null; | ||||||
|  |                         Config.Save(); | ||||||
|  |                          | ||||||
|  |                         updates.Check(true); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (!Config.EnableTrayHighlight){ | ||||||
|  |                         trayIcon.HasNotifications = false; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (Config.AllowDataCollection){ | ||||||
|  |                         if (analytics == null){ | ||||||
|  |                             analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else if (analytics != null){ | ||||||
|  |                         analytics.Dispose(); | ||||||
|  |                         analytics = null; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     BrowserCache.RefreshTimer(); | ||||||
|  |                      | ||||||
|  |                     if (form.ShouldReloadBrowser){ | ||||||
|  |                         FormManager.TryFind<FormPlugins>()?.Close(); | ||||||
|  |                         plugins.Reload(); // also reloads the browser | ||||||
|  |                     } | ||||||
|  |                     else{ | ||||||
|  |                         browser.UpdateProperties(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     notification.RequiresResize = true; | ||||||
|  |                     form.Dispose(); | ||||||
|  |                 }; | ||||||
|  |                  | ||||||
|  |                 AnalyticsFile.OpenOptions.Trigger(); | ||||||
|  |                 ShowChildForm(form); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OpenAbout(){ | ||||||
|  |             if (!FormManager.TryBringToFront<FormAbout>()){ | ||||||
|  |                 AnalyticsFile.OpenAbout.Trigger(); | ||||||
|  |                 ShowChildForm(new FormAbout()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OpenPlugins(){ | ||||||
|  |             if (!FormManager.TryBringToFront<FormPlugins>()){ | ||||||
|  |                 AnalyticsFile.OpenPlugins.Trigger(); | ||||||
|  |                 ShowChildForm(new FormPlugins(plugins)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OpenProfileImport(){ | ||||||
|  |             FormManager.TryFind<FormSettings>()?.Close(); | ||||||
|  | 
 | ||||||
|  |             using(DialogSettingsManage dialog = new DialogSettingsManage(plugins, true)){ | ||||||
|  |                 if (!dialog.IsDisposed && dialog.ShowDialog() == DialogResult.OK && !dialog.IsRestarting){ // needs disposal check because the dialog may be closed in constructor | ||||||
|  |                     BrowserProcessHandler.UpdatePrefs(); | ||||||
|  |                     FormManager.TryFind<FormPlugins>()?.Close(); | ||||||
|  |                     plugins.Reload(); // also reloads the browser | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnTweetNotification(){ // may be called multiple times, once for each type of notification | ||||||
|  |             if (!ContainsFocus){ | ||||||
|  |                 if (Config.EnableTrayHighlight){ | ||||||
|  |                     trayIcon.HasNotifications = true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 taskbarIcon.HasNotifications = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnTweetSound(){ | ||||||
|  |             AnalyticsFile.SoundNotifications.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void PlayVideo(string url, string username){ | ||||||
|  |             if (string.IsNullOrEmpty(url)){ | ||||||
|  |                 videoPlayer?.Close(); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (videoPlayer == null){ | ||||||
|  |                 videoPlayer = new VideoPlayer(this); | ||||||
|  | 
 | ||||||
|  |                 videoPlayer.ProcessExited += (sender, args) => { | ||||||
|  |                     browser.HideVideoOverlay(true); | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             videoPlayer.Launch(url, username); | ||||||
|  |             AnalyticsFile.VideoPlays.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool ProcessBrowserKey(Keys key){ | ||||||
|  |             if (videoPlayer != null && videoPlayer.Running){ | ||||||
|  |                 videoPlayer.SendKeyEvent(key); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){ | ||||||
|  |             Activate(); | ||||||
|  | 
 | ||||||
|  |             if (!browser.IsTweetDeckWebsite){ | ||||||
|  |                 FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             notification.FinishCurrentNotification(); | ||||||
|  |             browser.ShowTweetDetail(columnId, chirpId, fallbackUrl); | ||||||
|  |             AnalyticsFile.TweetDetails.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnTweetScreenshotReady(string html, int width){ | ||||||
|  |             if (notificationScreenshotManager == null){ | ||||||
|  |                 notificationScreenshotManager = new TweetScreenshotManager(this, plugins); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             notificationScreenshotManager.Trigger(html, width); | ||||||
|  |             AnalyticsFile.TweetScreenshots.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void DisplayTooltip(string text){ | ||||||
|  |             if (string.IsNullOrEmpty(text)){ | ||||||
|  |                 toolTip.Hide(this); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 Point position = PointToClient(Cursor.Position); | ||||||
|  |                 position.Offset(20, 10); | ||||||
|  |                 toolTip.Show(text, this, position); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								Core/FormManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								Core/FormManager.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | using System.Linq; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | 
 | ||||||
|  | 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 bool HasAnyDialogs => Application.OpenForms.OfType<IAppDialog>().Any(); | ||||||
|  |          | ||||||
|  |         public static void CloseAllDialogs(){ | ||||||
|  |             foreach(IAppDialog dialog in Application.OpenForms.OfType<IAppDialog>().Reverse()){ | ||||||
|  |                 ((Form)dialog).Close(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public interface IAppDialog{} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										229
									
								
								Core/Handling/ContextMenuBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								Core/Handling/ContextMenuBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | |||||||
|  | using System; | ||||||
|  | using System.Text.RegularExpressions; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using System.Linq; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Core.Bridge; | ||||||
|  | using TweetDuck.Core.Management; | ||||||
|  | using TweetDuck.Core.Notification; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Other.Analytics; | ||||||
|  | using TweetDuck.Resources; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     abstract class ContextMenuBase : IContextMenuHandler{ | ||||||
|  |         protected static UserConfig Config => Program.Config.User; | ||||||
|  | 
 | ||||||
|  |         private static TwitterUtils.ImageQuality ImageQuality => Config.TwitterImageQuality; | ||||||
|  |          | ||||||
|  |         private const CefMenuCommand MenuOpenLinkUrl     = (CefMenuCommand)26500; | ||||||
|  |         private const CefMenuCommand MenuCopyLinkUrl     = (CefMenuCommand)26501; | ||||||
|  |         private const CefMenuCommand MenuCopyUsername    = (CefMenuCommand)26502; | ||||||
|  |         private const CefMenuCommand MenuViewImage       = (CefMenuCommand)26503; | ||||||
|  |         private const CefMenuCommand MenuOpenMediaUrl    = (CefMenuCommand)26504; | ||||||
|  |         private const CefMenuCommand MenuCopyMediaUrl    = (CefMenuCommand)26505; | ||||||
|  |         private const CefMenuCommand MenuSaveMedia       = (CefMenuCommand)26506; | ||||||
|  |         private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507; | ||||||
|  |         private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508; | ||||||
|  |         private const CefMenuCommand MenuReadApplyROT13  = (CefMenuCommand)26509; | ||||||
|  |         private const CefMenuCommand MenuOpenDevTools    = (CefMenuCommand)26599; | ||||||
|  |          | ||||||
|  |         protected ContextInfo.ContextData Context { get; private set; } | ||||||
|  | 
 | ||||||
|  |         private readonly AnalyticsFile.IProvider analytics; | ||||||
|  | 
 | ||||||
|  |         protected ContextMenuBase(AnalyticsFile.IProvider analytics){ | ||||||
|  |             this.analytics = analytics; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ | ||||||
|  |             if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ | ||||||
|  |                 Context = TweetDeckBridge.ContextInfo.Reset(); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 Context = TweetDeckBridge.ContextInfo.Create(parameters); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){ | ||||||
|  |                 model.AddItem(MenuSearchInBrowser, "Search in browser"); | ||||||
|  |                 model.AddSeparator(); | ||||||
|  |                 model.AddItem(MenuReadApplyROT13, "Apply ROT13"); | ||||||
|  |                 model.AddSeparator(); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             string TextOpen(string name) => "Open "+name+" in browser"; | ||||||
|  |             string TextCopy(string name) => "Copy "+name+" address"; | ||||||
|  |             string TextSave(string name) => "Save "+name+" as..."; | ||||||
|  |              | ||||||
|  |             if (Context.Types.HasFlag(ContextInfo.ContextType.Link) && !Context.UnsafeLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){ | ||||||
|  |                 if (TwitterUtils.RegexAccount.IsMatch(Context.UnsafeLinkUrl)){ | ||||||
|  |                     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 (Context.Types.HasFlag(ContextInfo.ContextType.Video)){ | ||||||
|  |                 model.AddItem(MenuOpenMediaUrl, TextOpen("video")); | ||||||
|  |                 model.AddItem(MenuCopyMediaUrl, TextCopy("video")); | ||||||
|  |                 model.AddItem(MenuSaveMedia, TextSave("video")); | ||||||
|  |                 model.AddSeparator(); | ||||||
|  |             } | ||||||
|  |             else if (Context.Types.HasFlag(ContextInfo.ContextType.Image) && Context.MediaUrl != TweetNotification.AppLogo.Url){ | ||||||
|  |                 model.AddItem(MenuViewImage, "View image in photo viewer"); | ||||||
|  |                 model.AddItem(MenuOpenMediaUrl, TextOpen("image")); | ||||||
|  |                 model.AddItem(MenuCopyMediaUrl, TextCopy("image")); | ||||||
|  |                 model.AddItem(MenuSaveMedia, TextSave("image")); | ||||||
|  | 
 | ||||||
|  |                 if (Context.Chirp.Images.Length > 1){ | ||||||
|  |                     model.AddItem(MenuSaveTweetImages, TextSave("all images")); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 model.AddSeparator(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ | ||||||
|  |             Control control = browserControl.AsControl(); | ||||||
|  | 
 | ||||||
|  |             switch(commandId){ | ||||||
|  |                 case MenuOpenLinkUrl: | ||||||
|  |                     OpenBrowser(control, Context.LinkUrl); | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MenuCopyLinkUrl: | ||||||
|  |                     SetClipboardText(control, Context.UnsafeLinkUrl); | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MenuCopyUsername: { | ||||||
|  |                     string url = Context.UnsafeLinkUrl; | ||||||
|  |                     Match match = TwitterUtils.RegexAccount.Match(url); | ||||||
|  | 
 | ||||||
|  |                     SetClipboardText(control, match.Success ? match.Groups[1].Value : url); | ||||||
|  |                     control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 case MenuOpenMediaUrl: | ||||||
|  |                     OpenBrowser(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality)); | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MenuCopyMediaUrl: | ||||||
|  |                     SetClipboardText(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality)); | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MenuViewImage: { | ||||||
|  |                     string url = Context.MediaUrl; | ||||||
|  | 
 | ||||||
|  |                     control.InvokeAsyncSafe(() => { | ||||||
|  |                         TwitterUtils.ViewImage(url, ImageQuality); | ||||||
|  |                         analytics.AnalyticsFile.ViewedImages.Trigger(); | ||||||
|  |                     }); | ||||||
|  |                      | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 case MenuSaveMedia: { | ||||||
|  |                     bool isVideo = Context.Types.HasFlag(ContextInfo.ContextType.Video); | ||||||
|  |                     string url = Context.MediaUrl; | ||||||
|  |                     string username = Context.Chirp.Authors.LastOrDefault(); | ||||||
|  |                      | ||||||
|  |                     control.InvokeAsyncSafe(() => { | ||||||
|  |                         if (isVideo){ | ||||||
|  |                             TwitterUtils.DownloadVideo(url, username); | ||||||
|  |                             analytics.AnalyticsFile.DownloadedVideos.Trigger(); | ||||||
|  |                         } | ||||||
|  |                         else{ | ||||||
|  |                             TwitterUtils.DownloadImage(url, username, ImageQuality); | ||||||
|  |                             analytics.AnalyticsFile.DownloadedImages.Trigger(); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 case MenuSaveTweetImages: { | ||||||
|  |                     string[] urls = Context.Chirp.Images; | ||||||
|  |                     string username = Context.Chirp.Authors.LastOrDefault(); | ||||||
|  |                      | ||||||
|  |                     control.InvokeAsyncSafe(() => { | ||||||
|  |                         TwitterUtils.DownloadImages(urls, username, ImageQuality); | ||||||
|  |                         analytics.AnalyticsFile.DownloadedImages.Trigger(); | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 case MenuReadApplyROT13: | ||||||
|  |                     string selection = parameters.SelectionText; | ||||||
|  |                     control.InvokeAsyncSafe(() => FormMessage.Information("ROT13", StringUtils.ConvertRot13(selection), FormMessage.OK)); | ||||||
|  |                     control.InvokeAsyncSafe(analytics.AnalyticsFile.UsedROT13.Trigger); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuSearchInBrowser: | ||||||
|  |                     string query = parameters.SelectionText; | ||||||
|  |                     control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalSearch(query)); | ||||||
|  |                     DeselectAll(frame); | ||||||
|  |                     break; | ||||||
|  |                      | ||||||
|  |                 case MenuOpenDevTools: | ||||||
|  |                     browserControl.ShowDevTools(); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){ | ||||||
|  |             Context = TweetDeckBridge.ContextInfo.Reset(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){ | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected static void DeselectAll(IFrame frame){ | ||||||
|  |             ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected static void OpenBrowser(Control control, string url){ | ||||||
|  |             control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected static void SetClipboardText(Control control, string text){ | ||||||
|  |             control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected static void InsertSelectionSearchItem(IMenuModel model, CefMenuCommand insertCommand, string insertLabel){ | ||||||
|  |             model.InsertItemAt(model.GetIndexOf(MenuSearchInBrowser)+1, insertCommand, insertLabel); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         protected static void AddDebugMenuItems(IMenuModel model){ | ||||||
|  |             if (BrowserUtils.HasDevTools){ | ||||||
|  |                 AddSeparator(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(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										179
									
								
								Core/Handling/ContextMenuBrowser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								Core/Handling/ContextMenuBrowser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | |||||||
|  | using CefSharp; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Management; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     sealed class ContextMenuBrowser : ContextMenuBase{ | ||||||
|  |         private const CefMenuCommand MenuGlobal   = (CefMenuCommand)26600; | ||||||
|  |         private const CefMenuCommand MenuMute     = (CefMenuCommand)26601; | ||||||
|  |         private const CefMenuCommand MenuSettings = (CefMenuCommand)26602; | ||||||
|  |         private const CefMenuCommand MenuPlugins  = (CefMenuCommand)26003; | ||||||
|  |         private const CefMenuCommand MenuAbout    = (CefMenuCommand)26604; | ||||||
|  |          | ||||||
|  |         private const CefMenuCommand MenuOpenTweetUrl       = (CefMenuCommand)26610; | ||||||
|  |         private const CefMenuCommand MenuCopyTweetUrl       = (CefMenuCommand)26611; | ||||||
|  |         private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612; | ||||||
|  |         private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613; | ||||||
|  |         private const CefMenuCommand MenuScreenshotTweet    = (CefMenuCommand)26614; | ||||||
|  |         private const CefMenuCommand MenuWriteApplyROT13    = (CefMenuCommand)26615; | ||||||
|  |         private const CefMenuCommand MenuSearchInColumn     = (CefMenuCommand)26616; | ||||||
|  | 
 | ||||||
|  |         private const string TitleReloadBrowser = "Reload browser"; | ||||||
|  |         private const string TitleMuteNotifications = "Mute notifications"; | ||||||
|  |         private const string TitleSettings = "Options"; | ||||||
|  |         private const string TitlePlugins = "Plugins"; | ||||||
|  |         private const string TitleAboutProgram = "About "+Program.BrandName; | ||||||
|  | 
 | ||||||
|  |         private readonly FormBrowser form; | ||||||
|  |          | ||||||
|  |         public ContextMenuBrowser(FormBrowser form) : base(form){ | ||||||
|  |             this.form = form; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ | ||||||
|  |             bool isSelecting = parameters.TypeFlags.HasFlag(ContextMenuType.Selection); | ||||||
|  |             bool isEditing = parameters.TypeFlags.HasFlag(ContextMenuType.Editable); | ||||||
|  | 
 | ||||||
|  |             model.Remove(CefMenuCommand.Back); | ||||||
|  |             model.Remove(CefMenuCommand.Forward); | ||||||
|  |             model.Remove(CefMenuCommand.Print); | ||||||
|  |             model.Remove(CefMenuCommand.ViewSource); | ||||||
|  |             RemoveSeparatorIfLast(model); | ||||||
|  | 
 | ||||||
|  |             if (isSelecting){ | ||||||
|  |                 if (isEditing){ | ||||||
|  |                     model.AddSeparator(); | ||||||
|  |                     model.AddItem(MenuWriteApplyROT13, "Apply ROT13"); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 model.AddSeparator(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); | ||||||
|  | 
 | ||||||
|  |             if (isSelecting && !isEditing && TwitterUtils.IsTweetDeckWebsite(frame)){ | ||||||
|  |                 InsertSelectionSearchItem(model, MenuSearchInColumn, "Search in a column"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (Context.Types.HasFlag(ContextInfo.ContextType.Chirp) && !isSelecting && !isEditing){ | ||||||
|  |                 model.AddItem(MenuOpenTweetUrl, "Open tweet in browser"); | ||||||
|  |                 model.AddItem(MenuCopyTweetUrl, "Copy tweet address"); | ||||||
|  |                 model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard"); | ||||||
|  | 
 | ||||||
|  |                 if (!string.IsNullOrEmpty(Context.Chirp.QuoteUrl)){ | ||||||
|  |                     model.AddSeparator(); | ||||||
|  |                     model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser"); | ||||||
|  |                     model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 model.AddSeparator(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!isSelecting && !isEditing){ | ||||||
|  |                 AddSeparator(model); | ||||||
|  | 
 | ||||||
|  |                 IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName); | ||||||
|  |              | ||||||
|  |                 globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser); | ||||||
|  |                 globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications); | ||||||
|  |                 globalMenu.SetChecked(MenuMute, Config.MuteNotifications); | ||||||
|  |                 globalMenu.AddSeparator(); | ||||||
|  | 
 | ||||||
|  |                 globalMenu.AddItem(MenuSettings, TitleSettings); | ||||||
|  |                 globalMenu.AddItem(MenuPlugins, TitlePlugins); | ||||||
|  |                 globalMenu.AddItem(MenuAbout, TitleAboutProgram); | ||||||
|  |                  | ||||||
|  |                 AddDebugMenuItems(globalMenu); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             RemoveSeparatorIfLast(model); | ||||||
|  |              | ||||||
|  |             form.InvokeAsyncSafe(form.AnalyticsFile.BrowserContextMenus.Trigger); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ | ||||||
|  |             if (base.OnContextMenuCommand(browserControl, browser, frame, parameters, commandId, eventFlags)){ | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch(commandId){ | ||||||
|  |                 case CefMenuCommand.Reload: | ||||||
|  |                     form.InvokeAsyncSafe(form.ReloadToTweetDeck); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuSettings: | ||||||
|  |                     form.InvokeAsyncSafe(form.OpenSettings); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuAbout: | ||||||
|  |                     form.InvokeAsyncSafe(form.OpenAbout); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuPlugins: | ||||||
|  |                     form.InvokeAsyncSafe(form.OpenPlugins); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuMute: | ||||||
|  |                     form.InvokeAsyncSafe(ToggleMuteNotifications); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuOpenTweetUrl: | ||||||
|  |                     OpenBrowser(form, Context.Chirp.TweetUrl); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuCopyTweetUrl: | ||||||
|  |                     SetClipboardText(form, Context.Chirp.TweetUrl); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuScreenshotTweet: | ||||||
|  |                     form.InvokeAsyncSafe(form.TriggerTweetScreenshot); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuOpenQuotedTweetUrl: | ||||||
|  |                     OpenBrowser(form, Context.Chirp.QuoteUrl); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuCopyQuotedTweetUrl: | ||||||
|  |                     SetClipboardText(form, Context.Chirp.QuoteUrl); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuWriteApplyROT13: | ||||||
|  |                     form.InvokeAsyncSafe(form.ApplyROT13); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuSearchInColumn: | ||||||
|  |                     string query = parameters.SelectionText; | ||||||
|  |                     form.InvokeAsyncSafe(() => form.AddSearchColumn(query)); | ||||||
|  |                     DeselectAll(frame); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static ContextMenu CreateMenu(FormBrowser form){ | ||||||
|  |             ContextMenu menu = new ContextMenu(); | ||||||
|  | 
 | ||||||
|  |             menu.MenuItems.Add(TitleReloadBrowser, (sender, args) => form.ReloadToTweetDeck()); | ||||||
|  |             menu.MenuItems.Add(TitleMuteNotifications, (sender, args) => ToggleMuteNotifications()); | ||||||
|  |             menu.MenuItems.Add("-"); | ||||||
|  |             menu.MenuItems.Add(TitleSettings, (sender, args) => form.OpenSettings()); | ||||||
|  |             menu.MenuItems.Add(TitlePlugins, (sender, args) => form.OpenPlugins()); | ||||||
|  |             menu.MenuItems.Add(TitleAboutProgram,  (sender, args) => form.OpenAbout()); | ||||||
|  | 
 | ||||||
|  |             menu.Popup += (sender, args) => { | ||||||
|  |                 menu.MenuItems[1].Checked = Config.MuteNotifications; | ||||||
|  |                 form.AnalyticsFile.BrowserContextMenus.Trigger(); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             return menu; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void ToggleMuteNotifications(){ | ||||||
|  |             Config.MuteNotifications = !Config.MuteNotifications; | ||||||
|  |             Config.Save(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								Core/Handling/ContextMenuGuide.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Core/Handling/ContextMenuGuide.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Other.Analytics; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     sealed class ContextMenuGuide : ContextMenuBase{ | ||||||
|  |         public ContextMenuGuide(AnalyticsFile.IProvider analytics) : base(analytics){} | ||||||
|  | 
 | ||||||
|  |         public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ | ||||||
|  |             model.Clear(); | ||||||
|  |             base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); | ||||||
|  |             AddDebugMenuItems(model); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								Core/Handling/ContextMenuNotification.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								Core/Handling/ContextMenuNotification.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Notification; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     sealed class ContextMenuNotification : ContextMenuBase{ | ||||||
|  |         private const CefMenuCommand MenuViewDetail         = (CefMenuCommand)26600; | ||||||
|  |         private const CefMenuCommand MenuSkipTweet          = (CefMenuCommand)26601; | ||||||
|  |         private const CefMenuCommand MenuFreeze             = (CefMenuCommand)26602; | ||||||
|  |         private const CefMenuCommand MenuCopyTweetUrl       = (CefMenuCommand)26603; | ||||||
|  |         private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26604; | ||||||
|  | 
 | ||||||
|  |         private readonly FormNotificationBase form; | ||||||
|  |         private readonly bool enableCustomMenu; | ||||||
|  | 
 | ||||||
|  |         public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){ | ||||||
|  |             this.form = form; | ||||||
|  |             this.enableCustomMenu = enableCustomMenu; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ | ||||||
|  |             model.Clear(); | ||||||
|  | 
 | ||||||
|  |             if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){ | ||||||
|  |                 model.AddItem(CefMenuCommand.Copy, "Copy"); | ||||||
|  |                 model.AddSeparator(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); | ||||||
|  | 
 | ||||||
|  |             if (enableCustomMenu){ | ||||||
|  |                 if (form.CanViewDetail){ | ||||||
|  |                     model.AddItem(MenuViewDetail, "View detail"); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 model.AddItem(MenuSkipTweet, "Skip tweet"); | ||||||
|  |                 model.AddCheckItem(MenuFreeze, "Freeze"); | ||||||
|  |                 model.SetChecked(MenuFreeze, form.FreezeTimer); | ||||||
|  | 
 | ||||||
|  |                 if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){ | ||||||
|  |                     model.AddSeparator(); | ||||||
|  |                     model.AddItem(MenuCopyTweetUrl, "Copy tweet address"); | ||||||
|  | 
 | ||||||
|  |                     if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){ | ||||||
|  |                         model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             AddDebugMenuItems(model); | ||||||
|  |             RemoveSeparatorIfLast(model); | ||||||
|  | 
 | ||||||
|  |             form.InvokeAsyncSafe(() => { | ||||||
|  |                 form.ContextMenuOpen = true; | ||||||
|  |                 form.AnalyticsFile.NotificationContextMenus.Trigger(); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ | ||||||
|  |             if (base.OnContextMenuCommand(browserControl, browser, frame, parameters, commandId, eventFlags)){ | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch(commandId){ | ||||||
|  |                 case MenuSkipTweet: | ||||||
|  |                     form.InvokeAsyncSafe(form.FinishCurrentNotification); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuFreeze: | ||||||
|  |                     form.InvokeAsyncSafe(() => form.FreezeTimer = !form.FreezeTimer); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuViewDetail: | ||||||
|  |                     form.InvokeSafe(form.ShowTweetDetail); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuCopyTweetUrl: | ||||||
|  |                     SetClipboardText(form, form.CurrentTweetUrl); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 case MenuCopyQuotedTweetUrl: | ||||||
|  |                     SetClipboardText(form, form.CurrentQuoteUrl); | ||||||
|  |                     return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){ | ||||||
|  |             base.OnContextMenuDismissed(browserControl, browser, frame); | ||||||
|  |             form.InvokeAsyncSafe(() => form.ContextMenuOpen = false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								Core/Handling/DragHandlerBrowser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Core/Handling/DragHandlerBrowser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | using System.Collections.Generic; | ||||||
|  | using CefSharp; | ||||||
|  | using CefSharp.Enums; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     sealed class DragHandlerBrowser : IDragHandler{ | ||||||
|  |         private readonly RequestHandlerBrowser requestHandler; | ||||||
|  | 
 | ||||||
|  |         public DragHandlerBrowser(RequestHandlerBrowser requestHandler){ | ||||||
|  |             this.requestHandler = requestHandler; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         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); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             requestHandler.BlockNextUserNavUrl = dragData.LinkUrl; // empty if not a link | ||||||
|  | 
 | ||||||
|  |             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){} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										72
									
								
								Core/Handling/Filters/ResponseFilterBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								Core/Handling/Filters/ResponseFilterBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using System.Text; | ||||||
|  | using CefSharp; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling.Filters{ | ||||||
|  |     abstract class ResponseFilterBase : IResponseFilter{ | ||||||
|  |         private enum State{ | ||||||
|  |             Reading, Writing, Done | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly Encoding encoding; | ||||||
|  |         private byte[] responseData; | ||||||
|  | 
 | ||||||
|  |         private State state; | ||||||
|  |         private int offset; | ||||||
|  | 
 | ||||||
|  |         protected ResponseFilterBase(int totalBytes, Encoding encoding){ | ||||||
|  |             this.responseData = new byte[totalBytes]; | ||||||
|  |             this.encoding = encoding; | ||||||
|  |             this.state = State.Reading; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         FilterStatus IResponseFilter.Filter(Stream dataIn, out long dataInRead, Stream dataOut, out long dataOutWritten){ | ||||||
|  |             int responseLength = responseData.Length; | ||||||
|  | 
 | ||||||
|  |             if (state == State.Reading){ | ||||||
|  |                 int bytesToRead = Math.Min(responseLength-offset, (int)Math.Min(dataIn?.Length ?? 0, int.MaxValue)); | ||||||
|  |                  | ||||||
|  |                 dataIn?.Read(responseData, offset, bytesToRead); | ||||||
|  |                 offset += bytesToRead; | ||||||
|  |                  | ||||||
|  |                 dataInRead = bytesToRead; | ||||||
|  |                 dataOutWritten = 0; | ||||||
|  |                  | ||||||
|  |                 if (offset >= responseLength){ | ||||||
|  |                     responseData = encoding.GetBytes(ProcessResponse(encoding.GetString(responseData))); | ||||||
|  |                     state = State.Writing; | ||||||
|  |                     offset = 0; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return FilterStatus.NeedMoreData; | ||||||
|  |             } | ||||||
|  |             else if (state == State.Writing){ | ||||||
|  |                 int bytesToWrite = Math.Min(responseLength-offset, (int)Math.Min(dataOut.Length, int.MaxValue)); | ||||||
|  | 
 | ||||||
|  |                 if (bytesToWrite > 0){ | ||||||
|  |                     dataOut.Write(responseData, offset, bytesToWrite); | ||||||
|  |                     offset += bytesToWrite; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 dataOutWritten = bytesToWrite; | ||||||
|  |                 dataInRead = 0; | ||||||
|  | 
 | ||||||
|  |                 if (offset < responseLength){ | ||||||
|  |                     return FilterStatus.NeedMoreData; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     state = State.Done; | ||||||
|  |                     return FilterStatus.Done; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 throw new InvalidOperationException("This resource filter cannot be reused."); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public abstract bool InitFilter(); | ||||||
|  |         protected abstract string ProcessResponse(string text); | ||||||
|  |         public abstract void Dispose(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								Core/Handling/Filters/ResponseFilterVendor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Core/Handling/Filters/ResponseFilterVendor.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | using System.Text; | ||||||
|  | using System.Text.RegularExpressions; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling.Filters{ | ||||||
|  |     sealed class ResponseFilterVendor : ResponseFilterBase{ | ||||||
|  |         private static readonly Regex RegexRestoreJQuery = new Regex(@"(\w+)\.fn=\1\.prototype", RegexOptions.Compiled); | ||||||
|  | 
 | ||||||
|  |         public ResponseFilterVendor(int totalBytes) : base(totalBytes, Encoding.UTF8){} | ||||||
|  | 
 | ||||||
|  |         public override bool InitFilter(){ | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         protected override string ProcessResponse(string text){ | ||||||
|  |             return RegexRestoreJQuery.Replace(text, "window.$$=$1;$&", 1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void Dispose(){} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								Core/Handling/General/BrowserProcessHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Core/Handling/General/BrowserProcessHandler.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | using System; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling.General{ | ||||||
|  |     sealed class BrowserProcessHandler : IBrowserProcessHandler{ | ||||||
|  |         public static Task UpdatePrefs(){ | ||||||
|  |             return Cef.UIThreadTaskFactory.StartNew(UpdatePrefsInternal); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void UpdatePrefsInternal(){ | ||||||
|  |             UserConfig config = Program.Config.User; | ||||||
|  | 
 | ||||||
|  |             using(IRequestContext ctx = Cef.GetGlobalRequestContext()){ | ||||||
|  |                 ctx.SetPreference("browser.enable_spellchecking", config.EnableSpellCheck, out string _); | ||||||
|  |                 ctx.SetPreference("spellcheck.dictionary", config.SpellCheckLanguage, out string _); | ||||||
|  |                 ctx.SetPreference("settings.a11y.animation_policy", config.EnableAnimatedImages ? "allowed" : "none", out string _); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void IBrowserProcessHandler.OnContextInitialized(){ | ||||||
|  |             UpdatePrefsInternal(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){} | ||||||
|  |         void IDisposable.Dispose(){} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								Core/Handling/General/FileDialogHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								Core/Handling/General/FileDialogHandler.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | 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, CefFileDialogFlags flags, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){ | ||||||
|  |             if (mode == CefFileDialogMode.Open || mode == CefFileDialogMode.OpenMultiple){ | ||||||
|  |                 string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter)); | ||||||
|  | 
 | ||||||
|  |                 using(OpenFileDialog dialog = new OpenFileDialog{ | ||||||
|  |                     AutoUpgradeEnabled = true, | ||||||
|  |                     DereferenceLinks = true, | ||||||
|  |                     Multiselect = mode == 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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										90
									
								
								Core/Handling/General/JavaScriptDialogHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								Core/Handling/General/JavaScriptDialogHandler.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | |||||||
|  | using System.Drawing; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling.General{ | ||||||
|  |     sealed class JavaScriptDialogHandler : IJsDialogHandler{ | ||||||
|  |         private static FormMessage CreateMessageForm(string caption, string text){ | ||||||
|  |             MessageBoxIcon icon = MessageBoxIcon.None; | ||||||
|  |             int pipe = text.IndexOf('|'); | ||||||
|  | 
 | ||||||
|  |             if (pipe != -1){ | ||||||
|  |                 switch(text.Substring(0, pipe)){ | ||||||
|  |                     case "error": icon = MessageBoxIcon.Error; break; | ||||||
|  |                     case "warning": icon = MessageBoxIcon.Warning; break; | ||||||
|  |                     case "info": icon = MessageBoxIcon.Information; break; | ||||||
|  |                     case "question": icon = MessageBoxIcon.Question; break; | ||||||
|  |                     default: return new FormMessage(caption, text, icon); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 text = text.Substring(pipe+1); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return new FormMessage(caption, text, icon); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){ | ||||||
|  |             browserControl.AsControl().InvokeSafe(() => { | ||||||
|  |                 FormMessage form; | ||||||
|  |                 TextBox input = null; | ||||||
|  | 
 | ||||||
|  |                 if (dialogType == CefJsDialogType.Alert){ | ||||||
|  |                     form = CreateMessageForm("Browser Message", messageText); | ||||||
|  |                     form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused); | ||||||
|  |                 } | ||||||
|  |                 else if (dialogType == CefJsDialogType.Confirm){ | ||||||
|  |                     form = CreateMessageForm("Browser Confirmation", messageText); | ||||||
|  |                     form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel); | ||||||
|  |                     form.AddButton(FormMessage.Yes, ControlType.Focused); | ||||||
|  |                 } | ||||||
|  |                 else if (dialogType == CefJsDialogType.Prompt){ | ||||||
|  |                     form = CreateMessageForm("Browser Prompt", messageText); | ||||||
|  |                     form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel); | ||||||
|  |                     form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused); | ||||||
|  | 
 | ||||||
|  |                     float dpiScale = form.GetDPIScale(); | ||||||
|  |                     int inputPad = form.HasIcon ? 43 : 0; | ||||||
|  | 
 | ||||||
|  |                     input = new TextBox{ | ||||||
|  |                         Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom, | ||||||
|  |                         Font = SystemFonts.MessageBoxFont, | ||||||
|  |                         Location = new Point(BrowserUtils.Scale(22+inputPad, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)), | ||||||
|  |                         Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44+inputPad, dpiScale), BrowserUtils.Scale(23, dpiScale)) | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     form.Controls.Add(input); | ||||||
|  |                     form.ActiveControl = input; | ||||||
|  |                     form.Height += input.Size.Height+input.Margin.Vertical; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     callback.Continue(false); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 bool success = form.ShowDialog() == DialogResult.OK; | ||||||
|  | 
 | ||||||
|  |                 if (input == null){ | ||||||
|  |                     callback.Continue(success); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     callback.Continue(success, input.Text); | ||||||
|  |                     input.Dispose(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 form.Dispose(); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){ | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void IJsDialogHandler.OnResetDialogState(IWebBrowser browserControl, IBrowser browser){} | ||||||
|  |         void IJsDialogHandler.OnDialogClosed(IWebBrowser browserControl, IBrowser browser){} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								Core/Handling/General/LifeSpanHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Core/Handling/General/LifeSpanHandler.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling.General{ | ||||||
|  |     sealed class LifeSpanHandler : ILifeSpanHandler{ | ||||||
|  |         public static bool HandleLinkClick(IWebBrowser browserControl, WindowOpenDisposition targetDisposition, string targetUrl){ | ||||||
|  |             switch(targetDisposition){ | ||||||
|  |                 case WindowOpenDisposition.NewBackgroundTab: | ||||||
|  |                 case WindowOpenDisposition.NewForegroundTab: | ||||||
|  |                 case WindowOpenDisposition.NewPopup: | ||||||
|  |                 case WindowOpenDisposition.NewWindow: | ||||||
|  |                     browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl)); | ||||||
|  |                     return true; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){ | ||||||
|  |             newBrowser = null; | ||||||
|  |             return HandleLinkClick(browserControl, targetDisposition, targetUrl); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser){} | ||||||
|  | 
 | ||||||
|  |         public bool DoClose(IWebBrowser browserControl, IBrowser browser){ | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser){} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								Core/Handling/KeyboardHandlerBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								Core/Handling/KeyboardHandlerBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     class KeyboardHandlerBase : IKeyboardHandler{ | ||||||
|  |         protected virtual bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){ | ||||||
|  |             if (modifiers == (CefEventFlags.ControlDown | CefEventFlags.ShiftDown) && key == Keys.I){ | ||||||
|  |                 if (BrowserUtils.HasDevTools){ | ||||||
|  |                     browser.ShowDevTools(); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     browserControl.AsControl().InvokeSafe(() => { | ||||||
|  |                         string extraMessage; | ||||||
|  |                          | ||||||
|  |                         if (Program.IsPortable){ | ||||||
|  |                             extraMessage = "Please download the portable installer, select the folder with your current installation of TweetDuck Portable, and tick 'Install dev tools' during the installation process."; | ||||||
|  |                         } | ||||||
|  |                         else{ | ||||||
|  |                             extraMessage = "Please download the installer, and tick 'Install dev tools' during the installation process. The installer will automatically find and update your current installation of TweetDuck."; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         FormMessage.Information("Dev Tools", "You do not have dev tools installed. "+extraMessage, FormMessage.OK); | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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://")){ | ||||||
|  |                 return HandleRawKey(browserControl, browser, (Keys)windowsKeyCode, modifiers); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){ | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								Core/Handling/KeyboardHandlerBrowser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Core/Handling/KeyboardHandlerBrowser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     sealed class KeyboardHandlerBrowser : KeyboardHandlerBase{ | ||||||
|  |         private readonly FormBrowser form; | ||||||
|  | 
 | ||||||
|  |         public KeyboardHandlerBrowser(FormBrowser form){ | ||||||
|  |             this.form = form; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){ | ||||||
|  |             if (base.HandleRawKey(browserControl, browser, key, modifiers)){ | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return form.ProcessBrowserKey(key); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								Core/Handling/KeyboardHandlerNotification.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Core/Handling/KeyboardHandlerNotification.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | using CefSharp; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Notification; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling { | ||||||
|  |     sealed class KeyboardHandlerNotification : KeyboardHandlerBase{ | ||||||
|  |         private readonly FormNotificationBase notification; | ||||||
|  | 
 | ||||||
|  |         public KeyboardHandlerNotification(FormNotificationBase notification){ | ||||||
|  |             this.notification = notification; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void TriggerKeyboardShortcutAnalytics(){ | ||||||
|  |             notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){ | ||||||
|  |             if (base.HandleRawKey(browserControl, browser, key, modifiers)){ | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch(key){ | ||||||
|  |                 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; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										89
									
								
								Core/Handling/RequestHandlerBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								Core/Handling/RequestHandlerBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Collections.Specialized; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text.RegularExpressions; | ||||||
|  | using CefSharp; | ||||||
|  | using CefSharp.Handler; | ||||||
|  | using TweetDuck.Core.Handling.General; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     class RequestHandlerBase : DefaultRequestHandler{ | ||||||
|  |         private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$", RegexOptions.Compiled); | ||||||
|  |         private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4); | ||||||
|  | 
 | ||||||
|  |         public static void LoadResourceRewriteRules(string rules){ | ||||||
|  |             if (string.IsNullOrEmpty(rules)){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             TweetDeckHashes.Clear(); | ||||||
|  | 
 | ||||||
|  |             foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){ | ||||||
|  |                 string[] split = rule.Split('='); | ||||||
|  | 
 | ||||||
|  |                 if (split.Length == 2){ | ||||||
|  |                     string key = split[0]; | ||||||
|  |                     string hash = split[1]; | ||||||
|  | 
 | ||||||
|  |                     if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){ | ||||||
|  |                         TweetDeckHashes.Add(key, hash); | ||||||
|  |                     } | ||||||
|  |                     else{ | ||||||
|  |                         throw new ArgumentException("Invalid hash characters: "+rule); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     throw new ArgumentException("A rule must have exactly one '=' character: "+rule); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly bool autoReload; | ||||||
|  | 
 | ||||||
|  |         public RequestHandlerBase(bool autoReload){ | ||||||
|  |             this.autoReload = autoReload; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){ | ||||||
|  |             return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){ | ||||||
|  |             if (BrowserUtils.HasDevTools){ | ||||||
|  |                 NameValueCollection headers = request.Headers; | ||||||
|  |                 headers.Remove("x-devtools-emulate-network-conditions-client-id"); | ||||||
|  |                 request.Headers = headers; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){ | ||||||
|  |             if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0){ | ||||||
|  |                 string url = request.Url; | ||||||
|  |                 Match match = TweetDeckResourceUrl.Match(url); | ||||||
|  | 
 | ||||||
|  |                 if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){ | ||||||
|  |                     if (match.Groups[2].Value == hash){ | ||||||
|  |                         Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url); | ||||||
|  |                     } | ||||||
|  |                     else{ | ||||||
|  |                         Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash); | ||||||
|  |                         request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3"); | ||||||
|  |                         return true; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return base.OnResourceResponse(browserControl, browser, frame, request, response); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){ | ||||||
|  |             if (autoReload){ | ||||||
|  |                 browser.Reload(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								Core/Handling/RequestHandlerBrowser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								Core/Handling/RequestHandlerBrowser.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | using System.Collections.Specialized; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Handling.Filters; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     sealed class RequestHandlerBrowser : RequestHandlerBase{ | ||||||
|  |         private const string UrlVendorResource = "/dist/vendor"; | ||||||
|  |         private const string UrlLoadingSpinner = "/backgrounds/spinner_blue"; | ||||||
|  | 
 | ||||||
|  |         public string BlockNextUserNavUrl { get; set; } | ||||||
|  | 
 | ||||||
|  |         public RequestHandlerBrowser() : base(true){} | ||||||
|  | 
 | ||||||
|  |         public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){ | ||||||
|  |             if (request.ResourceType == ResourceType.MainFrame){ | ||||||
|  |                 if (request.Url.EndsWith("//twitter.com/")){ | ||||||
|  |                     request.Url = TwitterUtils.TweetDeckURL; // redirect plain twitter.com requests, fixes bugs with login 2FA | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (request.ResourceType == ResourceType.Script){ | ||||||
|  |                 string url = request.Url; | ||||||
|  | 
 | ||||||
|  |                 if (url.Contains("analytics.")){ | ||||||
|  |                     callback.Dispose(); | ||||||
|  |                     return CefReturnValue.Cancel; | ||||||
|  |                 } | ||||||
|  |                 else if (url.Contains(UrlVendorResource)){ | ||||||
|  |                     NameValueCollection headers = request.Headers; | ||||||
|  |                     headers["Accept-Encoding"] = "identity"; | ||||||
|  |                     request.Headers = headers; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect){ | ||||||
|  |             if (userGesture && request.TransitionType == TransitionType.LinkClicked){ | ||||||
|  |                 bool block = request.Url == BlockNextUserNavUrl; | ||||||
|  |                 BlockNextUserNavUrl = string.Empty; | ||||||
|  |                 return block; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){ | ||||||
|  |             if (request.ResourceType == ResourceType.Image && request.Url.Contains(UrlLoadingSpinner)){ | ||||||
|  |                 request.Url = TwitterUtils.LoadingSpinner.Url; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return base.OnResourceResponse(browserControl, browser, frame, request, response); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){ | ||||||
|  |             if (request.ResourceType == ResourceType.Script && request.Url.Contains(UrlVendorResource) && int.TryParse(response.ResponseHeaders["Content-Length"], out int totalBytes)){ | ||||||
|  |                 return new ResponseFilterVendor(totalBytes); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return base.GetResourceResponseFilter(browserControl, browser, frame, request, response); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								Core/Handling/ResourceHandlerFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Core/Handling/ResourceHandlerFactory.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Handling{ | ||||||
|  |     sealed class ResourceHandlerFactory : IResourceHandlerFactory{ | ||||||
|  |         public bool HasHandlers => !handlers.IsEmpty; | ||||||
|  | 
 | ||||||
|  |         private readonly ConcurrentDictionary<string, IResourceHandler> handlers = new ConcurrentDictionary<string, IResourceHandler>(StringComparer.OrdinalIgnoreCase); | ||||||
|  | 
 | ||||||
|  |         public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request){ | ||||||
|  |             try{ | ||||||
|  |                 return handlers.TryGetValue(request.Url, out IResourceHandler handler) ? handler : null; | ||||||
|  |             }finally{ | ||||||
|  |                 request.Dispose(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // registration | ||||||
|  | 
 | ||||||
|  |         public bool RegisterHandler(string url, IResourceHandler handler){ | ||||||
|  |             if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){ | ||||||
|  |                 handlers.AddOrUpdate(uri.AbsoluteUri, handler, (key, prev) => handler); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool RegisterHandler(ResourceLink link){ | ||||||
|  |             return RegisterHandler(link.Url, link.Handler); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool UnregisterHandler(string url){ | ||||||
|  |             return handlers.TryRemove(url, out IResourceHandler _); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool UnregisterHandler(ResourceLink link){ | ||||||
|  |             return UnregisterHandler(link.Url); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								Core/Handling/ResourceHandlerNotification.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Core/Handling/ResourceHandlerNotification.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | 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(){} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										76
									
								
								Core/Management/BrowserCache.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								Core/Management/BrowserCache.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | |||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Management{ | ||||||
|  |     static class BrowserCache{ | ||||||
|  |         public static string CacheFolder => Path.Combine(Program.StoragePath, "Cache"); | ||||||
|  |          | ||||||
|  |         private static bool ClearOnExit; | ||||||
|  |         private static Timer AutoClearTimer; | ||||||
|  | 
 | ||||||
|  |         private static long CalculateCacheSize(){ | ||||||
|  |             return new DirectoryInfo(CacheFolder).EnumerateFiles().Select(file => { | ||||||
|  |                 try{ | ||||||
|  |                     return file.Length; | ||||||
|  |                 }catch{ | ||||||
|  |                     return 0L; | ||||||
|  |                 } | ||||||
|  |             }).Sum(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void GetCacheSize(Action<Task<long>> callbackBytes){ | ||||||
|  |             Task<long> task = new Task<long>(CalculateCacheSize); | ||||||
|  |             task.ContinueWith(callbackBytes); | ||||||
|  |             task.Start(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public static void RefreshTimer(){ | ||||||
|  |             bool shouldRun = Program.Config.System.ClearCacheAutomatically && !ClearOnExit; | ||||||
|  | 
 | ||||||
|  |             if (!shouldRun && AutoClearTimer != null){ | ||||||
|  |                 AutoClearTimer.Dispose(); | ||||||
|  |                 AutoClearTimer = null; | ||||||
|  |             } | ||||||
|  |             else if (shouldRun && AutoClearTimer == null){ | ||||||
|  |                 AutoClearTimer = new Timer(state => { | ||||||
|  |                     if (AutoClearTimer != null){ | ||||||
|  |                         try{ | ||||||
|  |                             if (CalculateCacheSize() >= Program.Config.System.ClearCacheThreshold*1024L*1024L){ | ||||||
|  |                                 SetClearOnExit(); | ||||||
|  |                             } | ||||||
|  |                         }catch(Exception){ | ||||||
|  |                             // TODO should probably log errors and report them at some point | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }, null, TimeSpan.FromSeconds(30), TimeSpan.FromHours(4)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void SetClearOnExit(){ | ||||||
|  |             ClearOnExit = true; | ||||||
|  |             RefreshTimer(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void TryClearNow(){ | ||||||
|  |             try{ | ||||||
|  |                 Directory.Delete(CacheFolder, true); | ||||||
|  |             }catch{ | ||||||
|  |                 // welp, too bad | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void Exit(){ | ||||||
|  |             if (AutoClearTimer != null){ | ||||||
|  |                 AutoClearTimer.Dispose(); | ||||||
|  |                 AutoClearTimer = null; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (ClearOnExit){ | ||||||
|  |                 TryClearNow(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										161
									
								
								Core/Management/ContextInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								Core/Management/ContextInfo.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | |||||||
|  | using System; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Management{ | ||||||
|  |     sealed class ContextInfo{ | ||||||
|  |         private LinkInfo link; | ||||||
|  |         private ChirpInfo? chirp; | ||||||
|  |          | ||||||
|  |         public ContextInfo(){ | ||||||
|  |             Reset(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public void SetLink(string type, string url){ | ||||||
|  |             link = string.IsNullOrEmpty(url) ? null : new LinkInfo(type, url); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SetChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ | ||||||
|  |             chirp = string.IsNullOrEmpty(tweetUrl) ? (ChirpInfo?)null : new ChirpInfo(tweetUrl, quoteUrl, chirpAuthors, chirpImages); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public ContextData Reset(){ | ||||||
|  |             link = null; | ||||||
|  |             chirp = null; | ||||||
|  |             return ContextData.Empty; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public ContextData Create(IContextMenuParams parameters){ | ||||||
|  |             ContextData.Builder builder = new ContextData.Builder(); | ||||||
|  |             builder.AddContext(parameters); | ||||||
|  | 
 | ||||||
|  |             if (link != null){ | ||||||
|  |                 builder.AddOverride(link.Type, link.Url); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if (chirp.HasValue){ | ||||||
|  |                 builder.AddChirp(chirp.Value); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return builder.Build(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Data structures | ||||||
|  | 
 | ||||||
|  |         private sealed class LinkInfo{ | ||||||
|  |             public string Type { get; } | ||||||
|  |             public string Url { get; } | ||||||
|  | 
 | ||||||
|  |             public LinkInfo(string type, string url){ | ||||||
|  |                 this.Type = type; | ||||||
|  |                 this.Url = url; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public struct ChirpInfo{ | ||||||
|  |             public string TweetUrl { get; } | ||||||
|  |             public string QuoteUrl { get; } | ||||||
|  | 
 | ||||||
|  |             public string[] Authors => chirpAuthors?.Split(';') ?? StringUtils.EmptyArray; | ||||||
|  |             public string[] Images => chirpImages?.Split(';') ?? StringUtils.EmptyArray; | ||||||
|  |              | ||||||
|  |             private readonly string chirpAuthors; | ||||||
|  |             private readonly string chirpImages; | ||||||
|  | 
 | ||||||
|  |             public ChirpInfo(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ | ||||||
|  |                 this.TweetUrl = tweetUrl; | ||||||
|  |                 this.QuoteUrl = quoteUrl; | ||||||
|  |                 this.chirpAuthors = chirpAuthors; | ||||||
|  |                 this.chirpImages = chirpImages; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Constructed context | ||||||
|  | 
 | ||||||
|  |         [Flags] | ||||||
|  |         public enum ContextType{ | ||||||
|  |             Unknown = 0, | ||||||
|  |             Link    = 0b0001, | ||||||
|  |             Image   = 0b0010, | ||||||
|  |             Video   = 0b0100, | ||||||
|  |             Chirp   = 0b1000 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public sealed class ContextData{ | ||||||
|  |             public static readonly ContextData Empty = new Builder().Build(); | ||||||
|  | 
 | ||||||
|  |             public ContextType Types { get; } | ||||||
|  | 
 | ||||||
|  |             public string LinkUrl { get; } | ||||||
|  |             public string UnsafeLinkUrl { get; } | ||||||
|  |             public string MediaUrl { get; } | ||||||
|  | 
 | ||||||
|  |             public ChirpInfo Chirp { get; } | ||||||
|  | 
 | ||||||
|  |             public ContextData(ContextType types, string linkUrl, string unsafeLinkUrl, string mediaUrl, ChirpInfo chirp){ | ||||||
|  |                 Types = types; | ||||||
|  |                 LinkUrl = linkUrl; | ||||||
|  |                 UnsafeLinkUrl = unsafeLinkUrl; | ||||||
|  |                 MediaUrl = mediaUrl; | ||||||
|  |                 Chirp = chirp; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public sealed class Builder{ | ||||||
|  |                 private ContextType types = ContextType.Unknown; | ||||||
|  | 
 | ||||||
|  |                 private string linkUrl = string.Empty; | ||||||
|  |                 private string unsafeLinkUrl = string.Empty; | ||||||
|  |                 private string mediaUrl = string.Empty; | ||||||
|  | 
 | ||||||
|  |                 private ChirpInfo chirp = default(ChirpInfo); | ||||||
|  | 
 | ||||||
|  |                 public void AddContext(IContextMenuParams parameters){ | ||||||
|  |                     ContextMenuType flags = parameters.TypeFlags; | ||||||
|  | 
 | ||||||
|  |                     if (flags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){ | ||||||
|  |                         types |= ContextType.Image; | ||||||
|  |                         types &= ~ContextType.Video; | ||||||
|  |                         mediaUrl = parameters.SourceUrl; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (flags.HasFlag(ContextMenuType.Link)){ | ||||||
|  |                         types |= ContextType.Link; | ||||||
|  |                         linkUrl = parameters.LinkUrl; | ||||||
|  |                         unsafeLinkUrl = parameters.UnfilteredLinkUrl; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 public void AddOverride(string type, string url){ | ||||||
|  |                     switch(type){ | ||||||
|  |                         case "link": | ||||||
|  |                             types |= ContextType.Link; | ||||||
|  |                             linkUrl = url; | ||||||
|  |                             unsafeLinkUrl = url; | ||||||
|  |                             break; | ||||||
|  | 
 | ||||||
|  |                         case "image": | ||||||
|  |                             types |= ContextType.Image; | ||||||
|  |                             types &= ~(ContextType.Video | ContextType.Link); | ||||||
|  |                             mediaUrl = url; | ||||||
|  |                             break; | ||||||
|  | 
 | ||||||
|  |                         case "video": | ||||||
|  |                             types |= ContextType.Video; | ||||||
|  |                             types &= ~(ContextType.Image | ContextType.Link); | ||||||
|  |                             mediaUrl = url; | ||||||
|  |                             break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 public void AddChirp(ChirpInfo chirp){ | ||||||
|  |                     this.types |= ContextType.Chirp; | ||||||
|  |                     this.chirp = chirp; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 public ContextData Build(){ | ||||||
|  |                     return new ContextData(types, linkUrl, unsafeLinkUrl, mediaUrl, chirp); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										215
									
								
								Core/Management/ProfileManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								Core/Management/ProfileManager.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Plugins.Enums; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Management{ | ||||||
|  |     sealed class ProfileManager{ | ||||||
|  |         private static readonly string CookiesPath = Path.Combine(Program.StoragePath, "Cookies"); | ||||||
|  |         private static readonly string TempCookiesPath = Path.Combine(Program.StoragePath, "CookiesTmp"); | ||||||
|  | 
 | ||||||
|  |         [Flags] | ||||||
|  |         public enum Items{ | ||||||
|  |             None = 0, | ||||||
|  |             UserConfig = 1, | ||||||
|  |             SystemConfig = 2, | ||||||
|  |             Session = 4, | ||||||
|  |             PluginData = 8, | ||||||
|  |             All = UserConfig|SystemConfig|Session|PluginData | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private readonly string file; | ||||||
|  |         private readonly PluginManager plugins; | ||||||
|  | 
 | ||||||
|  |         public ProfileManager(string file, PluginManager plugins){ | ||||||
|  |             this.file = file; | ||||||
|  |             this.plugins = plugins; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool Export(Items items){ | ||||||
|  |             try{ | ||||||
|  |                 using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){ | ||||||
|  |                     if (items.HasFlag(Items.UserConfig)){ | ||||||
|  |                         stream.WriteFile("config", Program.UserConfigFilePath); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (items.HasFlag(Items.SystemConfig)){ | ||||||
|  |                         stream.WriteFile("system", Program.SystemConfigFilePath); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (items.HasFlag(Items.PluginData)){ | ||||||
|  |                         stream.WriteFile("plugin.config", Program.PluginConfigFilePath); | ||||||
|  | 
 | ||||||
|  |                         foreach(Plugin plugin in plugins.Plugins){ | ||||||
|  |                             foreach(PathInfo path in EnumerateFilesRelative(plugin.GetPluginFolder(PluginFolder.Data))){ | ||||||
|  |                                 try{ | ||||||
|  |                                     stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full); | ||||||
|  |                                 }catch(ArgumentOutOfRangeException e){ | ||||||
|  |                                     FormMessage.Warning("Export Profile", "Could not include a plugin file in the export. "+e.Message, FormMessage.OK); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (items.HasFlag(Items.Session)){ | ||||||
|  |                         stream.WriteFile("cookies", CookiesPath); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     stream.Flush(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return true; | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, e); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Items FindImportItems(){ | ||||||
|  |             Items items = Items.None; | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                 using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){ | ||||||
|  |                     string key; | ||||||
|  | 
 | ||||||
|  |                     while((key = stream.SkipFile()) != null){ | ||||||
|  |                         switch(key){ | ||||||
|  |                             case "config": | ||||||
|  |                                 items |= Items.UserConfig; | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case "system": | ||||||
|  |                                 items |= Items.SystemConfig; | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case "plugin.config": | ||||||
|  |                             case "plugin.data": | ||||||
|  |                                 items |= Items.PluginData; | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case "cookies": | ||||||
|  |                                 items |= Items.Session; | ||||||
|  |                                 break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }catch(Exception){ | ||||||
|  |                 items = Items.None; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return items; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool Import(Items items){ | ||||||
|  |             try{ | ||||||
|  |                 HashSet<string> missingPlugins = new HashSet<string>(); | ||||||
|  | 
 | ||||||
|  |                 using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){ | ||||||
|  |                     CombinedFileStream.Entry entry; | ||||||
|  | 
 | ||||||
|  |                     while((entry = stream.ReadFile()) != null){ | ||||||
|  |                         switch(entry.KeyName){ | ||||||
|  |                             case "config": | ||||||
|  |                                 if (items.HasFlag(Items.UserConfig)){ | ||||||
|  |                                     entry.WriteToFile(Program.UserConfigFilePath); | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case "system": | ||||||
|  |                                 if (items.HasFlag(Items.SystemConfig)){ | ||||||
|  |                                     entry.WriteToFile(Program.SystemConfigFilePath); | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case "plugin.config": | ||||||
|  |                                 if (items.HasFlag(Items.PluginData)){ | ||||||
|  |                                     entry.WriteToFile(Program.PluginConfigFilePath); | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case "plugin.data": | ||||||
|  |                                 if (items.HasFlag(Items.PluginData)){ | ||||||
|  |                                     string[] value = entry.KeyValue; | ||||||
|  | 
 | ||||||
|  |                                     entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true); | ||||||
|  | 
 | ||||||
|  |                                     if (!plugins.IsPluginInstalled(value[0])){ | ||||||
|  |                                         missingPlugins.Add(value[0]); | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case "cookies": | ||||||
|  |                                 if (items.HasFlag(Items.Session)){ | ||||||
|  |                                     entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath)); | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (missingPlugins.Count > 0){ | ||||||
|  |                     FormMessage.Information("Profile Import", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return true; | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Profile Import", "An exception happened while importing TweetDuck profile.", true, e); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void ImportCookies(){ | ||||||
|  |             if (File.Exists(TempCookiesPath)){ | ||||||
|  |                 try{ | ||||||
|  |                     if (File.Exists(CookiesPath)){ | ||||||
|  |                         File.Delete(CookiesPath); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     File.Move(TempCookiesPath, CookiesPath); | ||||||
|  |                 }catch(Exception e){ | ||||||
|  |                     Program.Reporter.HandleException("Profile Import Error", "Could not import the cookie file to restore login session.", true, e); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void DeleteCookies(){ | ||||||
|  |             try{ | ||||||
|  |                 if (File.Exists(CookiesPath)){ | ||||||
|  |                     File.Delete(CookiesPath); | ||||||
|  |                 } | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){ | ||||||
|  |             if (Directory.Exists(root)){ | ||||||
|  |                 int rootLength = root.Length; | ||||||
|  |                 return Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo(fullPath, rootLength)); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 return Enumerable.Empty<PathInfo>(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private sealed class PathInfo{ | ||||||
|  |             public string Full { get; } | ||||||
|  |             public string Relative { get; } | ||||||
|  | 
 | ||||||
|  |             public PathInfo(string fullPath, int rootLength){ | ||||||
|  |                 this.Full = fullPath; | ||||||
|  |                 this.Relative = fullPath.Substring(rootLength).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); // strip leading separator character | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										213
									
								
								Core/Management/VideoPlayer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								Core/Management/VideoPlayer.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | |||||||
|  | using System; | ||||||
|  | using System.Diagnostics; | ||||||
|  | using System.IO; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetLib.Communication; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Management{ | ||||||
|  |     sealed class VideoPlayer : IDisposable{ | ||||||
|  |         private static UserConfig Config => Program.Config.User; | ||||||
|  | 
 | ||||||
|  |         public bool Running => currentInstance != null && currentInstance.Running; | ||||||
|  | 
 | ||||||
|  |         public event EventHandler ProcessExited; | ||||||
|  | 
 | ||||||
|  |         private readonly FormBrowser owner; | ||||||
|  | 
 | ||||||
|  |         private Instance currentInstance; | ||||||
|  |         private bool isClosing; | ||||||
|  | 
 | ||||||
|  |         public VideoPlayer(FormBrowser owner){ | ||||||
|  |             this.owner = owner; | ||||||
|  |             this.owner.FormClosing += owner_FormClosing; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Launch(string url, string username){ | ||||||
|  |             if (Running){ | ||||||
|  |                 Destroy(); | ||||||
|  |                 isClosing = false; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             try{ | ||||||
|  |                 DuplexPipe.Server pipe = DuplexPipe.CreateServer(); | ||||||
|  |                 pipe.DataIn += pipe_DataIn; | ||||||
|  | 
 | ||||||
|  |                 Process process; | ||||||
|  | 
 | ||||||
|  |                 if ((process = Process.Start(new ProcessStartInfo{ | ||||||
|  |                     FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"), | ||||||
|  |                     Arguments = $"{owner.Handle} {(int)Math.Floor(100F*owner.GetDPIScale())} {Config.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"", | ||||||
|  |                     UseShellExecute = false, | ||||||
|  |                     RedirectStandardOutput = true | ||||||
|  |                 })) != null){ | ||||||
|  |                     currentInstance = new Instance(process, pipe, url, username); | ||||||
|  | 
 | ||||||
|  |                     process.EnableRaisingEvents = true; | ||||||
|  |                     process.Exited += process_Exited; | ||||||
|  |                      | ||||||
|  |                     process.BeginOutputReadLine(); | ||||||
|  |                     process.OutputDataReceived += process_OutputDataReceived; | ||||||
|  | 
 | ||||||
|  |                     pipe.DisposeToken(); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     pipe.DataIn -= pipe_DataIn; | ||||||
|  |                     pipe.Dispose(); | ||||||
|  |                 } | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SendKeyEvent(Keys key){ | ||||||
|  |             currentInstance?.Pipe.Write("key", ((int)key).ToString()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){ | ||||||
|  |             owner.InvokeSafe(() => { | ||||||
|  |                 switch(e.Key){ | ||||||
|  |                     case "vol": | ||||||
|  |                         if (int.TryParse(e.Data, out int volume) && volume != Config.VideoPlayerVolume){ | ||||||
|  |                             Config.VideoPlayerVolume = volume; | ||||||
|  |                             Config.Save(); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|  |                     case "download": | ||||||
|  |                         if (currentInstance != null){ | ||||||
|  |                             owner.AnalyticsFile.DownloadedVideos.Trigger(); | ||||||
|  |                             TwitterUtils.DownloadVideo(currentInstance.Url, currentInstance.Username); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         break; | ||||||
|  | 
 | ||||||
|  |                     case "rip": | ||||||
|  |                         currentInstance?.Dispose(); | ||||||
|  |                         currentInstance = null; | ||||||
|  | 
 | ||||||
|  |                         isClosing = false; | ||||||
|  |                         TriggerProcessExitEventUnsafe(); | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Close(){ | ||||||
|  |             if (currentInstance != null){ | ||||||
|  |                 if (isClosing){ | ||||||
|  |                     Destroy(); | ||||||
|  |                     isClosing = false; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     isClosing = true; | ||||||
|  |                     currentInstance.Process.Exited -= process_Exited; | ||||||
|  |                     currentInstance.Pipe.Write("die"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Dispose(){ | ||||||
|  |             ProcessExited = null; | ||||||
|  | 
 | ||||||
|  |             isClosing = true; | ||||||
|  |             Destroy(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Destroy(){ | ||||||
|  |             if (currentInstance != null){ | ||||||
|  |                 currentInstance.KillAndDispose(); | ||||||
|  |                 currentInstance = null; | ||||||
|  | 
 | ||||||
|  |                 TriggerProcessExitEventUnsafe(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void owner_FormClosing(object sender, FormClosingEventArgs e){ | ||||||
|  |             if (currentInstance != null){ | ||||||
|  |                 currentInstance.Process.Exited -= process_Exited; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){ | ||||||
|  |             if (!string.IsNullOrEmpty(e.Data)){ | ||||||
|  |                 Program.Reporter.LogVerbose("[VideoPlayer] "+e.Data); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void process_Exited(object sender, EventArgs e){ | ||||||
|  |             if (currentInstance == null){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             int exitCode = currentInstance.Process.ExitCode; | ||||||
|  |             string url = currentInstance.Url; | ||||||
|  | 
 | ||||||
|  |             currentInstance.Dispose(); | ||||||
|  |             currentInstance = null; | ||||||
|  | 
 | ||||||
|  |             switch(exitCode){ | ||||||
|  |                 case 3: // CODE_LAUNCH_FAIL | ||||||
|  |                     if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ | ||||||
|  |                         BrowserUtils.OpenExternalBrowser(url); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case 4: // CODE_MEDIA_ERROR | ||||||
|  |                     if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ | ||||||
|  |                         BrowserUtils.OpenExternalBrowser(url); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void TriggerProcessExitEventUnsafe(){ | ||||||
|  |             ProcessExited?.Invoke(this, EventArgs.Empty); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private sealed class Instance : IDisposable{ | ||||||
|  |             public bool Running{ | ||||||
|  |                 get{ | ||||||
|  |                     Process.Refresh(); | ||||||
|  |                     return !Process.HasExited; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public Process Process { get; } | ||||||
|  |             public DuplexPipe.Server Pipe { get; } | ||||||
|  | 
 | ||||||
|  |             public string Url { get; } | ||||||
|  |             public string Username { get; } | ||||||
|  | 
 | ||||||
|  |             public Instance(Process process, DuplexPipe.Server pipe, string url, string username){ | ||||||
|  |                 this.Process = process; | ||||||
|  |                 this.Pipe = pipe; | ||||||
|  |                 this.Url = url; | ||||||
|  |                 this.Username = username; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void KillAndDispose(){ | ||||||
|  |                 try{ | ||||||
|  |                     Process.Kill(); | ||||||
|  |                 }catch{ | ||||||
|  |                     // kill me instead then | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 Dispose(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void Dispose(){ | ||||||
|  |                 Process.Dispose(); | ||||||
|  |                 Pipe.Dispose(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								Core/Notification/Example/FormNotificationExample.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								Core/Notification/Example/FormNotificationExample.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | |||||||
|  | using System; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Resources; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification.Example{ | ||||||
|  |     sealed class FormNotificationExample : FormNotificationMain{ | ||||||
|  |         public override bool RequiresResize => true; | ||||||
|  |         protected override bool CanDragWindow => Config.NotificationPosition == TweetNotification.Position.Custom; | ||||||
|  | 
 | ||||||
|  |         protected override FormBorderStyle NotificationBorderStyle{ | ||||||
|  |             get{ | ||||||
|  |                 if (Config.NotificationSize == TweetNotification.Size.Custom){ | ||||||
|  |                     switch(base.NotificationBorderStyle){ | ||||||
|  |                         case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable; | ||||||
|  |                         case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return base.NotificationBorderStyle; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override string BodyClasses => base.BodyClasses+" td-example"; | ||||||
|  | 
 | ||||||
|  |         public event EventHandler Ready; | ||||||
|  | 
 | ||||||
|  |         private readonly TweetNotification exampleNotification; | ||||||
|  | 
 | ||||||
|  |         public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){ | ||||||
|  |             browser.LoadingStateChanged += browser_LoadingStateChanged; | ||||||
|  | 
 | ||||||
|  |             string exampleTweetHTML = ScriptLoader.LoadResourceSilent("pages/example.html")?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty; | ||||||
|  | 
 | ||||||
|  |             #if DEBUG | ||||||
|  |             exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>"); | ||||||
|  |             #endif | ||||||
|  | 
 | ||||||
|  |             exampleNotification = new TweetNotification(string.Empty, string.Empty, "Home", exampleTweetHTML, 176, string.Empty, string.Empty); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ | ||||||
|  |             if (!e.IsLoading){ | ||||||
|  |                 Ready?.Invoke(this, EventArgs.Empty); | ||||||
|  |                 browser.LoadingStateChanged -= browser_LoadingStateChanged; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void HideNotification(){ | ||||||
|  |             Location = ControlExtensions.InvisibleLocation; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void FinishCurrentNotification(){} | ||||||
|  | 
 | ||||||
|  |         public void ShowExampleNotification(bool reset){ | ||||||
|  |             if (reset){ | ||||||
|  |                 LoadTweet(exampleNotification); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 PrepareAndDisplayWindow(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             UpdateTitle(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,10 +1,21 @@ | |||||||
| namespace TweetDuck.Browser.Notification { | namespace TweetDuck.Core.Notification { | ||||||
|     partial class FormNotificationBase { |     partial class FormNotificationBase { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         private System.ComponentModel.IContainer components = null; |         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 |         #region Windows Form Designer generated code | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @@ -22,9 +33,8 @@ | |||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||||
|             this.BackColor = System.Drawing.SystemColors.Control; |             this.BackColor = System.Drawing.SystemColors.Control; | ||||||
|             this.ClientSize = new System.Drawing.Size(284, 122); |             this.ClientSize = new System.Drawing.Size(284, 122); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; |             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; | ||||||
|             this.Location = TweetDuck.Controls.ControlExtensions.InvisibleLocation; |             this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
|             this.MinimizeBox = false; |             this.MinimizeBox = false; | ||||||
|             this.Name = "FormNotification"; |             this.Name = "FormNotification"; | ||||||
							
								
								
									
										236
									
								
								Core/Notification/FormNotificationBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								Core/Notification/FormNotificationBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,236 @@ | |||||||
|  | using CefSharp.WinForms; | ||||||
|  | using System.Drawing; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Core.Bridge; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Handling; | ||||||
|  | using TweetDuck.Core.Handling.General; | ||||||
|  | using TweetDuck.Core.Other.Analytics; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification{ | ||||||
|  |     abstract partial class FormNotificationBase : Form, AnalyticsFile.IProvider{ | ||||||
|  |         protected static UserConfig Config => Program.Config.User; | ||||||
|  | 
 | ||||||
|  |         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{ | ||||||
|  |                 Screen screen; | ||||||
|  | 
 | ||||||
|  |                 if (Config.NotificationDisplay > 0 && Config.NotificationDisplay <= Screen.AllScreens.Length){ | ||||||
|  |                     screen = Screen.AllScreens[Config.NotificationDisplay-1]; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     screen = Screen.FromControl(owner); | ||||||
|  |                 } | ||||||
|  |              | ||||||
|  |                 int edgeDist = Config.NotificationEdgeDistance; | ||||||
|  | 
 | ||||||
|  |                 switch(Config.NotificationPosition){ | ||||||
|  |                     case TweetNotification.Position.TopLeft: | ||||||
|  |                         return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+edgeDist); | ||||||
|  | 
 | ||||||
|  |                     case TweetNotification.Position.TopRight: | ||||||
|  |                         return new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist); | ||||||
|  | 
 | ||||||
|  |                     case TweetNotification.Position.BottomLeft: | ||||||
|  |                         return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height); | ||||||
|  | 
 | ||||||
|  |                     case TweetNotification.Position.BottomRight: | ||||||
|  |                         return new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height); | ||||||
|  | 
 | ||||||
|  |                     case TweetNotification.Position.Custom: | ||||||
|  |                         if (!Config.IsCustomNotificationPositionSet){ | ||||||
|  |                             Config.CustomNotificationPosition = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist); | ||||||
|  |                             Config.Save(); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         return Config.CustomNotificationPosition; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return Location; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation; | ||||||
|  |         protected virtual bool CanDragWindow => true; | ||||||
|  | 
 | ||||||
|  |         public new Point Location{ | ||||||
|  |             get{ | ||||||
|  |                 return base.Location; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             set{ | ||||||
|  |                 Visible = (base.Location = value) != ControlExtensions.InvisibleLocation; | ||||||
|  |                 FormBorderStyle = NotificationBorderStyle; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected virtual FormBorderStyle NotificationBorderStyle{ | ||||||
|  |             get{ | ||||||
|  |                 if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab | ||||||
|  |                     return FormBorderStyle.FixedSingle; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     return FormBorderStyle.FixedToolWindow; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public AnalyticsFile AnalyticsFile => owner.AnalyticsFile; | ||||||
|  |          | ||||||
|  |         protected override bool ShowWithoutActivation => true; | ||||||
|  |          | ||||||
|  |         protected float DpiScale { get; } | ||||||
|  |         protected double SizeScale => DpiScale*Config.ZoomLevel/100.0; | ||||||
|  | 
 | ||||||
|  |         protected readonly FormBrowser owner; | ||||||
|  |         protected readonly ChromiumWebBrowser browser; | ||||||
|  |          | ||||||
|  |         private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification(); | ||||||
|  | 
 | ||||||
|  |         private TweetNotification currentNotification; | ||||||
|  |         private int pauseCounter; | ||||||
|  |          | ||||||
|  |         public string CurrentTweetUrl => currentNotification?.TweetUrl; | ||||||
|  |         public string CurrentQuoteUrl => currentNotification?.QuoteUrl; | ||||||
|  | 
 | ||||||
|  |         public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId); | ||||||
|  | 
 | ||||||
|  |         protected bool IsPaused => pauseCounter > 0; | ||||||
|  |         protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position)); | ||||||
|  |          | ||||||
|  |         public bool FreezeTimer { get; set; } | ||||||
|  |         public bool ContextMenuOpen { get; set; } | ||||||
|  | 
 | ||||||
|  |         protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             this.owner = owner; | ||||||
|  |             this.owner.FormClosed += owner_FormClosed; | ||||||
|  | 
 | ||||||
|  |             ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory(); | ||||||
|  |             resourceHandlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler); | ||||||
|  |             resourceHandlerFactory.RegisterHandler(TweetNotification.AppLogo); | ||||||
|  | 
 | ||||||
|  |             this.browser = new ChromiumWebBrowser("about:blank"){ | ||||||
|  |                 MenuHandler = new ContextMenuNotification(this, enableContextMenu), | ||||||
|  |                 JsDialogHandler = new JavaScriptDialogHandler(), | ||||||
|  |                 LifeSpanHandler = new LifeSpanHandler(), | ||||||
|  |                 RequestHandler = new RequestHandlerBase(false), | ||||||
|  |                 ResourceHandlerFactory = resourceHandlerFactory | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             this.browser.Dock = DockStyle.None; | ||||||
|  |             this.browser.ClientSize = ClientSize; | ||||||
|  |             this.browser.SetupZoomEvents(); | ||||||
|  | 
 | ||||||
|  |             Controls.Add(browser); | ||||||
|  | 
 | ||||||
|  |             Disposed += (sender, args) => { | ||||||
|  |                 this.browser.Dispose(); | ||||||
|  |                 this.owner.FormClosed -= owner_FormClosed; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             DpiScale = this.GetDPIScale(); | ||||||
|  | 
 | ||||||
|  |             // ReSharper disable once VirtualMemberCallInContructor | ||||||
|  |             UpdateTitle(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override void WndProc(ref Message m){ | ||||||
|  |             if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanDragWindow){ // WM_SYSCOMMAND, SC_MOVE | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             base.WndProc(ref m); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // event handlers | ||||||
|  | 
 | ||||||
|  |         private void owner_FormClosed(object sender, FormClosedEventArgs e){ | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // notification methods | ||||||
|  | 
 | ||||||
|  |         public virtual void HideNotification(){ | ||||||
|  |             browser.Load("about:blank"); | ||||||
|  |             DisplayTooltip(null); | ||||||
|  | 
 | ||||||
|  |             Location = ControlExtensions.InvisibleLocation; | ||||||
|  |             currentNotification = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public virtual void FinishCurrentNotification(){} | ||||||
|  | 
 | ||||||
|  |         public virtual void PauseNotification(){ | ||||||
|  |             if (pauseCounter++ == 0 && IsNotificationVisible){ | ||||||
|  |                 Location = ControlExtensions.InvisibleLocation; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public virtual void ResumeNotification(){ | ||||||
|  |             if (pauseCounter > 0){ | ||||||
|  |                 --pauseCounter; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected abstract string GetTweetHTML(TweetNotification tweet); | ||||||
|  | 
 | ||||||
|  |         protected virtual void LoadTweet(TweetNotification tweet){ | ||||||
|  |             currentNotification = tweet; | ||||||
|  |             resourceHandler.SetHTML(GetTweetHTML(tweet)); | ||||||
|  | 
 | ||||||
|  |             browser.Load(TwitterUtils.TweetDeckURL); | ||||||
|  |             DisplayTooltip(null); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected virtual void SetNotificationSize(int width, int height){ | ||||||
|  |             browser.ClientSize = ClientSize = new Size(BrowserUtils.Scale(width, SizeScale), BrowserUtils.Scale(height, SizeScale)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected virtual void UpdateTitle(){ | ||||||
|  |             string title = currentNotification?.ColumnTitle; | ||||||
|  |             Text = string.IsNullOrEmpty(title) || !Config.DisplayNotificationColumn ? Program.BrandName : $"{Program.BrandName} - {title}"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ShowTweetDetail(){ | ||||||
|  |             if (currentNotification != null){ | ||||||
|  |                 owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void MoveToVisibleLocation(){ | ||||||
|  |             bool needsReactivating = Location == ControlExtensions.InvisibleLocation; | ||||||
|  |             Location = PrimaryLocation; | ||||||
|  | 
 | ||||||
|  |             if (needsReactivating){ | ||||||
|  |                 NativeMethods.SetFormPos(this, NativeMethods.HWND_TOPMOST, NativeMethods.SWP_NOACTIVATE); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void DisplayTooltip(string text){ | ||||||
|  |             if (string.IsNullOrEmpty(text)){ | ||||||
|  |                 toolTip.Hide(this); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 Point position = PointToClient(Cursor.Position); | ||||||
|  |                 position.Offset(20, 5); | ||||||
|  |                 toolTip.Show(text, this, position); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Browser.Notification { | namespace TweetDuck.Core.Notification { | ||||||
|     partial class FormNotificationMain { |     partial class FormNotificationMain { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -26,7 +26,7 @@ | |||||||
|             this.components = new System.ComponentModel.Container(); |             this.components = new System.ComponentModel.Container(); | ||||||
|             this.timerDisplayDelay = new System.Windows.Forms.Timer(this.components); |             this.timerDisplayDelay = new System.Windows.Forms.Timer(this.components); | ||||||
|             this.timerProgress = new System.Windows.Forms.Timer(this.components); |             this.timerProgress = new System.Windows.Forms.Timer(this.components); | ||||||
|             this.progressBarTimer = new TweetDuck.Controls.FlatProgressBar(); |             this.progressBarTimer = new TweetDuck.Core.Controls.FlatProgressBar(); | ||||||
|             this.SuspendLayout(); |             this.SuspendLayout(); | ||||||
|             //  |             //  | ||||||
|             // timerDisplayDelay |             // timerDisplayDelay | ||||||
| @@ -59,7 +59,6 @@ | |||||||
|             this.BackColor = System.Drawing.SystemColors.Control; |             this.BackColor = System.Drawing.SystemColors.Control; | ||||||
|             this.ClientSize = new System.Drawing.Size(284, 122); |             this.ClientSize = new System.Drawing.Size(284, 122); | ||||||
|             this.Controls.Add(this.progressBarTimer); |             this.Controls.Add(this.progressBarTimer); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotification_FormClosing); |             this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotification_FormClosing); | ||||||
|             this.ResumeLayout(false); |             this.ResumeLayout(false); | ||||||
| 
 | 
 | ||||||
							
								
								
									
										276
									
								
								Core/Notification/FormNotificationMain.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								Core/Notification/FormNotificationMain.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,276 @@ | |||||||
|  | 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.Utils; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Plugins.Enums; | ||||||
|  | using TweetDuck.Resources; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification{ | ||||||
|  |     abstract partial class FormNotificationMain : FormNotificationBase{ | ||||||
|  |         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 != Config.DisplayNotificationTimer || prevFontSize != FontSizeLevel; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             set{ | ||||||
|  |                 if (value){ | ||||||
|  |                     prevDisplayTimer = null; | ||||||
|  |                     prevFontSize = null; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     prevDisplayTimer = Config.DisplayNotificationTimer; | ||||||
|  |                     prevFontSize = FontSizeLevel; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private int BaseClientWidth{ | ||||||
|  |             get{ | ||||||
|  |                 switch(Config.NotificationSize){ | ||||||
|  |                     default: | ||||||
|  |                         return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel)); | ||||||
|  | 
 | ||||||
|  |                     case TweetNotification.Size.Custom: | ||||||
|  |                         return Config.CustomNotificationSize.Width; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private int BaseClientHeight{ | ||||||
|  |             get{ | ||||||
|  |                 switch(Config.NotificationSize){ | ||||||
|  |                     default: | ||||||
|  |                         return BrowserUtils.Scale(122, SizeScale*(1.0+0.08*FontSizeLevel)); | ||||||
|  | 
 | ||||||
|  |                     case TweetNotification.Size.Custom: | ||||||
|  |                         return Config.CustomNotificationSize.Height; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected virtual string BodyClasses => IsCursorOverBrowser ? "td-notification td-hover" : "td-notification"; | ||||||
|  |          | ||||||
|  |         public Size BrowserSize => Config.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize; | ||||||
|  | 
 | ||||||
|  |         protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             this.plugins = pluginManager; | ||||||
|  |             this.timerBarHeight = BrowserUtils.Scale(4, DpiScale); | ||||||
|  |              | ||||||
|  |             browser.KeyboardHandler = new KeyboardHandlerNotification(this); | ||||||
|  |             browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge.Notification(owner, this)); | ||||||
|  | 
 | ||||||
|  |             browser.LoadingStateChanged += Browser_LoadingStateChanged; | ||||||
|  |             browser.FrameLoadEnd += Browser_FrameLoadEnd; | ||||||
|  | 
 | ||||||
|  |             plugins.Register(browser, PluginEnvironment.Notification, this); | ||||||
|  | 
 | ||||||
|  |             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), Config.NotificationScrollSpeed*0.01), CefEventFlags.None); | ||||||
|  |                     return NativeMethods.HOOK_HANDLED; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){ | ||||||
|  |                     int extraButton = NativeMethods.GetMouseHookData(lParam); | ||||||
|  | 
 | ||||||
|  |                     if (extraButton == 2){ // forward button | ||||||
|  |                         this.InvokeAsyncSafe(FinishCurrentNotification); | ||||||
|  |                     } | ||||||
|  |                     else if (extraButton == 1){ // back button | ||||||
|  |                         this.InvokeAsyncSafe(Close); | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     blockXButtonUp = true; | ||||||
|  |                     this.InvokeAsyncSafe(AnalyticsFile.NotificationExtraMouseButtons.Trigger); | ||||||
|  |                     return NativeMethods.HOOK_HANDLED; | ||||||
|  |                 } | ||||||
|  |                 else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){ | ||||||
|  |                     blockXButtonUp = false; | ||||||
|  | 
 | ||||||
|  |                     if (!Visible){ | ||||||
|  |                         StopMouseHook(false); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return NativeMethods.HOOK_HANDLED; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return NativeMethods.CallNextHookEx(mouseHook, nCode, wParam, lParam); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // event handlers | ||||||
|  | 
 | ||||||
|  |         private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){ | ||||||
|  |             if (e.CloseReason == CloseReason.UserClosing){ | ||||||
|  |                 HideNotification(); | ||||||
|  |                 e.Cancel = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ | ||||||
|  |             if (!e.IsLoading && browser.Address != "about:blank"){ | ||||||
|  |                 this.InvokeSafe(() => { | ||||||
|  |                     Visible = true; // ensures repaint before moving the window to a visible location | ||||||
|  |                     timerDisplayDelay.Start(); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ | ||||||
|  |             IFrame frame = e.Frame; | ||||||
|  | 
 | ||||||
|  |             if (frame.IsMain && browser.Address != "about:blank"){ | ||||||
|  |                 frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification)); | ||||||
|  |                 ScriptLoader.ExecuteFile(frame, "notification.js", this); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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(Config.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 = Config.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 = tweet.GenerateHtml(BodyClasses, this); | ||||||
|  | 
 | ||||||
|  |             foreach(InjectedHTML injection in plugins.NotificationInjections){ | ||||||
|  |                 html = injection.InjectInto(html); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return html; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override void LoadTweet(TweetNotification tweet){ | ||||||
|  |             timerProgress.Stop(); | ||||||
|  |             totalTime = timeLeft = tweet.GetDisplayDuration(Config.NotificationDurationValue); | ||||||
|  |             progressBarTimer.Value = Config.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum; | ||||||
|  | 
 | ||||||
|  |             base.LoadTweet(tweet); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override void SetNotificationSize(int width, int height){ | ||||||
|  |             if (Config.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,4 +1,4 @@ | |||||||
| namespace TweetDuck.Browser.Notification { | namespace TweetDuck.Core.Notification { | ||||||
|     partial class FormNotificationTweet { |     partial class FormNotificationTweet { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
							
								
								
									
										165
									
								
								Core/Notification/FormNotificationTweet.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								Core/Notification/FormNotificationTweet.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Drawing; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification{ | ||||||
|  |     sealed partial class FormNotificationTweet : FormNotificationMain{ | ||||||
|  |         private const int NonIntrusiveIdleLimit = 30; | ||||||
|  |         private const int TrimMinimum = 32; | ||||||
|  | 
 | ||||||
|  |         protected override Point PrimaryLocation => hasTemporarilyMoved && IsNotificationVisible ? Location : base.PrimaryLocation; | ||||||
|  |         private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position); | ||||||
|  | 
 | ||||||
|  |         protected override bool CanDragWindow{ | ||||||
|  |             get{ | ||||||
|  |                 if (ModifierKeys.HasFlag(Keys.Alt)){ | ||||||
|  |                     hasTemporarilyMoved = true; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4); | ||||||
|  |         private bool needsTrim; | ||||||
|  |         private bool hasTemporarilyMoved; | ||||||
|  | 
 | ||||||
|  |         public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             Config.MuteToggled += Config_MuteToggled; | ||||||
|  |             Disposed += (sender, args) => Config.MuteToggled -= Config_MuteToggled; | ||||||
|  | 
 | ||||||
|  |             if (Config.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 (Config.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() < Config.NotificationIdlePauseSeconds){ | ||||||
|  |                 ResumeNotification(); | ||||||
|  |                 timerIdlePauseCheck.Stop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // notification methods | ||||||
|  | 
 | ||||||
|  |         public override void ShowNotification(TweetNotification notification){ | ||||||
|  |             tweetQueue.Enqueue(notification); | ||||||
|  |              | ||||||
|  |             if (!IsPaused){ | ||||||
|  |                 UpdateTitle(); | ||||||
|  | 
 | ||||||
|  |                 if (totalTime == 0){ | ||||||
|  |                     LoadNextNotification(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             needsTrim |= tweetQueue.Count >= TrimMinimum; | ||||||
|  |             AnalyticsFile.DesktopNotifications.Trigger(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void HideNotification(){ | ||||||
|  |             base.HideNotification(); | ||||||
|  |             tweetQueue.Clear(); | ||||||
|  | 
 | ||||||
|  |             if (needsTrim){ | ||||||
|  |                 tweetQueue.TrimExcess(); | ||||||
|  |                 needsTrim = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             hasTemporarilyMoved = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void FinishCurrentNotification(){ | ||||||
|  |             if (tweetQueue.Count > 0){ | ||||||
|  |                 LoadNextNotification(); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 HideNotification(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void ResumeNotification(){ | ||||||
|  |             bool wasPaused = IsPaused; | ||||||
|  |             base.ResumeNotification(); | ||||||
|  | 
 | ||||||
|  |             if (wasPaused && !IsPaused && !pausedDuringNotification && tweetQueue.Count > 0){ | ||||||
|  |                 LoadNextNotification(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void LoadNextNotification(){ | ||||||
|  |             if (!IsNotificationVisible){ | ||||||
|  |                 if (Config.NotificationNonIntrusiveMode && IsCursorOverNotificationArea && NativeMethods.GetIdleSeconds() < NonIntrusiveIdleLimit){ | ||||||
|  |                     if (!timerCursorCheck.Enabled){ | ||||||
|  |                         PauseNotification(); | ||||||
|  |                         timerCursorCheck.Start(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 else if (Config.NotificationIdlePauseSeconds > 0 && NativeMethods.GetIdleSeconds() >= Config.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(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,98 @@ | |||||||
|  | using System; | ||||||
|  | using System.Drawing; | ||||||
|  | using System.Drawing.Imaging; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Resources; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification.Screenshot{ | ||||||
|  |     sealed class FormNotificationScreenshotable : FormNotificationBase{ | ||||||
|  |         protected override bool CanDragWindow => false; | ||||||
|  | 
 | ||||||
|  |         private readonly PluginManager plugins; | ||||||
|  |         private int height; | ||||||
|  | 
 | ||||||
|  |         public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager, string html, int width) : base(owner, false){ | ||||||
|  |             this.plugins = pluginManager; | ||||||
|  | 
 | ||||||
|  |             int realWidth = BrowserUtils.Scale(width, DpiScale); | ||||||
|  | 
 | ||||||
|  |             browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback)); | ||||||
|  | 
 | ||||||
|  |             browser.LoadingStateChanged += (sender, args) => { | ||||||
|  |                 if (args.IsLoading){ | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 string script = ScriptLoader.LoadResourceSilent("screenshot.js"); | ||||||
|  |                          | ||||||
|  |                 if (script == null){ | ||||||
|  |                     this.InvokeAsyncSafe(callback); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 using(IFrame frame = args.Browser.MainFrame){ | ||||||
|  |                     ScriptLoader.ExecuteScript(frame, script.Replace("{width}", realWidth.ToString()).Replace("{frames}", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot"); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |              | ||||||
|  |             SetNotificationSize(realWidth, 1024); | ||||||
|  |             LoadTweet(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override string GetTweetHTML(TweetNotification tweet){ | ||||||
|  |             string html = tweet.GenerateHtml("td-screenshot", this); | ||||||
|  | 
 | ||||||
|  |             foreach(InjectedHTML injection in plugins.NotificationInjections){ | ||||||
|  |                 html = injection.InjectInto(html); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return html; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SetScreenshotHeight(int browserHeight){ | ||||||
|  |             this.height = BrowserUtils.Scale(browserHeight, SizeScale); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool TakeScreenshot(bool ignoreHeightError = false){ | ||||||
|  |             if (!ignoreHeightError){ | ||||||
|  |                 if (height == 0){ | ||||||
|  |                     FormMessage.Error("Screenshot Failed", "Could not detect screenshot size.", FormMessage.OK); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |                 else if (height > ClientSize.Height){ | ||||||
|  |                     FormMessage.Error("Screenshot Failed", $"Screenshot is too large: {height}px > {ClientSize.Height}px", FormMessage.OK); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!WindowsUtils.IsAeroEnabled){ | ||||||
|  |                 MoveToVisibleLocation(); // TODO make this look nicer I guess | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             IntPtr context = NativeMethods.GetDC(this.Handle); | ||||||
|  | 
 | ||||||
|  |             if (context == IntPtr.Zero){ | ||||||
|  |                 FormMessage.Error("Screenshot Failed", "Could not retrieve a graphics context handle for the notification window to take the screenshot.", FormMessage.OK); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 using(Bitmap bmp = new Bitmap(ClientSize.Width, Math.Max(1, height), PixelFormat.Format32bppRgb)){ | ||||||
|  |                     try{ | ||||||
|  |                         NativeMethods.RenderSourceIntoBitmap(context, bmp); | ||||||
|  |                     }finally{ | ||||||
|  |                         NativeMethods.ReleaseDC(this.Handle, context); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     Clipboard.SetImage(bmp); | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								Core/Notification/Screenshot/ScreenshotBridge.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Core/Notification/Screenshot/ScreenshotBridge.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | using System; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification.Screenshot{ | ||||||
|  |     sealed class ScreenshotBridge{ | ||||||
|  |         private readonly Control owner; | ||||||
|  | 
 | ||||||
|  |         private readonly Action<int> safeSetHeight; | ||||||
|  |         private readonly Action safeTriggerScreenshot; | ||||||
|  | 
 | ||||||
|  |         public ScreenshotBridge(Control owner, Action<int> safeSetHeight, Action safeTriggerScreenshot){ | ||||||
|  |             this.owner = owner; | ||||||
|  |             this.safeSetHeight = safeSetHeight; | ||||||
|  |             this.safeTriggerScreenshot = safeTriggerScreenshot; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SetHeight(int tweetHeight){ | ||||||
|  |             owner.InvokeSafe(() => safeSetHeight(tweetHeight)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void TriggerScreenshot(){ | ||||||
|  |             owner.InvokeSafe(safeTriggerScreenshot); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										148
									
								
								Core/Notification/Screenshot/TweetScreenshotManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								Core/Notification/Screenshot/TweetScreenshotManager.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | #if DEBUG | ||||||
|  | // Uncomment to keep screenshot windows visible for debugging | ||||||
|  | // #define NO_HIDE_SCREENSHOTS | ||||||
|  | 
 | ||||||
|  | // Uncomment to generate screenshots of individual frames for at most 1 second | ||||||
|  | // #define GEN_SCREENSHOT_FRAMES | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | 
 | ||||||
|  | #if GEN_SCREENSHOT_FRAMES | ||||||
|  | using System.Drawing.Imaging; | ||||||
|  | using System.IO; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification.Screenshot{ | ||||||
|  |     sealed class TweetScreenshotManager : IDisposable{ | ||||||
|  |         private readonly FormBrowser owner; | ||||||
|  |         private readonly PluginManager plugins; | ||||||
|  |         private readonly Timer timeout; | ||||||
|  |         private readonly Timer disposer; | ||||||
|  | 
 | ||||||
|  |         #if GEN_SCREENSHOT_FRAMES | ||||||
|  |         private readonly Timer debugger; | ||||||
|  |         private int frameCounter; | ||||||
|  | 
 | ||||||
|  |         public const int WaitFrames = 60; | ||||||
|  |         #else | ||||||
|  |         public const int WaitFrames = 5; | ||||||
|  |         #endif | ||||||
|  |          | ||||||
|  |         private FormNotificationScreenshotable screenshot; | ||||||
|  | 
 | ||||||
|  |         public TweetScreenshotManager(FormBrowser owner, PluginManager pluginManager){ | ||||||
|  |             this.owner = owner; | ||||||
|  |             this.plugins = pluginManager; | ||||||
|  | 
 | ||||||
|  |             this.timeout = new Timer{ Interval = 8000 }; | ||||||
|  |             this.timeout.Tick += timeout_Tick; | ||||||
|  | 
 | ||||||
|  |             this.disposer = new Timer{ Interval = 1 }; | ||||||
|  |             this.disposer.Tick += disposer_Tick; | ||||||
|  | 
 | ||||||
|  |             #if GEN_SCREENSHOT_FRAMES | ||||||
|  |             this.debugger = new Timer{ Interval = 16 }; | ||||||
|  |             this.debugger.Tick += debugger_Tick; | ||||||
|  |             #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void timeout_Tick(object sender, EventArgs e){ | ||||||
|  |             timeout.Stop(); | ||||||
|  |             OnFinished(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void disposer_Tick(object sender, EventArgs e){ | ||||||
|  |             disposer.Stop(); | ||||||
|  |             screenshot.Dispose(); | ||||||
|  |             screenshot = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Trigger(string html, int width){ | ||||||
|  |             if (screenshot != null){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             screenshot = new FormNotificationScreenshotable(Callback, owner, plugins, html, width); | ||||||
|  |             screenshot.Show(); | ||||||
|  |             timeout.Start(); | ||||||
|  | 
 | ||||||
|  |             #if GEN_SCREENSHOT_FRAMES | ||||||
|  |             StartDebugger(); | ||||||
|  |             #endif | ||||||
|  | 
 | ||||||
|  |             #if !NO_HIDE_SCREENSHOTS | ||||||
|  |             owner.IsWaiting = true; | ||||||
|  |             #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Callback(){ | ||||||
|  |             if (!timeout.Enabled){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             timeout.Stop(); | ||||||
|  |             screenshot.TakeScreenshot(); | ||||||
|  |              | ||||||
|  |             #if !NO_HIDE_SCREENSHOTS | ||||||
|  |             OnFinished(); | ||||||
|  |             #else | ||||||
|  |             screenshot.MoveToVisibleLocation(); | ||||||
|  |             screenshot.FormClosed += (sender, args) => disposer.Start(); | ||||||
|  |             #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void OnFinished(){ | ||||||
|  |             #if GEN_SCREENSHOT_FRAMES | ||||||
|  |             debugger.Stop(); | ||||||
|  |             #endif | ||||||
|  | 
 | ||||||
|  |             screenshot.Location = ControlExtensions.InvisibleLocation; | ||||||
|  |             owner.IsWaiting = false; | ||||||
|  |             disposer.Start(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Dispose(){ | ||||||
|  |             #if GEN_SCREENSHOT_FRAMES | ||||||
|  |             debugger.Dispose(); | ||||||
|  |             #endif | ||||||
|  | 
 | ||||||
|  |             timeout.Dispose(); | ||||||
|  |             disposer.Dispose(); | ||||||
|  |             screenshot?.Dispose(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #if GEN_SCREENSHOT_FRAMES | ||||||
|  |         private static readonly string DebugScreenshotPath = Path.Combine(Program.StoragePath, "TD_Screenshots"); | ||||||
|  | 
 | ||||||
|  |         private void StartDebugger(){ | ||||||
|  |             frameCounter = 0; | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                 Directory.Delete(DebugScreenshotPath, true); | ||||||
|  |                 WindowsUtils.TrySleepUntil(() => !Directory.Exists(DebugScreenshotPath), 1000, 10); | ||||||
|  |             }catch(DirectoryNotFoundException){} | ||||||
|  |              | ||||||
|  |             Directory.CreateDirectory(DebugScreenshotPath); | ||||||
|  |             debugger.Start(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void debugger_Tick(object sender, EventArgs e){ | ||||||
|  |             if (frameCounter < 63 && screenshot.TakeScreenshot(true)){ | ||||||
|  |                 try{ | ||||||
|  |                     Clipboard.GetImage()?.Save(Path.Combine(DebugScreenshotPath, "frame_"+(++frameCounter)+".png"), ImageFormat.Png); | ||||||
|  |                 }catch{ | ||||||
|  |                     System.Diagnostics.Debug.WriteLine("Failed generating frame "+frameCounter); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 debugger.Stop(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         #endif | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								Core/Notification/SoundNotification.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								Core/Notification/SoundNotification.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | using System.Drawing; | ||||||
|  | using System.IO; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Other; | ||||||
|  | using TweetDuck.Core.Other.Settings; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification{ | ||||||
|  |     static class SoundNotification{ | ||||||
|  |         public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm"; | ||||||
|  |          | ||||||
|  |         public static IResourceHandler CreateFileHandler(string path){ | ||||||
|  |             string mimeType; | ||||||
|  | 
 | ||||||
|  |             switch(Path.GetExtension(path)){ | ||||||
|  |                 case ".weba": | ||||||
|  |                 case ".webm": mimeType = "audio/webm"; break; | ||||||
|  |                 case ".wav": mimeType = "audio/wav"; break; | ||||||
|  |                 case ".ogg": mimeType = "audio/ogg"; break; | ||||||
|  |                 case ".mp3": mimeType = "audio/mp3"; break; | ||||||
|  |                 case ".flac": mimeType = "audio/flac"; break; | ||||||
|  |                 case ".opus": mimeType = "audio/ogg; codecs=opus"; break; | ||||||
|  |                 default: mimeType = null; break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                 return ResourceHandler.FromFilePath(path, mimeType); | ||||||
|  |             }catch{ | ||||||
|  |                 FormBrowser browser = FormManager.TryFind<FormBrowser>(); | ||||||
|  | 
 | ||||||
|  |                 browser?.InvokeAsyncSafe(() => { | ||||||
|  |                     using(FormMessage form = new FormMessage("Sound Notification Error", "Could not find custom notification sound file:\n"+path, MessageBoxIcon.Error)){ | ||||||
|  |                         form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused); | ||||||
|  |                          | ||||||
|  |                         Button btnViewOptions = form.AddButton("View Options"); | ||||||
|  |                         btnViewOptions.Width += 16; | ||||||
|  |                         btnViewOptions.Location = new Point(btnViewOptions.Location.X-16, btnViewOptions.Location.Y); | ||||||
|  | 
 | ||||||
|  |                         if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnViewOptions){ | ||||||
|  |                             browser.OpenSettings(typeof(TabSettingsSounds)); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                  | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								Core/Notification/TweetNotification.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								Core/Notification/TweetNotification.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | using System; | ||||||
|  | using System.Text; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using TweetDuck.Core.Bridge; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | using TweetDuck.Resources; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Notification{ | ||||||
|  |     sealed class TweetNotification{ | ||||||
|  |         private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>"; | ||||||
|  |         public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png")); | ||||||
|  |          | ||||||
|  |         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; | ||||||
|  |          | ||||||
|  |         public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl){ | ||||||
|  |             this.ColumnId = columnId; | ||||||
|  |             this.ChirpId = chirpId; | ||||||
|  | 
 | ||||||
|  |             this.ColumnTitle = title; | ||||||
|  |             this.TweetUrl = tweetUrl; | ||||||
|  |             this.QuoteUrl = quoteUrl; | ||||||
|  | 
 | ||||||
|  |             this.html = html; | ||||||
|  |             this.characters = characters; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int GetDisplayDuration(int value){ | ||||||
|  |             return 2000+Math.Max(1000, value*characters); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string GenerateHtml(string bodyClasses, Control sync){ | ||||||
|  |             string headLayout = TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout; | ||||||
|  |             string mainCSS = ScriptLoader.LoadResource("styles/notification.css", sync) ?? string.Empty; | ||||||
|  |             string customCSS = Program.Config.User.CustomNotificationCSS ?? string.Empty; | ||||||
|  |              | ||||||
|  |             StringBuilder build = new StringBuilder(320 + headLayout.Length + mainCSS.Length + customCSS.Length + html.Length); | ||||||
|  |             build.Append("<!DOCTYPE html>"); | ||||||
|  |             build.Append(headLayout); | ||||||
|  |             build.Append("<style type='text/css'>").Append(mainCSS).Append("</style>"); | ||||||
|  | 
 | ||||||
|  |             if (!string.IsNullOrWhiteSpace(customCSS)){ | ||||||
|  |                 build.Append("<style type='text/css'>").Append(customCSS).Append("</style>"); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             build.Append("</head><body class='scroll-styled-v"); | ||||||
|  | 
 | ||||||
|  |             if (!string.IsNullOrEmpty(bodyClasses)){ | ||||||
|  |                 build.Append(' ').Append(bodyClasses); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             build.Append("'><div class='column' style='width:100%!important;min-height:100vh!important;height:auto!important;overflow:initial!important;'>"); | ||||||
|  |             build.Append(html); | ||||||
|  |             build.Append("</div></body></html>"); | ||||||
|  |             return build.ToString(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										135
									
								
								Core/Other/Analytics/AnalyticsFile.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								Core/Other/Analytics/AnalyticsFile.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | |||||||
|  | using System; | ||||||
|  | using System.Diagnostics.CodeAnalysis; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Reflection; | ||||||
|  | using TweetDuck.Data.Serialization; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Analytics{ | ||||||
|  |     [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")] | ||||||
|  |     sealed class AnalyticsFile{ | ||||||
|  |         private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>(); | ||||||
|  | 
 | ||||||
|  |         static AnalyticsFile(){ | ||||||
|  |             Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{ | ||||||
|  |                 ConvertToString = value => value.ToBinary().ToString(), | ||||||
|  |                 ConvertToObject = value => DateTime.FromBinary(long.Parse(value)) | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             Serializer.RegisterTypeConverter(typeof(Counter), new SingleTypeConverter<Counter>{ | ||||||
|  |                 ConvertToString = value => value.Value.ToString(), | ||||||
|  |                 ConvertToObject = value => int.Parse(value) | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static readonly AnalyticsFile Dummy = new AnalyticsFile(); | ||||||
|  | 
 | ||||||
|  |         // STATE PROPERTIES | ||||||
|  |          | ||||||
|  |         public DateTime LastDataCollection  { get; set; } = DateTime.MinValue; | ||||||
|  |         public string LastCollectionVersion { get; set; } = null; | ||||||
|  |         public string LastCollectionMessage { get; set; } = null; | ||||||
|  | 
 | ||||||
|  |         // USAGE DATA | ||||||
|  | 
 | ||||||
|  |         public Counter OpenOptions { get; private set; } = 0; | ||||||
|  |         public Counter OpenPlugins { get; private set; } = 0; | ||||||
|  |         public Counter OpenAbout   { get; private set; } = 0; | ||||||
|  |         public Counter OpenGuide   { get; private set; } = 0; | ||||||
|  | 
 | ||||||
|  |         public Counter DesktopNotifications { get; private set; } = 0; | ||||||
|  |         public Counter SoundNotifications   { get; private set; } = 0; | ||||||
|  |         public Counter NotificationMutes    { get; private set; } = 0; | ||||||
|  |          | ||||||
|  |         public Counter BrowserContextMenus           { get; private set; } = 0; | ||||||
|  |         public Counter BrowserExtraMouseButtons      { get; private set; } = 0; | ||||||
|  |         public Counter NotificationContextMenus      { get; private set; } = 0; | ||||||
|  |         public Counter NotificationExtraMouseButtons { get; private set; } = 0; | ||||||
|  |         public Counter NotificationKeyboardShortcuts { get; private set; } = 0; | ||||||
|  | 
 | ||||||
|  |         public Counter BrowserReloads   { get; private set; } = 0; | ||||||
|  |         public Counter CopiedUsernames  { get; private set; } = 0; | ||||||
|  |         public Counter ViewedImages     { get; private set; } = 0; | ||||||
|  |         public Counter DownloadedImages { get; private set; } = 0; | ||||||
|  |         public Counter DownloadedVideos { get; private set; } = 0; | ||||||
|  |         public Counter UsedROT13        { get; private set; } = 0; | ||||||
|  | 
 | ||||||
|  |         public Counter TweetScreenshots { get; private set; } = 0; | ||||||
|  |         public Counter TweetDetails     { get; private set; } = 0; | ||||||
|  |         public Counter VideoPlays       { get; private set; } = 0; | ||||||
|  | 
 | ||||||
|  |         // END OF DATA | ||||||
|  | 
 | ||||||
|  |         public event EventHandler PropertyChanged; | ||||||
|  |          | ||||||
|  |         private readonly string file; | ||||||
|  |          | ||||||
|  |         private AnalyticsFile(string file){ | ||||||
|  |             this.file = file; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private AnalyticsFile(){ | ||||||
|  |             this.file = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SetupProperties(){ | ||||||
|  |             foreach(Counter counter in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(Counter)).Select(prop => (Counter)prop.GetValue(this))){ | ||||||
|  |                 counter.SetOwner(this); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void OnPropertyChanged(){ | ||||||
|  |             PropertyChanged?.Invoke(this, EventArgs.Empty); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Save(){ | ||||||
|  |             if (file == null){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                 Serializer.Write(file, this); | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Analytics File Error", "Could not save the analytics file.", true, e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public static AnalyticsFile Load(string file){ | ||||||
|  |             AnalyticsFile config = new AnalyticsFile(file); | ||||||
|  |              | ||||||
|  |             try{ | ||||||
|  |                 Serializer.ReadIfExists(file, config); | ||||||
|  |             }catch(Exception e){ | ||||||
|  |                 Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             config.SetupProperties(); | ||||||
|  |             return config; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public interface IProvider{ | ||||||
|  |             AnalyticsFile AnalyticsFile { get; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public sealed class Counter{ | ||||||
|  |             public int Value { get; private set; } | ||||||
|  | 
 | ||||||
|  |             private AnalyticsFile owner; | ||||||
|  | 
 | ||||||
|  |             public Counter(int value){ | ||||||
|  |                 this.Value = value; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void SetOwner(AnalyticsFile owner){ | ||||||
|  |                 this.owner = owner; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public void Trigger(){ | ||||||
|  |                 ++Value; | ||||||
|  |                 owner?.OnPropertyChanged(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public static implicit operator int(Counter counter) => counter.Value; | ||||||
|  |             public static implicit operator Counter(int value) => new Counter(value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										158
									
								
								Core/Other/Analytics/AnalyticsManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								Core/Other/Analytics/AnalyticsManager.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | |||||||
|  | // Uncomment to debug reports locally | ||||||
|  | // #define ANALYTICS_LOCALHOST | ||||||
|  | // #define ANALYTICS_INSTANT | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Net; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using System.Timers; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Analytics{ | ||||||
|  |     sealed class AnalyticsManager : IDisposable{ | ||||||
|  |         private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(14); | ||||||
|  | 
 | ||||||
|  |         private static readonly Uri CollectionUrl = new Uri( | ||||||
|  |             #if (DEBUG && ANALYTICS_LOCALHOST) | ||||||
|  |             "http://localhost/newhome/tweetduck/~breadcrumb/request.php?type=report" | ||||||
|  |             #else | ||||||
|  |             "https://tweetduck.chylex.com/breadcrumb/report" | ||||||
|  |             #endif | ||||||
|  |             ); | ||||||
|  |          | ||||||
|  |         public AnalyticsFile File { get; } | ||||||
|  | 
 | ||||||
|  |         private readonly FormBrowser browser; | ||||||
|  |         private readonly PluginManager plugins; | ||||||
|  |         private readonly Timer currentTimer, saveTimer; | ||||||
|  | 
 | ||||||
|  |         public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){ | ||||||
|  |             this.browser = browser; | ||||||
|  |             this.plugins = plugins; | ||||||
|  | 
 | ||||||
|  |             this.File = AnalyticsFile.Load(file); | ||||||
|  |             this.File.PropertyChanged += File_PropertyChanged; | ||||||
|  | 
 | ||||||
|  |             this.currentTimer = new Timer{ SynchronizingObject = browser }; | ||||||
|  |             this.currentTimer.Elapsed += currentTimer_Elapsed; | ||||||
|  | 
 | ||||||
|  |             this.saveTimer = new Timer{ SynchronizingObject = browser, Interval = 60000 }; | ||||||
|  |             this.saveTimer.Elapsed += saveTimer_Elapsed; | ||||||
|  | 
 | ||||||
|  |             if (this.File.LastCollectionVersion != Program.VersionTag){ | ||||||
|  |                 ScheduleReportIn(TimeSpan.FromHours(8), string.Empty); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 RestartTimer(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             #if (DEBUG && ANALYTICS_INSTANT) | ||||||
|  |             SendReport(); | ||||||
|  |             #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Dispose(){ | ||||||
|  |             File.PropertyChanged -= File_PropertyChanged; | ||||||
|  | 
 | ||||||
|  |             if (saveTimer.Enabled){ | ||||||
|  |                 File.Save(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             currentTimer.Dispose(); | ||||||
|  |             saveTimer.Dispose(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void File_PropertyChanged(object sender, EventArgs e){ | ||||||
|  |             saveTimer.Enabled = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void saveTimer_Elapsed(object sender, ElapsedEventArgs e){ | ||||||
|  |             saveTimer.Stop(); | ||||||
|  |             File.Save(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void ScheduleReportIn(TimeSpan delay, string message = null){ | ||||||
|  |             SetLastDataCollectionTime(DateTime.Now.Subtract(CollectionInterval).Add(delay), message); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SetLastDataCollectionTime(DateTime dt, string message = null){ | ||||||
|  |             File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind); | ||||||
|  |             File.LastCollectionVersion = Program.VersionTag; | ||||||
|  |             File.LastCollectionMessage = message ?? dt.ToString("g", Program.Culture); | ||||||
|  | 
 | ||||||
|  |             File.Save(); | ||||||
|  |             RestartTimer(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void RestartTimer(){ | ||||||
|  |             TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection); | ||||||
|  |             int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes)); | ||||||
|  |              | ||||||
|  |             currentTimer.Interval = Math.Max(minutesTillNext, 2)*60000; | ||||||
|  |             currentTimer.Start(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void currentTimer_Elapsed(object sender, ElapsedEventArgs e){ | ||||||
|  |             currentTimer.Stop(); | ||||||
|  | 
 | ||||||
|  |             TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection); | ||||||
|  |              | ||||||
|  |             if (Math.Floor(diff.TotalMinutes) >= CollectionInterval.TotalMinutes){ | ||||||
|  |                 SendReport(); | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 RestartTimer(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private void SendReport(){ | ||||||
|  |             AnalyticsReportGenerator.ExternalInfo info = AnalyticsReportGenerator.ExternalInfo.From(browser); | ||||||
|  | 
 | ||||||
|  |             Task.Factory.StartNew(() => { | ||||||
|  |                 AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins); | ||||||
|  | 
 | ||||||
|  |                 #if (DEBUG && !ANALYTICS_INSTANT) | ||||||
|  |                 System.Diagnostics.Debugger.Break(); | ||||||
|  |                 #endif | ||||||
|  | 
 | ||||||
|  |                 BrowserUtils.CreateWebClient().UploadValues(CollectionUrl, "POST", report.ToNameValueCollection()); | ||||||
|  |             }).ContinueWith(task => browser.InvokeAsyncSafe(() => { | ||||||
|  |                 if (task.Status == TaskStatus.RanToCompletion){ | ||||||
|  |                     SetLastDataCollectionTime(DateTime.Now); | ||||||
|  |                 } | ||||||
|  |                 else if (task.Exception != null){ | ||||||
|  |                     string message = null; | ||||||
|  | 
 | ||||||
|  |                     if (task.Exception.InnerException is WebException e){ | ||||||
|  |                         switch(e.Status){ | ||||||
|  |                             case WebExceptionStatus.ConnectFailure: | ||||||
|  |                                 message = "Connection Error"; | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case WebExceptionStatus.NameResolutionFailure: | ||||||
|  |                                 message = "DNS Error"; | ||||||
|  |                                 break; | ||||||
|  | 
 | ||||||
|  |                             case WebExceptionStatus.ProtocolError: | ||||||
|  |                                 HttpWebResponse response = e.Response as HttpWebResponse; | ||||||
|  |                                 message = "HTTP Error "+(response != null ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)"); | ||||||
|  |                                 break; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         #if DEBUG | ||||||
|  |                         System.IO.Stream responseStream = e.Response.GetResponseStream(); | ||||||
|  | 
 | ||||||
|  |                         if (responseStream != null){ | ||||||
|  |                             System.Diagnostics.Debug.WriteLine(new System.IO.StreamReader(responseStream).ReadToEnd()); | ||||||
|  |                         } | ||||||
|  |                         #endif | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: "+(task.Exception.InnerException?.Message ?? task.Exception.Message)); | ||||||
|  |                 } | ||||||
|  |             })); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								Core/Other/Analytics/AnalyticsReport.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								Core/Other/Analytics/AnalyticsReport.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | 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(625); | ||||||
|  | 
 | ||||||
|  |             foreach(DictionaryEntry entry in data){ | ||||||
|  |                 if (entry.Value == null){ | ||||||
|  |                     build.AppendLine(); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     build.AppendLine(entry.Key+": "+entry.Value); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return build.ToString(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										325
									
								
								Core/Other/Analytics/AnalyticsReportGenerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								Core/Other/Analytics/AnalyticsReportGenerator.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,325 @@ | |||||||
|  | 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.Notification; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Plugins.Enums; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Analytics{ | ||||||
|  |     static class AnalyticsReportGenerator{ | ||||||
|  |         public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins){ | ||||||
|  |             Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData; | ||||||
|  | 
 | ||||||
|  |             return new AnalyticsReport{ | ||||||
|  |                 { "App Version"   , Program.VersionTag }, | ||||||
|  |                 { "App Type"      , Program.IsPortable ? "portable" : "installed" }, | ||||||
|  |                 { "App Dev Tools" , Bool(BrowserUtils.HasDevTools) }, | ||||||
|  |                 0, | ||||||
|  |                 { "System Name"        , SystemName }, | ||||||
|  |                 { "System Edition"     , SystemEdition }, | ||||||
|  |                 { "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" }, | ||||||
|  |                 { "System Build"       , SystemBuild }, | ||||||
|  |                 { "System Locale"      , Program.Culture.Name.ToLower() }, | ||||||
|  |                 0, | ||||||
|  |                 { "RAM" , Exact(RamSize) }, | ||||||
|  |                 { "GPU" , GpuVendor }, | ||||||
|  |                 0, | ||||||
|  |                 { "Screen Count"      , Exact(Screen.AllScreens.Length) }, | ||||||
|  |                 { "Screen Resolution" , info.Resolution ?? "(unknown)" }, | ||||||
|  |                 { "Screen DPI"        , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" }, | ||||||
|  |                 0, | ||||||
|  |                 { "Hardware Acceleration"     , Bool(SysConfig.HardwareAcceleration) }, | ||||||
|  |                 { "Clear Cache Automatically" , Bool(SysConfig.ClearCacheAutomatically) }, | ||||||
|  |                 { "Clear Cache Threshold"     , Exact(SysConfig.ClearCacheThreshold) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Expand Links"                  , Bool(UserConfig.ExpandLinksOnHover) }, | ||||||
|  |                 { "Search In First Column"        , Bool(UserConfig.OpenSearchInFirstColumn) }, | ||||||
|  |                 { "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) }, | ||||||
|  |                 { "Best Image Quality"            , Bool(UserConfig.BestImageQuality) }, | ||||||
|  |                 { "Animated Images"               , Bool(UserConfig.EnableAnimatedImages) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Smooth Scrolling" , Bool(UserConfig.EnableSmoothScrolling) }, | ||||||
|  |                 { "Custom Browser"   , CustomBrowser }, | ||||||
|  |                 { "Zoom"             , Exact(UserConfig.ZoomLevel) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Spell Check"                 , Bool(UserConfig.EnableSpellCheck) }, | ||||||
|  |                 { "Spell Check Language"        , UserConfig.SpellCheckLanguage.ToLower() }, | ||||||
|  |                 { "Translation Target Language" , UserConfig.TranslationTarget }, | ||||||
|  |                 0, | ||||||
|  |                 { "Updates"          , Bool(UserConfig.EnableUpdateCheck) }, | ||||||
|  |                 { "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Tray"           , TrayMode }, | ||||||
|  |                 { "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Notification Position"            , NotificationPosition }, | ||||||
|  |                 { "Notification Size"                , NotificationSize }, | ||||||
|  |                 { "Notification Timer"               , NotificationTimer }, | ||||||
|  |                 { "Notification Timer Speed"         , RoundUp(UserConfig.NotificationDurationValue, 5) }, | ||||||
|  |                 { "Notification Scroll Speed"        , Exact(UserConfig.NotificationScrollSpeed) }, | ||||||
|  |                 { "Notification Column Title"        , Bool(UserConfig.DisplayNotificationColumn) }, | ||||||
|  |                 { "Notification Media Previews"      , Bool(UserConfig.NotificationMediaPreviews) }, | ||||||
|  |                 { "Notification Link Skip"           , Bool(UserConfig.NotificationSkipOnLinkClick) }, | ||||||
|  |                 { "Notification Non-Intrusive"       , Bool(UserConfig.NotificationNonIntrusiveMode) }, | ||||||
|  |                 { "Notification Idle Pause"          , Exact(UserConfig.NotificationIdlePauseSeconds) }, | ||||||
|  |                 { "Custom Sound Notification"        , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) }, | ||||||
|  |                 { "Custom Sound Notification Volume" , RoundUp(UserConfig.NotificationSoundVolume, 5) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Program Arguments"       , List(ProgramArguments) }, | ||||||
|  |                 { "Custom CEF Arguments"    , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) }, | ||||||
|  |                 { "Custom Browser CSS"      , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) }, | ||||||
|  |                 { "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Plugins All"     , List(plugins.Plugins.Select(Plugin)) }, | ||||||
|  |                 { "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(Plugin)) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Theme"               , Dict(editLayoutDesign, "_theme",                  "light/def") }, | ||||||
|  |                 { "Column Width"        , Dict(editLayoutDesign, "columnWidth",             "310px/def") }, | ||||||
|  |                 { "Font Size"           , Dict(editLayoutDesign, "fontSize",                "12px/def") }, | ||||||
|  |                 { "Large Quote Font"    , Dict(editLayoutDesign, "increaseQuoteTextSize",   "false/def") }, | ||||||
|  |                 { "Small Compose Font"  , Dict(editLayoutDesign, "smallComposeTextSize",    "false/def") }, | ||||||
|  |                 { "Avatar Radius"       , Dict(editLayoutDesign, "avatarRadius",            "2/def") }, | ||||||
|  |                 { "Hide Tweet Actions"  , Dict(editLayoutDesign, "hideTweetActions",        "true/def") }, | ||||||
|  |                 { "Move Tweet Actions"  , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") }, | ||||||
|  |                 { "Theme Color Tweaks"  , Dict(editLayoutDesign, "themeColorTweaks",        "true/def") }, | ||||||
|  |                 { "Revert Icons"        , Dict(editLayoutDesign, "revertIcons",             "true/def") }, | ||||||
|  |                 { "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations",      "true/def") }, | ||||||
|  |                 { "Reply Account Mode"  , ReplyAccountConfigFromPlugin }, | ||||||
|  |                 { "Template Count"      , Exact(TemplateCountFromPlugin) }, | ||||||
|  |                 0, | ||||||
|  |                 { "Opened Options"                   , LogRound(file.OpenOptions, 4) }, | ||||||
|  |                 { "Opened Plugins"                   , LogRound(file.OpenPlugins, 4) }, | ||||||
|  |                 { "Opened About"                     , LogRound(file.OpenAbout, 4) }, | ||||||
|  |                 { "Opened Guide"                     , LogRound(file.OpenGuide, 4) }, | ||||||
|  |                 { "Desktop Notifications"            , LogRound(file.DesktopNotifications, 5) }, | ||||||
|  |                 { "Sound Notifications"              , LogRound(file.SoundNotifications, 5) }, | ||||||
|  |                 { "Notification Mutes"               , LogRound(file.NotificationMutes, 2) }, | ||||||
|  |                 { "Browser Context Menus"            , LogRound(file.BrowserContextMenus, 2) }, | ||||||
|  |                 { "Browser Extra Mouse Buttons"      , LogRound(file.BrowserExtraMouseButtons, 2) }, | ||||||
|  |                 { "Notification Context Menus"       , LogRound(file.NotificationContextMenus, 2) }, | ||||||
|  |                 { "Notification Extra Mouse Buttons" , LogRound(file.NotificationExtraMouseButtons, 2) }, | ||||||
|  |                 { "Notification Keyboard Shortcuts"  , LogRound(file.NotificationKeyboardShortcuts, 2) }, | ||||||
|  |                 { "Browser Reloads"                  , LogRound(file.BrowserReloads, 2) }, | ||||||
|  |                 { "Copied Usernames"                 , LogRound(file.CopiedUsernames, 2) }, | ||||||
|  |                 { "Viewed Images"                    , LogRound(file.ViewedImages, 2) }, | ||||||
|  |                 { "Downloaded Images"                , LogRound(file.DownloadedImages, 2) }, | ||||||
|  |                 { "Downloaded Videos"                , LogRound(file.DownloadedVideos, 2) }, | ||||||
|  |                 { "Used ROT13"                       , LogRound(file.UsedROT13, 2) }, | ||||||
|  |                 { "Tweet Screenshots"                , LogRound(file.TweetScreenshots, 2) }, | ||||||
|  |                 { "Tweet Details"                    , LogRound(file.TweetDetails, 2) }, | ||||||
|  |                 { "Video Plays"                      , LogRound(file.VideoPlays, 4) } | ||||||
|  |             }.FinalizeReport(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static UserConfig UserConfig => Program.Config.User; | ||||||
|  |         private static SystemConfig SysConfig => Program.Config.System; | ||||||
|  | 
 | ||||||
|  |         private static string Bool(bool value) => value ? "on" : "off"; | ||||||
|  |         private static string Exact(int value) => value.ToString(); | ||||||
|  |         private static string RoundUp(int value, int multiple) => (multiple*(int)Math.Ceiling((double)value/multiple)).ToString(); | ||||||
|  |         private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString(); | ||||||
|  |         private static string Plugin(Plugin plugin) => plugin.Group.GetIdentifierPrefixShort()+plugin.Identifier.Substring(plugin.Group.GetIdentifierPrefix().Length); | ||||||
|  |         private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def; | ||||||
|  |         private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)")); | ||||||
|  | 
 | ||||||
|  |         private static string SystemName { get; } | ||||||
|  |         private static string SystemEdition { get; } | ||||||
|  |         private static string SystemBuild { get; } | ||||||
|  |         private static int RamSize { get; } | ||||||
|  |         private static string GpuVendor { get; } | ||||||
|  |         private static string[] ProgramArguments { get; } | ||||||
|  | 
 | ||||||
|  |         static AnalyticsReportGenerator(){ | ||||||
|  |             string osName, osEdition, osBuild; | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                 using(RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false)){ | ||||||
|  |                     // ReSharper disable once PossibleNullReferenceException | ||||||
|  |                     osName = key.GetValue("ProductName") as string; | ||||||
|  |                     osBuild = key.GetValue("CurrentBuild") as string; | ||||||
|  |                     osEdition = null; | ||||||
|  |                      | ||||||
|  |                     if (osName != null){ | ||||||
|  |                         Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$"); | ||||||
|  | 
 | ||||||
|  |                         if (match.Success){ | ||||||
|  |                             osName = match.Groups[1].Value; | ||||||
|  |                             osEdition = match.Groups[2].Value; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }catch{ | ||||||
|  |                 osName = osEdition = osBuild = null; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             SystemName = osName ?? "Windows (unknown)"; | ||||||
|  |             SystemEdition = osEdition ?? "(unknown)"; | ||||||
|  |             SystemBuild = osBuild ?? "(unknown)"; | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                 using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory")){ | ||||||
|  |                     foreach(ManagementBaseObject obj in searcher.Get()){ | ||||||
|  |                         RamSize += (int)((ulong)obj["Capacity"]/(1024L*1024L)); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }catch{ | ||||||
|  |                 RamSize = 0; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string gpu = null; | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                 using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController")){ | ||||||
|  |                     foreach(ManagementBaseObject obj in searcher.Get()){ | ||||||
|  |                         string vendor = obj["Caption"] as string; | ||||||
|  | 
 | ||||||
|  |                         if (!string.IsNullOrEmpty(vendor)){ | ||||||
|  |                             gpu = vendor; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }catch{ | ||||||
|  |                 // rip | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             GpuVendor = gpu ?? "(unknown)"; | ||||||
|  | 
 | ||||||
|  |             Dictionary<string, string> args = new Dictionary<string, string>(); | ||||||
|  |             Arguments.GetCurrentClean().ToDictionary(args); | ||||||
|  |             ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static string CustomBrowser{ | ||||||
|  |             get{ | ||||||
|  |                 return Path.GetFileName(UserConfig.BrowserPath) ?? string.Empty; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static string TrayMode{ | ||||||
|  |             get{ | ||||||
|  |                 switch(UserConfig.TrayBehavior){ | ||||||
|  |                     case TrayIcon.Behavior.DisplayOnly: return "icon"; | ||||||
|  |                     case TrayIcon.Behavior.MinimizeToTray: return "minimize"; | ||||||
|  |                     case TrayIcon.Behavior.CloseToTray: return "close"; | ||||||
|  |                     case TrayIcon.Behavior.Combined: return "combined"; | ||||||
|  |                     default: return "off"; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static string NotificationPosition{ | ||||||
|  |             get{ | ||||||
|  |                 switch(UserConfig.NotificationPosition){ | ||||||
|  |                     case TweetNotification.Position.TopLeft: return "top left"; | ||||||
|  |                     case TweetNotification.Position.TopRight: return "top right"; | ||||||
|  |                     case TweetNotification.Position.BottomLeft: return "bottom left"; | ||||||
|  |                     case TweetNotification.Position.BottomRight: return "bottom right"; | ||||||
|  |                     default: return "custom"; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static string NotificationSize{ | ||||||
|  |             get{ | ||||||
|  |                 switch(UserConfig.NotificationSize){ | ||||||
|  |                     case TweetNotification.Size.Auto: return "auto"; | ||||||
|  |                     default: return RoundUp(UserConfig.CustomNotificationSize.Width, 20)+"x"+RoundUp(UserConfig.CustomNotificationSize.Height, 20); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static string NotificationTimer{ | ||||||
|  |             get{ | ||||||
|  |                 if (!UserConfig.DisplayNotificationTimer){ | ||||||
|  |                     return "off"; | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     return UserConfig.NotificationTimerCountDown ? "count down" : "count up"; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static Dictionary<string, string> EditLayoutDesignPluginData{ | ||||||
|  |             get{ | ||||||
|  |                 Dictionary<string, string> dict = new Dictionary<string, string>(); | ||||||
|  |                  | ||||||
|  |                 try{ | ||||||
|  |                     string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json")); | ||||||
|  | 
 | ||||||
|  |                     foreach(Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")){ | ||||||
|  |                         dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"'); | ||||||
|  |                     } | ||||||
|  |                 }catch{ | ||||||
|  |                     // rip | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return dict; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static int TemplateCountFromPlugin{ | ||||||
|  |             get{ | ||||||
|  |                 try{ | ||||||
|  |                     string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json")); | ||||||
|  |                     return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":")); | ||||||
|  |                 }catch{ | ||||||
|  |                     return 0; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static string ReplyAccountConfigFromPlugin{ | ||||||
|  |             get{ | ||||||
|  |                 try{ | ||||||
|  |                     string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", ""); | ||||||
|  | 
 | ||||||
|  |                     Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)"); | ||||||
|  |                     Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline); | ||||||
|  | 
 | ||||||
|  |                     if (!matchType.Success){ | ||||||
|  |                         return data.Contains("defaultAccount:\"\"") ? "(legacy)" : "(unknown)"; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account"; | ||||||
|  |                     return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/"+accType : accType; | ||||||
|  |                 }catch{ | ||||||
|  |                     return "(unknown)"; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public class ExternalInfo{ | ||||||
|  |             public static ExternalInfo From(Form form){ | ||||||
|  |                 if (form == null){ | ||||||
|  |                     return new ExternalInfo(); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     Screen screen = Screen.FromControl(form); // works on multi-monitor setups even in tray | ||||||
|  |                     int dpi; | ||||||
|  | 
 | ||||||
|  |                     using(Graphics graphics = form.CreateGraphics()){ | ||||||
|  |                         dpi = (int)graphics.DpiY; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return new ExternalInfo{ | ||||||
|  |                         Resolution = screen.Bounds.Width+"x"+screen.Bounds.Height, | ||||||
|  |                         DPI = dpi | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public string Resolution { get; private set; } | ||||||
|  |             public int? DPI { get; private set; } | ||||||
|  | 
 | ||||||
|  |             private ExternalInfo(){} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs { | namespace TweetDuck.Core.Other { | ||||||
|     sealed partial class FormAbout { |     sealed partial class FormAbout { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -126,7 +126,6 @@ namespace TweetDuck.Dialogs { | |||||||
|             this.Controls.Add(this.tablePanelLinks); |             this.Controls.Add(this.tablePanelLinks); | ||||||
|             this.Controls.Add(this.labelDescription); |             this.Controls.Add(this.labelDescription); | ||||||
|             this.Controls.Add(this.pictureLogo); |             this.Controls.Add(this.pictureLogo); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; |             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; | ||||||
|             this.HelpButton = true; |             this.HelpButton = true; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
							
								
								
									
										46
									
								
								Core/Other/FormAbout.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Core/Other/FormAbout.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | using System.ComponentModel; | ||||||
|  | using System.Drawing; | ||||||
|  | using System.IO; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other{ | ||||||
|  |     sealed partial class FormAbout : Form, FormManager.IAppDialog{ | ||||||
|  |         private const string TipsLink = "https://github.com/chylex/TweetDuck/wiki"; | ||||||
|  |         private const string IssuesLink = "https://github.com/chylex/TweetDuck/issues"; | ||||||
|  | 
 | ||||||
|  |         public FormAbout(){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             Text = "About "+Program.BrandName+" "+Program.VersionTag; | ||||||
|  | 
 | ||||||
|  |             labelDescription.Text = "TweetDuck was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license."; | ||||||
|  |              | ||||||
|  |             labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website)); | ||||||
|  |             labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink)); | ||||||
|  |             labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, IssuesLink)); | ||||||
|  | 
 | ||||||
|  |             MemoryStream logoStream = new MemoryStream(Properties.Resources.avatar); | ||||||
|  |             pictureLogo.Image = Image.FromStream(logoStream); | ||||||
|  |             Disposed += (sender, args) => logoStream.Dispose(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){ | ||||||
|  |             BrowserUtils.OpenExternalBrowser(e.Link.LinkData as string); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){ | ||||||
|  |             ShowGuide(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void FormAbout_HelpButtonClicked(object sender, CancelEventArgs e){ | ||||||
|  |             e.Cancel = true; | ||||||
|  |             ShowGuide(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void ShowGuide(){ | ||||||
|  |             FormGuide.Show(); | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,10 +1,21 @@ | |||||||
| namespace TweetDuck.Dialogs { | namespace TweetDuck.Core.Other { | ||||||
|     partial class FormGuide { |     partial class FormGuide { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         private System.ComponentModel.IContainer components = null; |         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 |         #region Windows Form Designer generated code | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
| @@ -20,7 +31,6 @@ | |||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||||
|             this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(34)))), ((int)(((byte)(34)))), ((int)(((byte)(34))))); |             this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(34)))), ((int)(((byte)(34)))), ((int)(((byte)(34))))); | ||||||
|             this.ClientSize = new System.Drawing.Size(424, 282); |             this.ClientSize = new System.Drawing.Size(424, 282); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.Icon = global::TweetDuck.Properties.Resources.icon; |             this.Icon = global::TweetDuck.Properties.Resources.icon; | ||||||
|             this.MinimumSize = new System.Drawing.Size(440, 320); |             this.MinimumSize = new System.Drawing.Size(440, 320); | ||||||
|             this.Name = "FormGuide"; |             this.Name = "FormGuide"; | ||||||
							
								
								
									
										122
									
								
								Core/Other/FormGuide.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								Core/Other/FormGuide.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | |||||||
|  | using System.Drawing; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using CefSharp; | ||||||
|  | using CefSharp.WinForms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Handling; | ||||||
|  | using TweetDuck.Core.Handling.General; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using System.Text.RegularExpressions; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | using TweetDuck.Resources; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other{ | ||||||
|  |     sealed partial class FormGuide : Form, FormManager.IAppDialog{ | ||||||
|  |         private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/"; | ||||||
|  |         private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?"; | ||||||
|  |          | ||||||
|  |         private static readonly ResourceLink DummyPage = new ResourceLink("http://td/dummy", ResourceHandler.FromString("")); | ||||||
|  | 
 | ||||||
|  |         public static bool CheckGuideUrl(string url, out string hash){ | ||||||
|  |             if (!url.Contains("//tweetduck.chylex.com/guide")){ | ||||||
|  |                 hash = null; | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string path = url.Substring(url.IndexOf("/guide")+1); | ||||||
|  |             Match match = Regex.Match(path, GuidePathRegex); | ||||||
|  | 
 | ||||||
|  |             if (match.Success){ | ||||||
|  |                 hash = match.Groups[1].Value; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 hash = null; | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void Show(string hash = null){ | ||||||
|  |             string url = GuideUrl+(hash ?? string.Empty); | ||||||
|  |             FormGuide guide = FormManager.TryFind<FormGuide>(); | ||||||
|  |              | ||||||
|  |             if (guide == null){ | ||||||
|  |                 FormBrowser owner = FormManager.TryFind<FormBrowser>(); | ||||||
|  | 
 | ||||||
|  |                 if (owner != null){ | ||||||
|  |                     owner.AnalyticsFile.OpenGuide.Trigger(); | ||||||
|  |                     new FormGuide(url, owner).Show(owner); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 guide.Reload(url); | ||||||
|  |                 guide.Activate(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly ChromiumWebBrowser browser; | ||||||
|  |         private string nextUrl; | ||||||
|  | 
 | ||||||
|  |         private FormGuide(string url, FormBrowser owner){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             Text = Program.BrandName+" Guide"; | ||||||
|  |             Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4); | ||||||
|  |             VisibleChanged += (sender, args) => this.MoveToCenter(owner); | ||||||
|  | 
 | ||||||
|  |             ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory(); | ||||||
|  |             resourceHandlerFactory.RegisterHandler(DummyPage); | ||||||
|  |              | ||||||
|  |             this.browser = new ChromiumWebBrowser(url){ | ||||||
|  |                 MenuHandler = new ContextMenuGuide(owner), | ||||||
|  |                 JsDialogHandler = new JavaScriptDialogHandler(), | ||||||
|  |                 KeyboardHandler = new KeyboardHandlerBase(), | ||||||
|  |                 LifeSpanHandler = new LifeSpanHandler(), | ||||||
|  |                 RequestHandler = new RequestHandlerBase(true), | ||||||
|  |                 ResourceHandlerFactory = resourceHandlerFactory | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             browser.LoadingStateChanged += browser_LoadingStateChanged; | ||||||
|  |             browser.FrameLoadEnd += browser_FrameLoadEnd; | ||||||
|  |              | ||||||
|  |             browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb(); | ||||||
|  |             browser.Dock = DockStyle.None; | ||||||
|  |             browser.Location = ControlExtensions.InvisibleLocation; | ||||||
|  |             browser.SetupZoomEvents(); | ||||||
|  | 
 | ||||||
|  |             Controls.Add(browser); | ||||||
|  | 
 | ||||||
|  |             Disposed += (sender, args) => { | ||||||
|  |                 browser.Dispose(); | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Reload(string url){ | ||||||
|  |             nextUrl = url; | ||||||
|  |             browser.LoadingStateChanged += browser_LoadingStateChanged; | ||||||
|  |             browser.Dock = DockStyle.None; | ||||||
|  |             browser.Location = ControlExtensions.InvisibleLocation; | ||||||
|  |             browser.Load(DummyPage.Url); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ | ||||||
|  |             if (!e.IsLoading){ | ||||||
|  |                 if (browser.Address == DummyPage.Url){ | ||||||
|  |                     browser.Load(nextUrl); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     this.InvokeAsyncSafe(() => { | ||||||
|  |                         browser.Location = Point.Empty; | ||||||
|  |                         browser.Dock = DockStyle.Fill; | ||||||
|  |                     }); | ||||||
|  | 
 | ||||||
|  |                     browser.LoadingStateChanged -= browser_LoadingStateChanged; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ | ||||||
|  |             ScriptLoader.ExecuteScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs { | namespace TweetDuck.Core.Other { | ||||||
|     partial class FormMessage { |     partial class FormMessage { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -60,7 +60,6 @@ | |||||||
|             this.ClientSize = new System.Drawing.Size(98, 133); |             this.ClientSize = new System.Drawing.Size(98, 133); | ||||||
|             this.Controls.Add(this.labelMessage); |             this.Controls.Add(this.labelMessage); | ||||||
|             this.Controls.Add(this.panelActions); |             this.Controls.Add(this.panelActions); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; |             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
|             this.MinimizeBox = false; |             this.MinimizeBox = false; | ||||||
							
								
								
									
										222
									
								
								Core/Other/FormMessage.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								Core/Other/FormMessage.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,222 @@ | |||||||
|  | using System; | ||||||
|  | using System.Drawing; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other{ | ||||||
|  |     [Flags] | ||||||
|  |     enum ControlType{ | ||||||
|  |         None = 0, | ||||||
|  |         Accept = 1, // triggered by pressing enter when a non-button is focused | ||||||
|  |         Cancel = 2, // triggered by closing the dialog without pressing a button | ||||||
|  |         Focused = 4 // active control after the dialog is showed | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sealed partial class FormMessage : Form{ | ||||||
|  |         public const string OK = "OK"; | ||||||
|  |         public const string Yes = "Yes"; | ||||||
|  |         public const string No = "No"; | ||||||
|  |         public const string Cancel = "Cancel"; | ||||||
|  |         public const string Retry = "Retry"; | ||||||
|  |         public const string Ignore = "Ignore"; | ||||||
|  |         public const string Exit = "Exit"; | ||||||
|  | 
 | ||||||
|  |         public static bool Information(string caption, string text, string buttonAccept, string buttonCancel = null){ | ||||||
|  |             return Show(caption, text, MessageBoxIcon.Information, buttonAccept, buttonCancel); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static bool Warning(string caption, string text, string buttonAccept, string buttonCancel = null){ | ||||||
|  |             return Show(caption, text, MessageBoxIcon.Warning, buttonAccept, buttonCancel); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static bool Error(string caption, string text, string buttonAccept, string buttonCancel = null){ | ||||||
|  |             return Show(caption, text, MessageBoxIcon.Error, buttonAccept, buttonCancel); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static bool Question(string caption, string text, string buttonAccept, string buttonCancel = null){ | ||||||
|  |             return Show(caption, text, MessageBoxIcon.Question, buttonAccept, buttonCancel); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static bool Show(string caption, string text, MessageBoxIcon icon, string button){ | ||||||
|  |             return Show(caption, text, icon, button, null); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static bool Show(string caption, string text, MessageBoxIcon icon, string buttonAccept, string buttonCancel){ | ||||||
|  |             using(FormMessage message = new FormMessage(caption, text, icon)){ | ||||||
|  |                 if (buttonCancel == null){ | ||||||
|  |                     message.AddButton(buttonAccept, DialogResult.OK, ControlType.Cancel | ControlType.Focused); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     message.AddButton(buttonCancel, DialogResult.Cancel, ControlType.Cancel); | ||||||
|  |                     message.AddButton(buttonAccept, DialogResult.OK, ControlType.Accept | ControlType.Focused); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return message.ShowDialog() == DialogResult.OK; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Instance | ||||||
|  | 
 | ||||||
|  |         public Button ClickedButton { get; private set; } | ||||||
|  | 
 | ||||||
|  |         public bool HasIcon => icon != null; | ||||||
|  |         public int ActionPanelY => panelActions.Location.Y; | ||||||
|  | 
 | ||||||
|  |         private int ClientWidth{ | ||||||
|  |             get => ClientSize.Width; | ||||||
|  |             set => ClientSize = new Size(value, ClientSize.Height); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private int ButtonDistance{ | ||||||
|  |             get => BrowserUtils.Scale(96, dpiScale); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private readonly Icon icon; | ||||||
|  |         private readonly bool isReady; | ||||||
|  |         private readonly float dpiScale; | ||||||
|  | 
 | ||||||
|  |         private int realFormWidth, minFormWidth; | ||||||
|  |         private int buttonCount; | ||||||
|  |         private int prevLabelWidth, prevLabelHeight; | ||||||
|  |         private bool wasLabelMultiline; | ||||||
|  | 
 | ||||||
|  |         public FormMessage(string caption, string text, MessageBoxIcon messageIcon){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             this.dpiScale = this.GetDPIScale(); | ||||||
|  | 
 | ||||||
|  |             this.prevLabelWidth = labelMessage.Width; | ||||||
|  |             this.prevLabelHeight = labelMessage.Height; | ||||||
|  |             this.minFormWidth = BrowserUtils.Scale(42, dpiScale); | ||||||
|  | 
 | ||||||
|  |             switch(messageIcon){ | ||||||
|  |                 case MessageBoxIcon.Information: | ||||||
|  |                     icon = SystemIcons.Information; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MessageBoxIcon.Warning: | ||||||
|  |                     icon = SystemIcons.Warning; | ||||||
|  |                     break; | ||||||
|  |                      | ||||||
|  |                 case MessageBoxIcon.Error: | ||||||
|  |                     icon = SystemIcons.Error; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case MessageBoxIcon.Question: | ||||||
|  |                     icon = SystemIcons.Question; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     icon = null; | ||||||
|  |                     labelMessage.Location = new Point(BrowserUtils.Scale(19, dpiScale), labelMessage.Location.Y); // 19 instead of 9 due to larger height | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.isReady = true; | ||||||
|  | 
 | ||||||
|  |             this.Text = caption; | ||||||
|  |             this.labelMessage.Text = text.Replace("\r", "").Replace("\n", Environment.NewLine); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void FormMessage_SizeChanged(object sender, EventArgs e){ | ||||||
|  |             RecalculateButtonLocation(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Button AddButton(string title, ControlType type){ | ||||||
|  |             return AddButton(title, DialogResult.OK, type); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Button AddButton(string title, DialogResult result = DialogResult.OK, ControlType type = ControlType.None){ | ||||||
|  |             Button button = new Button{ | ||||||
|  |                 Anchor = AnchorStyles.Bottom, | ||||||
|  |                 Font = SystemFonts.MessageBoxFont, | ||||||
|  |                 Location = new Point(0, 12), | ||||||
|  |                 Size = new Size(BrowserUtils.Scale(88, dpiScale), BrowserUtils.Scale(26, dpiScale)), | ||||||
|  |                 TabIndex = 256-buttonCount, | ||||||
|  |                 Text = title, | ||||||
|  |                 UseVisualStyleBackColor = true | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             button.Click += (sender, args) => { | ||||||
|  |                 ClickedButton = (Button)sender; | ||||||
|  |                 DialogResult = result; | ||||||
|  |                 Close(); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             panelActions.Controls.Add(button); | ||||||
|  |             ++buttonCount; | ||||||
|  | 
 | ||||||
|  |             minFormWidth += ButtonDistance; | ||||||
|  |             ClientWidth = Math.Max(realFormWidth, minFormWidth); | ||||||
|  |             RecalculateButtonLocation(); | ||||||
|  | 
 | ||||||
|  |             if (type.HasFlag(ControlType.Accept)){ | ||||||
|  |                 AcceptButton = button; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (type.HasFlag(ControlType.Cancel)){ | ||||||
|  |                 CancelButton = button; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (type.HasFlag(ControlType.Focused)){ | ||||||
|  |                 ActiveControl = button; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return button; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void AddActionControl(Control control){ | ||||||
|  |             panelActions.Controls.Add(control); | ||||||
|  |              | ||||||
|  |             control.Size = new Size(BrowserUtils.Scale(control.Width, dpiScale), BrowserUtils.Scale(control.Height, dpiScale)); | ||||||
|  | 
 | ||||||
|  |             minFormWidth += control.Width+control.Margin.Horizontal; | ||||||
|  |             ClientWidth = Math.Max(realFormWidth, minFormWidth); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private void RecalculateButtonLocation(){ | ||||||
|  |             int dist = ButtonDistance; | ||||||
|  |             int start = ClientWidth-dist; | ||||||
|  | 
 | ||||||
|  |             for(int index = 0; index < buttonCount; index++){ | ||||||
|  |                 Control control = panelActions.Controls[index]; | ||||||
|  |                 control.Location = new Point(start-index*dist, control.Location.Y); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void labelMessage_SizeChanged(object sender, EventArgs e){ | ||||||
|  |             if (!isReady){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             bool isMultiline = labelMessage.Height > labelMessage.MinimumSize.Height; | ||||||
|  |             int labelOffset = BrowserUtils.Scale(8, dpiScale); | ||||||
|  | 
 | ||||||
|  |             if (isMultiline && !wasLabelMultiline){ | ||||||
|  |                 labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-labelOffset); | ||||||
|  |                 prevLabelHeight += labelOffset; | ||||||
|  |             } | ||||||
|  |             else if (!isMultiline && wasLabelMultiline){ | ||||||
|  |                 labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+labelOffset); | ||||||
|  |                 prevLabelHeight -= labelOffset; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             realFormWidth = ClientWidth-(icon == null ? BrowserUtils.Scale(50, dpiScale) : 0)+labelMessage.Width-prevLabelWidth; | ||||||
|  |             ClientWidth = Math.Max(realFormWidth, minFormWidth); | ||||||
|  |             Height += labelMessage.Height-prevLabelHeight; | ||||||
|  | 
 | ||||||
|  |             prevLabelWidth = labelMessage.Width; | ||||||
|  |             prevLabelHeight = labelMessage.Height; | ||||||
|  |             wasLabelMultiline = isMultiline; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override void OnPaint(PaintEventArgs e){ | ||||||
|  |             if (icon != null){ | ||||||
|  |                 e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1+BrowserUtils.Scale(25, dpiScale)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             base.OnPaint(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,6 +1,4 @@ | |||||||
| using TweetDuck.Controls; | namespace TweetDuck.Core.Other { | ||||||
| 
 |  | ||||||
| namespace TweetDuck.Dialogs { |  | ||||||
|     partial class FormPlugins { |     partial class FormPlugins { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -29,7 +27,7 @@ namespace TweetDuck.Dialogs { | |||||||
|             this.btnClose = new System.Windows.Forms.Button(); |             this.btnClose = new System.Windows.Forms.Button(); | ||||||
|             this.btnReload = new System.Windows.Forms.Button(); |             this.btnReload = new System.Windows.Forms.Button(); | ||||||
|             this.btnOpenFolder = new System.Windows.Forms.Button(); |             this.btnOpenFolder = new System.Windows.Forms.Button(); | ||||||
|             this.flowLayoutPlugins = new FlowLayoutPanelNoHScroll(); |             this.flowLayoutPlugins = new TweetDuck.Plugins.Controls.PluginListFlowLayout(); | ||||||
|             this.timerLayout = new System.Windows.Forms.Timer(this.components); |             this.timerLayout = new System.Windows.Forms.Timer(this.components); | ||||||
|             this.SuspendLayout(); |             this.SuspendLayout(); | ||||||
|             //  |             //  | ||||||
| @@ -103,7 +101,6 @@ namespace TweetDuck.Dialogs { | |||||||
|             this.Controls.Add(this.btnOpenFolder); |             this.Controls.Add(this.btnOpenFolder); | ||||||
|             this.Controls.Add(this.btnReload); |             this.Controls.Add(this.btnReload); | ||||||
|             this.Controls.Add(this.btnClose); |             this.Controls.Add(this.btnClose); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.Icon = global::TweetDuck.Properties.Resources.icon; |             this.Icon = global::TweetDuck.Properties.Resources.icon; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
|             this.MinimizeBox = false; |             this.MinimizeBox = false; | ||||||
| @@ -120,7 +117,7 @@ namespace TweetDuck.Dialogs { | |||||||
|         private System.Windows.Forms.Button btnClose; |         private System.Windows.Forms.Button btnClose; | ||||||
|         private System.Windows.Forms.Button btnReload; |         private System.Windows.Forms.Button btnReload; | ||||||
|         private System.Windows.Forms.Button btnOpenFolder; |         private System.Windows.Forms.Button btnOpenFolder; | ||||||
|         private FlowLayoutPanelNoHScroll flowLayoutPlugins; |         private Plugins.Controls.PluginListFlowLayout flowLayoutPlugins; | ||||||
|         private System.Windows.Forms.Timer timerLayout; |         private System.Windows.Forms.Timer timerLayout; | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										112
									
								
								Core/Other/FormPlugins.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								Core/Other/FormPlugins.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | using System; | ||||||
|  | using System.Diagnostics; | ||||||
|  | using System.Drawing; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | using TweetDuck.Plugins.Controls; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other{ | ||||||
|  |     sealed partial class FormPlugins : Form, FormManager.IAppDialog{ | ||||||
|  |         private static UserConfig Config => Program.Config.User; | ||||||
|  | 
 | ||||||
|  |         private readonly PluginManager pluginManager; | ||||||
|  |          | ||||||
|  |         public FormPlugins(){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             Text = Program.BrandName+" Plugins"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public FormPlugins(PluginManager pluginManager) : this(){ | ||||||
|  |             this.pluginManager = pluginManager; | ||||||
|  | 
 | ||||||
|  |             if (!Config.PluginsWindowSize.IsEmpty){ | ||||||
|  |                 Size targetSize = Config.PluginsWindowSize; | ||||||
|  |                 Size = new Size(Math.Max(MinimumSize.Width, targetSize.Width), Math.Max(MinimumSize.Height, targetSize.Height)); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             Shown += (sender, args) => { | ||||||
|  |                 ReloadPluginList(); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             FormClosed += (sender, args) => { | ||||||
|  |                 Config.PluginsWindowSize = Size; | ||||||
|  |                 Config.Save(); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             ResizeEnd += (sender, args) => { | ||||||
|  |                 timerLayout.Start(); | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private int GetPluginOrderIndex(Plugin plugin){ | ||||||
|  |             return !plugin.CanRun ? 0 : pluginManager.Config.IsEnabled(plugin) ? 1 : 2; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void ReloadPluginList(){ | ||||||
|  |             flowLayoutPlugins.Controls.Clear(); | ||||||
|  |             flowLayoutPlugins.SuspendLayout(); | ||||||
|  | 
 | ||||||
|  |             foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){ | ||||||
|  |                 flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin)); | ||||||
|  | 
 | ||||||
|  |                 flowLayoutPlugins.Controls.Add(new Panel{ | ||||||
|  |                     BackColor = Color.DimGray, | ||||||
|  |                     Margin = new Padding(0), | ||||||
|  |                     Size = new Size(1, 1) | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             flowLayoutPlugins.ResumeLayout(true); | ||||||
|  |              | ||||||
|  |             timerLayout_Tick(null, EventArgs.Empty); | ||||||
|  |             timerLayout.Start(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void timerLayout_Tick(object sender, EventArgs e){ | ||||||
|  |             timerLayout.Stop(); | ||||||
|  |              | ||||||
|  |             // stupid WinForms scrollbars and panels | ||||||
|  |             Padding = new Padding(Padding.Left, Padding.Top, Padding.Right+1, Padding.Bottom+1); | ||||||
|  |             Padding = new Padding(Padding.Left, Padding.Top, Padding.Right-1, Padding.Bottom-1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void flowLayoutPlugins_Resize(object sender, EventArgs e){ | ||||||
|  |             Control lastPlugin = flowLayoutPlugins.Controls.OfType<PluginControl>().LastOrDefault(); | ||||||
|  |              | ||||||
|  |             if (lastPlugin == null){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             bool showScrollBar = lastPlugin.Location.Y+lastPlugin.Height+1 >= flowLayoutPlugins.Height; | ||||||
|  |             int horizontalOffset = showScrollBar ? SystemInformation.VerticalScrollBarWidth : 0; | ||||||
|  |              | ||||||
|  |             flowLayoutPlugins.AutoScroll = showScrollBar; | ||||||
|  |             flowLayoutPlugins.VerticalScroll.Visible = showScrollBar; | ||||||
|  | 
 | ||||||
|  |             foreach(Control control in flowLayoutPlugins.Controls){ | ||||||
|  |                 control.Width = flowLayoutPlugins.Width-control.Margin.Horizontal-horizontalOffset; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             flowLayoutPlugins.Controls[flowLayoutPlugins.Controls.Count-1].Visible = !showScrollBar; | ||||||
|  |             flowLayoutPlugins.Focus(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnOpenFolder_Click(object sender, EventArgs e){ | ||||||
|  |             using(Process.Start("explorer.exe", '"'+pluginManager.PathCustomPlugins+'"')){} | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnReload_Click(object sender, EventArgs e){ | ||||||
|  |             if (FormMessage.Warning("Reloading Plugins", "This will also reload the browser window. Do you want to proceed?", FormMessage.Yes, FormMessage.No)){ | ||||||
|  |                 pluginManager.Reload(); | ||||||
|  |                 ReloadPluginList(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnClose_Click(object sender, EventArgs e){ | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs { | namespace TweetDuck.Core.Other { | ||||||
|     sealed partial class FormSettings { |     sealed partial class FormSettings { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -90,7 +90,6 @@ | |||||||
|             this.Controls.Add(this.panelContents); |             this.Controls.Add(this.panelContents); | ||||||
|             this.Controls.Add(this.panelButtons); |             this.Controls.Add(this.panelButtons); | ||||||
|             this.Controls.Add(this.btnClose); |             this.Controls.Add(this.btnClose); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; |             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | ||||||
|             this.Icon = global::TweetDuck.Properties.Resources.icon; |             this.Icon = global::TweetDuck.Properties.Resources.icon; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
							
								
								
									
										210
									
								
								Core/Other/FormSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								Core/Other/FormSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | 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, FormManager.IAppDialog{ | ||||||
|  |         public bool ShouldReloadBrowser { get; private set; } | ||||||
|  | 
 | ||||||
|  |         private readonly FormBrowser browser; | ||||||
|  |         private readonly PluginManager plugins; | ||||||
|  | 
 | ||||||
|  |         private readonly int buttonHeight; | ||||||
|  | 
 | ||||||
|  |         private readonly Dictionary<Type, SettingsTab> tabs = new Dictionary<Type, SettingsTab>(8); | ||||||
|  |         private SettingsTab currentTab; | ||||||
|  | 
 | ||||||
|  |         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; | ||||||
|  | 
 | ||||||
|  |             PrepareLoad(); | ||||||
|  | 
 | ||||||
|  |             AddButton("General", () => new TabSettingsGeneral(this.browser.ReloadColumns, updates)); | ||||||
|  |             AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins))); | ||||||
|  |             AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification)); | ||||||
|  |             AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins)); | ||||||
|  |             AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS, this.browser.OpenDevTools)); | ||||||
|  | 
 | ||||||
|  |             SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void PrepareLoad(){ | ||||||
|  |             Program.Config.ProgramRestartRequested += Config_ProgramRestartRequested; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void PrepareUnload(){ // TODO refactor this further later | ||||||
|  |             currentTab.Control.OnClosing(); | ||||||
|  |              | ||||||
|  |             Program.Config.ProgramRestartRequested -= Config_ProgramRestartRequested; | ||||||
|  |             Program.Config.SaveAll(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Config_ProgramRestartRequested(object sender, EventArgs e){ | ||||||
|  |             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(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){ | ||||||
|  |             PrepareUnload(); | ||||||
|  | 
 | ||||||
|  |             foreach(SettingsTab tab in tabs.Values){ | ||||||
|  |                 if (tab.IsInitialized){ | ||||||
|  |                     tab.Control.Dispose(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             browser.ResumeNotification(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnManageOptions_Click(object sender, EventArgs e){ | ||||||
|  |             PrepareUnload(); | ||||||
|  | 
 | ||||||
|  |             using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){ | ||||||
|  |                 FormClosing -= FormSettings_FormClosing; | ||||||
|  |                  | ||||||
|  |                 if (dialog.ShowDialog() == DialogResult.OK){ | ||||||
|  |                     if (!dialog.IsRestarting){ | ||||||
|  |                         browser.ResumeNotification(); | ||||||
|  | 
 | ||||||
|  |                         if (dialog.ShouldReloadBrowser){ | ||||||
|  |                             BrowserProcessHandler.UpdatePrefs(); | ||||||
|  |                             ShouldReloadBrowser = true; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     Close(); | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     FormClosing += FormSettings_FormClosing; | ||||||
|  |                     PrepareLoad(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnClose_Click(object sender, EventArgs e){ | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void AddButton<T>(string title, Func<T> constructor) where T : BaseTabSettings{ | ||||||
|  |             FlatButton btn = new FlatButton{ | ||||||
|  |                 BackColor = SystemColors.Control, | ||||||
|  |                 FlatStyle = FlatStyle.Flat, | ||||||
|  |                 Font = SystemFonts.MessageBoxFont, | ||||||
|  |                 Location = new Point(0, (buttonHeight+1)*(panelButtons.Controls.Count/2)), | ||||||
|  |                 Margin = new Padding(0), | ||||||
|  |                 Size = new Size(panelButtons.Width, buttonHeight), | ||||||
|  |                 Text = title, | ||||||
|  |                 UseVisualStyleBackColor = true | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             btn.FlatAppearance.BorderSize = 0; | ||||||
|  |             btn.FlatAppearance.MouseDownBackColor = Color.FromArgb(179, 213, 232); | ||||||
|  |             btn.FlatAppearance.MouseOverBackColor = Color.FromArgb(216, 230, 237); | ||||||
|  |              | ||||||
|  |             panelButtons.Controls.Add(btn); | ||||||
|  | 
 | ||||||
|  |             panelButtons.Controls.Add(new Panel{ | ||||||
|  |                 BackColor = Color.DimGray, | ||||||
|  |                 Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count-1].Location.Y+buttonHeight), | ||||||
|  |                 Margin = new Padding(0), | ||||||
|  |                 Size = new Size(panelButtons.Width, 1) | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             tabs.Add(typeof(T), new SettingsTab(btn, constructor)); | ||||||
|  | 
 | ||||||
|  |             btn.Click += (sender, args) => SelectTab<T>(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SelectTab<T>() where T : BaseTabSettings{ | ||||||
|  |             SelectTab(tabs[typeof(T)]); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SelectTab(SettingsTab tab){ | ||||||
|  |             if (currentTab != null){ | ||||||
|  |                 currentTab.Button.BackColor = SystemColors.Control; | ||||||
|  |                 currentTab.Control.OnClosing(); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor; | ||||||
|  | 
 | ||||||
|  |             if (!tab.IsInitialized){ | ||||||
|  |                 foreach(Control control in tab.Control.InteractiveControls){ | ||||||
|  |                     if (control is ComboBox){ | ||||||
|  |                         control.MouseLeave += control_MouseLeave; | ||||||
|  |                     } | ||||||
|  |                     else if (control is TrackBar){ | ||||||
|  |                         control.MouseWheel += control_MouseWheel; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (tab.Control.Height < panelContents.Height-2){ | ||||||
|  |                     tab.Control.Height = panelContents.Height-2; // fixes off-by-pixel error on high DPI | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 tab.Control.OnReady(); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             panelContents.VerticalScroll.Enabled = false; // required to stop animation that would otherwise break everything | ||||||
|  |             panelContents.PerformLayout(); | ||||||
|  | 
 | ||||||
|  |             panelContents.SuspendLayout(); | ||||||
|  |             panelContents.VerticalScroll.Value = 0; // https://gfycat.com/GrotesqueTastyAstarte | ||||||
|  |             panelContents.Controls.Clear(); | ||||||
|  |             panelContents.Controls.Add(tab.Control); | ||||||
|  |             panelContents.ResumeLayout(true); | ||||||
|  | 
 | ||||||
|  |             panelContents.VerticalScroll.Enabled = true; | ||||||
|  |             panelContents.Focus(); | ||||||
|  | 
 | ||||||
|  |             currentTab = tab; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void control_MouseLeave(object sender, EventArgs e){ | ||||||
|  |             if (sender is ComboBox cb && cb.DroppedDown){ | ||||||
|  |                 return; // prevents comboboxes from closing when MouseLeave event triggers during opening animation | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             panelContents.Focus(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void control_MouseWheel(object sender, MouseEventArgs e){ | ||||||
|  |             ((HandledMouseEventArgs)e).Handled = true; | ||||||
|  |             panelContents.Focus(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private sealed class SettingsTab{ | ||||||
|  |             public Button Button { get; } | ||||||
|  | 
 | ||||||
|  |             public BaseTabSettings Control => control ?? (control = constructor()); | ||||||
|  |             public bool IsInitialized => control != null; | ||||||
|  | 
 | ||||||
|  |             private readonly Func<BaseTabSettings> constructor; | ||||||
|  |             private BaseTabSettings control; | ||||||
|  | 
 | ||||||
|  |             public SettingsTab(Button button, Func<BaseTabSettings> constructor){ | ||||||
|  |                 this.Button = button; | ||||||
|  |                 this.constructor = constructor; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								Core/Other/Settings/BaseTabSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								Core/Other/Settings/BaseTabSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Settings{ | ||||||
|  |     class BaseTabSettings : UserControl{ | ||||||
|  |         protected static UserConfig Config => Program.Config.User; | ||||||
|  |         protected static SystemConfig SysConfig => Program.Config.System; | ||||||
|  | 
 | ||||||
|  |         public IEnumerable<Control> InteractiveControls{ | ||||||
|  |             get{ | ||||||
|  |                 IEnumerable<Control> FindInteractiveControls(Control parent){ | ||||||
|  |                     foreach(Control control in parent.Controls){ | ||||||
|  |                         if (control is Panel subPanel){ | ||||||
|  |                             foreach(Control subControl in FindInteractiveControls(subPanel)){ | ||||||
|  |                                 yield return subControl; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         else{ | ||||||
|  |                             yield return control; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return FindInteractiveControls(this); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected BaseTabSettings(){ | ||||||
|  |             Padding = new Padding(6); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public virtual void OnReady(){} | ||||||
|  |         public virtual void OnClosing(){} | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								Core/Other/Settings/Dialogs/DialogSettingsAnalytics.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								Core/Other/Settings/Dialogs/DialogSettingsAnalytics.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | 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); | ||||||
|  |             this.textBoxReport.Location = new System.Drawing.Point(12, 45); | ||||||
|  |             this.textBoxReport.Multiline = true; | ||||||
|  |             this.textBoxReport.Name = "textBoxReport"; | ||||||
|  |             this.textBoxReport.ReadOnly = true; | ||||||
|  |             this.textBoxReport.ScrollBars = System.Windows.Forms.ScrollBars.Both; | ||||||
|  |             this.textBoxReport.Size = new System.Drawing.Size(435, 474); | ||||||
|  |             this.textBoxReport.TabIndex = 1; | ||||||
|  |             //  | ||||||
|  |             // btnClose | ||||||
|  |             //  | ||||||
|  |             this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | ||||||
|  |             this.btnClose.AutoSize = true; | ||||||
|  |             this.btnClose.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular); | ||||||
|  |             this.btnClose.Location = new System.Drawing.Point(397, 525); | ||||||
|  |             this.btnClose.Name = "btnClose"; | ||||||
|  |             this.btnClose.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); | ||||||
|  |             this.btnClose.Size = new System.Drawing.Size(50, 25); | ||||||
|  |             this.btnClose.TabIndex = 2; | ||||||
|  |             this.btnClose.Text = "Close"; | ||||||
|  |             this.btnClose.UseVisualStyleBackColor = true; | ||||||
|  |             this.btnClose.Click += new System.EventHandler(this.btnClose_Click); | ||||||
|  |             //  | ||||||
|  |             // labelInfo | ||||||
|  |             //  | ||||||
|  |             this.labelInfo.AutoSize = true; | ||||||
|  |             this.labelInfo.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular); | ||||||
|  |             this.labelInfo.Location = new System.Drawing.Point(12, 9); | ||||||
|  |             this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); | ||||||
|  |             this.labelInfo.Name = "labelInfo"; | ||||||
|  |             this.labelInfo.Size = new System.Drawing.Size(434, 30); | ||||||
|  |             this.labelInfo.TabIndex = 0; | ||||||
|  |             this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly every 14 days." + | ||||||
|  |     "\r\nSome numbers in the report were made imprecise on purpose."; | ||||||
|  |             //  | ||||||
|  |             // DialogSettingsAnalytics | ||||||
|  |             //  | ||||||
|  |             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | ||||||
|  |             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||||
|  |             this.ClientSize = new System.Drawing.Size(459, 562); | ||||||
|  |             this.Controls.Add(this.labelInfo); | ||||||
|  |             this.Controls.Add(this.btnClose); | ||||||
|  |             this.Controls.Add(this.textBoxReport); | ||||||
|  |             this.MinimumSize = new System.Drawing.Size(475, 340); | ||||||
|  |             this.Name = "DialogSettingsAnalytics"; | ||||||
|  |             this.ShowIcon = false; | ||||||
|  |             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; | ||||||
|  |             this.ResumeLayout(false); | ||||||
|  |             this.PerformLayout(); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         private System.Windows.Forms.TextBox textBoxReport; | ||||||
|  |         private System.Windows.Forms.Button btnClose; | ||||||
|  |         private System.Windows.Forms.Label labelInfo; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								Core/Other/Settings/Dialogs/DialogSettingsAnalytics.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Core/Other/Settings/Dialogs/DialogSettingsAnalytics.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | 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,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs.Settings { | namespace TweetDuck.Core.Other.Settings.Dialogs { | ||||||
|     partial class DialogSettingsCSS { |     partial class DialogSettingsCSS { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -160,7 +160,6 @@ | |||||||
|             this.Controls.Add(this.btnOpenDevTools); |             this.Controls.Add(this.btnOpenDevTools); | ||||||
|             this.Controls.Add(this.btnApply); |             this.Controls.Add(this.btnApply); | ||||||
|             this.Controls.Add(this.btnCancel); |             this.Controls.Add(this.btnCancel); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.MinimumSize = new System.Drawing.Size(320, 240); |             this.MinimumSize = new System.Drawing.Size(320, 240); | ||||||
|             this.Name = "DialogSettingsCSS"; |             this.Name = "DialogSettingsCSS"; | ||||||
|             this.ShowIcon = false; |             this.ShowIcon = false; | ||||||
							
								
								
									
										142
									
								
								Core/Other/Settings/Dialogs/DialogSettingsCSS.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								Core/Other/Settings/Dialogs/DialogSettingsCSS.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | using System; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text.RegularExpressions; | ||||||
|  | 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; | ||||||
|  |         private readonly Action openDevTools; | ||||||
|  | 
 | ||||||
|  |         public DialogSettingsCSS(string browserCSS, string notificationCSS, Action<string> reinjectBrowserCSS, Action openDevTools){ | ||||||
|  |             InitializeComponent(); | ||||||
|  |              | ||||||
|  |             Text = Program.BrandName+" Options - CSS"; | ||||||
|  | 
 | ||||||
|  |             this.reinjectBrowserCSS = reinjectBrowserCSS; | ||||||
|  |             this.openDevTools = openDevTools; | ||||||
|  |              | ||||||
|  |             textBoxBrowserCSS.EnableMultilineShortcuts(); | ||||||
|  |             textBoxBrowserCSS.Text = browserCSS ?? ""; | ||||||
|  | 
 | ||||||
|  |             textBoxNotificationCSS.EnableMultilineShortcuts(); | ||||||
|  |             textBoxNotificationCSS.Text = notificationCSS ?? ""; | ||||||
|  | 
 | ||||||
|  |             if (!BrowserUtils.HasDevTools){ | ||||||
|  |                 btnOpenDevTools.Enabled = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ActiveControl = textBoxBrowserCSS; | ||||||
|  |             textBoxBrowserCSS.Select(textBoxBrowserCSS.TextLength, 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void tabPanel_SelectedIndexChanged(object sender, EventArgs e){ | ||||||
|  |             TextBox tb = tabPanel.SelectedTab.Controls.OfType<TextBox>().FirstOrDefault(); | ||||||
|  | 
 | ||||||
|  |             if (tb != null){ | ||||||
|  |                 tb.Focus(); | ||||||
|  |                 tb.Select(tb.TextLength, 0); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void textBoxCSS_KeyDown(object sender, KeyEventArgs e){ | ||||||
|  |             TextBox tb = (TextBox)sender; | ||||||
|  |             string text = tb.Text; | ||||||
|  | 
 | ||||||
|  |             if (e.KeyCode == Keys.Back && e.Modifiers == Keys.Control){ | ||||||
|  |                 e.SuppressKeyPress = true; | ||||||
|  | 
 | ||||||
|  |                 int deleteTo = tb.SelectionStart; | ||||||
|  | 
 | ||||||
|  |                 if (deleteTo > 0){ | ||||||
|  |                     char initialChar = text[--deleteTo]; | ||||||
|  |                     bool shouldDeleteAlphanumeric = char.IsLetterOrDigit(initialChar); | ||||||
|  |                  | ||||||
|  |                     while(--deleteTo >= 0){ | ||||||
|  |                         if ((shouldDeleteAlphanumeric && !char.IsLetterOrDigit(text[deleteTo])) || | ||||||
|  |                             (!shouldDeleteAlphanumeric && text[deleteTo] != initialChar)){ | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     if (!(deleteTo < text.Length-1 && text[deleteTo] == '\r' && text[deleteTo+1] == '\n')){ | ||||||
|  |                         ++deleteTo; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     tb.Select(deleteTo, tb.SelectionLength+tb.SelectionStart-deleteTo); | ||||||
|  |                     tb.SelectedText = string.Empty; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (e.KeyCode == Keys.Back && e.Modifiers == Keys.None){ | ||||||
|  |                 int deleteTo = tb.SelectionStart; | ||||||
|  | 
 | ||||||
|  |                 if (deleteTo > 1 && text[deleteTo-1] == ' ' && text[deleteTo-2] == ' '){ | ||||||
|  |                     e.SuppressKeyPress = true; | ||||||
|  | 
 | ||||||
|  |                     tb.Select(deleteTo-2, 2); | ||||||
|  |                     tb.SelectedText = string.Empty; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (e.KeyCode == Keys.Enter && e.Modifiers == Keys.None && tb.SelectionLength == 0){ | ||||||
|  |                 int insertAt = tb.SelectionStart, cursorOffset = 0; | ||||||
|  |                 string insertText; | ||||||
|  | 
 | ||||||
|  |                 if (insertAt == 0){ | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 else if (text[insertAt-1] == '{'){ | ||||||
|  |                     insertText = Environment.NewLine+"  "; | ||||||
|  | 
 | ||||||
|  |                     int nextBracket = insertAt < text.Length ? text.IndexOfAny(new char[]{ '{', '}' }, insertAt+1) : -1; | ||||||
|  | 
 | ||||||
|  |                     if (nextBracket == -1 || text[nextBracket] == '{'){ | ||||||
|  |                         string insertExtra = Environment.NewLine+"}"; | ||||||
|  |                         insertText += insertExtra; | ||||||
|  |                         cursorOffset -= insertExtra.Length; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else{ | ||||||
|  |                     int lineStart = text.LastIndexOf('\n', tb.SelectionStart-1); | ||||||
|  | 
 | ||||||
|  |                     Match match = Regex.Match(text.Substring(lineStart == -1 ? 0 : lineStart+1), "^([ \t]+)"); | ||||||
|  |                     insertText = match.Success ? Environment.NewLine+match.Groups[1].Value : null; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (!string.IsNullOrEmpty(insertText)){ | ||||||
|  |                     e.SuppressKeyPress = true; | ||||||
|  |                     tb.Text = text.Insert(insertAt, insertText); | ||||||
|  |                     tb.SelectionStart = insertAt+cursorOffset+insertText.Length; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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 btnOpenDevTools_Click(object sender, EventArgs e){ | ||||||
|  |             openDevTools(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs.Settings { | namespace TweetDuck.Core.Other.Settings.Dialogs { | ||||||
|     partial class DialogSettingsCefArgs { |     partial class DialogSettingsCefArgs { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -106,7 +106,6 @@ | |||||||
|             this.Controls.Add(this.btnApply); |             this.Controls.Add(this.btnApply); | ||||||
|             this.Controls.Add(this.btnCancel); |             this.Controls.Add(this.btnCancel); | ||||||
|             this.Controls.Add(this.textBoxArgs); |             this.Controls.Add(this.textBoxArgs); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.MinimumSize = new System.Drawing.Size(520, 160); |             this.MinimumSize = new System.Drawing.Size(520, 160); | ||||||
|             this.Name = "DialogSettingsCefArgs"; |             this.Name = "DialogSettingsCefArgs"; | ||||||
|             this.ShowIcon = false; |             this.ShowIcon = false; | ||||||
							
								
								
									
										48
									
								
								Core/Other/Settings/Dialogs/DialogSettingsCefArgs.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Core/Other/Settings/Dialogs/DialogSettingsCefArgs.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  |         private readonly string initialArgs; | ||||||
|  | 
 | ||||||
|  |         public DialogSettingsCefArgs(string args){ | ||||||
|  |             InitializeComponent(); | ||||||
|  |              | ||||||
|  |             Text = Program.BrandName+" Options - CEF Arguments"; | ||||||
|  |              | ||||||
|  |             textBoxArgs.EnableMultilineShortcuts(); | ||||||
|  |             textBoxArgs.Text = initialArgs = args ?? ""; | ||||||
|  |             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){ | ||||||
|  |             if (CefArgs == initialArgs){ | ||||||
|  |                 DialogResult = DialogResult.Cancel; | ||||||
|  |                 Close(); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             int count = CommandLineArgs.ReadCefArguments(CefArgs).Count; | ||||||
|  |             string prompt = count == 0 && !string.IsNullOrWhiteSpace(initialArgs) ? "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,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs.Settings { | namespace TweetDuck.Core.Other.Settings.Dialogs { | ||||||
|     partial class DialogSettingsManage { |     partial class DialogSettingsManage { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -121,7 +121,7 @@ | |||||||
|             this.cbSystemConfig.Size = new System.Drawing.Size(109, 19); |             this.cbSystemConfig.Size = new System.Drawing.Size(109, 19); | ||||||
|             this.cbSystemConfig.TabIndex = 1; |             this.cbSystemConfig.TabIndex = 1; | ||||||
|             this.cbSystemConfig.Text = "System Options"; |             this.cbSystemConfig.Text = "System Options"; | ||||||
|             this.toolTip.SetToolTip(this.cbSystemConfig, "Options related to the current system and hardware,\r\nsuch as hardware acceleration, proxy, and cache settings."); |             this.toolTip.SetToolTip(this.cbSystemConfig, "Hardware acceleration and cache options."); | ||||||
|             this.cbSystemConfig.UseVisualStyleBackColor = true; |             this.cbSystemConfig.UseVisualStyleBackColor = true; | ||||||
|             this.cbSystemConfig.CheckedChanged += new System.EventHandler(this.checkBoxSelection_CheckedChanged); |             this.cbSystemConfig.CheckedChanged += new System.EventHandler(this.checkBoxSelection_CheckedChanged); | ||||||
|             //  |             //  | ||||||
| @@ -206,7 +206,6 @@ | |||||||
|             this.Controls.Add(this.btnCancel); |             this.Controls.Add(this.btnCancel); | ||||||
|             this.Controls.Add(this.panelDecision); |             this.Controls.Add(this.panelDecision); | ||||||
|             this.Controls.Add(this.panelSelection); |             this.Controls.Add(this.panelSelection); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; |             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
|             this.MinimizeBox = false; |             this.MinimizeBox = false; | ||||||
							
								
								
									
										244
									
								
								Core/Other/Settings/Dialogs/DialogSettingsManage.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								Core/Other/Settings/Dialogs/DialogSettingsManage.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Core.Management; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | using TweetDuck.Plugins; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Settings.Dialogs{ | ||||||
|  |     sealed partial class DialogSettingsManage : Form{ | ||||||
|  |         private enum State{ | ||||||
|  |             Deciding, Reset, Import, Export | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private ProfileManager.Items SelectedItems{ | ||||||
|  |             get => _selectedItems; | ||||||
|  | 
 | ||||||
|  |             set{ | ||||||
|  |                 // this will call events and SetFlag, which also updates the UI | ||||||
|  |                 foreach(KeyValuePair<CheckBox, ProfileManager.Items> kvp in checkBoxMap){ | ||||||
|  |                     kvp.Key.Checked = value.HasFlag(kvp.Value); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private bool SelectedItemsForceRestart{ | ||||||
|  |             get => _selectedItems.HasFlag(ProfileManager.Items.Session); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public bool IsRestarting { get; private set; } | ||||||
|  |         public bool ShouldReloadBrowser { get; private set; } | ||||||
|  |          | ||||||
|  |         private readonly PluginManager plugins; | ||||||
|  |         private readonly Dictionary<CheckBox, ProfileManager.Items> checkBoxMap = new Dictionary<CheckBox, ProfileManager.Items>(4); | ||||||
|  |         private readonly bool openImportImmediately; | ||||||
|  | 
 | ||||||
|  |         private State currentState; | ||||||
|  |         private ProfileManager importManager; | ||||||
|  |         private bool requestedRestartFromConfig; | ||||||
|  | 
 | ||||||
|  |         private ProfileManager.Items _selectedItems = ProfileManager.Items.None; | ||||||
|  | 
 | ||||||
|  |         public DialogSettingsManage(PluginManager plugins, bool openImportImmediately = false){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             this.plugins = plugins; | ||||||
|  |             this.currentState = State.Deciding; | ||||||
|  | 
 | ||||||
|  |             this.checkBoxMap[cbProgramConfig] = ProfileManager.Items.UserConfig; | ||||||
|  |             this.checkBoxMap[cbSystemConfig] = ProfileManager.Items.SystemConfig; | ||||||
|  |             this.checkBoxMap[cbSession] = ProfileManager.Items.Session; | ||||||
|  |             this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData; | ||||||
|  | 
 | ||||||
|  |             this.openImportImmediately = openImportImmediately; | ||||||
|  | 
 | ||||||
|  |             if (openImportImmediately){ | ||||||
|  |                 radioImport.Checked = true; | ||||||
|  |                 btnContinue_Click(null, EventArgs.Empty); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void radioDecision_CheckedChanged(object sender, EventArgs e){ | ||||||
|  |             btnContinue.Enabled = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void checkBoxSelection_CheckedChanged(object sender, EventArgs e){ | ||||||
|  |             CheckBox cb = (CheckBox)sender; | ||||||
|  |             SetFlag(checkBoxMap[cb], cb.Checked); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnContinue_Click(object sender, EventArgs e){ | ||||||
|  |             string file; | ||||||
|  | 
 | ||||||
|  |             switch(currentState){ | ||||||
|  |                 case State.Deciding: | ||||||
|  |                     // Reset | ||||||
|  |                     if (radioReset.Checked){ | ||||||
|  |                         currentState = State.Reset; | ||||||
|  | 
 | ||||||
|  |                         Text = "Restore Defaults"; | ||||||
|  |                         SelectedItems = ProfileManager.Items.UserConfig; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // Import | ||||||
|  |                     else if (radioImport.Checked){ | ||||||
|  |                         using(OpenFileDialog dialog = new OpenFileDialog{ | ||||||
|  |                             AutoUpgradeEnabled = true, | ||||||
|  |                             DereferenceLinks = true, | ||||||
|  |                             Title = "Import TweetDuck Profile", | ||||||
|  |                             Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings" | ||||||
|  |                         }){ | ||||||
|  |                             if (dialog.ShowDialog() != DialogResult.OK){ | ||||||
|  |                                 if (openImportImmediately){ | ||||||
|  |                                     Close(); | ||||||
|  |                                 } | ||||||
|  | 
 | ||||||
|  |                                 return; | ||||||
|  |                             } | ||||||
|  | 
 | ||||||
|  |                             file = dialog.FileName; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         importManager = new ProfileManager(file, plugins); | ||||||
|  |                         currentState = State.Import; | ||||||
|  | 
 | ||||||
|  |                         Text = "Import Profile"; | ||||||
|  |                         SelectedItems = importManager.FindImportItems(); | ||||||
|  | 
 | ||||||
|  |                         foreach(CheckBox cb in checkBoxMap.Keys){ | ||||||
|  |                             cb.Enabled = cb.Checked; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // Export | ||||||
|  |                     else if (radioExport.Checked){ | ||||||
|  |                         currentState = State.Export; | ||||||
|  | 
 | ||||||
|  |                         Text = "Export Profile"; | ||||||
|  |                         btnContinue.Text = "Export Profile"; | ||||||
|  |                         SelectedItems = ProfileManager.Items.UserConfig | ProfileManager.Items.PluginData; | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     // Continue... | ||||||
|  |                     panelDecision.Visible = false; | ||||||
|  |                     panelSelection.Visible = true; | ||||||
|  |                     Height += panelSelection.Height-panelDecision.Height; | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case State.Reset: | ||||||
|  |                     if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){ | ||||||
|  |                         Program.Config.ProgramRestartRequested += Config_ProgramRestartRequested; | ||||||
|  | 
 | ||||||
|  |                         if (SelectedItems.HasFlag(ProfileManager.Items.UserConfig)){ | ||||||
|  |                             Program.Config.User.Reset(); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){ | ||||||
|  |                             Program.Config.System.Reset(); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         Program.Config.ProgramRestartRequested -= Config_ProgramRestartRequested; | ||||||
|  | 
 | ||||||
|  |                         if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)){ | ||||||
|  |                             Program.Config.Plugins.Reset(); | ||||||
|  | 
 | ||||||
|  |                             try{ | ||||||
|  |                                 Directory.Delete(Program.PluginDataPath, true); | ||||||
|  |                             }catch(Exception ex){ | ||||||
|  |                                 Program.Reporter.HandleException("Profile Reset", "Could not delete plugin data.", true, ex); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         if (SelectedItemsForceRestart){ | ||||||
|  |                             RestartProgram(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[]{ Arguments.ArgDeleteCookies } : StringUtils.EmptyArray); | ||||||
|  |                         } | ||||||
|  |                         else if (requestedRestartFromConfig){ | ||||||
|  |                             if (FormMessage.Information("Profile Reset", "The application must restart for some of the restored options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)){ | ||||||
|  |                                 RestartProgram(); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         ShouldReloadBrowser = true; | ||||||
|  | 
 | ||||||
|  |                         DialogResult = DialogResult.OK; | ||||||
|  |                         Close(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case State.Import: | ||||||
|  |                     if (importManager.Import(SelectedItems)){ | ||||||
|  |                         Program.Config.ProgramRestartRequested += Config_ProgramRestartRequested; | ||||||
|  |                         Program.Config.ReloadAll(); | ||||||
|  |                         Program.Config.ProgramRestartRequested -= Config_ProgramRestartRequested; | ||||||
|  |                          | ||||||
|  |                         if (SelectedItemsForceRestart){ | ||||||
|  |                             RestartProgram(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[]{ Arguments.ArgImportCookies } : StringUtils.EmptyArray); | ||||||
|  |                         } | ||||||
|  |                         else if (requestedRestartFromConfig){ | ||||||
|  |                             if (FormMessage.Information("Profile Import", "The application must restart for some of the imported options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)){ | ||||||
|  |                                 RestartProgram(); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     ShouldReloadBrowser = true; | ||||||
|  | 
 | ||||||
|  |                     DialogResult = DialogResult.OK; | ||||||
|  |                     Close(); | ||||||
|  |                     break; | ||||||
|  | 
 | ||||||
|  |                 case State.Export: | ||||||
|  |                     using(SaveFileDialog dialog = new SaveFileDialog{ | ||||||
|  |                         AddExtension = true, | ||||||
|  |                         AutoUpgradeEnabled = true, | ||||||
|  |                         OverwritePrompt = true, | ||||||
|  |                         DefaultExt = "tdsettings", | ||||||
|  |                         FileName = "TweetDuck.tdsettings", | ||||||
|  |                         Title = "Export TweetDuck Profile", | ||||||
|  |                         Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings" | ||||||
|  |                     }){ | ||||||
|  |                         if (dialog.ShowDialog() != DialogResult.OK){ | ||||||
|  |                             return; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         file = dialog.FileName; | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     new ProfileManager(file, plugins).Export(SelectedItems); | ||||||
|  | 
 | ||||||
|  |                     DialogResult = DialogResult.OK; | ||||||
|  |                     Close(); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnCancel_Click(object sender, EventArgs e){ | ||||||
|  |             DialogResult = DialogResult.Cancel; | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Config_ProgramRestartRequested(object sender, EventArgs e){ | ||||||
|  |             requestedRestartFromConfig = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SetFlag(ProfileManager.Items flag, bool enable){ | ||||||
|  |             _selectedItems = enable ? _selectedItems | flag : _selectedItems & ~flag; | ||||||
|  |             btnContinue.Enabled = _selectedItems != ProfileManager.Items.None; | ||||||
|  |              | ||||||
|  |             if (currentState == State.Import){ | ||||||
|  |                 btnContinue.Text = SelectedItemsForceRestart ? "Import && Restart" : "Import Profile"; | ||||||
|  |             } | ||||||
|  |             else if (currentState == State.Reset){ | ||||||
|  |                 btnContinue.Text = SelectedItemsForceRestart ? "Restore && Restart" : "Restore Defaults"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void RestartProgram(params string[] extraArgs){ | ||||||
|  |             IsRestarting = true; | ||||||
|  |             Program.Restart(extraArgs); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs.Settings { | namespace TweetDuck.Core.Other.Settings.Dialogs { | ||||||
|     partial class DialogSettingsRestart { |     partial class DialogSettingsRestart { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -148,7 +148,6 @@ | |||||||
|             this.Controls.Add(this.flowPanel); |             this.Controls.Add(this.flowPanel); | ||||||
|             this.Controls.Add(this.btnRestart); |             this.Controls.Add(this.btnRestart); | ||||||
|             this.Controls.Add(this.btnCancel); |             this.Controls.Add(this.btnCancel); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; |             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
|             this.MinimizeBox = false; |             this.MinimizeBox = false; | ||||||
							
								
								
									
										61
									
								
								Core/Other/Settings/Dialogs/DialogSettingsRestart.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								Core/Other/Settings/Dialogs/DialogSettingsRestart.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | using System; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Data; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Settings.Dialogs{ | ||||||
|  |     sealed partial class DialogSettingsRestart : Form{ | ||||||
|  |         public CommandLineArgs Args { get; private set; } | ||||||
|  | 
 | ||||||
|  |         public DialogSettingsRestart(CommandLineArgs currentArgs){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging); | ||||||
|  |             cbLogging.CheckedChanged += control_Change; | ||||||
|  |              | ||||||
|  |             if (Program.IsPortable){ | ||||||
|  |                 tbDataFolder.Text = "Not available in portable version"; | ||||||
|  |                 tbDataFolder.Enabled = false; | ||||||
|  |             } | ||||||
|  |             else{ | ||||||
|  |                 tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty); | ||||||
|  |                 tbDataFolder.TextChanged += control_Change; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             control_Change(this, EventArgs.Empty); | ||||||
|  | 
 | ||||||
|  |             Text = Program.BrandName+" Arguments"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void control_Change(object sender, EventArgs e){ | ||||||
|  |             Args = new CommandLineArgs(); | ||||||
|  |              | ||||||
|  |             if (cbLogging.Checked){ | ||||||
|  |                 Args.AddFlag(Arguments.ArgLogging); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){ | ||||||
|  |                 Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             tbShortcutTarget.Text = $@"""{Application.ExecutablePath}""{(Args.Count > 0 ? " " : "")}{Args}"; | ||||||
|  |             tbShortcutTarget.Select(tbShortcutTarget.Text.Length, 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void tbShortcutTarget_Click(object sender, EventArgs e){ | ||||||
|  |             if (tbShortcutTarget.SelectionLength == 0){ | ||||||
|  |                 tbShortcutTarget.SelectAll(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnRestart_Click(object sender, EventArgs e){ | ||||||
|  |             DialogResult = DialogResult.OK; | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnCancel_Click(object sender, EventArgs e){ | ||||||
|  |             DialogResult = DialogResult.Cancel; | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs.Settings { | namespace TweetDuck.Core.Other.Settings.Dialogs { | ||||||
|     partial class DialogSettingsSearchEngine { |     partial class DialogSettingsSearchEngine { | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -88,7 +88,6 @@ | |||||||
|             this.Controls.Add(this.btnApply); |             this.Controls.Add(this.btnApply); | ||||||
|             this.Controls.Add(this.btnCancel); |             this.Controls.Add(this.btnCancel); | ||||||
|             this.Controls.Add(this.textBoxUrl); |             this.Controls.Add(this.textBoxUrl); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; |             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | ||||||
|             this.MaximizeBox = false; |             this.MaximizeBox = false; | ||||||
|             this.MinimizeBox = false; |             this.MinimizeBox = false; | ||||||
							
								
								
									
										27
									
								
								Core/Other/Settings/Dialogs/DialogSettingsSearchEngine.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Core/Other/Settings/Dialogs/DialogSettingsSearchEngine.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | using System; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Settings.Dialogs{ | ||||||
|  |     sealed partial class DialogSettingsSearchEngine : Form{ | ||||||
|  |         public string Url => textBoxUrl.Text; | ||||||
|  | 
 | ||||||
|  |         public DialogSettingsSearchEngine(){ | ||||||
|  |             InitializeComponent(); | ||||||
|  |              | ||||||
|  |             Text = Program.BrandName+" Options - Custom Search Engine"; | ||||||
|  |              | ||||||
|  |             textBoxUrl.Text = Program.Config.User.SearchEngineUrl ?? ""; | ||||||
|  |             textBoxUrl.Select(textBoxUrl.Text.Length, 0); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private void btnApply_Click(object sender, EventArgs e){ | ||||||
|  |             DialogResult = DialogResult.OK; | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnCancel_Click(object sender, EventArgs e){ | ||||||
|  |             DialogResult = DialogResult.Cancel; | ||||||
|  |             Close(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs.Settings { | namespace TweetDuck.Core.Other.Settings { | ||||||
|     partial class TabSettingsAdvanced { |     partial class TabSettingsAdvanced { | ||||||
|         /// <summary>  |         /// <summary>  | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -32,7 +32,7 @@ | |||||||
|             this.btnRestart = new System.Windows.Forms.Button(); |             this.btnRestart = new System.Windows.Forms.Button(); | ||||||
|             this.btnOpenAppFolder = new System.Windows.Forms.Button(); |             this.btnOpenAppFolder = new System.Windows.Forms.Button(); | ||||||
|             this.btnOpenDataFolder = new System.Windows.Forms.Button(); |             this.btnOpenDataFolder = new System.Windows.Forms.Button(); | ||||||
|             this.numClearCacheThreshold = new TweetDuck.Controls.NumericUpDownEx(); |             this.numClearCacheThreshold = new TweetDuck.Core.Controls.NumericUpDownEx(); | ||||||
|             this.checkClearCacheAuto = new System.Windows.Forms.CheckBox(); |             this.checkClearCacheAuto = new System.Windows.Forms.CheckBox(); | ||||||
|             this.labelApp = new System.Windows.Forms.Label(); |             this.labelApp = new System.Windows.Forms.Label(); | ||||||
|             this.panelAppButtons = new System.Windows.Forms.Panel(); |             this.panelAppButtons = new System.Windows.Forms.Panel(); | ||||||
| @@ -40,34 +40,22 @@ | |||||||
|             this.panelClearCacheAuto = new System.Windows.Forms.Panel(); |             this.panelClearCacheAuto = new System.Windows.Forms.Panel(); | ||||||
|             this.panelConfiguration = new System.Windows.Forms.Panel(); |             this.panelConfiguration = new System.Windows.Forms.Panel(); | ||||||
|             this.labelConfiguration = new System.Windows.Forms.Label(); |             this.labelConfiguration = new System.Windows.Forms.Label(); | ||||||
|             this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel(); |             this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); | ||||||
|             this.labelBrowserSettings = new System.Windows.Forms.Label(); |  | ||||||
|             this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.checkAutomaticallyDetectColorProfile = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.checkTouchAdjustment = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelProxy = new System.Windows.Forms.Label(); |  | ||||||
|             this.checkUseSystemProxyForAllConnections = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelDevTools = new System.Windows.Forms.Label(); |  | ||||||
|             this.checkDevToolsInContextMenu = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.checkDevToolsWindowOnTop = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.flowPanelRight = new System.Windows.Forms.FlowLayoutPanel(); |  | ||||||
|             this.panelSeparator = new System.Windows.Forms.Panel(); |  | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit(); |             ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit(); | ||||||
|             this.panelAppButtons.SuspendLayout(); |             this.panelAppButtons.SuspendLayout(); | ||||||
|             this.panelClearCacheAuto.SuspendLayout(); |             this.panelClearCacheAuto.SuspendLayout(); | ||||||
|             this.panelConfiguration.SuspendLayout(); |             this.panelConfiguration.SuspendLayout(); | ||||||
|             this.flowPanelLeft.SuspendLayout(); |             this.flowPanel.SuspendLayout(); | ||||||
|             this.flowPanelRight.SuspendLayout(); |  | ||||||
|             this.SuspendLayout(); |             this.SuspendLayout(); | ||||||
|             //  |             //  | ||||||
|             // btnClearCache |             // btnClearCache | ||||||
|             //  |             //  | ||||||
|             this.btnClearCache.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.btnClearCache.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.btnClearCache.Location = new System.Drawing.Point(5, 145); |             this.btnClearCache.Location = new System.Drawing.Point(5, 135); | ||||||
|             this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |             this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); | ||||||
|             this.btnClearCache.Name = "btnClearCache"; |             this.btnClearCache.Name = "btnClearCache"; | ||||||
|             this.btnClearCache.Size = new System.Drawing.Size(143, 25); |             this.btnClearCache.Size = new System.Drawing.Size(143, 25); | ||||||
|             this.btnClearCache.TabIndex = 5; |             this.btnClearCache.TabIndex = 3; | ||||||
|             this.btnClearCache.Text = "Clear Cache (...)"; |             this.btnClearCache.Text = "Clear Cache (...)"; | ||||||
|             this.btnClearCache.UseVisualStyleBackColor = true; |             this.btnClearCache.UseVisualStyleBackColor = true; | ||||||
|             //  |             //  | ||||||
| @@ -154,7 +142,7 @@ | |||||||
|             0, |             0, | ||||||
|             0}); |             0}); | ||||||
|             this.numClearCacheThreshold.Name = "numClearCacheThreshold"; |             this.numClearCacheThreshold.Name = "numClearCacheThreshold"; | ||||||
|             this.numClearCacheThreshold.Size = new System.Drawing.Size(80, 23); |             this.numClearCacheThreshold.Size = new System.Drawing.Size(89, 23); | ||||||
|             this.numClearCacheThreshold.TabIndex = 1; |             this.numClearCacheThreshold.TabIndex = 1; | ||||||
|             this.numClearCacheThreshold.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; |             this.numClearCacheThreshold.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; | ||||||
|             this.numClearCacheThreshold.TextSuffix = " MB"; |             this.numClearCacheThreshold.TextSuffix = " MB"; | ||||||
| @@ -203,206 +191,67 @@ | |||||||
|             //  |             //  | ||||||
|             this.labelCache.AutoSize = true; |             this.labelCache.AutoSize = true; | ||||||
|             this.labelCache.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |             this.labelCache.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelCache.Location = new System.Drawing.Point(0, 122); |             this.labelCache.Location = new System.Drawing.Point(0, 112); | ||||||
|             this.labelCache.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1); |             this.labelCache.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1); | ||||||
|             this.labelCache.Name = "labelCache"; |             this.labelCache.Name = "labelCache"; | ||||||
|             this.labelCache.Size = new System.Drawing.Size(123, 19); |             this.labelCache.Size = new System.Drawing.Size(123, 19); | ||||||
|             this.labelCache.TabIndex = 4; |             this.labelCache.TabIndex = 2; | ||||||
|             this.labelCache.Text = "BROWSER CACHE"; |             this.labelCache.Text = "BROWSER CACHE"; | ||||||
|             //  |             //  | ||||||
|             // panelClearCacheAuto |             // panelClearCacheAuto | ||||||
|             //  |             //  | ||||||
|             this.panelClearCacheAuto.Controls.Add(this.checkClearCacheAuto); |             this.panelClearCacheAuto.Controls.Add(this.checkClearCacheAuto); | ||||||
|             this.panelClearCacheAuto.Controls.Add(this.numClearCacheThreshold); |             this.panelClearCacheAuto.Controls.Add(this.numClearCacheThreshold); | ||||||
|             this.panelClearCacheAuto.Location = new System.Drawing.Point(0, 173); |             this.panelClearCacheAuto.Location = new System.Drawing.Point(0, 163); | ||||||
|             this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0); |             this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0); | ||||||
|             this.panelClearCacheAuto.Name = "panelClearCacheAuto"; |             this.panelClearCacheAuto.Name = "panelClearCacheAuto"; | ||||||
|             this.panelClearCacheAuto.Size = new System.Drawing.Size(300, 28); |             this.panelClearCacheAuto.Size = new System.Drawing.Size(300, 28); | ||||||
|             this.panelClearCacheAuto.TabIndex = 6; |             this.panelClearCacheAuto.TabIndex = 4; | ||||||
|             //  |             //  | ||||||
|             // panelConfiguration |             // panelConfiguration | ||||||
|             //  |             //  | ||||||
|             this.panelConfiguration.Controls.Add(this.btnEditCSS); |             this.panelConfiguration.Controls.Add(this.btnEditCSS); | ||||||
|             this.panelConfiguration.Controls.Add(this.btnEditCefArgs); |             this.panelConfiguration.Controls.Add(this.btnEditCefArgs); | ||||||
|             this.panelConfiguration.Location = new System.Drawing.Point(0, 132); |             this.panelConfiguration.Location = new System.Drawing.Point(0, 241); | ||||||
|             this.panelConfiguration.Margin = new System.Windows.Forms.Padding(0); |             this.panelConfiguration.Margin = new System.Windows.Forms.Padding(0); | ||||||
|             this.panelConfiguration.Name = "panelConfiguration"; |             this.panelConfiguration.Name = "panelConfiguration"; | ||||||
|             this.panelConfiguration.Size = new System.Drawing.Size(300, 31); |             this.panelConfiguration.Size = new System.Drawing.Size(300, 31); | ||||||
|             this.panelConfiguration.TabIndex = 3; |             this.panelConfiguration.TabIndex = 6; | ||||||
|             //  |             //  | ||||||
|             // labelConfiguration |             // labelConfiguration | ||||||
|             //  |             //  | ||||||
|             this.labelConfiguration.AutoSize = true; |             this.labelConfiguration.AutoSize = true; | ||||||
|             this.labelConfiguration.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |             this.labelConfiguration.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelConfiguration.Location = new System.Drawing.Point(0, 112); |             this.labelConfiguration.Location = new System.Drawing.Point(0, 221); | ||||||
|             this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1); |             this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1); | ||||||
|             this.labelConfiguration.Name = "labelConfiguration"; |             this.labelConfiguration.Name = "labelConfiguration"; | ||||||
|             this.labelConfiguration.Size = new System.Drawing.Size(123, 19); |             this.labelConfiguration.Size = new System.Drawing.Size(123, 19); | ||||||
|             this.labelConfiguration.TabIndex = 2; |             this.labelConfiguration.TabIndex = 5; | ||||||
|             this.labelConfiguration.Text = "CONFIGURATION"; |             this.labelConfiguration.Text = "CONFIGURATION"; | ||||||
|             //  |             //  | ||||||
|             // flowPanelLeft |             // flowPanel | ||||||
|             //  |             //  | ||||||
|             this.flowPanelLeft.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |             this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left))); |             | System.Windows.Forms.AnchorStyles.Left))); | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelBrowserSettings); |             this.flowPanel.Controls.Add(this.labelApp); | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkHardwareAcceleration); |             this.flowPanel.Controls.Add(this.panelAppButtons); | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkAutomaticallyDetectColorProfile); |             this.flowPanel.Controls.Add(this.labelCache); | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkTouchAdjustment); |             this.flowPanel.Controls.Add(this.btnClearCache); | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelCache); |             this.flowPanel.Controls.Add(this.panelClearCacheAuto); | ||||||
|             this.flowPanelLeft.Controls.Add(this.btnClearCache); |             this.flowPanel.Controls.Add(this.labelConfiguration); | ||||||
|             this.flowPanelLeft.Controls.Add(this.panelClearCacheAuto); |             this.flowPanel.Controls.Add(this.panelConfiguration); | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelProxy); |             this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkUseSystemProxyForAllConnections); |             this.flowPanel.Location = new System.Drawing.Point(9, 9); | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelDevTools); |             this.flowPanel.Name = "flowPanel"; | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkDevToolsInContextMenu); |             this.flowPanel.Size = new System.Drawing.Size(300, 462); | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkDevToolsWindowOnTop); |             this.flowPanel.TabIndex = 0; | ||||||
|             this.flowPanelLeft.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |             this.flowPanel.WrapContents = false; | ||||||
|             this.flowPanelLeft.Location = new System.Drawing.Point(9, 9); |  | ||||||
|             this.flowPanelLeft.Name = "flowPanelLeft"; |  | ||||||
|             this.flowPanelLeft.Size = new System.Drawing.Size(300, 462); |  | ||||||
|             this.flowPanelLeft.TabIndex = 0; |  | ||||||
|             this.flowPanelLeft.WrapContents = false; |  | ||||||
|             //  |  | ||||||
|             // labelBrowserSettings |  | ||||||
|             //  |  | ||||||
|             this.labelBrowserSettings.AutoSize = true; |  | ||||||
|             this.labelBrowserSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |  | ||||||
|             this.labelBrowserSettings.Location = new System.Drawing.Point(0, 0); |  | ||||||
|             this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1); |  | ||||||
|             this.labelBrowserSettings.Name = "labelBrowserSettings"; |  | ||||||
|             this.labelBrowserSettings.Size = new System.Drawing.Size(143, 19); |  | ||||||
|             this.labelBrowserSettings.TabIndex = 0; |  | ||||||
|             this.labelBrowserSettings.Text = "BROWSER SETTINGS"; |  | ||||||
|             //  |  | ||||||
|             // checkHardwareAcceleration |  | ||||||
|             //  |  | ||||||
|             this.checkHardwareAcceleration.AutoSize = true; |  | ||||||
|             this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 23); |  | ||||||
|             this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkHardwareAcceleration.Name = "checkHardwareAcceleration"; |  | ||||||
|             this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19); |  | ||||||
|             this.checkHardwareAcceleration.TabIndex = 1; |  | ||||||
|             this.checkHardwareAcceleration.Text = "Hardware Acceleration"; |  | ||||||
|             this.checkHardwareAcceleration.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // checkAutomaticallyDetectColorProfile |  | ||||||
|             //  |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.AutoSize = true; |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.Location = new System.Drawing.Point(6, 47); |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.Name = "checkAutomaticallyDetectColorProfile"; |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.Size = new System.Drawing.Size(206, 19); |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.TabIndex = 2; |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.Text = "Automatically Detect Color Profile"; |  | ||||||
|             this.checkAutomaticallyDetectColorProfile.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // checkTouchAdjustment |  | ||||||
|             //  |  | ||||||
|             this.checkTouchAdjustment.AutoSize = true; |  | ||||||
|             this.checkTouchAdjustment.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkTouchAdjustment.Location = new System.Drawing.Point(6, 71); |  | ||||||
|             this.checkTouchAdjustment.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkTouchAdjustment.Name = "checkTouchAdjustment"; |  | ||||||
|             this.checkTouchAdjustment.Size = new System.Drawing.Size(163, 19); |  | ||||||
|             this.checkTouchAdjustment.TabIndex = 3; |  | ||||||
|             this.checkTouchAdjustment.Text = "Touch Screen Adjustment"; |  | ||||||
|             this.checkTouchAdjustment.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelProxy |  | ||||||
|             //  |  | ||||||
|             this.labelProxy.AutoSize = true; |  | ||||||
|             this.labelProxy.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |  | ||||||
|             this.labelProxy.Location = new System.Drawing.Point(0, 231); |  | ||||||
|             this.labelProxy.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1); |  | ||||||
|             this.labelProxy.Name = "labelProxy"; |  | ||||||
|             this.labelProxy.Size = new System.Drawing.Size(54, 19); |  | ||||||
|             this.labelProxy.TabIndex = 7; |  | ||||||
|             this.labelProxy.Text = "PROXY"; |  | ||||||
|             //  |  | ||||||
|             // checkUseSystemProxyForAllConnections |  | ||||||
|             //  |  | ||||||
|             this.checkUseSystemProxyForAllConnections.AutoSize = true; |  | ||||||
|             this.checkUseSystemProxyForAllConnections.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkUseSystemProxyForAllConnections.Location = new System.Drawing.Point(6, 257); |  | ||||||
|             this.checkUseSystemProxyForAllConnections.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |  | ||||||
|             this.checkUseSystemProxyForAllConnections.Name = "checkUseSystemProxyForAllConnections"; |  | ||||||
|             this.checkUseSystemProxyForAllConnections.Size = new System.Drawing.Size(223, 19); |  | ||||||
|             this.checkUseSystemProxyForAllConnections.TabIndex = 8; |  | ||||||
|             this.checkUseSystemProxyForAllConnections.Text = "Use System Proxy for All Connections"; |  | ||||||
|             this.checkUseSystemProxyForAllConnections.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelDevTools |  | ||||||
|             //  |  | ||||||
|             this.labelDevTools.AutoSize = true; |  | ||||||
|             this.labelDevTools.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |  | ||||||
|             this.labelDevTools.Location = new System.Drawing.Point(0, 308); |  | ||||||
|             this.labelDevTools.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1); |  | ||||||
|             this.labelDevTools.Name = "labelDevTools"; |  | ||||||
|             this.labelDevTools.Size = new System.Drawing.Size(156, 19); |  | ||||||
|             this.labelDevTools.TabIndex = 9; |  | ||||||
|             this.labelDevTools.Text = "DEVELOPMENT TOOLS"; |  | ||||||
|             //  |  | ||||||
|             // checkDevToolsInContextMenu |  | ||||||
|             //  |  | ||||||
|             this.checkDevToolsInContextMenu.AutoSize = true; |  | ||||||
|             this.checkDevToolsInContextMenu.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkDevToolsInContextMenu.Location = new System.Drawing.Point(6, 334); |  | ||||||
|             this.checkDevToolsInContextMenu.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |  | ||||||
|             this.checkDevToolsInContextMenu.Name = "checkDevToolsInContextMenu"; |  | ||||||
|             this.checkDevToolsInContextMenu.Size = new System.Drawing.Size(201, 19); |  | ||||||
|             this.checkDevToolsInContextMenu.TabIndex = 10; |  | ||||||
|             this.checkDevToolsInContextMenu.Text = "Show Dev Tools in Context Menu"; |  | ||||||
|             this.checkDevToolsInContextMenu.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // checkDevToolsWindowOnTop |  | ||||||
|             //  |  | ||||||
|             this.checkDevToolsWindowOnTop.AutoSize = true; |  | ||||||
|             this.checkDevToolsWindowOnTop.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkDevToolsWindowOnTop.Location = new System.Drawing.Point(6, 358); |  | ||||||
|             this.checkDevToolsWindowOnTop.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkDevToolsWindowOnTop.Name = "checkDevToolsWindowOnTop"; |  | ||||||
|             this.checkDevToolsWindowOnTop.Size = new System.Drawing.Size(168, 19); |  | ||||||
|             this.checkDevToolsWindowOnTop.TabIndex = 11; |  | ||||||
|             this.checkDevToolsWindowOnTop.Text = "Dev Tools Window On Top"; |  | ||||||
|             this.checkDevToolsWindowOnTop.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // flowPanelRight |  | ||||||
|             //  |  | ||||||
|             this.flowPanelRight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left))); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.labelApp); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.panelAppButtons); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.labelConfiguration); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.panelConfiguration); |  | ||||||
|             this.flowPanelRight.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |  | ||||||
|             this.flowPanelRight.Location = new System.Drawing.Point(322, 9); |  | ||||||
|             this.flowPanelRight.Name = "flowPanelRight"; |  | ||||||
|             this.flowPanelRight.Size = new System.Drawing.Size(300, 462); |  | ||||||
|             this.flowPanelRight.TabIndex = 1; |  | ||||||
|             this.flowPanelRight.WrapContents = false; |  | ||||||
|             //  |  | ||||||
|             // panelSeparator |  | ||||||
|             //  |  | ||||||
|             this.panelSeparator.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left))); |  | ||||||
|             this.panelSeparator.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(200)))), ((int)(((byte)(200)))), ((int)(((byte)(200))))); |  | ||||||
|             this.panelSeparator.Location = new System.Drawing.Point(312, 0); |  | ||||||
|             this.panelSeparator.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0); |  | ||||||
|             this.panelSeparator.Name = "panelSeparator"; |  | ||||||
|             this.panelSeparator.Size = new System.Drawing.Size(1, 480); |  | ||||||
|             this.panelSeparator.TabIndex = 3; |  | ||||||
|             //  |             //  | ||||||
|             // TabSettingsAdvanced |             // TabSettingsAdvanced | ||||||
|             //  |             //  | ||||||
|             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); |             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | ||||||
|             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; |             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||||
|             this.Controls.Add(this.panelSeparator); |             this.Controls.Add(this.flowPanel); | ||||||
|             this.Controls.Add(this.flowPanelRight); |  | ||||||
|             this.Controls.Add(this.flowPanelLeft); |  | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.Name = "TabSettingsAdvanced"; |             this.Name = "TabSettingsAdvanced"; | ||||||
|             this.Size = new System.Drawing.Size(631, 480); |             this.Size = new System.Drawing.Size(631, 480); | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).EndInit(); |             ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).EndInit(); | ||||||
| @@ -410,10 +259,8 @@ | |||||||
|             this.panelClearCacheAuto.ResumeLayout(false); |             this.panelClearCacheAuto.ResumeLayout(false); | ||||||
|             this.panelClearCacheAuto.PerformLayout(); |             this.panelClearCacheAuto.PerformLayout(); | ||||||
|             this.panelConfiguration.ResumeLayout(false); |             this.panelConfiguration.ResumeLayout(false); | ||||||
|             this.flowPanelLeft.ResumeLayout(false); |             this.flowPanel.ResumeLayout(false); | ||||||
|             this.flowPanelLeft.PerformLayout(); |             this.flowPanel.PerformLayout(); | ||||||
|             this.flowPanelRight.ResumeLayout(false); |  | ||||||
|             this.flowPanelRight.PerformLayout(); |  | ||||||
|             this.ResumeLayout(false); |             this.ResumeLayout(false); | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| @@ -436,17 +283,6 @@ | |||||||
|         private System.Windows.Forms.Label labelConfiguration; |         private System.Windows.Forms.Label labelConfiguration; | ||||||
|         private Controls.NumericUpDownEx numClearCacheThreshold; |         private Controls.NumericUpDownEx numClearCacheThreshold; | ||||||
|         private System.Windows.Forms.CheckBox checkClearCacheAuto; |         private System.Windows.Forms.CheckBox checkClearCacheAuto; | ||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanelLeft; |         private System.Windows.Forms.FlowLayoutPanel flowPanel; | ||||||
|         private System.Windows.Forms.Label labelDevTools; |  | ||||||
|         private System.Windows.Forms.CheckBox checkDevToolsInContextMenu; |  | ||||||
|         private System.Windows.Forms.CheckBox checkDevToolsWindowOnTop; |  | ||||||
|         private System.Windows.Forms.Label labelProxy; |  | ||||||
|         private System.Windows.Forms.CheckBox checkUseSystemProxyForAllConnections; |  | ||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanelRight; |  | ||||||
|         private System.Windows.Forms.Label labelBrowserSettings; |  | ||||||
|         private System.Windows.Forms.CheckBox checkTouchAdjustment; |  | ||||||
|         private System.Windows.Forms.CheckBox checkAutomaticallyDetectColorProfile; |  | ||||||
|         private System.Windows.Forms.CheckBox checkHardwareAcceleration; |  | ||||||
|         private System.Windows.Forms.Panel panelSeparator; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										157
									
								
								Core/Other/Settings/TabSettingsAdvanced.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								Core/Other/Settings/TabSettingsAdvanced.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | using System; | ||||||
|  | using System.Diagnostics; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using System.Windows.Forms; | ||||||
|  | using TweetDuck.Configuration; | ||||||
|  | using TweetDuck.Core.Controls; | ||||||
|  | using TweetDuck.Core.Management; | ||||||
|  | using TweetDuck.Core.Other.Settings.Dialogs; | ||||||
|  | using TweetDuck.Core.Utils; | ||||||
|  | 
 | ||||||
|  | namespace TweetDuck.Core.Other.Settings{ | ||||||
|  |     sealed partial class TabSettingsAdvanced : BaseTabSettings{ | ||||||
|  |         private readonly Action<string> reinjectBrowserCSS; | ||||||
|  |         private readonly Action openDevTools; | ||||||
|  | 
 | ||||||
|  |         public TabSettingsAdvanced(Action<string> reinjectBrowserCSS, Action openDevTools){ | ||||||
|  |             InitializeComponent(); | ||||||
|  | 
 | ||||||
|  |             this.reinjectBrowserCSS = reinjectBrowserCSS; | ||||||
|  |             this.openDevTools = openDevTools; | ||||||
|  | 
 | ||||||
|  |             // application | ||||||
|  |              | ||||||
|  |             toolTip.SetToolTip(btnOpenAppFolder, "Opens the folder where the app is located."); | ||||||
|  |             toolTip.SetToolTip(btnOpenDataFolder, "Opens the folder where your profile data is located."); | ||||||
|  |             toolTip.SetToolTip(btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch."); | ||||||
|  |             toolTip.SetToolTip(btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments."); | ||||||
|  | 
 | ||||||
|  |             // browser cache | ||||||
|  | 
 | ||||||
|  |             toolTip.SetToolTip(btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources."); | ||||||
|  |             toolTip.SetToolTip(checkClearCacheAuto, "Automatically clears cache when its size exceeds the set threshold. Note that cache can only be cleared when closing TweetDuck."); | ||||||
|  | 
 | ||||||
|  |             checkClearCacheAuto.Checked = SysConfig.ClearCacheAutomatically; | ||||||
|  |             numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked; | ||||||
|  |             numClearCacheThreshold.SetValueSafe(SysConfig.ClearCacheThreshold); | ||||||
|  |              | ||||||
|  |             BrowserCache.GetCacheSize(task => { | ||||||
|  |                 string text = task.Status == TaskStatus.RanToCompletion ? (int)Math.Ceiling(task.Result/(1024.0*1024.0))+" MB" : "unknown"; | ||||||
|  |                 this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})"); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // configuration | ||||||
|  | 
 | ||||||
|  |             toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework."); | ||||||
|  |             toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows."); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void OnReady(){ | ||||||
|  |             btnOpenAppFolder.Click += btnOpenAppFolder_Click; | ||||||
|  |             btnOpenDataFolder.Click += btnOpenDataFolder_Click; | ||||||
|  |             btnRestart.Click += btnRestart_Click; | ||||||
|  |             btnRestartArgs.Click += btnRestartArgs_Click; | ||||||
|  | 
 | ||||||
|  |             btnClearCache.Click += btnClearCache_Click; | ||||||
|  |             checkClearCacheAuto.CheckedChanged += checkClearCacheAuto_CheckedChanged; | ||||||
|  |              | ||||||
|  |             btnEditCefArgs.Click += btnEditCefArgs_Click; | ||||||
|  |             btnEditCSS.Click += btnEditCSS_Click; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override void OnClosing(){ | ||||||
|  |             SysConfig.ClearCacheAutomatically = checkClearCacheAuto.Checked; | ||||||
|  |             SysConfig.ClearCacheThreshold = (int)numClearCacheThreshold.Value; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #region Application | ||||||
|  |          | ||||||
|  |         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); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |         #region Browser Cache | ||||||
|  | 
 | ||||||
|  |         private void btnClearCache_Click(object sender, EventArgs e){ | ||||||
|  |             btnClearCache.Enabled = false; | ||||||
|  |             BrowserCache.SetClearOnExit(); | ||||||
|  |             FormMessage.Information("Clear Cache", "Cache will be automatically cleared when TweetDuck exits.", FormMessage.OK); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void checkClearCacheAuto_CheckedChanged(object sender, EventArgs e){ | ||||||
|  |             numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |         #region Configuration | ||||||
|  | 
 | ||||||
|  |         private void btnEditCefArgs_Click(object sender, EventArgs e){ | ||||||
|  |             DialogSettingsCefArgs form = new DialogSettingsCefArgs(Config.CustomCefArgs); | ||||||
|  | 
 | ||||||
|  |             form.VisibleChanged += (sender2, args2) => { | ||||||
|  |                 form.MoveToCenter(ParentForm); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             form.FormClosed += (sender2, args2) => { | ||||||
|  |                 RestoreParentForm(); | ||||||
|  | 
 | ||||||
|  |                 if (form.DialogResult == DialogResult.OK){ | ||||||
|  |                     Config.CustomCefArgs = form.CefArgs; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 form.Dispose(); | ||||||
|  |             }; | ||||||
|  |              | ||||||
|  |             form.Show(ParentForm); | ||||||
|  |             NativeMethods.SetFormDisabled(ParentForm, true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void btnEditCSS_Click(object sender, EventArgs e){ | ||||||
|  |             DialogSettingsCSS form = new DialogSettingsCSS(Config.CustomBrowserCSS, Config.CustomNotificationCSS, reinjectBrowserCSS, openDevTools); | ||||||
|  | 
 | ||||||
|  |             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 RestoreParentForm(){ | ||||||
|  |             if (ParentForm != null){ // when the parent is closed first, ParentForm is null in FormClosed event | ||||||
|  |                 NativeMethods.SetFormDisabled(ParentForm, false); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										182
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								Core/Other/Settings/TabSettingsFeedback.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | |||||||
|  | namespace TweetDuck.Core.Other.Settings { | ||||||
|  |     partial class TabSettingsFeedback { | ||||||
|  |         /// <summary>  | ||||||
|  |         /// Required designer variable. | ||||||
|  |         /// </summary> | ||||||
|  |         private System.ComponentModel.IContainer components = null; | ||||||
|  | 
 | ||||||
|  |         /// <summary>  | ||||||
|  |         /// Clean up any resources being used. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | ||||||
|  |         protected override void Dispose(bool disposing) { | ||||||
|  |             if (disposing && (components != null)) { | ||||||
|  |                 components.Dispose(); | ||||||
|  |             } | ||||||
|  |             base.Dispose(disposing); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #region Component Designer generated code | ||||||
|  | 
 | ||||||
|  |         /// <summary>  | ||||||
|  |         /// Required method for Designer support - do not modify  | ||||||
|  |         /// the contents of this method with the code editor. | ||||||
|  |         /// </summary> | ||||||
|  |         private void InitializeComponent() { | ||||||
|  |             this.components = new System.ComponentModel.Container(); | ||||||
|  |             this.panelDataCollection = new System.Windows.Forms.Panel(); | ||||||
|  |             this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel(); | ||||||
|  |             this.checkDataCollection = new System.Windows.Forms.CheckBox(); | ||||||
|  |             this.labelDataCollectionMessage = new System.Windows.Forms.Label(); | ||||||
|  |             this.btnViewReport = new System.Windows.Forms.Button(); | ||||||
|  |             this.btnSendFeedback = new System.Windows.Forms.Button(); | ||||||
|  |             this.labelDataCollection = new System.Windows.Forms.Label(); | ||||||
|  |             this.labelFeedback = new System.Windows.Forms.Label(); | ||||||
|  |             this.toolTip = new System.Windows.Forms.ToolTip(this.components); | ||||||
|  |             this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); | ||||||
|  |             this.panelDataCollection.SuspendLayout(); | ||||||
|  |             this.flowPanel.SuspendLayout(); | ||||||
|  |             this.SuspendLayout(); | ||||||
|  |             //  | ||||||
|  |             // panelDataCollection | ||||||
|  |             //  | ||||||
|  |             this.panelDataCollection.Controls.Add(this.labelDataCollectionLink); | ||||||
|  |             this.panelDataCollection.Controls.Add(this.checkDataCollection); | ||||||
|  |             this.panelDataCollection.Location = new System.Drawing.Point(0, 78); | ||||||
|  |             this.panelDataCollection.Margin = new System.Windows.Forms.Padding(0); | ||||||
|  |             this.panelDataCollection.Name = "panelDataCollection"; | ||||||
|  |             this.panelDataCollection.Size = new System.Drawing.Size(300, 28); | ||||||
|  |             this.panelDataCollection.TabIndex = 3; | ||||||
|  |             //  | ||||||
|  |             // labelDataCollectionLink | ||||||
|  |             //  | ||||||
|  |             this.labelDataCollectionLink.AutoSize = true; | ||||||
|  |             this.labelDataCollectionLink.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10); | ||||||
|  |             this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; | ||||||
|  |             this.labelDataCollectionLink.Location = new System.Drawing.Point(153, 4); | ||||||
|  |             this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0); | ||||||
|  |             this.labelDataCollectionLink.Name = "labelDataCollectionLink"; | ||||||
|  |             this.labelDataCollectionLink.Size = new System.Drawing.Size(71, 21); | ||||||
|  |             this.labelDataCollectionLink.TabIndex = 1; | ||||||
|  |             this.labelDataCollectionLink.TabStop = true; | ||||||
|  |             this.labelDataCollectionLink.Text = "(learn more)"; | ||||||
|  |             this.labelDataCollectionLink.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; | ||||||
|  |             this.labelDataCollectionLink.UseCompatibleTextRendering = true; | ||||||
|  |             //  | ||||||
|  |             // checkDataCollection | ||||||
|  |             //  | ||||||
|  |             this.checkDataCollection.AutoSize = true; | ||||||
|  |             this.checkDataCollection.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.checkDataCollection.Location = new System.Drawing.Point(6, 6); | ||||||
|  |             this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 6, 0, 2); | ||||||
|  |             this.checkDataCollection.Name = "checkDataCollection"; | ||||||
|  |             this.checkDataCollection.Size = new System.Drawing.Size(147, 19); | ||||||
|  |             this.checkDataCollection.TabIndex = 0; | ||||||
|  |             this.checkDataCollection.Text = "Send Anonymous Data"; | ||||||
|  |             this.checkDataCollection.UseVisualStyleBackColor = true; | ||||||
|  |             //  | ||||||
|  |             // labelDataCollectionMessage | ||||||
|  |             //  | ||||||
|  |             this.labelDataCollectionMessage.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 143); | ||||||
|  |             this.labelDataCollectionMessage.Margin = new System.Windows.Forms.Padding(6); | ||||||
|  |             this.labelDataCollectionMessage.Name = "labelDataCollectionMessage"; | ||||||
|  |             this.labelDataCollectionMessage.Size = new System.Drawing.Size(288, 67); | ||||||
|  |             this.labelDataCollectionMessage.TabIndex = 5; | ||||||
|  |             //  | ||||||
|  |             // btnViewReport | ||||||
|  |             //  | ||||||
|  |             this.btnViewReport.AutoSize = true; | ||||||
|  |             this.btnViewReport.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.btnViewReport.Location = new System.Drawing.Point(5, 109); | ||||||
|  |             this.btnViewReport.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); | ||||||
|  |             this.btnViewReport.Name = "btnViewReport"; | ||||||
|  |             this.btnViewReport.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); | ||||||
|  |             this.btnViewReport.Size = new System.Drawing.Size(155, 25); | ||||||
|  |             this.btnViewReport.TabIndex = 4; | ||||||
|  |             this.btnViewReport.Text = "View My Analytics Report"; | ||||||
|  |             this.btnViewReport.UseVisualStyleBackColor = true; | ||||||
|  |             //  | ||||||
|  |             // btnSendFeedback | ||||||
|  |             //  | ||||||
|  |             this.btnSendFeedback.AutoSize = true; | ||||||
|  |             this.btnSendFeedback.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.btnSendFeedback.Location = new System.Drawing.Point(5, 23); | ||||||
|  |             this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); | ||||||
|  |             this.btnSendFeedback.Name = "btnSendFeedback"; | ||||||
|  |             this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); | ||||||
|  |             this.btnSendFeedback.Size = new System.Drawing.Size(170, 25); | ||||||
|  |             this.btnSendFeedback.TabIndex = 1; | ||||||
|  |             this.btnSendFeedback.Text = "Send Feedback / Bug Report"; | ||||||
|  |             this.btnSendFeedback.UseVisualStyleBackColor = true; | ||||||
|  |             //  | ||||||
|  |             // labelDataCollection | ||||||
|  |             //  | ||||||
|  |             this.labelDataCollection.AutoSize = true; | ||||||
|  |             this.labelDataCollection.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); | ||||||
|  |             this.labelDataCollection.Location = new System.Drawing.Point(3, 63); | ||||||
|  |             this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); | ||||||
|  |             this.labelDataCollection.Name = "labelDataCollection"; | ||||||
|  |             this.labelDataCollection.Size = new System.Drawing.Size(88, 15); | ||||||
|  |             this.labelDataCollection.TabIndex = 2; | ||||||
|  |             this.labelDataCollection.Text = "Data Collection"; | ||||||
|  |             //  | ||||||
|  |             // labelFeedback | ||||||
|  |             //  | ||||||
|  |             this.labelFeedback.AutoSize = true; | ||||||
|  |             this.labelFeedback.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); | ||||||
|  |             this.labelFeedback.Location = new System.Drawing.Point(0, 0); | ||||||
|  |             this.labelFeedback.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1); | ||||||
|  |             this.labelFeedback.Name = "labelFeedback"; | ||||||
|  |             this.labelFeedback.Size = new System.Drawing.Size(75, 19); | ||||||
|  |             this.labelFeedback.TabIndex = 0; | ||||||
|  |             this.labelFeedback.Text = "FEEDBACK"; | ||||||
|  |             //  | ||||||
|  |             // flowPanel | ||||||
|  |             //  | ||||||
|  |             this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  | ||||||
|  |             | System.Windows.Forms.AnchorStyles.Left)  | ||||||
|  |             | System.Windows.Forms.AnchorStyles.Right))); | ||||||
|  |             this.flowPanel.Controls.Add(this.labelFeedback); | ||||||
|  |             this.flowPanel.Controls.Add(this.btnSendFeedback); | ||||||
|  |             this.flowPanel.Controls.Add(this.labelDataCollection); | ||||||
|  |             this.flowPanel.Controls.Add(this.panelDataCollection); | ||||||
|  |             this.flowPanel.Controls.Add(this.btnViewReport); | ||||||
|  |             this.flowPanel.Controls.Add(this.labelDataCollectionMessage); | ||||||
|  |             this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; | ||||||
|  |             this.flowPanel.Location = new System.Drawing.Point(9, 9); | ||||||
|  |             this.flowPanel.Name = "flowPanel"; | ||||||
|  |             this.flowPanel.Size = new System.Drawing.Size(300, 462); | ||||||
|  |             this.flowPanel.TabIndex = 0; | ||||||
|  |             this.flowPanel.WrapContents = false; | ||||||
|  |             //  | ||||||
|  |             // TabSettingsFeedback | ||||||
|  |             //  | ||||||
|  |             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | ||||||
|  |             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||||
|  |             this.Controls.Add(this.flowPanel); | ||||||
|  |             this.Name = "TabSettingsFeedback"; | ||||||
|  |             this.Size = new System.Drawing.Size(631, 480); | ||||||
|  |             this.panelDataCollection.ResumeLayout(false); | ||||||
|  |             this.panelDataCollection.PerformLayout(); | ||||||
|  |             this.flowPanel.ResumeLayout(false); | ||||||
|  |             this.flowPanel.PerformLayout(); | ||||||
|  |             this.ResumeLayout(false); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         private System.Windows.Forms.Panel panelDataCollection; | ||||||
|  |         private System.Windows.Forms.CheckBox checkDataCollection; | ||||||
|  |         private System.Windows.Forms.Label labelDataCollection; | ||||||
|  |         private System.Windows.Forms.Label labelFeedback; | ||||||
|  |         private System.Windows.Forms.ToolTip toolTip; | ||||||
|  |         private System.Windows.Forms.LinkLabel labelDataCollectionLink; | ||||||
|  |         private System.Windows.Forms.Button btnSendFeedback; | ||||||
|  |         private System.Windows.Forms.Button btnViewReport; | ||||||
|  |         private System.Windows.Forms.Label labelDataCollectionMessage; | ||||||
|  |         private System.Windows.Forms.FlowLayoutPanel flowPanel; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								Core/Other/Settings/TabSettingsFeedback.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								Core/Other/Settings/TabSettingsFeedback.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  |             // feedback | ||||||
|  | 
 | ||||||
|  |             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; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #region Feedback | ||||||
|  | 
 | ||||||
|  |         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(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace TweetDuck.Dialogs.Settings { | namespace TweetDuck.Core.Other.Settings { | ||||||
|     partial class TabSettingsGeneral { |     partial class TabSettingsGeneral { | ||||||
|         /// <summary>  |         /// <summary>  | ||||||
|         /// Required designer variable. |         /// Required designer variable. | ||||||
| @@ -34,43 +34,37 @@ | |||||||
|             this.trackBarZoom = new System.Windows.Forms.TrackBar(); |             this.trackBarZoom = new System.Windows.Forms.TrackBar(); | ||||||
|             this.labelZoom = new System.Windows.Forms.Label(); |             this.labelZoom = new System.Windows.Forms.Label(); | ||||||
|             this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components); |             this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components); | ||||||
|  |             this.labelUI = new System.Windows.Forms.Label(); | ||||||
|             this.panelZoom = new System.Windows.Forms.Panel(); |             this.panelZoom = new System.Windows.Forms.Panel(); | ||||||
|             this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox(); |             this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox(); | ||||||
|             this.labelUpdates = new System.Windows.Forms.Label(); |             this.labelUpdates = new System.Windows.Forms.Label(); | ||||||
|             this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel(); |             this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel(); | ||||||
|             this.labelUI = new System.Windows.Forms.Label(); |  | ||||||
|             this.checkFocusDmInput = new System.Windows.Forms.CheckBox(); |             this.checkFocusDmInput = new System.Windows.Forms.CheckBox(); | ||||||
|             this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox(); |             this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox(); | ||||||
|  |             this.labelTray = new System.Windows.Forms.Label(); | ||||||
|  |             this.comboBoxTrayType = new System.Windows.Forms.ComboBox(); | ||||||
|  |             this.labelTrayIcon = new System.Windows.Forms.Label(); | ||||||
|  |             this.checkTrayHighlight = new System.Windows.Forms.CheckBox(); | ||||||
|  |             this.labelBrowserSettings = new System.Windows.Forms.Label(); | ||||||
|             this.checkSmoothScrolling = new System.Windows.Forms.CheckBox(); |             this.checkSmoothScrolling = new System.Windows.Forms.CheckBox(); | ||||||
|             this.labelTweetDeckSettings = new System.Windows.Forms.Label(); |             this.checkTouchAdjustment = new System.Windows.Forms.CheckBox(); | ||||||
|             this.checkHideTweetsByNftUsers = new System.Windows.Forms.CheckBox(); |  | ||||||
|             this.labelBrowserPath = new System.Windows.Forms.Label(); |             this.labelBrowserPath = new System.Windows.Forms.Label(); | ||||||
|             this.comboBoxCustomBrowser = new System.Windows.Forms.ComboBox(); |             this.comboBoxBrowserPath = new System.Windows.Forms.ComboBox(); | ||||||
|             this.labelSearchEngine = new System.Windows.Forms.Label(); |             this.labelSearchEngine = new System.Windows.Forms.Label(); | ||||||
|             this.comboBoxSearchEngine = new System.Windows.Forms.ComboBox(); |             this.comboBoxSearchEngine = new System.Windows.Forms.ComboBox(); | ||||||
|             this.flowPanelRight = new System.Windows.Forms.FlowLayoutPanel(); |             this.flowPanelRight = new System.Windows.Forms.FlowLayoutPanel(); | ||||||
|  |             this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox(); | ||||||
|             this.labelLocales = new System.Windows.Forms.Label(); |             this.labelLocales = new System.Windows.Forms.Label(); | ||||||
|             this.checkSpellCheck = new System.Windows.Forms.CheckBox(); |             this.checkSpellCheck = new System.Windows.Forms.CheckBox(); | ||||||
|             this.labelSpellCheckLanguage = new System.Windows.Forms.Label(); |             this.labelSpellCheckLanguage = new System.Windows.Forms.Label(); | ||||||
|             this.comboBoxSpellCheckLanguage = new System.Windows.Forms.ComboBox(); |             this.comboBoxSpellCheckLanguage = new System.Windows.Forms.ComboBox(); | ||||||
|             this.labelTranslationTarget = new System.Windows.Forms.Label(); |             this.labelTranslationTarget = new System.Windows.Forms.Label(); | ||||||
|             this.comboBoxTranslationTarget = new System.Windows.Forms.ComboBox(); |             this.comboBoxTranslationTarget = new System.Windows.Forms.ComboBox(); | ||||||
|             this.labelFirstDayOfWeek = new System.Windows.Forms.Label(); |  | ||||||
|             this.comboBoxFirstDayOfWeek = new System.Windows.Forms.ComboBox(); |  | ||||||
|             this.labelExternalApplications = new System.Windows.Forms.Label(); |  | ||||||
|             this.panelCustomBrowser = new System.Windows.Forms.Panel(); |  | ||||||
|             this.btnCustomBrowserChange = new System.Windows.Forms.Button(); |  | ||||||
|             this.labelVideoPlayerPath = new System.Windows.Forms.Label(); |  | ||||||
|             this.panelCustomVideoPlayer = new System.Windows.Forms.Panel(); |  | ||||||
|             this.comboBoxCustomVideoPlayer = new System.Windows.Forms.ComboBox(); |  | ||||||
|             this.btnCustomVideoPlayerChange = new System.Windows.Forms.Button(); |  | ||||||
|             this.panelSeparator = new System.Windows.Forms.Panel(); |             this.panelSeparator = new System.Windows.Forms.Panel(); | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); |             ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); | ||||||
|             this.panelZoom.SuspendLayout(); |             this.panelZoom.SuspendLayout(); | ||||||
|             this.flowPanelLeft.SuspendLayout(); |             this.flowPanelLeft.SuspendLayout(); | ||||||
|             this.flowPanelRight.SuspendLayout(); |             this.flowPanelRight.SuspendLayout(); | ||||||
|             this.panelCustomBrowser.SuspendLayout(); |  | ||||||
|             this.panelCustomVideoPlayer.SuspendLayout(); |  | ||||||
|             this.SuspendLayout(); |             this.SuspendLayout(); | ||||||
|             //  |             //  | ||||||
|             // checkExpandLinks |             // checkExpandLinks | ||||||
| @@ -89,11 +83,11 @@ | |||||||
|             //  |             //  | ||||||
|             this.checkUpdateNotifications.AutoSize = true; |             this.checkUpdateNotifications.AutoSize = true; | ||||||
|             this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 381); |             this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 409); | ||||||
|             this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |             this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); | ||||||
|             this.checkUpdateNotifications.Name = "checkUpdateNotifications"; |             this.checkUpdateNotifications.Name = "checkUpdateNotifications"; | ||||||
|             this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19); |             this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19); | ||||||
|             this.checkUpdateNotifications.TabIndex = 13; |             this.checkUpdateNotifications.TabIndex = 14; | ||||||
|             this.checkUpdateNotifications.Text = "Check Updates Automatically"; |             this.checkUpdateNotifications.Text = "Check Updates Automatically"; | ||||||
|             this.checkUpdateNotifications.UseVisualStyleBackColor = true; |             this.checkUpdateNotifications.UseVisualStyleBackColor = true; | ||||||
|             //  |             //  | ||||||
| @@ -101,12 +95,12 @@ | |||||||
|             //  |             //  | ||||||
|             this.btnCheckUpdates.AutoSize = true; |             this.btnCheckUpdates.AutoSize = true; | ||||||
|             this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.btnCheckUpdates.Location = new System.Drawing.Point(5, 405); |             this.btnCheckUpdates.Location = new System.Drawing.Point(5, 433); | ||||||
|             this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); |             this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); | ||||||
|             this.btnCheckUpdates.Name = "btnCheckUpdates"; |             this.btnCheckUpdates.Name = "btnCheckUpdates"; | ||||||
|             this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |             this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); | ||||||
|             this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25); |             this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25); | ||||||
|             this.btnCheckUpdates.TabIndex = 14; |             this.btnCheckUpdates.TabIndex = 15; | ||||||
|             this.btnCheckUpdates.Text = "Check Updates Now"; |             this.btnCheckUpdates.Text = "Check Updates Now"; | ||||||
|             this.btnCheckUpdates.UseVisualStyleBackColor = true; |             this.btnCheckUpdates.UseVisualStyleBackColor = true; | ||||||
|             //  |             //  | ||||||
| @@ -114,7 +108,7 @@ | |||||||
|             //  |             //  | ||||||
|             this.labelZoomValue.BackColor = System.Drawing.Color.Transparent; |             this.labelZoomValue.BackColor = System.Drawing.Color.Transparent; | ||||||
|             this.labelZoomValue.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.labelZoomValue.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.labelZoomValue.Location = new System.Drawing.Point(176, 4); |             this.labelZoomValue.Location = new System.Drawing.Point(147, 4); | ||||||
|             this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); |             this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); | ||||||
|             this.labelZoomValue.Name = "labelZoomValue"; |             this.labelZoomValue.Name = "labelZoomValue"; | ||||||
|             this.labelZoomValue.Size = new System.Drawing.Size(38, 13); |             this.labelZoomValue.Size = new System.Drawing.Size(38, 13); | ||||||
| @@ -126,12 +120,12 @@ | |||||||
|             //  |             //  | ||||||
|             this.checkBestImageQuality.AutoSize = true; |             this.checkBestImageQuality.AutoSize = true; | ||||||
|             this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.checkBestImageQuality.Location = new System.Drawing.Point(6, 259); |             this.checkBestImageQuality.Location = new System.Drawing.Point(6, 122); | ||||||
|             this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |             this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); | ||||||
|             this.checkBestImageQuality.Name = "checkBestImageQuality"; |             this.checkBestImageQuality.Name = "checkBestImageQuality"; | ||||||
|             this.checkBestImageQuality.Size = new System.Drawing.Size(182, 19); |             this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19); | ||||||
|             this.checkBestImageQuality.TabIndex = 9; |             this.checkBestImageQuality.TabIndex = 5; | ||||||
|             this.checkBestImageQuality.Text = "Download Best Image Quality"; |             this.checkBestImageQuality.Text = "Best Image Quality"; | ||||||
|             this.checkBestImageQuality.UseVisualStyleBackColor = true; |             this.checkBestImageQuality.UseVisualStyleBackColor = true; | ||||||
|             //  |             //  | ||||||
|             // checkOpenSearchInFirstColumn |             // checkOpenSearchInFirstColumn | ||||||
| @@ -155,7 +149,7 @@ | |||||||
|             this.trackBarZoom.Maximum = 200; |             this.trackBarZoom.Maximum = 200; | ||||||
|             this.trackBarZoom.Minimum = 50; |             this.trackBarZoom.Minimum = 50; | ||||||
|             this.trackBarZoom.Name = "trackBarZoom"; |             this.trackBarZoom.Name = "trackBarZoom"; | ||||||
|             this.trackBarZoom.Size = new System.Drawing.Size(177, 30); |             this.trackBarZoom.Size = new System.Drawing.Size(148, 30); | ||||||
|             this.trackBarZoom.SmallChange = 5; |             this.trackBarZoom.SmallChange = 5; | ||||||
|             this.trackBarZoom.TabIndex = 0; |             this.trackBarZoom.TabIndex = 0; | ||||||
|             this.trackBarZoom.TickFrequency = 25; |             this.trackBarZoom.TickFrequency = 25; | ||||||
| @@ -165,11 +159,11 @@ | |||||||
|             //  |             //  | ||||||
|             this.labelZoom.AutoSize = true; |             this.labelZoom.AutoSize = true; | ||||||
|             this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); |             this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelZoom.Location = new System.Drawing.Point(3, 155); |             this.labelZoom.Location = new System.Drawing.Point(3, 179); | ||||||
|             this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |             this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); | ||||||
|             this.labelZoom.Name = "labelZoom"; |             this.labelZoom.Name = "labelZoom"; | ||||||
|             this.labelZoom.Size = new System.Drawing.Size(39, 15); |             this.labelZoom.Size = new System.Drawing.Size(39, 15); | ||||||
|             this.labelZoom.TabIndex = 6; |             this.labelZoom.TabIndex = 7; | ||||||
|             this.labelZoom.Text = "Zoom"; |             this.labelZoom.Text = "Zoom"; | ||||||
|             //  |             //  | ||||||
|             // zoomUpdateTimer |             // zoomUpdateTimer | ||||||
| @@ -177,65 +171,6 @@ | |||||||
|             this.zoomUpdateTimer.Interval = 250; |             this.zoomUpdateTimer.Interval = 250; | ||||||
|             this.zoomUpdateTimer.Tick += new System.EventHandler(this.zoomUpdateTimer_Tick); |             this.zoomUpdateTimer.Tick += new System.EventHandler(this.zoomUpdateTimer_Tick); | ||||||
|             //  |             //  | ||||||
|             // panelZoom |  | ||||||
|             //  |  | ||||||
|             this.panelZoom.Controls.Add(this.trackBarZoom); |  | ||||||
|             this.panelZoom.Controls.Add(this.labelZoomValue); |  | ||||||
|             this.panelZoom.Location = new System.Drawing.Point(0, 171); |  | ||||||
|             this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0); |  | ||||||
|             this.panelZoom.Name = "panelZoom"; |  | ||||||
|             this.panelZoom.Size = new System.Drawing.Size(300, 35); |  | ||||||
|             this.panelZoom.TabIndex = 7; |  | ||||||
|             //  |  | ||||||
|             // checkAnimatedAvatars |  | ||||||
|             //  |  | ||||||
|             this.checkAnimatedAvatars.AutoSize = true; |  | ||||||
|             this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 307); |  | ||||||
|             this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkAnimatedAvatars.Name = "checkAnimatedAvatars"; |  | ||||||
|             this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19); |  | ||||||
|             this.checkAnimatedAvatars.TabIndex = 11; |  | ||||||
|             this.checkAnimatedAvatars.Text = "Enable Animated Avatars"; |  | ||||||
|             this.checkAnimatedAvatars.UseVisualStyleBackColor = true; |  | ||||||
|             //  |  | ||||||
|             // labelUpdates |  | ||||||
|             //  |  | ||||||
|             this.labelUpdates.AutoSize = true; |  | ||||||
|             this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |  | ||||||
|             this.labelUpdates.Location = new System.Drawing.Point(0, 355); |  | ||||||
|             this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1); |  | ||||||
|             this.labelUpdates.Name = "labelUpdates"; |  | ||||||
|             this.labelUpdates.Size = new System.Drawing.Size(69, 19); |  | ||||||
|             this.labelUpdates.TabIndex = 12; |  | ||||||
|             this.labelUpdates.Text = "UPDATES"; |  | ||||||
|             //  |  | ||||||
|             // flowPanelLeft |  | ||||||
|             //  |  | ||||||
|             this.flowPanelLeft.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left))); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelUI); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkExpandLinks); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkFocusDmInput); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkSmoothScrolling); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelZoom); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.panelZoom); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelTweetDeckSettings); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkBestImageQuality); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkHideTweetsByNftUsers); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkAnimatedAvatars); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.labelUpdates); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.checkUpdateNotifications); |  | ||||||
|             this.flowPanelLeft.Controls.Add(this.btnCheckUpdates); |  | ||||||
|             this.flowPanelLeft.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |  | ||||||
|             this.flowPanelLeft.Location = new System.Drawing.Point(9, 9); |  | ||||||
|             this.flowPanelLeft.Name = "flowPanelLeft"; |  | ||||||
|             this.flowPanelLeft.Size = new System.Drawing.Size(300, 462); |  | ||||||
|             this.flowPanelLeft.TabIndex = 0; |  | ||||||
|             this.flowPanelLeft.WrapContents = false; |  | ||||||
|             //  |  | ||||||
|             // labelUI |             // labelUI | ||||||
|             //  |             //  | ||||||
|             this.labelUI.AutoSize = true; |             this.labelUI.AutoSize = true; | ||||||
| @@ -247,6 +182,66 @@ | |||||||
|             this.labelUI.TabIndex = 0; |             this.labelUI.TabIndex = 0; | ||||||
|             this.labelUI.Text = "USER INTERFACE"; |             this.labelUI.Text = "USER INTERFACE"; | ||||||
|             //  |             //  | ||||||
|  |             // panelZoom | ||||||
|  |             //  | ||||||
|  |             this.panelZoom.Controls.Add(this.trackBarZoom); | ||||||
|  |             this.panelZoom.Controls.Add(this.labelZoomValue); | ||||||
|  |             this.panelZoom.Location = new System.Drawing.Point(0, 195); | ||||||
|  |             this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0); | ||||||
|  |             this.panelZoom.Name = "panelZoom"; | ||||||
|  |             this.panelZoom.Size = new System.Drawing.Size(300, 35); | ||||||
|  |             this.panelZoom.TabIndex = 8; | ||||||
|  |             //  | ||||||
|  |             // checkAnimatedAvatars | ||||||
|  |             //  | ||||||
|  |             this.checkAnimatedAvatars.AutoSize = true; | ||||||
|  |             this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 146); | ||||||
|  |             this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); | ||||||
|  |             this.checkAnimatedAvatars.Name = "checkAnimatedAvatars"; | ||||||
|  |             this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19); | ||||||
|  |             this.checkAnimatedAvatars.TabIndex = 6; | ||||||
|  |             this.checkAnimatedAvatars.Text = "Enable Animated Avatars"; | ||||||
|  |             this.checkAnimatedAvatars.UseVisualStyleBackColor = true; | ||||||
|  |             //  | ||||||
|  |             // labelUpdates | ||||||
|  |             //  | ||||||
|  |             this.labelUpdates.AutoSize = true; | ||||||
|  |             this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); | ||||||
|  |             this.labelUpdates.Location = new System.Drawing.Point(0, 383); | ||||||
|  |             this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1); | ||||||
|  |             this.labelUpdates.Name = "labelUpdates"; | ||||||
|  |             this.labelUpdates.Size = new System.Drawing.Size(69, 19); | ||||||
|  |             this.labelUpdates.TabIndex = 13; | ||||||
|  |             this.labelUpdates.Text = "UPDATES"; | ||||||
|  |             //  | ||||||
|  |             // flowPanelLeft | ||||||
|  |             //  | ||||||
|  |             this.flowPanelLeft.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  | ||||||
|  |             | System.Windows.Forms.AnchorStyles.Left))); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.labelUI); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkExpandLinks); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkFocusDmInput); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkBestImageQuality); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkAnimatedAvatars); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.labelZoom); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.panelZoom); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.labelTray); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.comboBoxTrayType); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.labelTrayIcon); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkTrayHighlight); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.labelUpdates); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.checkUpdateNotifications); | ||||||
|  |             this.flowPanelLeft.Controls.Add(this.btnCheckUpdates); | ||||||
|  |             this.flowPanelLeft.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; | ||||||
|  |             this.flowPanelLeft.Location = new System.Drawing.Point(9, 9); | ||||||
|  |             this.flowPanelLeft.Name = "flowPanelLeft"; | ||||||
|  |             this.flowPanelLeft.Size = new System.Drawing.Size(300, 462); | ||||||
|  |             this.flowPanelLeft.TabIndex = 0; | ||||||
|  |             this.flowPanelLeft.WrapContents = false; | ||||||
|  |             //  | ||||||
|             // checkFocusDmInput |             // checkFocusDmInput | ||||||
|             //  |             //  | ||||||
|             this.checkFocusDmInput.AutoSize = true; |             this.checkFocusDmInput.AutoSize = true; | ||||||
| @@ -271,72 +266,117 @@ | |||||||
|             this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open"; |             this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open"; | ||||||
|             this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true; |             this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true; | ||||||
|             //  |             //  | ||||||
|  |             // labelTray | ||||||
|  |             //  | ||||||
|  |             this.labelTray.AutoSize = true; | ||||||
|  |             this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); | ||||||
|  |             this.labelTray.Location = new System.Drawing.Point(0, 255); | ||||||
|  |             this.labelTray.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1); | ||||||
|  |             this.labelTray.Name = "labelTray"; | ||||||
|  |             this.labelTray.Size = new System.Drawing.Size(99, 19); | ||||||
|  |             this.labelTray.TabIndex = 9; | ||||||
|  |             this.labelTray.Text = "SYSTEM TRAY"; | ||||||
|  |             //  | ||||||
|  |             // comboBoxTrayType | ||||||
|  |             //  | ||||||
|  |             this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | ||||||
|  |             this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.comboBoxTrayType.FormattingEnabled = true; | ||||||
|  |             this.comboBoxTrayType.Location = new System.Drawing.Point(5, 279); | ||||||
|  |             this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); | ||||||
|  |             this.comboBoxTrayType.Name = "comboBoxTrayType"; | ||||||
|  |             this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23); | ||||||
|  |             this.comboBoxTrayType.TabIndex = 10; | ||||||
|  |             //  | ||||||
|  |             // labelTrayIcon | ||||||
|  |             //  | ||||||
|  |             this.labelTrayIcon.AutoSize = true; | ||||||
|  |             this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); | ||||||
|  |             this.labelTrayIcon.Location = new System.Drawing.Point(3, 314); | ||||||
|  |             this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0); | ||||||
|  |             this.labelTrayIcon.Name = "labelTrayIcon"; | ||||||
|  |             this.labelTrayIcon.Size = new System.Drawing.Size(56, 15); | ||||||
|  |             this.labelTrayIcon.TabIndex = 11; | ||||||
|  |             this.labelTrayIcon.Text = "Tray Icon"; | ||||||
|  |             //  | ||||||
|  |             // checkTrayHighlight | ||||||
|  |             //  | ||||||
|  |             this.checkTrayHighlight.AutoSize = true; | ||||||
|  |             this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.checkTrayHighlight.Location = new System.Drawing.Point(6, 335); | ||||||
|  |             this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); | ||||||
|  |             this.checkTrayHighlight.Name = "checkTrayHighlight"; | ||||||
|  |             this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19); | ||||||
|  |             this.checkTrayHighlight.TabIndex = 12; | ||||||
|  |             this.checkTrayHighlight.Text = "Enable Highlight"; | ||||||
|  |             this.checkTrayHighlight.UseVisualStyleBackColor = true; | ||||||
|  |             //  | ||||||
|  |             // labelBrowserSettings | ||||||
|  |             //  | ||||||
|  |             this.labelBrowserSettings.AutoSize = true; | ||||||
|  |             this.labelBrowserSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); | ||||||
|  |             this.labelBrowserSettings.Location = new System.Drawing.Point(0, 0); | ||||||
|  |             this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1); | ||||||
|  |             this.labelBrowserSettings.Name = "labelBrowserSettings"; | ||||||
|  |             this.labelBrowserSettings.Size = new System.Drawing.Size(143, 19); | ||||||
|  |             this.labelBrowserSettings.TabIndex = 0; | ||||||
|  |             this.labelBrowserSettings.Text = "BROWSER SETTINGS"; | ||||||
|  |             //  | ||||||
|             // checkSmoothScrolling |             // checkSmoothScrolling | ||||||
|             //  |             //  | ||||||
|             this.checkSmoothScrolling.AutoSize = true; |             this.checkSmoothScrolling.AutoSize = true; | ||||||
|             this.checkSmoothScrolling.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.checkSmoothScrolling.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 122); |             this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 26); | ||||||
|             this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |             this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); | ||||||
|             this.checkSmoothScrolling.Name = "checkSmoothScrolling"; |             this.checkSmoothScrolling.Name = "checkSmoothScrolling"; | ||||||
|             this.checkSmoothScrolling.Size = new System.Drawing.Size(117, 19); |             this.checkSmoothScrolling.Size = new System.Drawing.Size(117, 19); | ||||||
|             this.checkSmoothScrolling.TabIndex = 5; |             this.checkSmoothScrolling.TabIndex = 1; | ||||||
|             this.checkSmoothScrolling.Text = "Smooth Scrolling"; |             this.checkSmoothScrolling.Text = "Smooth Scrolling"; | ||||||
|             this.checkSmoothScrolling.UseVisualStyleBackColor = true; |             this.checkSmoothScrolling.UseVisualStyleBackColor = true; | ||||||
|             //  |             //  | ||||||
|             // labelTweetDeckSettings |             // checkTouchAdjustment | ||||||
|             //  |             //  | ||||||
|             this.labelTweetDeckSettings.AutoSize = true; |             this.checkTouchAdjustment.AutoSize = true; | ||||||
|             this.labelTweetDeckSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |             this.checkTouchAdjustment.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.labelTweetDeckSettings.Location = new System.Drawing.Point(0, 233); |             this.checkTouchAdjustment.Location = new System.Drawing.Point(6, 50); | ||||||
|             this.labelTweetDeckSettings.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1); |             this.checkTouchAdjustment.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); | ||||||
|             this.labelTweetDeckSettings.Name = "labelTweetDeckSettings"; |             this.checkTouchAdjustment.Name = "checkTouchAdjustment"; | ||||||
|             this.labelTweetDeckSettings.Size = new System.Drawing.Size(67, 19); |             this.checkTouchAdjustment.Size = new System.Drawing.Size(163, 19); | ||||||
|             this.labelTweetDeckSettings.TabIndex = 8; |             this.checkTouchAdjustment.TabIndex = 2; | ||||||
|             this.labelTweetDeckSettings.Text = "TWITTER"; |             this.checkTouchAdjustment.Text = "Touch Screen Adjustment"; | ||||||
|             //  |             this.checkTouchAdjustment.UseVisualStyleBackColor = true; | ||||||
|             // checkHideTweetsByNftUsers |  | ||||||
|             //  |  | ||||||
|             this.checkHideTweetsByNftUsers.AutoSize = true; |  | ||||||
|             this.checkHideTweetsByNftUsers.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.checkHideTweetsByNftUsers.Location = new System.Drawing.Point(6, 283); |  | ||||||
|             this.checkHideTweetsByNftUsers.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); |  | ||||||
|             this.checkHideTweetsByNftUsers.Name = "checkHideTweetsByNftUsers"; |  | ||||||
|             this.checkHideTweetsByNftUsers.Size = new System.Drawing.Size(231, 19); |  | ||||||
|             this.checkHideTweetsByNftUsers.TabIndex = 10; |  | ||||||
|             this.checkHideTweetsByNftUsers.Text = "Hide Tweets by Users with NFT Avatars"; |  | ||||||
|             this.checkHideTweetsByNftUsers.UseVisualStyleBackColor = true; |  | ||||||
|             //  |             //  | ||||||
|             // labelBrowserPath |             // labelBrowserPath | ||||||
|             //  |             //  | ||||||
|             this.labelBrowserPath.AutoSize = true; |             this.labelBrowserPath.AutoSize = true; | ||||||
|             this.labelBrowserPath.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); |             this.labelBrowserPath.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelBrowserPath.Location = new System.Drawing.Point(3, 275); |             this.labelBrowserPath.Location = new System.Drawing.Point(3, 107); | ||||||
|             this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |             this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); | ||||||
|             this.labelBrowserPath.Name = "labelBrowserPath"; |             this.labelBrowserPath.Name = "labelBrowserPath"; | ||||||
|             this.labelBrowserPath.Size = new System.Drawing.Size(104, 15); |             this.labelBrowserPath.Size = new System.Drawing.Size(104, 15); | ||||||
|             this.labelBrowserPath.TabIndex = 9; |             this.labelBrowserPath.TabIndex = 4; | ||||||
|             this.labelBrowserPath.Text = "Open Links With..."; |             this.labelBrowserPath.Text = "Open Links With..."; | ||||||
|             //  |             //  | ||||||
|             // comboBoxCustomBrowser |             // comboBoxBrowserPath | ||||||
|             //  |             //  | ||||||
|             this.comboBoxCustomBrowser.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |             this.comboBoxBrowserPath.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | ||||||
|             this.comboBoxCustomBrowser.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.comboBoxBrowserPath.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.comboBoxCustomBrowser.FormattingEnabled = true; |             this.comboBoxBrowserPath.FormattingEnabled = true; | ||||||
|             this.comboBoxCustomBrowser.Location = new System.Drawing.Point(5, 1); |             this.comboBoxBrowserPath.Location = new System.Drawing.Point(5, 126); | ||||||
|             this.comboBoxCustomBrowser.Margin = new System.Windows.Forms.Padding(5, 1, 3, 0); |             this.comboBoxBrowserPath.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); | ||||||
|             this.comboBoxCustomBrowser.Name = "comboBoxCustomBrowser"; |             this.comboBoxBrowserPath.Name = "comboBoxBrowserPath"; | ||||||
|             this.comboBoxCustomBrowser.Size = new System.Drawing.Size(173, 23); |             this.comboBoxBrowserPath.Size = new System.Drawing.Size(173, 23); | ||||||
|             this.comboBoxCustomBrowser.TabIndex = 0; |             this.comboBoxBrowserPath.TabIndex = 5; | ||||||
|             //  |             //  | ||||||
|             // labelSearchEngine |             // labelSearchEngine | ||||||
|             //  |             //  | ||||||
|             this.labelSearchEngine.AutoSize = true; |             this.labelSearchEngine.AutoSize = true; | ||||||
|             this.labelSearchEngine.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); |             this.labelSearchEngine.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelSearchEngine.Location = new System.Drawing.Point(3, 389); |             this.labelSearchEngine.Location = new System.Drawing.Point(3, 164); | ||||||
|             this.labelSearchEngine.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |             this.labelSearchEngine.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); | ||||||
|             this.labelSearchEngine.Name = "labelSearchEngine"; |             this.labelSearchEngine.Name = "labelSearchEngine"; | ||||||
|             this.labelSearchEngine.Size = new System.Drawing.Size(82, 15); |             this.labelSearchEngine.Size = new System.Drawing.Size(82, 15); | ||||||
|             this.labelSearchEngine.TabIndex = 13; |             this.labelSearchEngine.TabIndex = 6; | ||||||
|             this.labelSearchEngine.Text = "Search Engine"; |             this.labelSearchEngine.Text = "Search Engine"; | ||||||
|             //  |             //  | ||||||
|             // comboBoxSearchEngine |             // comboBoxSearchEngine | ||||||
| @@ -344,58 +384,69 @@ | |||||||
|             this.comboBoxSearchEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |             this.comboBoxSearchEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | ||||||
|             this.comboBoxSearchEngine.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.comboBoxSearchEngine.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.comboBoxSearchEngine.FormattingEnabled = true; |             this.comboBoxSearchEngine.FormattingEnabled = true; | ||||||
|             this.comboBoxSearchEngine.Location = new System.Drawing.Point(5, 408); |             this.comboBoxSearchEngine.Location = new System.Drawing.Point(5, 183); | ||||||
|             this.comboBoxSearchEngine.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); |             this.comboBoxSearchEngine.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); | ||||||
|             this.comboBoxSearchEngine.Name = "comboBoxSearchEngine"; |             this.comboBoxSearchEngine.Name = "comboBoxSearchEngine"; | ||||||
|             this.comboBoxSearchEngine.Size = new System.Drawing.Size(173, 23); |             this.comboBoxSearchEngine.Size = new System.Drawing.Size(173, 23); | ||||||
|             this.comboBoxSearchEngine.TabIndex = 14; |             this.comboBoxSearchEngine.TabIndex = 7; | ||||||
|             //  |             //  | ||||||
|             // flowPanelRight |             // flowPanelRight | ||||||
|             //  |             //  | ||||||
|             this.flowPanelRight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  |             this.flowPanelRight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)  | ||||||
|             | System.Windows.Forms.AnchorStyles.Left))); |             | System.Windows.Forms.AnchorStyles.Left))); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.labelBrowserSettings); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.checkSmoothScrolling); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.checkTouchAdjustment); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.checkHardwareAcceleration); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.labelBrowserPath); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.comboBoxBrowserPath); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.labelSearchEngine); | ||||||
|  |             this.flowPanelRight.Controls.Add(this.comboBoxSearchEngine); | ||||||
|             this.flowPanelRight.Controls.Add(this.labelLocales); |             this.flowPanelRight.Controls.Add(this.labelLocales); | ||||||
|             this.flowPanelRight.Controls.Add(this.checkSpellCheck); |             this.flowPanelRight.Controls.Add(this.checkSpellCheck); | ||||||
|             this.flowPanelRight.Controls.Add(this.labelSpellCheckLanguage); |             this.flowPanelRight.Controls.Add(this.labelSpellCheckLanguage); | ||||||
|             this.flowPanelRight.Controls.Add(this.comboBoxSpellCheckLanguage); |             this.flowPanelRight.Controls.Add(this.comboBoxSpellCheckLanguage); | ||||||
|             this.flowPanelRight.Controls.Add(this.labelTranslationTarget); |             this.flowPanelRight.Controls.Add(this.labelTranslationTarget); | ||||||
|             this.flowPanelRight.Controls.Add(this.comboBoxTranslationTarget); |             this.flowPanelRight.Controls.Add(this.comboBoxTranslationTarget); | ||||||
|             this.flowPanelRight.Controls.Add(this.labelFirstDayOfWeek); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.comboBoxFirstDayOfWeek); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.labelExternalApplications); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.labelBrowserPath); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.panelCustomBrowser); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.labelVideoPlayerPath); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.panelCustomVideoPlayer); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.labelSearchEngine); |  | ||||||
|             this.flowPanelRight.Controls.Add(this.comboBoxSearchEngine); |  | ||||||
|             this.flowPanelRight.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; |             this.flowPanelRight.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; | ||||||
|             this.flowPanelRight.Location = new System.Drawing.Point(322, 9); |             this.flowPanelRight.Location = new System.Drawing.Point(322, 9); | ||||||
|             this.flowPanelRight.Name = "flowPanelRight"; |             this.flowPanelRight.Name = "flowPanelRight"; | ||||||
|             this.flowPanelRight.Size = new System.Drawing.Size(300, 462); |             this.flowPanelRight.Size = new System.Drawing.Size(300, 462); | ||||||
|             this.flowPanelRight.TabIndex = 2; |             this.flowPanelRight.TabIndex = 1; | ||||||
|             this.flowPanelRight.WrapContents = false; |             this.flowPanelRight.WrapContents = false; | ||||||
|             //  |             //  | ||||||
|  |             // checkHardwareAcceleration | ||||||
|  |             //  | ||||||
|  |             this.checkHardwareAcceleration.AutoSize = true; | ||||||
|  |             this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|  |             this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 74); | ||||||
|  |             this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); | ||||||
|  |             this.checkHardwareAcceleration.Name = "checkHardwareAcceleration"; | ||||||
|  |             this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19); | ||||||
|  |             this.checkHardwareAcceleration.TabIndex = 3; | ||||||
|  |             this.checkHardwareAcceleration.Text = "Hardware Acceleration"; | ||||||
|  |             this.checkHardwareAcceleration.UseVisualStyleBackColor = true; | ||||||
|  |             //  | ||||||
|             // labelLocales |             // labelLocales | ||||||
|             //  |             //  | ||||||
|             this.labelLocales.AutoSize = true; |             this.labelLocales.AutoSize = true; | ||||||
|             this.labelLocales.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |             this.labelLocales.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelLocales.Location = new System.Drawing.Point(0, 0); |             this.labelLocales.Location = new System.Drawing.Point(0, 236); | ||||||
|             this.labelLocales.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1); |             this.labelLocales.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1); | ||||||
|             this.labelLocales.Name = "labelLocales"; |             this.labelLocales.Name = "labelLocales"; | ||||||
|             this.labelLocales.Size = new System.Drawing.Size(67, 19); |             this.labelLocales.Size = new System.Drawing.Size(67, 19); | ||||||
|             this.labelLocales.TabIndex = 0; |             this.labelLocales.TabIndex = 8; | ||||||
|             this.labelLocales.Text = "LOCALES"; |             this.labelLocales.Text = "LOCALES"; | ||||||
|             //  |             //  | ||||||
|             // checkSpellCheck |             // checkSpellCheck | ||||||
|             //  |             //  | ||||||
|             this.checkSpellCheck.AutoSize = true; |             this.checkSpellCheck.AutoSize = true; | ||||||
|             this.checkSpellCheck.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.checkSpellCheck.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.checkSpellCheck.Location = new System.Drawing.Point(6, 26); |             this.checkSpellCheck.Location = new System.Drawing.Point(6, 262); | ||||||
|             this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); |             this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); | ||||||
|             this.checkSpellCheck.Name = "checkSpellCheck"; |             this.checkSpellCheck.Name = "checkSpellCheck"; | ||||||
|             this.checkSpellCheck.Size = new System.Drawing.Size(125, 19); |             this.checkSpellCheck.Size = new System.Drawing.Size(125, 19); | ||||||
|             this.checkSpellCheck.TabIndex = 1; |             this.checkSpellCheck.TabIndex = 9; | ||||||
|             this.checkSpellCheck.Text = "Enable Spell Check"; |             this.checkSpellCheck.Text = "Enable Spell Check"; | ||||||
|             this.checkSpellCheck.UseVisualStyleBackColor = true; |             this.checkSpellCheck.UseVisualStyleBackColor = true; | ||||||
|             //  |             //  | ||||||
| @@ -403,11 +454,11 @@ | |||||||
|             //  |             //  | ||||||
|             this.labelSpellCheckLanguage.AutoSize = true; |             this.labelSpellCheckLanguage.AutoSize = true; | ||||||
|             this.labelSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); |             this.labelSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelSpellCheckLanguage.Location = new System.Drawing.Point(3, 59); |             this.labelSpellCheckLanguage.Location = new System.Drawing.Point(3, 295); | ||||||
|             this.labelSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |             this.labelSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); | ||||||
|             this.labelSpellCheckLanguage.Name = "labelSpellCheckLanguage"; |             this.labelSpellCheckLanguage.Name = "labelSpellCheckLanguage"; | ||||||
|             this.labelSpellCheckLanguage.Size = new System.Drawing.Size(123, 15); |             this.labelSpellCheckLanguage.Size = new System.Drawing.Size(123, 15); | ||||||
|             this.labelSpellCheckLanguage.TabIndex = 2; |             this.labelSpellCheckLanguage.TabIndex = 10; | ||||||
|             this.labelSpellCheckLanguage.Text = "Spell Check Language"; |             this.labelSpellCheckLanguage.Text = "Spell Check Language"; | ||||||
|             //  |             //  | ||||||
|             // comboBoxSpellCheckLanguage |             // comboBoxSpellCheckLanguage | ||||||
| @@ -415,21 +466,21 @@ | |||||||
|             this.comboBoxSpellCheckLanguage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |             this.comboBoxSpellCheckLanguage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | ||||||
|             this.comboBoxSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.comboBoxSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.comboBoxSpellCheckLanguage.FormattingEnabled = true; |             this.comboBoxSpellCheckLanguage.FormattingEnabled = true; | ||||||
|             this.comboBoxSpellCheckLanguage.Location = new System.Drawing.Point(5, 78); |             this.comboBoxSpellCheckLanguage.Location = new System.Drawing.Point(5, 314); | ||||||
|             this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); |             this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); | ||||||
|             this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage"; |             this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage"; | ||||||
|             this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(290, 23); |             this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(290, 23); | ||||||
|             this.comboBoxSpellCheckLanguage.TabIndex = 3; |             this.comboBoxSpellCheckLanguage.TabIndex = 11; | ||||||
|             //  |             //  | ||||||
|             // labelTranslationTarget |             // labelTranslationTarget | ||||||
|             //  |             //  | ||||||
|             this.labelTranslationTarget.AutoSize = true; |             this.labelTranslationTarget.AutoSize = true; | ||||||
|             this.labelTranslationTarget.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); |             this.labelTranslationTarget.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); | ||||||
|             this.labelTranslationTarget.Location = new System.Drawing.Point(3, 116); |             this.labelTranslationTarget.Location = new System.Drawing.Point(3, 352); | ||||||
|             this.labelTranslationTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |             this.labelTranslationTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); | ||||||
|             this.labelTranslationTarget.Name = "labelTranslationTarget"; |             this.labelTranslationTarget.Name = "labelTranslationTarget"; | ||||||
|             this.labelTranslationTarget.Size = new System.Drawing.Size(142, 15); |             this.labelTranslationTarget.Size = new System.Drawing.Size(142, 15); | ||||||
|             this.labelTranslationTarget.TabIndex = 4; |             this.labelTranslationTarget.TabIndex = 12; | ||||||
|             this.labelTranslationTarget.Text = "Bing Translator Language"; |             this.labelTranslationTarget.Text = "Bing Translator Language"; | ||||||
|             //  |             //  | ||||||
|             // comboBoxTranslationTarget |             // comboBoxTranslationTarget | ||||||
| @@ -437,114 +488,11 @@ | |||||||
|             this.comboBoxTranslationTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |             this.comboBoxTranslationTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | ||||||
|             this.comboBoxTranslationTarget.Font = new System.Drawing.Font("Segoe UI", 9F); |             this.comboBoxTranslationTarget.Font = new System.Drawing.Font("Segoe UI", 9F); | ||||||
|             this.comboBoxTranslationTarget.FormattingEnabled = true; |             this.comboBoxTranslationTarget.FormattingEnabled = true; | ||||||
|             this.comboBoxTranslationTarget.Location = new System.Drawing.Point(5, 135); |             this.comboBoxTranslationTarget.Location = new System.Drawing.Point(5, 371); | ||||||
|             this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); |             this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); | ||||||
|             this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget"; |             this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget"; | ||||||
|             this.comboBoxTranslationTarget.Size = new System.Drawing.Size(290, 23); |             this.comboBoxTranslationTarget.Size = new System.Drawing.Size(290, 23); | ||||||
|             this.comboBoxTranslationTarget.TabIndex = 5; |             this.comboBoxTranslationTarget.TabIndex = 13; | ||||||
|             //  |  | ||||||
|             // labelFirstDayOfWeek |  | ||||||
|             //  |  | ||||||
|             this.labelFirstDayOfWeek.AutoSize = true; |  | ||||||
|             this.labelFirstDayOfWeek.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); |  | ||||||
|             this.labelFirstDayOfWeek.Location = new System.Drawing.Point(3, 173); |  | ||||||
|             this.labelFirstDayOfWeek.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelFirstDayOfWeek.Name = "labelFirstDayOfWeek"; |  | ||||||
|             this.labelFirstDayOfWeek.Size = new System.Drawing.Size(125, 15); |  | ||||||
|             this.labelFirstDayOfWeek.TabIndex = 6; |  | ||||||
|             this.labelFirstDayOfWeek.Text = "First Day Of The Week"; |  | ||||||
|             //  |  | ||||||
|             // comboBoxFirstDayOfWeek |  | ||||||
|             //  |  | ||||||
|             this.comboBoxFirstDayOfWeek.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |  | ||||||
|             this.comboBoxFirstDayOfWeek.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.comboBoxFirstDayOfWeek.FormattingEnabled = true; |  | ||||||
|             this.comboBoxFirstDayOfWeek.Location = new System.Drawing.Point(5, 192); |  | ||||||
|             this.comboBoxFirstDayOfWeek.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); |  | ||||||
|             this.comboBoxFirstDayOfWeek.Name = "comboBoxFirstDayOfWeek"; |  | ||||||
|             this.comboBoxFirstDayOfWeek.Size = new System.Drawing.Size(173, 23); |  | ||||||
|             this.comboBoxFirstDayOfWeek.TabIndex = 7; |  | ||||||
|             //  |  | ||||||
|             // labelExternalApplications |  | ||||||
|             //  |  | ||||||
|             this.labelExternalApplications.AutoSize = true; |  | ||||||
|             this.labelExternalApplications.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); |  | ||||||
|             this.labelExternalApplications.Location = new System.Drawing.Point(0, 243); |  | ||||||
|             this.labelExternalApplications.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1); |  | ||||||
|             this.labelExternalApplications.Name = "labelExternalApplications"; |  | ||||||
|             this.labelExternalApplications.Size = new System.Drawing.Size(176, 19); |  | ||||||
|             this.labelExternalApplications.TabIndex = 8; |  | ||||||
|             this.labelExternalApplications.Text = "EXTERNAL APPLICATIONS"; |  | ||||||
|             //  |  | ||||||
|             // panelCustomBrowser |  | ||||||
|             //  |  | ||||||
|             this.panelCustomBrowser.Controls.Add(this.comboBoxCustomBrowser); |  | ||||||
|             this.panelCustomBrowser.Controls.Add(this.btnCustomBrowserChange); |  | ||||||
|             this.panelCustomBrowser.Location = new System.Drawing.Point(0, 293); |  | ||||||
|             this.panelCustomBrowser.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); |  | ||||||
|             this.panelCustomBrowser.Name = "panelCustomBrowser"; |  | ||||||
|             this.panelCustomBrowser.Size = new System.Drawing.Size(300, 24); |  | ||||||
|             this.panelCustomBrowser.TabIndex = 10; |  | ||||||
|             //  |  | ||||||
|             // btnCustomBrowserChange |  | ||||||
|             //  |  | ||||||
|             this.btnCustomBrowserChange.AutoSize = true; |  | ||||||
|             this.btnCustomBrowserChange.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.btnCustomBrowserChange.Location = new System.Drawing.Point(186, 0); |  | ||||||
|             this.btnCustomBrowserChange.Margin = new System.Windows.Forms.Padding(5, 0, 3, 0); |  | ||||||
|             this.btnCustomBrowserChange.Name = "btnCustomBrowserChange"; |  | ||||||
|             this.btnCustomBrowserChange.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnCustomBrowserChange.Size = new System.Drawing.Size(71, 25); |  | ||||||
|             this.btnCustomBrowserChange.TabIndex = 1; |  | ||||||
|             this.btnCustomBrowserChange.Text = "Change..."; |  | ||||||
|             this.btnCustomBrowserChange.UseVisualStyleBackColor = true; |  | ||||||
|             this.btnCustomBrowserChange.Visible = false; |  | ||||||
|             //  |  | ||||||
|             // labelVideoPlayerPath |  | ||||||
|             //  |  | ||||||
|             this.labelVideoPlayerPath.AutoSize = true; |  | ||||||
|             this.labelVideoPlayerPath.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); |  | ||||||
|             this.labelVideoPlayerPath.Location = new System.Drawing.Point(3, 332); |  | ||||||
|             this.labelVideoPlayerPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); |  | ||||||
|             this.labelVideoPlayerPath.Name = "labelVideoPlayerPath"; |  | ||||||
|             this.labelVideoPlayerPath.Size = new System.Drawing.Size(106, 15); |  | ||||||
|             this.labelVideoPlayerPath.TabIndex = 11; |  | ||||||
|             this.labelVideoPlayerPath.Text = "Play Videos With..."; |  | ||||||
|             //  |  | ||||||
|             // panelCustomVideoPlayer |  | ||||||
|             //  |  | ||||||
|             this.panelCustomVideoPlayer.Controls.Add(this.comboBoxCustomVideoPlayer); |  | ||||||
|             this.panelCustomVideoPlayer.Controls.Add(this.btnCustomVideoPlayerChange); |  | ||||||
|             this.panelCustomVideoPlayer.Location = new System.Drawing.Point(0, 350); |  | ||||||
|             this.panelCustomVideoPlayer.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); |  | ||||||
|             this.panelCustomVideoPlayer.Name = "panelCustomVideoPlayer"; |  | ||||||
|             this.panelCustomVideoPlayer.Size = new System.Drawing.Size(300, 24); |  | ||||||
|             this.panelCustomVideoPlayer.TabIndex = 12; |  | ||||||
|             //  |  | ||||||
|             // comboBoxCustomVideoPlayer |  | ||||||
|             //  |  | ||||||
|             this.comboBoxCustomVideoPlayer.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; |  | ||||||
|             this.comboBoxCustomVideoPlayer.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.comboBoxCustomVideoPlayer.FormattingEnabled = true; |  | ||||||
|             this.comboBoxCustomVideoPlayer.Location = new System.Drawing.Point(5, 1); |  | ||||||
|             this.comboBoxCustomVideoPlayer.Margin = new System.Windows.Forms.Padding(5, 1, 3, 0); |  | ||||||
|             this.comboBoxCustomVideoPlayer.Name = "comboBoxCustomVideoPlayer"; |  | ||||||
|             this.comboBoxCustomVideoPlayer.Size = new System.Drawing.Size(173, 23); |  | ||||||
|             this.comboBoxCustomVideoPlayer.TabIndex = 0; |  | ||||||
|             //  |  | ||||||
|             // btnCustomVideoPlayerChange |  | ||||||
|             //  |  | ||||||
|             this.btnCustomVideoPlayerChange.AutoSize = true; |  | ||||||
|             this.btnCustomVideoPlayerChange.Font = new System.Drawing.Font("Segoe UI", 9F); |  | ||||||
|             this.btnCustomVideoPlayerChange.Location = new System.Drawing.Point(186, 0); |  | ||||||
|             this.btnCustomVideoPlayerChange.Margin = new System.Windows.Forms.Padding(5, 0, 3, 0); |  | ||||||
|             this.btnCustomVideoPlayerChange.Name = "btnCustomVideoPlayerChange"; |  | ||||||
|             this.btnCustomVideoPlayerChange.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); |  | ||||||
|             this.btnCustomVideoPlayerChange.Size = new System.Drawing.Size(71, 25); |  | ||||||
|             this.btnCustomVideoPlayerChange.TabIndex = 1; |  | ||||||
|             this.btnCustomVideoPlayerChange.Text = "Change..."; |  | ||||||
|             this.btnCustomVideoPlayerChange.UseVisualStyleBackColor = true; |  | ||||||
|             this.btnCustomVideoPlayerChange.Visible = false; |  | ||||||
|             //  |             //  | ||||||
|             // panelSeparator |             // panelSeparator | ||||||
|             //  |             //  | ||||||
| @@ -555,7 +503,7 @@ | |||||||
|             this.panelSeparator.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0); |             this.panelSeparator.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0); | ||||||
|             this.panelSeparator.Name = "panelSeparator"; |             this.panelSeparator.Name = "panelSeparator"; | ||||||
|             this.panelSeparator.Size = new System.Drawing.Size(1, 480); |             this.panelSeparator.Size = new System.Drawing.Size(1, 480); | ||||||
|             this.panelSeparator.TabIndex = 1; |             this.panelSeparator.TabIndex = 2; | ||||||
|             //  |             //  | ||||||
|             // TabSettingsGeneral |             // TabSettingsGeneral | ||||||
|             //  |             //  | ||||||
| @@ -564,7 +512,6 @@ | |||||||
|             this.Controls.Add(this.panelSeparator); |             this.Controls.Add(this.panelSeparator); | ||||||
|             this.Controls.Add(this.flowPanelRight); |             this.Controls.Add(this.flowPanelRight); | ||||||
|             this.Controls.Add(this.flowPanelLeft); |             this.Controls.Add(this.flowPanelLeft); | ||||||
|             this.Font = TweetDuck.Controls.ControlExtensions.DefaultFont; |  | ||||||
|             this.Name = "TabSettingsGeneral"; |             this.Name = "TabSettingsGeneral"; | ||||||
|             this.Size = new System.Drawing.Size(631, 480); |             this.Size = new System.Drawing.Size(631, 480); | ||||||
|             ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); |             ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); | ||||||
| @@ -573,10 +520,6 @@ | |||||||
|             this.flowPanelLeft.PerformLayout(); |             this.flowPanelLeft.PerformLayout(); | ||||||
|             this.flowPanelRight.ResumeLayout(false); |             this.flowPanelRight.ResumeLayout(false); | ||||||
|             this.flowPanelRight.PerformLayout(); |             this.flowPanelRight.PerformLayout(); | ||||||
|             this.panelCustomBrowser.ResumeLayout(false); |  | ||||||
|             this.panelCustomBrowser.PerformLayout(); |  | ||||||
|             this.panelCustomVideoPlayer.ResumeLayout(false); |  | ||||||
|             this.panelCustomVideoPlayer.PerformLayout(); |  | ||||||
|             this.ResumeLayout(false); |             this.ResumeLayout(false); | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| @@ -591,6 +534,7 @@ | |||||||
|         private System.Windows.Forms.Label labelZoomValue; |         private System.Windows.Forms.Label labelZoomValue; | ||||||
|         private System.Windows.Forms.TrackBar trackBarZoom; |         private System.Windows.Forms.TrackBar trackBarZoom; | ||||||
|         private System.Windows.Forms.Timer zoomUpdateTimer; |         private System.Windows.Forms.Timer zoomUpdateTimer; | ||||||
|  |         private System.Windows.Forms.Label labelUI; | ||||||
|         private System.Windows.Forms.Panel panelZoom; |         private System.Windows.Forms.Panel panelZoom; | ||||||
|         private System.Windows.Forms.Label labelUpdates; |         private System.Windows.Forms.Label labelUpdates; | ||||||
|         private System.Windows.Forms.CheckBox checkBestImageQuality; |         private System.Windows.Forms.CheckBox checkBestImageQuality; | ||||||
| @@ -599,30 +543,25 @@ | |||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanelLeft; |         private System.Windows.Forms.FlowLayoutPanel flowPanelLeft; | ||||||
|         private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen; |         private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen; | ||||||
|         private System.Windows.Forms.Label labelBrowserPath; |         private System.Windows.Forms.Label labelBrowserPath; | ||||||
|         private System.Windows.Forms.ComboBox comboBoxCustomBrowser; |         private System.Windows.Forms.ComboBox comboBoxBrowserPath; | ||||||
|  |         private System.Windows.Forms.Label labelBrowserSettings; | ||||||
|         private System.Windows.Forms.CheckBox checkSmoothScrolling; |         private System.Windows.Forms.CheckBox checkSmoothScrolling; | ||||||
|         private System.Windows.Forms.Label labelSearchEngine; |         private System.Windows.Forms.Label labelSearchEngine; | ||||||
|         private System.Windows.Forms.ComboBox comboBoxSearchEngine; |         private System.Windows.Forms.ComboBox comboBoxSearchEngine; | ||||||
|  |         private System.Windows.Forms.CheckBox checkTouchAdjustment; | ||||||
|         private System.Windows.Forms.FlowLayoutPanel flowPanelRight; |         private System.Windows.Forms.FlowLayoutPanel flowPanelRight; | ||||||
|         private System.Windows.Forms.Panel panelSeparator; |         private System.Windows.Forms.Panel panelSeparator; | ||||||
|  |         private System.Windows.Forms.Label labelTray; | ||||||
|  |         private System.Windows.Forms.ComboBox comboBoxTrayType; | ||||||
|  |         private System.Windows.Forms.Label labelTrayIcon; | ||||||
|  |         private System.Windows.Forms.CheckBox checkTrayHighlight; | ||||||
|         private System.Windows.Forms.Label labelLocales; |         private System.Windows.Forms.Label labelLocales; | ||||||
|         private System.Windows.Forms.CheckBox checkSpellCheck; |         private System.Windows.Forms.CheckBox checkSpellCheck; | ||||||
|         private System.Windows.Forms.Label labelSpellCheckLanguage; |         private System.Windows.Forms.Label labelSpellCheckLanguage; | ||||||
|         private System.Windows.Forms.ComboBox comboBoxSpellCheckLanguage; |         private System.Windows.Forms.ComboBox comboBoxSpellCheckLanguage; | ||||||
|         private System.Windows.Forms.Label labelTranslationTarget; |         private System.Windows.Forms.Label labelTranslationTarget; | ||||||
|         private System.Windows.Forms.ComboBox comboBoxTranslationTarget; |         private System.Windows.Forms.ComboBox comboBoxTranslationTarget; | ||||||
|  |         private System.Windows.Forms.CheckBox checkHardwareAcceleration; | ||||||
|         private System.Windows.Forms.CheckBox checkFocusDmInput; |         private System.Windows.Forms.CheckBox checkFocusDmInput; | ||||||
|         private System.Windows.Forms.Panel panelCustomBrowser; |  | ||||||
|         private System.Windows.Forms.Button btnCustomBrowserChange; |  | ||||||
|         private System.Windows.Forms.Label labelVideoPlayerPath; |  | ||||||
|         private System.Windows.Forms.Panel panelCustomVideoPlayer; |  | ||||||
|         private System.Windows.Forms.ComboBox comboBoxCustomVideoPlayer; |  | ||||||
|         private System.Windows.Forms.Button btnCustomVideoPlayerChange; |  | ||||||
|         private System.Windows.Forms.Label labelExternalApplications; |  | ||||||
|         private System.Windows.Forms.Label labelFirstDayOfWeek; |  | ||||||
|         private System.Windows.Forms.ComboBox comboBoxFirstDayOfWeek; |  | ||||||
|         private System.Windows.Forms.Label labelUI; |  | ||||||
|         private System.Windows.Forms.CheckBox checkHideTweetsByNftUsers; |  | ||||||
|         private System.Windows.Forms.Label labelTweetDeckSettings; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user