Merge pull request #136 from informatik-ag-ngl/f/javafx
Replaced Swing with JavaFX
This commit is contained in:
commit
b068796f46
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,4 @@
|
||||
/target/
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
@ -128,3 +128,364 @@ org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
|
||||
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
|
||||
org.eclipse.jdt.core.compiler.release=disabled
|
||||
org.eclipse.jdt.core.compiler.source=11
|
||||
org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=true
|
||||
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=1
|
||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=true
|
||||
org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=true
|
||||
org.eclipse.jdt.core.formatter.align_with_spaces=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=84
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=20
|
||||
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
|
||||
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=84
|
||||
org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
|
||||
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true
|
||||
org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
|
||||
org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_header=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_html=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
|
||||
org.eclipse.jdt.core.formatter.comment.format_source_code=true
|
||||
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
|
||||
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
|
||||
org.eclipse.jdt.core.formatter.comment.indent_tag_description=false
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.line_length=80
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
|
||||
org.eclipse.jdt.core.formatter.compact_else_if=true
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
|
||||
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=true
|
||||
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
|
||||
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_empty_lines=false
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
|
||||
org.eclipse.jdt.core.formatter.indentation.size=4
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.join_lines_in_comments=false
|
||||
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
|
||||
org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_if_single_item
|
||||
org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never
|
||||
org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_if_empty
|
||||
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=true
|
||||
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never
|
||||
org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never
|
||||
org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_if_single_item
|
||||
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_always
|
||||
org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_if_empty
|
||||
org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_if_single_item
|
||||
org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=true
|
||||
org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=true
|
||||
org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never
|
||||
org.eclipse.jdt.core.formatter.lineSplit=150
|
||||
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0
|
||||
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=separate_lines_if_wrapped
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
|
||||
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
|
||||
org.eclipse.jdt.core.formatter.tabulation.char=tab
|
||||
org.eclipse.jdt.core.formatter.tabulation.size=4
|
||||
org.eclipse.jdt.core.formatter.text_block_indentation=0
|
||||
org.eclipse.jdt.core.formatter.use_on_off_tags=false
|
||||
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
|
||||
org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
|
||||
org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
|
||||
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
|
||||
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
|
||||
|
File diff suppressed because one or more lines are too long
3
.settings/org.hibernate.eclipse.console.prefs
Normal file
3
.settings/org.hibernate.eclipse.console.prefs
Normal file
@ -0,0 +1,3 @@
|
||||
default.configuration=
|
||||
eclipse.preferences.version=1
|
||||
hibernate3.enabled=false
|
12
pom.xml
12
pom.xml
@ -28,7 +28,17 @@
|
||||
<dependency>
|
||||
<groupId>com.github.informatik-ag-ngl</groupId>
|
||||
<artifactId>envoy-common</artifactId>
|
||||
<version>develop-SNAPSHOT</version>
|
||||
<version>f~groups-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>11.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>11.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -1,177 +0,0 @@
|
||||
package envoy.client;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.net.WriteProxy;
|
||||
import envoy.client.ui.StatusTrayIcon;
|
||||
import envoy.client.ui.container.ChatWindow;
|
||||
import envoy.client.ui.container.LoginDialog;
|
||||
import envoy.data.Config;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.User.UserStatus;
|
||||
import envoy.exception.EnvoyException;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Starts the Envoy client and prompts the user to enter their name.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Startup.java</strong><br>
|
||||
* Created: <strong>12 Oct 2019</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public class Startup {
|
||||
|
||||
private static ChatWindow chatWindow;
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
|
||||
|
||||
/**
|
||||
* Loads the application by first loading the configuration, then acquiring a
|
||||
* user name and connecting to the server. If the server cannot be reached,
|
||||
* offline mode is entered if possible. After that, a {@link ChatWindow}
|
||||
* instance is initialized and then displayed to the user. Upon application
|
||||
* exit, settings and the local database are saved.
|
||||
*
|
||||
* @param args the command line arguments may contain configuration parameters
|
||||
* and are parsed by the {@link Config} class
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
ClientConfig config = ClientConfig.getInstance();
|
||||
SwingUtilities.invokeLater(() -> chatWindow = new ChatWindow());
|
||||
|
||||
try {
|
||||
// Load the configuration from client.properties first
|
||||
Properties properties = new Properties();
|
||||
properties.load(Startup.class.getClassLoader().getResourceAsStream("client.properties"));
|
||||
config.load(properties);
|
||||
|
||||
// Override configuration values with command line arguments
|
||||
if (args.length > 0) config.load(args);
|
||||
|
||||
// Check if all mandatory configuration values have been initialized
|
||||
if (!config.isInitialized()) throw new EnvoyException("Configuration is not fully initialized");
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(null, "Error loading configuration values:\n" + e, "Configuration error", JOptionPane.ERROR_MESSAGE);
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Setup logger for the envoy package
|
||||
EnvoyLog.initialize(config);
|
||||
EnvoyLog.attach("envoy");
|
||||
EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier());
|
||||
EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier());
|
||||
|
||||
// Initialize the local database
|
||||
LocalDB localDB;
|
||||
if (config.isIgnoreLocalDB()) {
|
||||
localDB = new TransientLocalDB();
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Ignoring local database.\nMessages will not be saved!",
|
||||
"Local database warning",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
} else try {
|
||||
localDB = new PersistentLocalDB(new File(config.getHomeDirectory(), config.getLocalDB().getPath()));
|
||||
} catch (IOException e3) {
|
||||
logger.log(Level.SEVERE, "Could not initialize local database", e3);
|
||||
JOptionPane.showMessageDialog(null, "Could not initialize local database!\n" + e3, "Local database error", JOptionPane.ERROR_MESSAGE);
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize client and unread message cache
|
||||
Client client = new Client();
|
||||
Cache<Message> cache = new Cache<>();
|
||||
|
||||
// Try to connect to the server
|
||||
new LoginDialog(client, localDB, cache);
|
||||
SwingUtilities.invokeLater(() -> chatWindow.setVisible(true));
|
||||
|
||||
// Set client user in local database
|
||||
localDB.setUser(client.getSender());
|
||||
|
||||
// Initialize chats in local database
|
||||
try {
|
||||
localDB.initializeUserStorage();
|
||||
localDB.loadUserData();
|
||||
} catch (FileNotFoundException e) {
|
||||
// The local database file has not yet been created, probably first login
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Error while loading local database: " + e + "\nChats will not be stored locally.",
|
||||
"Local DB error",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
|
||||
// Initialize write proxy
|
||||
final WriteProxy writeProxy = client.createWriteProxy(localDB);
|
||||
|
||||
if (client.isOnline()) {
|
||||
|
||||
// Save all users to the local database and flush cache
|
||||
localDB.setUsers(client.getUsers());
|
||||
writeProxy.flushCache();
|
||||
} else
|
||||
// Set all contacts to offline mode
|
||||
localDB.getUsers().values().stream().filter(u -> u != localDB.getUser()).forEach(u -> u.setStatus(UserStatus.OFFLINE));
|
||||
|
||||
// Display ChatWindow and StatusTrayIcon
|
||||
EventQueue.invokeLater(() -> {
|
||||
try {
|
||||
chatWindow.initContent(client, localDB, writeProxy);
|
||||
|
||||
// Relay unread messages from cache
|
||||
if (cache != null && client.isOnline()) cache.relay();
|
||||
|
||||
try {
|
||||
new StatusTrayIcon(chatWindow).show();
|
||||
|
||||
// If the tray icon is supported and corresponding settings is set, hide the
|
||||
// chat window on close
|
||||
Settings.getInstance()
|
||||
.getItems()
|
||||
.get("onCloseMode")
|
||||
.setChangeHandler((onCloseMode) -> chatWindow
|
||||
.setDefaultCloseOperation((Boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE));
|
||||
} catch (EnvoyException e) {
|
||||
logger.warning("The StatusTrayIcon is not supported on this platform!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
// Save Settings and PersistentLocalDB on shutdown
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
logger.info("Closing connection...");
|
||||
client.close();
|
||||
|
||||
logger.info("Saving local database and settings...");
|
||||
localDB.save();
|
||||
Settings.getInstance().save();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Unable to save local files", e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
@ -2,12 +2,12 @@ package envoy.client.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import envoy.client.net.WriteProxy;
|
||||
import envoy.client.ui.list.Model;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.*;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.data.User;
|
||||
import envoy.event.MessageStatusChangeEvent;
|
||||
|
||||
/**
|
||||
@ -23,12 +23,12 @@ import envoy.event.MessageStatusChangeEvent;
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public class Chat implements Serializable {
|
||||
public final class Chat implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
private final Contact recipient;
|
||||
private final List<Message> messages = new ArrayList<>();
|
||||
|
||||
private final User recipient;
|
||||
private final Model<Message> model = new Model<>();
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Provides the list of messages that the recipient receives.<br>
|
||||
@ -37,15 +37,10 @@ public class Chat implements Serializable {
|
||||
* @param recipient the user who receives the messages
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public Chat(User recipient) { this.recipient = recipient; }
|
||||
public Chat(Contact recipient) { this.recipient = recipient; }
|
||||
|
||||
/**
|
||||
* Appends a message to the bottom of this chat
|
||||
*
|
||||
* @param message the message to append
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public void appendMessage(Message message) { model.add(message); }
|
||||
@Override
|
||||
public String toString() { return String.format("Chat[recipient=%s,messages=%d]", recipient, messages.size()); }
|
||||
|
||||
/**
|
||||
* Sets the status of all chat messages received from the recipient to
|
||||
@ -59,8 +54,8 @@ public class Chat implements Serializable {
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void read(WriteProxy writeProxy) throws IOException {
|
||||
for (int i = model.size() - 1; i >= 0; --i) {
|
||||
final Message m = model.get(i);
|
||||
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||
final Message m = messages.get(i);
|
||||
if (m.getSenderID() == recipient.getID()) if (m.getStatus() == MessageStatus.READ) break;
|
||||
else {
|
||||
m.setStatus(MessageStatus.READ);
|
||||
@ -74,17 +69,29 @@ public class Chat implements Serializable {
|
||||
* the status {@code READ}
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public boolean isUnread() { return !model.isEmpty() && model.get(model.size() - 1).getStatus() != MessageStatus.READ; }
|
||||
public boolean isUnread() { return !messages.isEmpty() && messages.get(messages.size() - 1).getStatus() != MessageStatus.READ; }
|
||||
|
||||
/**
|
||||
* @return all messages in the current chat
|
||||
* @since Envoy Client v0.1-alpha
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Model<Message> getModel() { return model; }
|
||||
public List<Message> getMessages() { return messages; }
|
||||
|
||||
/**
|
||||
* @return the recipient of a message
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public User getRecipient() { return recipient; }
|
||||
public Contact getRecipient() { return recipient; }
|
||||
|
||||
/**
|
||||
* @return whether this {@link Chat} points at a {@link User}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public boolean isUserChat() { return recipient instanceof User; }
|
||||
|
||||
/**
|
||||
* @return whether this {@link Chat} points at a {@link Group}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public boolean isGroupChat() { return recipient instanceof Group; }
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package envoy.client.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ -110,11 +109,5 @@ public class ClientConfig extends Config {
|
||||
* the registration option
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public LoginCredentials getLoginCredentials() {
|
||||
try {
|
||||
return new LoginCredentials(getUser(), getPassword(), false);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public LoginCredentials getLoginCredentials() { return new LoginCredentials(getUser(), getPassword(), false); }
|
||||
}
|
||||
|
@ -2,10 +2,10 @@ package envoy.client.data;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import envoy.data.IDGenerator;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.User;
|
||||
import envoy.data.*;
|
||||
import envoy.event.GroupResizeEvent;
|
||||
import envoy.event.MessageStatusChangeEvent;
|
||||
import envoy.event.NameChangeEvent;
|
||||
|
||||
/**
|
||||
* Stores information about the current {@link User} and their {@link Chat}s.
|
||||
@ -21,7 +21,7 @@ import envoy.event.MessageStatusChangeEvent;
|
||||
public abstract class LocalDB {
|
||||
|
||||
protected User user;
|
||||
protected Map<String, User> users = new HashMap<>();
|
||||
protected Map<String, Contact> users = new HashMap<>();
|
||||
protected List<Chat> chats = new ArrayList<>();
|
||||
protected IDGenerator idGenerator;
|
||||
protected Cache<Message> messageCache = new Cache<>();
|
||||
@ -71,12 +71,12 @@ public abstract class LocalDB {
|
||||
* user names as keys
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Map<String, User> getUsers() { return users; }
|
||||
public Map<String, Contact> getUsers() { return users; }
|
||||
|
||||
/**
|
||||
* @param users the users to set
|
||||
*/
|
||||
public void setUsers(Map<String, User> users) { this.users = users; }
|
||||
public void setUsers(Map<String, Contact> users) { this.users = users; }
|
||||
|
||||
/**
|
||||
* @return all saved {@link Chat} objects that list the client user as the
|
||||
@ -153,9 +153,41 @@ public abstract class LocalDB {
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Message getMessage(long id) {
|
||||
for (Chat c : chats)
|
||||
for (Message m : c.getModel())
|
||||
if (m.getID() == id) return m;
|
||||
return null;
|
||||
return chats.stream().map(Chat::getMessages).flatMap(List::stream).filter(m -> m.getID() == id).findAny().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a contact name change if the corresponding contact is present.
|
||||
*
|
||||
* @param event the {@link NameChangeEvent} to process
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void replaceContactName(NameChangeEvent event) {
|
||||
chats.stream().map(Chat::getRecipient).filter(c -> c.getID() == event.getID()).findAny().ifPresent(c -> c.setName(event.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a group resize operation if the corresponding group is present.
|
||||
*
|
||||
* @param event the {@link GroupResizeEvent} to process
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void updateGroup(GroupResizeEvent event) {
|
||||
chats.stream()
|
||||
.map(Chat::getRecipient)
|
||||
.filter(Group.class::isInstance)
|
||||
.filter(g -> g.getID() == event.getGroupID() && g.getID() != user.getID())
|
||||
.map(Group.class::cast)
|
||||
.findAny()
|
||||
.ifPresent(group -> {
|
||||
switch (event.getOperation()) {
|
||||
case ADD:
|
||||
group.getContacts().add(event.get());
|
||||
break;
|
||||
case REMOVE:
|
||||
group.getContacts().remove(event.get());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -40,18 +40,18 @@ public class PersistentLocalDB extends LocalDB {
|
||||
* Constructs an empty local database. To serialize any chats to the file
|
||||
* system, call {@link PersistentLocalDB#initializeUserStorage()}.
|
||||
*
|
||||
* @param localDbDir the directory in which to store users and chats
|
||||
* @param localDBDir the directory in which to store users and chats
|
||||
* @throws IOException if the PersistentLocalDB could not be initialized
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public PersistentLocalDB(File localDbDir) throws IOException {
|
||||
localDBDir = localDbDir;
|
||||
public PersistentLocalDB(File localDBDir) throws IOException {
|
||||
this.localDBDir = localDBDir;
|
||||
|
||||
// Initialize local database directory
|
||||
if (localDbDir.exists() && !localDbDir.isDirectory())
|
||||
throw new IOException(String.format("LocalDbDir '%s' is not a directory!", localDbDir.getAbsolutePath()));
|
||||
usersFile = new File(localDbDir, "users.db");
|
||||
idGeneratorFile = new File(localDbDir, "id_generator.db");
|
||||
if (localDBDir.exists() && !localDBDir.isDirectory())
|
||||
throw new IOException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath()));
|
||||
usersFile = new File(localDBDir, "users.db");
|
||||
idGeneratorFile = new File(localDBDir, "id_generator.db");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,8 +6,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import envoy.client.ui.Color;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.util.SerializationUtils;
|
||||
|
||||
/**
|
||||
@ -28,26 +26,20 @@ public class Settings {
|
||||
|
||||
// Actual settings accessible by the rest of the application
|
||||
private Map<String, SettingsItem<?>> items;
|
||||
private Map<String, Theme> themes;
|
||||
|
||||
/**
|
||||
* Settings are stored in this file.
|
||||
*/
|
||||
private static final File settingsFile = new File(ClientConfig.getInstance().getHomeDirectory(), "settings.ser");
|
||||
|
||||
/**
|
||||
* User-defined themes are stored inside this file.
|
||||
*/
|
||||
private static final File themeFile = new File(ClientConfig.getInstance().getHomeDirectory(), "themes.ser");
|
||||
|
||||
/**
|
||||
* Singleton instance of this class.
|
||||
*/
|
||||
private static Settings settings = new Settings();
|
||||
|
||||
/**
|
||||
* The way to instantiate the settings.
|
||||
* Is set to private to deny other instances of that object.
|
||||
* The way to instantiate the settings. Is set to private to deny other
|
||||
* instances of that object.
|
||||
*
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
@ -59,22 +51,6 @@ public class Settings {
|
||||
items = new HashMap<>();
|
||||
}
|
||||
supplementDefaults();
|
||||
|
||||
// Load themes from theme file
|
||||
try {
|
||||
themes = SerializationUtils.read(themeFile, HashMap.class);
|
||||
} catch (ClassNotFoundException | IOException e1) {
|
||||
themes = new HashMap<>();
|
||||
setCurrentTheme("dark");
|
||||
}
|
||||
|
||||
// Load standard themes not defined in the themes file
|
||||
themes.put("dark",
|
||||
new Theme("dark", Color.black, Color.darkGray, Color.white, new Color(165, 60, 232), Color.white, Color.orange, Color.blue,
|
||||
Color.white, Color.white));
|
||||
themes.put("light",
|
||||
new Theme("light", new Color(235, 235, 235), Color.white, Color.white, Color.darkGray, Color.black, Color.orange, Color.darkGray,
|
||||
Color.black, Color.black));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,46 +64,29 @@ public class Settings {
|
||||
/**
|
||||
* Updates the preferences when the save button is clicked.
|
||||
*
|
||||
* @throws IOException if an error occurs while saving the themes to the theme
|
||||
* file
|
||||
* @throws IOException if an error occurs while saving the themes
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
|
||||
// Save settings to settings file
|
||||
SerializationUtils.write(settingsFile, items);
|
||||
|
||||
// Save themes to theme file
|
||||
SerializationUtils.write(themeFile, themes);
|
||||
}
|
||||
|
||||
private void supplementDefaults() {
|
||||
items.putIfAbsent("enterToSend", new SettingsItem<>(true, "Enter to send", "Sends a message by pressing the enter key."));
|
||||
items.putIfAbsent("onCloseMode", new SettingsItem<>(true, "Hide on close", "Hides the chat window when it is closed."));
|
||||
items.putIfAbsent("currentTheme", new SettingsItem<>("dark", null));
|
||||
items.putIfAbsent("currentTheme", new SettingsItem<>("dark", "Current Theme Name", "The name of the currently selected theme."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new theme to the theme map.
|
||||
*
|
||||
* @param theme the {@link Theme} to add
|
||||
* @return the name of the currently active theme
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public void addNewThemeToMap(Theme theme) { getThemes().put(theme.getThemeName(), theme); }
|
||||
public String getCurrentTheme() { return (String) items.get("currentTheme").get(); }
|
||||
|
||||
/**
|
||||
* @return the name of the currently active {@link Theme}
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public String getCurrentThemeName() { return (String) items.get("currentTheme").get(); }
|
||||
|
||||
/**
|
||||
* @return the currently active {@link Theme}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Theme getCurrentTheme() { return getTheme(getCurrentThemeName()); }
|
||||
|
||||
/**
|
||||
* Sets the name of the current {@link Theme}.
|
||||
* Sets the name of the current theme.
|
||||
*
|
||||
* @param themeName the name to set
|
||||
* @since Envoy Client v0.2-alpha
|
||||
@ -175,25 +134,4 @@ public class Settings {
|
||||
* @param items the items to set
|
||||
*/
|
||||
public void setItems(Map<String, SettingsItem<?>> items) { this.items = items; }
|
||||
|
||||
/**
|
||||
* @return a {@code Map<String, Theme>} of all themes with their names as keys
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Map<String, Theme> getThemes() { return themes; }
|
||||
|
||||
/**
|
||||
* Sets the {@code Map<String, Theme>} of all themes with their names as keys
|
||||
*
|
||||
* @param themes the theme map to set
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public void setThemes(Map<String, Theme> themes) { this.themes = themes; }
|
||||
|
||||
/**
|
||||
* @param themeName the name of the {@link Theme} to get
|
||||
* @return the {@link Theme} with the specified name
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public Theme getTheme(String themeName) { return themes.get(themeName); }
|
||||
}
|
||||
|
@ -1,19 +1,15 @@
|
||||
package envoy.client.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import envoy.client.ui.primary.PrimaryToggleSwitch;
|
||||
|
||||
/**
|
||||
* Encapsulates a persistent value that is directly or indirectly mutable by the
|
||||
* user.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-clientChess</strong><br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsItem.java</strong><br>
|
||||
* Created: <strong>23.12.2019</strong><br>
|
||||
*
|
||||
@ -24,18 +20,11 @@ import envoy.client.ui.primary.PrimaryToggleSwitch;
|
||||
public class SettingsItem<T> implements Serializable {
|
||||
|
||||
private T value;
|
||||
private Class<? extends JComponent> componentClass;
|
||||
private String userFriendlyName, description;
|
||||
|
||||
transient private Consumer<T> changeHandler;
|
||||
private transient Consumer<T> changeHandler;
|
||||
|
||||
private static final Map<Class<?>, Class<? extends JComponent>> componentClasses = new HashMap<>();
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
static {
|
||||
componentClasses.put(Boolean.class, PrimaryToggleSwitch.class);
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Initializes a {@link SettingsItem}. The default value's class will be mapped
|
||||
@ -48,39 +37,11 @@ public class SettingsItem<T> implements Serializable {
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public SettingsItem(T value, String userFriendlyName, String description) {
|
||||
this(value, componentClasses.get(value.getClass()));
|
||||
this.value = value;
|
||||
this.userFriendlyName = userFriendlyName;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a {@link SettingsItem}. The default value's class will be mapped
|
||||
* to a specific {@link JComponent}. The mapping can also be disables if this
|
||||
* parameter is {@code null}. In that case a {@link NullPointerException} will
|
||||
* be thrown if the method {@link SettingsItem#getComponent()} is called.
|
||||
*
|
||||
* @param value the default value
|
||||
* @param componentClass the class of the {@link JComponent} to represent this
|
||||
* {@link SettingsItem} with
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public SettingsItem(T value, Class<? extends JComponent> componentClass) {
|
||||
this.value = value;
|
||||
this.componentClass = componentClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an instance of the {@link JComponent} that represents this
|
||||
* {@link SettingsItem}
|
||||
* @throws ReflectiveOperationException if the component initialization failed
|
||||
* @throws SecurityException if the component initialization failed
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public JComponent getComponent() throws ReflectiveOperationException, SecurityException {
|
||||
if (componentClass == null) throw new NullPointerException("Component class is null");
|
||||
return componentClass.getConstructor(SettingsItem.class).newInstance(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value
|
||||
* @since Envoy Client v0.3-alpha
|
||||
@ -99,18 +60,6 @@ public class SettingsItem<T> implements Serializable {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the componentClass
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public Class<? extends JComponent> getComponentClass() { return componentClass; }
|
||||
|
||||
/**
|
||||
* @param componentClass the componentClass to set
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void setComponentClass(Class<? extends JComponent> componentClass) { this.componentClass = componentClass; }
|
||||
|
||||
/**
|
||||
* @return the userFriendlyName
|
||||
* @since Envoy Client v0.3-alpha
|
||||
|
@ -1,18 +0,0 @@
|
||||
package envoy.client.event;
|
||||
|
||||
import envoy.event.Event;
|
||||
|
||||
/**
|
||||
* This {@link Event} indicates that a handshake was completed successfully.
|
||||
*
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>HandshakeSuccessfulEvent.java</strong><br>
|
||||
* Created: <strong>8 Feb 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public class HandshakeSuccessfulEvent extends Event.Valueless {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package envoy.client.event;
|
||||
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.event.Event;
|
||||
|
||||
/**
|
||||
@ -11,16 +10,16 @@ import envoy.event.Event;
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public class ThemeChangeEvent extends Event<Theme> {
|
||||
public class ThemeChangeEvent extends Event<String> {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Initializes a {@link ThemeChangeEvent} conveying information about the change
|
||||
* of the {@link Theme} currently in use
|
||||
* of the theme currently in use.
|
||||
*
|
||||
* @param theme the new currently used {@link Theme} object
|
||||
* @param theme the name of the new theme
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public ThemeChangeEvent(Theme theme) { super(theme); }
|
||||
public ThemeChangeEvent(String theme) { super(theme); }
|
||||
}
|
||||
|
@ -5,17 +5,18 @@ import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.naming.TimeLimitExceededException;
|
||||
|
||||
import envoy.client.data.Cache;
|
||||
import envoy.client.data.ClientConfig;
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.client.event.SendEvent;
|
||||
import envoy.data.*;
|
||||
import envoy.event.*;
|
||||
import envoy.event.ContactOperationEvent.Operation;
|
||||
import envoy.event.contact.ContactOperationEvent;
|
||||
import envoy.event.contact.ContactSearchResult;
|
||||
import envoy.util.EnvoyLog;
|
||||
import envoy.util.SerializationUtils;
|
||||
|
||||
@ -41,12 +42,13 @@ public class Client implements Closeable {
|
||||
|
||||
// Asynchronously initialized during handshake
|
||||
private volatile User sender;
|
||||
private volatile Contacts contacts;
|
||||
private volatile Set<? extends Contact> contacts;
|
||||
private volatile boolean rejected;
|
||||
|
||||
// Configuration and logging
|
||||
// Configuration, logging and event management
|
||||
private static final ClientConfig config = ClientConfig.getInstance();
|
||||
private static final Logger logger = EnvoyLog.getLogger(Client.class);
|
||||
private static final EventBus eventBus = EventBus.getInstance();
|
||||
|
||||
/**
|
||||
* Enters the online mode by acquiring a user ID from the server. As a
|
||||
@ -58,29 +60,27 @@ public class Client implements Closeable {
|
||||
* @param receivedMessageCache a message cache containing all unread messages
|
||||
* from the server that can be relayed after
|
||||
* initialization
|
||||
* @throws TimeLimitExceededException if the server could not be reached
|
||||
* @throws TimeoutException if the server could not be reached
|
||||
* @throws IOException if the login credentials could not be
|
||||
* written
|
||||
* @throws InterruptedException if the current thread is interrupted while
|
||||
* waiting for the handshake response
|
||||
*/
|
||||
public void performHandshake(LoginCredentials credentials, Cache<Message> receivedMessageCache)
|
||||
throws TimeLimitExceededException, IOException, InterruptedException {
|
||||
throws TimeoutException, IOException, InterruptedException {
|
||||
if (online) throw new IllegalStateException("Handshake has already been performed successfully");
|
||||
|
||||
// Establish TCP connection
|
||||
logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
|
||||
logger.finer(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
|
||||
socket = new Socket(config.getServer(), config.getPort());
|
||||
logger.info("Successfully connected to server.");
|
||||
logger.fine("Successfully established TCP connection to server");
|
||||
|
||||
// Create message receiver
|
||||
// Create object receiver
|
||||
receiver = new Receiver(socket.getInputStream());
|
||||
|
||||
// Register user creation processor, contact list processor and message cache
|
||||
receiver.registerProcessor(User.class, sender -> this.sender = sender);
|
||||
receiver.registerProcessor(Contacts.class, contacts -> this.contacts = contacts);
|
||||
receiver.registerProcessor(User.class, sender -> { this.sender = sender; contacts = sender.getContacts(); });
|
||||
receiver.registerProcessor(Message.class, receivedMessageCache);
|
||||
receiver.registerProcessor(HandshakeRejectionEvent.class, evt -> { rejected = true; EventBus.getInstance().dispatch(evt); });
|
||||
receiver.registerProcessor(HandshakeRejectionEvent.class, evt -> { rejected = true; eventBus.dispatch(evt); });
|
||||
|
||||
rejected = false;
|
||||
|
||||
@ -91,8 +91,8 @@ public class Client implements Closeable {
|
||||
SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream());
|
||||
|
||||
// Wait for a maximum of five seconds to acquire the sender object
|
||||
long start = System.currentTimeMillis();
|
||||
while (sender == null || contacts == null) {
|
||||
final long start = System.currentTimeMillis();
|
||||
while (sender == null) {
|
||||
|
||||
// Quit immediately after handshake rejection
|
||||
// This method can then be called again
|
||||
@ -102,15 +102,16 @@ public class Client implements Closeable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - start > 5000) throw new TimeLimitExceededException("Did not log in after 5 seconds");
|
||||
if (System.currentTimeMillis() - start > 5000) throw new TimeoutException("Did not log in after 5 seconds");
|
||||
Thread.sleep(500);
|
||||
}
|
||||
|
||||
logger.info("Handshake completed.");
|
||||
online = true;
|
||||
|
||||
// Remove user creation processor
|
||||
// Remove all processors as they are only used during the handshake
|
||||
receiver.removeAllProcessors();
|
||||
|
||||
logger.info("Handshake completed.");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,17 +146,23 @@ public class Client implements Closeable {
|
||||
// Process message ID generation
|
||||
receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator);
|
||||
|
||||
// Process contact searches
|
||||
receiver.registerProcessor(ContactSearchResult.class, EventBus.getInstance()::dispatch);
|
||||
// Process name changes
|
||||
receiver.registerProcessor(NameChangeEvent.class, evt -> { localDB.replaceContactName(evt); eventBus.dispatch(evt); });
|
||||
|
||||
receiver.registerProcessor(Contacts.class,
|
||||
contacts -> EventBus.getInstance().dispatch(new ContactOperationEvent(contacts.getContacts().get(0), Operation.ADD)));
|
||||
// Process contact searches
|
||||
receiver.registerProcessor(ContactSearchResult.class, eventBus::dispatch);
|
||||
|
||||
// Process contact operations
|
||||
receiver.registerProcessor(ContactOperationEvent.class, eventBus::dispatch);
|
||||
|
||||
// Process group size changes
|
||||
receiver.registerProcessor(GroupResizeEvent.class, evt -> { localDB.updateGroup(evt); eventBus.dispatch(evt); });
|
||||
|
||||
// Send event
|
||||
EventBus.getInstance().register(SendEvent.class, evt -> {
|
||||
eventBus.register(SendEvent.class, evt -> {
|
||||
try {
|
||||
sendEvent(evt.get());
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
@ -212,10 +219,11 @@ public class Client implements Closeable {
|
||||
* user names as keys
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Map<String, User> getUsers() {
|
||||
public Map<String, Contact> getUsers() {
|
||||
checkOnline();
|
||||
Map<String, User> users = new HashMap<>();
|
||||
contacts.getContacts().forEach(u -> users.put(u.getName(), u));
|
||||
final Map<String, Contact> users = new HashMap<>();
|
||||
contacts.forEach(u -> users.put(u.getName(), u));
|
||||
users.put(sender.getName(), sender);
|
||||
return users;
|
||||
}
|
||||
|
||||
@ -224,7 +232,7 @@ public class Client implements Closeable {
|
||||
|
||||
private void writeObject(Object obj) throws IOException {
|
||||
checkOnline();
|
||||
logger.fine("Sending object " + obj);
|
||||
logger.fine("Sending " + obj);
|
||||
SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream());
|
||||
}
|
||||
|
||||
@ -239,10 +247,10 @@ public class Client implements Closeable {
|
||||
/**
|
||||
* Sets the client user which is used to send messages.
|
||||
*
|
||||
* @param sender the client user to set
|
||||
* @param clientUser the client user to set
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public void setSender(User sender) { this.sender = sender; }
|
||||
public void setSender(User clientUser) { this.sender = clientUser; }
|
||||
|
||||
/**
|
||||
* @return the {@link Receiver} used by this {@link Client}
|
||||
@ -259,11 +267,11 @@ public class Client implements Closeable {
|
||||
* @return the contacts of this {@link Client}
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public Contacts getContacts() { return contacts; }
|
||||
public Set<? extends Contact> getContacts() { return contacts; }
|
||||
|
||||
/**
|
||||
* @param contacts the contacts to set
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void setContacts(Contacts contacts) { this.contacts = contacts; }
|
||||
public void setContacts(Set<? extends Contact> contacts) { this.contacts = contacts; }
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ import envoy.util.EnvoyLog;
|
||||
import envoy.util.SerializationUtils;
|
||||
|
||||
/**
|
||||
* Receives objects from the server and passes them to processor objects based
|
||||
* on their class.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Receiver.java</strong><br>
|
||||
* Created: <strong>30.12.2019</strong><br>
|
||||
@ -32,12 +35,19 @@ public class Receiver extends Thread {
|
||||
* Creates an instance of {@link Receiver}.
|
||||
*
|
||||
* @param in the {@link InputStream} to parse objects from
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public Receiver(InputStream in) {
|
||||
super("Receiver");
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the receiver loop. When an object is read, it is passed to the
|
||||
* appropriate processor.
|
||||
*
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@ -54,7 +64,7 @@ public class Receiver extends Thread {
|
||||
|
||||
try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(objBytes))) {
|
||||
Object obj = oin.readObject();
|
||||
logger.fine("Received object " + obj);
|
||||
logger.fine("Received " + obj);
|
||||
|
||||
// Get appropriate processor
|
||||
@SuppressWarnings("rawtypes")
|
||||
@ -65,7 +75,7 @@ public class Receiver extends Thread {
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
logger.info("Connection probably closed by client. Exiting receiver thread...");
|
||||
// Connection probably closed by client.
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error on receiver thread", e);
|
||||
e.printStackTrace();
|
||||
@ -78,11 +88,14 @@ public class Receiver extends Thread {
|
||||
*
|
||||
* @param processorClass the object class accepted by the processor
|
||||
* @param processor the object processor
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public <T> void registerProcessor(Class<T> processorClass, Consumer<T> processor) { processors.put(processorClass, processor); }
|
||||
|
||||
/**
|
||||
* Removes all object processors registered at this {@link Receiver}.
|
||||
*
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void removeAllProcessors() { processors.clear(); }
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package envoy.client.net;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.data.User;
|
||||
import envoy.event.EventBus;
|
||||
import envoy.event.UserStatusChangeEvent;
|
||||
|
||||
@ -26,7 +27,7 @@ public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent
|
||||
|
||||
@Override
|
||||
public void accept(UserStatusChangeEvent evt) {
|
||||
localDB.getUsers().values().stream().filter(u -> u.getID() == evt.getID()).findFirst().get().setStatus(evt.get());
|
||||
localDB.getUsers().values().stream().filter(u -> u.getID() == evt.getID()).map(User.class::cast).findFirst().get().setStatus(evt.get());
|
||||
EventBus.getInstance().dispatch(evt);
|
||||
}
|
||||
}
|
||||
|
@ -1,114 +0,0 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
|
||||
/**
|
||||
* This class further develops {@link java.awt.Color} by adding extra methods
|
||||
* and more default colors.
|
||||
*
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Color.java</strong><br>
|
||||
* Created: <strong>23.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class Color extends java.awt.Color {
|
||||
|
||||
/**
|
||||
* The color white. In the default sRGB space.
|
||||
*/
|
||||
public static final Color white = new Color(255, 255, 255);
|
||||
|
||||
/**
|
||||
* The color light gray. In the default sRGB space.
|
||||
*/
|
||||
public static final Color lightGray = new Color(192, 192, 192);
|
||||
|
||||
/**
|
||||
* The color gray. In the default sRGB space.
|
||||
*/
|
||||
public static final Color gray = new Color(128, 128, 128);
|
||||
|
||||
/**
|
||||
* The color dark gray. In the default sRGB space.
|
||||
*/
|
||||
public static final Color darkGray = new Color(64, 64, 64);
|
||||
|
||||
/**
|
||||
* The color black. In the default sRGB space.
|
||||
*/
|
||||
public static final Color black = new Color(0, 0, 0);
|
||||
|
||||
/**
|
||||
* The color red. In the default sRGB space.
|
||||
*/
|
||||
public static final Color red = new Color(255, 0, 0);
|
||||
|
||||
/**
|
||||
* The color pink. In the default sRGB space.
|
||||
*/
|
||||
public static final Color pink = new Color(255, 175, 175);
|
||||
|
||||
/**
|
||||
* The color orange. In the default sRGB space.
|
||||
*/
|
||||
public static final Color orange = new Color(255, 200, 0);
|
||||
|
||||
/**
|
||||
* The color yellow. In the default sRGB space.
|
||||
*/
|
||||
public static final Color yellow = new Color(255, 255, 0);
|
||||
|
||||
/**
|
||||
* The color green. In the default sRGB space.
|
||||
*/
|
||||
public static final Color green = new Color(0, 255, 0);
|
||||
|
||||
/**
|
||||
* The color magenta. In the default sRGB space.
|
||||
*/
|
||||
public static final Color magenta = new Color(255, 0, 255);
|
||||
|
||||
/**
|
||||
* The color cyan. In the default sRGB space.
|
||||
*/
|
||||
public static final Color cyan = new Color(0, 255, 255);
|
||||
|
||||
/**
|
||||
* The color blue. In the default sRGB space.
|
||||
*/
|
||||
public static final Color blue = new Color(0, 0, 255);
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
public Color(java.awt.Color other) { this(other.getRGB()); }
|
||||
|
||||
public Color(int rgb) { super(rgb); }
|
||||
|
||||
public Color(int rgba, boolean hasalpha) { super(rgba, hasalpha); }
|
||||
|
||||
public Color(int r, int g, int b) { super(r, g, b); }
|
||||
|
||||
public Color(float r, float g, float b) { super(r, g, b); }
|
||||
|
||||
public Color(ColorSpace cspace, float[] components, float alpha) { super(cspace, components, alpha); }
|
||||
|
||||
public Color(int r, int g, int b, int a) { super(r, g, b, a); }
|
||||
|
||||
public Color(float r, float g, float b, float a) { super(r, g, b, a); }
|
||||
|
||||
/**
|
||||
* @return the inversion of this {@link Color} by replacing the red, green and
|
||||
* blue values by subtracting them form 255
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public Color invert() { return new Color(255 - getRed(), 255 - getGreen(), 255 - getBlue()); }
|
||||
|
||||
/**
|
||||
* @return the hex value of this {@link Color}
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public String toHex() { return String.format("#%02x%02x%02x", getRed(), getGreen(), getBlue()); }
|
||||
}
|
60
src/main/java/envoy/client/ui/ContactListCell.java
Normal file
60
src/main/java/envoy/client/ui/ContactListCell.java
Normal file
@ -0,0 +1,60 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import envoy.data.Contact;
|
||||
import envoy.data.Group;
|
||||
import envoy.data.User;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>UserListCell.java</strong><br>
|
||||
* Created: <strong>28.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContactListCell extends ListCell<Contact> {
|
||||
|
||||
/**
|
||||
* Displays the name of a contact. If the contact is a user, their online status
|
||||
* is displayed as well.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Contact contact, boolean empty) {
|
||||
super.updateItem(contact, empty);
|
||||
if (!empty && contact != null) {
|
||||
// the infoLabel displays specific contact info, i.e. status of a user or amount
|
||||
// of members in a group
|
||||
Label infoLabel = null;
|
||||
if (contact instanceof User) {
|
||||
// user specific info
|
||||
infoLabel = new Label(((User) contact).getStatus().toString());
|
||||
Color textColor = null;
|
||||
switch (((User) contact).getStatus()) {
|
||||
case ONLINE:
|
||||
textColor = Color.LIMEGREEN;
|
||||
break;
|
||||
case AWAY:
|
||||
textColor = Color.ORANGERED;
|
||||
break;
|
||||
case BUSY:
|
||||
textColor = Color.RED;
|
||||
break;
|
||||
case OFFLINE:
|
||||
textColor = Color.GRAY;
|
||||
break;
|
||||
}
|
||||
infoLabel.setTextFill(textColor);
|
||||
} else
|
||||
// group specific infos
|
||||
infoLabel = new Label(String.valueOf(((Group) contact).getContacts().size()) + " members");
|
||||
setGraphic(new VBox(new Label(contact.getName()), infoLabel));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* This class defines a menu that will be automatically called if
|
||||
* {@link MouseEvent#isPopupTrigger()} returns true for the parent component.
|
||||
* The user has the possibility to directly add actions to be performed when
|
||||
* clicking on the element with the selected String. Additionally, for each
|
||||
* element an {@link Icon} can be added, but it must not be.
|
||||
* If the key(text) of an element starts with one of the predefined values, a
|
||||
* special component will be called: either a {@link JRadioButtonMenuItem}, a
|
||||
* {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ContextMenu.java</strong><br>
|
||||
* Created: <strong>17 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContextMenu extends JPopupMenu {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* If a key starts with this String, a {@link JCheckBoxMenuItem} will be created
|
||||
*/
|
||||
public static final String checkboxMenuItem = "ChBoMI";
|
||||
/**
|
||||
* If a key starts with this String, a {@link JRadioButtonMenuItem} will be
|
||||
* created
|
||||
*/
|
||||
public static final String radioButtonMenuItem = "RaBuMI";
|
||||
/**
|
||||
* If a key starts with this String, a {@link JMenu} will be created
|
||||
*/
|
||||
public static final String subMenuItem = "SubMI";
|
||||
|
||||
private Map<String, ActionListener> items = new HashMap<>();
|
||||
private Map<String, Icon> icons = new HashMap<>();
|
||||
private Map<String, Integer> mnemonics = new HashMap<>();
|
||||
|
||||
private ButtonGroup radioButtonGroup = new ButtonGroup();
|
||||
private boolean built = false;
|
||||
|
||||
/**
|
||||
* @param parent the component which will call this
|
||||
* {@link ContextMenu}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu(Component parent) { setInvoker(parent); }
|
||||
|
||||
/**
|
||||
* @param label the string that a UI may use to display as a title
|
||||
* for the pop-up menu
|
||||
* @param parent the component which will call this
|
||||
* {@link ContextMenu}
|
||||
* @param itemsWithActions a map of all strings to be displayed with according
|
||||
* actions
|
||||
* @param itemIcons the icons to be displayed before a name, if wanted.
|
||||
* Only keys in here will have an Icon displayed. More
|
||||
* precisely, all keys here not included in the first
|
||||
* map will be thrown out.
|
||||
* @param itemMnemonics the keyboard shortcuts that need to be pressed to
|
||||
* automatically execute the {@link JMenuItem} with the
|
||||
* given text
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
|
||||
Map<String, Integer> itemMnemonics) {
|
||||
super(label);
|
||||
setInvoker(parent);
|
||||
this.items = (itemsWithActions != null) ? itemsWithActions : items;
|
||||
this.icons = (itemIcons != null) ? itemIcons : icons;
|
||||
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the PopupMenu to be displayed. Should only be used once all map
|
||||
* values have been set.
|
||||
*
|
||||
* @return this instance of {@link ContextMenu} to allow chaining behind the
|
||||
* constructor
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu build() {
|
||||
items.forEach((text, action) -> {
|
||||
// case radio button wanted
|
||||
AbstractButton item;
|
||||
if (text.startsWith(radioButtonMenuItem)) {
|
||||
item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
||||
radioButtonGroup.add(item);
|
||||
// case check box wanted
|
||||
} else if (text.startsWith(checkboxMenuItem))
|
||||
item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
||||
// case sub-menu wanted
|
||||
else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
|
||||
else // normal JMenuItem wanted
|
||||
item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
|
||||
item.addActionListener(action);
|
||||
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
|
||||
add(item);
|
||||
});
|
||||
if (getInvoker() != null) {
|
||||
getInvoker().addMouseListener(getShowingListener());
|
||||
built = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param label the string that a UI may use to display as a title for the
|
||||
* pop-up menu.
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu(String label) { super(label); }
|
||||
|
||||
private MouseAdapter getShowingListener() {
|
||||
return new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) { action(e); }
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) { action(e); }
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) { action(e); }
|
||||
|
||||
private void action(MouseEvent e) {
|
||||
if (!built) build();
|
||||
if (e.isPopupTrigger()) // hides the menu if already visible
|
||||
if (!isVisible()) show(e.getComponent(), e.getX(), e.getY());
|
||||
else setVisible(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all subcomponents of this menu.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void clear() {
|
||||
removeAll();
|
||||
items = new HashMap<>();
|
||||
icons = new HashMap<>();
|
||||
mnemonics = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the items
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Map<String, ActionListener> getItems() { return items; }
|
||||
|
||||
/**
|
||||
* @param items the items with the displayed text and the according action to
|
||||
* take once called
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setItems(Map<String, ActionListener> items) { this.items = items; }
|
||||
|
||||
/**
|
||||
* @return the icons
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Map<String, Icon> getIcons() { return icons; }
|
||||
|
||||
/**
|
||||
* @param icons the icons to set
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setIcons(Map<String, Icon> icons) { this.icons = icons; }
|
||||
|
||||
/**
|
||||
* @return the mnemonics (the keyboard shortcuts that automatically execute the
|
||||
* command for a {@link JMenuItem} with corresponding text)
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Map<String, Integer> getMnemonics() { return mnemonics; }
|
||||
|
||||
/**
|
||||
* @param mnemonics the keyboard shortcuts that need to be pressed to
|
||||
* automatically execute the {@link JMenuItem} with the given
|
||||
* text
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; }
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
/**
|
||||
* Provides static utility methods for loading icons from the resource
|
||||
@ -24,7 +22,17 @@ public class IconUtil {
|
||||
private IconUtil() {}
|
||||
|
||||
/**
|
||||
* Loads an icon from resource folder and scales it to a given size.
|
||||
* Loads an icon from the resource folder.
|
||||
*
|
||||
* @param path the path to the icon inside the resource folder
|
||||
* @return the icon
|
||||
* @throws IOException if the loading process failed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static Image load(String path) throws IOException { return new Image(IconUtil.class.getResource(path).toExternalForm()); }
|
||||
|
||||
/**
|
||||
* Loads an icon from the resource folder and scales it to a given size.
|
||||
*
|
||||
* @param path the path to the icon inside the resource folder
|
||||
* @param size the size to scale the icon to
|
||||
@ -32,8 +40,8 @@ public class IconUtil {
|
||||
* @throws IOException if the loading process failed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static ImageIcon load(String path, int size) throws IOException {
|
||||
return new ImageIcon(ImageIO.read(IconUtil.class.getResourceAsStream(path)).getScaledInstance(size, size, Image.SCALE_SMOOTH));
|
||||
public static Image load(String path, int size) throws IOException {
|
||||
return new Image(IconUtil.class.getResource(path).toExternalForm(), size, size, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,8 +59,8 @@ public class IconUtil {
|
||||
* @throws IOException if the loading process failed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static <T extends Enum<T>> EnumMap<T, ImageIcon> loadByEnum(Class<T> enumClass, int size) throws IOException {
|
||||
var icons = new EnumMap<T, ImageIcon>(enumClass);
|
||||
public static <T extends Enum<T>> EnumMap<T, Image> loadByEnum(Class<T> enumClass, int size) throws IOException {
|
||||
var icons = new EnumMap<T, Image>(enumClass);
|
||||
var path = "/icons/" + enumClass.getSimpleName().toLowerCase() + "/";
|
||||
for (var e : EnumSet.allOf(enumClass))
|
||||
icons.put(e, load(path + e.toString().toLowerCase() + ".png", size));
|
||||
|
54
src/main/java/envoy/client/ui/MessageListCell.java
Normal file
54
src/main/java/envoy/client/ui/MessageListCell.java
Normal file
@ -0,0 +1,54 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import envoy.data.Message;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
|
||||
/**
|
||||
* Displays a single message inside the message list.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>MessageListCell.java</strong><br>
|
||||
* Created: <strong>28.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class MessageListCell extends ListCell<Message> {
|
||||
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
private static Map<MessageStatus, Image> statusImages;
|
||||
|
||||
static {
|
||||
try {
|
||||
statusImages = IconUtil.loadByEnum(MessageStatus.class, 32);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the text, the data of creation and the status of a message.
|
||||
*
|
||||
* @since Envoy v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Message message, boolean empty) {
|
||||
super.updateItem(message, empty);
|
||||
setGraphic(!empty && message != null ? new HBox(
|
||||
new VBox(
|
||||
new Label(dateFormat.format(message.getCreationDate())),
|
||||
new Label(message.getText())),
|
||||
new Label("", new ImageView(statusImages.get(message.getStatus())))) : null);
|
||||
}
|
||||
}
|
152
src/main/java/envoy/client/ui/SceneContext.java
Normal file
152
src/main/java/envoy/client/ui/SceneContext.java
Normal file
@ -0,0 +1,152 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Stack;
|
||||
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.event.ThemeChangeEvent;
|
||||
import envoy.event.EventBus;
|
||||
|
||||
/**
|
||||
* Manages a stack of scenes. The most recently added scene is displayed inside
|
||||
* a stage. When a scene is removed from the stack, its predecessor is
|
||||
* displayed.
|
||||
* <p>
|
||||
* When a scene is loaded, the style sheet for the current theme is applied to
|
||||
* it.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SceneContext.java</strong><br>
|
||||
* Created: <strong>06.06.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class SceneContext {
|
||||
|
||||
/**
|
||||
* Contains information about different scenes and their FXML resource files.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static enum SceneInfo {
|
||||
|
||||
/**
|
||||
* The main scene in which chats are displayed.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
CHAT_SCENE("/fxml/ChatScene.fxml"),
|
||||
|
||||
/**
|
||||
* The scene in which settings are displayed.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
SETTINGS_SCENE("/fxml/SettingsScene.fxml"),
|
||||
|
||||
/**
|
||||
* The scene in which the contact search is displayed.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
CONTACT_SEARCH_SCENE("/fxml/ContactSearchScene.fxml"),
|
||||
|
||||
/**
|
||||
* The scene in which the login screen is displayed.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
LOGIN_SCENE("/fxml/LoginScene.fxml");
|
||||
|
||||
/**
|
||||
* The path to the FXML resource.
|
||||
*/
|
||||
public final String path;
|
||||
|
||||
SceneInfo(String path) { this.path = path; }
|
||||
}
|
||||
|
||||
private final Stage stage;
|
||||
private final FXMLLoader loader = new FXMLLoader();
|
||||
private final Stack<Scene> sceneStack = new Stack<>();
|
||||
|
||||
private static final Settings settings = Settings.getInstance();
|
||||
|
||||
/**
|
||||
* Initializes the scene context.
|
||||
*
|
||||
* @param stage the stage in which scenes will be displayed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public SceneContext(Stage stage) {
|
||||
this.stage = stage;
|
||||
EventBus.getInstance().register(ThemeChangeEvent.class, theme -> applyCSS());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a new scene specified by a scene info.
|
||||
*
|
||||
* @param sceneInfo specifies the scene to load
|
||||
* @throws RuntimeException if the loading process fails
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void load(SceneInfo sceneInfo) {
|
||||
loader.setRoot(null);
|
||||
loader.setController(null);
|
||||
|
||||
try {
|
||||
final var rootNode = (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path));
|
||||
final var scene = new Scene(rootNode);
|
||||
|
||||
sceneStack.push(scene);
|
||||
stage.setScene(scene);
|
||||
applyCSS();
|
||||
stage.show();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current scene and displays the previous one.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void pop() {
|
||||
sceneStack.pop();
|
||||
if (!sceneStack.isEmpty()) {
|
||||
stage.setScene(sceneStack.peek());
|
||||
applyCSS();
|
||||
}
|
||||
stage.show();
|
||||
}
|
||||
|
||||
private void applyCSS() {
|
||||
if (!sceneStack.isEmpty()) {
|
||||
final var styleSheets = stage.getScene().getStylesheets();
|
||||
final var themeCSS = "/css/" + settings.getCurrentTheme() + ".css";
|
||||
styleSheets.clear();
|
||||
styleSheets.addAll(getClass().getResource("/css/base.css").toExternalForm(), getClass().getResource(themeCSS).toExternalForm());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> the type of the controller
|
||||
* @return the controller used by the current scene
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public <T> T getController() { return loader.getController(); }
|
||||
|
||||
/**
|
||||
* @return the stage in which the scenes are displayed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Stage getStage() { return stage; }
|
||||
}
|
127
src/main/java/envoy/client/ui/Startup.java
Normal file
127
src/main/java/envoy/client/ui/Startup.java
Normal file
@ -0,0 +1,127 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.ui.SceneContext.SceneInfo;
|
||||
import envoy.client.ui.controller.LoginScene;
|
||||
import envoy.data.Message;
|
||||
import envoy.exception.EnvoyException;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Handles application startup and shutdown.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Startup.java</strong><br>
|
||||
* Created: <strong>26.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class Startup extends Application {
|
||||
|
||||
private LocalDB localDB;
|
||||
private Client client;
|
||||
private Cache<Message> cache;
|
||||
|
||||
private static final ClientConfig config = ClientConfig.getInstance();
|
||||
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
|
||||
|
||||
/**
|
||||
* Loads the configuration, initializes the client and the local database and
|
||||
* delegates the rest of the startup process to {@link LoginScene}.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
try {
|
||||
// Load the configuration from client.properties first
|
||||
final Properties properties = new Properties();
|
||||
properties.load(Startup.class.getClassLoader().getResourceAsStream("client.properties"));
|
||||
config.load(properties);
|
||||
|
||||
// Override configuration values with command line arguments
|
||||
final String[] args = getParameters().getRaw().toArray(new String[0]);
|
||||
if (args.length > 0) config.load(args);
|
||||
|
||||
// Check if all mandatory configuration values have been initialized
|
||||
if (!config.isInitialized()) throw new EnvoyException("Configuration is not fully initialized");
|
||||
} catch (final Exception e) {
|
||||
new Alert(AlertType.ERROR, "Error loading configuration values:\n" + e);
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Setup logger for the envoy package
|
||||
EnvoyLog.initialize(config);
|
||||
EnvoyLog.attach("envoy");
|
||||
EnvoyLog.setFileLevelBarrier(config.getFileLevelBarrier());
|
||||
EnvoyLog.setConsoleLevelBarrier(config.getConsoleLevelBarrier());
|
||||
|
||||
// Initialize the local database
|
||||
if (config.isIgnoreLocalDB()) {
|
||||
localDB = new TransientLocalDB();
|
||||
new Alert(AlertType.WARNING, "Ignoring local database.\nMessages will not be saved!").showAndWait();
|
||||
} else {
|
||||
try {
|
||||
localDB = new PersistentLocalDB(new File(config.getHomeDirectory(), config.getLocalDB().getPath()));
|
||||
} catch (final IOException e3) {
|
||||
logger.log(Level.SEVERE, "Could not initialize local database", e3);
|
||||
new Alert(AlertType.ERROR, "Could not initialize local database!\n" + e3).showAndWait();
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize client and unread message cache
|
||||
client = new Client();
|
||||
cache = new Cache<>();
|
||||
|
||||
stage.setTitle("Envoy");
|
||||
stage.getIcons().add(IconUtil.load("/icons/envoy_logo.png"));
|
||||
|
||||
final var sceneContext = new SceneContext(stage);
|
||||
sceneContext.load(SceneInfo.LOGIN_SCENE);
|
||||
sceneContext.<LoginScene>getController().initializeData(client, localDB, cache, sceneContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the client connection and saves the local database and settings.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
try {
|
||||
logger.info("Closing connection...");
|
||||
client.close();
|
||||
|
||||
logger.info("Saving local database and settings...");
|
||||
localDB.save();
|
||||
Settings.getInstance().save();
|
||||
} catch (final Exception e) {
|
||||
logger.log(Level.SEVERE, "Unable to save local files", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the application.
|
||||
*
|
||||
* @param args the command line arguments are processed by the
|
||||
* {@link ClientConfig}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static void main(String[] args) { launch(args); }
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Theme.java</strong><br>
|
||||
* Created: <strong>23 Nov 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public class Theme implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private String themeName;
|
||||
private Map<String, Color> colors = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Initializes a {@link Theme} with all colors relevant to the application GUI.
|
||||
*
|
||||
* @param themeName the name of the {@link Theme}
|
||||
* @param backgroundColor the background color
|
||||
* @param cellColor the cell color
|
||||
* @param interactableForegroundColor the color of interactable foreground UI
|
||||
* elements
|
||||
* @param interactableBackgroundColor the color of interactable background UI
|
||||
* elements
|
||||
* @param textColor the color normal text should be displayed
|
||||
* in
|
||||
* @param dateColorChat the color of chat message metadata
|
||||
* @param selectionColor the section color
|
||||
* @param typingMessageColor the color of currently typed messages
|
||||
* @param userNameColor the color of user names
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Theme(String themeName, Color backgroundColor, Color cellColor, Color interactableForegroundColor, Color interactableBackgroundColor,
|
||||
Color textColor, Color dateColorChat, Color selectionColor, Color typingMessageColor, Color userNameColor) {
|
||||
|
||||
this.themeName = themeName;
|
||||
|
||||
colors.put("backgroundColor", backgroundColor);
|
||||
colors.put("cellColor", cellColor);
|
||||
colors.put("interactableForegroundColor", interactableForegroundColor);
|
||||
colors.put("interactableBackgroundColor", interactableBackgroundColor);
|
||||
colors.put("textColor", textColor);
|
||||
colors.put("dateColorChat", dateColorChat);
|
||||
colors.put("selectionColor", selectionColor);
|
||||
colors.put("typingMessageColor", typingMessageColor);
|
||||
colors.put("userNameColor", userNameColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a {@link Theme} by copying all parameters except for the name
|
||||
* from another {@link Theme} instance.
|
||||
*
|
||||
* @param name the name of the {@link Theme}
|
||||
* @param other the {@link Theme} to copy
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Theme(String name, Theme other) {
|
||||
themeName = name;
|
||||
colors.putAll(other.colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a {@code Map<String, Color>} of all colors defined for this theme
|
||||
* with their names as keys
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Map<String, Color> getColors() { return colors; }
|
||||
|
||||
/**
|
||||
* @return name of the theme
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public String getThemeName() { return themeName; }
|
||||
|
||||
/**
|
||||
* @return interactableForegroundColor
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getInteractableForegroundColor() { return colors.get("interactableForegroundColor"); }
|
||||
|
||||
/**
|
||||
* @return the {@link Color} in which the text content of a message should be
|
||||
* displayed
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getTextColor() { return colors.get("textColor"); }
|
||||
|
||||
/**
|
||||
* @return the {@link Color} in which the creation date of a message should be
|
||||
* displayed
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getDateColor() { return colors.get("dateColorChat"); }
|
||||
|
||||
/**
|
||||
* @return selectionColor
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getSelectionColor() { return colors.get("selectionColor"); }
|
||||
|
||||
/**
|
||||
* @return typingMessageColor
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getTypingMessageColor() { return colors.get("typingMessageColor"); }
|
||||
|
||||
/**
|
||||
* @return backgroundColor
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getBackgroundColor() { return colors.get("backgroundColor"); }
|
||||
|
||||
/**
|
||||
* @return cellColor
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getCellColor() { return colors.get("cellColor"); }
|
||||
|
||||
/**
|
||||
* @return interactableBackgroundColor
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getInteractableBackgroundColor() { return colors.get("interactableBackgroundColor"); }
|
||||
|
||||
/**
|
||||
* @return userNameColor
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public Color getUserNameColor() { return colors.get("userNameColor"); }
|
||||
|
||||
/**
|
||||
* Sets the a specific {@link Color} in this theme to a new {@link Color}
|
||||
*
|
||||
* @param colorName the name of the {@link Color} to set
|
||||
* @param newColor the new {@link Color} to be set
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public void setColor(String colorName, Color newColor) { colors.put(colorName, newColor); }
|
||||
}
|
@ -1,687 +0,0 @@
|
||||
package envoy.client.ui.container;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import envoy.client.data.Chat;
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.event.MessageCreationEvent;
|
||||
import envoy.client.event.ThemeChangeEvent;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.net.WriteProxy;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.ui.list.ComponentList;
|
||||
import envoy.client.ui.list.ComponentList.SelectionMode;
|
||||
import envoy.client.ui.list.Model;
|
||||
import envoy.client.ui.list_component.ContactSearchComponent;
|
||||
import envoy.client.ui.list_component.MessageComponent;
|
||||
import envoy.client.ui.primary.PrimaryButton;
|
||||
import envoy.client.ui.primary.PrimaryScrollPane;
|
||||
import envoy.client.ui.primary.PrimaryTextArea;
|
||||
import envoy.client.ui.renderer.UserListRenderer;
|
||||
import envoy.client.ui.settings.SettingsScreen;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.data.MessageBuilder;
|
||||
import envoy.data.User;
|
||||
import envoy.event.*;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ChatWindow.java</strong><br>
|
||||
* Created: <strong>28 Sep 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public class ChatWindow extends JFrame {
|
||||
|
||||
/**
|
||||
* This integer defines the maximum amount of chars allowed per message.
|
||||
*
|
||||
* @since Envoy 0.1-beta
|
||||
*/
|
||||
public static final int MAX_MESSAGE_LENGTH = 200;
|
||||
|
||||
// User specific objects
|
||||
private Client client;
|
||||
private WriteProxy writeProxy;
|
||||
private LocalDB localDB;
|
||||
private Chat currentChat;
|
||||
|
||||
// GUI components
|
||||
private JPanel contentPane = new JPanel();
|
||||
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
|
||||
private JList<User> userList = new JList<>();
|
||||
private DefaultListModel<User> userListModel = new DefaultListModel<>();
|
||||
private ComponentList<Message> messageList = new ComponentList<>();
|
||||
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
|
||||
private JTextPane textPane = new JTextPane();
|
||||
private PrimaryButton postButton = new PrimaryButton("Post");
|
||||
private PrimaryButton settingsButton = new PrimaryButton("Settings");
|
||||
private JPopupMenu contextMenu;
|
||||
|
||||
// Contacts Header
|
||||
private JPanel contactsHeader = new JPanel();
|
||||
private JTextPane contactsDisplay = new JTextPane();
|
||||
private PrimaryButton addContact = new PrimaryButton("+");
|
||||
|
||||
// Search Contacts
|
||||
private final JPanel searchPane = new JPanel();
|
||||
private final PrimaryButton cancelButton = new PrimaryButton("x");
|
||||
private final PrimaryTextArea searchField = new PrimaryTextArea(space);
|
||||
private final PrimaryScrollPane scrollForPossibleContacts = new PrimaryScrollPane();
|
||||
private final Model<User> contactsModel = new Model<>();
|
||||
private final ComponentList<User> contactList = new ComponentList<User>().setRenderer(ContactSearchComponent::new);
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class);
|
||||
|
||||
// GUI component spacing
|
||||
private final static int space = 4;
|
||||
private static final Insets insets = new Insets(space, space, space, space);
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Initializes a {@link JFrame} with UI elements used to send and read messages
|
||||
* to different users.
|
||||
*
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public ChatWindow() {
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setBounds(100, 100, 600, 800);
|
||||
setMinimumSize(new Dimension(400, 300));
|
||||
setTitle("Envoy");
|
||||
setLocationRelativeTo(null);
|
||||
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
|
||||
|
||||
contentPane.setBorder(new EmptyBorder(space, space, space, space));
|
||||
setContentPane(contentPane);
|
||||
GridBagLayout gbl_contentPane = new GridBagLayout();
|
||||
gbl_contentPane.columnWidths = new int[] { 1, 1, 1 };
|
||||
gbl_contentPane.rowHeights = new int[] { 1, 1, 1, 1 };
|
||||
gbl_contentPane.columnWeights = new double[] { 0.03, 1.0, 0.1 };
|
||||
gbl_contentPane.rowWeights = new double[] { 0.03, 0.001, 1.0, 0.001 };
|
||||
contentPane.setLayout(gbl_contentPane);
|
||||
|
||||
messageList.setBorder(new EmptyBorder(space, space, space, space));
|
||||
messageList.setSelectionMode(SelectionMode.SINGLE);
|
||||
messageList.setSelectionHandler((message, comp, isSelected) -> {
|
||||
final var theme = Settings.getInstance().getCurrentTheme();
|
||||
comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
|
||||
|
||||
// ContextMenu
|
||||
Map<String, ActionListener> commands = Map.of("forward selected message", evt -> {
|
||||
final Message selectedMessage = messageList.getSingleSelectedElement();
|
||||
List<User> chosenContacts = ContactsChooserDialog
|
||||
.showForwardingDialog("Forward selected message to", null, selectedMessage, localDB.getUsers().values());
|
||||
if (chosenContacts != null && chosenContacts.size() > 0) forwardMessage(selectedMessage, chosenContacts.toArray(new User[0]));
|
||||
}, "copy", evt -> {
|
||||
// TODO should be enhanced to allow also copying of message attachments,
|
||||
// especially pictures
|
||||
StringSelection copy = new StringSelection(messageList.getSingleSelectedElement().getText());
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(copy, copy);
|
||||
// TODO insert implementation to edit and delete messages
|
||||
}, "delete", evt -> {}, "edit", evt -> {}, "quote", evt -> {});
|
||||
|
||||
if (isSelected) {
|
||||
contextMenu = new ContextMenu(null, comp, commands, null, null).build();
|
||||
contextMenu.show(comp, 0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
scrollPane.setViewportView(messageList);
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scrollPane.addComponentListener(new ComponentAdapter() {
|
||||
|
||||
// Update list elements when scroll pane (and thus list) is resized
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
messageList.setMaximumSize(new Dimension(scrollPane.getWidth(), Integer.MAX_VALUE));
|
||||
messageList.synchronizeModel();
|
||||
}
|
||||
});
|
||||
|
||||
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
|
||||
gbc_scrollPane.fill = GridBagConstraints.BOTH;
|
||||
gbc_scrollPane.gridwidth = 2;
|
||||
gbc_scrollPane.gridheight = 2;
|
||||
gbc_scrollPane.gridx = 1;
|
||||
gbc_scrollPane.gridy = 1;
|
||||
|
||||
gbc_scrollPane.insets = insets;
|
||||
|
||||
drawChatBox(gbc_scrollPane);
|
||||
|
||||
// MessageEnterTextArea
|
||||
messageEnterTextArea.addInputMethodListener(new InputMethodListener() {
|
||||
|
||||
@Override
|
||||
public void inputMethodTextChanged(InputMethodEvent event) {
|
||||
checkMessageTextLength();
|
||||
checkPostButton(messageEnterTextArea.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void caretPositionChanged(InputMethodEvent event) {}
|
||||
});
|
||||
|
||||
messageEnterTextArea.addKeyListener(new KeyAdapter() {
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER
|
||||
&& (Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0 || e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK)
|
||||
&& postButton.isEnabled())
|
||||
postMessage();
|
||||
// Checking if text is too long
|
||||
checkMessageTextLength();
|
||||
checkPostButton(messageEnterTextArea.getText());
|
||||
}
|
||||
});
|
||||
|
||||
GridBagConstraints gbc_messageEnterTextArea = new GridBagConstraints();
|
||||
gbc_messageEnterTextArea.fill = GridBagConstraints.BOTH;
|
||||
gbc_messageEnterTextArea.gridx = 1;
|
||||
gbc_messageEnterTextArea.gridy = 3;
|
||||
gbc_messageEnterTextArea.insets = insets;
|
||||
contentPane.add(messageEnterTextArea, gbc_messageEnterTextArea);
|
||||
|
||||
// Post Button
|
||||
GridBagConstraints gbc_postButton = new GridBagConstraints();
|
||||
gbc_postButton.fill = GridBagConstraints.BOTH;
|
||||
gbc_postButton.gridx = 2;
|
||||
gbc_postButton.gridy = 3;
|
||||
gbc_postButton.insets = insets;
|
||||
postButton.addActionListener((evt) -> { postMessage(); });
|
||||
postButton.setEnabled(false);
|
||||
contentPane.add(postButton, gbc_postButton);
|
||||
|
||||
// Settings Button
|
||||
GridBagConstraints gbc_moveSelectionSettingsButton = new GridBagConstraints();
|
||||
|
||||
gbc_moveSelectionSettingsButton.fill = GridBagConstraints.BOTH;
|
||||
gbc_moveSelectionSettingsButton.gridx = 2;
|
||||
gbc_moveSelectionSettingsButton.gridy = 0;
|
||||
|
||||
gbc_moveSelectionSettingsButton.insets = insets;
|
||||
|
||||
settingsButton.addActionListener(evt -> new SettingsScreen().setVisible(true));
|
||||
contentPane.add(settingsButton, gbc_moveSelectionSettingsButton);
|
||||
|
||||
// Partner name display
|
||||
textPane.setFont(new Font("Arial", Font.PLAIN, 20));
|
||||
textPane.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_partnerName = new GridBagConstraints();
|
||||
gbc_partnerName.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_partnerName.gridx = 1;
|
||||
gbc_partnerName.gridy = 0;
|
||||
|
||||
gbc_partnerName.insets = insets;
|
||||
contentPane.add(textPane, gbc_partnerName);
|
||||
|
||||
userList.setCellRenderer(new UserListRenderer());
|
||||
userList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
userList.addListSelectionListener((listSelectionEvent) -> {
|
||||
if (client != null && localDB != null && !listSelectionEvent.getValueIsAdjusting()) {
|
||||
final JList<User> selectedUserList = (JList<User>) listSelectionEvent.getSource();
|
||||
final User user = selectedUserList.getSelectedValue();
|
||||
|
||||
for (int i = 0; i < contentPane.getComponents().length; i++)
|
||||
if (contentPane.getComponent(i).equals(searchPane)) drawChatBox(gbc_scrollPane);
|
||||
if (user != null) {
|
||||
// Select current chat
|
||||
currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getID() == user.getID()).findFirst().get();
|
||||
|
||||
// Read current chat
|
||||
readCurrentChat();
|
||||
|
||||
// Set chat title
|
||||
textPane.setText(currentChat.getRecipient().getName());
|
||||
|
||||
// Update model and scroll down
|
||||
messageList.setModel(currentChat.getModel());
|
||||
scrollPane.setChatOpened(true);
|
||||
|
||||
messageList.synchronizeModel();
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
userList.setFont(new Font("Arial", Font.PLAIN, 17));
|
||||
userList.setBorder(new EmptyBorder(space, space, space, space));
|
||||
|
||||
GridBagConstraints gbc_userList = new GridBagConstraints();
|
||||
gbc_userList.fill = GridBagConstraints.VERTICAL;
|
||||
gbc_userList.gridx = 0;
|
||||
gbc_userList.gridy = 2;
|
||||
gbc_userList.gridheight = 2;
|
||||
gbc_userList.anchor = GridBagConstraints.PAGE_START;
|
||||
gbc_userList.insets = insets;
|
||||
|
||||
contentPane.add(userList, gbc_userList);
|
||||
contentPane.revalidate();
|
||||
|
||||
// Contacts Search
|
||||
GridBagConstraints gbc_searchPane = new GridBagConstraints();
|
||||
gbc_searchPane.fill = GridBagConstraints.BOTH;
|
||||
gbc_searchPane.gridwidth = 2;
|
||||
gbc_searchPane.gridheight = 2;
|
||||
gbc_searchPane.gridx = 1;
|
||||
gbc_searchPane.gridy = 1;
|
||||
|
||||
gbc_searchPane.insets = insets;
|
||||
|
||||
GridBagLayout gbl_contactsSearch = new GridBagLayout();
|
||||
gbl_contactsSearch.columnWidths = new int[] { 1, 1 };
|
||||
gbl_contactsSearch.rowHeights = new int[] { 1, 1 };
|
||||
gbl_contactsSearch.columnWeights = new double[] { 1, 0.1 };
|
||||
gbl_contactsSearch.rowWeights = new double[] { 0.001, 1 };
|
||||
searchPane.setLayout(gbl_contactsSearch);
|
||||
|
||||
GridBagConstraints gbc_searchField = new GridBagConstraints();
|
||||
gbc_searchField.fill = GridBagConstraints.BOTH;
|
||||
gbc_searchField.gridx = 0;
|
||||
gbc_searchField.gridy = 0;
|
||||
gbc_searchField.insets = new Insets(7, 4, 4, 4);
|
||||
|
||||
searchPane.add(searchField, gbc_searchField);
|
||||
|
||||
// Sends event to server, if input has changed
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent evt) {
|
||||
if (client.isOnline()) if (searchField.getText().isEmpty()) {
|
||||
contactsModel.clear();
|
||||
revalidate();
|
||||
repaint();
|
||||
} else try {
|
||||
client.sendEvent(new ContactSearchRequest(searchField.getText()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent evt) {
|
||||
if (client.isOnline()) try {
|
||||
client.sendEvent(new ContactSearchRequest(searchField.getText()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent evt) {}
|
||||
});
|
||||
|
||||
GridBagConstraints gbc_cancelButton = new GridBagConstraints();
|
||||
gbc_cancelButton.fill = GridBagConstraints.BOTH;
|
||||
gbc_cancelButton.gridx = 1;
|
||||
gbc_cancelButton.gridy = 0;
|
||||
gbc_cancelButton.insets = new Insets(7, 4, 4, 4);
|
||||
|
||||
cancelButton.addActionListener((evt) -> { drawChatBox(gbc_scrollPane); });
|
||||
|
||||
searchPane.add(cancelButton, gbc_cancelButton);
|
||||
|
||||
contactList.setModel(contactsModel);
|
||||
scrollForPossibleContacts.setBorder(new EmptyBorder(space, space, space, space));
|
||||
scrollForPossibleContacts.setViewportView(contactList);
|
||||
|
||||
GridBagConstraints gbc_possibleContacts = new GridBagConstraints();
|
||||
gbc_possibleContacts.fill = GridBagConstraints.BOTH;
|
||||
gbc_possibleContacts.gridwidth = 2;
|
||||
gbc_possibleContacts.gridx = 0;
|
||||
gbc_possibleContacts.gridy = 1;
|
||||
|
||||
gbc_possibleContacts.insets = insets;
|
||||
|
||||
searchPane.add(scrollForPossibleContacts, gbc_possibleContacts);
|
||||
|
||||
// Contacts Header
|
||||
GridBagConstraints gbc_contactsHeader = new GridBagConstraints();
|
||||
gbc_contactsHeader.fill = GridBagConstraints.BOTH;
|
||||
gbc_contactsHeader.gridx = 0;
|
||||
gbc_contactsHeader.gridy = 1;
|
||||
gbc_contactsHeader.insets = insets;
|
||||
|
||||
GridBagLayout gbl_contactHeader = new GridBagLayout();
|
||||
gbl_contactHeader.columnWidths = new int[] { 1, 1 };
|
||||
gbl_contactHeader.rowHeights = new int[] { 1 };
|
||||
gbl_contactHeader.columnWeights = new double[] { 1, 1 };
|
||||
gbl_contactHeader.rowWeights = new double[] { 1 };
|
||||
contactsHeader.setLayout(gbl_contactHeader);
|
||||
|
||||
contactsDisplay.setEditable(false);
|
||||
contactsDisplay.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||
contactsDisplay.setText("Contacts");
|
||||
|
||||
GridBagConstraints gbc_contactsDisplay = new GridBagConstraints();
|
||||
gbc_contactsDisplay.fill = GridBagConstraints.BOTH;
|
||||
gbc_contactsDisplay.gridx = 0;
|
||||
gbc_contactsDisplay.gridy = 0;
|
||||
|
||||
contactsHeader.add(contactsDisplay, gbc_contactsDisplay);
|
||||
|
||||
addContact.setFont(new Font("Arial", Font.PLAIN, 15));
|
||||
|
||||
GridBagConstraints gbc_addContact = new GridBagConstraints();
|
||||
gbc_addContact.fill = GridBagConstraints.BOTH;
|
||||
gbc_addContact.gridx = 1;
|
||||
gbc_addContact.gridy = 0;
|
||||
gbc_addContact.insets = insets;
|
||||
|
||||
addContact.addActionListener(evt -> drawContactSearch(gbc_searchPane));
|
||||
|
||||
contactsHeader.add(addContact, gbc_addContact);
|
||||
|
||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
||||
|
||||
contentPane.add(contactsHeader, gbc_contactsHeader);
|
||||
contentPane.revalidate();
|
||||
|
||||
// Listen to theme changes
|
||||
EventBus.getInstance().register(ThemeChangeEvent.class, evt -> applyTheme(evt.get()));
|
||||
|
||||
// Listen to user status changes
|
||||
EventBus.getInstance().register(UserStatusChangeEvent.class, evt -> { userList.revalidate(); userList.repaint(); });
|
||||
|
||||
// Listen to received messages
|
||||
EventBus.getInstance().register(MessageCreationEvent.class, evt -> {
|
||||
Message message = evt.get();
|
||||
Chat chat = localDB.getChats().stream().filter(c -> c.getRecipient().getID() == message.getSenderID()).findFirst().get();
|
||||
chat.appendMessage(message);
|
||||
|
||||
// Read message and update UI if in current chat
|
||||
if (chat == currentChat) readCurrentChat();
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
// Listen to message status changes
|
||||
EventBus.getInstance().register(MessageStatusChangeEvent.class, evt -> {
|
||||
final long id = evt.getID();
|
||||
final MessageStatus status = evt.get();
|
||||
|
||||
for (Chat c : localDB.getChats())
|
||||
for (Message m : c.getModel())
|
||||
if (m.getID() == id) {
|
||||
|
||||
// Update message status
|
||||
m.setStatus(status);
|
||||
|
||||
// Update model and scroll down if current chat
|
||||
if (c == currentChat) {
|
||||
messageList.setModel(currentChat.getModel());
|
||||
scrollPane.setChatOpened(true);
|
||||
} else messageList.synchronizeModel();
|
||||
}
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
// Listen to contact search results
|
||||
EventBus.getInstance()
|
||||
.register(ContactSearchResult.class,
|
||||
evt -> {
|
||||
contactsModel.clear();
|
||||
final java.util.List<User> contacts = evt.get();
|
||||
contacts.forEach(contactsModel::add);
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
// Add new contacts to the contact list
|
||||
EventBus.getInstance().register(ContactOperationEvent.class, evt -> {
|
||||
User contact = evt.get();
|
||||
|
||||
// Clearing the search field and the searchResultList
|
||||
searchField.setText("");
|
||||
contactsModel.clear();
|
||||
|
||||
// Update LocalDB
|
||||
userListModel.addElement(contact);
|
||||
localDB.getUsers().put(contact.getName(), contact);
|
||||
localDB.getChats().add(new Chat(contact));
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to immediately reload the {@link ChatWindow} when settings were changed.
|
||||
*
|
||||
* @param theme the theme to change colors into
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
private void applyTheme(Theme theme) {
|
||||
// contentPane
|
||||
contentPane.setBackground(theme.getBackgroundColor());
|
||||
contentPane.setForeground(theme.getUserNameColor());
|
||||
// messageList
|
||||
messageList.setForeground(theme.getTextColor());
|
||||
messageList.setBackground(theme.getCellColor());
|
||||
messageList.synchronizeModel();
|
||||
// scrollPane
|
||||
scrollPane.applyTheme(theme);
|
||||
scrollPane.autoscroll();
|
||||
// messageEnterTextArea
|
||||
messageEnterTextArea.setCaretColor(theme.getTypingMessageColor());
|
||||
messageEnterTextArea.setForeground(theme.getTypingMessageColor());
|
||||
messageEnterTextArea.setBackground(theme.getCellColor());
|
||||
// postButton
|
||||
postButton.setForeground(theme.getInteractableForegroundColor());
|
||||
postButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
// settingsButton
|
||||
settingsButton.setForeground(theme.getInteractableForegroundColor());
|
||||
settingsButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
// textPane
|
||||
textPane.setBackground(theme.getBackgroundColor());
|
||||
textPane.setForeground(theme.getUserNameColor());
|
||||
// userList
|
||||
userList.setSelectionForeground(theme.getUserNameColor());
|
||||
userList.setSelectionBackground(theme.getSelectionColor());
|
||||
userList.setForeground(theme.getUserNameColor());
|
||||
userList.setBackground(theme.getCellColor());
|
||||
// contacts header
|
||||
contactsHeader.setBackground(theme.getCellColor());
|
||||
contactsDisplay.setBackground(theme.getCellColor());
|
||||
contactsDisplay.setForeground(theme.getUserNameColor());
|
||||
addContact.setBackground(theme.getInteractableBackgroundColor());
|
||||
addContact.setForeground(theme.getInteractableForegroundColor());
|
||||
// SearchPane
|
||||
searchPane.setBackground(theme.getCellColor());
|
||||
searchField.setBackground(theme.getBackgroundColor());
|
||||
searchField.setForeground(theme.getUserNameColor());
|
||||
cancelButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
cancelButton.setForeground(theme.getInteractableForegroundColor());
|
||||
contactList.setForeground(theme.getTextColor());
|
||||
contactList.setBackground(theme.getCellColor());
|
||||
scrollForPossibleContacts.applyTheme(theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a new message to the server based on the text entered in the textArea.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void postMessage() {
|
||||
if (userList.isSelectionEmpty()) {
|
||||
JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
String text = messageEnterTextArea.getText().trim();
|
||||
if (!text.isEmpty()) checkMessageTextLength();
|
||||
|
||||
// Create message
|
||||
final Message message = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||
.setText(text)
|
||||
.build();
|
||||
sendMessage(message);
|
||||
// Clear text field
|
||||
messageEnterTextArea.setText("");
|
||||
postButton.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards a message.
|
||||
*
|
||||
* @param message the message to forward
|
||||
* @param recipient the new recipient of the message
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void forwardMessage(Message message, User... recipients) {
|
||||
Arrays.stream(recipients).forEach(recipient -> {
|
||||
if (message != null && recipients != null) sendMessage(new MessageBuilder(message, recipient.getID(), localDB.getIDGenerator()).build());
|
||||
else throw new NullPointerException("No recipient or no message selected");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void forwardMessages(Collection<Message> messages, User... recipients) {
|
||||
messages.forEach(message -> { forwardMessage(message, recipients); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a {@link Message} to the server.
|
||||
*
|
||||
* @param message the message to send
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void sendMessage(final Message message) {
|
||||
try {
|
||||
// Send message
|
||||
writeProxy.writeMessage(message);
|
||||
|
||||
// Add message to PersistentLocalDB and update UI
|
||||
currentChat.appendMessage(message);
|
||||
|
||||
// Update UI
|
||||
revalidate();
|
||||
repaint();
|
||||
|
||||
// Request a new id generator if all IDs were used
|
||||
if (!localDB.getIDGenerator().hasNext()) client.requestIdGenerator();
|
||||
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(this, "Error sending message:\n" + e.toString(), "Message sending error", JOptionPane.ERROR_MESSAGE);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void readCurrentChat() {
|
||||
try {
|
||||
currentChat.read(writeProxy);
|
||||
if (messageList.getRenderer() != null) messageList.synchronizeModel();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.log(Level.WARNING, "Couldn't notify server about message status change", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawChatBox(GridBagConstraints gbc_scrollPane) {
|
||||
contentPane.remove(searchPane);
|
||||
contentPane.add(scrollPane, gbc_scrollPane);
|
||||
contentPane.revalidate();
|
||||
contentPane.repaint();
|
||||
}
|
||||
|
||||
private void drawContactSearch(GridBagConstraints gbc_searchPane) {
|
||||
currentChat = null;
|
||||
userList.removeSelectionInterval(0, userList.getModel().getSize() - 1);
|
||||
messageList.setModel(null);
|
||||
textPane.setText("");
|
||||
contentPane.remove(scrollPane);
|
||||
contentPane.add(searchPane, gbc_searchPane);
|
||||
contentPane.revalidate();
|
||||
contentPane.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the components responsible server communication and
|
||||
* persistence.<br>
|
||||
* <br>
|
||||
* This will trigger the display of the contact list.
|
||||
*
|
||||
* @param client the client used to send and receive messages
|
||||
* @param localDB the local database used to manage stored messages
|
||||
* and users
|
||||
* @param writeProxy the write proxy used to send messages and status change
|
||||
* events to the server or cache them inside the local
|
||||
* database
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void initContent(Client client, LocalDB localDB, WriteProxy writeProxy) {
|
||||
this.client = client;
|
||||
this.localDB = localDB;
|
||||
this.writeProxy = writeProxy;
|
||||
|
||||
messageList.setRenderer((list, message) -> new MessageComponent(list, message, client.getSender().getID()));
|
||||
|
||||
// Load users and chats
|
||||
new Thread(() -> {
|
||||
localDB.getUsers().values().forEach(user -> {
|
||||
userListModel.addElement(user);
|
||||
|
||||
// Check if user exists in local DB
|
||||
if (localDB.getChats().stream().noneMatch(c -> c.getRecipient().getID() == user.getID())) localDB.getChats().add(new Chat(user));
|
||||
});
|
||||
SwingUtilities.invokeLater(() -> userList.setModel(userListModel));
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the length of the text inside messageEnterTextArea >=
|
||||
* {@link ChatWindow#MAX_MESSAGE_LENGTH}
|
||||
* and splits the text into the allowed part, if that is the case.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void checkMessageTextLength() {
|
||||
String input = messageEnterTextArea.getText();
|
||||
if (input.length() >= MAX_MESSAGE_LENGTH) {
|
||||
messageEnterTextArea.setText(input.substring(0, MAX_MESSAGE_LENGTH - 1));
|
||||
// TODO: current notification is like being hit with a hammer, maybe it should
|
||||
// be replaced with a more subtle notification
|
||||
JOptionPane.showMessageDialog(messageEnterTextArea,
|
||||
"the maximum length for a message has been reached",
|
||||
"maximum message length reached",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPostButton(String text) { postButton.setEnabled(!text.trim().isBlank()); }
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
package envoy.client.ui.container;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.ui.list.ComponentList;
|
||||
import envoy.client.ui.list.ComponentList.SelectionMode;
|
||||
import envoy.client.ui.list.Model;
|
||||
import envoy.client.ui.list_component.UserComponent;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.User;
|
||||
|
||||
/**
|
||||
* This class defines a dialog to choose contacts from.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ContactsChooserDialog.java</strong><br>
|
||||
* Created: <strong>15 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContactsChooserDialog extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private ComponentList<User> contactList = new ComponentList<User>().setModel(new Model<User>())
|
||||
.setRenderer((list, user) -> new UserComponent(user));
|
||||
private JButton okButton = new JButton("Ok");
|
||||
private JButton cancelButton = new JButton("Cancel");
|
||||
|
||||
private final Theme theme = Settings.getInstance().getCurrentTheme();
|
||||
|
||||
private final JPanel contentPanel = new JPanel();
|
||||
|
||||
/**
|
||||
* Shows a modal contacts-chooser dialog and blocks until the
|
||||
* dialog is hidden. If the user presses the "OK" button, then
|
||||
* this method hides/disposes the dialog and returns the selected element (has
|
||||
* yet
|
||||
* to be casted back to its original type due to the limitations of Generics in
|
||||
* Java).
|
||||
* If the user presses the "Cancel" button or closes the dialog without
|
||||
* pressing "OK", then this method disposes the dialog and returns an empty
|
||||
* <code>ArrayList</code>.
|
||||
*
|
||||
* @param title the title of the dialog
|
||||
* @param parent this @{@link Component} will be parsed to
|
||||
* {@link java.awt.Window#setLocationRelativeTo(Component)} in
|
||||
* order to change the location of the dialog
|
||||
* @param message the {@link Message} to display on top of the Dialog
|
||||
* @param users the users that should be displayed
|
||||
* @return the selected Element (yet has to be casted to the wanted type due to
|
||||
* the Generics limitations in Java)
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static List<User> showForwardingDialog(String title, Component parent, Message message, Collection<User> users) {
|
||||
ContactsChooserDialog dialog = new ContactsChooserDialog(parent);
|
||||
dialog.setTitle(title);
|
||||
dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
dialog.addCancelButtonActionListener(e -> dialog.dispose());
|
||||
|
||||
List<User> results = new ArrayList<>();
|
||||
dialog.addOkButtonActionListener(e -> { results.addAll(dialog.getContactList().getSelectedElements()); dialog.dispose(); });
|
||||
Model<User> contactListModel = dialog.getContactList().getModel();
|
||||
users.forEach(contactListModel::add);
|
||||
|
||||
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
dialog.setVisible(true);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parent this @{@link Component} will be parsed to
|
||||
* {@link java.awt.Window#setLocationRelativeTo(Component)}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private ContactsChooserDialog(Component parent) {
|
||||
contactList.setSelectionMode(SelectionMode.MULTIPLE);
|
||||
contactList.setSelectionHandler((user, comp, isSelected) -> {
|
||||
final var theme = Settings.getInstance().getCurrentTheme();
|
||||
comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
|
||||
});
|
||||
setLocationRelativeTo(parent);
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
setBackground(theme.getBackgroundColor());
|
||||
setForeground(theme.getTextColor());
|
||||
setSize(400, 400);
|
||||
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
getContentPane().add(contentPanel, BorderLayout.CENTER);
|
||||
contentPanel.setLayout(new BorderLayout(0, 0));
|
||||
contentPanel.add(contactList, BorderLayout.CENTER);
|
||||
{
|
||||
JPanel buttonPane = new JPanel();
|
||||
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
||||
{
|
||||
okButton = new JButton("OK");
|
||||
okButton.setMnemonic(KeyEvent.VK_ENTER);
|
||||
okButton.setActionCommand("OK");
|
||||
buttonPane.setLayout(new BorderLayout(0, 0));
|
||||
buttonPane.add(okButton, BorderLayout.EAST);
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
}
|
||||
{
|
||||
cancelButton = new JButton("Cancel");
|
||||
cancelButton.setActionCommand("Cancel");
|
||||
buttonPane.add(cancelButton, BorderLayout.WEST);
|
||||
}
|
||||
}
|
||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
||||
}
|
||||
|
||||
private void applyTheme(Theme theme) {
|
||||
contentPanel.setBackground(theme.getBackgroundColor());
|
||||
contentPanel.setForeground(theme.getTextColor());
|
||||
contactList.setBackground(theme.getCellColor());
|
||||
okButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
okButton.setForeground(theme.getTextColor());
|
||||
cancelButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
cancelButton.setForeground(theme.getTextColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the underlying {@link ComponentList}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private ComponentList<User> getContactList() { return contactList; }
|
||||
|
||||
private void addOkButtonActionListener(ActionListener l) { okButton.addActionListener(l); }
|
||||
|
||||
private void addCancelButtonActionListener(ActionListener l) { cancelButton.addActionListener(l); }
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
package envoy.client.ui.container;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.ui.Theme;
|
||||
|
||||
/**
|
||||
* This class defines a menu that will be automatically called if
|
||||
* {@link MouseEvent#isPopupTrigger()} returns true for the parent component.
|
||||
* The user has the possibility to directly add actions to be performed when
|
||||
* clicking on the element with the selected String. Additionally, for each
|
||||
* element an {@link Icon} can be added, but it must not be.
|
||||
* If the key(text) of an element starts with one of the predefined values, a
|
||||
* special component will be called: either a {@link JRadioButtonMenuItem}, a
|
||||
* {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ContextMenu.java</strong><br>
|
||||
* Created: <strong>17 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContextMenu extends JPopupMenu {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* If a key starts with this String, a {@link JCheckBoxMenuItem} will be created
|
||||
*/
|
||||
public static final String checkboxMenuItem = "ChBoMI";
|
||||
/**
|
||||
* If a key starts with this String, a {@link JRadioButtonMenuItem} will be
|
||||
* created
|
||||
*/
|
||||
public static final String radioButtonMenuItem = "RaBuMI";
|
||||
/**
|
||||
* If a key starts with this String, a {@link JMenu} will be created
|
||||
*/
|
||||
public static final String subMenuItem = "SubMI";
|
||||
|
||||
private Map<String, ActionListener> items = new HashMap<>();
|
||||
private Map<String, Icon> icons = new HashMap<>();
|
||||
private Map<String, Integer> mnemonics = new HashMap<>();
|
||||
|
||||
private ButtonGroup radioButtonGroup = new ButtonGroup();
|
||||
private boolean built = false;
|
||||
private boolean visible = false;
|
||||
|
||||
/**
|
||||
* @param parent the component which will call this
|
||||
* {@link ContextMenu}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu(Component parent) {
|
||||
setInvoker(parent);
|
||||
setOpaque(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param label the string that a UI may use to display as a title
|
||||
* for the pop-up menu
|
||||
* @param parent the component which will call this
|
||||
* {@link ContextMenu}
|
||||
* @param itemsWithActions a map of all strings to be displayed with according
|
||||
* actions
|
||||
* @param itemIcons the icons to be displayed before a name, if wanted.
|
||||
* Only keys in here will have an Icon displayed. More
|
||||
* precisely, all keys here not included in the first
|
||||
* map will be thrown out.
|
||||
* @param itemMnemonics the keyboard shortcuts that need to be pressed to
|
||||
* automatically execute the {@link JMenuItem} with the
|
||||
* given text
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
|
||||
Map<String, Integer> itemMnemonics) {
|
||||
this(label);
|
||||
setInvoker(parent);
|
||||
this.items = (itemsWithActions != null) ? itemsWithActions : items;
|
||||
this.icons = (itemIcons != null) ? itemIcons : icons;
|
||||
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param label the string that a UI may use to display as a title for the
|
||||
* pop-up menu.
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu(String label) {
|
||||
super(label);
|
||||
setOpaque(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the PopupMenu to be displayed. Should only be used once all map
|
||||
* values have been set.
|
||||
*
|
||||
* @return this instance of {@link ContextMenu} to allow chaining behind the
|
||||
* constructor
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu build() {
|
||||
items.forEach((text, action) -> {
|
||||
// case radio button wanted
|
||||
AbstractButton item;
|
||||
if (text.startsWith(radioButtonMenuItem)) {
|
||||
item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
||||
radioButtonGroup.add(item);
|
||||
// case check box wanted
|
||||
} else if (text.startsWith(checkboxMenuItem))
|
||||
item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
||||
// case sub-menu wanted
|
||||
else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
|
||||
else // normal JMenuItem wanted
|
||||
item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
|
||||
item.addActionListener(action);
|
||||
item.setOpaque(true);
|
||||
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
|
||||
add(item);
|
||||
});
|
||||
getInvoker().addMouseListener(getShowingListener());
|
||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
||||
built = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
private MouseAdapter getShowingListener() {
|
||||
return new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) { action(e); }
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) { action(e); }
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) { action(e); }
|
||||
|
||||
private void action(MouseEvent e) {
|
||||
if (!built) build();
|
||||
if (e.isPopupTrigger()) {
|
||||
// hides the menu if already visible
|
||||
visible = !visible;
|
||||
if (visible) show(e.getComponent(), e.getX(), e.getY());
|
||||
else setVisible(false);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all subcomponents of this menu.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void clear() {
|
||||
removeAll();
|
||||
items = new HashMap<>();
|
||||
icons = new HashMap<>();
|
||||
mnemonics = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the items
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Map<String, ActionListener> getItems() { return items; }
|
||||
|
||||
/**
|
||||
* @param items the items with the displayed text and the according action to
|
||||
* take once called
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setItems(Map<String, ActionListener> items) { this.items = items; }
|
||||
|
||||
/**
|
||||
* @return the icons
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Map<String, Icon> getIcons() { return icons; }
|
||||
|
||||
/**
|
||||
* @param icons the icons to set
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setIcons(Map<String, Icon> icons) { this.icons = icons; }
|
||||
|
||||
/**
|
||||
* @return the mnemonics (the keyboard shortcuts that automatically execute the
|
||||
* command for a {@link JMenuItem} with corresponding text)
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Map<String, Integer> getMnemonics() { return mnemonics; }
|
||||
|
||||
/**
|
||||
* @param mnemonics the keyboard shortcuts that need to be pressed to
|
||||
* automatically execute the {@link JMenuItem} with the given
|
||||
* text
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}<br>
|
||||
* Additionally sets the foreground of all subcomponents of this
|
||||
* {@link ContextMenu}.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
public void setForeground(Color color) {
|
||||
super.setForeground(color);
|
||||
for (MenuElement element : getSubElements())
|
||||
((Component) element).setForeground(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}<br>
|
||||
* Additionally sets the background of all subcomponents of this
|
||||
* {@link ContextMenu}.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
public void setBackground(Color color) {
|
||||
super.setBackground(color);
|
||||
for (MenuElement element : getSubElements())
|
||||
((Component) element).setBackground(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fore- and background of all elements contained in this
|
||||
* {@link ContextMenu}
|
||||
* This method is to be only used by Envoy as {@link Theme} is an
|
||||
* Envoy-exclusive object.
|
||||
*
|
||||
* @param theme the theme to use
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
protected void applyTheme(Theme theme) {
|
||||
setBackground(theme.getCellColor());
|
||||
setForeground(theme.getTextColor());
|
||||
}
|
||||
}
|
@ -1,346 +0,0 @@
|
||||
package envoy.client.ui.container;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.naming.TimeLimitExceededException;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.event.HandshakeSuccessfulEvent;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.ui.primary.PrimaryButton;
|
||||
import envoy.data.LoginCredentials;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.User;
|
||||
import envoy.event.EventBus;
|
||||
import envoy.event.HandshakeRejectionEvent;
|
||||
import envoy.exception.EnvoyException;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>LoginDialog.java</strong><br>
|
||||
* Created: <strong>01.01.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public class LoginDialog extends JDialog {
|
||||
|
||||
private JPanel contentPanel;
|
||||
private JTextField textField;
|
||||
private JPasswordField passwordField;
|
||||
private JPasswordField repeatPasswordField;
|
||||
|
||||
private JLabel lblUserName;
|
||||
private JLabel lblPassword;
|
||||
private JLabel lblRepeatPassword;
|
||||
private JLabel errorMessage;
|
||||
|
||||
private GridBagConstraints gbc_lblRepeatPassword;
|
||||
private GridBagConstraints gbc_repeatPasswordField;
|
||||
private GridBagConstraints gbc_errorMessage;
|
||||
|
||||
private JPanel buttonPane;
|
||||
private JTextPane registerText;
|
||||
private JCheckBox registerCheckBox;
|
||||
private PrimaryButton okButton;
|
||||
private PrimaryButton cancelButton;
|
||||
|
||||
private LoginCredentials credentials;
|
||||
|
||||
private final Client client;
|
||||
private final LocalDB localDB;
|
||||
private final Cache<Message> receivedMessageCache;
|
||||
|
||||
private static final ClientConfig config = ClientConfig.getInstance();
|
||||
private static final Logger logger = EnvoyLog.getLogger(LoginDialog.class);
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Displays a dialog enabling the user to enter their user name and password.
|
||||
*
|
||||
* @param client the client used to perform the handshake
|
||||
* @param localDB the local database in which data is persisted
|
||||
* @param receivedMessageCache the cache that stored messages received during
|
||||
* the handshake
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public LoginDialog(Client client, LocalDB localDB, Cache<Message> receivedMessageCache) {
|
||||
this.client = client;
|
||||
this.localDB = localDB;
|
||||
this.receivedMessageCache = receivedMessageCache;
|
||||
|
||||
// Prepare handshake
|
||||
localDB.loadIDGenerator();
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) { abortLogin(); }
|
||||
});
|
||||
|
||||
initUi();
|
||||
|
||||
okButton.addActionListener((evt) -> {
|
||||
try {
|
||||
if (registerCheckBox.isSelected()) {
|
||||
// Check password equality
|
||||
if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword())) {
|
||||
credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), true);
|
||||
performHandshake();
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(this, "The repeated password is not the original password!");
|
||||
clearPasswordFields();
|
||||
}
|
||||
} else {
|
||||
credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), false);
|
||||
performHandshake();
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
// Listen to handshake rejections
|
||||
EventBus.getInstance()
|
||||
.register(HandshakeRejectionEvent.class,
|
||||
evt -> { clearPasswordFields(); errorMessage.setVisible(true); errorMessage.setText(evt.get()); });
|
||||
|
||||
// Exit the application when the dialog is cancelled
|
||||
cancelButton.addActionListener(evt -> abortLogin());
|
||||
|
||||
// Log in directly if configured
|
||||
if (config.hasLoginCredentials()) {
|
||||
credentials = config.getLoginCredentials();
|
||||
performHandshake();
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
private void performHandshake() {
|
||||
try {
|
||||
client.performHandshake(credentials, receivedMessageCache);
|
||||
if (client.isOnline()) {
|
||||
client.initReceiver(localDB, receivedMessageCache);
|
||||
dispose();
|
||||
}
|
||||
} catch (IOException | InterruptedException | TimeLimitExceededException e) {
|
||||
logger.warning("Could not connect to server. Trying offline mode...");
|
||||
e.printStackTrace();
|
||||
try {
|
||||
// Try entering offline mode
|
||||
localDB.loadUsers();
|
||||
User clientUser = localDB.getUsers().get(credentials.getIdentifier());
|
||||
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
|
||||
client.setSender(clientUser);
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"A connection to the server could not be established. Starting in offline mode.\n" + e,
|
||||
"Connection error",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
dispose();
|
||||
} catch (Exception e1) {
|
||||
JOptionPane.showMessageDialog(null, e1, "Client error", JOptionPane.ERROR_MESSAGE);
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initUi() {
|
||||
setSize(338, 123);
|
||||
setLocationRelativeTo(null);
|
||||
setResizable(false);
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
contentPanel = new JPanel();
|
||||
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
getContentPane().add(contentPanel, BorderLayout.CENTER);
|
||||
GridBagLayout gbl_contentPanel = new GridBagLayout();
|
||||
gbl_contentPanel.columnWidths = new int[] { 0, 0, 0 };
|
||||
gbl_contentPanel.rowHeights = new int[] { 0, 0, 0 };
|
||||
gbl_contentPanel.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE };
|
||||
gbl_contentPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
|
||||
contentPanel.setLayout(gbl_contentPanel);
|
||||
|
||||
lblUserName = new JLabel("Username:");
|
||||
GridBagConstraints gbc_lblUserName = new GridBagConstraints();
|
||||
gbc_lblUserName.anchor = GridBagConstraints.EAST;
|
||||
gbc_lblUserName.insets = new Insets(0, 0, 5, 5);
|
||||
gbc_lblUserName.gridx = 0;
|
||||
gbc_lblUserName.gridy = 0;
|
||||
contentPanel.add(lblUserName, gbc_lblUserName);
|
||||
|
||||
textField = new JTextField();
|
||||
textField.setBorder(null);
|
||||
GridBagConstraints gbc_textField = new GridBagConstraints();
|
||||
gbc_textField.insets = new Insets(0, 0, 5, 0);
|
||||
gbc_textField.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_textField.gridx = 1;
|
||||
gbc_textField.gridy = 0;
|
||||
contentPanel.add(textField, gbc_textField);
|
||||
textField.setColumns(10);
|
||||
|
||||
lblPassword = new JLabel("Password:");
|
||||
GridBagConstraints gbc_lblPassword = new GridBagConstraints();
|
||||
gbc_lblPassword.anchor = GridBagConstraints.EAST;
|
||||
gbc_lblPassword.insets = new Insets(0, 0, 0, 5);
|
||||
gbc_lblPassword.gridx = 0;
|
||||
gbc_lblPassword.gridy = 1;
|
||||
contentPanel.add(lblPassword, gbc_lblPassword);
|
||||
|
||||
passwordField = new JPasswordField();
|
||||
passwordField.setBorder(null);
|
||||
GridBagConstraints gbc_passwordField = new GridBagConstraints();
|
||||
gbc_passwordField.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_passwordField.gridx = 1;
|
||||
gbc_passwordField.gridy = 1;
|
||||
contentPanel.add(passwordField, gbc_passwordField);
|
||||
|
||||
lblRepeatPassword = new JLabel("Repeat Password:");
|
||||
gbc_lblRepeatPassword = new GridBagConstraints();
|
||||
gbc_lblRepeatPassword.anchor = GridBagConstraints.EAST;
|
||||
gbc_lblRepeatPassword.insets = new Insets(0, 0, 0, 5);
|
||||
gbc_lblRepeatPassword.gridx = 0;
|
||||
gbc_lblRepeatPassword.gridy = 2;
|
||||
|
||||
repeatPasswordField = new JPasswordField();
|
||||
gbc_repeatPasswordField = new GridBagConstraints();
|
||||
gbc_repeatPasswordField.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_repeatPasswordField.gridx = 1;
|
||||
gbc_repeatPasswordField.gridy = 2;
|
||||
|
||||
errorMessage = new JLabel();
|
||||
gbc_errorMessage = new GridBagConstraints();
|
||||
gbc_errorMessage.gridx = 1;
|
||||
gbc_errorMessage.gridy = 3;
|
||||
gbc_errorMessage.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_errorMessage.insets = new Insets(5, 5, 5, 5);
|
||||
errorMessage.setForeground(Color.RED);
|
||||
errorMessage.setVisible(false);
|
||||
contentPanel.add(errorMessage, gbc_errorMessage);
|
||||
|
||||
buttonPane = new JPanel();
|
||||
|
||||
registerText = new JTextPane();
|
||||
registerText.setEditable(false);
|
||||
registerText.setText("Register?");
|
||||
registerText.setFont(new Font("Arial", Font.BOLD, 12));
|
||||
registerText.setAlignmentX(LEFT_ALIGNMENT);
|
||||
buttonPane.add(registerText);
|
||||
|
||||
registerCheckBox = new JCheckBox();
|
||||
registerCheckBox.setAlignmentX(LEFT_ALIGNMENT);
|
||||
registerCheckBox.addItemListener(e -> {
|
||||
switch (e.getStateChange()) {
|
||||
case ItemEvent.SELECTED:
|
||||
contentPanel.add(lblRepeatPassword, gbc_lblRepeatPassword);
|
||||
contentPanel.add(repeatPasswordField, gbc_repeatPasswordField);
|
||||
setSize(338, 173);
|
||||
break;
|
||||
|
||||
case ItemEvent.DESELECTED:
|
||||
if (repeatPasswordField.getParent() == contentPanel) {
|
||||
contentPanel.remove(lblRepeatPassword);
|
||||
contentPanel.remove(repeatPasswordField);
|
||||
setSize(338, 148);
|
||||
}
|
||||
break;
|
||||
}
|
||||
contentPanel.revalidate();
|
||||
contentPanel.repaint();
|
||||
});
|
||||
buttonPane.add(registerCheckBox);
|
||||
|
||||
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
|
||||
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
||||
okButton = new PrimaryButton("OK");
|
||||
okButton.setActionCommand("OK");
|
||||
buttonPane.add(okButton);
|
||||
getRootPane().setDefaultButton(okButton);
|
||||
|
||||
cancelButton = new PrimaryButton("Cancel");
|
||||
cancelButton.setActionCommand("Cancel");
|
||||
buttonPane.add(cancelButton);
|
||||
setTheme();
|
||||
|
||||
setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
|
||||
|
||||
EventBus.getInstance().register(HandshakeSuccessfulEvent.class, evt -> dispose());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the text stored in the password fields.
|
||||
*
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
private void clearPasswordFields() {
|
||||
passwordField.setText(null);
|
||||
repeatPasswordField.setText(null);
|
||||
}
|
||||
|
||||
private void setTheme() {
|
||||
Theme theme = Settings.getInstance().getCurrentTheme();
|
||||
|
||||
// Panels
|
||||
contentPanel.setBackground(theme.getBackgroundColor());
|
||||
contentPanel.setForeground(theme.getBackgroundColor());
|
||||
|
||||
buttonPane.setBackground(theme.getBackgroundColor());
|
||||
buttonPane.setForeground(theme.getBackgroundColor());
|
||||
|
||||
// Input Fields
|
||||
textField.setBackground(theme.getCellColor());
|
||||
textField.setForeground(theme.getUserNameColor());
|
||||
|
||||
passwordField.setBackground(theme.getCellColor());
|
||||
passwordField.setForeground(theme.getUserNameColor());
|
||||
|
||||
repeatPasswordField.setBackground(theme.getCellColor());
|
||||
repeatPasswordField.setForeground(theme.getUserNameColor());
|
||||
|
||||
// JLabels
|
||||
lblUserName.setBackground(theme.getCellColor());
|
||||
lblUserName.setForeground(theme.getUserNameColor());
|
||||
|
||||
lblPassword.setBackground(theme.getCellColor());
|
||||
lblPassword.setForeground(theme.getUserNameColor());
|
||||
|
||||
lblRepeatPassword.setBackground(theme.getCellColor());
|
||||
lblRepeatPassword.setForeground(theme.getUserNameColor());
|
||||
|
||||
// Register
|
||||
registerText.setBackground(theme.getCellColor());
|
||||
registerText.setForeground(theme.getUserNameColor());
|
||||
|
||||
registerCheckBox.setBackground(theme.getCellColor());
|
||||
|
||||
// Buttons
|
||||
okButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
okButton.setForeground(theme.getInteractableForegroundColor());
|
||||
|
||||
cancelButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
cancelButton.setForeground(theme.getInteractableForegroundColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts the system down properly if the login was aborted.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void abortLogin() {
|
||||
logger.info("The login process has been cancelled. Exiting...");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* This package contains all graphical Containers, like Dialogs and Frames.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>16 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.container;
|
283
src/main/java/envoy/client/ui/controller/ChatScene.java
Normal file
283
src/main/java/envoy/client/ui/controller/ChatScene.java
Normal file
@ -0,0 +1,283 @@
|
||||
package envoy.client.ui.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import envoy.client.data.Chat;
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.event.MessageCreationEvent;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.net.WriteProxy;
|
||||
import envoy.client.ui.ContactListCell;
|
||||
import envoy.client.ui.MessageListCell;
|
||||
import envoy.client.ui.SceneContext;
|
||||
import envoy.data.Contact;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.MessageBuilder;
|
||||
import envoy.event.EventBus;
|
||||
import envoy.event.MessageStatusChangeEvent;
|
||||
import envoy.event.UserStatusChangeEvent;
|
||||
import envoy.event.contact.ContactOperationEvent;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ChatSceneController.java</strong><br>
|
||||
* Created: <strong>26.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class ChatScene {
|
||||
|
||||
@FXML
|
||||
private Label contactLabel;
|
||||
|
||||
@FXML
|
||||
private ListView<Message> messageList;
|
||||
|
||||
@FXML
|
||||
private ListView<Contact> userList;
|
||||
|
||||
@FXML
|
||||
private Button postButton;
|
||||
|
||||
@FXML
|
||||
private Button settingsButton;
|
||||
|
||||
@FXML
|
||||
private TextArea messageTextArea;
|
||||
|
||||
@FXML
|
||||
private Label remainingChars;
|
||||
|
||||
private LocalDB localDB;
|
||||
private Client client;
|
||||
private WriteProxy writeProxy;
|
||||
private SceneContext sceneContext;
|
||||
|
||||
private Chat currentChat;
|
||||
|
||||
private static final Settings settings = Settings.getInstance();
|
||||
private static final EventBus eventBus = EventBus.getInstance();
|
||||
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
|
||||
private static final int MAX_MESSAGE_LENGTH = 255;
|
||||
|
||||
/**
|
||||
* Initializes the appearance of certain visual components.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void initialize() {
|
||||
|
||||
// Initialize message and user rendering
|
||||
messageList.setCellFactory(listView -> new MessageListCell());
|
||||
userList.setCellFactory(listView -> new ContactListCell());
|
||||
|
||||
// Listen to received messages
|
||||
eventBus.register(MessageCreationEvent.class, e -> {
|
||||
final var message = e.get();
|
||||
final var chat = localDB.getChats().stream().filter(c -> c.getRecipient().getID() == message.getSenderID()).findAny().get();
|
||||
|
||||
// Update UI if in current chat
|
||||
if (chat == currentChat) Platform.runLater(() -> messageList.getItems().add(message));
|
||||
});
|
||||
|
||||
// Listen to message status changes
|
||||
eventBus.register(MessageStatusChangeEvent.class, e -> {
|
||||
final var message = localDB.getMessage(e.getID());
|
||||
message.setStatus(e.get());
|
||||
|
||||
// Update UI if in current chat
|
||||
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
||||
});
|
||||
|
||||
// Listen to user status changes
|
||||
eventBus.register(UserStatusChangeEvent.class, e -> Platform.runLater(userList::refresh));
|
||||
|
||||
// Listen to contacts changes
|
||||
eventBus.register(ContactOperationEvent.class, e -> {
|
||||
final var contact = e.get();
|
||||
switch (e.getOperationType()) {
|
||||
case ADD:
|
||||
localDB.getUsers().put(contact.getName(), contact);
|
||||
localDB.getChats().add(new Chat(contact));
|
||||
Platform.runLater(() -> userList.getItems().add(contact));
|
||||
break;
|
||||
case REMOVE:
|
||||
localDB.getUsers().remove(contact.getName());
|
||||
localDB.getChats().removeIf(c -> c.getRecipient().getID() == contact.getID());
|
||||
Platform.runLater(() -> userList.getItems().removeIf(c -> c.getID() == contact.getID()));
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all necessary data via dependency injection-
|
||||
*
|
||||
* @param sceneContext the scene context used to load other scenes
|
||||
* @param localDB the local database form which chats and users are loaded
|
||||
* @param client the client used to request ID generators
|
||||
* @param writeProxy the write proxy used to send messages and other data to
|
||||
* the server
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void initializeData(SceneContext sceneContext, LocalDB localDB, Client client, WriteProxy writeProxy) {
|
||||
this.sceneContext = sceneContext;
|
||||
this.localDB = localDB;
|
||||
this.client = client;
|
||||
this.writeProxy = writeProxy;
|
||||
|
||||
userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions to perform when the list of contacts has been clicked.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void userListClicked() {
|
||||
final Contact user = userList.getSelectionModel().getSelectedItem();
|
||||
if (user != null && (currentChat == null || user.getID() != currentChat.getRecipient().getID())) {
|
||||
contactLabel.setText(user.getName());
|
||||
|
||||
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
|
||||
|
||||
// Load the chat or create a new one and add it to the LocalDB
|
||||
currentChat = localDB.getChats()
|
||||
.stream()
|
||||
.filter(c -> c.getRecipient().getID() == user.getID())
|
||||
.findAny()
|
||||
.orElseGet(() -> { final var chat = new Chat(user); localDB.getChats().add(chat); return chat; });
|
||||
|
||||
messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
|
||||
|
||||
remainingChars.setVisible(true);
|
||||
remainingChars
|
||||
.setText(String.format("remaining chars: %d/%d", MAX_MESSAGE_LENGTH - messageTextArea.getText().length(), MAX_MESSAGE_LENGTH));
|
||||
}
|
||||
messageTextArea.setDisable(currentChat == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions to perform when the Post Button has been clicked.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void postButtonClicked() { postMessage(); }
|
||||
|
||||
/**
|
||||
* Actions to perform when the Settings Button has been clicked.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void settingsButtonClicked() {
|
||||
sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE);
|
||||
sceneContext.<SettingsScene>getController().initializeData(sceneContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions to perform when the "Add Contact" - Button has been clicked.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void addContactButtonClicked() {
|
||||
sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE);
|
||||
sceneContext.<ContactSearchScene>getController().initializeData(sceneContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions to perform when the text was updated in the messageTextArea.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void messageTextUpdated() {
|
||||
// Truncating messages that are too long and staying at the same position
|
||||
if (messageTextArea.getText().length() >= MAX_MESSAGE_LENGTH) {
|
||||
messageTextArea.setText(messageTextArea.getText().substring(0, MAX_MESSAGE_LENGTH));
|
||||
messageTextArea.positionCaret(MAX_MESSAGE_LENGTH);
|
||||
messageTextArea.setScrollTop(Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
// Redesigning the remainingChars - Label
|
||||
final int currentLength = messageTextArea.getText().length();
|
||||
final int remainingLength = MAX_MESSAGE_LENGTH - currentLength;
|
||||
remainingChars.setText(String.format("remaining chars: %d/%d", remainingLength, MAX_MESSAGE_LENGTH));
|
||||
remainingChars.setTextFill(Color.rgb(currentLength, remainingLength, 0, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions to perform when a key has been entered.
|
||||
*
|
||||
* @param e the Keys that have been entered
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void checkKeyCombination(KeyEvent e) {
|
||||
// Automatic sending of messages via (ctrl +) enter
|
||||
if (!postButton.isDisabled() && settings.isEnterToSend() && e.getCode() == KeyCode.ENTER
|
||||
|| !settings.isEnterToSend() && e.getCode() == KeyCode.ENTER && e.isControlDown())
|
||||
postMessage();
|
||||
postButton.setDisable(messageTextArea.getText().isBlank() || currentChat == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a new message to the server based on the text entered in the
|
||||
* messageTextArea.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void postMessage() {
|
||||
|
||||
// Create and send message
|
||||
sendMessage(new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||
.setText(messageTextArea.getText().strip())
|
||||
.build());
|
||||
|
||||
// Clear text field and disable post button
|
||||
messageTextArea.setText("");
|
||||
postButton.setDisable(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the server and appends it to the current chat. If all
|
||||
* message IDs have been used, a new ID generator is requested.
|
||||
*
|
||||
* @param message the message to send
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void sendMessage(Message message) {
|
||||
try {
|
||||
|
||||
// Send message
|
||||
writeProxy.writeMessage(message);
|
||||
|
||||
// Add message to LocalDB and update UI
|
||||
messageList.getItems().add(message);
|
||||
|
||||
// Request a new ID generator if all IDs were used
|
||||
if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIdGenerator();
|
||||
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.SEVERE, "Error sending message", e);
|
||||
}
|
||||
}
|
||||
}
|
127
src/main/java/envoy/client/ui/controller/ContactSearchScene.java
Normal file
127
src/main/java/envoy/client/ui/controller/ContactSearchScene.java
Normal file
@ -0,0 +1,127 @@
|
||||
package envoy.client.ui.controller;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
|
||||
import envoy.client.event.SendEvent;
|
||||
import envoy.client.ui.SceneContext;
|
||||
import envoy.client.ui.ContactListCell;
|
||||
import envoy.data.Contact;
|
||||
import envoy.event.ElementOperation;
|
||||
import envoy.event.EventBus;
|
||||
import envoy.event.contact.ContactOperationEvent;
|
||||
import envoy.event.contact.ContactSearchRequest;
|
||||
import envoy.event.contact.ContactSearchResult;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ContactSearchSceneController.java</strong><br>
|
||||
* Created: <strong>07.06.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContactSearchScene {
|
||||
|
||||
@FXML
|
||||
private Button backButton;
|
||||
|
||||
@FXML
|
||||
private Button clearButton;
|
||||
|
||||
@FXML
|
||||
private Button searchButton;
|
||||
|
||||
@FXML
|
||||
private TextField searchBar;
|
||||
|
||||
@FXML
|
||||
private ListView<Contact> contactList;
|
||||
|
||||
private SceneContext sceneContext;
|
||||
|
||||
private static EventBus eventBus = EventBus.getInstance();
|
||||
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
|
||||
|
||||
/**
|
||||
* @param sceneContext enables the user to return to the chat scene
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void initializeData(SceneContext sceneContext) { this.sceneContext = sceneContext; }
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
contactList.setCellFactory(e -> new ContactListCell());
|
||||
eventBus.register(ContactSearchResult.class, response -> Platform.runLater(() -> {
|
||||
contactList.getItems().clear();
|
||||
contactList.getItems().addAll(response.get());
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the clear and search button if no text is present in the search bar.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void checkClearButton() {
|
||||
final var containsContent = searchBar.getText().strip().isEmpty();
|
||||
clearButton.setDisable(containsContent);
|
||||
searchButton.setDisable(containsContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a {@link ContactSearchRequest} to the server.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void suggestContacts() { eventBus.dispatch(new SendEvent(new ContactSearchRequest(searchBar.getText()))); }
|
||||
|
||||
/**
|
||||
* Clears the text in the search bar and the items shown in the list.
|
||||
* Additionally disables both clear and search button.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void clear() {
|
||||
searchBar.setText(null);
|
||||
contactList.getItems().clear();
|
||||
clearButton.setDisable(true);
|
||||
searchButton.setDisable(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an {@link ContactOperationEvent} for every selected contact to the
|
||||
* server.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FXML
|
||||
private void contactListClicked() {
|
||||
final var contact = contactList.getSelectionModel().getSelectedItem();
|
||||
if (contact != null) {
|
||||
final var alert = new Alert(AlertType.CONFIRMATION);
|
||||
alert.setTitle("Add Contact to Contact List");
|
||||
alert.setHeaderText("Add the user " + contact.getName() + " to your contact list?");
|
||||
alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> {
|
||||
final var event = new ContactOperationEvent(contact, ElementOperation.ADD);
|
||||
// Sends the event to the server
|
||||
eventBus.dispatch(new SendEvent(event));
|
||||
// Updates the UI
|
||||
eventBus.dispatch(event);
|
||||
logger.log(Level.INFO, "Added contact " + contact);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void backButtonClicked() { sceneContext.pop(); }
|
||||
}
|
202
src/main/java/envoy/client/ui/controller/LoginScene.java
Normal file
202
src/main/java/envoy/client/ui/controller/LoginScene.java
Normal file
@ -0,0 +1,202 @@
|
||||
package envoy.client.ui.controller;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
|
||||
import envoy.client.data.Cache;
|
||||
import envoy.client.data.ClientConfig;
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.ui.SceneContext;
|
||||
import envoy.data.LoginCredentials;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.User;
|
||||
import envoy.data.User.UserStatus;
|
||||
import envoy.event.EventBus;
|
||||
import envoy.event.HandshakeRejectionEvent;
|
||||
import envoy.exception.EnvoyException;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>LoginDialog.java</strong><br>
|
||||
* Created: <strong>03.04.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class LoginScene {
|
||||
|
||||
@FXML
|
||||
private TextField userTextField;
|
||||
|
||||
@FXML
|
||||
private PasswordField passwordField;
|
||||
|
||||
@FXML
|
||||
private PasswordField repeatPasswordField;
|
||||
|
||||
@FXML
|
||||
private Label repeatPasswordLabel;
|
||||
|
||||
@FXML
|
||||
private CheckBox registerCheckBox;
|
||||
|
||||
@FXML
|
||||
private Label connectionLabel;
|
||||
|
||||
private Client client;
|
||||
private LocalDB localDB;
|
||||
private Cache<Message> receivedMessageCache;
|
||||
private SceneContext sceneContext;
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
|
||||
private static final EventBus eventBus = EventBus.getInstance();
|
||||
private static final ClientConfig config = ClientConfig.getInstance();
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
connectionLabel.setText("Server: " + config.getServer() + ":" + config.getPort());
|
||||
|
||||
// Show an alert after an unsuccessful handshake
|
||||
eventBus.register(HandshakeRejectionEvent.class,
|
||||
e -> Platform.runLater(() -> { clearPasswordFields(); new Alert(AlertType.ERROR, e.get()).showAndWait(); }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the login dialog using the FXML file {@code LoginDialog.fxml}.
|
||||
*
|
||||
* @param client the client used to perform the handshake
|
||||
* @param localDB the local database used for offline login
|
||||
* @param receivedMessageCache the cache storing messages received during
|
||||
* the handshake
|
||||
* @param sceneContext the scene context used to initialize the chat
|
||||
* scene
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void initializeData(Client client, LocalDB localDB, Cache<Message> receivedMessageCache, SceneContext sceneContext) {
|
||||
this.client = client;
|
||||
this.localDB = localDB;
|
||||
this.receivedMessageCache = receivedMessageCache;
|
||||
this.sceneContext = sceneContext;
|
||||
|
||||
// Prepare handshake
|
||||
localDB.loadIDGenerator();
|
||||
|
||||
// Set initial cursor
|
||||
userTextField.requestFocus();
|
||||
|
||||
// Perform automatic login if configured
|
||||
if (config.hasLoginCredentials()) performHandshake(config.getLoginCredentials());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void loginButtonPressed() {
|
||||
|
||||
// Prevent registration with unequal passwords
|
||||
if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) {
|
||||
clearPasswordFields();
|
||||
new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait();
|
||||
} else performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected()));
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void offlineModeButtonPressed() {
|
||||
attemptOfflineMode(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), false));
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void registerCheckboxChanged() {
|
||||
|
||||
// Make repeat password field and label visible / invisible
|
||||
repeatPasswordField.setVisible(registerCheckBox.isSelected());
|
||||
repeatPasswordLabel.setVisible(registerCheckBox.isSelected());
|
||||
clearPasswordFields();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void abortLogin() {
|
||||
logger.info("The login process has been cancelled. Exiting...");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private void performHandshake(LoginCredentials credentials) {
|
||||
try {
|
||||
client.performHandshake(credentials, receivedMessageCache);
|
||||
if (client.isOnline()) {
|
||||
client.initReceiver(localDB, receivedMessageCache);
|
||||
loadChatScene();
|
||||
}
|
||||
} catch (IOException | InterruptedException | TimeoutException e) {
|
||||
logger.warning("Could not connect to server: " + e);
|
||||
logger.finer("Attempting offline mode...");
|
||||
attemptOfflineMode(credentials);
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptOfflineMode(LoginCredentials credentials) {
|
||||
try {
|
||||
// Try entering offline mode
|
||||
localDB.loadUsers();
|
||||
final User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier());
|
||||
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
|
||||
client.setSender(clientUser);
|
||||
new Alert(AlertType.WARNING, "A connection to the server could not be established. Starting in offline mode.").showAndWait();
|
||||
loadChatScene();
|
||||
} catch (final Exception e) {
|
||||
new Alert(AlertType.ERROR, "Client error: " + e).showAndWait();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadChatScene() {
|
||||
|
||||
// Set client user in local database
|
||||
localDB.setUser(client.getSender());
|
||||
|
||||
// Initialize chats in local database
|
||||
try {
|
||||
localDB.initializeUserStorage();
|
||||
localDB.loadUserData();
|
||||
} catch (final FileNotFoundException e) {
|
||||
// The local database file has not yet been created, probably first login
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
|
||||
}
|
||||
|
||||
// Initialize write proxy
|
||||
final var writeProxy = client.createWriteProxy(localDB);
|
||||
|
||||
if (client.isOnline()) {
|
||||
|
||||
// Save all users to the local database and flush cache
|
||||
localDB.setUsers(client.getUsers());
|
||||
writeProxy.flushCache();
|
||||
} else
|
||||
// Set all contacts to offline mode
|
||||
localDB.getUsers().values().stream().filter(User.class::isInstance).map(User.class::cast).forEach(u -> u.setStatus(UserStatus.OFFLINE));
|
||||
|
||||
// Load ChatScene
|
||||
sceneContext.pop();
|
||||
sceneContext.getStage().setMinHeight(400);
|
||||
sceneContext.getStage().setMinWidth(350);
|
||||
sceneContext.load(SceneContext.SceneInfo.CHAT_SCENE);
|
||||
sceneContext.<ChatScene>getController().initializeData(sceneContext, localDB, client, writeProxy);
|
||||
|
||||
// Relay unread messages from cache
|
||||
if (receivedMessageCache != null && client.isOnline()) receivedMessageCache.relay();
|
||||
}
|
||||
|
||||
private void clearPasswordFields() {
|
||||
passwordField.clear();
|
||||
repeatPasswordField.clear();
|
||||
}
|
||||
}
|
59
src/main/java/envoy/client/ui/controller/SettingsScene.java
Normal file
59
src/main/java/envoy/client/ui/controller/SettingsScene.java
Normal file
@ -0,0 +1,59 @@
|
||||
package envoy.client.ui.controller;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
|
||||
import envoy.client.ui.SceneContext;
|
||||
import envoy.client.ui.settings.GeneralSettingsPane;
|
||||
import envoy.client.ui.settings.SettingsPane;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsSceneController.java</strong><br>
|
||||
* Created: <strong>10.04.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class SettingsScene {
|
||||
|
||||
@FXML
|
||||
private ListView<SettingsPane> settingsList;
|
||||
|
||||
@FXML
|
||||
private TitledPane titledPane;
|
||||
|
||||
private SceneContext sceneContext;
|
||||
|
||||
/**
|
||||
* @param sceneContext enables the user to return to the chat scene
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void initializeData(SceneContext sceneContext) { this.sceneContext = sceneContext; }
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
settingsList.setCellFactory(listView -> new ListCell<>() {
|
||||
|
||||
@Override
|
||||
protected void updateItem(SettingsPane item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (!empty && item != null) setGraphic(new Label(item.getTitle()));
|
||||
}
|
||||
});
|
||||
|
||||
settingsList.getItems().add(new GeneralSettingsPane());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void settingsListClicked() {
|
||||
final var pane = settingsList.getSelectionModel().getSelectedItem();
|
||||
if (pane != null) {
|
||||
titledPane.setText(pane.getTitle());
|
||||
titledPane.setContent(pane);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void backButtonClicked() { sceneContext.pop(); }
|
||||
}
|
11
src/main/java/envoy/client/ui/controller/package-info.java
Normal file
11
src/main/java/envoy/client/ui/controller/package-info.java
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Contains JavaFX scene controllers.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>08.06.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.controller;
|
@ -1,261 +0,0 @@
|
||||
package envoy.client.ui.list;
|
||||
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Provides a vertical list layout of components provided in a
|
||||
* {@link Model}. Similar to {@link javax.swing.JList} but capable
|
||||
* of rendering {@link JPanel}s.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ComponentList.java</strong><br>
|
||||
* Created: <strong>25.01.2020</strong><br>
|
||||
*
|
||||
* @param <E> the type of object displayed in this list
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public class ComponentList<E> extends JPanel {
|
||||
|
||||
private Model<E> model;
|
||||
private Renderer<E> renderer;
|
||||
private SelectionHandler<E> selectionHandler;
|
||||
private SelectionMode selectionMode = SelectionMode.NONE;
|
||||
private Set<Integer> selection = new HashSet<>();
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Defines the possible modes of selection that can be performed by the user
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static enum SelectionMode {
|
||||
/**
|
||||
* Selection is completely ignored.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Only a single element can be selected.
|
||||
*/
|
||||
SINGLE,
|
||||
|
||||
/**
|
||||
* Multiple elements can be selected regardless of their position.
|
||||
*/
|
||||
MULTIPLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link ComponentList}.
|
||||
*
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
|
||||
|
||||
/**
|
||||
* Removes all child components and then adds all components representing the
|
||||
* elements of the {@link Model}.
|
||||
*
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void synchronizeModel() {
|
||||
if (model != null) {
|
||||
removeAll();
|
||||
model.forEach(this::addElement);
|
||||
revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a list element by index. If the element is already selected, it is
|
||||
* removed from the selection.
|
||||
*
|
||||
* @param index the index of the selected component
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void selectElement(int index) {
|
||||
final JComponent element = getComponent(index);
|
||||
if (selection.contains(index)) {
|
||||
|
||||
// Deselect if clicked again
|
||||
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
|
||||
selection.remove(index);
|
||||
|
||||
} else {
|
||||
|
||||
// Remove old selection if single selection is enabled
|
||||
if (selectionMode == SelectionMode.SINGLE) clearSelection();
|
||||
|
||||
// Select item
|
||||
if (selectionMode != SelectionMode.NONE) {
|
||||
|
||||
// Assign new selection
|
||||
selection.add(index);
|
||||
|
||||
// Update element
|
||||
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
|
||||
}
|
||||
}
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current selection.
|
||||
*
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public void clearSelection() {
|
||||
if (selectionHandler != null) selection.forEach(i -> selectionHandler.selectionChanged(model.get(i), getComponent(i), false));
|
||||
selection.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an object to the list by rendering it with the current
|
||||
* {@link Renderer}.
|
||||
*
|
||||
* @param elem the element to add
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
void addElement(E elem) {
|
||||
if (renderer != null) {
|
||||
final JComponent component = renderer.getListCellComponent(this, elem);
|
||||
component.addMouseListener(getSelectionListener(getComponentCount()));
|
||||
add(component, getComponentCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param componentIndex the index of the list component to which the mouse
|
||||
* listener will be added
|
||||
* @return a mouse listener calling the
|
||||
* {@link ComponentList#selectElement(int)} method with the
|
||||
* component's index when a left click is performed by the user
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private MouseListener getSelectionListener(int componentIndex) {
|
||||
return new MouseAdapter() {
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) selectElement(componentIndex); }
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent(int n) { return (JComponent) super.getComponent(n); }
|
||||
|
||||
/**
|
||||
* @return a set of all selected indices
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Set<Integer> getSelection() { return selection; }
|
||||
|
||||
/**
|
||||
* @return a set of all selected elements
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Set<E> getSelectedElements() {
|
||||
var selectedElements = new HashSet<E>();
|
||||
selection.forEach(i -> selectedElements.add(model.get(i)));
|
||||
return selectedElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the index of an arbitrary selected element
|
||||
* @throws java.util.NoSuchElementException if no selection is present
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public int getSingleSelection() { return selection.stream().findAny().get(); }
|
||||
|
||||
/**
|
||||
* @return an arbitrary selected element
|
||||
* @throws java.util.NoSuchElementException if no selection is present
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public E getSingleSelectedElement() { return model.get(getSingleSelection()); }
|
||||
|
||||
/**
|
||||
* @return the model
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Model<E> getModel() { return model; }
|
||||
|
||||
/**
|
||||
* Sets the list model providing the list elements to render. The rendered
|
||||
* components will be synchronized with the contents of the new model or removed
|
||||
* if the new model is {@code null}.
|
||||
*
|
||||
* @param model the list model to set
|
||||
* @return this component list
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public ComponentList<E> setModel(Model<E> model) {
|
||||
|
||||
// Remove old model
|
||||
if (this.model != null) this.model.setComponentList(null);
|
||||
|
||||
// Synchronize with new model
|
||||
this.model = model;
|
||||
if (model != null) model.setComponentList(this);
|
||||
synchronizeModel();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the renderer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Renderer<E> getRenderer() { return renderer; }
|
||||
|
||||
/**
|
||||
* @param renderer the renderer to set
|
||||
* @return this component list
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ComponentList<E> setRenderer(Renderer<E> renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the selection mode
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public SelectionMode getSelectionMode() { return selectionMode; }
|
||||
|
||||
/**
|
||||
* Sets a new selection mode. The current selection will be cleared during this
|
||||
* action.
|
||||
*
|
||||
* @param selectionMode the selection mode to set
|
||||
* @return this component list
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ComponentList<E> setSelectionMode(SelectionMode selectionMode) {
|
||||
this.selectionMode = selectionMode;
|
||||
clearSelection();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the selection handler
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public SelectionHandler<E> getSelectionHandler() { return selectionHandler; }
|
||||
|
||||
/**
|
||||
* @param selectionHandler the selection handler to set
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setSelectionHandler(SelectionHandler<E> selectionHandler) { this.selectionHandler = selectionHandler; }
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
package envoy.client.ui.list;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Stores objects that will be displayed in a {@link ComponentList}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Model.java</strong><br>
|
||||
* Created: <strong>25.01.2020</strong><br>
|
||||
*
|
||||
* @param <E> the type of object displayed in this list
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public final class Model<E> implements Iterable<E>, Serializable {
|
||||
|
||||
private List<E> elements = new ArrayList<>();
|
||||
transient private ComponentList<E> componentList;
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Adds an element to this model and notifies the associated
|
||||
* {@link ComponentList} to add the corresponding component.
|
||||
*
|
||||
* @param e the element to add
|
||||
* @return {@code true}
|
||||
* @see java.util.List#add(java.lang.Object)
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public boolean add(E e) {
|
||||
if (componentList != null) {
|
||||
componentList.addElement(e);
|
||||
componentList.revalidate();
|
||||
}
|
||||
return elements.add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from this model and clears the associated
|
||||
* {@link ComponentList}.
|
||||
*
|
||||
* @see java.util.List#clear()
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void clear() {
|
||||
elements.clear();
|
||||
if (componentList != null) componentList.removeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index the index to retrieve the element from
|
||||
* @return the element located at the index
|
||||
* @see java.util.List#get(int)
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public E get(int index) { return elements.get(index); }
|
||||
|
||||
/**
|
||||
* Removes the element at a specific index from this model and the corresponding
|
||||
* component from the {@link ComponentList}.
|
||||
*
|
||||
* @param index the index of the element to remove
|
||||
* @return the removed element
|
||||
* @see java.util.List#remove(int)
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public E remove(int index) {
|
||||
if (componentList != null) componentList.remove(index);
|
||||
return elements.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of elements in this list model
|
||||
* @see java.util.List#size()
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public int size() { return elements.size(); }
|
||||
|
||||
/**
|
||||
* @return {@code true} if this model contains no elements
|
||||
* @see java.util.List#isEmpty()
|
||||
*/
|
||||
public boolean isEmpty() { return elements.isEmpty(); }
|
||||
|
||||
/**
|
||||
* @return an iterator over the elements of this list model
|
||||
* @see java.util.List#iterator()
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<>() {
|
||||
|
||||
Iterator<E> iter = elements.iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() { return iter.hasNext(); }
|
||||
|
||||
@Override
|
||||
public E next() { return iter.next(); }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the component list displaying the elements of this model and triggers a
|
||||
* synchronization.
|
||||
*
|
||||
* @param componentList the component list to set
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
void setComponentList(ComponentList<E> componentList) {
|
||||
this.componentList = componentList;
|
||||
if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel();
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package envoy.client.ui.list;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* Allows a {@link ComponentList} convert its elements into Swing components
|
||||
* that can be rendered.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Renderer.java</strong><br>
|
||||
* Created: <strong>25.01.2020</strong><br>
|
||||
*
|
||||
* @param <E> the type of object displayed in this list
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Renderer<E> {
|
||||
|
||||
/**
|
||||
* Provides a Swing component representing a list element.
|
||||
*
|
||||
* @param list the list in which the component will be displayed
|
||||
* @param value the list element that will be converted
|
||||
* @param isSelected {@code true} if the user has selected the list cell in
|
||||
* which the list element is rendered
|
||||
* @return the component representing the list element
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
JComponent getListCellComponent(ComponentList<? extends E> list, E value);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package envoy.client.ui.list;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* Handles the selection of elements in a {@link ComponentList}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong>
|
||||
* File: <strong>SelectionHandler.java</strong>
|
||||
* Created: <strong>21.03.2020</strong>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @param <E> the type of the underlying {@link ComponentList}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SelectionHandler<E> {
|
||||
|
||||
/**
|
||||
* Notifies the handler about a selection.
|
||||
*
|
||||
* @param element the selected element
|
||||
* @param component the selected component
|
||||
* @param isSelected contains the selection state
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
void selectionChanged(E element, JComponent component, boolean isSelected);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
/**
|
||||
* This package defines a Swing component that can be used to display lists of
|
||||
* other components to the user.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
package envoy.client.ui.list;
|
@ -1,73 +0,0 @@
|
||||
package envoy.client.ui.list_component;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.event.SendEvent;
|
||||
import envoy.client.ui.list.ComponentList;
|
||||
import envoy.client.ui.primary.PrimaryButton;
|
||||
import envoy.data.User;
|
||||
import envoy.event.ContactOperationEvent;
|
||||
import envoy.event.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong>
|
||||
* File: <strong>ContactSearchComponent.java</strong>
|
||||
* Created: <strong>21.03.2020</strong>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContactSearchComponent extends JComponent {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* @param list the {@link ComponentList} that is used to display search results
|
||||
* @param user the {@link User} that appears as a search result
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContactSearchComponent(ComponentList<? extends User> list, User user) {
|
||||
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||
|
||||
setBackground(list.getBackground());
|
||||
setForeground(list.getForeground());
|
||||
|
||||
JLabel display = new JLabel(user.getName());
|
||||
display.setForeground(Settings.getInstance().getCurrentTheme().getTextColor());
|
||||
display.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
display.setAlignmentY(Component.CENTER_ALIGNMENT);
|
||||
display.setFont(new Font("Arial", Font.PLAIN, 16));
|
||||
add(display);
|
||||
|
||||
PrimaryButton add = new PrimaryButton("+");
|
||||
add.setFont(new Font("Arial", Font.PLAIN, 19));
|
||||
add.setPreferredSize(new Dimension(45, 45));
|
||||
add.setMinimumSize(new Dimension(45, 45));
|
||||
add.setMaximumSize(new Dimension(45, 45));
|
||||
|
||||
add.setBackground(list.getBackground());
|
||||
add.setForeground(list.getForeground());
|
||||
|
||||
add.addActionListener(evt -> {
|
||||
ContactOperationEvent contactsOperationEvent = new ContactOperationEvent(user, ContactOperationEvent.Operation.ADD);
|
||||
EventBus.getInstance().dispatch(contactsOperationEvent);
|
||||
EventBus.getInstance().dispatch(new SendEvent(contactsOperationEvent));
|
||||
});
|
||||
|
||||
add(add);
|
||||
|
||||
// Define some space to the messages below
|
||||
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder()));
|
||||
|
||||
// Define a maximum height of 50px
|
||||
Dimension size = new Dimension(435, 50);
|
||||
setMaximumSize(size);
|
||||
setMinimumSize(size);
|
||||
setPreferredSize(size);
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
package envoy.client.ui.list_component;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.EnumMap;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import envoy.client.data.Chat;
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.ui.Color;
|
||||
import envoy.client.ui.IconUtil;
|
||||
import envoy.client.ui.list.ComponentList;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.data.User;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong>
|
||||
* File: <strong>MessageComponent.java</strong>
|
||||
* Created: <strong>21.03.2020</strong>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class MessageComponent extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private static EnumMap<MessageStatus, ImageIcon> statusIcons;
|
||||
private static ImageIcon forwardIcon;
|
||||
|
||||
static {
|
||||
try {
|
||||
statusIcons = IconUtil.loadByEnum(MessageStatus.class, 16);
|
||||
forwardIcon = IconUtil.load("/icons/forward.png", 16);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list the {@link ComponentList} that displays this {@link Chat}
|
||||
* @param message the {@link Message} to display
|
||||
* @param senderId the id of the {@link User} who sends messages from this
|
||||
* account
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public MessageComponent(ComponentList<? extends Message> list, Message message, long senderId) {
|
||||
var width = list.getMaximumSize().width;
|
||||
final var theme = Settings.getInstance().getCurrentTheme();
|
||||
final int padding = (int) (width * 0.35);
|
||||
|
||||
GridBagLayout gbl_panel = new GridBagLayout();
|
||||
gbl_panel.columnWidths = new int[] { 1, 1 };
|
||||
gbl_panel.rowHeights = new int[] { 1, 1 };
|
||||
gbl_panel.columnWeights = new double[] { 1, 1 };
|
||||
gbl_panel.rowWeights = new double[] { 1, 1 };
|
||||
|
||||
setLayout(gbl_panel);
|
||||
setBackground(theme.getCellColor());
|
||||
|
||||
// Date Label - The Label that displays the creation date of a message
|
||||
var dateLabel = new JLabel(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(message.getCreationDate()));
|
||||
dateLabel.setForeground(theme.getDateColor());
|
||||
dateLabel.setAlignmentX(1f);
|
||||
dateLabel.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||
dateLabel.setPreferredSize(dateLabel.getPreferredSize());
|
||||
|
||||
var gbc_dateLabel = new GridBagConstraints();
|
||||
gbc_dateLabel.fill = GridBagConstraints.BOTH;
|
||||
gbc_dateLabel.gridx = 0;
|
||||
gbc_dateLabel.gridy = 0;
|
||||
add(dateLabel, gbc_dateLabel);
|
||||
|
||||
// Message area - The JTextArea that displays the text content of a message.
|
||||
var messageTextArea = new JTextArea(message.getText());
|
||||
messageTextArea.setLineWrap(true);
|
||||
messageTextArea.setWrapStyleWord(true);
|
||||
messageTextArea.setForeground(theme.getTextColor());
|
||||
messageTextArea.setAlignmentX(0.5f);
|
||||
messageTextArea.setBackground(theme.getCellColor());
|
||||
messageTextArea.setEditable(false);
|
||||
var font = new Font("Arial", Font.PLAIN, 14);
|
||||
messageTextArea.setFont(font);
|
||||
messageTextArea.setSize(width - padding - 16, 10);
|
||||
|
||||
var gbc_messageTextArea = new GridBagConstraints();
|
||||
gbc_messageTextArea.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_messageTextArea.gridx = 0;
|
||||
gbc_messageTextArea.gridy = 1;
|
||||
add(messageTextArea, gbc_messageTextArea);
|
||||
|
||||
// Status Label - displays the status of the message
|
||||
var statusLabel = new JLabel(statusIcons.get(message.getStatus()));
|
||||
|
||||
var gbc_statusLabel = new GridBagConstraints();
|
||||
gbc_statusLabel.gridx = 1;
|
||||
gbc_statusLabel.gridy = 1;
|
||||
add(statusLabel, gbc_statusLabel);
|
||||
|
||||
// Forwarding
|
||||
if (message.isForwarded()) {
|
||||
var forwardLabel = new JLabel("Forwarded", forwardIcon, SwingConstants.CENTER);
|
||||
forwardLabel.setBackground(getBackground());
|
||||
forwardLabel.setForeground(Color.lightGray);
|
||||
|
||||
var gbc_forwardLabel = new GridBagConstraints();
|
||||
gbc_forwardLabel.fill = GridBagConstraints.BOTH;
|
||||
gbc_forwardLabel.gridx = 1;
|
||||
gbc_forwardLabel.gridy = 0;
|
||||
add(forwardLabel, gbc_forwardLabel);
|
||||
}
|
||||
|
||||
// Define an etched border and some space to the messages below
|
||||
var ours = senderId == message.getSenderID();
|
||||
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, ours ? padding : 10, 10, ours ? 0 : padding),
|
||||
BorderFactory.createEtchedBorder()));
|
||||
|
||||
var size = new Dimension(width - 50, getPreferredSize().height);
|
||||
|
||||
setPreferredSize(size);
|
||||
setMinimumSize(size);
|
||||
setMaximumSize(size);
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package envoy.client.ui.list_component;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.ui.Color;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.data.User;
|
||||
import envoy.data.User.UserStatus;
|
||||
|
||||
/**
|
||||
* Displays a {@link User}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong>
|
||||
* File: <strong>UserComponent.java</strong>
|
||||
* Created: <strong>21.03.2020</strong>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class UserComponent extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* @param user the {@link User} whose information is displayed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public UserComponent(User user) {
|
||||
final Theme theme = Settings.getInstance().getCurrentTheme();
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
// Panel background
|
||||
setBackground(theme.getCellColor());
|
||||
setOpaque(true);
|
||||
setPreferredSize(new Dimension(100, 35));
|
||||
|
||||
// TODO add profile picture support in BorderLayout.West
|
||||
|
||||
JLabel username = new JLabel(user.getName());
|
||||
username.setForeground(theme.getUserNameColor());
|
||||
add(username, BorderLayout.CENTER);
|
||||
|
||||
final UserStatus status = user.getStatus();
|
||||
JLabel statusLabel = new JLabel(status.toString());
|
||||
Color foreground;
|
||||
switch (status) {
|
||||
case AWAY:
|
||||
foreground = Color.yellow;
|
||||
break;
|
||||
case BUSY:
|
||||
foreground = Color.blue;
|
||||
break;
|
||||
case ONLINE:
|
||||
foreground = Color.green;
|
||||
break;
|
||||
default:
|
||||
foreground = Color.lightGray;
|
||||
break;
|
||||
}
|
||||
statusLabel.setForeground(foreground);
|
||||
add(statusLabel, BorderLayout.NORTH);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* This package contains swing components that can be displayed by
|
||||
* {@link envoy.client.ui.list.ComponentList}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>21 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.list_component;
|
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* This package contains classes defining the user interface.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
|
@ -1,63 +0,0 @@
|
||||
package envoy.client.ui.primary;
|
||||
|
||||
import java.awt.Graphics;
|
||||
|
||||
import javax.swing.JButton;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>PrimaryButton.javaEvent.java</strong><br>
|
||||
* Created: <strong>07.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public class PrimaryButton extends JButton {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private int arcSize;
|
||||
|
||||
/**
|
||||
* Creates a primary button
|
||||
*
|
||||
* @param title the title of the button
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public PrimaryButton(String title) { this(title, 6); }
|
||||
|
||||
/**
|
||||
* Creates a primary button
|
||||
*
|
||||
* @param title the title of the button
|
||||
* @param arcSize the size of the arc used to draw the round button edges
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public PrimaryButton(String title, int arcSize) {
|
||||
super(title);
|
||||
setBorderPainted(false);
|
||||
setFocusPainted(false);
|
||||
setContentAreaFilled(false);
|
||||
this.arcSize = arcSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(getBackground());
|
||||
g.fillRoundRect(0, 0, getWidth(), getHeight(), arcSize, arcSize);
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the arcSize
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public int getArcSize() { return arcSize; }
|
||||
|
||||
/**
|
||||
* @param arcSize the arcSize to set
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public void setArcSize(int arcSize) { this.arcSize = arcSize; }
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
package envoy.client.ui.primary;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.plaf.basic.BasicScrollBarUI;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.ui.Theme;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>PrimaryScrollBar.java</strong><br>
|
||||
* Created: <strong>14.12.2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public class PrimaryScrollBar extends BasicScrollBarUI {
|
||||
|
||||
private final Dimension d = new Dimension();
|
||||
private final int arcSize;
|
||||
private final Color scrollBarColor;
|
||||
private final Color hoverColor;
|
||||
private final Color draggingColor;
|
||||
private final boolean isVertical;
|
||||
|
||||
/**
|
||||
* Initializes a {@link PrimaryScrollBar} with a color scheme.
|
||||
*
|
||||
* @param arcSize the size of the arc used to draw the round scroll bar
|
||||
* edges
|
||||
* @param scrollBarColor the default color
|
||||
* @param hoverColor the color while hovering
|
||||
* @param draggingColor the color while dragging
|
||||
* @param isVertical indicates whether this is a vertical
|
||||
* {@link PrimaryScrollBar}
|
||||
*/
|
||||
public PrimaryScrollBar(int arcSize, Color scrollBarColor, Color hoverColor, Color draggingColor, boolean isVertical) {
|
||||
this.arcSize = arcSize;
|
||||
this.scrollBarColor = scrollBarColor;
|
||||
this.hoverColor = hoverColor;
|
||||
this.draggingColor = draggingColor;
|
||||
this.isVertical = isVertical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a {@link PrimaryScrollBar} using a color scheme specified in a
|
||||
* {@link Theme}
|
||||
*
|
||||
* @param theme the {@link Theme} to be applied to this
|
||||
* {@link PrimaryScrollBar}
|
||||
* @param isVertical indicates whether this is a vertical
|
||||
* {@link PrimaryScrollBar}
|
||||
*/
|
||||
public PrimaryScrollBar(Theme theme, boolean isVertical) {
|
||||
this(5, theme.getInteractableBackgroundColor(), new Color(theme.getInteractableBackgroundColor().getRGB() - 50),
|
||||
new Color(theme.getInteractableBackgroundColor().getRGB() + 170), isVertical);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected JButton createDecreaseButton(int orientation) {
|
||||
JButton button = new JButton();
|
||||
button.setPreferredSize(d);
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected JButton createIncreaseButton(int orientation) {
|
||||
JButton button = new JButton();
|
||||
button.setPreferredSize(d);
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void paintTrack(Graphics g, JComponent c, Rectangle r) {}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void paintThumb(Graphics g, JComponent c, Rectangle r) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
Color color;
|
||||
JScrollBar sb = (JScrollBar) c;
|
||||
|
||||
if (!sb.isEnabled()) return;
|
||||
|
||||
if (isDragging) color = draggingColor;
|
||||
else if (isThumbRollover()) color = hoverColor;
|
||||
else color = scrollBarColor;
|
||||
|
||||
g2.setPaint(color);
|
||||
if (isVertical) {
|
||||
g2.fillRoundRect(r.x - 9, r.y, r.width, r.height, arcSize, arcSize);
|
||||
g2.setPaint(Settings.getInstance().getCurrentTheme().getCellColor());
|
||||
g2.drawRoundRect(r.x - 9, r.y, r.width, r.height, arcSize, arcSize);
|
||||
} else {
|
||||
g2.fillRoundRect(r.x, r.y + 9, r.width, r.height - 10, arcSize, arcSize);
|
||||
g2.setPaint(Settings.getInstance().getCurrentTheme().getCellColor());
|
||||
g2.drawRoundRect(r.x, r.y + 9, r.width, r.height - 10, arcSize, arcSize);
|
||||
}
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void setThumbBounds(int x, int y, int width, int height) {
|
||||
super.setThumbBounds(x, y, width, height);
|
||||
scrollbar.repaint();
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package envoy.client.ui.primary;
|
||||
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
import envoy.client.ui.Theme;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>PrimaryScrollPane.java</strong><br>
|
||||
* Created: <strong>15 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
*/
|
||||
public class PrimaryScrollPane extends JScrollPane {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private int verticalScrollBarMaximum = getVerticalScrollBar().getMaximum();
|
||||
private boolean chatOpened = false;
|
||||
|
||||
/**
|
||||
* Initializes a {@link JScrollPane} with the primary Envoy design scheme
|
||||
*
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public PrimaryScrollPane() { setBorder(null); }
|
||||
|
||||
/**
|
||||
* Styles the vertical and horizontal scroll bars.
|
||||
*
|
||||
* @param theme the color set used to color the component
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public void applyTheme(Theme theme) {
|
||||
setForeground(theme.getBackgroundColor());
|
||||
setBackground(theme.getCellColor());
|
||||
|
||||
getVerticalScrollBar().setBackground(theme.getCellColor());
|
||||
getVerticalScrollBar().setUI(new PrimaryScrollBar(theme, true));
|
||||
|
||||
getHorizontalScrollBar().setBackground(theme.getCellColor());
|
||||
getHorizontalScrollBar().setUI(new PrimaryScrollBar(theme, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements <b>autoscroll functionality</b> for the vertical scroll bar. </br>
|
||||
* </br>
|
||||
* Functionality to automatically scroll down when user views </br>
|
||||
* the bottom of the chat while there are new messages added. </br>
|
||||
* </br>
|
||||
* When chat is opened, the vertical scroll bar starts at the bottom. </br>
|
||||
* </br>
|
||||
* When rereading messages, the chat doesn't scroll down if new messages </br>
|
||||
* are added. (Besides see first point)
|
||||
*
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public void autoscroll() {
|
||||
// Automatic scrolling to the bottom
|
||||
getVerticalScrollBar().addAdjustmentListener(e -> {
|
||||
if (verticalScrollBarMaximum == e.getAdjustable().getMaximum()) return;
|
||||
|
||||
if (chatOpened) {
|
||||
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
|
||||
verticalScrollBarMaximum = getVerticalScrollBar().getMaximum();
|
||||
chatOpened = false;
|
||||
return;
|
||||
}
|
||||
if (getVerticalScrollBar().getValue() + getVerticalScrollBar().getVisibleAmount() + 100 >= getVerticalScrollBar().getMaximum()) {
|
||||
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
|
||||
verticalScrollBarMaximum = getVerticalScrollBar().getMaximum();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a chat being opened by the user to this {@link PrimaryScrollPane}
|
||||
* triggering it to automatically scroll down.
|
||||
*
|
||||
* @param chatOpened indicates the chat opening status
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public void setChatOpened(boolean chatOpened) { this.chatOpened = chatOpened; }
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package envoy.client.ui.primary;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>PrimaryTextArea.javaEvent.java</strong><br>
|
||||
* Created: <strong>07.12.2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public class PrimaryTextArea extends JTextArea {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
private int arcSize;
|
||||
|
||||
/**
|
||||
* Creates the text area
|
||||
*
|
||||
* @param borderSpace the space between components
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public PrimaryTextArea(int borderSpace) { this(6, borderSpace); }
|
||||
|
||||
/**
|
||||
* Creates the text area
|
||||
*
|
||||
* @param arcSize is the diameter of the arc at the four corners.
|
||||
* @param borderSpace is the insets of the border on all four sides.
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public PrimaryTextArea(int arcSize, int borderSpace) {
|
||||
super();
|
||||
setWrapStyleWord(true);
|
||||
setLineWrap(true);
|
||||
setBorder(null);
|
||||
setFont(new Font("Arial", Font.PLAIN, 17));
|
||||
setBorder(new EmptyBorder(borderSpace, borderSpace, borderSpace, borderSpace));
|
||||
setOpaque(false);
|
||||
|
||||
this.arcSize = arcSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.setColor(getBackground());
|
||||
g.fillRoundRect(0, 0, getWidth(), getHeight(), arcSize, arcSize);
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the arcSize - the diameter of the arc at the four corners.
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public int getArcSize() { return arcSize; }
|
||||
|
||||
/**
|
||||
* @param arcSize the arcSize to set
|
||||
* @since Envoy 0.2-alpha
|
||||
*/
|
||||
public void setArcSize(int arcSize) { this.arcSize = arcSize; }
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package envoy.client.ui.primary;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
|
||||
import javax.swing.JButton;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.data.SettingsItem;
|
||||
import envoy.client.ui.Color;
|
||||
|
||||
/**
|
||||
* This component can be used to toggle between two options. This will change
|
||||
* the state of a {@code boolean} {@link SettingsItem}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>PrimaryToggleSwitch.java</strong><br>
|
||||
* Created: <strong>21 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public class PrimaryToggleSwitch extends JButton {
|
||||
|
||||
private boolean state;
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Initializes a {@link PrimaryToggleSwitch}.
|
||||
*
|
||||
* @param settingsItem the {@link SettingsItem} that is controlled by this
|
||||
* {@link PrimaryToggleSwitch}
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public PrimaryToggleSwitch(SettingsItem<Boolean> settingsItem) {
|
||||
setPreferredSize(new Dimension(50, 25));
|
||||
setMinimumSize(new Dimension(50, 25));
|
||||
setMaximumSize(new Dimension(50, 25));
|
||||
|
||||
setBorderPainted(false);
|
||||
setFocusPainted(false);
|
||||
setContentAreaFilled(false);
|
||||
|
||||
state = settingsItem.get();
|
||||
addActionListener((evt) -> { state = !state; settingsItem.set(state); revalidate(); repaint(); });
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
g.setColor(state ? Color.GREEN : Color.LIGHT_GRAY);
|
||||
g.fillRoundRect(0, 0, getWidth(), getHeight(), 25, 25);
|
||||
|
||||
g.setColor(Settings.getInstance().getCurrentTheme().getInteractableBackgroundColor());
|
||||
g.fillRoundRect(state ? 25 : 0, 0, 25, 25, 25, 25);
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
/**
|
||||
* This package defines all "primary" components that were defined specifically
|
||||
* for the visual improvement of Envoy. However, they can still be used in
|
||||
* general for other projects.<br>
|
||||
* Primary elements are supposed to provide the main functionality of a UI
|
||||
* component.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>14 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.primary;
|
@ -1,65 +0,0 @@
|
||||
package envoy.client.ui.renderer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.ListCellRenderer;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.data.User;
|
||||
import envoy.data.User.UserStatus;
|
||||
|
||||
/**
|
||||
* Defines how the {@code UserList} is displayed.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>UserListRenderer.java</strong><br>
|
||||
* Created: <strong>12 Oct 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends User> list, User value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
if (isSelected) {
|
||||
setBackground(list.getSelectionBackground());
|
||||
setForeground(list.getSelectionForeground());
|
||||
} else {
|
||||
setBackground(list.getBackground());
|
||||
setForeground(list.getForeground());
|
||||
}
|
||||
|
||||
// Enable background rendering
|
||||
setOpaque(true);
|
||||
|
||||
final String name = value.getName();
|
||||
final UserStatus status = value.getStatus();
|
||||
|
||||
this.setPreferredSize(new Dimension(100, 35));
|
||||
|
||||
// Getting the UserNameColor of the current theme
|
||||
String textColor = null;
|
||||
textColor = Settings.getInstance().getCurrentTheme().getUserNameColor().toHex();
|
||||
switch (status) {
|
||||
case ONLINE:
|
||||
setText(String
|
||||
.format("<html><p style=\"color:#03fc20\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", status, textColor, name));
|
||||
break;
|
||||
case OFFLINE:
|
||||
setText(String
|
||||
.format("<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", status, textColor, name));
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* This package contains all Envoy-specific renderers for lists that store an
|
||||
* arbitrary number of JComponents.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>14 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.renderer;
|
@ -0,0 +1,57 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.data.SettingsItem;
|
||||
import envoy.client.event.ThemeChangeEvent;
|
||||
import envoy.data.User.UserStatus;
|
||||
import envoy.event.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>GeneralSettingsPane.java</strong><br>
|
||||
* Created: <strong>18.04.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class GeneralSettingsPane extends SettingsPane {
|
||||
|
||||
private static final Settings settings = Settings.getInstance();
|
||||
|
||||
/**
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public GeneralSettingsPane() {
|
||||
super("General");
|
||||
final var vbox = new VBox();
|
||||
|
||||
// TODO: Support other value types
|
||||
List.of("onCloseMode", "enterToSend")
|
||||
.stream()
|
||||
.map(settings.getItems()::get)
|
||||
.map(i -> new SettingsCheckbox((SettingsItem<Boolean>) i))
|
||||
.forEach(vbox.getChildren()::add);
|
||||
|
||||
final var combobox = new ComboBox<String>();
|
||||
combobox.getItems().add("dark");
|
||||
combobox.getItems().add("light");
|
||||
combobox.setValue(settings.getCurrentTheme());
|
||||
combobox.setOnAction(
|
||||
e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent(combobox.getValue())); });
|
||||
vbox.getChildren().add(combobox);
|
||||
|
||||
final var statusComboBox = new ComboBox<UserStatus>();
|
||||
statusComboBox.getItems().setAll(UserStatus.values());
|
||||
statusComboBox.setValue(UserStatus.ONLINE);
|
||||
// TODO add action when value is changed
|
||||
statusComboBox.setOnAction(e -> {});
|
||||
vbox.getChildren().add(statusComboBox);
|
||||
|
||||
getChildren().add(vbox);
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTextPane;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.data.SettingsItem;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Displays GUI components that allow general settings regarding the client.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>GeneralSettingsPanel.java</strong><br>
|
||||
* Created: <strong>21 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public class GeneralSettingsPanel extends SettingsPanel {
|
||||
|
||||
private Theme theme;
|
||||
|
||||
private static final String[] items = { "onCloseMode", "enterToSend" };
|
||||
private static final Logger logger = EnvoyLog.getLogger(GeneralSettingsPanel.class);
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* This is the constructor for the General class. Here the user can set general
|
||||
* settings for the client.
|
||||
*
|
||||
* @param parent the {@link SettingsScreen} as a part of which this
|
||||
* {@link SettingsPanel} is displayed
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public GeneralSettingsPanel(SettingsScreen parent) {
|
||||
super(parent);
|
||||
theme = Settings.getInstance().getCurrentTheme();
|
||||
|
||||
setBackground(theme.getCellColor());
|
||||
|
||||
GridBagLayout gbl_general = new GridBagLayout();
|
||||
gbl_general.columnWidths = new int[] { 1, 1 };
|
||||
gbl_general.rowHeights = new int[] { 1, 1, 1 };
|
||||
gbl_general.columnWeights = new double[] { 1.0, 0.1 };
|
||||
gbl_general.rowWeights = new double[] { 0.02, 0.02, 1.0 };
|
||||
|
||||
setLayout(gbl_general);
|
||||
|
||||
for (int i = 0; i < items.length; i++)
|
||||
try {
|
||||
createSettingElement(i, Settings.getInstance().getItems().get(items[i]));
|
||||
} catch (SecurityException | ReflectiveOperationException e) {
|
||||
logger.log(Level.WARNING, "Could not create settings item", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createSettingElement(int gridy, SettingsItem<?> settingsItem) throws SecurityException, ReflectiveOperationException {
|
||||
JTextPane descriptionText = new JTextPane();
|
||||
|
||||
JComponent settingComponent = settingsItem.getComponent();
|
||||
|
||||
GridBagConstraints gbc_toggleSwitch = new GridBagConstraints();
|
||||
gbc_toggleSwitch.gridx = 1;
|
||||
gbc_toggleSwitch.gridy = gridy;
|
||||
|
||||
add(settingComponent, gbc_toggleSwitch);
|
||||
|
||||
descriptionText.setText(settingsItem.getDescription());
|
||||
descriptionText.setBackground(theme.getBackgroundColor());
|
||||
descriptionText.setForeground(theme.getBackgroundColor().invert());
|
||||
descriptionText.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_descriptionText = new GridBagConstraints();
|
||||
gbc_descriptionText.fill = GridBagConstraints.BOTH;
|
||||
gbc_descriptionText.gridx = 0;
|
||||
gbc_descriptionText.gridy = gridy;
|
||||
gbc_descriptionText.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
add(descriptionText, gbc_descriptionText);
|
||||
}
|
||||
}
|
@ -1,228 +0,0 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextPane;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.ui.primary.PrimaryButton;
|
||||
import envoy.client.ui.primary.PrimaryTextArea;
|
||||
|
||||
/**
|
||||
* Displays window where you can choose a name for the new {@link Theme}.
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>NewThemeScreen.java</strong><br>
|
||||
* Created: <strong>26 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public class NewThemeScreen extends JDialog {
|
||||
|
||||
private final JPanel standardPanel = new JPanel();
|
||||
private final JPanel secondaryPanel = new JPanel();
|
||||
private JTextPane text = new JTextPane();
|
||||
private PrimaryTextArea nameEnterTextArea = new PrimaryTextArea(4);
|
||||
private PrimaryButton confirmButton = new PrimaryButton("Confirm");
|
||||
|
||||
private JTextPane errorText = new JTextPane();
|
||||
private PrimaryButton otherName = new PrimaryButton("Other Name");
|
||||
private PrimaryButton overwrite = new PrimaryButton("Overwrite");
|
||||
|
||||
private final Consumer<String> newThemeAction, modifyThemeAction;
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Creates a window, where you can choose a name for a new {@link Theme}. <br>
|
||||
* There are two versions of this Window. The first one is responsible for
|
||||
* choosing the name, the second one appears, if the name already exists.
|
||||
*
|
||||
* @param parent the dialog is launched with its location relative to
|
||||
* this {@link SettingsScreen}
|
||||
* @param newThemeAction is executed when a new theme name is entered
|
||||
* @param modifyThemeAction is executed when an existing theme name is entered
|
||||
* and confirmed
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public NewThemeScreen(SettingsScreen parent, Consumer<String> newThemeAction, Consumer<String> modifyThemeAction) {
|
||||
this.newThemeAction = newThemeAction;
|
||||
this.modifyThemeAction = modifyThemeAction;
|
||||
|
||||
setLocationRelativeTo(parent);
|
||||
setTitle("New Theme");
|
||||
setModal(true);
|
||||
|
||||
setDimensions(true);
|
||||
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
|
||||
Theme theme = Settings.getInstance().getCurrentTheme();
|
||||
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
standardPanel.setBackground(theme.getBackgroundColor());
|
||||
secondaryPanel.setBackground(theme.getBackgroundColor());
|
||||
loadStandardContent(theme);
|
||||
}
|
||||
|
||||
private void setDimensions(boolean isStandard) {
|
||||
Dimension size = isStandard ? new Dimension(300, 170) : new Dimension(300, 225);
|
||||
setPreferredSize(size);
|
||||
setMinimumSize(size);
|
||||
setMaximumSize(size);
|
||||
}
|
||||
|
||||
private void loadStandardContent(Theme theme) {
|
||||
getContentPane().removeAll();
|
||||
|
||||
// ContentPane
|
||||
GridBagLayout gbl_contentPanel = new GridBagLayout();
|
||||
|
||||
gbl_contentPanel.columnWidths = new int[] { 1, 1 };
|
||||
gbl_contentPanel.rowHeights = new int[] { 1, 1, 1 };
|
||||
gbl_contentPanel.columnWeights = new double[] { 1, 1 };
|
||||
gbl_contentPanel.rowWeights = new double[] { 1, 1, 1 };
|
||||
|
||||
getContentPane().add(standardPanel, BorderLayout.CENTER);
|
||||
standardPanel.setLayout(gbl_contentPanel);
|
||||
|
||||
// text.setFont(new Font());
|
||||
text.setText("Please enter a name for the new Theme");
|
||||
text.setAlignmentX(CENTER_ALIGNMENT);
|
||||
text.setBackground(theme.getCellColor());
|
||||
text.setForeground(theme.getUserNameColor());
|
||||
text.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_text = new GridBagConstraints();
|
||||
gbc_text.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_text.gridx = 0;
|
||||
gbc_text.gridy = 0;
|
||||
gbc_text.gridwidth = 2;
|
||||
gbc_text.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
standardPanel.add(text, gbc_text);
|
||||
|
||||
nameEnterTextArea.setBackground(theme.getCellColor());
|
||||
nameEnterTextArea.setForeground(theme.getTypingMessageColor());
|
||||
nameEnterTextArea.setText("");
|
||||
nameEnterTextArea.setEditable(true);
|
||||
|
||||
GridBagConstraints gbc_input = new GridBagConstraints();
|
||||
gbc_input.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_input.gridx = 0;
|
||||
gbc_input.gridy = 1;
|
||||
gbc_input.gridwidth = 2;
|
||||
gbc_input.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
standardPanel.add(nameEnterTextArea, gbc_input);
|
||||
|
||||
confirmButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
confirmButton.setForeground(theme.getInteractableForegroundColor());
|
||||
|
||||
GridBagConstraints gbc_confirmButton = new GridBagConstraints();
|
||||
gbc_confirmButton.gridx = 0;
|
||||
gbc_confirmButton.gridy = 2;
|
||||
gbc_confirmButton.gridwidth = 2;
|
||||
gbc_confirmButton.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
standardPanel.add(confirmButton, gbc_confirmButton);
|
||||
|
||||
confirmButton.addActionListener((evt) -> {
|
||||
if (!nameEnterTextArea.getText().isEmpty()) if (Settings.getInstance().getThemes().containsKey(nameEnterTextArea.getText())) {
|
||||
// load other panel
|
||||
setDimensions(false);
|
||||
loadSecondaryPage(theme);
|
||||
} else {
|
||||
newThemeAction.accept(nameEnterTextArea.getText());
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadSecondaryPage(Theme theme) {
|
||||
// ContentPane
|
||||
getContentPane().removeAll();
|
||||
|
||||
GridBagLayout gbl_secondaryPanel = new GridBagLayout();
|
||||
|
||||
gbl_secondaryPanel.columnWidths = new int[] { 1, 1 };
|
||||
gbl_secondaryPanel.rowHeights = new int[] { 1, 1, 1, 1 };
|
||||
gbl_secondaryPanel.columnWeights = new double[] { 1, 1 };
|
||||
gbl_secondaryPanel.rowWeights = new double[] { 1, 1, 1, 1 };
|
||||
|
||||
getContentPane().add(secondaryPanel, BorderLayout.CENTER);
|
||||
secondaryPanel.setLayout(gbl_secondaryPanel);
|
||||
|
||||
// text.setFont(new Font());
|
||||
text.setText("Please enter a name for the new Theme");
|
||||
text.setAlignmentX(CENTER_ALIGNMENT);
|
||||
text.setBackground(theme.getCellColor());
|
||||
text.setForeground(theme.getUserNameColor());
|
||||
text.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_text = new GridBagConstraints();
|
||||
gbc_text.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_text.gridx = 0;
|
||||
gbc_text.gridy = 0;
|
||||
gbc_text.gridwidth = 2;
|
||||
gbc_text.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
secondaryPanel.add(text, gbc_text);
|
||||
|
||||
nameEnterTextArea.setBackground(theme.getCellColor());
|
||||
nameEnterTextArea.setForeground(theme.getTypingMessageColor());
|
||||
nameEnterTextArea.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_input = new GridBagConstraints();
|
||||
gbc_input.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_input.gridx = 0;
|
||||
gbc_input.gridy = 1;
|
||||
gbc_input.gridwidth = 2;
|
||||
gbc_input.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
secondaryPanel.add(nameEnterTextArea, gbc_input);
|
||||
|
||||
errorText.setText("The name does already exist. Choose another one or overwrite the old theme.");
|
||||
errorText.setAlignmentX(CENTER_ALIGNMENT);
|
||||
errorText.setBackground(theme.getCellColor());
|
||||
errorText.setForeground(theme.getUserNameColor());
|
||||
errorText.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_errorText = new GridBagConstraints();
|
||||
gbc_errorText.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_errorText.gridx = 0;
|
||||
gbc_errorText.gridy = 2;
|
||||
gbc_errorText.gridwidth = 2;
|
||||
gbc_errorText.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
secondaryPanel.add(errorText, gbc_errorText);
|
||||
|
||||
otherName.setBackground(theme.getInteractableBackgroundColor());
|
||||
otherName.setForeground(theme.getInteractableForegroundColor());
|
||||
|
||||
GridBagConstraints gbc_otherName = new GridBagConstraints();
|
||||
gbc_otherName.gridx = 0;
|
||||
gbc_otherName.gridy = 3;
|
||||
gbc_otherName.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
secondaryPanel.add(otherName, gbc_otherName);
|
||||
|
||||
overwrite.setBackground(theme.getInteractableBackgroundColor());
|
||||
overwrite.setForeground(theme.getInteractableForegroundColor());
|
||||
|
||||
GridBagConstraints gbc_overwrite = new GridBagConstraints();
|
||||
gbc_overwrite.gridx = 1;
|
||||
gbc_overwrite.gridy = 3;
|
||||
gbc_overwrite.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
secondaryPanel.add(overwrite, gbc_overwrite);
|
||||
|
||||
otherName.addActionListener((evt) -> { setDimensions(true); loadStandardContent(theme); });
|
||||
|
||||
overwrite.addActionListener((evt) -> { modifyThemeAction.accept(nameEnterTextArea.getText()); dispose(); });
|
||||
}
|
||||
}
|
32
src/main/java/envoy/client/ui/settings/SettingsCheckbox.java
Normal file
32
src/main/java/envoy/client/ui/settings/SettingsCheckbox.java
Normal file
@ -0,0 +1,32 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.scene.control.CheckBox;
|
||||
|
||||
import envoy.client.data.SettingsItem;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsToggleButton.java</strong><br>
|
||||
* Created: <strong>18.04.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class SettingsCheckbox extends CheckBox {
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link SettingsCheckbox}.
|
||||
*
|
||||
* @param settingsItem the {@link SettingsItem} whose values could be adapted
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public SettingsCheckbox(SettingsItem<Boolean> settingsItem) {
|
||||
super(settingsItem.getUserFriendlyName());
|
||||
setSelected(settingsItem.get());
|
||||
|
||||
// "Schau, es hat sich behindert" - Kai, 2020
|
||||
|
||||
addEventHandler(ActionEvent.ACTION, e -> settingsItem.set(isSelected()));
|
||||
}
|
||||
}
|
24
src/main/java/envoy/client/ui/settings/SettingsPane.java
Normal file
24
src/main/java/envoy/client/ui/settings/SettingsPane.java
Normal file
@ -0,0 +1,24 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import javafx.scene.layout.Pane;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsPane.java</strong><br>
|
||||
* Created: <strong>18.04.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public abstract class SettingsPane extends Pane {
|
||||
|
||||
protected String title;
|
||||
|
||||
protected SettingsPane(String title) { this.title = title; }
|
||||
|
||||
/**
|
||||
* @return the title of this settings pane
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public String getTitle() { return title; }
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
/**
|
||||
* Serves as an interface between {@link SettingsScreen} and different
|
||||
* {@link JPanel}s with actual settings that are defined as sub classes of this
|
||||
* class.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsPanel.java</strong><br>
|
||||
* Created: <strong>20 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public abstract class SettingsPanel extends JPanel {
|
||||
|
||||
protected final SettingsScreen parent;
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Initializes a {@link SettingsPanel}.
|
||||
*
|
||||
* @param parent the {@link SettingsScreen} as a part of which this
|
||||
* {@link SettingsPanel} is displayed
|
||||
*/
|
||||
public SettingsPanel(SettingsScreen parent) { this.parent = parent; }
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.event.ThemeChangeEvent;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.ui.primary.PrimaryButton;
|
||||
import envoy.event.EventBus;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* This class provides the GUI to change the user specific settings.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsScreen.java</strong><br>
|
||||
* Created: <strong>31 Oct 2019</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public class SettingsScreen extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private final JPanel contentPanel = new JPanel();
|
||||
|
||||
// Settings panel list
|
||||
private final DefaultListModel<String> optionsListModel = new DefaultListModel<>();
|
||||
private final JList<String> options = new JList<>(optionsListModel);
|
||||
|
||||
// OK and cancel buttons
|
||||
private final JPanel buttonPane = new JPanel();
|
||||
private final PrimaryButton cancelButton = new PrimaryButton("Cancel");
|
||||
|
||||
private final Insets insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
private SettingsPanel settingsPanel;
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(SettingsScreen.class);
|
||||
|
||||
/**
|
||||
* Initializes the settings screen.
|
||||
*
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public SettingsScreen() {
|
||||
// Initialize settings pages
|
||||
Map<String, Class<? extends SettingsPanel>> panels = new HashMap<>();
|
||||
panels.put("General", GeneralSettingsPanel.class);
|
||||
panels.put("Color Themes", ThemeCustomizationPanel.class);
|
||||
|
||||
setBounds(10, 10, 450, 650);
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
{
|
||||
// ContentPane
|
||||
GridBagLayout gbl_contentPanel = new GridBagLayout();
|
||||
|
||||
gbl_contentPanel.columnWidths = new int[] { 1, 1 };
|
||||
gbl_contentPanel.rowHeights = new int[] { 1 };
|
||||
gbl_contentPanel.columnWeights = new double[] { 0.05, 1.0 };
|
||||
gbl_contentPanel.rowWeights = new double[] { 1.0 };
|
||||
|
||||
getContentPane().add(contentPanel, BorderLayout.CENTER);
|
||||
contentPanel.setLayout(gbl_contentPanel);
|
||||
|
||||
// Constraints for the settings panel
|
||||
GridBagConstraints gbc_panel = new GridBagConstraints();
|
||||
gbc_panel.fill = GridBagConstraints.BOTH;
|
||||
gbc_panel.gridx = 1;
|
||||
gbc_panel.gridy = 0;
|
||||
gbc_panel.anchor = GridBagConstraints.PAGE_START;
|
||||
gbc_panel.insets = insets;
|
||||
|
||||
options.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
options.addListSelectionListener((listSelectionEvent) -> {
|
||||
if (!listSelectionEvent.getValueIsAdjusting()) {
|
||||
// Get selected settings panel
|
||||
final String option = options.getSelectedValue();
|
||||
logger.log(Level.FINEST, "Selected settings panel: " + option);
|
||||
|
||||
// Remove previous settings panel
|
||||
if (settingsPanel != null) contentPanel.remove(settingsPanel);
|
||||
|
||||
try {
|
||||
settingsPanel = panels.get(option).getDeclaredConstructor(getClass()).newInstance(this);
|
||||
|
||||
// Add selected settings panel
|
||||
contentPanel.add(settingsPanel, gbc_panel);
|
||||
revalidate();
|
||||
repaint();
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
logger.log(Level.SEVERE, "Failed to invoke constructor of SettingsPanel " + option, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
options.setFont(new Font("Arial", Font.PLAIN, 14));
|
||||
|
||||
GridBagConstraints gbc_optionsList = new GridBagConstraints();
|
||||
gbc_optionsList.fill = GridBagConstraints.BOTH;
|
||||
gbc_optionsList.gridx = 0;
|
||||
gbc_optionsList.gridy = 0;
|
||||
gbc_optionsList.anchor = GridBagConstraints.PAGE_START;
|
||||
gbc_optionsList.insets = insets;
|
||||
|
||||
panels.keySet().forEach(name -> optionsListModel.addElement(name));
|
||||
contentPanel.add(options, gbc_optionsList);
|
||||
|
||||
// ButtonPane
|
||||
GridBagLayout gbl_buttonPane = new GridBagLayout();
|
||||
gbl_buttonPane.columnWidths = new int[] { 1, 1 };
|
||||
gbl_buttonPane.rowHeights = new int[] { 25 };
|
||||
gbl_buttonPane.columnWeights = new double[] { 1.0, 1.0 };
|
||||
gbl_buttonPane.rowWeights = new double[] { 0.0 };
|
||||
|
||||
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
||||
buttonPane.setLayout(gbl_buttonPane);
|
||||
{
|
||||
cancelButton.setActionCommand("Cancel");
|
||||
cancelButton.setBorderPainted(false);
|
||||
GridBagConstraints gbc_cancelButton = new GridBagConstraints();
|
||||
gbc_cancelButton.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc_cancelButton.insets = insets;
|
||||
gbc_cancelButton.gridx = 0;
|
||||
gbc_cancelButton.gridy = 0;
|
||||
buttonPane.add(cancelButton, gbc_cancelButton);
|
||||
|
||||
cancelButton.addActionListener((evt) -> { dispose(); });
|
||||
}
|
||||
}
|
||||
|
||||
// Apply current theme
|
||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
||||
|
||||
// Respond to theme changes
|
||||
EventBus.getInstance().register(ThemeChangeEvent.class, evt -> applyTheme(evt.get()));
|
||||
|
||||
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
setModal(true);
|
||||
}
|
||||
|
||||
private void applyTheme(Theme theme) {
|
||||
// JDialog
|
||||
setBackground(theme.getBackgroundColor());
|
||||
|
||||
// contentPanel
|
||||
contentPanel.setBackground(theme.getBackgroundColor());
|
||||
|
||||
// buttonPane
|
||||
buttonPane.setBackground(theme.getCellColor());
|
||||
|
||||
// cancelButton
|
||||
cancelButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
cancelButton.setForeground(theme.getInteractableForegroundColor());
|
||||
|
||||
// options
|
||||
options.setSelectionForeground(theme.getUserNameColor());
|
||||
options.setSelectionBackground(theme.getSelectionColor());
|
||||
options.setForeground(theme.getUserNameColor());
|
||||
options.setBackground(theme.getCellColor());
|
||||
}
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.event.ThemeChangeEvent;
|
||||
import envoy.client.ui.Color;
|
||||
import envoy.client.ui.Theme;
|
||||
import envoy.client.ui.primary.PrimaryButton;
|
||||
import envoy.event.EventBus;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Displays GUI components that allow changing the current {@Theme} and creating
|
||||
* new ones.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ThemeCustomizationPanel.java</strong><br>
|
||||
* Created: <strong>20 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public class ThemeCustomizationPanel extends SettingsPanel {
|
||||
|
||||
private JPanel colorsPanel = new JPanel();
|
||||
|
||||
private DefaultComboBoxModel<String> themesModel;
|
||||
private JComboBox<String> themes;
|
||||
private Theme temporaryTheme;
|
||||
private PrimaryButton createThemeButton = new PrimaryButton("Create Theme");
|
||||
|
||||
private boolean themeChanged;
|
||||
private final Insets insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(ThemeCustomizationPanel.class);
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* Initializes a {@link ThemeCustomizationPanel} that enables the user to change
|
||||
* the current {@link Theme} and create new themes as part of the
|
||||
* {@link SettingsScreen}.
|
||||
*
|
||||
* @param parent the {@link SettingsScreen} as a part of which this
|
||||
* {@link SettingsPanel} is displayed
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
public ThemeCustomizationPanel(SettingsScreen parent) {
|
||||
super(parent);
|
||||
temporaryTheme = new Theme("temporaryTheme", Settings.getInstance().getCurrentTheme());
|
||||
|
||||
var themeNames = Settings.getInstance().getThemes().keySet().toArray(new String[0]);
|
||||
String currentThemeName = Settings.getInstance().getCurrentThemeName();
|
||||
for (int i = 0; i < themeNames.length; i++)
|
||||
if (currentThemeName.equals(themeNames[i])) {
|
||||
themeNames[i] = themeNames[0];
|
||||
themeNames[0] = currentThemeName;
|
||||
break;
|
||||
}
|
||||
themesModel = new DefaultComboBoxModel<>(themeNames);
|
||||
themes = new JComboBox<>(themesModel);
|
||||
GridBagLayout gbl_themeLayout = new GridBagLayout();
|
||||
|
||||
gbl_themeLayout.columnWidths = new int[] { 1, 1 };
|
||||
gbl_themeLayout.rowHeights = new int[] { 1, 1, 1 };
|
||||
gbl_themeLayout.columnWeights = new double[] { 1.0, 1.0 };
|
||||
gbl_themeLayout.rowWeights = new double[] { 0.01, 1.0, 0.01 };
|
||||
|
||||
setLayout(gbl_themeLayout);
|
||||
|
||||
themes.setSelectedItem(Settings.getInstance().getCurrentTheme());
|
||||
|
||||
GridBagConstraints gbc_themes = new GridBagConstraints();
|
||||
gbc_themes.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_themes.gridwidth = 2;
|
||||
gbc_themes.gridx = 0;
|
||||
gbc_themes.gridy = 0;
|
||||
gbc_themes.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc_themes.insets = new Insets(10, 10, 20, 10);
|
||||
|
||||
add(themes, gbc_themes);
|
||||
GridBagLayout gbl_colorCustomizations = new GridBagLayout();
|
||||
|
||||
gbl_colorCustomizations.columnWidths = new int[] { 1, 1 };
|
||||
gbl_colorCustomizations.rowHeights = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||
gbl_colorCustomizations.columnWeights = new double[] { 1, 1 };
|
||||
gbl_colorCustomizations.rowWeights = new double[] { 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||
|
||||
colorsPanel.setLayout(gbl_colorCustomizations);
|
||||
|
||||
Theme theme = Settings.getInstance().getCurrentTheme();
|
||||
buildCustomizeElements(theme);
|
||||
|
||||
GridBagConstraints gbc_colorsPanel = new GridBagConstraints();
|
||||
gbc_colorsPanel.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_colorsPanel.gridx = 0;
|
||||
gbc_colorsPanel.gridy = 1;
|
||||
gbc_colorsPanel.gridwidth = 2;
|
||||
gbc_colorsPanel.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc_colorsPanel.insets = insets;
|
||||
|
||||
add(colorsPanel, gbc_colorsPanel);
|
||||
|
||||
createThemeButton.addActionListener((evt) -> {
|
||||
if (themeChanged) {
|
||||
new NewThemeScreen(parent, name -> {
|
||||
// Create new theme
|
||||
logger.log(Level.FINEST, name);
|
||||
Settings.getInstance().addNewThemeToMap(new Theme(name, temporaryTheme));
|
||||
|
||||
// Add new theme name to combo box
|
||||
themesModel.addElement(name);
|
||||
|
||||
// Select new theme name
|
||||
themes.setSelectedIndex(themesModel.getSize() - 1);
|
||||
}, name -> {
|
||||
// Modify theme
|
||||
Settings.getInstance().getThemes().replace(name, new Theme(name, temporaryTheme));
|
||||
if (themes.getSelectedItem().equals(name))
|
||||
EventBus.getInstance().dispatch(new ThemeChangeEvent(Settings.getInstance().getTheme(name)));
|
||||
else themes.setSelectedItem(name);
|
||||
}).setVisible(true);
|
||||
themeChanged = false;
|
||||
}
|
||||
});
|
||||
GridBagConstraints gbc_createThemeButton = new GridBagConstraints();
|
||||
gbc_createThemeButton.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_createThemeButton.gridx = 0;
|
||||
gbc_createThemeButton.gridy = 2;
|
||||
gbc_createThemeButton.anchor = GridBagConstraints.CENTER;
|
||||
gbc_createThemeButton.insets = insets;
|
||||
add(createThemeButton, gbc_createThemeButton);
|
||||
|
||||
colorsPanel.setBackground(theme.getCellColor());
|
||||
|
||||
// Apply theme upon selection
|
||||
themes.addItemListener(e -> {
|
||||
String selectedValue = (String) themes.getSelectedItem();
|
||||
logger.log(Level.FINEST, "Selected theme: " + selectedValue);
|
||||
|
||||
final Theme currentTheme = Settings.getInstance().getTheme(selectedValue);
|
||||
Settings.getInstance().setCurrentTheme(selectedValue);
|
||||
EventBus.getInstance().dispatch(new ThemeChangeEvent(currentTheme));
|
||||
});
|
||||
|
||||
// Apply current theme
|
||||
applyTheme(theme);
|
||||
|
||||
// Respond to theme changes
|
||||
EventBus.getInstance()
|
||||
.register(ThemeChangeEvent.class,
|
||||
evt -> {
|
||||
final Theme currentTheme = evt.get();
|
||||
temporaryTheme = new Theme("temporaryTheme", currentTheme);
|
||||
applyTheme(currentTheme);
|
||||
});
|
||||
}
|
||||
|
||||
private void applyTheme(Theme theme) {
|
||||
// themeContent
|
||||
setForeground(theme.getUserNameColor());
|
||||
setBackground(theme.getCellColor());
|
||||
|
||||
// createThemeButton
|
||||
createThemeButton.setForeground(theme.getInteractableForegroundColor());
|
||||
createThemeButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
|
||||
// themes
|
||||
themes.setBackground(theme.getInteractableBackgroundColor());
|
||||
themes.setForeground(theme.getInteractableForegroundColor());
|
||||
colorsPanel.setBackground(theme.getCellColor());
|
||||
|
||||
// Color panel
|
||||
updateColorVariables(theme);
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void updateColorVariables(Theme theme) {
|
||||
colorsPanel.removeAll();
|
||||
buildCustomizeElements(theme);
|
||||
}
|
||||
|
||||
private void buildCustomizeElements(Theme theme) {
|
||||
buildCustomizeElement(theme, theme.getBackgroundColor(), "Background", "backgroundColor", 1);
|
||||
buildCustomizeElement(theme, theme.getCellColor(), "Cells", "cellColor", 2);
|
||||
buildCustomizeElement(theme, theme.getInteractableForegroundColor(), "Interactable Foreground", "interactableForegroundColor", 3);
|
||||
buildCustomizeElement(theme, theme.getInteractableBackgroundColor(), "Interactable Background", "interactableBackgroundColor", 4);
|
||||
buildCustomizeElement(theme, theme.getTextColor(), "Text Color", "textColor", 5);
|
||||
buildCustomizeElement(theme, theme.getDateColor(), "Date Chat", "dateColorChat", 6);
|
||||
buildCustomizeElement(theme, theme.getSelectionColor(), "Selection", "selectionColor", 7);
|
||||
buildCustomizeElement(theme, theme.getTypingMessageColor(), "Typing Message", "typingMessageColor", 8);
|
||||
buildCustomizeElement(theme, theme.getUserNameColor(), "User Names", "userNameColor", 9);
|
||||
}
|
||||
|
||||
private void buildCustomizeElement(Theme theme, Color color, String name, String colorName, int gridy) {
|
||||
JButton button = new JButton();
|
||||
JTextPane textPane = new JTextPane();
|
||||
|
||||
textPane.setFont(new Font("Arial", Font.PLAIN, 14));
|
||||
textPane.setBackground(theme.getBackgroundColor());
|
||||
textPane.setForeground(theme.getBackgroundColor().invert());
|
||||
textPane.setText(name);
|
||||
textPane.setEditable(false);
|
||||
|
||||
button.setBackground(color);
|
||||
button.setPreferredSize(new Dimension(25, 25));
|
||||
|
||||
button.addActionListener((evt) -> {
|
||||
java.awt.Color c = JColorChooser.showDialog(null, "Choose a color", color);
|
||||
if (c != null) {
|
||||
Color newColor = new Color(c);
|
||||
if (!color.equals(newColor)) {
|
||||
logger.log(Level.FINEST, "New Color: " + newColor);
|
||||
temporaryTheme.setColor(colorName, newColor);
|
||||
themeChanged = true;
|
||||
}
|
||||
button.setBackground(newColor);
|
||||
}
|
||||
});
|
||||
|
||||
GridBagConstraints gbc_textPane = new GridBagConstraints();
|
||||
gbc_textPane.fill = GridBagConstraints.BOTH;
|
||||
gbc_textPane.gridx = 0;
|
||||
gbc_textPane.gridy = gridy;
|
||||
gbc_textPane.anchor = GridBagConstraints.CENTER;
|
||||
gbc_textPane.insets = insets;
|
||||
|
||||
colorsPanel.add(textPane, gbc_textPane);
|
||||
|
||||
GridBagConstraints gbc_button = new GridBagConstraints();
|
||||
gbc_button.fill = GridBagConstraints.BOTH;
|
||||
gbc_button.gridx = 1;
|
||||
gbc_button.gridy = gridy;
|
||||
gbc_button.anchor = GridBagConstraints.CENTER;
|
||||
gbc_button.insets = insets;
|
||||
|
||||
colorsPanel.add(button, gbc_button);
|
||||
}
|
||||
}
|
@ -1,9 +1,14 @@
|
||||
/**
|
||||
* This package contains user interface classes related to the settings screen.
|
||||
* This package contains classes used for representing the settings
|
||||
* visually.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>19 Apr 2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-alpha
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.settings;
|
||||
|
@ -12,6 +12,12 @@ module envoy {
|
||||
requires transitive envoy.common;
|
||||
requires transitive java.desktop;
|
||||
requires transitive java.logging;
|
||||
requires transitive java.naming;
|
||||
requires transitive java.prefs;
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
requires javafx.base;
|
||||
requires javafx.graphics;
|
||||
|
||||
opens envoy.client.ui to javafx.graphics, javafx.fxml;
|
||||
opens envoy.client.ui.controller to javafx.graphics, javafx.fxml;
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
server=http://kske.feste-ip.net
|
||||
port=43315
|
||||
server=localhost
|
||||
port=8080
|
||||
localDB=.\\localDB
|
||||
|
3
src/main/resources/css/base.css
Normal file
3
src/main/resources/css/base.css
Normal file
@ -0,0 +1,3 @@
|
||||
.button {
|
||||
-fx-background-radius: 5em;
|
||||
}
|
4
src/main/resources/css/dark.css
Normal file
4
src/main/resources/css/dark.css
Normal file
@ -0,0 +1,4 @@
|
||||
.button{
|
||||
-fx-background-color: rgb(105,0,153);
|
||||
-fx-text-fill: white;
|
||||
}
|
3
src/main/resources/css/light.css
Normal file
3
src/main/resources/css/light.css
Normal file
@ -0,0 +1,3 @@
|
||||
.button{
|
||||
-fx-background-color: snow;
|
||||
}
|
48
src/main/resources/fxml/ChatScene.fxml
Normal file
48
src/main/resources/fxml/ChatScene.fxml
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.TextArea?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
|
||||
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="350.0" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.ChatScene">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="10.0" percentWidth="20.0" prefWidth="161.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="10.0" percentWidth="65.0" prefWidth="357.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="6.285736083984375" percentWidth="15.0" prefWidth="48.000030517578125" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="10.0" prefHeight="70.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="70.0" prefHeight="326.2857404436384" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="5.0" prefHeight="50.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="15.0" prefHeight="100.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<ListView fx:id="userList" onMouseClicked="#userListClicked" prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1" GridPane.rowSpan="2147483647" />
|
||||
<Label fx:id="contactLabel" prefHeight="16.0" prefWidth="250.0" text="Select a contact to chat with" GridPane.columnSpan="2" />
|
||||
<Button fx:id="settingsButton" mnemonicParsing="true" onAction="#settingsButtonClicked" text="_Settings" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
|
||||
<ListView fx:id="messageList" prefHeight="257.0" prefWidth="155.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" GridPane.rowSpan="2" />
|
||||
<Button fx:id="postButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#postButtonClicked" prefHeight="10.0" prefWidth="65.0" text="_Post" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
|
||||
<TextArea fx:id="messageTextArea" disable="true" onInputMethodTextChanged="#messageTextUpdated" onKeyPressed="#checkKeyCombination" onKeyTyped="#messageTextUpdated" prefHeight="200.0" prefWidth="200.0" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||
<Button mnemonicParsing="true" onAction="#addContactButtonClicked" text="_Add Contacts" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<Label fx:id="remainingChars" disable="true" ellipsisString="" maxHeight="30.0" maxWidth="180.0" prefHeight="30.0" prefWidth="180.0" text="remaining chars: 0/x" textFill="LIME" textOverrun="LEADING_WORD_ELLIPSIS" visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
</children>
|
||||
</GridPane>
|
67
src/main/resources/fxml/ContactSearchScene.fxml
Normal file
67
src/main/resources/fxml/ContactSearchScene.fxml
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
|
||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.ContactSearchScene">
|
||||
<children>
|
||||
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
|
||||
<children>
|
||||
<TextField fx:id="searchBar" onInputMethodTextChanged="#checkClearButton" onKeyTyped="#checkClearButton" prefColumnCount="22" promptText="Enter username to search for">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Enter a name. If an account by that name exists, it will be displayed below." wrapText="true" />
|
||||
</tooltip>
|
||||
</TextField>
|
||||
<Button fx:id="clearButton" disable="true" mnemonicParsing="true" onAction="#clear" text="Clea_r">
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true" text="Clears the text to the left and the elements below" wrapText="true" />
|
||||
</tooltip>
|
||||
</Button>
|
||||
<Pane disable="true" maxWidth="20.0" prefWidth="20.0" visible="false">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Pane>
|
||||
<Button fx:id="searchButton" disable="true" defaultButton="true" mnemonicParsing="true" onAction="#suggestContacts" prefHeight="26.0" prefWidth="71.0" text="_Search" textOverrun="LEADING_WORD_ELLIPSIS">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true" text="Search for accounts with the name entered to the left" wrapText="true" />
|
||||
</tooltip>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
<ListView fx:id="contactList" onMouseClicked="#contactListClicked" prefHeight="314.0" prefWidth="600.0" />
|
||||
<Button fx:id="backButton" cancelButton="true" mnemonicParsing="true" onAction="#backButtonClicked" text="_Back">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true" text="Takes you back to the screen where you can chat with others" wrapText="true" />
|
||||
</tooltip>
|
||||
</Button>
|
||||
</children>
|
||||
</VBox>
|
52
src/main/resources/fxml/LoginScene.fxml
Normal file
52
src/main/resources/fxml/LoginScene.fxml
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.PasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
|
||||
<VBox prefHeight="206.0" prefWidth="440.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.LoginScene">
|
||||
<children>
|
||||
<Label text="User Login">
|
||||
<font>
|
||||
<Font size="26.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="40.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="User Name:" />
|
||||
<Label text="Password" GridPane.rowIndex="1" />
|
||||
<Label fx:id="repeatPasswordLabel" text="Repeat Password:" visible="false" GridPane.rowIndex="2" />
|
||||
<TextField fx:id="userTextField" GridPane.columnIndex="1" />
|
||||
<PasswordField fx:id="passwordField" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||
<PasswordField fx:id="repeatPasswordField" promptText="Repeat the chosen password" visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||
</children>
|
||||
</GridPane>
|
||||
<CheckBox fx:id="registerCheckBox" mnemonicParsing="true" onAction="#registerCheckboxChanged" prefHeight="17.0" prefWidth="181.0" text="_Register" />
|
||||
<Label fx:id="connectionLabel" />
|
||||
<ButtonBar prefHeight="40.0" prefWidth="200.0">
|
||||
<buttons>
|
||||
<Button mnemonicParsing="false" onAction="#abortLogin" text="Close" />
|
||||
<Button mnemonicParsing="false" onAction="#offlineModeButtonPressed" text="Offline mode" />
|
||||
<Button defaultButton="true" mnemonicParsing="false" onAction="#loginButtonPressed" text="Login" />
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
</VBox>
|
24
src/main/resources/fxml/SettingsScene.fxml
Normal file
24
src/main/resources/fxml/SettingsScene.fxml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.TitledPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox alignment="TOP_RIGHT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.SettingsScene">
|
||||
<children>
|
||||
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||
<children>
|
||||
<ListView fx:id="settingsList" onMouseClicked="#settingsListClicked" prefHeight="200.0" prefWidth="200.0" />
|
||||
<TitledPane fx:id="titledPane" collapsible="false" prefHeight="200.0" prefWidth="200.0" />
|
||||
</children>
|
||||
</HBox>
|
||||
<Button defaultButton="true" mnemonicParsing="true" onMouseClicked="#backButtonClicked" text="_Back">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
</Button>
|
||||
</children>
|
||||
</VBox>
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Reference in New Issue
Block a user