diff --git a/pom.xml b/pom.xml index 2a60df405ae..968e610f65f 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,7 @@ resources debug ua + terminal/plugins/org.eclipse.tm.terminal.control diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.classpath b/terminal/plugins/org.eclipse.tm.terminal.control/.classpath new file mode 100644 index 00000000000..a7bc712151f --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.gitignore b/terminal/plugins/org.eclipse.tm.terminal.control/.gitignore new file mode 100644 index 00000000000..ae3c1726048 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.options b/terminal/plugins/org.eclipse.tm.terminal.control/.options new file mode 100644 index 00000000000..752c05f737c --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.options @@ -0,0 +1,4 @@ +org.eclipse.tm.terminal.control/debug/log = false +org.eclipse.tm.terminal.control/debug/log/char = false +org.eclipse.tm.terminal.control/debug/log/VT100Backend = false +org.eclipse.tm.terminal.control/debug/log/hover = false diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.project b/terminal/plugins/org.eclipse.tm.terminal.control/.project new file mode 100644 index 00000000000..11e18c380e4 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.project @@ -0,0 +1,34 @@ + + + org.eclipse.tm.terminal.control + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.core.resources.prefs b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.core.prefs b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..9df862f8d49 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,486 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=abort +org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch, *.xtend +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.mainOnlyProjectHasTestOnlyDependency=error +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=error +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +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=0 +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=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +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_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_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +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_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +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_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=false +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=false +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=false +org.eclipse.jdt.core.formatter.comment.format_line_comments=false +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.insert_new_line_before_root_tags=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=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false +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=false +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=do not 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_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=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_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_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_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_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=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_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=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +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_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +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_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +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.use_on_off_tags=true +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_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.launching.prefs b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.launching.prefs new file mode 100644 index 00000000000..f8a131b56e0 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.launching.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.launching.PREF_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE=warning +org.eclipse.jdt.launching.PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE=warning diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.ui.prefs b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000000..d35ba9b5231 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,213 @@ +cleanup.add_all=false +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.array_with_curly=false +cleanup.arrays_fill=false +cleanup.bitwise_conditional_expression=false +cleanup.boolean_literal=false +cleanup.boolean_value_rather_than_comparison=false +cleanup.break_loop=false +cleanup.collection_cloning=false +cleanup.comparing_on_criteria=false +cleanup.comparison_statement=false +cleanup.controlflow_merge=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.convert_to_enhanced_for_loop_if_loop_var_used=true +cleanup.convert_to_switch_expressions=false +cleanup.correct_indentation=false +cleanup.do_while_rather_than_while=true +cleanup.double_negation=false +cleanup.else_if=false +cleanup.embedded_if=false +cleanup.evaluate_nullable=false +cleanup.extract_increment=false +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.hash=false +cleanup.if_condition=false +cleanup.insert_inferred_type_arguments=false +cleanup.instanceof=false +cleanup.instanceof_keyword=false +cleanup.invert_equals=false +cleanup.join=false +cleanup.lazy_logical_operator=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.map_cloning=false +cleanup.merge_conditional_blocks=false +cleanup.multi_catch=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.no_string_creation=false +cleanup.no_super=false +cleanup.number_suffix=false +cleanup.objects_equals=false +cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=false +cleanup.operand_factorization=false +cleanup.organize_imports=true +cleanup.overridden_assignment=false +cleanup.plain_replacement=false +cleanup.precompile_regex=false +cleanup.primitive_comparison=false +cleanup.primitive_parsing=false +cleanup.primitive_rather_than_wrapper=false +cleanup.primitive_serialization=false +cleanup.pull_out_if_from_if_else=false +cleanup.pull_up_assignment=false +cleanup.push_down_negation=false +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=false +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.reduce_indentation=false +cleanup.redundant_comparator=false +cleanup.redundant_falling_through_block_end=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_modifiers=false +cleanup.remove_redundant_semicolons=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_array_creation=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=false +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_method_parameters=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.return_expression=false +cleanup.simplify_lambda_expression_and_method_ref=false +cleanup.single_used_field=false +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.standard_comparison=false +cleanup.static_inner_class=false +cleanup.strictly_equal_or_different=false +cleanup.stringbuffer_to_stringbuilder=false +cleanup.stringbuilder=false +cleanup.stringbuilder_for_local_vars=true +cleanup.stringconcat_to_textblock=false +cleanup.substring=false +cleanup.switch=false +cleanup.system_property=false +cleanup.system_property_boolean=false +cleanup.system_property_file_encoding=false +cleanup.system_property_file_separator=false +cleanup.system_property_line_separator=false +cleanup.system_property_path_separator=false +cleanup.ternary_operator=false +cleanup.try_with_resource=false +cleanup.unlooped_while=false +cleanup.unreachable_block=false +cleanup.use_anonymous_class_creation=false +cleanup.use_autoboxing=false +cleanup.use_blocks=false +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_directly_map_method=false +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_string_is_blank=false +cleanup.use_this_for_non_static_field_access=false +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup.use_unboxing=false +cleanup.use_var=false +cleanup.useless_continue=false +cleanup.useless_return=false +cleanup.valueof_rather_than_instantiation=false +cleanup_profile=_CDT +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_CDT +formatter_settings_version=14 +internal.default.compliance=user +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.keywordthis=false +org.eclipse.jdt.ui.ondemandthreshold=1000 +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.staticondemandthreshold=1000 +org.eclipse.jdt.ui.text.custom_code_templates= +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_modifiers=false +sp_cleanup.remove_redundant_semicolons=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.pde.api.tools.prefs b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.pde.api.tools.prefs new file mode 100644 index 00000000000..ec9fbf321d0 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.pde.api.tools.prefs @@ -0,0 +1,184 @@ +ANNOTATION_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error +ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error +ANNOTATION_ELEMENT_TYPE_ADDED_METHOD=Error +ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error +ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error +ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_TO_CLASS=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_TO_ENUM=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITHOUT_DEFAULT_VALUE=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITH_DEFAULT_VALUE=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error +API_USE_SCAN_FIELD_SEVERITY=Error +API_USE_SCAN_METHOD_SEVERITY=Error +API_USE_SCAN_TYPE_SEVERITY=Error +CLASS_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +CLASS_ELEMENT_TYPE_ADDED_FIELD=Error +CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error +CLASS_ELEMENT_TYPE_ADDED_METHOD=Error +CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +CLASS_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERCLASS_SET=Error +CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +CLASS_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error +CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +CLASS_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error +CLASS_ELEMENT_TYPE_CHANGED_SUPERCLASS=Error +CLASS_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error +CLASS_ELEMENT_TYPE_CHANGED_TO_ENUM=Error +CLASS_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error +CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +CLASS_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error +CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error +CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error +CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error +CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error +CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error +CONSTRUCTOR_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error +CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error +ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +ENUM_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error +ENUM_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error +ENUM_ELEMENT_TYPE_CHANGED_TO_CLASS=Error +ENUM_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error +ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error +ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error +ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error +ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +FIELD_ELEMENT_TYPE_ADDED_VALUE=Error +FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error +FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error +FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error +FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error +FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error +FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error +FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENTS=Error +FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error +ILLEGAL_EXTEND=Warning +ILLEGAL_IMPLEMENT=Warning +ILLEGAL_INSTANTIATE=Warning +ILLEGAL_OVERRIDE=Warning +ILLEGAL_REFERENCE=Warning +INTERFACE_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error +INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error +INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error +INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error +INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error +INTERFACE_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error +INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETERS=Error +INTERFACE_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error +INTERFACE_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error +INTERFACE_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error +INTERFACE_ELEMENT_TYPE_CHANGED_TO_CLASS=Error +INTERFACE_ELEMENT_TYPE_CHANGED_TO_ENUM=Error +INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +INTERFACE_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error +INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error +INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error +INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +INVALID_ANNOTATION=Ignore +INVALID_JAVADOC_TAG=Error +INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Warning +LEAK_EXTEND=Warning +LEAK_FIELD_DECL=Warning +LEAK_IMPLEMENT=Warning +LEAK_METHOD_PARAM=Warning +LEAK_METHOD_RETURN_TYPE=Warning +METHOD_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error +METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +METHOD_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +METHOD_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error +METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error +METHOD_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error +METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error +METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error +METHOD_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error +METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error +MISSING_EE_DESCRIPTIONS=Warning +TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +UNUSED_PROBLEM_FILTERS=Warning +automatically_removed_unused_problem_filters=false +changed_execution_env=Error +eclipse.preferences.version=1 +incompatible_api_component_version=Error +incompatible_api_component_version_include_major_without_breaking_change=Disabled +incompatible_api_component_version_include_minor_without_api_change=Disabled +incompatible_api_component_version_report_major_without_breaking_change=Warning +incompatible_api_component_version_report_minor_without_api_change=Warning +invalid_since_tag_version=Error +malformed_since_tag=Error +missing_since_tag=Error +report_api_breakage_when_major_version_incremented=Disabled +report_resolution_errors_api_component=Warning diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.pde.prefs b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.pde.prefs new file mode 100644 index 00000000000..ffaa8e3f1a7 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/.settings/org.eclipse.pde.prefs @@ -0,0 +1,36 @@ +compilers.f.unresolved-features=1 +compilers.f.unresolved-plugins=1 +compilers.incompatible-environment=1 +compilers.p.build=1 +compilers.p.build.bin.includes=0 +compilers.p.build.encodings=2 +compilers.p.build.java.compiler=2 +compilers.p.build.java.compliance=1 +compilers.p.build.missing.output=2 +compilers.p.build.output.library=1 +compilers.p.build.source.library=1 +compilers.p.build.src.includes=0 +compilers.p.deprecated=1 +compilers.p.discouraged-class=1 +compilers.p.exec-env-too-low=1 +compilers.p.internal=1 +compilers.p.missing-packages=2 +compilers.p.missing-version-export-package=2 +compilers.p.missing-version-import-package=2 +compilers.p.missing-version-require-bundle=2 +compilers.p.no-required-att=0 +compilers.p.no.automatic.module=1 +compilers.p.not-externalized-att=1 +compilers.p.service.component.without.lazyactivation=1 +compilers.p.unknown-attribute=1 +compilers.p.unknown-class=0 +compilers.p.unknown-element=1 +compilers.p.unknown-identifier=0 +compilers.p.unknown-resource=0 +compilers.p.unresolved-ex-points=0 +compilers.p.unresolved-import=0 +compilers.s.create-docs=false +compilers.s.doc-folder=doc +compilers.s.open-tags=1 +compilers.use-project=true +eclipse.preferences.version=1 diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/HelpContexts.xml b/terminal/plugins/org.eclipse.tm.terminal.control/HelpContexts.xml new file mode 100644 index 00000000000..2bde74230c5 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/HelpContexts.xml @@ -0,0 +1,18 @@ + + + + + + The Terminal View supports direct connections to remote systems via serial or network connections. + + + \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/META-INF/MANIFEST.MF b/terminal/plugins/org.eclipse.tm.terminal.control/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..c77b89c1bef --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/META-INF/MANIFEST.MF @@ -0,0 +1,26 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.tm.terminal.control; singleton:=true +Bundle-Version: 5.5.400.qualifier +Bundle-Activator: org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.ui +Bundle-ActivationPolicy: lazy +Eclipse-LazyStart: true +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-ClassPath: . +Export-Package: org.eclipse.tm.internal.terminal.connector;x-internal:=true, + org.eclipse.tm.internal.terminal.control, + org.eclipse.tm.internal.terminal.control.actions, + org.eclipse.tm.internal.terminal.control.impl;x-internal:=true, + org.eclipse.tm.internal.terminal.emulator;x-internal:=true, + org.eclipse.tm.internal.terminal.model;x-internal:=true, + org.eclipse.tm.internal.terminal.preferences, + org.eclipse.tm.internal.terminal.provisional.api, + org.eclipse.tm.internal.terminal.provisional.api.provider, + org.eclipse.tm.internal.terminal.textcanvas;x-internal:=true, + org.eclipse.tm.terminal.model +Automatic-Module-Name: org.eclipse.tm.terminal.control diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/README.txt b/terminal/plugins/org.eclipse.tm.terminal.control/README.txt new file mode 100644 index 00000000000..c88bc696e29 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/README.txt @@ -0,0 +1,18 @@ +Terminal README +=============== + +The Terminal is a UI-less model of a grid of characters, +plus an SWT widget that's updated asynchronously for +maximum performance. The widget can be hooked up to various +ITerminalConnectors providing an InputStream, OutputStream, +and a method for setting the Terminal Size. + +The widget processes ANSI control characters, including NUL, +backspace, carriage return, linefeed, and a subset of ANSI +escape sequences sufficient to allow use of screen-oriented +applications, such as vi, Emacs, and any GNU readline-enabled +application (Bash, bc, ncftp, etc.). + +This is not yet a fully compliant vt100 / vt102 terminal +emulator! + diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/about.html b/terminal/plugins/org.eclipse.tm.terminal.control/about.html new file mode 100644 index 00000000000..b3134865230 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/about.html @@ -0,0 +1,38 @@ + + + + + + About + + + +

About This Content

+ +

November 30, 2017

+

License

+ +

+ The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content + is provided to you under the terms and conditions of the Eclipse + Public License Version 2.0 ("EPL"). A copy of the EPL is + available at https://www.eclipse.org/legal/epl-2.0. + For purposes of the EPL, "Program" will mean the Content. +

+ +

+ If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may + apply to your use of any object code in the Content. Check the + Redistributor's license that was provided with the Content. If no such + license exists, contact the Redistributor. Unless otherwise indicated + below, the terms and conditions of the EPL still apply to any source + code in the Content and such source code may be obtained at https://www.eclipse.org. +

+ + + + \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/about.ini b/terminal/plugins/org.eclipse.tm.terminal.control/about.ini new file mode 100644 index 00000000000..e07a7bb377e --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/about.ini @@ -0,0 +1,24 @@ +# about.ini +# contains information about a feature +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# "%key" are externalized strings defined in about.properties +# This file does not need to be translated. + +# Property "aboutText" contains blurb for "About" dialog (translated) +aboutText=%blurb + +# Property "windowImage" contains path to window icon (16x16) +# needed for primary features only + +# Property "featureImage" contains path to feature image (32x32) +featureImage=cdt_logo_icon32.png + +# Property "aboutImage" contains path to product image (500x330 or 115x164) +# needed for primary features only + +# Property "appName" contains name of the application (translated) +# needed for primary features only + +# Property "welcomePerspective" contains the id of the perspective in which the +# welcome page is to be opened. +# optional diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/about.mappings b/terminal/plugins/org.eclipse.tm.terminal.control/about.mappings new file mode 100644 index 00000000000..936a8039c3e --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/about.mappings @@ -0,0 +1,9 @@ +# about.mappings +# contains fill-ins for about.properties +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# This file does not need to be translated. + +# The following should contain the build version. +# e.g. "0=20200106-1728" +# This value will be added automatically via the build scripts +0=${buildId} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/about.properties b/terminal/plugins/org.eclipse.tm.terminal.control/about.properties new file mode 100644 index 00000000000..1d0621870d8 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/about.properties @@ -0,0 +1,32 @@ +############################################################################### +# Copyright (c) 2018, 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +############################################################################### +# about.properties +# contains externalized strings for about.ini +# java.io.Properties file (ISO 8859-1 with "\" escapes) +# fill-ins are supplied by about.mappings +# This file should be translated. +# NOTE TO TRANSLATOR: Please do not translate the featureVersion variable. + + +blurb=TM Terminal Control\n\ +\n\ +Version: {featureVersion}\n\ +Build id: {0}\n\ +\n\ +Copyright (c) 2018, 2025 Contributors to the Eclipse Foundation +\n\ +See the NOTICE file(s) distributed with this work for additional\n\ +information regarding copyright ownership.\n\ +\n\ +Visit http://www.eclipse.org/cdt diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/build.properties b/terminal/plugins/org.eclipse.tm.terminal.control/build.properties new file mode 100644 index 00000000000..eb7d53d07b5 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/build.properties @@ -0,0 +1,38 @@ +############################################################################### +# Copyright (c) 2003, 2018 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Initial Contributors: +# The following Wind River employees contributed to the Terminal component +# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, +# Helmut Haigermoser and Ted Williams. +# +# Contributors: +# Michael Scharf (Wind River) - split into core, view and connector plugins +# Martin Oberhuber (Wind River) - fixed copyright headers and beautified +# Anna Dushistova (MontaVista) - added icons +############################################################################### +bin.includes = .,\ + META-INF/,\ + plugin.xml,\ + plugin.properties,\ + .options,\ + README.txt,\ + about.html,\ + about.ini,\ + about.properties,\ + icons/,\ + HelpContexts.xml,\ + about.mappings,\ + cdt_logo_icon32.png + +source.. = src/ +output.. = bin/ +src.includes = schema/,\ + about.html + diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/cdt_logo_icon32.png b/terminal/plugins/org.eclipse.tm.terminal.control/cdt_logo_icon32.png new file mode 100644 index 00000000000..470ca81b327 Binary files /dev/null and b/terminal/plugins/org.eclipse.tm.terminal.control/cdt_logo_icon32.png differ diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/icons/clcl16/clear_co.gif b/terminal/plugins/org.eclipse.tm.terminal.control/icons/clcl16/clear_co.gif new file mode 100644 index 00000000000..af30a42f83d Binary files /dev/null and b/terminal/plugins/org.eclipse.tm.terminal.control/icons/clcl16/clear_co.gif differ diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/icons/dlcl16/clear_co.gif b/terminal/plugins/org.eclipse.tm.terminal.control/icons/dlcl16/clear_co.gif new file mode 100644 index 00000000000..6775edfabb9 Binary files /dev/null and b/terminal/plugins/org.eclipse.tm.terminal.control/icons/dlcl16/clear_co.gif differ diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/icons/elcl16/clear_co.gif b/terminal/plugins/org.eclipse.tm.terminal.control/icons/elcl16/clear_co.gif new file mode 100644 index 00000000000..af30a42f83d Binary files /dev/null and b/terminal/plugins/org.eclipse.tm.terminal.control/icons/elcl16/clear_co.gif differ diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/plugin.properties b/terminal/plugins/org.eclipse.tm.terminal.control/plugin.properties new file mode 100644 index 00000000000..e3c9236c3f2 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/plugin.properties @@ -0,0 +1,49 @@ +############################################################################### +# Copyright (c) 2003, 2020 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Initial Contributors: +# The following Wind River employees contributed to the Terminal component +# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, +# Helmut Haigermoser and Ted Williams. +# +# Contributors: +# Michael Scharf (Wind River) - split into core, view and connector plugins +# Martin Oberhuber (Wind River) - fixed copyright headers and beautified +# Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget +############################################################################### + +# NLS_MESSAGEFORMAT_NONE + +pluginName = Terminal Control (Embeddable Widget) +providerName = Eclipse CDT + +terminal.context.name.edit=Terminal Control in Focus +terminal.context.description.edit=Show modified keyboard shortcuts in context menu + +terminal.context.name.terminal=Terminal Typing Connected +terminal.context.description.terminal=Override ALT+x menu access keys while typing into the Terminal + +terminal.insertion.description=Terminal view insertion +terminal.insertion.name=Terminal view insert +terminal.insertion.category.name=Terminal view commands +terminal.view.insertion.description = Terminal view commands + +terminal.command.copy.name=Copy +terminal.command.paste.name=Paste +terminal.command.maximize.name=Maximize Active View or Editor +terminal.command.quickaccess.name=Quick Access + +terminal.preferences.name = Terminal +terminal.font.description = The font for the terminal console. Please also see the Terminal Preference pages for changing colors. +terminal.font.label = Terminal Console Font + +terminal.views.theme.category.label = Terminal +terminal.views.theme.category.description = Fonts used in the Terminal View. Please also see the Terminal Preference pages for changing colors. + +terminal.connectors.name = Terminal Connector Extensions diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/plugin.xml b/terminal/plugins/org.eclipse.tm.terminal.control/plugin.xml new file mode 100644 index 00000000000..ee9eaaf0a03 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/plugin.xml @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %terminal.font.description + + + + + %terminal.views.theme.category.description + + + + + + diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/schema/connectors.exsd b/terminal/plugins/org.eclipse.tm.terminal.control/schema/connectors.exsd new file mode 100644 index 00000000000..8a0862cf413 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/schema/connectors.exsd @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A class extending TerminalConnectorImpl + + + + + + + + + + Uniquely identify this connector for programmatic access. Clients will use this ID in order to find and instantiate it. + + + + + + + The name of the connection (used in the UI) + + + + + + + + + + When set to "true", the terminal connector will not be visible to the user in connector selections. + + + + + + + + + + + + + + + + Copyright (c) 2006 - 2018 Wind River Systems, Inc. and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License 2.0 +which accompanies this distribution, and is available at +https://www.eclipse.org/legal/epl-2.0/ + +Contributors: +Michael Scharf (Wind River) - initial API and implementation +Martin Oberhuber (Wind River) - fixed copyright headers and beautified +Uwe Stieber (Wind River) - [282996] [terminal][api] Add "hidden" attribute to terminal connector extension point + + + + diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalConnector.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalConnector.java new file mode 100644 index 00000000000..3737a5cde3f --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalConnector.java @@ -0,0 +1,272 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Michael Scharf (Wind River) - [200541] Extract from TerminalConnectorExtension.TerminalConnectorProxy + * Martin Oberhuber (Wind River) - [225853][api] Provide more default functionality in TerminalConnectorImpl + * Uwe Stieber (Wind River) - [282996] [terminal][api] Add "hidden" attribute to terminal connector extension point + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.connector; + +import java.io.OutputStream; +import java.util.Optional; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.Platform; +import org.eclipse.tm.internal.terminal.control.impl.TerminalMessages; +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalConnectorExtension; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl; + +/** + * An {@link ITerminalConnector} instance, also known as terminal connection + * type, for maintaining a single terminal connection. + * + * It provides all terminal connector functions that can be provided by static + * markup without loading the actual implementation class. The actual + * {@link TerminalConnectorImpl} implementation class is lazily loaded by the + * provided {@link TerminalConnector.Factory} interface when needed. class, and + * delegates to the actual implementation when needed. The following methods can + * be called without initializing the contributed implementation class: + * {@link #getId()}, {@link #getName()}, {@link #getSettingsSummary()},{@link #load(ISettings)}, + * {@link #setTerminalSize(int, int)}, {@link #save(ISettings)}, + * {@link #getAdapter(Class)} + * + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + * Clients can get terminal connector instances through the + * {@link TerminalConnectorExtension} class. + * @since org.eclipse.tm.terminal 2.0 + */ +public class TerminalConnector implements ITerminalConnector { + /** + * Creates an instance of TerminalConnectorImpl. This is used to lazily load + * classed defined in extensions. + * + * @since org.eclipse.tm.terminal 2.0 + */ + public interface Factory { + /** + * Factory method to create the actual terminal connector implementation + * when needed. + * + * @return a Connector + * @throws Exception + */ + TerminalConnectorImpl makeConnector() throws Exception; + } + + /** + * The factory for creating impl instances. + */ + private final TerminalConnector.Factory fTerminalConnectorFactory; + /** + * The (display) name of the TerminalConnector + */ + private final String fName; + /** + * The unique id the connector + */ + private final String fId; + /** + * Flag to mark the connector as hidden. + */ + private final boolean fHidden; + /** + * The connector + */ + private TerminalConnectorImpl fConnector; + /** + * If the initialization of the class specified in the extension fails, + * this variable contains the error + */ + private Exception fException; + /** + * The store might be set before the real connector is initialized. + * This keeps the value until the connector is created. + */ + private ISettingsStore fStore; + + /** + * Constructor for the terminal connector. + * + * @param terminalConnectorFactory Factory for lazily instantiating the + * TerminalConnectorImpl when needed. + * @param id terminal connector ID. The connector is publicly known under + * this ID. + * @param name translatable name to display the connector in the UI. + */ + public TerminalConnector(TerminalConnector.Factory terminalConnectorFactory, String id, String name, + boolean hidden) { + fTerminalConnectorFactory = terminalConnectorFactory; + fId = id; + fName = name; + fHidden = hidden; + } + + @Override + public String getInitializationErrorMessage() { + getConnectorImpl(); + if (fException != null) + return fException.getLocalizedMessage(); + return null; + } + + @Override + public String getId() { + return fId; + } + + @Override + public String getName() { + return fName; + } + + @Override + public boolean isHidden() { + return fHidden; + } + + private TerminalConnectorImpl getConnectorImpl() { + if (!isInitialized()) { + try { + fConnector = fTerminalConnectorFactory.makeConnector(); + fConnector.initialize(); + } catch (Exception e) { + fException = e; + fConnector = new TerminalConnectorImpl() { + @Override + public void connect(ITerminalControl control) { + // super.connect(control); + control.setState(TerminalState.CLOSED); + control.setMsg(getInitializationErrorMessage()); + } + + @Override + public OutputStream getTerminalToRemoteStream() { + return null; + } + + @Override + public String getSettingsSummary() { + return null; + } + }; + // that's the place where we log the exception + Logger.logException(e); + } + if (fConnector != null && fStore != null) + fConnector.load(fStore); + } + return fConnector; + } + + @Override + public boolean isInitialized() { + return fConnector != null || fException != null; + } + + @Override + public void connect(ITerminalControl control) { + getConnectorImpl().connect(control); + } + + @Override + public void disconnect() { + getConnectorImpl().disconnect(); + } + + @Override + public OutputStream getTerminalToRemoteStream() { + return getConnectorImpl().getTerminalToRemoteStream(); + } + + @Override + public String getSettingsSummary() { + if (fConnector != null) + return getConnectorImpl().getSettingsSummary(); + else + return TerminalMessages.NotInitialized; + } + + @Override + public boolean isLocalEcho() { + return getConnectorImpl().isLocalEcho(); + } + + @Override + public void load(ISettingsStore store) { + if (fConnector == null) { + fStore = store; + } else { + getConnectorImpl().load(store); + } + } + + @Override + public void setDefaultSettings() { + getConnectorImpl().setDefaultSettings(); + } + + @Override + public void save(ISettingsStore store) { + // no need to save the settings: it cannot have changed + // because we are not initialized.... + if (fConnector != null) + getConnectorImpl().save(store); + } + + @Override + public void setTerminalSize(int newWidth, int newHeight) { + // we assume that setTerminalSize is called also after + // the terminal has been initialized. Else we would have to cache + // the values.... + if (fConnector != null) { + fConnector.setTerminalSize(newWidth, newHeight); + } + } + + @Override + public T getAdapter(Class adapter) { + TerminalConnectorImpl connector = null; + if (isInitialized()) + connector = getConnectorImpl(); + // if we cannot create the connector then we cannot adapt... + if (connector != null) { + // maybe the connector is adaptable + if (connector instanceof IAdaptable) { + Object result = ((IAdaptable) connector).getAdapter(adapter); + // Not sure if the next block is needed.... + if (result == null) + //defer to the platform + result = Platform.getAdapterManager().getAdapter(connector, adapter); + if (result != null) + return adapter.cast(result); + } + // maybe the real adapter is what we need.... + if (adapter.isInstance(connector)) + return adapter.cast(connector); + } + // maybe we have to be adapted.... + return Platform.getAdapterManager().getAdapter(this, adapter); + } + + @Override + public Optional getWorkingDirectory() { + if (fConnector != null) { + return fConnector.getWorkingDirectory(); + } + return Optional.empty(); + } +} \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStream.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStream.java new file mode 100644 index 00000000000..ea4b24273a0 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStream.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2008, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.connector; + +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @noreference This class is not intended to be referenced by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + * This class used to be package-protected. It is public only for access by the Unit Tests. + */ +public class TerminalToRemoteInjectionOutputStream extends FilterOutputStream { + /** + * This class handles bytes written to the {@link TerminalToRemoteInjectionOutputStream}. + */ + static abstract public class Interceptor { + protected OutputStream fOriginal; + + /** + * @param original the injection into the original stream begins + * @throws IOException + */ + public void begin(OutputStream original) throws IOException { + fOriginal = original; + } + + /** + * @param b a byte was written to the {@link TerminalToRemoteInjectionOutputStream}. + * @throws IOException + */ + public void write(int b) throws IOException { + } + + /** + * @param b bytes written to the {@link TerminalToRemoteInjectionOutputStream}. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @throws IOException + */ + public void write(byte[] b, int off, int len) throws IOException { + } + + /** + * The injection into the normal stream ends. + * @throws IOException + */ + public void close() throws IOException { + } + + public void flush() { + } + } + + static public class BufferInterceptor extends Interceptor { + private final ByteArrayOutputStream fBuffer = new ByteArrayOutputStream(); + + @Override + public void close() throws IOException { + fOriginal.write(fBuffer.toByteArray()); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + fBuffer.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + fBuffer.write(b); + } + } + + private class TerminalFilterOutputStream extends OutputStream { + final private Object fLock = TerminalToRemoteInjectionOutputStream.this; + + @Override + public void close() throws IOException { + synchronized (fLock) { + if (fInjection == this) { + flush(); + ungrabOutput(); + } + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + synchronized (fLock) { + checkStream(); + out.write(b, off, len); + } + } + + @Override + public void write(byte[] b) throws IOException { + synchronized (fLock) { + checkStream(); + out.write(b); + } + } + + @Override + public void flush() throws IOException { + synchronized (fLock) { + checkStream(); + out.flush(); + } + } + + @Override + public void write(int b) throws IOException { + synchronized (fLock) { + checkStream(); + out.write(b); + } + } + + private void checkStream() throws IOException { + if (fInjection != this) + throw new IOException("Stream is closed"); //$NON-NLS-1$ + } + } + + private Interceptor fInterceptor; + private TerminalFilterOutputStream fInjection; + + public TerminalToRemoteInjectionOutputStream(OutputStream out) { + super(out); + } + + synchronized protected void ungrabOutput() throws IOException { + if (fInterceptor != null) { + fInterceptor.close(); + fInterceptor = null; + fInjection = null; + } + } + + /** + * There can only be one injection stream active at a time. You must call close on the + * returned output stream to end the injection. + * @param interceptor This is used handle bytes sent while the injection stream is active. + * @return a output stream that can be used to write to the decorated stream. + * @throws IOException + */ + public synchronized OutputStream grabOutput(Interceptor interceptor) throws IOException { + if (fInjection != null) { + throw new IOException("Buffer in use"); //$NON-NLS-1$ + } + fInterceptor = interceptor; + fInterceptor.begin(out); + fInjection = new TerminalFilterOutputStream(); + return fInjection; + } + + /** See {@link #grabOutput(TerminalToRemoteInjectionOutputStream.Interceptor)}. + * @return injection output stream + * @throws IOException + */ + public synchronized OutputStream grabOutput() throws IOException { + return grabOutput(new BufferInterceptor()); + } + + @Override + synchronized public void close() throws IOException { + if (fInjection != null) { + fInjection.close(); + } + super.close(); + } + + @Override + synchronized public void flush() throws IOException { + if (fInterceptor != null) + fInterceptor.flush(); + out.flush(); + } + + @Override + synchronized public void write(byte[] b, int off, int len) throws IOException { + if (fInterceptor != null) + fInterceptor.write(b, off, len); + else + out.write(b, off, len); + } + + @Override + synchronized public void write(int b) throws IOException { + if (fInterceptor != null) + fInterceptor.write(b); + else + out.write(b); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/CommandInputFieldWithHistory.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/CommandInputFieldWithHistory.java new file mode 100644 index 00000000000..bb9ca19a0e9 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/CommandInputFieldWithHistory.java @@ -0,0 +1,341 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial implementation + * Michael Scharf (Wing River) - [211659] Add field assist to terminal input field + * Michael Scharf (Wing River) - [196447] The optional terminal input line should be resizeable + * Martin Oberhuber (Wind River) - [168197] Fix Terminal for CDC-1.1/Foundation-1.1 + * Michael Scharf (Wing River) - [236458] Fix 168197 lost the last entry + * Anton Leherbauer (Wind River) - [220971] The optional terminal input line has redraw problems when resizing + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; + +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; +import org.eclipse.jface.fieldassist.TextContentAdapter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Sash; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter; + +/** + * Manages the Command History for the command line input + * of the terminal control. + *
  • + *
      Navigate with ARROW_UP,ARROW_DOWN,PAGE_UP,PAGE_DOWN + *
        ESC to cancel history editing + *
          History can be edited (by moving up and edit) but changes are + * not persistent (like in bash). + *
            If the same command is entered multiple times in a row, + * only one entry is kept in the history. + * + * + */ +public class CommandInputFieldWithHistory implements ICommandInputField { + private class FieldAssist implements IContentProposalProvider { + + @Override + public IContentProposal[] getProposals(String contents, int position) { + String prefix = contents.substring(0, position); + List result = new ArrayList<>(); + // show an entry only once + Set seen = new HashSet<>(); + for (Iterator iterator = fHistory.iterator(); iterator.hasNext();) { + String history = iterator.next(); + if (history.startsWith(prefix) && !seen.contains(history)) { + // the content is the rest of the history item + String content = history.substring(prefix.length()); + result.add(new Proposal(content, history)); + // don't add this proposal again + seen.add(history); + } + } + return result.toArray(new IContentProposal[result.size()]); + } + + } + + private static class Proposal implements IContentProposal { + + private final String fContent; + private final String fLabel; + + Proposal(String content, String label) { + fContent = content; + fLabel = label; + } + + @Override + public String getContent() { + return fContent; + } + + @Override + public String getLabel() { + return fLabel; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public int getCursorPosition() { + return fContent.length(); + } + } + + final List fHistory = new ArrayList<>(); + /** + * Keeps a modifiable history while in history editing mode + */ + List fEditedHistory; + /** + * The current position in the edit history + */ + private int fEditHistoryPos = 0; + /** + * The limit of the history. + */ + private final int fMaxSize; + /** + * The input text field. + */ + private Text fInputField; + private Sash fSash; + private Composite fPanel; + + public CommandInputFieldWithHistory(int maxHistorySize) { + fMaxSize = maxHistorySize; + } + + /** + * Add a line to the history. + * @param line The line to be added to the history. + */ + protected void pushLine(String line) { + endHistoryMode(); + // anything to remember? + if (line == null || line.trim().length() == 0) + return; + fHistory.add(0, line); + // ignore if the same as last + if (fHistory.size() > 1 && line.equals(fHistory.get(1))) + fHistory.remove(0); + // limit the history size. + if (fHistory.size() >= fMaxSize) + fHistory.remove(fHistory.size() - 1); + } + + /** + * Sets the history + * @param history or null + */ + public void setHistory(String history) { + endHistoryMode(); + fHistory.clear(); + if (history == null) + return; + // add history entries separated by '\n' + // fHistory.addAll(Arrays.asList(history.split("\n"))); //$NON-NLS-1$ + // + StringTokenizer tok = new StringTokenizer(history, "\n"); //$NON-NLS-1$ + while (tok.hasMoreElements()) + fHistory.add((String) tok.nextElement()); + // + } + + /** + * @return the current content of the history buffer and new line separated list + */ + public String getHistory() { + StringBuffer buff = new StringBuffer(); + boolean sep = false; + for (Iterator iterator = fHistory.iterator(); iterator.hasNext();) { + String line = iterator.next(); + if (line.length() > 0) { + if (sep) + buff.append("\n"); //$NON-NLS-1$ + else + sep = true; + buff.append(line); + } + } + return buff.toString(); + } + + /** + * @param currLine Line of text to be moved in history + * @param count (+1 or -1) for forward and backward movement. -1 goes back + * @return the new string to be displayed in the command line or null, + * if the limit is reached. + */ + public String move(String currLine, int count) { + if (!inHistoryMode()) { + fEditedHistory = new ArrayList<>(fHistory.size() + 1); + fEditedHistory.add(currLine); + fEditedHistory.addAll(fHistory); + fEditHistoryPos = 0; + } + fEditedHistory.set(fEditHistoryPos, currLine); + if (fEditHistoryPos + count >= fEditedHistory.size()) + return null; + if (fEditHistoryPos + count < 0) + return null; + fEditHistoryPos += count; + return (String) fEditedHistory.get(fEditHistoryPos); + } + + private boolean inHistoryMode() { + return fEditedHistory != null; + } + + /** + * Exit the history movements and go to position 0; + * @return the string to be shown in the command line + */ + protected String escape() { + if (!inHistoryMode()) + return null; + String line = (String) fEditedHistory.get(0); + endHistoryMode(); + return line; + } + + /** + * End history editing + */ + private void endHistoryMode() { + fEditedHistory = null; + fEditHistoryPos = 0; + } + + @Override + public void createControl(final Composite parent, final ITerminalViewControl terminal) { + // fSash = new Sash(parent,SWT.HORIZONTAL|SWT.SMOOTH); + fSash = new Sash(parent, SWT.HORIZONTAL); + final GridData gd_sash = new GridData(SWT.FILL, SWT.CENTER, true, false); + gd_sash.heightHint = 5; + fSash.setLayoutData(gd_sash); + fSash.addListener(SWT.Selection, e -> { + if (e.detail == SWT.DRAG) { + // don't redraw during drag, it causes paint errors - bug 220971 + return; + } + // no idea why this is needed + GridData gdata = (GridData) fInputField.getLayoutData(); + Rectangle sashRect = fSash.getBounds(); + Rectangle containerRect = parent.getClientArea(); + + int h = fInputField.getLineHeight(); + // make sure the input filed height is a multiple of the line height + gdata.heightHint = Math.max(((containerRect.height - e.y - sashRect.height) / h) * h, h); + // do not show less then one line + e.y = Math.min(e.y, containerRect.height - h); + fInputField.setLayoutData(gdata); + parent.layout(); + // else the content assist icon will be replicated + parent.redraw(); + }); + fPanel = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + layout.marginTop = 0; + layout.marginBottom = 2; + fPanel.setLayout(layout); + fPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + fInputField = new Text(fPanel, SWT.MULTI | SWT.BORDER | SWT.WRAP | SWT.V_SCROLL); + GridData data = new GridData(SWT.FILL, SWT.FILL, true, false); + boolean installDecoration = true; + if (installDecoration) { + // The ContentAssistCommandAdapter says: "The client is responsible for + // ensuring that adequate space is reserved for the decoration." + // TODO: what is the "adequate space"??? + data.horizontalIndent = 6; + } + fInputField.setLayoutData(data); + fInputField.setFont(terminal.getFont()); + // Register field assist *before* the key listener. + // Else the ENTER key is sent *first* to the input field + // and then to the field assist popup. + // (https://bugs.eclipse.org/bugs/show_bug.cgi?id=211659) + new ContentAssistCommandAdapter(fInputField, new TextContentAdapter(), new FieldAssist(), null, null, + installDecoration); + fInputField.addKeyListener(new KeyListener() { + @Override + public void keyPressed(KeyEvent e) { + // if the field assist has handled the key already then + // ignore it (https://bugs.eclipse.org/bugs/show_bug.cgi?id=211659) + if (!e.doit) + return; + if (e.character == SWT.CR || e.character == SWT.LF) { + e.doit = false; + String line = fInputField.getText(); + if (!terminal.pasteString(line + '\r')) + return; + pushLine(line); + setCommand("");//$NON-NLS-1$ + } else if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.PAGE_UP) { + e.doit = false; + setCommand(move(fInputField.getText(), 1)); + } else if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.PAGE_DOWN) { + e.doit = false; + setCommand(move(fInputField.getText(), -1)); + } else if (e.keyCode == SWT.ESC) { + e.doit = false; + setCommand(escape()); + } + } + + private void setCommand(String line) { + if (line == null) + return; + fInputField.setText(line); + fInputField.setSelection(fInputField.getCharCount()); + } + + @Override + public void keyReleased(KeyEvent e) { + } + }); + } + + @Override + public void setFont(Font font) { + fInputField.setFont(font); + fInputField.getParent().layout(true); + } + + @Override + public void dispose() { + fSash.dispose(); + fSash = null; + fPanel.dispose(); + fPanel = null; + fInputField.dispose(); + fInputField = null; + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ICommandInputField.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ICommandInputField.java new file mode 100644 index 00000000000..8e314acd78b --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ICommandInputField.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Composite; + +/** + * Interface to create a command input control. + * + */ +public interface ICommandInputField { + /** + * @param parent + * @param terminal + */ + void createControl(Composite parent, ITerminalViewControl terminal); + + void dispose(); + + /** + * Sets the font of a control created with {@link #createControl(Composite, ITerminalViewControl)} + * @param control + * @param font the new text font + */ + void setFont(Font font); + +} \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener.java new file mode 100644 index 00000000000..400521ae774 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; + +/** + * Provided by a view implementation. + * @author Michael Scharf + * + */ +public interface ITerminalListener { + /** + * Called when the state of the connection has changed. + * @param state + */ + void setState(TerminalState state); + + /** + * @deprecated Migrate to implementing {@link ITerminalListener3} and + * override {@link ITerminalListener3#setTerminalTitle(String, String) + * @param title + */ + @Deprecated(forRemoval = true) + void setTerminalTitle(String title); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener2.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener2.java new file mode 100644 index 00000000000..30f22f82ba0 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener2.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2015, 2018 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License 2.0 which accompanies this distribution, and is + * available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +/** + * Terminal listener allowing to listen to terminal selection changes. + * + * @since 4.1 + */ +public interface ITerminalListener2 extends ITerminalListener { + + /** + * selection has been changed internally e.g. select all + * clients might want to react on that + * NOTE: this does not include mouse selections + * those are handled in separate MouseListeners + * TODO should be unified + */ + void setTerminalSelectionChanged(); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener3.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener3.java new file mode 100644 index 00000000000..e9e983621e8 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalListener3.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2023 Infineon Technologies AG. All Rights Reserved. + * + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License 2.0 which accompanies this distribution, and is + * available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +/** + * Terminal listener allowing to listen to terminal selection changes. + * The interface overrides the deprecated {@link #setTerminalTitle(String)} with + * {@link #setTerminalTitle(String, String)} that should be called instead. + * + * @since 5.5 + */ +public interface ITerminalListener3 extends ITerminalListener2 { + + /** + * Enum defines terminal title change requestors for + * setTerminalTitle method. + * + * @since 5.5 + */ + enum TerminalTitleRequestor { + ANSI, // Terminal tab title change requested using ANSI command in terminal. + MENU, // Terminal tab title change requested from menu. + OTHER; // Terminal tab title change requested by other requestors. + } + + /** + * Set the title of the terminal. + * + * @param title Terminal title. + * @param requestor Item that requests terminal title update. + */ + void setTerminalTitle(String title, TerminalTitleRequestor requestor); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener.java new file mode 100644 index 00000000000..0de6ff105ae --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2015, 2018 CWI. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License 2.0 which accompanies this distribution, and is + * available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Davy Landman (CWI) - [475267][api] Initial definition of interface + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; + +/** + * Terminal specific version of {@link org.eclipse.swt.events.MouseListener} + * @since 4.1 + * @see ITerminalMouseListener2 + */ +public interface ITerminalMouseListener { + /** + * Invoked when a double-click has happend inside the terminal control.
            + *
            + * Important: the event fires for every click, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + */ + void mouseDoubleClick(ITerminalTextDataReadOnly terminalText, int line, int column, int button); + + /** + * Invoked when a mouse button is pushed down inside the terminal control.
            + *
            + * Important: the event fires for every mouse down, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + */ + void mouseDown(ITerminalTextDataReadOnly terminalText, int line, int column, int button); + + /** + * Invoked when a mouse button is released inside the terminal control.
            + *
            + * Important: the event fires for every mouse up, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + */ + void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column, int button); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener2.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener2.java new file mode 100644 index 00000000000..e33a663ab5a --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalMouseListener2.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2021 Kichwa Coders Canada Inc. and others. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License 2.0 which accompanies this distribution, and is + * available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; + +/** + * Extension of {@link ITerminalMouseListener} for consumers that need the stateMask for a button mouse action. + * + * If ITerminalMouseListener2 is used, the methods in ITerminalMouseListener will not be called. + * + * @since 5.2 + * @see ITerminalMouseListener + */ +public interface ITerminalMouseListener2 extends ITerminalMouseListener { + /** + * Invoked when a double-click has happend inside the terminal control.
            + *
            + * Important: the event fires for every click, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + * @param stateMask see {@link org.eclipse.swt.events.MouseEvent#stateMask} for the meaning of the values + */ + default void mouseDoubleClick(ITerminalTextDataReadOnly terminalText, int line, int column, int button, + int stateMask) { + // do nothing by default so that implementors only need to implement methods they care about + } + + @Override + default void mouseDoubleClick(ITerminalTextDataReadOnly terminalText, int line, int column, int button) { + throw new UnsupportedOperationException(); + } + + /** + * Invoked when a mouse button is pushed down inside the terminal control.
            + *
            + * Important: the event fires for every mouse down, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + * @param stateMask see {@link org.eclipse.swt.events.MouseEvent#stateMask} for the meaning of the values + */ + default void mouseDown(ITerminalTextDataReadOnly terminalText, int line, int column, int button, int stateMask) { + // do nothing by default so that implementors only need to implement methods they care about + } + + @Override + default void mouseDown(ITerminalTextDataReadOnly terminalText, int line, int column, int button) { + throw new UnsupportedOperationException(); + } + + /** + * Invoked when a mouse button is released inside the terminal control.
            + *
            + * Important: the event fires for every mouse up, even outside the text region. + * @param terminalText a read-only view of the current terminal text + * @param button see {@link org.eclipse.swt.events.MouseEvent#button} for the meaning of the button values + * @param stateMask see {@link org.eclipse.swt.events.MouseEvent#stateMask} for the meaning of the values + */ + default void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column, int button, int stateMask) { + // do nothing by default so that implementors only need to implement methods they care about + } + + @Override + default void mouseUp(ITerminalTextDataReadOnly terminalText, int line, int column, int button) { + throw new UnsupportedOperationException(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalViewControl.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalViewControl.java new file mode 100644 index 00000000000..d19b6516975 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/ITerminalViewControl.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2006, 2021 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [204796] Terminal should allow setting the encoding to use + * Martin Oberhuber (Wind River) - [265352][api] Allow setting fonts programmatically + * Davy Landman (CWI) - [475267][api] Allow custom mouse listeners + ******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Control; +import org.eclipse.tm.internal.terminal.control.ITerminalListener3.TerminalTitleRequestor; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; + +/** + * @author Michael Scharf + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface ITerminalViewControl { + /** + * Set the encoding that the Terminal uses to decode byte streams into + * characters. + * + * @see ITerminalControl#setEncoding(String) + * @since org.eclipse.tm.terminal 2.0 + * @deprecated Use {@link #setCharset(Charset)} and do the error handling in the UI code. + */ + @Deprecated + void setEncoding(String encoding) throws UnsupportedEncodingException; + + /** + * Set the charset that the Terminal uses to decode byte streams into + * characters. + * + * @see ITerminalControl#setCharset(Charset) + * @since 5.3 + */ + void setCharset(Charset charset); + + /** + * Get the Terminal's current encoding. + * + * @return the current Encoding of the Terminal. + * @see ITerminalControl#getEncoding() + * @since org.eclipse.tm.terminal 2.0 + * @deprecated Use {@link #getCharset()} and call {@link Charset#name()} on the result + */ + @Deprecated + String getEncoding(); + + /** + * @return the non-null current Charset of the Terminal. + * @see ITerminalControl#getCharset() + * @since 5.3 + */ + Charset getCharset(); + + boolean isEmpty(); + + /** + * Sets the Terminal font + * @deprecated use {@link #setFont(String)} in order to support bold and italic variants of the given font + * @param font + */ + @Deprecated + void setFont(Font font); + + /** + * Sets the font for the Terminal, using a JFace symbolic font name, such + * that bold and italic variants can be leveraged. + * @since 3.2 + * @param fontName + */ + void setFont(String fontName); + + void setInvertedColors(boolean invert); + + /** + * @since 5.1 + */ + boolean isInvertedColors(); + + Font getFont(); + + /** + * @return the text control + */ + Control getControl(); + + /** + * @return the root of all controls + */ + Control getRootControl(); + + boolean isDisposed(); + + void selectAll(); + + void clearTerminal(); + + void copy(); + + void paste(); + + String getSelection(); + + TerminalState getState(); + + Clipboard getClipboard(); + + void disconnectTerminal(); + + void disposeTerminal(); + + String getSettingsSummary(); + + ITerminalConnector[] getConnectors(); + + void setFocus(); + + ITerminalConnector getTerminalConnector(); + + void setConnector(ITerminalConnector connector); + + void connectTerminal(); + + /** + * @param write a single character to terminal + */ + void sendKey(char arg0); + + /** + * @param string write string to terminal + */ + public boolean pasteString(String string); + + boolean isConnected(); + + /** + * @param inputField null means no input field is shown + */ + void setCommandInputField(ICommandInputField inputField); + + /** + * @return null or the current input field + */ + ICommandInputField getCommandInputField(); + + /** + * @return the maximum number of lines to display + * in the terminal view. -1 means unlimited. + */ + public int getBufferLineLimit(); + + /** + * @param bufferLineLimit the maximum number of lines to show + * in the terminal view. -1 means unlimited. + */ + public void setBufferLineLimit(int bufferLineLimit); + + boolean isScrollLock(); + + void setScrollLock(boolean on); + + /** + * @since 4.1 + * @param listener may be a {@link ITerminalMouseListener2} for extra callbacks + */ + void addMouseListener(ITerminalMouseListener listener); + + /** + * @since 4.1 + * @param listener may be a {@link ITerminalMouseListener2} for extra callbacks + */ + void removeMouseListener(ITerminalMouseListener listener); + + /** + * @since 5.1 + * @deprecated call {@link #setTerminalTitle(String, String)} instead + */ + @Deprecated(forRemoval = true) + void setTerminalTitle(String newTitle); + + /** + * Set the title of the terminal. + * @param newTitle + * @param requestor Item that requests terminal title update. + * @since 5.5 + */ + void setTerminalTitle(String newTitle, TerminalTitleRequestor requestor); + + /** + * @since 5.2 + */ + String getHoverSelection(); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/TerminalViewControlFactory.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/TerminalViewControlFactory.java new file mode 100644 index 00000000000..b04fb597701 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/TerminalViewControlFactory.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tm.internal.terminal.emulator.VT100TerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; + +public class TerminalViewControlFactory { + /** + * Instantiate a Terminal widget. + * @param target Callback for notifying the owner of Terminal state changes. + * @param wndParent The Window parent to embed the Terminal in. + * @param connectors Provided connectors. + */ + public static ITerminalViewControl makeControl(ITerminalListener target, Composite wndParent, + ITerminalConnector[] connectors) { + return makeControl(target, wndParent, connectors, false); + } + + /** + * Instantiate a Terminal widget. + * @param target Callback for notifying the owner of Terminal state changes. + * @param wndParent The Window parent to embed the Terminal in. + * @param connectors Provided connectors. + * @param useCommonPrefs If true, the Terminal widget will pick up settings + * from the org.eclipse.tm.terminal.TerminalPreferencePage Preference page. + * Otherwise, clients need to maintain settings themselves. + * @since 3.2 + */ + public static ITerminalViewControl makeControl(ITerminalListener target, Composite wndParent, + ITerminalConnector[] connectors, boolean useCommonPrefs) { + return new VT100TerminalControl(target, wndParent, connectors, useCommonPrefs); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/AbstractTerminalAction.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/AbstractTerminalAction.java new file mode 100644 index 00000000000..776f8ede4c1 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/AbstractTerminalAction.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2004, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - Adapted from TerminalAction + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; + +public abstract class AbstractTerminalAction extends Action { + private final ITerminalViewControl fTarget; + + public AbstractTerminalAction(String strId) { + this(null, strId, 0); + } + + public AbstractTerminalAction(ITerminalViewControl target, String strId) { + this(target, strId, 0); + } + + public AbstractTerminalAction(ITerminalViewControl target, String strId, int style) { + super("", style); //$NON-NLS-1$ + + fTarget = target; + + setId(strId); + } + + @Override + abstract public void run(); + + protected void setupAction(String strText, String strToolTip, String strImage, String strEnabledImage, + String strDisabledImage, boolean bEnabled) { + setupAction(strText, strToolTip, strImage, strEnabledImage, strDisabledImage, bEnabled, + TerminalPlugin.getDefault().getImageRegistry()); + } + + protected void setupAction(String strText, String strToolTip, String strHoverImage, String strEnabledImage, + String strDisabledImage, boolean bEnabled, ImageRegistry imageRegistry) { + setupAction(strText, strToolTip, imageRegistry.getDescriptor(strHoverImage), + imageRegistry.getDescriptor(strEnabledImage), imageRegistry.getDescriptor(strDisabledImage), bEnabled); + } + + protected void setupAction(String strText, String strToolTip, ImageDescriptor hoverImage, + ImageDescriptor enabledImage, ImageDescriptor disabledImage, boolean bEnabled) { + setText(strText); + setToolTipText(strToolTip); + setEnabled(bEnabled); + if (enabledImage != null) { + setImageDescriptor(enabledImage); + } + if (disabledImage != null) { + setDisabledImageDescriptor(disabledImage); + } + if (hoverImage != null) { + setHoverImageDescriptor(hoverImage); + } + } + + /** + * Return the terminal instance on which the action should operate. + * + * @return the terminal instance on which the action should operate. + */ + protected ITerminalViewControl getTarget() { + return fTarget; + } + + /** + * Subclasses can update their action + * + * @param aboutToShow true before the menu is shown -- false when the menu + * gets hidden + */ + public void updateAction(boolean aboutToShow) { + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ActionMessages.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ActionMessages.java new file mode 100644 index 00000000000..b613258f390 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ActionMessages.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2003, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +import org.eclipse.osgi.util.NLS; + +public class ActionMessages extends NLS { + static { + NLS.initializeMessages(ActionMessages.class.getName(), ActionMessages.class); + } + + public static String COPY; + public static String CUT; + public static String PASTE; + public static String SELECTALL; + public static String CLEARALL; + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ActionMessages.properties b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ActionMessages.properties new file mode 100644 index 00000000000..e6302102f4d --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ActionMessages.properties @@ -0,0 +1,27 @@ +############################################################################### +# Copyright (c) 2003, 2018 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Initial Contributors: +# The following Wind River employees contributed to the Terminal component +# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, +# Helmut Haigermoser and Ted Williams. +# +# Contributors: +# Michael Scharf (Wind River) - split into core, view and connector plugins +# Martin Oberhuber (Wind River) - fixed copyright headers and beautified +# Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin +############################################################################### + +# NLS_MESSAGEFORMAT_NONE + +COPY = Copy +CUT = Cut +PASTE = Paste +SELECTALL = Select All +CLEARALL = Clear Terminal diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ImageConsts.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ImageConsts.java new file mode 100644 index 00000000000..5d3852f41b7 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/ImageConsts.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2003, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - extracted from TerminalConsts + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +public interface ImageConsts { + public final static String IMAGE_DIR_ROOT = "icons/"; //$NON-NLS-1$ + public final static String IMAGE_DIR_LOCALTOOL = IMAGE_DIR_ROOT + "clcl16/"; // basic colors - size 16x16 //$NON-NLS-1$ + public final static String IMAGE_DIR_DLCL = IMAGE_DIR_ROOT + "dlcl16/"; // disabled - size 16x16 //$NON-NLS-1$ + public final static String IMAGE_DIR_ELCL = IMAGE_DIR_ROOT + "elcl16/"; // enabled - size 16x16 //$NON-NLS-1$ + + public static final String IMAGE_CLCL_CLEAR_ALL = "ImageClclClearAll"; //$NON-NLS-1$ + + public static final String IMAGE_DLCL_CLEAR_ALL = "ImageDlclClearAll"; //$NON-NLS-1$ + + public static final String IMAGE_ELCL_CLEAR_ALL = "ImageElclClearAll"; //$NON-NLS-1$ +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionClearAll.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionClearAll.java new file mode 100644 index 00000000000..fb3e4a244de --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionClearAll.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2004, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin + * Uwe Stieber (Wind River) - [260372] [terminal] Certain terminal actions are enabled if no target terminal control is available + ********************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; + +public class TerminalActionClearAll extends AbstractTerminalAction { + public TerminalActionClearAll() { + super(TerminalActionClearAll.class.getName()); + + setupAction(ActionMessages.CLEARALL, ActionMessages.CLEARALL, ImageConsts.IMAGE_CLCL_CLEAR_ALL, + ImageConsts.IMAGE_ELCL_CLEAR_ALL, ImageConsts.IMAGE_DLCL_CLEAR_ALL, false); + } + + public TerminalActionClearAll(ITerminalViewControl target) { + super(target, TerminalActionClearAll.class.getName()); + + setupAction(ActionMessages.CLEARALL, ActionMessages.CLEARALL, ImageConsts.IMAGE_CLCL_CLEAR_ALL, + ImageConsts.IMAGE_ELCL_CLEAR_ALL, ImageConsts.IMAGE_DLCL_CLEAR_ALL, false); + } + + @Override + public void run() { + ITerminalViewControl target = getTarget(); + if (target != null) { + target.clearTerminal(); + } + } + + @Override + public void updateAction(boolean aboutToShow) { + ITerminalViewControl target = getTarget(); + setEnabled(target != null && !target.isEmpty()); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionCopy.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionCopy.java new file mode 100644 index 00000000000..d504de61786 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionCopy.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2004, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin + * Uwe Stieber (Wind River) - [260372] [terminal] Certain terminal actions are enabled if no target terminal control is available + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; + +public class TerminalActionCopy extends AbstractTerminalAction { + public TerminalActionCopy() { + super(TerminalActionCopy.class.getName()); + setActionDefinitionId("org.eclipse.tm.terminal.copy"); //$NON-NLS-1$ + ISharedImages si = PlatformUI.getWorkbench().getSharedImages(); + setupAction(ActionMessages.COPY, ActionMessages.COPY, si.getImageDescriptor(ISharedImages.IMG_TOOL_COPY), + si.getImageDescriptor(ISharedImages.IMG_TOOL_COPY), + si.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED), true); + } + + public TerminalActionCopy(ITerminalViewControl target) { + super(target, TerminalActionCopy.class.getName()); + setActionDefinitionId("org.eclipse.tm.terminal.copy"); //$NON-NLS-1$ + ISharedImages si = PlatformUI.getWorkbench().getSharedImages(); + setupAction(ActionMessages.COPY, ActionMessages.COPY, si.getImageDescriptor(ISharedImages.IMG_TOOL_COPY), + si.getImageDescriptor(ISharedImages.IMG_TOOL_COPY), + si.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED), true); + } + + @Override + public void run() { + ITerminalViewControl target = getTarget(); + if (target != null) { + String selection = target.getSelection(); + + if (!selection.equals("")) {//$NON-NLS-1$ + target.copy(); + } else { + target.sendKey('\u0003'); + } + } + } + + @Override + public void updateAction(boolean aboutToShow) { + ITerminalViewControl target = getTarget(); + if (aboutToShow && target != null) { + setEnabled(!target.getSelection().isEmpty()); + } else { + setEnabled(false); + } + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionCut.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionCut.java new file mode 100644 index 00000000000..172c5f3ac35 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionCut.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2004, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Uwe Stieber (Wind River) - [260372] [terminal] Certain terminal actions are enabled if no target terminal control is available + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; + +public class TerminalActionCut extends AbstractTerminalAction { + public TerminalActionCut() { + super(TerminalActionCut.class.getName()); + ISharedImages si = PlatformUI.getWorkbench().getSharedImages(); + setupAction(ActionMessages.CUT, ActionMessages.CUT, si.getImageDescriptor(ISharedImages.IMG_TOOL_CUT), + si.getImageDescriptor(ISharedImages.IMG_TOOL_CUT), + si.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED), true); + } + + public TerminalActionCut(ITerminalViewControl target) { + super(target, TerminalActionCut.class.getName()); + ISharedImages si = PlatformUI.getWorkbench().getSharedImages(); + setupAction(ActionMessages.CUT, ActionMessages.CUT, si.getImageDescriptor(ISharedImages.IMG_TOOL_CUT), + si.getImageDescriptor(ISharedImages.IMG_TOOL_CUT), + si.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED), true); + } + + @Override + public void run() { + ITerminalViewControl target = getTarget(); + if (target != null) { + target.sendKey('\u0018'); + } + } + + @Override + public void updateAction(boolean aboutToShow) { + // Cut is always disabled + setEnabled(false); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionPaste.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionPaste.java new file mode 100644 index 00000000000..2003f8e9270 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionPaste.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2004, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin + * Uwe Stieber (Wind River) - [260372] [terminal] Certain terminal actions are enabled if no target terminal control is available + * Uwe Stieber (Wind River) - [294719] [terminal] SWT Widget disposed in TerminalActionPaste + * Martin Oberhuber (Wind River) - [296212] Cannot paste text into terminal on some Linux hosts + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; + +public class TerminalActionPaste extends AbstractTerminalAction { + public TerminalActionPaste() { + super(TerminalActionPaste.class.getName()); + setActionDefinitionId("org.eclipse.tm.terminal.paste"); //$NON-NLS-1$ + ISharedImages si = PlatformUI.getWorkbench().getSharedImages(); + setupAction(ActionMessages.PASTE, ActionMessages.PASTE, si.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE), + si.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE), + si.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED), false); + } + + public TerminalActionPaste(ITerminalViewControl target) { + super(target, TerminalActionPaste.class.getName()); + setActionDefinitionId("org.eclipse.tm.terminal.paste"); //$NON-NLS-1$ + ISharedImages si = PlatformUI.getWorkbench().getSharedImages(); + setupAction(ActionMessages.PASTE, ActionMessages.PASTE, si.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE), + si.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE), + si.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED), false); + } + + @Override + public void run() { + ITerminalViewControl target = getTarget(); + if (target != null) { + target.paste(); + } + } + + @Override + public void updateAction(boolean aboutToShow) { + ITerminalViewControl target = getTarget(); + boolean bEnabled = false; + if (target != null) { + if (target.getState() == TerminalState.CONNECTED) { + if (target.getClipboard() != null && !target.getClipboard().isDisposed()) { + String strText = (String) target.getClipboard().getContents(TextTransfer.getInstance()); + if (strText != null && !strText.isEmpty()) { + bEnabled = true; + } + } + } + } + setEnabled(bEnabled); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionSelectAll.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionSelectAll.java new file mode 100644 index 00000000000..23a6ebaa4d1 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/actions/TerminalActionSelectAll.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2004, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin + * Uwe Stieber (Wind River) - [260372] [terminal] Certain terminal actions are enabled if no target terminal control is available + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.actions; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; + +public class TerminalActionSelectAll extends AbstractTerminalAction { + public TerminalActionSelectAll() { + super(TerminalActionSelectAll.class.getName()); + + setupAction(ActionMessages.SELECTALL, ActionMessages.SELECTALL, (ImageDescriptor) null, null, null, false); + } + + public TerminalActionSelectAll(ITerminalViewControl target) { + super(target, TerminalActionSelectAll.class.getName()); + + setupAction(ActionMessages.SELECTALL, ActionMessages.SELECTALL, (ImageDescriptor) null, null, null, false); + } + + @Override + public void run() { + ITerminalViewControl target = getTarget(); + if (target != null) { + target.selectAll(); + } + } + + @Override + public void updateAction(boolean aboutToShow) { + ITerminalViewControl target = getTarget(); + setEnabled(target != null && !target.isEmpty()); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/ITerminalControlForText.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/ITerminalControlForText.java new file mode 100644 index 00000000000..fc492dea59d --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/ITerminalControlForText.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anton Leherbauer (Wind River) - [458398] Add support for normal/application cursor keys mode + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.impl; + +import java.io.OutputStream; + +import org.eclipse.tm.internal.terminal.control.ITerminalListener3.TerminalTitleRequestor; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; + +/** + * need a better name! + * @author Michael Scharf + * + */ +public interface ITerminalControlForText { + + TerminalState getState(); + + void setState(TerminalState state); + + /** + * Set the title of the terminal. + * + * @param title Termianl title. + * @param requestor Item that requests terminal title update. + */ + void setTerminalTitle(String title, TerminalTitleRequestor requestor); + + ITerminalConnector getTerminalConnector(); + + OutputStream getOutputStream(); + + /** + * Enable/disable Application Cursor Keys mode (DECCKM) + * @param enable + */ + void enableApplicationCursorKeys(boolean enable); + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalMessages.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalMessages.java new file mode 100644 index 00000000000..2826d00fc31 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalMessages.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.impl; + +import org.eclipse.osgi.util.NLS; + +public class TerminalMessages extends NLS { + static { + NLS.initializeMessages(TerminalMessages.class.getName(), TerminalMessages.class); + } + + public static String TerminalColorPresets_EclipseDark; + public static String TerminalColorPresets_EclipseLight; + public static String TerminalColorPresets_TerminalDefaults; + public static String TerminalColorsFieldEditor_Background; + public static String TerminalColorsFieldEditor_Black; + public static String TerminalColorsFieldEditor_Blue; + public static String TerminalColorsFieldEditor_BrightBlack; + public static String TerminalColorsFieldEditor_BrightBlue; + public static String TerminalColorsFieldEditor_BrightCyan; + public static String TerminalColorsFieldEditor_BrightGreen; + public static String TerminalColorsFieldEditor_BrightMagenta; + public static String TerminalColorsFieldEditor_BrightRed; + public static String TerminalColorsFieldEditor_BrightWhite; + public static String TerminalColorsFieldEditor_BrightYellow; + public static String TerminalColorsFieldEditor_Cyan; + public static String TerminalColorsFieldEditor_GeneralColors; + public static String TerminalColorsFieldEditor_Green; + public static String TerminalColorsFieldEditor_LoadPresets; + public static String TerminalColorsFieldEditor_Magenta; + public static String TerminalColorsFieldEditor_PaletteColors; + public static String TerminalColorsFieldEditor_Presets; + public static String TerminalColorsFieldEditor_Red; + public static String TerminalColorsFieldEditor_SelectedText; + public static String TerminalColorsFieldEditor_Selection; + public static String TerminalColorsFieldEditor_TextColor; + public static String TerminalColorsFieldEditor_White; + public static String TerminalColorsFieldEditor_Yellow; + public static String TerminalError; + public static String SocketError; + public static String IOError; + public static String CannotConnectTo; + public static String NotInitialized; + + //Preference Page + public static String INVERT_COLORS; + public static String BUFFERLINES; + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalMessages.properties b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalMessages.properties new file mode 100644 index 00000000000..7d6366889ea --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalMessages.properties @@ -0,0 +1,56 @@ +############################################################################### +# Copyright (c) 2003, 2020 Wind River Systems, Inc. and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Initial Contributors: +# The following Wind River employees contributed to the Terminal component +# that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, +# Helmut Haigermoser and Ted Williams. +# +# Contributors: +# Michael Scharf (Wind River) - split into core, view and connector plugins +# Martin Oberhuber (Wind River) - fixed copyright headers and beautified +# Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget +############################################################################### + +# NLS_MESSAGEFORMAT_VAR +TerminalColorPresets_EclipseLight=Eclipse Light +TerminalColorPresets_TerminalDefaults=Terminal Defaults +TerminalColorPresets_EclipseDark=Eclipse Dark +TerminalColorsFieldEditor_Background=Background +TerminalColorsFieldEditor_Black=Black +TerminalColorsFieldEditor_Blue=Blue +TerminalColorsFieldEditor_BrightBlack=Bright Black +TerminalColorsFieldEditor_BrightBlue=Bright Blue +TerminalColorsFieldEditor_BrightCyan=Bright Cyan +TerminalColorsFieldEditor_BrightGreen=Bright Green +TerminalColorsFieldEditor_BrightMagenta=Bright Magenta +TerminalColorsFieldEditor_BrightRed=Bright Red +TerminalColorsFieldEditor_BrightWhite=Bright White +TerminalColorsFieldEditor_BrightYellow=Bright Yellow +TerminalColorsFieldEditor_Cyan=Cyan +TerminalColorsFieldEditor_GeneralColors=General colors +TerminalColorsFieldEditor_Green=Green +TerminalColorsFieldEditor_LoadPresets=Load Presets... +TerminalColorsFieldEditor_Magenta=Magenta +TerminalColorsFieldEditor_PaletteColors=Palette colors +TerminalColorsFieldEditor_Presets=Presets +TerminalColorsFieldEditor_Red=Red +TerminalColorsFieldEditor_SelectedText=Selected text +TerminalColorsFieldEditor_Selection=Selection +TerminalColorsFieldEditor_TextColor=Text color +TerminalColorsFieldEditor_White=White +TerminalColorsFieldEditor_Yellow=Yellow +TerminalError = Terminal Error +SocketError = Socket Error +IOError = IO Error +CannotConnectTo = Cannot initialize {0}:\n{1} +NotInitialized = Not Initialized + +INVERT_COLORS = Invert terminal colors +BUFFERLINES = Terminal buffer lines: diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalPlugin.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalPlugin.java new file mode 100644 index 00000000000..f8a46ccda47 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/control/impl/TerminalPlugin.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2003, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Anna Dushistova (MontaVista) - [227537] moved actions from terminal.view to terminal plugin + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.control.impl; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.tm.internal.terminal.control.actions.ImageConsts; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +public class TerminalPlugin extends AbstractUIPlugin { + private static TerminalPlugin plugin; + public static final String PLUGIN_ID = "org.eclipse.tm.terminal.control"; //$NON-NLS-1$ + public static final String HELP_VIEW = PLUGIN_ID + ".terminal_view"; //$NON-NLS-1$ + + /** + * The constructor. + */ + public TerminalPlugin() { + } + + /** + * Returns the shared instance. + */ + public static TerminalPlugin getDefault() { + return plugin; + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + public static boolean isOptionEnabled(String strOption) { + String strEnabled = Platform.getDebugOption(strOption); + if (strEnabled == null) + return false; + + return Boolean.parseBoolean(strEnabled); + } + + @Override + protected void initializeImageRegistry(ImageRegistry imageRegistry) { + try { + // Local toolbars + putImageInRegistry(imageRegistry, ImageConsts.IMAGE_CLCL_CLEAR_ALL, + ImageConsts.IMAGE_DIR_LOCALTOOL + "clear_co.gif"); //$NON-NLS-1$ + // Enabled local toolbars + putImageInRegistry(imageRegistry, ImageConsts.IMAGE_ELCL_CLEAR_ALL, + ImageConsts.IMAGE_DIR_ELCL + "clear_co.gif"); //$NON-NLS-1$ + // Disabled local toolbars + putImageInRegistry(imageRegistry, ImageConsts.IMAGE_DLCL_CLEAR_ALL, + ImageConsts.IMAGE_DIR_DLCL + "clear_co.gif"); //$NON-NLS-1$ + } catch (MalformedURLException malformedURLException) { + malformedURLException.printStackTrace(); + } + } + + protected void putImageInRegistry(ImageRegistry imageRegistry, String strKey, String relativePath) + throws MalformedURLException { + URL url = TerminalPlugin.getDefault().getBundle().getEntry(relativePath); + ImageDescriptor imageDescriptor = ImageDescriptor.createFromURL(url); + imageRegistry.put(strKey, imageDescriptor); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/EditActionAccelerators.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/EditActionAccelerators.java new file mode 100644 index 00000000000..af825928674 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/EditActionAccelerators.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2013, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tm.internal.terminal.emulator; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.bindings.TriggerSequence; +import org.eclipse.jface.bindings.keys.KeySequence; +import org.eclipse.jface.bindings.keys.KeyStroke; +import org.eclipse.jface.bindings.keys.SWTKeySupport; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.keys.IBindingService; + +class EditActionAccelerators { + private static final String COPY_COMMAND_ID = "org.eclipse.tm.terminal.copy"; //$NON-NLS-1$ + private static final String PASTE_COMMAND_ID = "org.eclipse.tm.terminal.paste"; //$NON-NLS-1$ + + private final Map commandIdsByAccelerator = new HashMap<>(); + + private void load() { + addAccelerator(COPY_COMMAND_ID); + addAccelerator(PASTE_COMMAND_ID); + } + + private void addAccelerator(String commandId) { + TriggerSequence[] bindings = bindingsFor(commandId); + for (int i = 0; i < bindings.length; ++i) { + if (bindings[i] instanceof KeySequence) { + KeyStroke[] keyStrokes = ((KeySequence) bindings[i]).getKeyStrokes(); + if (keyStrokes.length != 0) { + int accelerator = SWTKeySupport.convertKeyStrokeToAccelerator(keyStrokes[0]); + commandIdsByAccelerator.put(Integer.valueOf(accelerator), commandId); + } + } + } + } + + private static TriggerSequence[] bindingsFor(String commandId) { + IBindingService bindingService = bindingService(); + return bindingService.getActiveBindingsFor(commandId); + } + + private static IBindingService bindingService() { + return PlatformUI.getWorkbench().getAdapter(IBindingService.class); + } + + boolean isCopyAction(int accelerator) { + return isMatchingAction(accelerator, COPY_COMMAND_ID); + } + + boolean isPasteAction(int accelerator) { + return isMatchingAction(accelerator, PASTE_COMMAND_ID); + } + + private boolean isMatchingAction(int accelerator, String commandId) { + if (commandIdsByAccelerator.isEmpty()) { + load(); + } + return commandId.equals(commandIdsByAccelerator.get(Integer.valueOf(accelerator))); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/IVT100EmulatorBackend.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/IVT100EmulatorBackend.java new file mode 100644 index 00000000000..1323f3a1f35 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/IVT100EmulatorBackend.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [433751] Add option to enable VT100 line wrapping mode + * Anton Leherbauer (Wind River) - [458218] Add support for ANSI insert mode + * Anton Leherbauer (Wind River) - [458402] Add support for scroll up/down and scroll region + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.emulator; + +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * + */ +public interface IVT100EmulatorBackend { + + /** + * This method erases all text from the Terminal view. Including the history + */ + void clearAll(); + + /** + * Sets the Dimensions of the addressable scroll space of the screen.... + * Keeps the cursor position relative to the bottom of the screen! + * @param lines + * @param cols + */ + void setDimensions(int lines, int cols); + + /** + * This method makes room for N characters on the current line at the cursor + * position. Text under the cursor moves right without wrapping at the end + * of the line. + * 01234 + * 0 123 + */ + void insertCharacters(int charactersToInsert); + + /** + * Erases from cursor to end of screen, including cursor position. Cursor does not move. + */ + void eraseToEndOfScreen(); + + /** + * Erases from beginning of screen to cursor, including cursor position. Cursor does not move. + */ + void eraseToCursor(); + + /** + * Erases complete display. All lines are erased and changed to single-width. Cursor does not move. + */ + void eraseAll(); + + /** + * Erases complete line. + */ + void eraseLine(); + + /** + * Erases from cursor to end of line, including cursor position. + */ + void eraseLineToEnd(); + + /** + * Erases from beginning of line to cursor, including cursor position. + */ + void eraseLineToCursor(); + + /** + * Inserts n lines at line with cursor. Lines displayed below cursor move down. + * Lines moved past the bottom margin are lost. This sequence is ignored when + * cursor is outside scrolling region. + * @param n the number of lines to insert + */ + void insertLines(int n); + + /** + * Deletes n characters, starting with the character at cursor position. + * When a character is deleted, all characters to the right of cursor move + * left. This creates a space character at right margin. This character + * has same character attribute as the last character moved left. + * @param n + * 012345 + * 0145xx + */ + void deleteCharacters(int n); + + /** + * Deletes n lines, starting at line with cursor. As lines are deleted, + * lines displayed below cursor move up. Lines added to bottom of screen + * have spaces with same character attributes as last line moved up. This + * sequence is ignored when cursor is outside scrolling region. + * @param n the number of lines to delete + */ + void deleteLines(int n); + + TerminalStyle getDefaultStyle(); + + void setDefaultStyle(TerminalStyle defaultStyle); + + TerminalStyle getStyle(); + + /** + * Sets the style to be used from now on + * @param style + */ + void setStyle(TerminalStyle style); + + /** + * This method displays a subset of the newly-received text in the Terminal + * view, wrapping text at the right edge of the screen and overwriting text + * when the cursor is not at the very end of the screen's text. + *

            + * + * There are never any ANSI control characters or escape sequences in the + * text being displayed by this method (this includes newlines, carriage + * returns, and tabs). + *

            + */ + void appendString(String buffer); + + /** + * Process a newline (Control-J) character. A newline (NL) character just + * moves the cursor to the same column on the next line, creating new lines + * when the cursor reaches the bottom edge of the terminal. This is + * counter-intuitive, especially to UNIX programmers who are taught that + * writing a single NL to a terminal is sufficient to move the cursor to the + * first column of the next line, as if a carriage return (CR) and a NL were + * written. + *

            + * + * UNIX terminals typically display a NL character as a CR followed by a NL + * because the terminal device typically has the ONLCR attribute bit set + * (see the termios(4) man page for details), which causes the terminal + * device driver to translate NL to CR + NL on output. The terminal itself + * (i.e., a hardware terminal or a terminal emulator, like xterm or this + * code) _always_ interprets a CR to mean "move the cursor to the beginning + * of the current line" and a NL to mean "move the cursor to the same column + * on the next line". + *

            + */ + void processNewline(); + + /** + * This method returns the relative line number of the line containing the + * cursor. The returned line number is relative to the topmost visible line, + * which has relative line number 0. + * + * @return The relative line number of the line containing the cursor. + */ + int getCursorLine(); + + int getCursorColumn(); + + /** + * This method moves the cursor to the specified line and column. Parameter + * targetLine is the line number of a screen line, so it has a + * minimum value of 0 (the topmost screen line) and a maximum value of + * heightInLines - 1 (the bottommost screen line). A line does not have to + * contain any text to move the cursor to any column in that line. + */ + void setCursor(int targetLine, int targetColumn); + + void setCursorColumn(int targetColumn); + + void setCursorLine(int targetLine); + + int getLines(); + + int getColumns(); + + /** + * Enables VT100 line wrapping mode (default is off). + * This corresponds to the VT100 'eat_newline_glitch' terminal capability. + * If enabled, writing to the rightmost column does not cause + * an immediate wrap to the next line. Instead the line wrap occurs on the + * next output character. + * + * @param enable whether to enable or disable VT100 line wrapping mode + */ + void setVT100LineWrapping(boolean enable); + + /** + * @return whether VT100 line wrapping mode is enabled + */ + boolean isVT100LineWrapping(); + + /** + * Enables/disables insert mode (IRM). + * + * @param enable whether to enable insert mode + */ + void setInsertMode(boolean enable); + + /** + * Set scrolling region. Negative values reset the scroll region. + * + * @param top top line of scroll region + * @param bottom bottom line of scroll region + */ + void setScrollRegion(int top, int bottom); + + /** + * Scroll text upwards. + * + * @param lines number of lines to scroll + */ + void scrollUp(int lines); + + /** + * Scroll text downwards. + * + * @param lines number of lines to scroll + */ + void scrollDown(int lines); + + /** + * Process a reverse line feed/reverse index. + * + * The content is scrolled down if the cursor is at the top of the + * scroll region. + */ + void processReverseLineFeed(); + + /** + * Replaces characters from the cursor position with space characters. + * + * @param n number of characters to replace + */ + void eraseCharacters(int n); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/LoggingOutputStream.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/LoggingOutputStream.java new file mode 100644 index 00000000000..24c50555b26 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/LoggingOutputStream.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.emulator; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +public class LoggingOutputStream extends FilterOutputStream { + + public LoggingOutputStream(OutputStream out) { + super(out); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (Logger.isLogEnabled()) + Logger.log("Received " + len + " bytes: '" + //$NON-NLS-1$ //$NON-NLS-2$ + Logger.encode(new String(b, 0, len)) + "'"); //$NON-NLS-1$ + + // we cannot call super.write, because this would call our write + // which logs character by character..... + //super.write(b, off, len); + if ((off | len | (b.length - (len + off)) | (off + len)) < 0) + throw new IndexOutOfBoundsException(); + + for (int i = 0; i < len; i++) { + super.write(b[off + i]); + } + } + + @Override + public void write(int b) throws IOException { + if (Logger.isLogEnabled()) + Logger.log("Received " + 1 + " bytes: '" + //$NON-NLS-1$ //$NON-NLS-2$ + Logger.encode(new String(new byte[] { (byte) b }, 0, 1)) + "'"); //$NON-NLS-1$ + super.write(b); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100BackendTraceDecorator.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100BackendTraceDecorator.java new file mode 100644 index 00000000000..013beb1c189 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100BackendTraceDecorator.java @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [433751] Add option to enable VT100 line wrapping mode + * Anton Leherbauer (Wind River) - [458218] Add support for ANSI insert mode + * Anton Leherbauer (Wind River) - [458402] Add support for scroll up/down and scroll region + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.emulator; + +import java.io.PrintStream; + +import org.eclipse.tm.terminal.model.TerminalStyle; + +public class VT100BackendTraceDecorator implements IVT100EmulatorBackend { + final IVT100EmulatorBackend fBackend; + final PrintStream fWriter; + + public VT100BackendTraceDecorator(IVT100EmulatorBackend backend, PrintStream out) { + fBackend = backend; + fWriter = out; + } + + @Override + public void appendString(String buffer) { + fWriter.println("appendString(\"" + buffer + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.appendString(buffer); + } + + @Override + public void clearAll() { + fWriter.println("clearAll()"); //$NON-NLS-1$ + fBackend.clearAll(); + } + + @Override + public void deleteCharacters(int n) { + fWriter.println("deleteCharacters(" + n + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.deleteCharacters(n); + } + + @Override + public void deleteLines(int n) { + fWriter.println("deleteLines(" + n + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.deleteLines(n); + } + + @Override + public void eraseAll() { + fWriter.println("eraseAll()"); //$NON-NLS-1$ + fBackend.eraseAll(); + } + + @Override + public void eraseLine() { + fWriter.println("eraseLine()"); //$NON-NLS-1$ + fBackend.eraseLine(); + } + + @Override + public void eraseLineToCursor() { + fWriter.println("eraseLineToCursor()"); //$NON-NLS-1$ + fBackend.eraseLineToCursor(); + } + + @Override + public void eraseLineToEnd() { + fWriter.println("eraseLineToEnd()"); //$NON-NLS-1$ + fBackend.eraseLineToEnd(); + } + + @Override + public void eraseToCursor() { + fWriter.println("eraseToCursor()"); //$NON-NLS-1$ + fBackend.eraseToCursor(); + } + + @Override + public void eraseToEndOfScreen() { + fWriter.println("eraseToEndOfScreen()"); //$NON-NLS-1$ + fBackend.eraseToEndOfScreen(); + } + + @Override + public int getColumns() { + return fBackend.getColumns(); + } + + @Override + public int getCursorColumn() { + return fBackend.getCursorColumn(); + } + + @Override + public int getCursorLine() { + return fBackend.getCursorLine(); + } + + @Override + public TerminalStyle getDefaultStyle() { + return fBackend.getDefaultStyle(); + } + + @Override + public int getLines() { + return fBackend.getLines(); + } + + @Override + public TerminalStyle getStyle() { + return fBackend.getStyle(); + } + + @Override + public void insertCharacters(int charactersToInsert) { + fWriter.println("insertCharacters(" + charactersToInsert + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.insertCharacters(charactersToInsert); + } + + @Override + public void insertLines(int n) { + fWriter.println("insertLines(" + n + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.insertLines(n); + } + + @Override + public void processNewline() { + fWriter.println("processNewline()"); //$NON-NLS-1$ + fBackend.processNewline(); + } + + @Override + public void setCursor(int targetLine, int targetColumn) { + fWriter.println("setCursor(" + targetLine + ", " + targetColumn + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + fBackend.setCursor(targetLine, targetColumn); + } + + @Override + public void setCursorColumn(int targetColumn) { + fWriter.println("setCursorColumn(" + targetColumn + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.setCursorColumn(targetColumn); + } + + @Override + public void setCursorLine(int targetLine) { + fWriter.println("setCursorLine(" + targetLine + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.setCursorLine(targetLine); + } + + @Override + public void setDefaultStyle(TerminalStyle defaultStyle) { + fWriter.println("setDefaultStyle(" + defaultStyle + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.setDefaultStyle(defaultStyle); + } + + @Override + public void setDimensions(int lines, int cols) { + fWriter.println("setDimensions(" + lines + "," + cols + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + fBackend.setDimensions(lines, cols); + } + + @Override + public void setStyle(TerminalStyle style) { + fWriter.println("setStyle(" + style + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.setStyle(style); + } + + @Override + public void setVT100LineWrapping(boolean enable) { + fWriter.println("setVT100LineWrapping(" + enable + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.setVT100LineWrapping(enable); + } + + @Override + public boolean isVT100LineWrapping() { + return fBackend.isVT100LineWrapping(); + } + + @Override + public void setInsertMode(boolean enable) { + fWriter.println("setInsertMode(" + enable + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.setInsertMode(enable); + } + + @Override + public void setScrollRegion(int top, int bottom) { + fWriter.println("setScrollRegion(" + top + ',' + bottom + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.setScrollRegion(top, bottom); + } + + @Override + public void scrollUp(int lines) { + fWriter.println("scrollUp(" + lines + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.scrollUp(lines); + } + + @Override + public void scrollDown(int lines) { + fWriter.println("scrollDown(" + lines + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.scrollDown(lines); + } + + @Override + public void processReverseLineFeed() { + fWriter.println("processReverseLineFeed()"); //$NON-NLS-1$ + fBackend.processReverseLineFeed(); + } + + @Override + public void eraseCharacters(int n) { + fWriter.println("eraseCharacters(" + n + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + fBackend.eraseCharacters(n); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100Emulator.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100Emulator.java new file mode 100644 index 00000000000..3e9323812e0 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100Emulator.java @@ -0,0 +1,1624 @@ +/******************************************************************************* + * Copyright (c) 2003, 2021 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Michael Scharf (Wind River) - [209746] There are cases where some colors not displayed correctly + * Martin Oberhuber (Wind River) - [168197] Fix Terminal for CDC-1.1/Foundation-1.1 + * Michael Scharf (Wind River) - [262996] get rid of TerminalState.OPENED + * Martin Oberhuber (Wind River) - [334969] Fix multi-command SGR sequence + * Kris De Volder (VMWare) - [392107] Switched interpretation for ESC[0K and ESC[1K sequences + * Martin Oberhuber (Wind River) - [401386] Regression: No header on top due to incorrect ESC[K interpretation + * Martin Oberhuber (Wind River) - [401480] Handle ESC[39;49m and ESC[G + * Anton Leherbauer (Wind River) - [433751] Add option to enable VT100 line wrapping mode + * Anton Leherbauer (Wind River) - [458218] Add support for ANSI insert mode + * Anton Leherbauer (Wind River) - [458398] Add support for normal/application cursor keys mode + * Anton Leherbauer (Wind River) - [458402] Add support for scroll up/down and scroll region + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.emulator; + +import static org.eclipse.tm.terminal.model.TerminalColor.BLACK; +import static org.eclipse.tm.terminal.model.TerminalColor.BLUE; +import static org.eclipse.tm.terminal.model.TerminalColor.CYAN; +import static org.eclipse.tm.terminal.model.TerminalColor.GREEN; +import static org.eclipse.tm.terminal.model.TerminalColor.MAGENTA; +import static org.eclipse.tm.terminal.model.TerminalColor.RED; +import static org.eclipse.tm.terminal.model.TerminalColor.WHITE; +import static org.eclipse.tm.terminal.model.TerminalColor.YELLOW; + +import java.io.IOException; +import java.io.Reader; +import java.util.Arrays; + +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.terminal.control.ITerminalListener3.TerminalTitleRequestor; +import org.eclipse.tm.internal.terminal.control.impl.ITerminalControlForText; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * This class processes character data received from the remote host and + * displays it to the user using the Terminal view's StyledText widget. This + * class processes ANSI control characters, including NUL, backspace, carriage + * return, linefeed, and a subset of ANSI escape sequences sufficient to allow + * use of screen-oriented applications, such as vi, Emacs, and any GNU + * readline-enabled application (Bash, bc, ncftp, etc.). + *

            + * + * @author Fran Litterio + * @author Chris Thew + */ +public class VT100Emulator implements ControlListener { + /** This is a character processing state: Initial state. */ + private static final int ANSISTATE_INITIAL = 0; + + /** This is a character processing state: We've seen an escape character. */ + private static final int ANSISTATE_ESCAPE = 1; + + /** + * This is a character processing state: We've seen a '[' after an escape + * character. Expecting a parameter character or a command character next. + */ + private static final int ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND = 2; + + /** + * This is a character processing state: We've seen a ']' after an escape + * character. We are now expecting an operating system command that + * reprograms an intelligent terminal. + */ + private static final int ANSISTATE_EXPECTING_OS_COMMAND = 3; + + /** + * This is a character processing state: We've seen a '[?' after an escape + * character. Expecting a parameter character or a command character next. + */ + private static final int ANSISTATE_EXPECTING_DEC_PRIVATE_COMMAND = 4; + + /** + * This is a character processing state: We've seen one of ()*+-./ after an escape + * character. Expecting a character set designation character. + */ + private static final int ANSISTATE_EXPECTING_CHARSET_DESIGNATION = 5; + + /** + * For cases where the OSC (OS Command) ends with a multi-byte ST (i.e. ESC \) + * we use this extra state. + */ + private static final int ANSISTATE_EXPECTING_OS_COMMAND_END = 6; + + /** + * This field holds the current state of the Finite TerminalState Automaton (FSA) + * that recognizes ANSI escape sequences. + * + * @see #processNewText() + */ + private int ansiState = ANSISTATE_INITIAL; + + /** + * This field holds a reference to the {@link TerminalControl} object that + * instantiates this class. + */ + private final ITerminalControlForText terminal; + + /** + * This field holds a reference to the StyledText widget that is used to + * display text to the user. + */ + final private IVT100EmulatorBackend text; + /** + * This field hold the saved absolute line number of the cursor when + * processing the "ESC 7" and "ESC 8" command sequences. + */ + private int savedCursorLine = 0; + + /** + * This field hold the saved column number of the cursor when processing the + * "ESC 7" and "ESC 8" command sequences. + */ + private int savedCursorColumn = 0; + + /** + * This field holds an array of StringBuffer objects, each of which is one + * parameter from the current ANSI escape sequence. For example, when + * parsing the escape sequence "\e[20;10H", this array holds the strings + * "20" and "10". + */ + private final StringBuffer[] ansiParameters = new StringBuffer[16]; + + /** + * This field holds the OS-specific command found in an escape sequence of + * the form "\e]...\u0007". + */ + private final StringBuffer ansiOsCommand = new StringBuffer(128); + + /** + * This field holds the index of the next unused element of the array stored + * in field {@link #ansiParameters}. + */ + private int nextAnsiParameter = 0; + + Reader fReader; + + boolean fCrAfterNewLine; + + /** + * The constructor. + */ + public VT100Emulator(ITerminalTextData data, ITerminalControlForText terminal, Reader reader) { + super(); + + Logger.log("entered"); //$NON-NLS-1$ + + this.terminal = terminal; + + for (int i = 0; i < ansiParameters.length; ++i) { + ansiParameters[i] = new StringBuffer(); + } + setInputStreamReader(reader); + if (TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_VT100BACKEND)) + text = new VT100BackendTraceDecorator(new VT100EmulatorBackend(data), System.out); + else + text = new VT100EmulatorBackend(data); + + // text.setDimensions(24, 80); + TerminalStyle style = TerminalStyle.getDefaultStyle(); + text.setDefaultStyle(style); + text.setStyle(style); + } + + /** + * Set the reader that this Terminal gets its input from. + * + * The reader can be changed while the Terminal is running, but a change of + * the reader likely loses some characters which have not yet been fully + * read. Changing the reader can be done in order to change the selected + * Encoding, though. This is typically done when the Terminal is + * constructed, i.e. before it really starts operation; or, when the user + * manually selects a different encoding and thus doesn't care about losing + * old characters. + * + * @param reader the new Reader + */ + public void setInputStreamReader(Reader reader) { + fReader = reader; + } + + public void setDimensions(int lines, int cols) { + text.setDimensions(lines, cols); + ITerminalConnector telnetConnection = getConnector(); + if (telnetConnection != null) { + telnetConnection.setTerminalSize(text.getColumns(), text.getLines()); + } + + } + + /** + * This method performs clean up when this VT100Emulator object is no longer + * needed. After calling this method, no other method on this object should + * be called. + */ + public void dispose() { + } + + /** + * This method is required by interface ControlListener. It allows us to + * know when the StyledText widget is moved. + */ + @Override + public void controlMoved(ControlEvent event) { + Logger.log("entered"); //$NON-NLS-1$ + // Empty. + } + + /** + * This method is required by interface ControlListener. It allows us to + * know when the StyledText widget is resized. + */ + @Override + public void controlResized(ControlEvent event) { + Logger.log("entered"); //$NON-NLS-1$ + adjustTerminalDimensions(); + } + + /** + * This method erases all text from the Terminal view. + */ + public void clearTerminal() { + Logger.log("entered"); //$NON-NLS-1$ + text.clearAll(); + } + + /** + * This method is called when the user changes the Terminal view's font. We + * attempt to recompute the pixel width of the new font's characters and fix + * the terminal's dimensions. + */ + public void fontChanged() { + Logger.log("entered"); //$NON-NLS-1$ + + if (text != null) + adjustTerminalDimensions(); + } + + // /** + // * This method executes in the Display thread to process data received from + // * the remote host by class {@link org.eclipse.tm.internal.terminal.telnet.TelnetConnection} and + // * other implementors of {@link ITerminalConnector}, like the + // * SerialPortHandler. + // *

            + // * These connectors write text to the terminal's buffer through + // * {@link TerminalControl#writeToTerminal(String)} and then have + // * this run method executed in the display thread. This method + // * must not execute at the same time as methods + // * {@link #setNewText(StringBuffer)} and {@link #clearTerminal()}. + // *

            + // * IMPORTANT: This method must be called in strict alternation with method + // * {@link #setNewText(StringBuffer)}. + // *

            + // */ + public void processText() { + try { + // Find the width and height of the terminal, and resize it to display an + // integral number of lines and columns. + + adjustTerminalDimensions(); + + // Restore the caret offset, process and display the new text, then save + // the caret offset. See the documentation for field caretOffset for + // details. + + // ISSUE: Is this causing the scroll-to-bottom-on-output behavior? + + try { + processNewText(); + } catch (IOException e) { + Logger.logException(e); + } + + } catch (Exception ex) { + Logger.logException(ex); + } + } + + /** + * This method scans the newly received text, processing ANSI control + * characters and escape sequences and displaying normal text. + * @throws IOException + */ + private void processNewText() throws IOException { + Logger.log("entered"); //$NON-NLS-1$ + + // Scan the newly received text. + + while (hasNextChar()) { + char character = getNextChar(); + + switch (ansiState) { + case ANSISTATE_INITIAL: + switch (character) { + case '\u0000': + break; // NUL character. Ignore it. + + case '\u0007': + processBEL(); // BEL (Control-G) + break; + + case '\b': + processBackspace(); // Backspace + break; + + case '\t': + processTab(); // Tab. + break; + + case '\n': + processNewline(); // Newline (Control-J) + if (fCrAfterNewLine) + processCarriageReturn(); // Carriage Return (Control-M) + break; + + case '\r': + processCarriageReturn(); // Carriage Return (Control-M) + break; + + case '\u001b': + ansiState = ANSISTATE_ESCAPE; // Escape. + break; + + default: + processNonControlCharacters(character); + break; + } + break; + + case ANSISTATE_ESCAPE: + // We've seen an escape character. Here, we process the character + // immediately following the escape. + + switch (character) { + case '[': + ansiState = ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND; + nextAnsiParameter = 0; + + // Erase the parameter strings in preparation for optional + // parameter characters. + + for (int i = 0; i < ansiParameters.length; ++i) { + ansiParameters[i].delete(0, ansiParameters[i].length()); + } + break; + + case ']': + ansiState = ANSISTATE_EXPECTING_OS_COMMAND; + ansiOsCommand.delete(0, ansiOsCommand.length()); + break; + + case ')': + case '(': + case '*': + case '+': + case '-': + case '.': + case '/': + ansiState = ANSISTATE_EXPECTING_CHARSET_DESIGNATION; + break; + + case '7': + // Save cursor position and character attributes + + ansiState = ANSISTATE_INITIAL; + savedCursorLine = relativeCursorLine(); + savedCursorColumn = getCursorColumn(); + break; + + case '8': + // Restore cursor and attributes to previously saved + // position + + ansiState = ANSISTATE_INITIAL; + moveCursor(savedCursorLine, savedCursorColumn); + break; + + case 'c': + // Reset the terminal + ansiState = ANSISTATE_INITIAL; + resetTerminal(); + break; + + case 'M': + // Reverse line feed + ansiState = ANSISTATE_INITIAL; + text.processReverseLineFeed(); + break; + + default: + Logger.log("Unsupported escape sequence: escape '" + character + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + ansiState = ANSISTATE_INITIAL; + break; + } + break; + + case ANSISTATE_EXPECTING_PARAMETER_OR_COMMAND: + // characters `<=>?` mark private commands + if (character >= '\u003c' && character <= '\u003f') { + ansiState = ANSISTATE_EXPECTING_DEC_PRIVATE_COMMAND; + break; + } + + // Parameters can appear after the '[' in an escape sequence, but they + // are optional. + + if (character >= '\u0040' && character < '\u007f') { + ansiState = ANSISTATE_INITIAL; + processAnsiCommandCharacter(character); + } else { + processAnsiParameterCharacter(character); + } + break; + + case ANSISTATE_EXPECTING_OS_COMMAND: + // A BEL (\u0007) or ESC \ ('\e\\') character marks the end of the OSC sequence. + + if (character == '\u0007') { + ansiState = ANSISTATE_INITIAL; + processAnsiOsCommand(); + } else if (character == '\u001b') { + ansiState = ANSISTATE_EXPECTING_OS_COMMAND_END; + } else { + ansiOsCommand.append(character); + } + break; + + case ANSISTATE_EXPECTING_OS_COMMAND_END: + ansiState = ANSISTATE_INITIAL; + if (character == '\\') { + processAnsiOsCommand(); + } else { + Logger.log("Unsupported escape sequence: escape '" + ansiOsCommand + " \\e" + character + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + break; + + case ANSISTATE_EXPECTING_DEC_PRIVATE_COMMAND: + // Parameters can appear after the '[?' in an escape sequence, but they + // are optional. + + if (character >= '\u0040' && character < '\u007f') { + ansiState = ANSISTATE_INITIAL; + processDecPrivateCommandCharacter(character); + } else { + processAnsiParameterCharacter(character); + } + break; + + case ANSISTATE_EXPECTING_CHARSET_DESIGNATION: + if (character != '%') + ansiState = ANSISTATE_INITIAL; + // Character set designation commands are ignored + break; + + default: + // This should never happen! If it does happen, it means there is a + // bug in the FSA. For robustness, we return to the initial + // state. + + Logger.log("INVALID ANSI FSA STATE: " + ansiState); //$NON-NLS-1$ + ansiState = ANSISTATE_INITIAL; + break; + } + } + } + + private void resetTerminal() { + text.eraseAll(); + text.setCursor(0, 0); + text.setStyle(text.getDefaultStyle()); + text.setScrollRegion(-1, -1); + text.setInsertMode(false); + terminal.enableApplicationCursorKeys(false); + } + + /** + * This method is called when we have parsed an OS Command escape sequence. + * The only one we support is "\e]0;...\u0007", which sets the terminal + * title. + */ + private void processAnsiOsCommand() { + if (ansiOsCommand.charAt(0) != '0' || ansiOsCommand.charAt(1) != ';') { + Logger.log("Ignoring unsupported ANSI OSC sequence: '" + ansiOsCommand + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + return; + } + terminal.setTerminalTitle(ansiOsCommand.substring(2), TerminalTitleRequestor.ANSI); + } + + /** + * This method dispatches control to various processing methods based on the + * command character found in the most recently received ANSI escape + * sequence. This method only handles command characters that follow the + * ANSI standard Control Sequence Introducer (CSI), which is "\e[...", where + * "..." is an optional ';'-separated sequence of numeric parameters. + *

            + */ + private void processAnsiCommandCharacter(char ansiCommandCharacter) { + // If the width or height of the terminal is ridiculously small (one line or + // column or less), don't even try to process the escape sequence. This avoids + // throwing an exception (SPR 107450). The display will be messed up, but what + // did you user expect by making the terminal so small? + + switch (ansiCommandCharacter) { + case '@': + // Insert character(s). + processAnsiCommand_atsign(); + break; + + case 'A': + // Move cursor up N lines (default 1). + processAnsiCommand_A(); + break; + + case 'B': + // Move cursor down N lines (default 1). + processAnsiCommand_B(); + break; + + case 'C': + // Move cursor forward N columns (default 1). + processAnsiCommand_C(); + break; + + case 'D': + // Move cursor backward N columns (default 1). + processAnsiCommand_D(); + break; + + case 'd': + // Line Position Absolute [row] (default = [1,column]) (VPA). + processAnsiCommand_d(); + break; + + case 'E': + // Move cursor to first column of Nth next line (default 1). + processAnsiCommand_E(); + break; + + case 'F': + // Move cursor to first column of Nth previous line (default 1). + processAnsiCommand_F(); + break; + + case 'G': + // Move to column N of current line (default 1). + processAnsiCommand_G(); + break; + + case 'H': + // Set cursor Position. + processAnsiCommand_H(); + break; + + case 'h': + // Reset Mode. + processAnsiCommand_h(); + break; + + case 'J': + // Erase part or all of display. Cursor does not move. + processAnsiCommand_J(); + break; + + case 'K': + // Erase in line (cursor does not move). + processAnsiCommand_K(); + break; + + case 'L': + // Insert line(s) (current line moves down). + processAnsiCommand_L(); + break; + + case 'l': + // Set Mode. + processAnsiCommand_l(); + break; + + case 'M': + // Delete line(s). + processAnsiCommand_M(); + break; + + case 'm': + // Set Graphics Rendition (SGR). + processAnsiCommand_m(); + break; + + case 'n': + // Device Status Report (DSR). + processAnsiCommand_n(); + break; + + case 'P': + // Delete character(s). + processAnsiCommand_P(); + break; + + case 'r': + // Set Scrolling Region. + processAnsiCommand_r(); + break; + + case 'S': + // Scroll up. + processAnsiCommand_S(); + break; + + case 'T': + // Scroll down. + processAnsiCommand_T(); + break; + + case 'X': + // Erase character. + processAnsiCommand_X(); + break; + + case 'Z': + // Cursor back tab. + // Emacs, vi, and GNU readline don't seem to use this command, so we ignore + // it for now. + break; + + default: + Logger.log("Ignoring unsupported ANSI command character: '" + //$NON-NLS-1$ + ansiCommandCharacter + "'"); //$NON-NLS-1$ + break; + } + } + + /** + * This method dispatches control to various processing methods based on the + * command character found in the most recently received DEC private mode escape + * sequence. This method only handles command characters that follow the + * control sequence CSI ? + */ + private void processDecPrivateCommandCharacter(char commandCharacter) { + switch (commandCharacter) { + case 'h': + // DEC Private Mode Set (DECSET) + processDecPrivateCommand_h(); + break; + + case 'l': + // DEC Private Mode Reset (DECRST) + processDecPrivateCommand_l(); + break; + + default: + Logger.log("Ignoring unsupported DEC private command character: '" + //$NON-NLS-1$ + commandCharacter + "'"); //$NON-NLS-1$ + break; + } + } + + /** + * This method makes room for N characters on the current line at the cursor + * position. Text under the cursor moves right without wrapping at the end + * of the line. + */ + private void processAnsiCommand_atsign() { + int charactersToInsert = getAnsiParameter(0); + text.insertCharacters(charactersToInsert); + } + + /** + * This method moves the cursor up by the number of lines specified by the + * escape sequence parameter (default 1). + */ + private void processAnsiCommand_A() { + moveCursorUp(getAnsiParameter(0)); + } + + /** + * This method moves the cursor down by the number of lines specified by the + * escape sequence parameter (default 1). + */ + private void processAnsiCommand_B() { + moveCursorDown(getAnsiParameter(0)); + } + + /** + * This method moves the cursor forward by the number of columns specified + * by the escape sequence parameter (default 1). + */ + private void processAnsiCommand_C() { + moveCursorForward(getAnsiParameter(0)); + } + + /** + * This method moves the cursor backward by the number of columns specified + * by the escape sequence parameter (default 1). + */ + private void processAnsiCommand_D() { + moveCursorBackward(getAnsiParameter(0)); + } + + /** + * This method moves the cursor to a specific row. + */ + private void processAnsiCommand_d() { + // Line Position Absolute [row] (default = [1,column]) (VPA). + text.setCursorLine(getAnsiParameter(0) - 1); + } + + /** + * This method moves the cursor to the first column of the Nth next line, + * where N is specified by the ANSI parameter (default 1). + */ + private void processAnsiCommand_E() { + int linesToMove = getAnsiParameter(0); + + moveCursor(relativeCursorLine() + linesToMove, 0); + } + + /** + * This method moves the cursor to the first column of the Nth previous + * line, where N is specified by the ANSI parameter (default 1). + */ + private void processAnsiCommand_F() { + int linesToMove = getAnsiParameter(0); + + moveCursor(relativeCursorLine() - linesToMove, 0); + } + + /** + * This method moves the cursor within the current line to the column + * specified by the ANSI parameter (default is column 1). + */ + private void processAnsiCommand_G() { + moveCursor(relativeCursorLine(), getAnsiParameter(0) - 1); + } + + /** + * This method sets the cursor to a position specified by the escape + * sequence parameters (default is the upper left corner of the screen). + */ + private void processAnsiCommand_H() { + moveCursor(getAnsiParameter(0) - 1, getAnsiParameter(1) - 1); + } + + /** + * This method sets terminal modes. + */ + private void processAnsiCommand_h() { + if (getAnsiParameter(0) == 4) { + // set insert mode + text.setInsertMode(true); + } + } + + /** + * This method deletes some (or all) of the text on the screen without + * moving the cursor. + */ + private void processAnsiCommand_J() { + int ansiParameter; + + if (ansiParameters[0].length() == 0) + ansiParameter = 0; + else + ansiParameter = getAnsiParameter(0); + + switch (ansiParameter) { + case 0: + text.eraseToEndOfScreen(); + break; + + case 1: + // Erase from beginning to current position (inclusive). + text.eraseToCursor(); + break; + + case 2: + // Erase entire display. + + text.eraseAll(); + break; + + case 3: + // Erase display and clear scrollback (extended "E3" capability) + text.clearAll(); + break; + + default: + Logger.log("Unexpected J-command parameter: " + ansiParameter); //$NON-NLS-1$ + break; + } + } + + /** + * This method deletes some (or all) of the text in the current line without + * moving the cursor. + */ + private void processAnsiCommand_K() { + //Bug 401386: missing parameter must be interpreted as 0, and not 1 like most other defaults. + int ansiParameter = 0; + if (ansiParameters[0].length() > 0) + ansiParameter = getAnsiParameter(0); + + switch (ansiParameter) { + case 0: + // Erase from current position to end (inclusive). + text.eraseLineToEnd(); + break; + + case 1: + // Erase from beginning to current position (inclusive). + text.eraseLineToCursor(); + break; + + case 2: + // Erase entire line. + text.eraseLine(); + break; + + default: + Logger.log("Unexpected K-command parameter: " + ansiParameter); //$NON-NLS-1$ + break; + } + } + + /** + * Insert one or more blank lines. The current line of text moves down. Text + * that falls off the bottom of the screen is deleted. + */ + private void processAnsiCommand_L() { + text.insertLines(getAnsiParameter(0)); + } + + /** + * This method resets terminal modes. + */ + private void processAnsiCommand_l() { + if (getAnsiParameter(0) == 4) { + // reset insert mode + text.setInsertMode(false); + } + } + + /** + * Delete one or more lines of text. Any lines below the deleted lines move + * up, which we implement by appending newlines to the end of the text. + */ + private void processAnsiCommand_M() { + text.deleteLines(getAnsiParameter(0)); + } + + /** + * This method sets a new graphics rendition mode, such as + * foreground/background color, bold/normal text, and reverse video. + */ + private void processAnsiCommand_m() { + if (ansiParameters[0].length() == 0) { + // This a special case: when no ANSI parameter is specified, act like a + // single parameter equal to 0 was specified. + + ansiParameters[0].append('0'); + } + TerminalStyle style = text.getStyle(); + // There are a non-zero number of ANSI parameters. Process each one in + // order. + + int totalParameters = ansiParameters.length; + int parameterIndex = 0; + + while (parameterIndex < totalParameters && ansiParameters[parameterIndex].length() > 0) { + int ansiParameter = getAnsiParameter(parameterIndex); + if (ansiParameter == 1) { + String parameter = ansiParameters[parameterIndex].toString(); + // Special case for ITU's T.416 foreground/background color specification + // which uses : to separate parameters instead of ; + if (parameter.startsWith("38:") || parameter.startsWith("48:")) { //$NON-NLS-1$ //$NON-NLS-2$ + String[] split = parameter.split(":"); //$NON-NLS-1$ + ProcessExtendedColorsReturn retval = processExtendedColors(split, style, true); + style = retval.style(); + parameterIndex++; + continue; + } + + } + + switch (ansiParameter) { + case 0: + // Reset all graphics modes. + style = text.getDefaultStyle(); + break; + + case 1: + style = style.setBold(true); + break; + + case 4: + style = style.setUnderline(true); + break; + + case 5: + style = style.setBlink(true); + break; + + case 7: + style = style.setReverse(true); + break; + + case 10: // Set primary font. Ignored. + break; + + case 21: + case 22: + style = style.setBold(false); + break; + + case 24: + style = style.setUnderline(false); + break; + + case 25: + style = style.setBlink(false); + break; + + case 27: + style = style.setReverse(false); + break; + + case 90: + style = style.setBold(true); + //$FALL-THROUGH$ + case 30: + style = style.setForeground(BLACK); + break; + + case 91: + style = style.setBold(true); + //$FALL-THROUGH$ + case 31: + style = style.setForeground(RED); + break; + + case 92: + style = style.setBold(true); + //$FALL-THROUGH$ + case 32: + style = style.setForeground(GREEN); + break; + + case 93: + style = style.setBold(true); + //$FALL-THROUGH$ + case 33: + style = style.setForeground(YELLOW); + break; + + case 94: + style = style.setBold(true); + //$FALL-THROUGH$ + case 34: + style = style.setForeground(BLUE); + break; + + case 95: + style = style.setBold(true); + //$FALL-THROUGH$ + case 35: + style = style.setForeground(MAGENTA); + break; + + case 96: + style = style.setBold(true); + //$FALL-THROUGH$ + case 36: + style = style.setForeground(CYAN); + break; + + case 97: + style = style.setBold(true); + //$FALL-THROUGH$ + case 37: + style = style.setForeground(WHITE); + break; + + case 39: //Foreground: Default + style = style.setForeground(text.getDefaultStyle()); + break; + + case 100: + style = style.setBold(true); + //$FALL-THROUGH$ + case 40: + style = style.setBackground(BLACK); + break; + + case 101: + style = style.setBold(true); + //$FALL-THROUGH$ + case 41: + style = style.setBackground(RED); + break; + + case 102: + style = style.setBold(true); + //$FALL-THROUGH$ + case 42: + style = style.setBackground(GREEN); + break; + + case 103: + style = style.setBold(true); + //$FALL-THROUGH$ + case 43: + style = style.setBackground(YELLOW); + break; + + case 104: + style = style.setBold(true); + //$FALL-THROUGH$ + case 44: + style = style.setBackground(BLUE); + break; + + case 105: + style = style.setBold(true); + //$FALL-THROUGH$ + case 45: + style = style.setBackground(MAGENTA); + break; + + case 106: + style = style.setBold(true); + //$FALL-THROUGH$ + case 46: + style = style.setBackground(CYAN); + break; + + case 107: + style = style.setBold(true); + //$FALL-THROUGH$ + case 47: + style = style.setBackground(WHITE); + break; + + case 49: //Background: Default + style = style.setBackground(text.getDefaultStyle()); + break; + + case 38: // Foreground color defined by sequence + case 48: // Background color defined by sequence + CharSequence[] params = Arrays.copyOfRange(ansiParameters, parameterIndex, ansiParameters.length); + ProcessExtendedColorsReturn retval = processExtendedColors(params, style, false); + parameterIndex += retval.consumed() - 1; + style = retval.style(); + break; + + default: + Logger.log("Unsupported graphics rendition parameter: " + ansiParameter); //$NON-NLS-1$ + break; + } + + ++parameterIndex; + } + text.setStyle(style); + } + + private interface ProcessExtendedColorsReturn { + /** + * @return the new style + */ + TerminalStyle style(); + + /** + * @return number of parameters consumed + */ + int consumed(); + } + + /** + * + * @param params array of parameters, starting with 38 or 48 being the command + * @param colorspace if a colorspace may be included (ITU T.416 mode) + */ + private ProcessExtendedColorsReturn processExtendedColors(CharSequence[] paramStrings, TerminalStyle style, + boolean colorspace) { + int params[] = new int[paramStrings.length]; + for (int i = 0; i < params.length; i++) { + try { + int parseInt = Integer.parseInt(paramStrings[i].toString()); + int inMagnitude = parseInt % 256; + int inRange = inMagnitude < 0 ? inMagnitude + 256 : inMagnitude; + params[i] = inRange; + } catch (NumberFormatException ex) { + params[i] = 0; + } + } + + boolean foreground = params[0] == 38; + int consumed = 1; + if (params.length > 1) { + int colorDepth = params[1]; + switch (colorDepth) { + case 2: // 24-bit RGB color + int r = 0, g = 0, b = 0; + if (colorspace) { + if (params.length < 6) { + Logger.log( + "Not enough parameters for 24-bit color depth, expected 5, one for color space and one for each of RGB"); //$NON-NLS-1$ + } + } else { + if (params.length < 5) { + Logger.log("Not enough parameters for 24-bit color depth, expected 3, one for each of RGB"); //$NON-NLS-1$ + } + } + int start = colorspace ? 3 : 2; + if (params.length > start + 0) { + r = params[start + 0]; + } + if (params.length > start + 1) { + g = params[start + 1]; + } + if (params.length > start + 2) { + b = params[start + 2]; + } + + RGB rgb = new RGB(r, g, b); + if (foreground) { + style = style.setForeground(rgb); + } else { + style = style.setBackground(rgb); + } + consumed = Math.min(6, params.length); + break; + case 5: // 8-bit color table lookup + int index = 0; + if (params.length < 3) { + Logger.log("Missing parameter for 8-bit color depth"); //$NON-NLS-1$ + } else { + index = params[2]; + } + if (foreground) { + style = style.setForeground(index); + } else { + style = style.setBackground(index); + } + consumed = Math.min(3, params.length); + break; + default: + Logger.log("Unsupported color depth " + colorDepth + " for: " + params[0]); //$NON-NLS-1$ //$NON-NLS-2$ + } + + } else { + Logger.log("Missing color depth for " + params[0]); //$NON-NLS-1$ + } + + TerminalStyle finalStyle = style; + int finalConsumed = consumed; + return new ProcessExtendedColorsReturn() { + @Override + public TerminalStyle style() { + return finalStyle; + } + + @Override + public int consumed() { + return finalConsumed; + } + }; + } + + /** + * This method responds to an ANSI Device Status Report (DSR) command from + * the remote endpoint requesting the ready status or the cursor position. + * Requests for other kinds of status are ignored. + */ + private void processAnsiCommand_n() { + String reply; + + if (getAnsiParameter(0) == 5) { + // Report that the terminal is ready and has no malfunctions. + reply = "\u001b[0n"; //$NON-NLS-1$ + + } else if (getAnsiParameter(0) == 6) { + // Send the ANSI cursor position (which is 1-based) to the remote + // endpoint. + reply = "\u001b[" + (relativeCursorLine() + 1) + ";" + //$NON-NLS-1$ //$NON-NLS-2$ + (getCursorColumn() + 1) + "R"; //$NON-NLS-1$ + + } else { + // Do nothing if the numeric parameter was not 5 or 6. + return; + } + + try { + terminal.getOutputStream().write(reply.getBytes("UTF-8")); //$NON-NLS-1$ + terminal.getOutputStream().flush(); + } catch (IOException ex) { + Logger.log("Caught IOException!"); //$NON-NLS-1$ + } + } + + /** + * Deletes one or more characters starting at the current cursor position. + * Characters on the same line and to the right of the deleted characters + * move left. If there are no characters on the current line at or to the + * right of the cursor column, no text is deleted. + */ + private void processAnsiCommand_P() { + text.deleteCharacters(getAnsiParameter(0)); + } + + /** + * Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM). + */ + private void processAnsiCommand_r() { + int top = 0; + int bottom = 0; + if (ansiParameters[0].length() > 0 && ansiParameters[1].length() > 0) { + top = getAnsiParameter(0); + bottom = getAnsiParameter(1); + } + text.setScrollRegion(top - 1, bottom - 1); + } + + /** + * Scroll up n lines (default = 1 line). + */ + private void processAnsiCommand_S() { + text.scrollUp(getAnsiParameter(0)); + } + + /** + * Scroll down n lines (default = 1 line). + */ + private void processAnsiCommand_T() { + text.scrollDown(getAnsiParameter(0)); + } + + /** + * Erases n characters from cursor (default = 1 character) + */ + private void processAnsiCommand_X() { + text.eraseCharacters(getAnsiParameter(0)); + } + + private void processDecPrivateCommand_h() { + int param = getAnsiParameter(0); + switch (param) { + case 1: + // Enable Application Cursor Keys (DECCKM) + terminal.enableApplicationCursorKeys(true); + break; + case 47: + case 1047: + case 1048: + case 1049: + // Use Alternate Screen Buffer (ignored). + break; + default: + Logger.log("Unsupported command parameter: CSI ?" + param + 'h'); //$NON-NLS-1$ + break; + } + } + + private void processDecPrivateCommand_l() { + int param = getAnsiParameter(0); + switch (param) { + case 1: + // Enable Normal Cursor Keys (DECCKM) + terminal.enableApplicationCursorKeys(false); + break; + case 47: + case 1047: + case 1048: + case 1049: + // Use Normal Screen Buffer (ignored, but reset scroll region). + text.setScrollRegion(-1, -1); + break; + default: + Logger.log("Unsupported command parameter: CSI ?" + param + 'l'); //$NON-NLS-1$ + break; + } + } + + /** + * This method returns one of the numeric ANSI parameters received in the + * most recent escape sequence. + * + * @return The parameterIndexth numeric ANSI parameter or -1 if the + * index is out of range or 1 if parse failed (1 is also a legitimate value) + */ + private int getAnsiParameter(int parameterIndex) { + if (parameterIndex < 0 || parameterIndex >= ansiParameters.length) { + // This should never happen. + return -1; + } + + String parameter = ansiParameters[parameterIndex].toString(); + + if (parameter.length() == 0) + return 1; + + // return 1 on failed parseInt + int parameterValue = 1; + + // Don't trust the remote endpoint to send well formed numeric + // parameters. + + try { + parameterValue = Integer.parseInt(parameter); + } catch (NumberFormatException ex) { + parameterValue = 1; + } + + return parameterValue; + } + + /** + * This method processes a single parameter character in an ANSI escape + * sequence. Parameters are the (optional) characters between the leading + * "\e[" and the command character in an escape sequence (e.g., in the + * escape sequence "\e[20;10H", the parameter characters are "20;10"). + * Parameters are integers separated by one or more ';'s. + */ + private void processAnsiParameterCharacter(char ch) { + if (ch == ';') { + ++nextAnsiParameter; + } else { + if (nextAnsiParameter < ansiParameters.length) + ansiParameters[nextAnsiParameter].append(ch); + } + } + + /** + * This method processes a contiguous sequence of non-control characters. + * This is a performance optimization, so that we don't have to insert or + * append each non-control character individually to the StyledText widget. + * A non-control character is any character that passes the condition in the + * below while loop. + * @throws IOException + */ + private void processNonControlCharacters(char character) throws IOException { + StringBuffer buffer = new StringBuffer(); + buffer.append(character); + // Identify a contiguous sequence of non-control characters, starting at + // firstNonControlCharacterIndex in newText. + while (hasNextChar()) { + character = getNextChar(); + if (character == '\u0000' || character == '\b' || character == '\t' || character == '\u0007' + || character == '\n' || character == '\r' || character == '\u001b') { + pushBackChar(character); + break; + } + buffer.append(character); + } + + // Now insert the sequence of non-control characters in the StyledText widget + // at the location of the cursor. + + displayNewText(buffer.toString()); + } + + /** + * This method displays a subset of the newly-received text in the Terminal + * view, wrapping text at the right edge of the screen and overwriting text + * when the cursor is not at the very end of the screen's text. + *

            + * + * There are never any ANSI control characters or escape sequences in the + * text being displayed by this method (this includes newlines, carriage + * returns, and tabs). + *

            + */ + private void displayNewText(String buffer) { + text.appendString(buffer); + } + + /** + * Process a BEL (Control-G) character. + */ + private void processBEL() { + // TODO + //Display.getDefault().beep(); + } + + /** + * Process a backspace (Control-H) character. + */ + private void processBackspace() { + moveCursorBackward(1); + } + + /** + * Process a tab (Control-I) character. We don't insert a tab character into + * the StyledText widget. Instead, we move the cursor forward to the next + * tab stop, without altering any of the text. Tab stops are every 8 + * columns. The cursor will never move past the rightmost column. + */ + private void processTab() { + moveCursorForward(8 - (getCursorColumn() % 8)); + } + + /** + * Process a newline (Control-J) character. A newline (NL) character just + * moves the cursor to the same column on the next line, creating new lines + * when the cursor reaches the bottom edge of the terminal. This is + * counter-intuitive, especially to UNIX programmers who are taught that + * writing a single NL to a terminal is sufficient to move the cursor to the + * first column of the next line, as if a carriage return (CR) and a NL were + * written. + *

            + * + * UNIX terminals typically display a NL character as a CR followed by a NL + * because the terminal device typically has the ONLCR attribute bit set + * (see the termios(4) man page for details), which causes the terminal + * device driver to translate NL to CR + NL on output. The terminal itself + * (i.e., a hardware terminal or a terminal emulator, like xterm or this + * code) _always_ interprets a CR to mean "move the cursor to the beginning + * of the current line" and a NL to mean "move the cursor to the same column + * on the next line". + *

            + */ + private void processNewline() { + text.processNewline(); + } + + /** + * Process a Carriage Return (Control-M). + */ + private void processCarriageReturn() { + text.setCursorColumn(0); + } + + /** + * This method computes the width of the terminal in columns and its height + * in lines, then adjusts the width and height of the view's StyledText + * widget so that it displays an integral number of lines and columns of + * text. The adjustment is always to shrink the widget vertically or + * horizontally, because if the control were to grow, it would be clipped by + * the edges of the view window (i.e., the view window does not become + * larger to accommodate its contents becoming larger). + *

            + * + * This method must be called immediately before each time text is written + * to the terminal so that we can properly line wrap text. Because it is + * called so frequently, it must be fast when there is no resizing to be + * done. + *

            + */ + private void adjustTerminalDimensions() { + // Compute how many pixels we need to shrink the StyledText control vertically + // to make it display an integral number of lines of text. + + // TODO + // if(text.getColumns()!=80 && text.getLines()!=80) + // text.setDimensions(24, 80); + // If we are in a TELNET connection and we know the dimensions of the terminal, + // we give the size information to the TELNET connection object so it can + // communicate it to the TELNET server. If we are in a serial connection, + // there is nothing we can do to tell the remote host about the size of the + // terminal. + ITerminalConnector telnetConnection = getConnector(); + // TODO MSA: send only if dimensions have really changed! + if (telnetConnection != null) { + telnetConnection.setTerminalSize(text.getColumns(), text.getLines()); + } + + } + + private ITerminalConnector getConnector() { + if (terminal.getTerminalConnector() != null) + return terminal.getTerminalConnector(); + return null; + } + + /** + * This method returns the relative line number of the line containing the + * cursor. The returned line number is relative to the topmost visible line, + * which has relative line number 0. + * + * @return The relative line number of the line containing the cursor. + */ + private int relativeCursorLine() { + return text.getCursorLine(); + } + + /** + * This method moves the cursor to the specified line and column. Parameter + * targetLine is the line number of a screen line, so it has a + * minimum value of 0 (the topmost screen line) and a maximum value of + * heightInLines - 1 (the bottommost screen line). A line does not have to + * contain any text to move the cursor to any column in that line. + */ + private void moveCursor(int targetLine, int targetColumn) { + text.setCursor(targetLine, targetColumn); + } + + /** + * This method moves the cursor down lines lines, but won't move the + * cursor past the bottom of the screen. This method does not cause any + * scrolling. + */ + private void moveCursorDown(int lines) { + moveCursor(relativeCursorLine() + lines, getCursorColumn()); + } + + /** + * This method moves the cursor up lines lines, but won't move the + * cursor past the top of the screen. This method does not cause any + * scrolling. + */ + private void moveCursorUp(int lines) { + moveCursor(relativeCursorLine() - lines, getCursorColumn()); + } + + /** + * This method moves the cursor forward columns columns, but won't + * move the cursor past the right edge of the screen, nor will it move the + * cursor onto the next line. This method does not cause any scrolling. + */ + private void moveCursorForward(int columnsToMove) { + moveCursor(relativeCursorLine(), getCursorColumn() + columnsToMove); + } + + /** + * This method moves the cursor backward columnsToMove columns, but + * won't move the cursor past the left edge of the screen, nor will it move + * the cursor onto the previous line. This method does not cause any + * scrolling. + */ + private void moveCursorBackward(int columnsToMove) { + moveCursor(relativeCursorLine(), getCursorColumn() - columnsToMove); + } + + /** + * Resets the state of the terminal text (foreground color, background color, + * font style and other internal state). It essentially makes it ready for new input. + */ + public void resetState() { + ansiState = ANSISTATE_INITIAL; + text.setStyle(text.getDefaultStyle()); + text.setScrollRegion(-1, -1); + text.setInsertMode(false); + } + + // public OutputStream getOutputStream() { + // return fTerminalInputStream.getOutputStream(); + // } + + /** + * Buffer for {@link #pushBackChar(char)}. + */ + private int fNextChar = -1; + + private char getNextChar() throws IOException { + int c = -1; + if (fNextChar != -1) { + c = fNextChar; + fNextChar = -1; + } else { + c = fReader.read(); + } + // TODO: better end of file handling + if (c == -1) + c = 0; + return (char) c; + } + + private boolean hasNextChar() throws IOException { + if (fNextChar < 0 && fReader.ready()) { + fNextChar = fReader.read(); + } + return fNextChar >= 0; + } + + /** + * Put back one character to the stream. This method can push + * back exactly one character. The character is the next character + * returned by {@link #getNextChar} + * @param c the character to be pushed back. + */ + void pushBackChar(char c) { + //assert fNextChar!=-1: "Already a character waiting:"+fNextChar; //$NON-NLS-1$ + fNextChar = c; + } + + private int getCursorColumn() { + return text.getCursorColumn(); + } + + public boolean isCrAfterNewLine() { + return fCrAfterNewLine; + } + + public void setCrAfterNewLine(boolean crAfterNewLine) { + fCrAfterNewLine = crAfterNewLine; + } + + void setVT100LineWrapping(boolean enable) { + text.setVT100LineWrapping(enable); + } + + boolean isVT100LineWrapping() { + return text.isVT100LineWrapping(); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100EmulatorBackend.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100EmulatorBackend.java new file mode 100644 index 00000000000..14d79198ce1 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100EmulatorBackend.java @@ -0,0 +1,504 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [206329] Changing terminal size right after connect does not scroll properly + * Anton Leherbauer (Wind River) - [433751] Add option to enable VT100 line wrapping mode + * Anton Leherbauer (Wind River) - [458218] Add support for ANSI insert mode + * Anton Leherbauer (Wind River) - [458402] Add support for scroll up/down and scroll region + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.emulator; + +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @noreference This class not intended to be referenced by clients. + * It used to be package protected, and it is public only for Unit Tests. + * + */ +public class VT100EmulatorBackend implements IVT100EmulatorBackend { + + private static class ScrollRegion { + static final ScrollRegion FULL_WINDOW = new ScrollRegion(0, Integer.MAX_VALUE - 1); + private final int fTop; + private final int fBottom; + + ScrollRegion(int top, int bottom) { + fTop = top; + fBottom = bottom; + } + + boolean contains(int line) { + return line >= fTop && line <= fBottom; + } + + int getTopLine() { + return fTop; + } + + int getBottomLine() { + return fBottom; + } + + int getHeight() { + return fBottom - fTop + 1; + } + } + + /** + * This field holds the number of the column in which the cursor is + * logically positioned. The leftmost column on the screen is column 0, and + * column numbers increase to the right. The maximum value of this field is + * {@link #widthInColumns} - 1. We track the cursor column using this field + * to avoid having to recompute it repeatly using StyledText method calls. + *

            + * + * The StyledText widget that displays text has a vertical bar (called the + * "caret") that appears _between_ character cells, but ANSI terminals have + * the concept of a cursor that appears _in_ a character cell, so we need a + * convention for which character cell the cursor logically occupies when + * the caret is physically between two cells. The convention used in this + * class is that the cursor is logically in column N when the caret is + * physically positioned immediately to the _left_ of column N. + *

            + * + * When fCursorColumn is N, the next character output to the terminal appears + * in column N. When a character is output to the rightmost column on a + * given line (column widthInColumns - 1), the cursor moves to column 0 on + * the next line after the character is drawn (this is the default line wrapping + * mode). If VT100 line wrapping mode is enabled, the cursor does not move + * to the next line until the next character is printed (this is known as + * the VT100 'eat_newline_glitch'). + * If the cursor is in the bottommost line when line wrapping + * occurs, the topmost visible line is scrolled off the top edge of the + * screen. + *

            + */ + private int fCursorColumn; + private int fCursorLine; + /* true if last output occurred on rightmost column + * and next output requires line wrap */ + private boolean fWrapPending; + private boolean fInsertMode; + private TerminalStyle fDefaultStyle; + private TerminalStyle fStyle; + int fLines; + int fColumns; + final private ITerminalTextData fTerminal; + private boolean fVT100LineWrapping; + private ScrollRegion fScrollRegion = ScrollRegion.FULL_WINDOW; + + public VT100EmulatorBackend(ITerminalTextData terminal) { + fTerminal = terminal; + } + + @Override + public void clearAll() { + synchronized (fTerminal) { + // clear the history + int n = fTerminal.getHeight(); + for (int line = 0; line < n; line++) { + fTerminal.cleanLine(line); + } + fTerminal.setDimensions(fLines, fTerminal.getWidth()); + setStyle(getDefaultStyle()); + setCursor(0, 0); + } + } + + @Override + public void setDimensions(int lines, int cols) { + synchronized (fTerminal) { + if (lines == fLines && cols == fColumns) + return; // nothing to do + // relative cursor line + int cl = getCursorLine(); + int cc = getCursorColumn(); + int height = fTerminal.getHeight(); + // absolute cursor line + int acl = cl + height - fLines; + int newLines = Math.max(lines, height); + if (lines < fLines) { + if (height == fLines) { + // if the terminal has no history, then resize by + // setting the size to the new size + // TODO We are assuming that cursor line points at end of text + newLines = Math.max(lines, cl + 1); + } + } + fLines = lines; + fColumns = cols; + // make the terminal at least as high as we need lines + fTerminal.setDimensions(newLines, fColumns); + // compute relative cursor line + cl = acl - (newLines - fLines); + setCursor(cl, cc); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * It used to be package protected, and it is public only for Unit Tests. + */ + public int toAbsoluteLine(int line) { + synchronized (fTerminal) { + return fTerminal.getHeight() - fLines + line; + } + } + + @Override + public void insertCharacters(int charactersToInsert) { + synchronized (fTerminal) { + int line = toAbsoluteLine(fCursorLine); + int n = charactersToInsert; + for (int col = fColumns - 1; col >= fCursorColumn + n; col--) { + char c = fTerminal.getChar(line, col - n); + TerminalStyle style = fTerminal.getStyle(line, col - n); + fTerminal.setChar(line, col, c, style); + } + int last = Math.min(fCursorColumn + n, fColumns); + for (int col = fCursorColumn; col < last; col++) { + fTerminal.setChar(line, col, '\000', null); + } + } + } + + @Override + public void eraseToEndOfScreen() { + synchronized (fTerminal) { + eraseLineToEnd(); + for (int line = toAbsoluteLine(fCursorLine + 1); line < toAbsoluteLine(fLines); line++) { + fTerminal.cleanLine(line); + } + } + + } + + @Override + public void eraseToCursor() { + synchronized (fTerminal) { + for (int line = toAbsoluteLine(0); line < toAbsoluteLine(fCursorLine); line++) { + fTerminal.cleanLine(line); + } + eraseLineToCursor(); + } + } + + @Override + public void eraseAll() { + synchronized (fTerminal) { + for (int line = toAbsoluteLine(0); line < toAbsoluteLine(fLines); line++) { + fTerminal.cleanLine(line); + } + } + } + + @Override + public void eraseLine() { + synchronized (fTerminal) { + fTerminal.cleanLine(toAbsoluteLine(fCursorLine)); + } + } + + @Override + public void eraseLineToEnd() { + synchronized (fTerminal) { + int line = toAbsoluteLine(fCursorLine); + for (int col = fCursorColumn; col < fColumns; col++) { + fTerminal.setChar(line, col, '\000', null); + } + } + } + + @Override + public void eraseLineToCursor() { + synchronized (fTerminal) { + int line = toAbsoluteLine(fCursorLine); + for (int col = 0; col <= fCursorColumn; col++) { + fTerminal.setChar(line, col, '\000', null); + } + } + } + + @Override + public void insertLines(int n) { + synchronized (fTerminal) { + if (!isCusorInScrollingRegion()) + return; + assert n > 0; + int line = toAbsoluteLine(fCursorLine); + int nLines = Math.min(fTerminal.getHeight() - line, fScrollRegion.getBottomLine() - fCursorLine + 1); + fTerminal.scroll(line, nLines, n); + } + } + + @Override + public void deleteCharacters(int n) { + synchronized (fTerminal) { + int line = toAbsoluteLine(fCursorLine); + for (int col = fCursorColumn + n; col < fColumns; col++) { + char c = fTerminal.getChar(line, col); + TerminalStyle style = fTerminal.getStyle(line, col); + fTerminal.setChar(line, col - n, c, style); + } + int first = Math.max(fCursorColumn, fColumns - n); + for (int col = first; col < fColumns; col++) { + fTerminal.setChar(line, col, '\000', null); + } + } + } + + @Override + public void deleteLines(int n) { + synchronized (fTerminal) { + if (!isCusorInScrollingRegion()) + return; + assert n > 0; + int line = toAbsoluteLine(fCursorLine); + int nLines = Math.min(fTerminal.getHeight() - line, fScrollRegion.getBottomLine() - fCursorLine + 1); + fTerminal.scroll(line, nLines, -n); + } + } + + private boolean isCusorInScrollingRegion() { + return fScrollRegion.contains(fCursorLine); + } + + @Override + public TerminalStyle getDefaultStyle() { + synchronized (fTerminal) { + return fDefaultStyle; + } + } + + @Override + public void setDefaultStyle(TerminalStyle defaultStyle) { + synchronized (fTerminal) { + fDefaultStyle = defaultStyle; + } + } + + @Override + public TerminalStyle getStyle() { + synchronized (fTerminal) { + if (fStyle == null) + return fDefaultStyle; + return fStyle; + } + } + + @Override + public void setStyle(TerminalStyle style) { + synchronized (fTerminal) { + fStyle = style; + } + } + + @Override + public void appendString(String buffer) { + synchronized (fTerminal) { + char[] chars = buffer.toCharArray(); + if (fInsertMode) + insertCharacters(chars.length); + int line = toAbsoluteLine(fCursorLine); + int i = 0; + while (i < chars.length) { + if (fWrapPending) { + line = doLineWrap(); + } + int n = Math.min(fColumns - fCursorColumn, chars.length - i); + fTerminal.setChars(line, fCursorColumn, chars, i, n, fStyle); + int col = fCursorColumn + n; + i += n; + // wrap needed? + if (col == fColumns) { + if (fVT100LineWrapping) { + // deferred line wrapping (eat_newline_glitch) + setCursorColumn(col - 1); + fWrapPending = true; + } else { + line = doLineWrap(); + } + } else { + setCursorColumn(col); + } + } + } + } + + private int doLineWrap() { + int line; + line = toAbsoluteLine(fCursorLine); + fTerminal.setWrappedLine(line); + doNewline(); + line = toAbsoluteLine(fCursorLine); + setCursorColumn(0); + return line; + } + + /** + * MUST be called from a synchronized block! + */ + private void doNewline() { + if (fCursorLine == fScrollRegion.getBottomLine()) + scrollUp(1); + else if (fCursorLine + 1 >= fLines) { + int h = fTerminal.getHeight(); + fTerminal.addLine(); + if (h != fTerminal.getHeight()) + setCursorLine(fCursorLine + 1); + } else { + setCursorLine(fCursorLine + 1); + } + } + + @Override + public void processNewline() { + synchronized (fTerminal) { + doNewline(); + } + } + + private void doReverseLineFeed() { + if (fCursorLine == fScrollRegion.getTopLine()) + scrollDown(1); + else + setCursorLine(fCursorLine - 1); + } + + @Override + public void processReverseLineFeed() { + synchronized (fTerminal) { + doReverseLineFeed(); + } + } + + @Override + public int getCursorLine() { + synchronized (fTerminal) { + return fCursorLine; + } + } + + @Override + public int getCursorColumn() { + synchronized (fTerminal) { + return fCursorColumn; + } + } + + @Override + public void setCursor(int targetLine, int targetColumn) { + synchronized (fTerminal) { + setCursorLine(targetLine); + setCursorColumn(targetColumn); + } + } + + @Override + public void setCursorColumn(int targetColumn) { + synchronized (fTerminal) { + if (targetColumn < 0) + targetColumn = 0; + else if (targetColumn >= fColumns) + targetColumn = fColumns - 1; + fCursorColumn = targetColumn; + fWrapPending = false; + // We make the assumption that nobody is changing the + // terminal cursor except this class! + // This assumption gives a huge performance improvement + fTerminal.setCursorColumn(targetColumn); + } + } + + @Override + public void setCursorLine(int targetLine) { + synchronized (fTerminal) { + if (targetLine < 0) + targetLine = 0; + else if (targetLine >= fLines) + targetLine = fLines - 1; + fCursorLine = targetLine; + // We make the assumption that nobody is changing the + // terminal cursor except this class! + // This assumption gives a huge performance improvement + fTerminal.setCursorLine(toAbsoluteLine(targetLine)); + } + } + + @Override + public int getLines() { + synchronized (fTerminal) { + return fLines; + } + } + + @Override + public int getColumns() { + synchronized (fTerminal) { + return fColumns; + } + } + + @Override + public void setVT100LineWrapping(boolean enable) { + fVT100LineWrapping = enable; + } + + @Override + public boolean isVT100LineWrapping() { + return fVT100LineWrapping; + } + + @Override + public void setInsertMode(boolean enable) { + fInsertMode = enable; + } + + @Override + public void setScrollRegion(int top, int bottom) { + if (top < 0 || bottom < 0) + fScrollRegion = ScrollRegion.FULL_WINDOW; + else if (top < bottom) + fScrollRegion = new ScrollRegion(top, bottom); + } + + @Override + public void scrollUp(int n) { + assert n > 0; + synchronized (fTerminal) { + int line = toAbsoluteLine(fScrollRegion.getTopLine()); + int nLines = Math.min(fTerminal.getHeight() - line, fScrollRegion.getHeight()); + fTerminal.scroll(line, nLines, -n); + } + } + + @Override + public void scrollDown(int n) { + assert n > 0; + synchronized (fTerminal) { + int line = toAbsoluteLine(fScrollRegion.getTopLine()); + int nLines = Math.min(fTerminal.getHeight() - line, fScrollRegion.getHeight()); + fTerminal.scroll(line, nLines, n); + } + } + + @Override + public void eraseCharacters(int n) { + synchronized (fTerminal) { + int line = toAbsoluteLine(fCursorLine); + int end = Math.min(fCursorColumn + n, fColumns); + for (int col = fCursorColumn; col < end; col++) { + fTerminal.setChar(line, col, '\000', null); + } + } + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100TerminalControl.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100TerminalControl.java new file mode 100644 index 00000000000..d6c94710ab1 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100TerminalControl.java @@ -0,0 +1,1437 @@ +/******************************************************************************* + * Copyright (c) 2003, 2021 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [206892] State handling: Only allow connect when CLOSED + * Martin Oberhuber (Wind River) - [206883] Serial Terminal leaks Jobs + * Martin Oberhuber (Wind River) - [208145] Terminal prints garbage after quick disconnect/reconnect + * Martin Oberhuber (Wind River) - [207785] NPE when trying to send char while no longer connected + * Michael Scharf (Wind River) - [209665] Add ability to log byte streams from terminal + * Ruslan Sychev (Xored Software) - [217675] NPE or SWTException when closing Terminal View while connection establishing + * Michael Scharf (Wing River) - [196447] The optional terminal input line should be resizeable + * Martin Oberhuber (Wind River) - [168197] Replace JFace MessagDialog by SWT MessageBox + * Martin Oberhuber (Wind River) - [204796] Terminal should allow setting the encoding to use + * Michael Scharf (Wind River) - [237398] Terminal get Invalid Thread Access when the title is set + * Martin Oberhuber (Wind River) - [240745] Pressing Ctrl+F1 in the Terminal should bring up context help + * Michael Scharf (Wind River) - [240098] The cursor should not blink when the terminal is disconnected + * Anton Leherbauer (Wind River) - [335021] Middle mouse button copy/paste does not work with the terminal + * Max Stepanov (Appcelerator) - [339768] Fix ANSI code for PgUp / PgDn + * Pawel Piech (Wind River) - [333613] "Job found still running" after shutdown + * Martin Oberhuber (Wind River) - [348700] Terminal unusable after disconnect + * Simon Bernard (Sierra Wireless) - [351424] [terminal] Terminal does not support del and insert key + * Martin Oberhuber (Wind River) - [265352][api] Allow setting fonts programmatically + * Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget + * Anton Leherbauer (Wind River) - [433751] Add option to enable VT100 line wrapping mode + * Anton Leherbauer (Wind River) - [434294] Incorrect handling of function keys with modifiers + * Martin Oberhuber (Wind River) - [434294] Add Mac bindings with COMMAND + * Anton Leherbauer (Wind River) - [434749] UnhandledEventLoopException when copying to clipboard while the selection is empty + * Martin Oberhuber (Wind River) - [436612] Restore Eclipse 3.4 compatibility by using Reflection + * Anton Leherbauer (Wind River) - [458398] Add support for normal/application cursor keys mode + * Anton Leherbauer (Wind River) - [420928] Terminal widget leaks memory + * Davy Landman (CWI) - [475267][api] Allow custom mouse listeners + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.emulator; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.net.SocketException; +import java.nio.charset.Charset; +import java.util.EnumMap; +import java.util.Map; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.bindings.Binding; +import org.eclipse.jface.bindings.keys.KeySequence; +import org.eclipse.jface.bindings.keys.KeyStroke; +import org.eclipse.jface.bindings.keys.SWTKeySupport; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.DataFormatException; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.StringConverter; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tm.internal.terminal.control.ICommandInputField; +import org.eclipse.tm.internal.terminal.control.ITerminalListener; +import org.eclipse.tm.internal.terminal.control.ITerminalListener2; +import org.eclipse.tm.internal.terminal.control.ITerminalListener3; +import org.eclipse.tm.internal.terminal.control.ITerminalListener3.TerminalTitleRequestor; +import org.eclipse.tm.internal.terminal.control.ITerminalMouseListener; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.control.impl.ITerminalControlForText; +import org.eclipse.tm.internal.terminal.control.impl.TerminalMessages; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.preferences.ITerminalConstants; +import org.eclipse.tm.internal.terminal.preferences.TerminalColorPresets; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; +import org.eclipse.tm.internal.terminal.textcanvas.PipedInputStream; +import org.eclipse.tm.internal.terminal.textcanvas.PollingTextCanvasModel; +import org.eclipse.tm.internal.terminal.textcanvas.TextCanvas; +import org.eclipse.tm.internal.terminal.textcanvas.TextLineRenderer; +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.TerminalColor; +import org.eclipse.tm.terminal.model.TerminalTextDataFactory; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.keys.IBindingService; + +/** + * + * This class was originally written to use nested classes, which unfortunately makes + * this source file larger and more complex than it needs to be. In particular, the + * methods in the nested classes directly access the fields of the enclosing class. + * One day we should pull the nested classes out into their own source files (but still + * in this package). + * + * @author Chris Thew + */ +public class VT100TerminalControl implements ITerminalControlForText, ITerminalControl, ITerminalViewControl { + protected final static String[] LINE_DELIMITERS = { "\n" }; //$NON-NLS-1$ + + /** + * This field holds a reference to a TerminalText object that performs all ANSI + * text processing on data received from the remote host and controls how text is + * displayed using the view's StyledText widget. + */ + private final VT100Emulator fTerminalText; + private Display fDisplay; + private TextCanvas fCtlText; + private Composite fWndParent; + private Clipboard fClipboard; + private KeyListener fKeyHandler; + private final ITerminalListener fTerminalListener; + private String fMsg = ""; //$NON-NLS-1$ + private TerminalFocusListener fFocusListener; + private ITerminalConnector fConnector; + private final ITerminalConnector[] fConnectors; + private final IPreferenceStore fPreferenceStore; + private boolean connectOnEnterIfClosed = true; + + PipedInputStream fInputStream; + private Charset fCharset = Charset.defaultCharset(); + private InputStreamReader fInputStreamReader; + + private ICommandInputField fCommandInputField; + + private volatile TerminalState fState; + + private final ITerminalTextData fTerminalModel; + + private final EditActionAccelerators editActionAccelerators = new EditActionAccelerators(); + + private boolean fApplicationCursorKeys; + + /** + * Listens to changes in the preferences + */ + private final IPropertyChangeListener fPreferenceListener = this::updatePreferences; + private final IPropertyChangeListener fFontListener = this::updateFont; + + /** + * Is protected by synchronize on this + */ + volatile private Job fJob; + + private PollingTextCanvasModel fPollingTextCanvasModel; + + /** + * In some circumstances (e.g PowerShell on Windows) the backspace + * character received from the keypress needs modifying. This + * system property allows disabling this new feature in case there + * are users who are negatively affected by this conversion. + * + * \b is ^H which is interpreted by the console as Ctrl + Backspace + * which deletes a word. \b on its own should just delete a character + * so we send 0x7f to do that. + */ + private boolean convertBackspace = Boolean + .parseBoolean(System.getProperty("org.eclipse.tm.terminal.control.convertBackspace", "true")); //$NON-NLS-1$ //$NON-NLS-2$ + + /** + * Instantiate a Terminal widget. + * @param target Callback for notifying the owner of Terminal state changes. + * @param wndParent The Window parent to embed the Terminal in. + * @param connectors Provided connectors. + * @param preferenceStore If non-null, the Terminal widget will pick up settings + * from the given store. + * @since 3.2 + */ + public VT100TerminalControl(ITerminalListener target, Composite wndParent, ITerminalConnector[] connectors) { + this(target, wndParent, connectors, null); + } + + /** + * Instantiate a Terminal widget using the org.eclipse.tm.terminal.TerminalPreferencePage Preference page's + * default preference store. + * @param target Callback for notifying the owner of Terminal state changes. + * @param wndParent The Window parent to embed the Terminal in. + * @param connectors Provided connectors. + * @param useCommonPrefs If true, the Terminal widget will pick up settings + * from the org.eclipse.tm.terminal.TerminalPreferencePage Preference page. + * Otherwise, clients need to maintain settings themselves. + * @since 3.2 + */ + public VT100TerminalControl(ITerminalListener target, Composite wndParent, ITerminalConnector[] connectors, + boolean useCommonPrefs) { + this(target, wndParent, connectors, useCommonPrefs ? TerminalPlugin.getDefault().getPreferenceStore() : null); + } + + /** + * Instantiate a Terminal widget. + * @param target Callback for notifying the owner of Terminal state changes. + * @param wndParent The Window parent to embed the Terminal in. + * @param connectors Provided connectors. + * @param preferenceStore If non-null, the Terminal widget will pick up settings + * from the given store. + * @since 5.0 + */ + public VT100TerminalControl(ITerminalListener target, Composite wndParent, ITerminalConnector[] connectors, + IPreferenceStore preferenceStore) { + fConnectors = connectors; + fPreferenceStore = preferenceStore; + fTerminalListener = target; + fTerminalModel = TerminalTextDataFactory.makeTerminalTextData(); + fTerminalModel.setMaxHeight(1000); + fInputStream = new PipedInputStream(8 * 1024); + fTerminalText = new VT100Emulator(fTerminalModel, this, null); + // Use Default Charset as start, until setCharset() is called + setCharset(Charset.defaultCharset()); + setupTerminal(wndParent); + } + + @Override + @Deprecated + public void setEncoding(String encoding) throws UnsupportedEncodingException { + Charset charset; + if (encoding == null) { + charset = Charset.defaultCharset(); + } else { + charset = Charset.forName(encoding); + } + // remember encoding if above didn't throw an exception + setCharset(charset); + } + + @Override + public void setCharset(Charset charset) { + if (charset == null) { + charset = Charset.defaultCharset(); + } + fInputStreamReader = new InputStreamReader(fInputStream, charset); + fCharset = charset; + fTerminalText.setInputStreamReader(fInputStreamReader); + } + + @Override + @Deprecated + public String getEncoding() { + return fCharset.name(); + } + + @Override + public Charset getCharset() { + return fCharset; + } + + @Override + public ITerminalConnector[] getConnectors() { + return fConnectors; + } + + @Override + public void copy() { + copy(DND.CLIPBOARD); + } + + private void copy(int clipboardType) { + String selection = getSelection(); + if (selection.length() > 0) { + Object[] data = new Object[] { selection }; + Transfer[] types = new Transfer[] { TextTransfer.getInstance() }; + fClipboard.setContents(data, types, clipboardType); + } + } + + @Override + public void paste() { + paste(DND.CLIPBOARD); + // TODO paste in another thread.... to avoid blocking + // new Thread() { + // public void run() { + // for (int i = 0; i < strText.length(); i++) { + // sendChar(strText.charAt(i), false); + // } + // + // } + // }.start(); + } + + private void paste(int clipboardType) { + TextTransfer textTransfer = TextTransfer.getInstance(); + String strText = (String) fClipboard.getContents(textTransfer, clipboardType); + pasteString(strText); + } + + /** + * @param strText the text to paste + */ + @Override + public boolean pasteString(String strText) { + if (!isConnected()) + return false; + if (strText == null) + return false; + sendString(strText); + return true; + } + + @Override + public void selectAll() { + getCtlText().selectAll(); + if (fTerminalListener instanceof ITerminalListener2) { + ((ITerminalListener2) fTerminalListener).setTerminalSelectionChanged(); + } + } + + @Override + public void sendKey(char character) { + Event event; + KeyEvent keyEvent; + + event = new Event(); + event.widget = getCtlText(); + event.character = character; + event.keyCode = 0; + event.stateMask = 0; + event.doit = true; + keyEvent = new KeyEvent(event); + + fKeyHandler.keyPressed(keyEvent); + } + + @Override + public void clearTerminal() { + // The TerminalText object does all text manipulation. + getTerminalText().clearTerminal(); + getCtlText().clearSelection(); + if (fTerminalListener instanceof ITerminalListener2) { + ((ITerminalListener2) fTerminalListener).setTerminalSelectionChanged(); + } + } + + @Override + public Clipboard getClipboard() { + return fClipboard; + } + + /** + * @return non null selection + */ + @Override + public String getSelection() { + String txt = fCtlText.getSelectionText(); + if (txt == null) + txt = ""; //$NON-NLS-1$ + return txt; + } + + @Override + public void setFocus() { + getCtlText().setFocus(); + } + + @Override + public boolean isEmpty() { + return getCtlText().isEmpty(); + } + + @Override + public boolean isDisposed() { + return getCtlText().isDisposed(); + } + + @Override + public boolean isConnected() { + return fState == TerminalState.CONNECTED; + } + + @Override + public void disposeTerminal() { + Logger.log("entered."); //$NON-NLS-1$ + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(fPreferenceListener); + } + JFaceResources.getFontRegistry().removeListener(fFontListener); + disconnectTerminal(); + fClipboard.dispose(); + getTerminalText().dispose(); + } + + @Override + public void connectTerminal() { + Logger.log("entered."); //$NON-NLS-1$ + if (getTerminalConnector() == null) + return; + fTerminalText.resetState(); + fApplicationCursorKeys = false; + if (fConnector.getInitializationErrorMessage() != null) { + showErrorMessage(NLS.bind(TerminalMessages.CannotConnectTo, fConnector.getName(), + fConnector.getInitializationErrorMessage())); + // we cannot connect because the connector was not initialized + return; + } + // clean the error message + setMsg(""); //$NON-NLS-1$ + getTerminalConnector().connect(this); + waitForConnect(); + } + + @Override + public ITerminalConnector getTerminalConnector() { + return fConnector; + } + + @Override + public void disconnectTerminal() { + Logger.log("entered."); //$NON-NLS-1$ + + //Disconnect the remote side first + if (getState() != TerminalState.CLOSED) { + if (getTerminalConnector() != null) { + getTerminalConnector().disconnect(); + } + } + + //Ensure that a new Job can be started; then clean up old Job. + Job job; + synchronized (this) { + job = fJob; + fJob = null; + } + if (job != null) { + job.cancel(); + // Join job to avoid leaving job running after workbench shutdown (333613). + // Interrupt to be fast enough; cannot close fInputStream since it is re-used (bug 348700). + Thread t = job.getThread(); + if (t != null) + t.interrupt(); + try { + job.join(); + } catch (InterruptedException e) { + } + } + fPollingTextCanvasModel.stopPolling(); + } + + private void waitForConnect() { + Logger.log("entered."); //$NON-NLS-1$ + + // TODO Eliminate the nested dispatch loop + do { + if (!fDisplay.readAndDispatch()) + fDisplay.sleep(); + } while (getState() == TerminalState.CONNECTING); + + if (getCtlText().isDisposed()) { + disconnectTerminal(); + return; + } + if (getMsg().length() > 0) { + showErrorMessage(getMsg()); + disconnectTerminal(); + return; + } + if (getCtlText().isFocusControl()) { + if (getState() == TerminalState.CONNECTED) + fFocusListener.captureKeyEvents(true); + } + fPollingTextCanvasModel.startPolling(); + startReaderJob(); + } + + private synchronized void startReaderJob() { + if (fJob == null) { + fJob = new Job("Terminal data reader") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + IStatus status = Status.OK_STATUS; + try { + while (true) { + while (fInputStream.available() == 0 && !monitor.isCanceled()) { + try { + fInputStream.waitForAvailable(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + if (monitor.isCanceled()) { + //Do not disconnect terminal here because another reader job may already be running + status = Status.CANCEL_STATUS; + break; + } + try { + // TODO: should block when no text is available! + fTerminalText.processText(); + } catch (Exception e) { + disconnectTerminal(); + status = new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, e.getLocalizedMessage(), + e); + break; + } + } + } finally { + // clean the job: start a new one when the connection gets restarted + // Bug 208145: make sure we do not clean an other job that's already started (since it would become a Zombie) + synchronized (VT100TerminalControl.this) { + if (fJob == this) { + fJob = null; + } + } + } + return status; + } + + }; + fJob.setSystem(true); + fJob.schedule(); + } + } + + private void showErrorMessage(String message) { + String strTitle = TerminalMessages.TerminalError; + // [168197] Replace JFace MessagDialog by SWT MessageBox + //MessageDialog.openError( getShell(), strTitle, message); + MessageBox mb = new MessageBox(getShell(), SWT.ICON_ERROR | SWT.OK); + mb.setText(strTitle); + mb.setMessage(message); + mb.open(); + } + + protected void sendString(String string) { + Logger.log(string); + try { + // Send the string after converting it to an array of bytes using the + // platform's default character encoding. + // + // TODO: Find a way to force this to use the ISO Latin-1 encoding. + // TODO: handle Encoding Errors in a better way + + getOutputStream().write(string.getBytes(fCharset)); + getOutputStream().flush(); + } catch (SocketException socketException) { + displayTextInTerminal(socketException.getMessage()); + + String strMsg = TerminalMessages.SocketError + "!\n" + socketException.getMessage(); //$NON-NLS-1$ + showErrorMessage(strMsg); + + Logger.logException(socketException); + + disconnectTerminal(); + } catch (IOException ioException) { + showErrorMessage(TerminalMessages.IOError + "!\n" + ioException.getMessage());//$NON-NLS-1$ + + Logger.logException(ioException); + + disconnectTerminal(); + } + } + + @Override + public Shell getShell() { + return getCtlText().getShell(); + } + + protected void sendChar(char chKey, boolean altKeyPressed) { + try { + int byteToSend = chKey; + OutputStream os = getOutputStream(); + if (os == null) { + // Bug 207785: NPE when trying to send char while no longer connected + Logger.log("NOT sending '" + byteToSend + "' because no longer connected"); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + if (altKeyPressed) { + // When the ALT key is pressed at the same time that a character is + // typed, translate it into an ESCAPE followed by the character. The + // alternative in this case is to set the high bit of the character + // being transmitted, but that will cause input such as ALT-f to be + // seen as the ISO Latin-1 character '�', which can be confusing to + // European users running Emacs, for whom Alt-f should move forward a + // word instead of inserting the '�' character. + // + // TODO: Make the ESCAPE-vs-highbit behavior user configurable. + + byte[] bytesToSend = String.valueOf(chKey).getBytes(fCharset); + StringBuilder b = new StringBuilder("sending ESC"); //$NON-NLS-1$ + for (int i = 0; i < bytesToSend.length; i++) { + if (i != 0) + b.append(" +"); //$NON-NLS-1$ + b.append(" '" + bytesToSend[i] + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + } + Logger.log(b.toString()); + os.write('\u001b'); + os.write(bytesToSend); + } else { + byte[] bytesToSend = String.valueOf(chKey).getBytes(fCharset); + StringBuilder b = new StringBuilder("sending"); //$NON-NLS-1$ + for (int i = 0; i < bytesToSend.length; i++) { + if (i != 0) + b.append(" +"); //$NON-NLS-1$ + b.append(" '" + bytesToSend[i] + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + } + Logger.log(b.toString()); + os.write(bytesToSend); + } + os.flush(); + } + } catch (SocketException socketException) { + Logger.logException(socketException); + + displayTextInTerminal(socketException.getMessage()); + + String strMsg = TerminalMessages.SocketError + "!\n" + socketException.getMessage(); //$NON-NLS-1$ + + showErrorMessage(strMsg); + Logger.logException(socketException); + + disconnectTerminal(); + } catch (IOException ioException) { + Logger.logException(ioException); + + displayTextInTerminal(ioException.getMessage()); + + String strMsg = TerminalMessages.IOError + "!\n" + ioException.getMessage(); //$NON-NLS-1$ + + showErrorMessage(strMsg); + Logger.logException(ioException); + + disconnectTerminal(); + } + } + + @Override + public void setupTerminal(Composite parent) { + Assert.isNotNull(parent); + boolean wasDisposed = true; + TerminalState oldState = fState; + fState = TerminalState.CLOSED; + if (fClipboard != null && !fClipboard.isDisposed()) { + // terminal was not disposed (DnD) + wasDisposed = false; + fClipboard.dispose(); + fPollingTextCanvasModel.stopPolling(); + } + if (fWndParent != null && !fWndParent.isDisposed()) { + // terminal widget gets a new parent (DnD) + fWndParent.dispose(); + } + setupControls(parent); + setCommandInputField(fCommandInputField); + setupListeners(); + if (fPreferenceStore != null && wasDisposed) { + updatePreferences(null); + fPreferenceStore.addPropertyChangeListener(fPreferenceListener); + } + JFaceResources.getFontRegistry().addListener(fFontListener); + setupHelp(fWndParent, TerminalPlugin.HELP_VIEW); + + if (!wasDisposed) { + fState = oldState; + } + } + + private void updatePreferences(PropertyChangeEvent unused) { + int bufferLineLimit = fPreferenceStore.getInt(ITerminalConstants.PREF_BUFFERLINES); + boolean invert = fPreferenceStore.getBoolean(ITerminalConstants.PREF_INVERT_COLORS); + setBufferLineLimit(bufferLineLimit); + setInvertedColors(invert); + onTerminalColorsChanged(); + onTerminalFontChanged(); + } + + private void onTerminalColorsChanged() { + Map map = new EnumMap<>(TerminalColor.class); + TerminalColor[] values = TerminalColor.values(); + for (TerminalColor terminalColor : values) { + RGB rgb = null; + if (fPreferenceStore != null) { + try { + rgb = StringConverter.asRGB( + fPreferenceStore.getString(ITerminalConstants.getPrefForTerminalColor(terminalColor))); + } catch (DataFormatException dfe) { + // bad color, use default preset value instead + } + } + + if (rgb == null) { + rgb = TerminalColorPresets.INSTANCE.getDefaultPreset().getRGB(terminalColor); + } + map.put(terminalColor, rgb); + } + fCtlText.updateColors(map); + } + + private String getFontDefinition() { + String definition; + if (fPreferenceStore != null) { + definition = fPreferenceStore.getString(ITerminalConstants.PREF_FONT_DEFINITION); + } else { + definition = ITerminalConstants.DEFAULT_FONT_DEFINITION; + } + if (definition == null || definition.isEmpty()) { + definition = "org.eclipse.jface.textfont"; //$NON-NLS-1$ + } + return definition; + } + + private void onTerminalFontChanged() { + setFont(getFontDefinition()); + } + + private void updateFont(PropertyChangeEvent event) { + if (event.getProperty().equals(getFontDefinition())) { + onTerminalFontChanged(); + } + } + + @Override + public void setFont(String fontName) { + Font font = JFaceResources.getFont(fontName); + getCtlText().setFont(font); + if (fCommandInputField != null) { + fCommandInputField.setFont(font); + } + // Tell the TerminalControl singleton that the font has changed. + fCtlText.updateFont(fontName); + getTerminalText().fontChanged(); + } + + @Override + @Deprecated + public void setFont(Font font) { + getCtlText().setFont(font); + if (fCommandInputField != null) { + fCommandInputField.setFont(font); + } + + // Tell the TerminalControl singleton that the font has changed. + fCtlText.onFontChange(); + getTerminalText().fontChanged(); + } + + @Override + public Font getFont() { + return getCtlText().getFont(); + } + + @Override + public Control getControl() { + return fCtlText; + } + + @Override + public Control getRootControl() { + return fWndParent; + } + + protected void setupControls(Composite parent) { + fWndParent = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginWidth = 0; + layout.marginHeight = 0; + layout.verticalSpacing = 0; + fWndParent.setLayout(layout); + + ITerminalTextDataSnapshot snapshot = fTerminalModel.makeSnapshot(); + // TODO how to get the initial size correctly! + snapshot.updateSnapshot(false); + fPollingTextCanvasModel = new PollingTextCanvasModel(snapshot); + fCtlText = new TextCanvas(fWndParent, fPollingTextCanvasModel, SWT.NONE, + new TextLineRenderer(fCtlText, fPollingTextCanvasModel)); + + fCtlText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + fCtlText.addResizeHandler((lines, columns) -> fTerminalText.setDimensions(lines, columns)); + fCtlText.addMouseListener(new MouseAdapter() { + @Override + public void mouseUp(MouseEvent e) { + // update selection used by middle mouse button paste + if (e.button == 1 && getSelection().length() > 0) { + copy(DND.SELECTION_CLIPBOARD); + } + } + }); + + fDisplay = getCtlText().getDisplay(); + fClipboard = new Clipboard(fDisplay); + } + + protected void setupListeners() { + fKeyHandler = new TerminalKeyHandler(); + fFocusListener = new TerminalFocusListener(); + + getCtlText().addKeyListener(fKeyHandler); + getCtlText().addFocusListener(fFocusListener); + + } + + /** + * Setup all the help contexts for the controls. + */ + protected void setupHelp(Composite parent, String id) { + Control[] children = parent.getChildren(); + + for (int nIndex = 0; nIndex < children.length; nIndex++) { + if (children[nIndex] instanceof Composite) { + setupHelp((Composite) children[nIndex], id); + } + } + + PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, id); + } + + @Override + public void displayTextInTerminal(String text) { + writeToTerminal("\r\n" + text + "\r\n"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private void writeToTerminal(String text) { + try { + getRemoteToTerminalOutputStream().write(text.getBytes(fCharset)); + } catch (IOException e) { + // should never happen! + e.printStackTrace(); + } + } + + @Override + public OutputStream getRemoteToTerminalOutputStream() { + if (Logger.isLogEnabled()) { + return new LoggingOutputStream(fInputStream.getOutputStream()); + } else { + return fInputStream.getOutputStream(); + } + } + + protected boolean isLogCharEnabled() { + return TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_CHAR); + } + + @Override + public OutputStream getOutputStream() { + if (getTerminalConnector() != null) + return getTerminalConnector().getTerminalToRemoteStream(); + return null; + } + + @Override + public void setMsg(String msg) { + fMsg = msg; + } + + public String getMsg() { + return fMsg; + } + + protected TextCanvas getCtlText() { + return fCtlText; + } + + public VT100Emulator getTerminalText() { + return fTerminalText; + } + + protected class TerminalFocusListener implements FocusListener { + private IContextActivation terminalContextActivation = null; + private IContextActivation editContextActivation = null; + + protected TerminalFocusListener() { + super(); + } + + @Override + public void focusGained(FocusEvent event) { + // Disable all keyboard accelerators (e.g., Control-B) so the Terminal view + // can see every keystroke. Without this, Emacs, vi, and Bash are unusable + // in the Terminal view. + if (getState() == TerminalState.CONNECTED) + captureKeyEvents(true); + + IContextService contextService = PlatformUI.getWorkbench().getAdapter(IContextService.class); + editContextActivation = contextService.activateContext("org.eclipse.tm.terminal.EditContext"); //$NON-NLS-1$ + } + + @Override + public void focusLost(FocusEvent event) { + // Enable all keybindings. + captureKeyEvents(false); + + // Restore the command context to its previous value. + + IContextService contextService = PlatformUI.getWorkbench().getAdapter(IContextService.class); + contextService.deactivateContext(editContextActivation); + } + + protected void captureKeyEvents(boolean capture) { + IBindingService bindingService = PlatformUI.getWorkbench().getAdapter(IBindingService.class); + IContextService contextService = PlatformUI.getWorkbench().getAdapter(IContextService.class); + + boolean enableKeyFilter = !capture; + if (bindingService.isKeyFilterEnabled() != enableKeyFilter) + bindingService.setKeyFilterEnabled(enableKeyFilter); + + if (capture && terminalContextActivation == null) { + // The above code fails to cause Eclipse to disable menu-activation + // accelerators (e.g., Alt-F for the File menu), so we set the command + // context to be the Terminal view's command context. This enables us to + // override menu-activation accelerators with no-op commands in our + // plugin.xml file, which enables the Terminal view to see absolutely _all_ + // key-presses. + terminalContextActivation = contextService.activateContext("org.eclipse.tm.terminal.TerminalContext"); //$NON-NLS-1$ + + } else if (!capture && terminalContextActivation != null) { + contextService.deactivateContext(terminalContextActivation); + terminalContextActivation = null; + } + } + } + + protected class TerminalKeyHandler extends KeyAdapter { + @Override + public void keyPressed(KeyEvent event) { + //TODO next 2 lines are probably obsolete now + if (getState() == TerminalState.CONNECTING) + return; + + //TODO we should no longer handle copy & paste specially. + //Instead, we should have Ctrl+Shift always go to local since there is no escape sequence for this. + //On Mac, Command+Anything already goes always to local. + //Note that this decision means that Command will NOT be Meta in Emacs on a Remote. + int accelerator = SWTKeySupport.convertEventToUnmodifiedAccelerator(event); + if (editActionAccelerators.isCopyAction(accelerator)) { + copy(); + return; + } + if (editActionAccelerators.isPasteAction(accelerator)) { + paste(); + return; + } + + // We set the event.doit to false to prevent any further processing of this + // key event. The only reason this is here is because I was seeing the F10 + // key both send an escape sequence (due to this method) and switch focus + // to the Workbench File menu (forcing the user to click in the Terminal + // view again to continue entering text). This fixes that. + + event.doit = false; + + char character = event.character; + int modifierKeys = event.stateMask & SWT.MODIFIER_MASK; + boolean ctrlKeyPressed = (event.stateMask & SWT.CTRL) != 0; + boolean onlyCtrlKeyPressed = modifierKeys == SWT.CTRL; + boolean macCmdKeyPressed = (event.stateMask & SWT.COMMAND) != 0; + + // To fix SPR 110341, we consider the Alt key to be pressed only when the + // Control key is _not_ also pressed. This works around a bug in SWT where, + // on European keyboards, the AltGr key being pressed appears to us as Control + // + Alt being pressed simultaneously. + boolean altKeyPressed = (event.stateMask & SWT.ALT) != 0 && !ctrlKeyPressed; + + //if (!isConnected()) { + if (fState == TerminalState.CLOSED) { + // Pressing ENTER while not connected causes us to connect. + if (character == '\r' && isConnectOnEnterIfClosed()) { + connectTerminal(); + return; + } + + // Ignore all other keyboard input when not connected. + // Allow other key handlers (such as Ctrl+F1) do their work + event.doit = true; + return; + } + + // Manage the Del key + if (event.keyCode == 0x000007f) { + sendString("\u001b[3~"); //$NON-NLS-1$ + return; + } + + // TODO Linux tty is usually expecting a DEL (^?) character + // but this causes issues with some telnet servers and + // serial connections. Workaround: stty erase ^H + //if (event.keyCode == SWT.BS) { + // sendChar(SWT.DEL, altKeyPressed); + // return; + //} + + // If the event character is NUL ('\u0000'), then a special key was pressed + // (e.g., PageUp, PageDown, an arrow key, a function key, Shift, Alt, + // Control, etc.). The one exception is when the user presses Control-@, + // which sends a NUL character, in which case we must send the NUL to the + // remote endpoint. This is necessary so that Emacs will work correctly, + // because Control-@ (i.e., NUL) invokes Emacs' set-mark-command when Emacs + // is running on a terminal. When the user presses Control-@, the keyCode + // is 50. + // On a Mac, the Cmd key is always used for local commands. + + if (macCmdKeyPressed || (character == '\u0000' && event.keyCode != 50)) { + // A special key was pressed. Figure out which one it was and send the + // appropriate ANSI escape sequence. + // + // IMPORTANT: Control will not enter this method for these special keys + // unless certain tags are present in the plugin.xml file + // for the Terminal view. Do not delete those tags. + + String escSeq = null; + boolean anyModifierPressed = modifierKeys != 0; + boolean onlyMacCmdKeyPressed = modifierKeys == SWT.COMMAND; + + switch (event.keyCode) { + case 0x1000001: // Up arrow. + if (!anyModifierPressed) + escSeq = fApplicationCursorKeys ? "\u001bOA" : "\u001b[A"; //$NON-NLS-1$ //$NON-NLS-2$ + break; + + case 0x1000002: // Down arrow. + if (!anyModifierPressed) + escSeq = fApplicationCursorKeys ? "\u001bOB" : "\u001b[B"; //$NON-NLS-1$ //$NON-NLS-2$ + break; + + case 0x1000003: // Left arrow. + if (onlyCtrlKeyPressed) { + escSeq = "\u001b[1;5D"; //$NON-NLS-1$ + } else if (!anyModifierPressed) { + escSeq = fApplicationCursorKeys ? "\u001bOD" : "\u001b[D"; //$NON-NLS-1$ //$NON-NLS-2$ + } else if (onlyMacCmdKeyPressed) { + // Cmd-Left is "Home" on the Mac + escSeq = fApplicationCursorKeys ? "\u001bOH" : "\u001b[H"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + + case 0x1000004: // Right arrow. + if (onlyCtrlKeyPressed) { + escSeq = "\u001b[1;5C"; //$NON-NLS-1$ + } else if (!anyModifierPressed) { + escSeq = fApplicationCursorKeys ? "\u001bOC" : "\u001b[C"; //$NON-NLS-1$ //$NON-NLS-2$ + } else if (onlyMacCmdKeyPressed) { + // Cmd-Right is "End" on the Mac + escSeq = fApplicationCursorKeys ? "\u001bOF" : "\u001b[F"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + + case 0x1000005: // PgUp key. + if (!anyModifierPressed) + escSeq = "\u001b[5~"; //$NON-NLS-1$ + break; + + case 0x1000006: // PgDn key. + if (!anyModifierPressed) + escSeq = "\u001b[6~"; //$NON-NLS-1$ + break; + + case 0x1000007: // Home key. + if (!anyModifierPressed) + escSeq = fApplicationCursorKeys ? "\u001bOH" : "\u001b[H"; //$NON-NLS-1$ //$NON-NLS-2$ + break; + + case 0x1000008: // End key. + if (!anyModifierPressed) + escSeq = fApplicationCursorKeys ? "\u001bOF" : "\u001b[F"; //$NON-NLS-1$ //$NON-NLS-2$ + break; + + case 0x1000009: // Insert. + if (!anyModifierPressed) + escSeq = "\u001b[2~"; //$NON-NLS-1$ + break; + + case 0x100000a: // F1 key. + if (!anyModifierPressed) + escSeq = "\u001bOP"; //$NON-NLS-1$ + break; + + case 0x100000b: // F2 key. + if (!anyModifierPressed) + escSeq = "\u001bOQ"; //$NON-NLS-1$ + break; + + case 0x100000c: // F3 key. + if (!anyModifierPressed) + escSeq = "\u001bOR"; //$NON-NLS-1$ + break; + + case 0x100000d: // F4 key. + if (!anyModifierPressed) + escSeq = "\u001bOS"; //$NON-NLS-1$ + break; + + case 0x100000e: // F5 key. + if (!anyModifierPressed) + escSeq = "\u001b[15~"; //$NON-NLS-1$ + break; + + case 0x100000f: // F6 key. + if (!anyModifierPressed) + escSeq = "\u001b[17~"; //$NON-NLS-1$ + break; + + case 0x1000010: // F7 key. + if (!anyModifierPressed) + escSeq = "\u001b[18~"; //$NON-NLS-1$ + break; + + case 0x1000011: // F8 key. + if (!anyModifierPressed) + escSeq = "\u001b[19~"; //$NON-NLS-1$ + break; + + case 0x1000012: // F9 key. + if (!anyModifierPressed) + escSeq = "\u001b[20~"; //$NON-NLS-1$ + break; + + case 0x1000013: // F10 key. + if (!anyModifierPressed) + escSeq = "\u001b[21~"; //$NON-NLS-1$ + break; + + case 0x1000014: // F11 key. + if (!anyModifierPressed) + escSeq = "\u001b[23~"; //$NON-NLS-1$ + break; + + case 0x1000015: // F12 key. + if (!anyModifierPressed) + escSeq = "\u001b[24~"; //$NON-NLS-1$ + break; + + default: + // Ignore other special keys. Control flows through this case when + // the user presses SHIFT, CONTROL, ALT, and any other key not + // handled by the above cases. + break; + } + + if (escSeq == null) { + // Any unmapped key should be handled locally by Eclipse + event.doit = true; + processKeyBinding(event, accelerator); + } else + sendString(escSeq); + + // It's ok to return here, because we never locally echo special keys. + + return; + } + + Logger.log("stateMask = " + event.stateMask); //$NON-NLS-1$ + + if (onlyCtrlKeyPressed) { + switch (character) { + case ' ': + // Send a NUL character -- many terminal emulators send NUL when + // Control-Space is pressed. This is used to set the mark in Emacs. + character = '\u0000'; + break; + case '/': + // Ctrl+/ is undo in emacs + character = '\u001f'; + break; + } + } + + // see javadoc on convertBackspace for details + if (convertBackspace && !ctrlKeyPressed && character == '\b') { + character = 0x7f; + } + + //TODO: At this point, Ctrl+M sends the same as Ctrl+Shift+M . + //This is undesired. Fixing this here might make the special Ctrl+Shift+C + //handling unnecessary further up. + sendChar(character, altKeyPressed); + + // Now decide if we should locally echo the character we just sent. We do + // _not_ locally echo the character if any of these conditions are true: + // + // o This is a serial connection. + // + // o This is a TCP connection (i.e., m_telnetConnection is not null) and + // the remote endpoint is not a TELNET server. + // + // o The ALT (or META) key is pressed. + // + // o The character is any of the first 32 ISO Latin-1 characters except + // Control-I or Control-M. + // + // o The character is the DELETE character. + + if (getTerminalConnector() == null || getTerminalConnector().isLocalEcho() == false || altKeyPressed + || (character >= '\u0001' && character < '\t') || (character > '\t' && character < '\r') + || (character > '\r' && character <= '\u001f') || character == '\u007f') { + // No local echoing. + return; + } + + // Locally echo the character. + + StringBuffer charBuffer = new StringBuffer(); + charBuffer.append(character); + + // If the character is a carriage return, we locally echo it as a CR + LF + // combination. + + if (character == '\r') + charBuffer.append('\n'); + + writeToTerminal(charBuffer.toString()); + } + + /* + * Process given event as Eclipse key binding. + */ + private void processKeyBinding(KeyEvent event, int accelerator) { + IBindingService bindingService = PlatformUI.getWorkbench().getAdapter(IBindingService.class); + KeyStroke keyStroke = SWTKeySupport.convertAcceleratorToKeyStroke(accelerator); + Binding binding = bindingService.getPerfectMatch(KeySequence.getInstance(keyStroke)); + if (binding != null) { + ParameterizedCommand cmd = binding.getParameterizedCommand(); + if (cmd != null) { + IHandlerService handlerService = PlatformUI.getWorkbench().getAdapter(IHandlerService.class); + Event cmdEvent = new Event(); + cmdEvent.type = SWT.KeyDown; + cmdEvent.display = event.display; + cmdEvent.widget = event.widget; + cmdEvent.character = event.character; + cmdEvent.keyCode = event.keyCode; + ////Bug - KeyEvent.keyLocation was introduced in Eclipse 3.6 + ////Use reflection for now to remain backward compatible down to Eclipse 3.4 + //cmdEvent.keyLocation = event.keyLocation; + try { + Field f1 = event.getClass().getField("keyLocation"); //$NON-NLS-1$ + Field f2 = cmdEvent.getClass().getField("keyLocation"); //$NON-NLS-1$ + f2.set(cmdEvent, f1.get(event)); + } catch (NoSuchFieldException nsfe) { + /* ignore, this is Eclipse 3.5 or earlier */ + } catch (Throwable t) { + t.printStackTrace(); + } + cmdEvent.stateMask = event.stateMask; + event.doit = false; + try { + handlerService.executeCommand(cmd, cmdEvent); + } catch (ExecutionException e) { + TerminalPlugin.getDefault().getLog() + .log(new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, e.getLocalizedMessage(), e)); + } catch (Exception e) { + // ignore other exceptions from cmd execution + } + } + } + } + + } + + @Override + public void setTerminalTitle(String title) { + setTerminalTitle(title, TerminalTitleRequestor.OTHER); + } + + @Override + public void setTerminalTitle(String title, TerminalTitleRequestor requestor) { + if (fTerminalListener instanceof ITerminalListener3 listener3) { + listener3.setTerminalTitle(title, requestor); + } else { + fTerminalListener.setTerminalTitle(title); + } + + } + + @Override + public TerminalState getState() { + return fState; + } + + @Override + public void setState(TerminalState state) { + fState = state; + fTerminalListener.setState(state); + // enable the (blinking) cursor if the terminal is connected + runAsyncInDisplayThread(() -> { + if (fCtlText != null && !fCtlText.isDisposed()) { + if (isConnected()) { + fCtlText.setCursorEnabled(true); + } else { + fCtlText.setCursorEnabled(false); + // Stop capturing all key events + fFocusListener.captureKeyEvents(false); + } + } + }); + } + + /** + * @param runnable run in display thread + */ + private void runAsyncInDisplayThread(Runnable runnable) { + if (Display.findDisplay(Thread.currentThread()) != null) + runnable.run(); + else if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench().getDisplay() != null + && !PlatformUI.getWorkbench().getDisplay().isDisposed()) + PlatformUI.getWorkbench().getDisplay().asyncExec(runnable); + // else should not happen and we ignore it... + } + + @Override + public String getSettingsSummary() { + if (getTerminalConnector() != null) + return getTerminalConnector().getSettingsSummary(); + return ""; //$NON-NLS-1$ + } + + @Override + public void setConnector(ITerminalConnector connector) { + fConnector = connector; + + } + + @Override + public ICommandInputField getCommandInputField() { + return fCommandInputField; + } + + @Override + public void setCommandInputField(ICommandInputField inputField) { + if (fCommandInputField != null) + fCommandInputField.dispose(); + fCommandInputField = inputField; + if (fCommandInputField != null) + fCommandInputField.createControl(fWndParent, this); + if (fWndParent.isVisible()) + fWndParent.layout(true); + } + + @Override + public int getBufferLineLimit() { + return fTerminalModel.getMaxHeight(); + } + + @Override + public void setBufferLineLimit(int bufferLineLimit) { + if (bufferLineLimit <= 0) + return; + synchronized (fTerminalModel) { + if (fTerminalModel.getHeight() > bufferLineLimit) + fTerminalModel.setDimensions(bufferLineLimit, fTerminalModel.getWidth()); + fTerminalModel.setMaxHeight(bufferLineLimit); + } + } + + @Override + public boolean isScrollLock() { + return fCtlText.isScrollLock(); + } + + @Override + public void setScrollLock(boolean on) { + fCtlText.setScrollLock(on); + } + + @Override + public void setInvertedColors(boolean invert) { + fCtlText.setInvertedColors(invert); + } + + @Override + public boolean isInvertedColors() { + return fCtlText.isInvertedColors(); + } + + @Override + public final void setConnectOnEnterIfClosed(boolean on) { + connectOnEnterIfClosed = on; + } + + @Override + public final boolean isConnectOnEnterIfClosed() { + return connectOnEnterIfClosed; + } + + @Override + public void setVT100LineWrapping(boolean enable) { + getTerminalText().setVT100LineWrapping(enable); + } + + @Override + public boolean isVT100LineWrapping() { + return getTerminalText().isVT100LineWrapping(); + } + + @Override + public void enableApplicationCursorKeys(boolean enable) { + fApplicationCursorKeys = enable; + } + + @Override + public void addMouseListener(ITerminalMouseListener listener) { + getCtlText().addTerminalMouseListener(listener); + } + + @Override + public void removeMouseListener(ITerminalMouseListener listener) { + getCtlText().removeTerminalMouseListener(listener); + } + + @Override + public String getHoverSelection() { + return fCtlText.getHoverSelection(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/ISnapshotChanges.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/ISnapshotChanges.java new file mode 100644 index 00000000000..9f51f2150ac --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/ISnapshotChanges.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import org.eclipse.tm.terminal.model.ITerminalTextData; + +public interface ISnapshotChanges { + + /** + * @param line might bigger than the number of lines.... + */ + void markLineChanged(int line); + + /** + * Marks all lines in the range as changed + * @param line >=0 + * @param n might be out of range + */ + void markLinesChanged(int line, int n); + + /** + * Marks all lines within the scrolling region + * changed and resets the scrolling information + */ + void convertScrollingIntoChanges(); + + /** + * @return true if something has changed + */ + boolean hasChanged(); + + /** + * @param startLine + * @param size + * @param shift + */ + void scroll(int startLine, int size, int shift); + + /** + * Mark all lines changed + * @param height if no window is set this is the number of + * lines that are marked as changed + */ + void setAllChanged(int height); + + int getFirstChangedLine(); + + int getLastChangedLine(); + + int getScrollWindowStartLine(); + + int getScrollWindowSize(); + + int getScrollWindowShift(); + + boolean hasLineChanged(int line); + + void markDimensionsChanged(); + + boolean hasDimensionsChanged(); + + void markCursorChanged(); + + /** + * @return true if the terminal data has changed + */ + boolean hasTerminalChanged(); + + /** + * mark the terminal as changed + */ + void setTerminalChanged(); + + void copyChangedLines(ITerminalTextData dest, ITerminalTextData source); + + /** + * @param startLine -1 means follow the end of the data + * @param size number of lines to follow + */ + void setInterestWindow(int startLine, int size); + + int getInterestWindowStartLine(); + + int getInterestWindowSize(); + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SnapshotChanges.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SnapshotChanges.java new file mode 100644 index 00000000000..c1b4638c5dc --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SnapshotChanges.java @@ -0,0 +1,421 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import org.eclipse.tm.terminal.model.ITerminalTextData; + +/** + * Collects the changes of the {@link ITerminalTextData} + * + * @noextend This class is not intended to be subclassed by clients. + * @noreference This class is not intended to be referenced by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + * This class used to be package-protected. It is public only for access by the Unit Tests. + */ +public class SnapshotChanges implements ISnapshotChanges { + /** + * The first line changed + */ + private int fFirstChangedLine; + /** + * The last line changed + */ + private int fLastChangedLine; + private int fScrollWindowStartLine; + private int fScrollWindowSize; + private int fScrollWindowShift; + /** + * true, if scrolling should not tracked anymore + */ + private boolean fScrollDontTrack; + /** + * The lines that need to be copied + * into the snapshot (lines that have + * not changed don't have to be copied) + */ + private boolean[] fChangedLines; + + private int fInterestWindowSize; + private int fInterestWindowStartLine; + private boolean fDimensionsChanged; + private boolean fTerminalHasChanged; + private boolean fCursorHasChanged; + + public SnapshotChanges(int nLines) { + setChangedLinesLength(nLines); + fFirstChangedLine = Integer.MAX_VALUE; + fLastChangedLine = -1; + } + + public SnapshotChanges(int windowStart, int windowSize) { + setChangedLinesLength(windowStart + windowSize); + fFirstChangedLine = Integer.MAX_VALUE; + fLastChangedLine = -1; + fInterestWindowStartLine = windowStart; + fInterestWindowSize = windowSize; + + } + + /** + * This is used in asserts to throw an {@link RuntimeException}. + * This is useful for tests. + * @return never -- throws an exception + */ + private boolean throwRuntimeException() { + throw new RuntimeException(); + } + + /** + * @param line + * @param size + * @return true if the range overlaps with the interest window + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * It used to be package protected, and it is public only for Unit Tests. + */ + public boolean isInInterestWindow(int line, int size) { + if (fInterestWindowSize <= 0) + return true; + if (line + size <= fInterestWindowStartLine || line >= fInterestWindowStartLine + fInterestWindowSize) + return false; + return true; + } + + /** + * @param line + * @return true if the line is within the interest window + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * It used to be package protected, and it is public only for Unit Tests. + */ + public boolean isInInterestWindow(int line) { + if (fInterestWindowSize <= 0) + return true; + if (line < fInterestWindowStartLine || line >= fInterestWindowStartLine + fInterestWindowSize) + return false; + return true; + } + + /** + * @param line + * @return the line within the window + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * It used to be package protected, and it is public only for Unit Tests. + */ + public int fitLineToWindow(int line) { + if (fInterestWindowSize <= 0) + return line; + if (line < fInterestWindowStartLine) + return fInterestWindowStartLine; + return line; + } + + /** + * The result is only defined if {@link #isInInterestWindow(int, int)} returns true! + * @param line the line before {@link #fitLineToWindow(int)} has been called! + * @param size + * @return the adjusted size. + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * It used to be package protected, and it is public only for Unit Tests. + * + *

            Note:

            {@link #fitLineToWindow(int)} has to be called on the line to + * move the window correctly! + */ + public int fitSizeToWindow(int line, int size) { + if (fInterestWindowSize <= 0) + return size; + if (line < fInterestWindowStartLine) { + size -= fInterestWindowStartLine - line; + line = fInterestWindowStartLine; + } + if (line + size > fInterestWindowStartLine + fInterestWindowSize) + size = fInterestWindowStartLine + fInterestWindowSize - line; + return size; + } + + @Override + public void markLineChanged(int line) { + if (!isInInterestWindow(line)) + return; + line = fitLineToWindow(line); + if (line < fFirstChangedLine) + fFirstChangedLine = line; + if (line > fLastChangedLine) + fLastChangedLine = line; + // in case the terminal got resized we expand + // don't remember the changed line because + // there is nothing to copy + if (line < getChangedLineLength()) { + setChangedLine(line, true); + } + } + + @Override + public void markLinesChanged(int line, int n) { + if (n <= 0 || !isInInterestWindow(line, n)) + return; + // do not exceed the bounds of fChangedLines + // the terminal might have been resized and + // we can only keep changes for the size of the + // previous terminal + n = fitSizeToWindow(line, n); + line = fitLineToWindow(line); + int m = Math.min(line + n, getChangedLineLength()); + for (int i = line; i < m; i++) { + setChangedLine(i, true); + } + // this sets fFirstChangedLine as well + markLineChanged(line); + // this sets fLastChangedLine as well + markLineChanged(line + n - 1); + } + + @Override + public void markCursorChanged() { + fCursorHasChanged = true; + } + + @Override + public void convertScrollingIntoChanges() { + markLinesChanged(fScrollWindowStartLine, fScrollWindowSize); + fScrollWindowStartLine = 0; + fScrollWindowSize = 0; + fScrollWindowShift = 0; + } + + @Override + public boolean hasChanged() { + if (fFirstChangedLine != Integer.MAX_VALUE || fLastChangedLine > 0 || fScrollWindowShift != 0 + || fDimensionsChanged || fCursorHasChanged) + return true; + return false; + } + + @Override + public void markDimensionsChanged() { + fDimensionsChanged = true; + } + + @Override + public boolean hasDimensionsChanged() { + return fDimensionsChanged; + } + + @Override + public boolean hasTerminalChanged() { + return fTerminalHasChanged; + } + + @Override + public void setTerminalChanged() { + fTerminalHasChanged = true; + } + + @Override + public void scroll(int startLine, int size, int shift) { + size = fitSizeToWindow(startLine, size); + startLine = fitLineToWindow(startLine); + // let's track only negative shifts + if (fScrollDontTrack) { + // we are in a state where we cannot track scrolling + // so let's simply mark the scrolled lines as changed + markLinesChanged(startLine, size); + } else if (shift >= 0) { + // we cannot handle positive scroll + // forget about clever caching of scroll events + doNotTrackScrollingAnymore(); + // mark all lines inside the scroll region as changed + markLinesChanged(startLine, size); + } else { + // we have already scrolled + if (fScrollWindowShift < 0) { + // we have already scrolled + if (fScrollWindowStartLine == startLine && fScrollWindowSize == size) { + // we are scrolling the same region again? + fScrollWindowShift += shift; + scrollChangesLinesWithNegativeShift(startLine, size, shift); + } else { + // mark all lines in the old scroll region as changed + doNotTrackScrollingAnymore(); + // mark all lines changed, because + markLinesChanged(startLine, size); + } + } else { + // first scroll in this change -- we just notify it + fScrollWindowStartLine = startLine; + fScrollWindowSize = size; + fScrollWindowShift = shift; + scrollChangesLinesWithNegativeShift(startLine, size, shift); + } + } + } + + /** + * Some incompatible scrolling occurred. We cannot do the + * scroll optimization anymore... + */ + private void doNotTrackScrollingAnymore() { + if (fScrollWindowSize > 0) { + // convert the current scrolling into changes + markLinesChanged(fScrollWindowStartLine, fScrollWindowSize); + fScrollWindowStartLine = 0; + fScrollWindowSize = 0; + fScrollWindowShift = 0; + } + // don't be clever on scrolling anymore + fScrollDontTrack = true; + } + + /** + * Scrolls the changed lines data + * + * @param line + * @param n + * @param shift must be negative! + */ + private void scrollChangesLinesWithNegativeShift(int line, int n, int shift) { + assert shift < 0 || throwRuntimeException(); + // scroll the region + // don't run out of bounds! + int m = Math.min(line + n + shift, getChangedLineLength() + shift); + for (int i = line; i < m; i++) { + setChangedLine(i, hasLineChanged(i - shift)); + // move the first changed line up. + // We don't have to move the maximum down, + // because with a shift scroll, the max is moved + // my the next loop in this method + if (i < fFirstChangedLine && hasLineChanged(i)) { + fFirstChangedLine = i; + } + } + // mark the "opened" lines as changed + for (int i = Math.max(0, line + n + shift); i < line + n; i++) { + markLineChanged(i); + } + } + + @Override + public void setAllChanged(int height) { + fScrollWindowStartLine = 0; + fScrollWindowSize = 0; + fScrollWindowShift = 0; + fFirstChangedLine = fitLineToWindow(0); + fLastChangedLine = fFirstChangedLine + fitSizeToWindow(0, height) - 1; + // no need to keep an array of changes anymore + setChangedLinesLength(0); + } + + @Override + public int getFirstChangedLine() { + return fFirstChangedLine; + } + + @Override + public int getLastChangedLine() { + return fLastChangedLine; + } + + @Override + public int getScrollWindowStartLine() { + return fScrollWindowStartLine; + } + + @Override + public int getScrollWindowSize() { + return fScrollWindowSize; + } + + @Override + public int getScrollWindowShift() { + return fScrollWindowShift; + } + + @Override + public void copyChangedLines(ITerminalTextData dest, ITerminalTextData source) { + int n = Math.min(fLastChangedLine + 1, source.getHeight()); + for (int i = fFirstChangedLine; i < n; i++) { + if (hasLineChanged(i)) + dest.copyLine(source, i, i); + } + } + + @Override + public int getInterestWindowSize() { + return fInterestWindowSize; + } + + @Override + public int getInterestWindowStartLine() { + return fInterestWindowStartLine; + } + + @Override + public void setInterestWindow(int startLine, int size) { + int oldStartLine = fInterestWindowStartLine; + int oldSize = fInterestWindowSize; + fInterestWindowStartLine = startLine; + fInterestWindowSize = size; + if (oldSize > 0) { + int shift = oldStartLine - startLine; + if (shift == 0) { + if (size > oldSize) { + // add lines to the end + markLinesChanged(oldStartLine + oldSize, size - oldSize); + } + // else no lines within the window have changed + + } else if (Math.abs(shift) < size) { + if (shift < 0) { + // we can scroll + scroll(startLine, oldSize, shift); + // mark the lines at the end as new + for (int i = oldStartLine + oldSize; i < startLine + size; i++) { + markLineChanged(i); + } + } else { + // we cannot shift positive -- mark all changed + markLinesChanged(startLine, size); + } + } else { + // no scrolling possible + markLinesChanged(startLine, size); + } + + } + } + + @Override + public boolean hasLineChanged(int line) { + if (line < fChangedLines.length) + return fChangedLines[line]; + // since the height of the terminal could + // have changed but we have tracked only changes + // of the previous terminal height, any line outside + // the the range of the previous height has changed + return isInInterestWindow(line); + } + + int getChangedLineLength() { + return fChangedLines.length; + } + + void setChangedLine(int line, boolean changed) { + fChangedLines[line] = changed; + } + + void setChangedLinesLength(int length) { + fChangedLines = new boolean[length]; + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SynchronizedTerminalTextData.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SynchronizedTerminalTextData.java new file mode 100644 index 00000000000..54f637a78ab --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SynchronizedTerminalTextData.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.LineSegment; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * This is a decorator to make all access to + * ITerminalTextData synchronized + * + */ +public class SynchronizedTerminalTextData implements ITerminalTextData { + final ITerminalTextData fData; + + public SynchronizedTerminalTextData(ITerminalTextData data) { + fData = data; + } + + @Override + synchronized public void addLine() { + fData.addLine(); + } + + @Override + synchronized public void cleanLine(int line) { + fData.cleanLine(line); + } + + @Override + synchronized public void copy(ITerminalTextData source) { + fData.copy(source); + } + + @Override + synchronized public void copyLine(ITerminalTextData source, int sourceLine, int destLine) { + fData.copyLine(source, sourceLine, destLine); + } + + @Override + synchronized public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) { + fData.copyRange(source, sourceStartLine, destStartLine, length); + } + + @Override + synchronized public char getChar(int line, int column) { + return fData.getChar(line, column); + } + + @Override + synchronized public char[] getChars(int line) { + return fData.getChars(line); + } + + @Override + synchronized public int getCursorColumn() { + return fData.getCursorColumn(); + } + + @Override + synchronized public int getCursorLine() { + return fData.getCursorLine(); + } + + @Override + synchronized public int getHeight() { + return fData.getHeight(); + } + + @Override + synchronized public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) { + return fData.getLineSegments(line, startCol, numberOfCols); + } + + @Override + synchronized public int getMaxHeight() { + return fData.getMaxHeight(); + } + + @Override + synchronized public TerminalStyle getStyle(int line, int column) { + return fData.getStyle(line, column); + } + + @Override + synchronized public TerminalStyle[] getStyles(int line) { + return fData.getStyles(line); + } + + @Override + synchronized public int getWidth() { + return fData.getWidth(); + } + + @Override + synchronized public ITerminalTextDataSnapshot makeSnapshot() { + return fData.makeSnapshot(); + } + + @Override + synchronized public void scroll(int startLine, int size, int shift) { + fData.scroll(startLine, size, shift); + } + + @Override + synchronized public void setChar(int line, int column, char c, TerminalStyle style) { + fData.setChar(line, column, c, style); + } + + @Override + synchronized public void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style) { + fData.setChars(line, column, chars, start, len, style); + } + + @Override + synchronized public void setChars(int line, int column, char[] chars, TerminalStyle style) { + fData.setChars(line, column, chars, style); + } + + @Override + synchronized public void setCursorColumn(int column) { + fData.setCursorColumn(column); + } + + @Override + synchronized public void setCursorLine(int line) { + fData.setCursorLine(line); + } + + @Override + synchronized public void setDimensions(int height, int width) { + fData.setDimensions(height, width); + } + + @Override + synchronized public void setMaxHeight(int height) { + fData.setMaxHeight(height); + } + + @Override + synchronized public boolean isWrappedLine(int line) { + return fData.isWrappedLine(line); + } + + @Override + synchronized public void setWrappedLine(int line) { + fData.setWrappedLine(line); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SystemDefaultColors.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SystemDefaultColors.java new file mode 100644 index 00000000000..930a97a1e73 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/SystemDefaultColors.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2020 Kichwa Coders Canada Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import java.util.function.Supplier; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.preferences.ScopedPreferenceStore; +import org.eclipse.ui.themes.ColorUtil; + +/** + * Wrapper class to get standard colors from Eclipse trying to match existing theme where possible + * by using standard editor colors. + * + * This class has an implied and optional dependency on org.eclipse.ui.editors bundle by reading + * that bundles preferences. + */ +public enum SystemDefaultColors implements Supplier { + + /** + * Standard text foreground. Typically black in Light theme. + */ + FOREGROUND("Foreground", "COLOR_LIST_FOREGROUND", new RGB(0, 0, 0)), //$NON-NLS-1$ //$NON-NLS-2$ + + /** + * Standard text background. Typically white in Light theme. + */ + BACKGROUND("Background", "COLOR_LIST_BACKGROUND", new RGB(255, 255, 255)), //$NON-NLS-1$ //$NON-NLS-2$ + + /** + * Selection foreground. Typically white in Light theme. + */ + SELECTION_FOREGROUND("SelectionForeground", "COLOR_LIST_SELECTION_TEXT", //$NON-NLS-1$ //$NON-NLS-2$ + new RGB(255, 255, 255)), + + /** + * Selection background. Typically blue in Light theme. + */ + SELECTION_BACKGROUND("SelectionBackground", "COLOR_LIST_SELECTION", new RGB(74, 144, 9)); //$NON-NLS-1$ //$NON-NLS-2$ + + private static final String EDITOR_SCOPE = "org.eclipse.ui.editors"; //$NON-NLS-1$ + private static final String PREF_PREFIX = "AbstractTextEditor.Color."; //$NON-NLS-1$ + private static final String PREF_SYSTEM_DEFAULT_SUFFIX = ".SystemDefault"; //$NON-NLS-1$ + + /** + * SWT Name of Color + * + * Values from SWT + */ + private String swtColor; + + /** + * Preference name for color. + * + * Values from org.eclipse.ui.texteditor.AbstractTextEditor.... + */ + private String editorColor; + + /** If all else fails, use this standard color */ + private RGB fallbackColor; + + SystemDefaultColors(String editorColor, String swtColor, RGB rgb) { + this.editorColor = editorColor; + this.swtColor = swtColor; + this.fallbackColor = rgb; + } + + /** + * Get the color for this enum value. + * + * @return the RGB color or a non-null color as a fallback. + */ + @Override + public RGB get() { + IPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, EDITOR_SCOPE); + + RGB rgb = null; + String pref = PREF_PREFIX + editorColor; + String prefSystemDefault = pref + PREF_SYSTEM_DEFAULT_SUFFIX; + if (Platform.getPreferencesService() != null) { + if (!store.getBoolean(prefSystemDefault)) { + if (store.contains(pref)) { + if (store.isDefault(pref)) + rgb = PreferenceConverter.getDefaultColor(store, pref); + else { + rgb = PreferenceConverter.getColor(store, pref); + } + } + } + } + + if (rgb == null) { + rgb = ColorUtil.getColorValue(swtColor); + } + + if (rgb == null) { + rgb = fallbackColor; + } + + return rgb; + } +} \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextData.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextData.java new file mode 100644 index 00000000000..675bfd66de2 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextData.java @@ -0,0 +1,333 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - [168197] Fix Terminal for CDC-1.1/Foundation-1.1 + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.LineSegment; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * This class is thread safe. + * + */ +public class TerminalTextData implements ITerminalTextData { + final ITerminalTextData fData; + /** + * A list of active snapshots + */ + public TerminalTextDataSnapshot[] fSnapshots = new TerminalTextDataSnapshot[0]; + private int fCursorColumn; + private int fCursorLine; + + /** + * Debug helper method -- use as "New Detail Formatter.." in the + * debugger variables view: + *
            TerminalTextData.toMultiLineText(this,0,200)
            + * @param term the terminal + * @param start start line to show + * @param len number of lines to show -- negative numbers means show all + * @return a string representation of the content + */ + static public String toMultiLineText(ITerminalTextDataReadOnly term, int start, int len) { + if (len < 0) + len = term.getHeight(); + StringBuffer buff = new StringBuffer(); + int width = term.getWidth(); + int n = Math.min(len, term.getHeight() - start); + for (int line = start; line < n; line++) { + if (line > 0) + buff.append("\n"); //$NON-NLS-1$ + for (int column = 0; column < width; column++) { + buff.append(term.getChar(line, column)); + } + } + // get rid of the empty space at the end of the lines + //return buff.toString().replaceAll("\000+", ""); //$NON-NLS-1$//$NON-NLS-2$ + // + int i = buff.length() - 1; + while (i >= 0 && buff.charAt(i) == '\000') { + i--; + } + buff.setLength(i + 1); + return buff.toString(); + // + } + + /** + * Show the first 100 lines + * see {@link #toMultiLineText(ITerminalTextDataReadOnly, int, int)} + * @param term A read-only terminal model + * @return a string representation of the terminal + */ + static public String toMultiLineText(ITerminalTextDataReadOnly term) { + return toMultiLineText(term, 0, 100); + } + + public TerminalTextData() { + this(new TerminalTextDataFastScroll()); + + // this(new TerminalTextDataStore()); + } + + public TerminalTextData(ITerminalTextData data) { + fData = data; + } + + @Override + public int getWidth() { + return fData.getWidth(); + } + + @Override + public int getHeight() { + // no need for an extra variable + return fData.getHeight(); + } + + @Override + public void setDimensions(int height, int width) { + int h = getHeight(); + int w = getWidth(); + if (w == width && h == height) + return; + fData.setDimensions(height, width); + sendDimensionsChanged(h, w, height, width); + } + + private void sendDimensionsChanged(int oldHeight, int oldWidth, int newHeight, int newWidth) { + // determine what has changed + if (oldWidth == newWidth) { + if (oldHeight < newHeight) + sendLinesChangedToSnapshot(oldHeight, newHeight - oldHeight); + else + sendLinesChangedToSnapshot(newHeight, oldHeight - newHeight); + } else { + sendLinesChangedToSnapshot(0, oldHeight); + } + sendDimensionsChanged(); + } + + @Override + public LineSegment[] getLineSegments(int line, int column, int len) { + return fData.getLineSegments(line, column, len); + } + + @Override + public char getChar(int line, int column) { + return fData.getChar(line, column); + } + + @Override + public TerminalStyle getStyle(int line, int column) { + return fData.getStyle(line, column); + } + + @Override + public void setChar(int line, int column, char c, TerminalStyle style) { + fData.setChar(line, column, c, style); + sendLineChangedToSnapshots(line); + } + + @Override + public void setChars(int line, int column, char[] chars, TerminalStyle style) { + fData.setChars(line, column, chars, style); + sendLineChangedToSnapshots(line); + } + + @Override + public void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style) { + fData.setChars(line, column, chars, start, len, style); + sendLineChangedToSnapshots(line); + } + + @Override + public void scroll(int startLine, int size, int shift) { + fData.scroll(startLine, size, shift); + sendScrolledToSnapshots(startLine, size, shift); + } + + @Override + public String toString() { + return fData.toString(); + } + + private void sendDimensionsChanged() { + for (int i = 0; i < fSnapshots.length; i++) { + fSnapshots[i].markDimensionsChanged(); + } + } + + /** + * @param line notifies snapshots that line line has changed + */ + protected void sendLineChangedToSnapshots(int line) { + for (int i = 0; i < fSnapshots.length; i++) { + fSnapshots[i].markLineChanged(line); + } + } + + /** + * Notify snapshots that multiple lines have changed + * @param line changed line + * @param n number of changed lines + */ + protected void sendLinesChangedToSnapshot(int line, int n) { + for (int i = 0; i < fSnapshots.length; i++) { + fSnapshots[i].markLinesChanged(line, n); + } + } + + /** + * Notify snapshot that a region was scrolled + * @param startLine first line of scrolled region + * @param size size of scrolled region (number of lines) + * @param shift delta by which the region is scrolled + */ + protected void sendScrolledToSnapshots(int startLine, int size, int shift) { + for (int i = 0; i < fSnapshots.length; i++) { + fSnapshots[i].scroll(startLine, size, shift); + } + } + + protected void sendCursorChanged() { + for (int i = 0; i < fSnapshots.length; i++) { + fSnapshots[i].markCursorChanged(); + } + } + + /** + * Removes the snapshot from the @observer@ list + * @param snapshot A snapshot of a terminal model + */ + protected void removeSnapshot(TerminalTextDataSnapshot snapshot) { + // poor mans approach to modify the array + List list = new ArrayList<>(); + list.addAll(Arrays.asList(fSnapshots)); + list.remove(snapshot); + fSnapshots = list.toArray(new TerminalTextDataSnapshot[list.size()]); + } + + @Override + public ITerminalTextDataSnapshot makeSnapshot() { + // poor mans approach to modify the array + TerminalTextDataSnapshot snapshot = new TerminalTextDataSnapshot(this); + snapshot.markDimensionsChanged(); + List list = new ArrayList<>(); + list.addAll(Arrays.asList(fSnapshots)); + list.add(snapshot); + fSnapshots = list.toArray(new TerminalTextDataSnapshot[list.size()]); + return snapshot; + } + + @Override + public void addLine() { + int oldHeight = getHeight(); + fData.addLine(); + // was is an append or a scroll? + int newHeight = getHeight(); + if (newHeight > oldHeight) { + //the line was appended + sendLinesChangedToSnapshot(oldHeight, 1); + int width = getWidth(); + sendDimensionsChanged(oldHeight, width, newHeight, width); + + } else { + // the line was scrolled + sendScrolledToSnapshots(0, oldHeight, -1); + } + + } + + @Override + public void copy(ITerminalTextData source) { + fData.copy(source); + fCursorLine = source.getCursorLine(); + fCursorColumn = source.getCursorColumn(); + } + + @Override + public void copyLine(ITerminalTextData source, int sourceLine, int destLine) { + fData.copyLine(source, sourceLine, destLine); + } + + @Override + public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) { + fData.copyRange(source, sourceStartLine, destStartLine, length); + } + + @Override + public char[] getChars(int line) { + return fData.getChars(line); + } + + @Override + public TerminalStyle[] getStyles(int line) { + return fData.getStyles(line); + } + + @Override + public int getMaxHeight() { + return fData.getMaxHeight(); + } + + @Override + public void setMaxHeight(int height) { + fData.setMaxHeight(height); + } + + @Override + public void cleanLine(int line) { + fData.cleanLine(line); + sendLineChangedToSnapshots(line); + } + + @Override + public int getCursorColumn() { + return fCursorColumn; + } + + @Override + public int getCursorLine() { + return fCursorLine; + } + + @Override + public void setCursorColumn(int column) { + fCursorColumn = column; + sendCursorChanged(); + } + + @Override + public void setCursorLine(int line) { + fCursorLine = line; + sendCursorChanged(); + } + + @Override + public boolean isWrappedLine(int line) { + return fData.isWrappedLine(line); + } + + @Override + public void setWrappedLine(int line) { + fData.setWrappedLine(line); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataFastScroll.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataFastScroll.java new file mode 100644 index 00000000000..519fad0a3e1 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataFastScroll.java @@ -0,0 +1,302 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.LineSegment; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * This class is optimized for scrolling the entire {@link #getHeight()}. + * The scrolling is done by moving an offset into the data and using + * the modulo operator. + * + */ +public class TerminalTextDataFastScroll implements ITerminalTextData { + + final ITerminalTextData fData; + private int fHeight; + private int fMaxHeight; + /** + * The offset into the array. + */ + int fOffset; + + public TerminalTextDataFastScroll(ITerminalTextData data, int maxHeight) { + fMaxHeight = maxHeight; + fData = data; + fData.setDimensions(maxHeight, fData.getWidth()); + if (maxHeight > 2) + assert shiftOffset(-2) || throwRuntimeException(); + } + + public TerminalTextDataFastScroll(int maxHeight) { + this(new TerminalTextDataStore(), maxHeight); + } + + public TerminalTextDataFastScroll() { + this(new TerminalTextDataStore(), 1); + } + + /** + * This is used in asserts to throw an {@link RuntimeException}. + * This is useful for tests. + * @return never -- throws an exception + */ + private boolean throwRuntimeException() { + throw new RuntimeException(); + } + + /** + * + * @param line + * @return the actual line number in {@link #fData} + */ + int getPositionOfLine(int line) { + return (line + fOffset) % fMaxHeight; + } + + /** + * Moves offset by delta. This does not move the data! + * @param delta + */ + void moveOffset(int delta) { + assert Math.abs(delta) < fMaxHeight || throwRuntimeException(); + fOffset = (fMaxHeight + fOffset + delta) % fMaxHeight; + + } + + /** + * Test method to shift the offset for testing (if assert ==true) + * @param shift TODO + * @return true + */ + private boolean shiftOffset(int shift) { + moveOffset(shift); + return true; + } + + @Override + public void addLine() { + if (getHeight() < fMaxHeight) { + setDimensions(getHeight() + 1, getWidth()); + } else { + scroll(0, getHeight(), -1); + } + } + + @Override + public void cleanLine(int line) { + fData.cleanLine(getPositionOfLine(line)); + } + + @Override + public void copy(ITerminalTextData source) { + int n = source.getHeight(); + setDimensions(source.getHeight(), source.getWidth()); + for (int i = 0; i < n; i++) { + fData.copyLine(source, i, getPositionOfLine(i)); + } + } + + @Override + public void copyLine(ITerminalTextData source, int sourceLine, int destLine) { + fData.copyLine(source, sourceLine, getPositionOfLine(destLine)); + } + + @Override + public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) { + assert (destStartLine >= 0 && destStartLine + length <= fHeight) || throwRuntimeException(); + for (int i = 0; i < length; i++) { + fData.copyLine(source, i + sourceStartLine, getPositionOfLine(i + destStartLine)); + } + } + + @Override + public char getChar(int line, int column) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + return fData.getChar(getPositionOfLine(line), column); + } + + @Override + public char[] getChars(int line) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + return fData.getChars(getPositionOfLine(line)); + } + + @Override + public int getHeight() { + return fHeight; + } + + @Override + public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + return fData.getLineSegments(getPositionOfLine(line), startCol, numberOfCols); + } + + @Override + public int getMaxHeight() { + return fMaxHeight; + } + + @Override + public TerminalStyle getStyle(int line, int column) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + return fData.getStyle(getPositionOfLine(line), column); + } + + @Override + public TerminalStyle[] getStyles(int line) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + return fData.getStyles(getPositionOfLine(line)); + } + + @Override + public int getWidth() { + return fData.getWidth(); + } + + @Override + public ITerminalTextDataSnapshot makeSnapshot() { + return fData.makeSnapshot(); + } + + private void cleanLines(int line, int len) { + for (int i = line; i < line + len; i++) { + fData.cleanLine(getPositionOfLine(i)); + } + } + + @Override + public void scroll(int startLine, int size, int shift) { + assert (startLine >= 0 && startLine + size <= fHeight) || throwRuntimeException(); + if (shift >= fMaxHeight || -shift >= fMaxHeight) { + cleanLines(startLine, fMaxHeight - startLine); + return; + } + if (size == fHeight) { + // This is the case this class is optimized for! + moveOffset(-shift); + // we only have to clean the lines that appear by the move + if (shift < 0) { + cleanLines(Math.max(startLine, startLine + size + shift), Math.min(-shift, getHeight() - startLine)); + } else { + cleanLines(startLine, Math.min(shift, getHeight() - startLine)); + } + } else { + // we have to copy the lines. + if (shift < 0) { + // move the region up + // shift is negative!! + for (int i = startLine; i < startLine + size + shift; i++) { + fData.copyLine(fData, getPositionOfLine(i - shift), getPositionOfLine(i)); + } + // then clean the opened lines + cleanLines(Math.max(0, startLine + size + shift), Math.min(-shift, getHeight() - startLine)); + } else { + for (int i = startLine + size - 1; i >= startLine && i - shift >= 0; i--) { + fData.copyLine(fData, getPositionOfLine(i - shift), getPositionOfLine(i)); + } + cleanLines(startLine, Math.min(shift, getHeight() - startLine)); + } + } + } + + @Override + public void setChar(int line, int column, char c, TerminalStyle style) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + fData.setChar(getPositionOfLine(line), column, c, style); + } + + @Override + public void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + fData.setChars(getPositionOfLine(line), column, chars, start, len, style); + } + + @Override + public void setChars(int line, int column, char[] chars, TerminalStyle style) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + fData.setChars(getPositionOfLine(line), column, chars, style); + } + + @Override + public void setDimensions(int height, int width) { + assert height >= 0 || throwRuntimeException(); + assert width >= 0 || throwRuntimeException(); + if (height > fMaxHeight) + setMaxHeight(height); + fHeight = height; + if (width != fData.getWidth()) + fData.setDimensions(fMaxHeight, width); + } + + @Override + public void setMaxHeight(int maxHeight) { + assert maxHeight >= fHeight || throwRuntimeException(); + // move everything to offset0 + int start = getPositionOfLine(0); + if (start != 0) { + // invent a more efficient algorithm.... + ITerminalTextData buffer = new TerminalTextDataStore(); + // create a buffer with the expected height + buffer.setDimensions(maxHeight, getWidth()); + int n = Math.min(fMaxHeight - start, maxHeight); + // copy the first part + buffer.copyRange(fData, start, 0, n); + // copy the second part + if (n < maxHeight) + buffer.copyRange(fData, 0, n, Math.min(fMaxHeight - n, maxHeight - n)); + // copy the buffer back to our data + fData.copy(buffer); + shiftOffset(-start); + } else { + fData.setDimensions(maxHeight, fData.getWidth()); + } + fMaxHeight = maxHeight; + } + + @Override + public int getCursorColumn() { + throw new UnsupportedOperationException(); + } + + @Override + public int getCursorLine() { + throw new UnsupportedOperationException(); + } + + @Override + public void setCursorColumn(int column) { + throw new UnsupportedOperationException(); + } + + @Override + public void setCursorLine(int line) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isWrappedLine(int line) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + return fData.isWrappedLine(getPositionOfLine(line)); + } + + @Override + public void setWrappedLine(int line) { + assert (line >= 0 && line < fHeight) || throwRuntimeException(); + fData.setWrappedLine(getPositionOfLine(line)); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataSnapshot.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataSnapshot.java new file mode 100644 index 00000000000..26a6982c871 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataSnapshot.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.LineSegment; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * The public methods of this class have to be called from one thread! + * + * Threading considerations: + * This class is not threadsafe! + */ +class TerminalTextDataSnapshot implements ITerminalTextDataSnapshot { + /** + * The changes of the current snapshot relative to the + * previous snapshot + */ + volatile ISnapshotChanges fCurrentChanges; + /** + * Keeps track of changes that happened since the current + * snapshot has been made. + */ + ISnapshotChanges fFutureChanges; + /** + * Is used as lock and is the reference to the terminal we take snapshots from. + */ + final TerminalTextData fTerminal; + /** + * A snapshot copy of of fTerminal + */ + // snapshot does not need internal synchronisation + final TerminalTextDataWindow fSnapshot; + // this variable is synchronized on fTerminal! + private SnapshotOutOfDateListener[] fListener = new SnapshotOutOfDateListener[0]; + // this variable is synchronized on fTerminal! + private boolean fListenersNeedNotify; + private int fInterestWindowSize; + private int fInterestWindowStartLine; + + TerminalTextDataSnapshot(TerminalTextData terminal) { + fSnapshot = new TerminalTextDataWindow(); + fTerminal = terminal; + fCurrentChanges = new SnapshotChanges(fTerminal.getHeight()); + fCurrentChanges.setTerminalChanged(); + fFutureChanges = new SnapshotChanges(fTerminal.getHeight()); + fFutureChanges.markLinesChanged(0, fTerminal.getHeight()); + fListenersNeedNotify = true; + fInterestWindowSize = -1; + } + + /** + * This is used in asserts to throw an {@link RuntimeException}. + * This is useful for tests. + * @return never -- throws an exception + */ + private boolean throwRuntimeException() { + throw new RuntimeException(); + } + + @Override + public void detach() { + fTerminal.removeSnapshot(this); + } + + @Override + public boolean isOutOfDate() { + // this is called from fTerminal, therefore we lock on fTerminal + synchronized (fTerminal) { + return fFutureChanges.hasChanged(); + } + } + + @Override + public void updateSnapshot(boolean detectScrolling) { + // make sure terminal does not change while we make the snapshot + synchronized (fTerminal) { + // let's make the future changes current + fCurrentChanges = fFutureChanges; + fFutureChanges = new SnapshotChanges(fTerminal.getHeight()); + fFutureChanges.setInterestWindow(fInterestWindowStartLine, fInterestWindowSize); + // and update the snapshot + if (fSnapshot.getHeight() != fTerminal.getHeight() || fSnapshot.getWidth() != fTerminal.getWidth()) { + if (fInterestWindowSize == -1) + fSnapshot.setWindow(0, fTerminal.getHeight()); + // if the dimensions have changed, we need a full copy + fSnapshot.copy(fTerminal); + // and we mark all lines as changed + fCurrentChanges.setAllChanged(fTerminal.getHeight()); + } else { + // first we do the scroll on the copy + int start = fCurrentChanges.getScrollWindowStartLine(); + int lines = Math.min(fCurrentChanges.getScrollWindowSize(), fSnapshot.getHeight() - start); + fSnapshot.scroll(start, lines, fCurrentChanges.getScrollWindowShift()); + // and then create the snapshot of the changed lines + fCurrentChanges.copyChangedLines(fSnapshot, fTerminal); + } + fListenersNeedNotify = true; + fSnapshot.setCursorLine(fTerminal.getCursorLine()); + fSnapshot.setCursorColumn(fTerminal.getCursorColumn()); + } + if (!detectScrolling) { + // let's pretend there was no scrolling and + // convert the scrolling into line changes + fCurrentChanges.convertScrollingIntoChanges(); + } + } + + @Override + public char getChar(int line, int column) { + return fSnapshot.getChar(line, column); + } + + @Override + public int getHeight() { + return fSnapshot.getHeight(); + } + + @Override + public LineSegment[] getLineSegments(int line, int column, int len) { + return fSnapshot.getLineSegments(line, column, len); + } + + @Override + public TerminalStyle getStyle(int line, int column) { + return fSnapshot.getStyle(line, column); + } + + @Override + public int getWidth() { + return fSnapshot.getWidth(); + } + + @Override + public int getFirstChangedLine() { + return fCurrentChanges.getFirstChangedLine(); + } + + @Override + public int getLastChangedLine() { + return fCurrentChanges.getLastChangedLine(); + } + + @Override + public boolean hasLineChanged(int line) { + return fCurrentChanges.hasLineChanged(line); + } + + @Override + public boolean hasDimensionsChanged() { + return fCurrentChanges.hasDimensionsChanged(); + } + + @Override + public boolean hasTerminalChanged() { + return fCurrentChanges.hasTerminalChanged(); + } + + @Override + public int getScrollWindowStartLine() { + return fCurrentChanges.getScrollWindowStartLine(); + } + + @Override + public int getScrollWindowSize() { + return fCurrentChanges.getScrollWindowSize(); + } + + @Override + public int getScrollWindowShift() { + return fCurrentChanges.getScrollWindowShift(); + } + + /** + * Announces a change in line line + * @param line + */ + void markLineChanged(int line) { + // threading + fFutureChanges.markLineChanged(line); + fFutureChanges.setTerminalChanged(); + notifyListers(); + } + + /** + * Announces a change of n lines beginning with line line + * @param line + * @param n + */ + void markLinesChanged(int line, int n) { + fFutureChanges.markLinesChanged(line, n); + fFutureChanges.setTerminalChanged(); + notifyListers(); + } + + void markDimensionsChanged() { + fFutureChanges.markDimensionsChanged(); + fFutureChanges.setTerminalChanged(); + notifyListers(); + } + + void markCursorChanged() { + fFutureChanges.markCursorChanged(); + fFutureChanges.setTerminalChanged(); + notifyListers(); + } + + /** + * @param startLine + * @param size + * @param shift + */ + void scroll(int startLine, int size, int shift) { + fFutureChanges.scroll(startLine, size, shift); + fFutureChanges.setTerminalChanged(); + notifyListers(); + } + + /** + * Notifies listeners about the change + */ + private void notifyListers() { + // this code has to be called from a block synchronized on fTerminal + synchronized (fTerminal) { + if (fListenersNeedNotify) { + for (int i = 0; i < fListener.length; i++) { + fListener[i].snapshotOutOfDate(this); + } + fListenersNeedNotify = false; + } + } + } + + @Override + public ITerminalTextDataSnapshot makeSnapshot() { + return fSnapshot.makeSnapshot(); + } + + @Override + synchronized public void addListener(SnapshotOutOfDateListener listener) { + List list = new ArrayList<>(); + list.addAll(Arrays.asList(fListener)); + list.add(listener); + fListener = list.toArray(new SnapshotOutOfDateListener[list.size()]); + } + + @Override + synchronized public void removeListener(SnapshotOutOfDateListener listener) { + List list = new ArrayList<>(); + list.addAll(Arrays.asList(fListener)); + list.remove(listener); + fListener = list.toArray(new SnapshotOutOfDateListener[list.size()]); + } + + @Override + public String toString() { + return fSnapshot.toString(); + } + + @Override + public int getInterestWindowSize() { + return fInterestWindowSize; + } + + @Override + public int getInterestWindowStartLine() { + return fInterestWindowStartLine; + } + + @Override + public void setInterestWindow(int startLine, int size) { + assert startLine >= 0 || throwRuntimeException(); + assert size >= 0 || throwRuntimeException(); + fInterestWindowStartLine = startLine; + fInterestWindowSize = size; + fSnapshot.setWindow(startLine, size); + fFutureChanges.setInterestWindow(startLine, size); + notifyListers(); + } + + @Override + public char[] getChars(int line) { + return fSnapshot.getChars(line); + } + + @Override + public TerminalStyle[] getStyles(int line) { + return fSnapshot.getStyles(line); + } + + @Override + public int getCursorColumn() { + return fSnapshot.getCursorColumn(); + } + + @Override + public int getCursorLine() { + return fSnapshot.getCursorLine(); + } + + @Override + public ITerminalTextData getTerminalTextData() { + return fTerminal; + } + + @Override + public boolean isWrappedLine(int line) { + return fSnapshot.isWrappedLine(line); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataStore.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataStore.java new file mode 100644 index 00000000000..f0472aba928 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataStore.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.LineSegment; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * This class is thread safe. + * + */ +public class TerminalTextDataStore implements ITerminalTextData { + private char[][] fChars; + private TerminalStyle[][] fStyle; + private int fWidth; + private int fHeight; + private int fMaxHeight; + private int fCursorColumn; + private int fCursorLine; + final private BitSet fWrappedLines = new BitSet(); + + public TerminalTextDataStore() { + fChars = new char[0][]; + fStyle = new TerminalStyle[0][]; + fWidth = 0; + } + + /** + * This is used in asserts to throw an {@link RuntimeException}. + * This is useful for tests. + * @return never -- throws an exception + */ + private boolean throwRuntimeException() { + throw new RuntimeException(); + } + + @Override + public int getWidth() { + return fWidth; + } + + @Override + public int getHeight() { + return fHeight; + } + + @Override + public void setDimensions(int height, int width) { + assert height >= 0 || throwRuntimeException(); + assert width >= 0 || throwRuntimeException(); + // just extend the region + if (height > fChars.length) { + int h = 4 * height / 3; + if (fMaxHeight > 0 && h > fMaxHeight) + h = fMaxHeight; + fStyle = (TerminalStyle[][]) resizeArray(fStyle, height); + fChars = (char[][]) resizeArray(fChars, height); + } + // clean the new lines + if (height > fHeight) { + for (int i = fHeight; i < height; i++) { + cleanLine(i); + } + } + // set dimensions after successful resize! + fWidth = width; + fHeight = height; + } + + /** + * Reallocates an array with a new size, and copies the contents of the old + * array to the new array. + * + * @param origArray the old array, to be reallocated. + * @param newSize the new array size. + * @return A new array with the same contents (chopped off if needed or filled with 0 or null). + */ + private Object resizeArray(Object origArray, int newSize) { + int oldSize = Array.getLength(origArray); + if (oldSize == newSize) + return origArray; + Class elementType = origArray.getClass().getComponentType(); + Object newArray = Array.newInstance(elementType, newSize); + int preserveLength = Math.min(oldSize, newSize); + if (preserveLength > 0) + System.arraycopy(origArray, 0, newArray, 0, preserveLength); + return newArray; + } + + @Override + public LineSegment[] getLineSegments(int line, int column, int len) { + // get the styles and chars for this line + TerminalStyle[] styles = fStyle[line]; + char[] chars = fChars[line]; + int col = column; + int n = column + len; + + // expand the line if needed.... + if (styles == null) + styles = new TerminalStyle[n]; + else if (styles.length < n) + styles = (TerminalStyle[]) resizeArray(styles, n); + + if (chars == null) + chars = new char[n]; + else if (chars.length < n) + chars = (char[]) resizeArray(chars, n); + + // and create the line segments + TerminalStyle style = styles[column]; + List segments = new ArrayList<>(); + for (int i = column; i < n; i++) { + if (styles[i] != style) { + segments.add(new LineSegment(col, new String(chars, col, i - col), style)); + style = styles[i]; + col = i; + } + } + if (col < n) { + segments.add(new LineSegment(col, new String(chars, col, n - col), style)); + } + return segments.toArray(new LineSegment[segments.size()]); + } + + @Override + public char getChar(int line, int column) { + assert column < fWidth || throwRuntimeException(); + if (fChars[line] == null || column >= fChars[line].length) + return 0; + return fChars[line][column]; + } + + @Override + public TerminalStyle getStyle(int line, int column) { + assert column < fWidth || throwRuntimeException(); + if (fStyle[line] == null || column >= fStyle[line].length) + return null; + return fStyle[line][column]; + } + + void ensureLineLength(int iLine, int length) { + if (length > fWidth) + throw new RuntimeException(); + if (fChars[iLine] == null) { + fChars[iLine] = new char[length]; + } else if (fChars[iLine].length < length) { + fChars[iLine] = (char[]) resizeArray(fChars[iLine], length); + } + if (fStyle[iLine] == null) { + fStyle[iLine] = new TerminalStyle[length]; + } else if (fStyle[iLine].length < length) { + fStyle[iLine] = (TerminalStyle[]) resizeArray(fStyle[iLine], length); + } + } + + @Override + public void setChar(int line, int column, char c, TerminalStyle style) { + ensureLineLength(line, column + 1); + fChars[line][column] = c; + fStyle[line][column] = style; + } + + @Override + public void setChars(int line, int column, char[] chars, TerminalStyle style) { + setChars(line, column, chars, 0, chars.length, style); + } + + @Override + public void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style) { + ensureLineLength(line, column + len); + for (int i = 0; i < len; i++) { + fChars[line][column + i] = chars[i + start]; + fStyle[line][column + i] = style; + } + } + + @Override + public void scroll(int startLine, int size, int shift) { + assert startLine + size <= getHeight() || throwRuntimeException(); + if (shift < 0) { + // move the region up + // shift is negative!! + for (int i = startLine; i < startLine + size + shift; i++) { + fChars[i] = fChars[i - shift]; + fStyle[i] = fStyle[i - shift]; + fWrappedLines.set(i, fWrappedLines.get(i - shift)); + } + // then clean the opened lines + cleanLines(Math.max(startLine, startLine + size + shift), Math.min(-shift, getHeight() - startLine)); + // cleanLines(Math.max(0, startLine+size+shift),Math.min(-shift, getHeight()-startLine)); + } else { + for (int i = startLine + size - 1; i >= startLine && i - shift >= 0; i--) { + fChars[i] = fChars[i - shift]; + fStyle[i] = fStyle[i - shift]; + fWrappedLines.set(i, fWrappedLines.get(i - shift)); + } + cleanLines(startLine, Math.min(shift, getHeight() - startLine)); + } + } + + /** + * Replaces the lines with new empty data + * @param line + * @param len + */ + private void cleanLines(int line, int len) { + for (int i = line; i < line + len; i++) { + cleanLine(i); + } + } + + /* + * @return a text representation of the object. + * Lines are separated by '\n'. No style information is returned. + */ + @Override + public String toString() { + StringBuffer buff = new StringBuffer(); + for (int line = 0; line < getHeight(); line++) { + if (line > 0) + buff.append("\n"); //$NON-NLS-1$ + for (int column = 0; column < fWidth; column++) { + buff.append(getChar(line, column)); + } + } + return buff.toString(); + } + + @Override + public ITerminalTextDataSnapshot makeSnapshot() { + throw new UnsupportedOperationException(); + } + + @Override + public void addLine() { + if (fMaxHeight > 0 && getHeight() < fMaxHeight) { + setDimensions(getHeight() + 1, getWidth()); + } else { + scroll(0, getHeight(), -1); + } + } + + @Override + public void copy(ITerminalTextData source) { + fWidth = source.getWidth(); + int n = source.getHeight(); + if (getHeight() != n) { + fChars = new char[n][]; + fStyle = new TerminalStyle[n][]; + } + for (int i = 0; i < n; i++) { + copyLine(source, i, i); + } + fHeight = n; + fCursorLine = source.getCursorLine(); + fCursorColumn = source.getCursorColumn(); + } + + @Override + public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) { + for (int i = 0; i < length; i++) { + copyLine(source, i + sourceStartLine, i + destStartLine); + } + } + + @Override + public void copyLine(ITerminalTextData source, int sourceLine, int destLine) { + fChars[destLine] = source.getChars(sourceLine); + fStyle[destLine] = source.getStyles(sourceLine); + fWrappedLines.set(destLine, source.isWrappedLine(sourceLine)); + } + + @Override + public char[] getChars(int line) { + if (fChars[line] == null) + return null; + return fChars[line].clone(); + } + + @Override + public TerminalStyle[] getStyles(int line) { + if (fStyle[line] == null) + return null; + return fStyle[line].clone(); + } + + public void setLine(int line, char[] chars, TerminalStyle[] styles) { + fChars[line] = chars.clone(); + fStyle[line] = styles.clone(); + fWrappedLines.clear(line); + } + + @Override + public void setMaxHeight(int height) { + fMaxHeight = height; + } + + @Override + public int getMaxHeight() { + return fMaxHeight; + } + + @Override + public void cleanLine(int line) { + fChars[line] = null; + fStyle[line] = null; + fWrappedLines.clear(line); + } + + @Override + public int getCursorColumn() { + return fCursorColumn; + } + + @Override + public int getCursorLine() { + return fCursorLine; + } + + @Override + public void setCursorColumn(int column) { + fCursorColumn = column; + } + + @Override + public void setCursorLine(int line) { + fCursorLine = line; + } + + @Override + public boolean isWrappedLine(int line) { + return fWrappedLines.get(line); + } + + @Override + public void setWrappedLine(int line) { + fWrappedLines.set(line); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataWindow.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataWindow.java new file mode 100644 index 00000000000..31e6d625901 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataWindow.java @@ -0,0 +1,259 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.model; + +import org.eclipse.tm.terminal.model.ITerminalTextData; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.LineSegment; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * This class stores the data only within a window {@link #setWindow(int, int)} and + * {@link #getWindowStartLine()} and {@link #getWindowSize()}. Everything outside + * the is char=='\000' and style=null. + * + */ +public class TerminalTextDataWindow implements ITerminalTextData { + final ITerminalTextData fData; + int fWindowStartLine; + int fWindowSize; + int fHeight; + int fMaxHeight; + + public TerminalTextDataWindow(ITerminalTextData data) { + fData = data; + } + + public TerminalTextDataWindow() { + this(new TerminalTextDataStore()); + } + + /** + * This is used in asserts to throw an {@link RuntimeException}. + * This is useful for tests. + * @return never -- throws an exception + */ + private boolean throwRuntimeException() { + throw new RuntimeException(); + } + + /** + * @param line + * @return true if the line is within the window + */ + boolean isInWindow(int line) { + return line >= fWindowStartLine && line < fWindowStartLine + fWindowSize; + } + + @Override + public char getChar(int line, int column) { + if (!isInWindow(line)) + return 0; + return fData.getChar(line - fWindowStartLine, column); + } + + @Override + public char[] getChars(int line) { + if (!isInWindow(line)) + return null; + return fData.getChars(line - fWindowStartLine); + } + + @Override + public int getHeight() { + return fHeight; + } + + @Override + public LineSegment[] getLineSegments(int line, int startCol, int numberOfCols) { + if (!isInWindow(line)) + return new LineSegment[] { new LineSegment(startCol, new String(new char[numberOfCols]), null) }; + return fData.getLineSegments(line - fWindowStartLine, startCol, numberOfCols); + } + + @Override + public int getMaxHeight() { + return fMaxHeight; + } + + @Override + public TerminalStyle getStyle(int line, int column) { + if (!isInWindow(line)) + return null; + return fData.getStyle(line - fWindowStartLine, column); + } + + @Override + public TerminalStyle[] getStyles(int line) { + if (!isInWindow(line)) + return null; + return fData.getStyles(line - fWindowStartLine); + } + + @Override + public int getWidth() { + return fData.getWidth(); + } + + @Override + public ITerminalTextDataSnapshot makeSnapshot() { + throw new UnsupportedOperationException(); + } + + @Override + public void addLine() { + if (fMaxHeight > 0 && getHeight() < fMaxHeight) { + setDimensions(getHeight() + 1, getWidth()); + } else { + scroll(0, getHeight(), -1); + } + } + + @Override + public void copy(ITerminalTextData source) { + // we inherit the dimensions of the source + setDimensions(source.getHeight(), source.getWidth()); + int n = Math.min(fWindowSize, source.getHeight() - fWindowStartLine); + if (n > 0) + fData.copyRange(source, fWindowStartLine, 0, n); + } + + @Override + public void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length) { + int n = length; + int dStart = destStartLine - fWindowStartLine; + int sStart = sourceStartLine; + // if start outside our range, cut the length to copy + if (dStart < 0) { + n += dStart; + sStart -= dStart; + dStart = 0; + } + // do not exceed the window size + n = Math.min(n, fWindowSize); + if (n > 0) + fData.copyRange(source, sStart, dStart, n); + + } + + @Override + public void copyLine(ITerminalTextData source, int sourceLine, int destLine) { + if (isInWindow(destLine)) + fData.copyLine(source, sourceLine, destLine - fWindowStartLine); + } + + @Override + public void scroll(int startLine, int size, int shift) { + assert (startLine >= 0 && startLine + size <= fHeight) || throwRuntimeException(); + int n = size; + int start = startLine - fWindowStartLine; + // if start outside our range, cut the length to copy + if (start < 0) { + n += start; + start = 0; + } + n = Math.min(n, fWindowSize - start); + // do not exceed the window size + if (n > 0) + fData.scroll(start, n, shift); + } + + @Override + public void setChar(int line, int column, char c, TerminalStyle style) { + if (!isInWindow(line)) + return; + fData.setChar(line - fWindowStartLine, column, c, style); + } + + @Override + public void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style) { + if (!isInWindow(line)) + return; + fData.setChars(line - fWindowStartLine, column, chars, start, len, style); + } + + @Override + public void setChars(int line, int column, char[] chars, TerminalStyle style) { + if (!isInWindow(line)) + return; + fData.setChars(line - fWindowStartLine, column, chars, style); + } + + @Override + public void setDimensions(int height, int width) { + assert height >= 0 || throwRuntimeException(); + fData.setDimensions(fWindowSize, width); + fHeight = height; + } + + @Override + public void setMaxHeight(int height) { + fMaxHeight = height; + } + + public void setWindow(int startLine, int size) { + fWindowStartLine = startLine; + fWindowSize = size; + fData.setDimensions(fWindowSize, getWidth()); + } + + public int getWindowStartLine() { + return fWindowStartLine; + } + + public int getWindowSize() { + return fWindowSize; + } + + public void setHeight(int height) { + fHeight = height; + } + + @Override + public void cleanLine(int line) { + if (isInWindow(line)) + fData.cleanLine(line - fWindowStartLine); + } + + @Override + public int getCursorColumn() { + return fData.getCursorColumn(); + } + + @Override + public int getCursorLine() { + return fData.getCursorLine(); + } + + @Override + public void setCursorColumn(int column) { + fData.setCursorColumn(column); + } + + @Override + public void setCursorLine(int line) { + fData.setCursorLine(line); + } + + @Override + public boolean isWrappedLine(int line) { + if (isInWindow(line)) + return fData.isWrappedLine(line - fWindowStartLine); + return false; + } + + @Override + public void setWrappedLine(int line) { + if (isInWindow(line)) + fData.setWrappedLine(line - fWindowStartLine); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/ITerminalConstants.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/ITerminalConstants.java new file mode 100644 index 00000000000..9f87a310388 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/ITerminalConstants.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.preferences; + +import org.eclipse.tm.terminal.model.TerminalColor; + +/** + * Constants for Terminal Preferences. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface ITerminalConstants { + + public static final String FONT_DEFINITION = "terminal.views.view.font.definition"; //$NON-NLS-1$ + public static final String PREF_HAS_MIGRATED = "TerminalPref.migrated"; //$NON-NLS-1$ + + public static final String PREF_BUFFERLINES = "TerminalPrefBufferLines"; //$NON-NLS-1$ + public static final String PREF_INVERT_COLORS = "TerminalPrefInvertColors"; //$NON-NLS-1$ + /** + * @since 5.0 + */ + public static final String PREF_FONT_DEFINITION = "TerminalFontDefinition"; //$NON-NLS-1$ + public static final int DEFAULT_BUFFERLINES = 1000; + public static final boolean DEFAULT_INVERT_COLORS = false; + /** + * @since 5.0 + */ + public static final String DEFAULT_FONT_DEFINITION = FONT_DEFINITION; + + /** + * @since 5.0 + */ + public static String getPrefForTerminalColor(TerminalColor tc) { + return tc.toString(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalColorPresets.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalColorPresets.java new file mode 100644 index 00000000000..108ac7de0c2 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalColorPresets.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2020 Kichwa Coders Canada Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.preferences; + +import static org.eclipse.tm.terminal.model.TerminalColor.BACKGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.BLACK; +import static org.eclipse.tm.terminal.model.TerminalColor.BLUE; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_BLACK; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_BLUE; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_CYAN; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_GREEN; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_MAGENTA; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_RED; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_WHITE; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_YELLOW; +import static org.eclipse.tm.terminal.model.TerminalColor.CYAN; +import static org.eclipse.tm.terminal.model.TerminalColor.FOREGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.GREEN; +import static org.eclipse.tm.terminal.model.TerminalColor.MAGENTA; +import static org.eclipse.tm.terminal.model.TerminalColor.RED; +import static org.eclipse.tm.terminal.model.TerminalColor.SELECTION_BACKGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.SELECTION_FOREGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.WHITE; +import static org.eclipse.tm.terminal.model.TerminalColor.YELLOW; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.terminal.control.impl.TerminalMessages; +import org.eclipse.tm.internal.terminal.model.SystemDefaultColors; +import org.eclipse.tm.terminal.model.TerminalColor; + +/** + * @since 5.0 + */ +public enum TerminalColorPresets { + + INSTANCE; + + private final List presets = new ArrayList<>(); + + public List getPresets() { + return presets.stream().map(Preset::getName).collect(Collectors.toList()); + } + + public Preset getPreset(int index) { + return presets.get(index); + } + + public static class Preset { + private String name; + private Map> map = new EnumMap<>(TerminalColor.class); + + Preset(String name) { + this.name = name; + set(BLACK, 0, 0, 0); + set(RED, 205, 0, 0); + set(GREEN, 0, 205, 0); + set(YELLOW, 205, 205, 0); + set(BLUE, 0, 0, 238); + set(MAGENTA, 205, 0, 205); + set(CYAN, 0, 205, 205); + set(WHITE, 229, 229, 229); + + set(BRIGHT_BLACK, 0, 0, 0); + set(BRIGHT_RED, 255, 0, 0); + set(BRIGHT_GREEN, 0, 255, 0); + set(BRIGHT_YELLOW, 255, 255, 0); + set(BRIGHT_BLUE, 92, 92, 255); + set(BRIGHT_MAGENTA, 255, 0, 255); + set(BRIGHT_CYAN, 0, 255, 255); + set(BRIGHT_WHITE, 255, 255, 255); + + set(FOREGROUND, SystemDefaultColors.FOREGROUND); + set(BACKGROUND, SystemDefaultColors.BACKGROUND); + set(SELECTION_FOREGROUND, SystemDefaultColors.SELECTION_FOREGROUND); + set(SELECTION_BACKGROUND, SystemDefaultColors.SELECTION_BACKGROUND); + } + + Preset set(TerminalColor color, RGB rgb) { + return set(color, () -> rgb); + } + + Preset set(TerminalColor color, int r, int g, int b) { + return set(color, new RGB(r, g, b)); + } + + Preset set(TerminalColor color, Supplier rgbSupplier) { + map.put(color, rgbSupplier); + return this; + } + + public String getName() { + return name; + } + + /** + * Returns the preset value for the given color. Will never return null + * because each color must be defined in the map. + * + * @param terminalColor to get RGB value for + * @return non-null color + */ + public RGB getRGB(TerminalColor terminalColor) { + return map.getOrDefault(terminalColor, () -> new RGB(0, 0, 0)).get(); + } + } + + TerminalColorPresets() { + presets.add(new Preset(TerminalMessages.TerminalColorPresets_TerminalDefaults)); + presets.add(new Preset(TerminalMessages.TerminalColorPresets_EclipseLight) // + .set(FOREGROUND, getDefaultPreset().getRGB(BLACK)) // + .set(BACKGROUND, getDefaultPreset().getRGB(WHITE))); + presets.add(new Preset(TerminalMessages.TerminalColorPresets_EclipseDark) // + .set(FOREGROUND, getDefaultPreset().getRGB(WHITE)) // + .set(BACKGROUND, getDefaultPreset().getRGB(BLACK))); + } + + public Preset getDefaultPreset() { + return presets.get(0); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalColorsFieldEditor.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalColorsFieldEditor.java new file mode 100644 index 00000000000..1ea8983c5ac --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalColorsFieldEditor.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2020 Kichwa Coders Canada Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.preferences; + +import static org.eclipse.tm.terminal.model.TerminalColor.BACKGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.BLACK; +import static org.eclipse.tm.terminal.model.TerminalColor.BLUE; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_BLACK; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_BLUE; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_CYAN; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_GREEN; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_MAGENTA; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_RED; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_WHITE; +import static org.eclipse.tm.terminal.model.TerminalColor.BRIGHT_YELLOW; +import static org.eclipse.tm.terminal.model.TerminalColor.CYAN; +import static org.eclipse.tm.terminal.model.TerminalColor.FOREGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.GREEN; +import static org.eclipse.tm.terminal.model.TerminalColor.MAGENTA; +import static org.eclipse.tm.terminal.model.TerminalColor.RED; +import static org.eclipse.tm.terminal.model.TerminalColor.SELECTION_BACKGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.SELECTION_FOREGROUND; +import static org.eclipse.tm.terminal.model.TerminalColor.WHITE; +import static org.eclipse.tm.terminal.model.TerminalColor.YELLOW; + +import java.util.EnumMap; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.preference.ColorSelector; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.tm.internal.terminal.control.impl.TerminalMessages; +import org.eclipse.tm.terminal.model.TerminalColor; + +/** + * A field editor that can be used for editing terminal colors. + * + * @since 5.0 + */ +public class TerminalColorsFieldEditor extends FieldEditor { + + private EnumMap colorSelectors; + private Composite controls; + private Font boldFont; + + /** + * Creates a field editor for editing colors of {@link TerminalColor}. + * The preference names used are as they are returned from {@link TerminalColor#getPreferenceName()} + * @param labelText + * @param parent + */ + public TerminalColorsFieldEditor(Composite parent) { + super("", "", parent); //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + protected void adjustForNumColumns(int numColumns) { + GridData gd = (GridData) controls.getLayoutData(); + gd.horizontalSpan = numColumns; + } + + @Override + public int getNumberOfControls() { + return 1; + } + + @Override + protected void doFillIntoGrid(Composite parent, int numColumns) { + colorSelectors = new EnumMap<>(TerminalColor.class); + controls = new Composite(parent, SWT.NONE); + GridDataFactory.fillDefaults().applyTo(controls); + GridLayoutFactory.fillDefaults().applyTo(controls); + + FontDescriptor boldDescriptor = FontDescriptor.createFrom(parent.getFont()).setStyle(SWT.BOLD); + boldFont = boldDescriptor.createFont(parent.getDisplay()); + + Group general = new Group(controls, SWT.SHADOW_NONE); + general.setText(TerminalMessages.TerminalColorsFieldEditor_GeneralColors); + general.setFont(boldFont); + GridDataFactory.fillDefaults().applyTo(general); + GridLayoutFactory.swtDefaults().numColumns(4).applyTo(general); + + createLabelledSelector(general, FOREGROUND, TerminalMessages.TerminalColorsFieldEditor_TextColor); + createLabelledSelector(general, BACKGROUND, TerminalMessages.TerminalColorsFieldEditor_Background); + createLabelledSelector(general, SELECTION_BACKGROUND, TerminalMessages.TerminalColorsFieldEditor_Selection); + createLabelledSelector(general, SELECTION_FOREGROUND, TerminalMessages.TerminalColorsFieldEditor_SelectedText); + + Group palette = new Group(controls, SWT.SHADOW_NONE); + palette.setText(TerminalMessages.TerminalColorsFieldEditor_PaletteColors); + palette.setFont(boldFont); + GridDataFactory.fillDefaults().applyTo(palette); + GridLayoutFactory.swtDefaults().numColumns(8).applyTo(palette); + + createSelector(palette, BLACK, TerminalMessages.TerminalColorsFieldEditor_Black); + createSelector(palette, RED, TerminalMessages.TerminalColorsFieldEditor_Red); + createSelector(palette, GREEN, TerminalMessages.TerminalColorsFieldEditor_Green); + createSelector(palette, YELLOW, TerminalMessages.TerminalColorsFieldEditor_Yellow); + createSelector(palette, BLUE, TerminalMessages.TerminalColorsFieldEditor_Blue); + createSelector(palette, MAGENTA, TerminalMessages.TerminalColorsFieldEditor_Magenta); + createSelector(palette, CYAN, TerminalMessages.TerminalColorsFieldEditor_Cyan); + createSelector(palette, WHITE, TerminalMessages.TerminalColorsFieldEditor_White); + + createSelector(palette, BRIGHT_BLACK, TerminalMessages.TerminalColorsFieldEditor_BrightBlack); + createSelector(palette, BRIGHT_RED, TerminalMessages.TerminalColorsFieldEditor_BrightRed); + createSelector(palette, BRIGHT_GREEN, TerminalMessages.TerminalColorsFieldEditor_BrightGreen); + createSelector(palette, BRIGHT_YELLOW, TerminalMessages.TerminalColorsFieldEditor_BrightYellow); + createSelector(palette, BRIGHT_BLUE, TerminalMessages.TerminalColorsFieldEditor_BrightBlue); + createSelector(palette, BRIGHT_MAGENTA, TerminalMessages.TerminalColorsFieldEditor_BrightMagenta); + createSelector(palette, BRIGHT_CYAN, TerminalMessages.TerminalColorsFieldEditor_BrightCyan); + createSelector(palette, BRIGHT_WHITE, TerminalMessages.TerminalColorsFieldEditor_BrightWhite); + + Group presets = new Group(controls, SWT.SHADOW_NONE); + presets.setText(TerminalMessages.TerminalColorsFieldEditor_Presets); + presets.setFont(boldFont); + GridDataFactory.fillDefaults().applyTo(presets); + GridLayoutFactory.swtDefaults().numColumns(2).applyTo(presets); + Combo presetsCombo = new Combo(presets, SWT.DROP_DOWN | SWT.READ_ONLY); + presetsCombo.add(TerminalMessages.TerminalColorsFieldEditor_LoadPresets); + TerminalColorPresets colorPresets = TerminalColorPresets.INSTANCE; + colorPresets.getPresets().forEach(presetsCombo::add); + presetsCombo.addListener(SWT.Selection, e -> { + int selectionIndex = presetsCombo.getSelectionIndex(); + if (selectionIndex > 0) { + int selectedPresetIndex = selectionIndex - 1; // account for "Load Presets..." entry + colorSelectors.forEach((terminalColor, colorSelector) -> colorSelector + .setColorValue(colorPresets.getPreset(selectedPresetIndex).getRGB(terminalColor))); + + } + }); + presetsCombo.select(0); + } + + @Override + public void dispose() { + if (boldFont != null) { + boldFont.dispose(); + } + } + + private void createLabelledSelector(Composite parent, TerminalColor color, String label) { + Label labelControl = new Label(parent, SWT.LEFT); + labelControl.setText(label); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).applyTo(labelControl); + createSelector(parent, color, label); + } + + private void createSelector(Composite parent, TerminalColor color, String label) { + ColorSelector colorSelector = new ColorSelector(parent); + colorSelector.getButton().setToolTipText(label); + GridDataFactory.fillDefaults().applyTo(colorSelector.getButton()); + colorSelectors.put(color, colorSelector); + } + + @Override + protected void doLoad() { + IPreferenceStore store = getPreferenceStore(); + colorSelectors.forEach((terminalColor, colorSelector) -> colorSelector.setColorValue( + PreferenceConverter.getColor(store, ITerminalConstants.getPrefForTerminalColor(terminalColor)))); + } + + @Override + protected void doLoadDefault() { + IPreferenceStore store = getPreferenceStore(); + colorSelectors.forEach((terminalColor, colorSelector) -> colorSelector.setColorValue( + PreferenceConverter.getDefaultColor(store, ITerminalConstants.getPrefForTerminalColor(terminalColor)))); + } + + @Override + public void store() { + IPreferenceStore store = getPreferenceStore(); + if (store == null) { + return; + } + + if (presentsDefaultValue()) { + doStoreDefault(store); + } else { + doStore(); + } + } + + /** + * Stores the default preference value from this field editor into + * the preference store. + */ + protected void doStoreDefault(IPreferenceStore store) { + colorSelectors.forEach((terminalColor, colorSelector) -> store + .setToDefault(ITerminalConstants.getPrefForTerminalColor(terminalColor))); + } + + @Override + protected void doStore() { + IPreferenceStore store = getPreferenceStore(); + colorSelectors.forEach((terminalColor, colorSelector) -> PreferenceConverter.setValue(store, + ITerminalConstants.getPrefForTerminalColor(terminalColor), colorSelector.getColorValue())); + } + + @Override + public String getPreferenceName() { + throw new IllegalArgumentException( + "preference name should not be accessed as this class represent multiple preferences"); //$NON-NLS-1$ + } + + @Override + public String getLabelText() { + throw new IllegalArgumentException( + "label text should not be accessed as this class represent multiple preferences"); //$NON-NLS-1$ + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalPreferenceInitializer.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalPreferenceInitializer.java new file mode 100644 index 00000000000..d6d737459b4 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalPreferenceInitializer.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget + * Martin Oberhuber (Wind River) - [436612] Restore Eclipse 3.4 compatibility + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.jface.resource.StringConverter; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.preferences.TerminalColorPresets.Preset; +import org.eclipse.tm.terminal.model.TerminalColor; + +/** + * Terminal Preference Initializer. + * + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + * @noreference This class is not intended to be referenced by clients. + */ +public class TerminalPreferenceInitializer extends AbstractPreferenceInitializer { + + public TerminalPreferenceInitializer() { + } + + @Override + public void initializeDefaultPreferences() { + //DefaultScope.INSTANCE was added in Eclipse 3.7 + IEclipsePreferences defaultPrefs = DefaultScope.INSTANCE.getNode(TerminalPlugin.PLUGIN_ID); + defaultPrefs.putBoolean(ITerminalConstants.PREF_INVERT_COLORS, ITerminalConstants.DEFAULT_INVERT_COLORS); + defaultPrefs.putInt(ITerminalConstants.PREF_BUFFERLINES, ITerminalConstants.DEFAULT_BUFFERLINES); + defaultPrefs.put(ITerminalConstants.PREF_FONT_DEFINITION, ITerminalConstants.DEFAULT_FONT_DEFINITION); + + Preset defaultPresets = TerminalColorPresets.INSTANCE.getDefaultPreset(); + TerminalColor[] colors = TerminalColor.values(); + for (TerminalColor color : colors) { + defaultPrefs.put(ITerminalConstants.getPrefForTerminalColor(color), + StringConverter.asString(defaultPresets.getRGB(color))); + } + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalPreferencePage.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalPreferencePage.java new file mode 100644 index 00000000000..27888302669 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/preferences/TerminalPreferencePage.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2003, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Initial Contributors: + * The following Wind River employees contributed to the Terminal component + * that contains this file: Chris Thew, Fran Litterio, Stephen Lamb, + * Helmut Haigermoser and Ted Williams. + * + * Contributors: + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [378691][api] push Preferences into the Widget + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.preferences; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.tm.internal.terminal.control.impl.TerminalMessages; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +/** + * Terminal Preference Page. + * + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + * @noreference This class is not intended to be referenced by clients. + */ +public class TerminalPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + private TerminalColorsFieldEditor terminalColorsFieldEditor; + + public TerminalPreferencePage() { + super(GRID); + } + + @Override + protected void createFieldEditors() { + setupPage(); + } + + @Override + public void init(IWorkbench workbench) { + // do nothing + } + + protected void setupPage() { + setupData(); + setupEditors(); + } + + protected void setupData() { + TerminalPlugin plugin; + IPreferenceStore preferenceStore; + + plugin = TerminalPlugin.getDefault(); + preferenceStore = plugin.getPreferenceStore(); + setPreferenceStore(preferenceStore); + } + + protected void setupEditors() { + addField(new BooleanFieldEditor(ITerminalConstants.PREF_INVERT_COLORS, TerminalMessages.INVERT_COLORS, + getFieldEditorParent())); + + addField(new IntegerFieldEditor(ITerminalConstants.PREF_BUFFERLINES, TerminalMessages.BUFFERLINES, + getFieldEditorParent())); + + terminalColorsFieldEditor = new TerminalColorsFieldEditor(getFieldEditorParent()); + addField(terminalColorsFieldEditor); + } + + @Override + public void dispose() { + if (terminalColorsFieldEditor != null) { + terminalColorsFieldEditor.dispose(); + } + + super.dispose(); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/AbstractSettingsPage.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/AbstractSettingsPage.java new file mode 100644 index 00000000000..971e30e05f1 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/AbstractSettingsPage.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2013, 2018 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License 2.0 which accompanies this distribution, and is + * available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; + +/** + * Abstract settings page providing a common implementation of the listener handling. + */ +public abstract class AbstractSettingsPage implements ISettingsPage, IMessageProvider { + // A message associated with the control. + private String message = null; + + // The message type of the associated message. + private int messageType = IMessageProvider.NONE; + + // Reference to the listener + private final ListenerList listeners = new ListenerList<>(); + + // Flag to control the control decorations + private boolean hasDecoration = false; + + @Override + public void addListener(Listener listener) { + Assert.isNotNull(listener); + listeners.add(listener); + } + + @Override + public void removeListener(Listener listener) { + Assert.isNotNull(listener); + listeners.remove(listener); + } + + /** + * Fire the listeners for the given control. + * + * @param control The control or null. + */ + public void fireListeners(Control control) { + Object[] list = listeners.getListeners(); + for (int i = 0; i < list.length; i++) { + Object l = list[i]; + if (!(l instanceof Listener)) + continue; + ((Listener) l).onSettingsPageChanged(control); + } + } + + @Override + public final String getMessage() { + return message; + } + + @Override + public final int getMessageType() { + return messageType; + } + + /** + * Set the message and the message type. + * + * @param message The message or null. + * @param messageType The type of the message (NONE, INFORMATION, WARNING, ERROR). + */ + public final void setMessage(String message, int messageType) { + this.message = message; + this.messageType = messageType; + } + + /** + * Sets if or if not the settings panel widgets will have control decorations + * or not. The method has effect only if called before {@link #createControl(org.eclipse.swt.widgets.Composite)}. + * + * @param value True if the panel widgets have control decorations, false otherwise. + */ + public final void setHasControlDecoration(boolean value) { + this.hasDecoration = value; + } + + /** + * Returns if or if not the settings panel widgets will have control + * decorations or not. + * + * @return True if the panel widgets have control decorations, false otherwise. + */ + protected final boolean hasControlDecoration() { + return hasDecoration; + } + + /** + * Creates a new instance of a {@link ControlDecoration} object associated with + * the given control. The method is called after the control has been created. + * + * @param control The control. Must not be null. + * @return The control decoration object instance. + */ + protected final ControlDecoration createControlDecoration(Control control) { + Assert.isNotNull(control); + if (!hasDecoration) + return null; + ControlDecoration controlDecoration = new ControlDecoration(control, getControlDecorationPosition()); + controlDecoration.setShowOnlyOnFocus(false); + control.setData("controlDecoration", controlDecoration); //$NON-NLS-1$ + return controlDecoration; + } + + /** + * Returns the control decoration position. The default is + * {@link SWT#TOP} | {@link SWT#LEFT}. + * + * @return The control position. + */ + protected int getControlDecorationPosition() { + return SWT.TOP | SWT.LEFT; + } + + /** + * Updates the control decoration of the given control to represent the given message + * and message type. If the message is null or the message type is + * {@link IMessageProvider#NONE} no decoration will be shown. + * + * @param control The control. Must not be null. + * @param message The message. + * @param messageType The message type. + */ + protected final void updateControlDecoration(Control control, String message, int messageType) { + Assert.isNotNull(control); + + ControlDecoration controlDecoration = (ControlDecoration) control.getData("controlDecoration"); //$NON-NLS-1$ + if (controlDecoration != null) { + // The description is the same as the message + controlDecoration.setDescriptionText(message); + + // The icon depends on the message type + FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault(); + + // Determine the id of the decoration to show + String decorationId = FieldDecorationRegistry.DEC_INFORMATION; + if (messageType == IMessageProvider.ERROR) { + decorationId = FieldDecorationRegistry.DEC_ERROR; + } else if (messageType == IMessageProvider.WARNING) { + decorationId = FieldDecorationRegistry.DEC_WARNING; + } + + // Get the field decoration + FieldDecoration fieldDeco = registry.getFieldDecoration(decorationId); + if (fieldDeco != null) { + controlDecoration.setImage(fieldDeco.getImage()); + } + + if (message == null || messageType == IMessageProvider.NONE) { + controlDecoration.hide(); + } else { + controlDecoration.show(); + } + } + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ISettingsPage.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ISettingsPage.java new file mode 100644 index 00000000000..33e895a0578 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ISettingsPage.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** + * @author Michael Scharf + * + * TODO: Michael Scharf: provide a long description of a wizard + * TODO: Michael Scharf: allow multiple pages to be generated + *

            + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will + * work or that it will remain the same. Please do not use this API without + * consulting with the Target Management team. + *

            + */ +public interface ISettingsPage { + + public interface Listener { + + /** + * Invoked by settings page controls to signal that the settings page + * changed and page container may update their state. + * + * @param control The control which triggered the event or null + */ + public void onSettingsPageChanged(Control control); + } + + /** + * Create a page to be shown in a dialog or wizard to setup the connection. + * @param parent + */ + void createControl(Composite parent); + + /** + * Called before the page is shown. Loads the state from the {@link ITerminalConnector}. + */ + void loadSettings(); + + /** + * Called when the OK button is pressed. + */ + void saveSettings(); + + /** + * @return true if the + */ + boolean validateSettings(); + + /** + * Adds the given listener. + *

            + * Has not effect if the same listener is already registered. + * + * @param listener The listener. Must not be null. + */ + public void addListener(Listener listener); + + /** + * Removes the given listener. + *

            + * Has no effect if the same listener was not registered. + * + * @param listener The listener. Must not be null. + */ + public void removeListener(Listener listener); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ISettingsStore.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ISettingsStore.java new file mode 100644 index 00000000000..71cfc58164b --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ISettingsStore.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +/** + * A simple interface to a store to persist the state of a connection. + * + * @author Michael Scharf + *

            + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will + * work or that it will remain the same. Please do not use this API without + * consulting with the Target Management team. + *

            + */ +public interface ISettingsStore { + /** + * @param key alpha numeric key, may contain dots (.) + * @return value + */ + String get(String key); + + /** + * @param key alpha numeric key, may contain dots (.) + * @param defaultValue + * @return the value or the default + */ + String get(String key, String defaultValue); + + /** + * Save a string value + * @param key alpha numeric key, may contain dots (.) + * @param value + */ + void put(String key, String value); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalConnector.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalConnector.java new file mode 100644 index 00000000000..d1a5fe55f70 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalConnector.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend + * Uwe Stieber (Wind River) - [282996] [terminal][api] Add "hidden" attribute to terminal connector extension point + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +import java.io.OutputStream; +import java.util.Optional; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl; + +/** + * A contributed connection type to manage a single connection. + * + * Implementations of this class are contributed through the + * org.eclipse.tm.terminal.control.connectors extension point. This + * class gives access to the static markup of a terminal connector extension as + * well as providing the lifecycle management for the dynamically loaded + * {@link TerminalConnectorImpl} instance, which performs the actual + * communications. This pattern allows for lazy initialization, bundle + * activation and class loading of the actual {@link TerminalConnectorImpl} + * instance. + * + * Clients can get terminal connector instances from the + * {@link TerminalConnectorExtension} class, or from + * {@link ITerminalViewControl#getTerminalConnector()} when running inside an + * active terminal widget. + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * + * @author Michael Scharf + *

            + * EXPERIMENTAL. This class or interface has been added + * as part of a work in progress. There is no guarantee that this API + * will work or that it will remain the same. Please do not use this API + * without consulting with the Target Management team. + *

            + */ +public interface ITerminalConnector extends IAdaptable { + /** + * @return an ID of this connector. The id from the plugin.xml. + * @since org.eclipse.tm.terminal 2.0 + */ + String getId(); + + /** + * @return null the name (as specified in the plugin.xml) + * @since org.eclipse.tm.terminal 2.0 + */ + String getName(); + + /** + * @return True if the connector is not visible in user + * selections. + * @since org.eclipse.tm.terminal 3.0.1 + */ + boolean isHidden(); + + /** + * @return true if the {@link TerminalConnectorImpl} has been initialized. + * If there was an initialization error, {@link #getInitializationErrorMessage()} + * returns the error message. + * @since org.eclipse.tm.terminal 2.0 + */ + boolean isInitialized(); + + /** + * This method initializes the connector if it is not initialized! + * If the connector was initialized successfully, null is + * returned. Otherwise an error message describing the problem is returned. + * @return null or a localized error message. + * @since org.eclipse.tm.terminal 2.0 + */ + String getInitializationErrorMessage(); + + /** + * Connect using the current state of the settings. + * @param control Used to inform the UI about state changes and messages from the connection. + */ + void connect(ITerminalControl control); + + /** + * Disconnect if connected. Else do nothing. + */ + void disconnect(); + + /** + * @return true if a local echo is needed. + * TODO:Michael Scharf: this should be handed within the connection.... + */ + boolean isLocalEcho(); + + /** + * Notify the remote site that the size of the terminal has changed. + * @param newWidth + * @param newHeight + */ + void setTerminalSize(int newWidth, int newHeight); + + /** + * @return the terminal to remote stream (bytes written to this stream will + * be sent to the remote site). For the stream in the other direction (remote to + * terminal see {@link ITerminalControl#getRemoteToTerminalOutputStream()} + * @since org.eclipse.tm.terminal 2.0 + */ + OutputStream getTerminalToRemoteStream(); + + /** + * Load the state of this connection. Is typically called before + * {@link #connect(ITerminalControl)}. + * + * @param store a string based data store. Short keys like "foo" can be used to + * store the state of the connection. + */ + void load(ISettingsStore store); + + /** + * When the view or dialog containing the terminal is closed, + * the state of the connection is saved into the settings store store + * @param store + */ + void save(ISettingsStore store); + + /** + * Set or reset the settings store to the default values. + */ + void setDefaultSettings(); + + /** + * @return A string that represents the settings of the connection. This representation + * may be shown in the status line of the terminal view. + */ + String getSettingsSummary(); + + /** + * @return An optional with the absolute path if available of the current working dir, empty otherwise. + * @since 5.2 + */ + default Optional getWorkingDirectory() { + return Optional.empty(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalControl.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalControl.java new file mode 100644 index 00000000000..b47c0788a2e --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/ITerminalControl.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Martin Oberhuber (Wind River) - [204796] Terminal should allow setting the encoding to use + * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend + * Anton Leherbauer (Wind River) - [433751] Add option to enable VT100 line wrapping mode + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tm.internal.terminal.control.ITerminalListener3.TerminalTitleRequestor; + +/** + * Represents the terminal view as seen by a terminal connection. + *

            + * EXPERIMENTAL. This class or interface has been added as part + * of a work in progress. There is no guarantee that this API will work or that + * it will remain the same. Please do not use this API without consulting with + * the Target Management team. + *

            + * + * @author Michael Scharf + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITerminalControl { + + /** + * @return the current state of the connection + */ + TerminalState getState(); + + /** + * @param state + */ + void setState(TerminalState state); + + /** + * Setup the terminal control within the given parent composite. + * + * @param parent The parent composite. Must not be null. + */ + void setupTerminal(Composite parent); + + /** + * A shell to show dialogs. + * @return the shell in which the terminal is shown. + */ + Shell getShell(); + + /** + * Set the encoding that the Terminal uses to decode bytes from the + * Terminal-to-remote-Stream into Unicode Characters used in Java; or, to + * encode Characters typed by the user into bytes sent over the wire to the + * remote. + * + * By default, the local Platform Default Encoding is used. Also note that + * the encoding must not be applied in case the terminal stream is processed + * by some data transfer protocol which requires binary data. + * + * Validity of the encoding set here is not checked. Since some encodings do + * not cover the entire range of Unicode characters, it can happen that a + * particular Unicode String typed in by the user can not be encoded into a + * byte Stream with the encoding specified. and UnsupportedEncodingException + * will be thrown in this case at the time the String is about to be + * processed. + * + * The concrete encoding to use can either be specified manually by a user, + * by means of a dialog, or a connector can try to obtain it automatically + * from the remote side e.g. by evaluating an environment variable such as + * LANG on UNIX systems. + * + * @since org.eclipse.tm.terminal 2.0 + * @deprecated Use {@link #setCharset(Charset)} and do the error handling in the UI code. + */ + @Deprecated + void setEncoding(String encoding) throws UnsupportedEncodingException; + + /** + * Set the charset that the Terminal uses to decode bytes from the + * Terminal-to-remote-Stream into Unicode Characters used in Java; or, to + * encode Characters typed by the user into bytes sent over the wire to the + * remote. + * + * By default, the local Platform Default charset is used. Also note that + * the encoding must not be applied in case the terminal stream is processed + * by some data transfer protocol which requires binary data. + * + * Validity of the charset set here is not checked. Since some encodings do + * not cover the entire range of Unicode characters, it can happen that a + * particular Unicode String typed in by the user can not be encoded into a + * byte Stream with the encoding specified. and UnsupportedEncodingException + * will be thrown in this case at the time the String is about to be + * processed. + * + * The concrete encoding to use can either be specified manually by a user, + * by means of a dialog, or a connector can try to obtain it automatically + * from the remote side e.g. by evaluating an environment variable such as + * LANG on UNIX systems. + * + * @param charset Charset to use, or null for platform's default charset. + * + * @since 5.3 + */ + void setCharset(Charset charset); + + /** + * Return the current encoding. That's interesting when the previous + * setEncoding() call failed and the fallback default encoding should be + * queried, such that e.g. a combobox with encodings to choose can be + * properly initialized. + * + * @return the current Encoding of the Terminal. + * @since org.eclipse.tm.terminal 2.0 + * @deprecated Use {@link #getCharset()} and call {@link Charset#name()} on the result + */ + @Deprecated + String getEncoding(); + + /** + * Return the current charset. + * + * @return the non-null current charset of the Terminal + * @since 5.3 + */ + Charset getCharset(); + + /** + * Show a text in the terminal. If puts newlines at the beginning and the + * end. + * + * @param text TODO: Michael Scharf: Is this really needed? + */ + void displayTextInTerminal(String text); + + /** + * @return a stream used to write to the terminal. Any bytes written to this + * stream appear in the terminal or are interpreted by the emulator as + * control sequences. The stream in the opposite direction, terminal + * to remote is in {@link ITerminalConnector#getTerminalToRemoteStream()}. + */ + OutputStream getRemoteToTerminalOutputStream(); + + /** + * @deprecated call {@link #setTerminalTitle(String, String)} instead + */ + @Deprecated(forRemoval = true) + void setTerminalTitle(String title); + + /** + * Set the title of the terminal view. + * @param title Termianl title. + * @param requestor Item that requests terminal title update. + * @since 5.5 + */ + void setTerminalTitle(String title, TerminalTitleRequestor requestor); + + /** + * Show an error message during connect. + * @param msg + * TODO: Michael Scharf: Should be replaced by a better error notification mechanism! + */ + void setMsg(String msg); + + /** + * Sets if or if not the terminal view control should try to reconnect + * the terminal connection if the user hits ENTER in a closed terminal. + *

            + * Reconnect on ENTER if terminal is closed is enabled by default. + * + * @param on True to enable the reconnect, false to disable it. + */ + void setConnectOnEnterIfClosed(boolean on); + + /** + * Returns if or if not the terminal view control should try to reconnect + * the terminal connection if the user hits ENTER in a closed terminal. + * + * @return True the reconnect is enabled, false if disabled. + */ + boolean isConnectOnEnterIfClosed(); + + /** + * Enables VT100 line wrapping mode (default is off). + * This corresponds to the VT100 'eat_newline_glitch' terminal capability. + * If enabled, writing to the rightmost column does not cause + * an immediate wrap to the next line. Instead the line wrap occurs on the + * next output character. + * + * @param enable whether to enable or disable VT100 line wrapping mode + */ + void setVT100LineWrapping(boolean enable); + + /** + * @return whether VT100 line wrapping mode is enabled + */ + boolean isVT100LineWrapping(); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/Logger.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/Logger.java new file mode 100644 index 00000000000..36096598ac4 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/Logger.java @@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (c) 2005, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Fran Litterio (Wind River) - initial API and implementation + * Ted Williams (Wind River) - refactored into org.eclipse namespace + * Michael Scharf (Wind River) - split into core, view and connector plugins + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.lang.StackWalker.StackFrame; +import java.util.Optional; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; + +/** + * A simple logger class. Every method in this class is static, so they can be + * called from both class and instance methods. To use this class, write code + * like this: + *

            + * + *

            + * Logger.log("something has happened");
            + * Logger.log("counter is " + counter);
            + * 
            + * + * @author Fran Litterio + *

            + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will + * work or that it will remain the same. Please do not use this API without + * consulting with the Target Management team. + *

            + */ +public final class Logger { + public static final String TRACE_DEBUG_LOG = "org.eclipse.tm.terminal.control/debug/log"; //$NON-NLS-1$ + public static final String TRACE_DEBUG_LOG_CHAR = "org.eclipse.tm.terminal.control/debug/log/char"; //$NON-NLS-1$ + public static final String TRACE_DEBUG_LOG_VT100BACKEND = "org.eclipse.tm.terminal.control/debug/log/VT100Backend"; //$NON-NLS-1$ + /** @since 5.2 */ + public static final String TRACE_DEBUG_LOG_HOVER = "org.eclipse.tm.terminal.control/debug/log/hover"; //$NON-NLS-1$ + + private static PrintStream logStream; + private static StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); + + private static boolean underTest = false; + + /** + * When underTest we want exception that are deep inside the code to be surfaced to the test + * @noreference This method is not intended to be referenced by clients. + */ + public static void setUnderTest(boolean underTest) { + Logger.underTest = underTest; + } + + static { + // Any of the known debugging options turns on the creation of the log file + boolean createLogFile = TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG) + || TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG_CHAR) + || TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG_VT100BACKEND) + || TerminalPlugin.isOptionEnabled(TRACE_DEBUG_LOG_HOVER); + + // Log only if tracing is enabled + if (createLogFile && TerminalPlugin.getDefault() != null) { + IPath logFile = Platform.getStateLocation(TerminalPlugin.getDefault().getBundle()); + if (logFile != null && logFile.toFile().isDirectory()) { + logFile = logFile.append("tmterminal.log"); //$NON-NLS-1$ + try { + logStream = new PrintStream(new FileOutputStream(logFile.toFile(), true)); + } catch (Exception ex) { + logStream = System.err; + logStream.println("Exception when opening log file -- logging to stderr!"); //$NON-NLS-1$ + ex.printStackTrace(logStream); + } + } + } + } + + /** + * Encodes a String such that non-printable control characters are + * converted into user-readable escape sequences for logging. + * @param message String to encode + * @return encoded String + */ + public static final String encode(String message) { + boolean encoded = false; + StringBuffer buf = new StringBuffer(message.length() + 32); + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + switch (c) { + case '\\': + case '\'': + buf.append('\\'); + buf.append(c); + encoded = true; + break; + case '\r': + buf.append('\\'); + buf.append('r'); + encoded = true; + break; + case '\n': + buf.append('\\'); + buf.append('n'); + encoded = true; + break; + case '\t': + buf.append('\\'); + buf.append('t'); + encoded = true; + break; + case '\f': + buf.append('\\'); + buf.append('f'); + encoded = true; + break; + case '\b': + buf.append('\\'); + buf.append('b'); + encoded = true; + break; + default: + if (c <= '\u000f') { + buf.append('\\'); + buf.append('x'); + buf.append('0'); + buf.append(Integer.toHexString(c)); + encoded = true; + } else if (c >= ' ' && c < '\u007f') { + buf.append(c); + } else if (c <= '\u00ff') { + buf.append('\\'); + buf.append('x'); + buf.append(Integer.toHexString(c)); + encoded = true; + } else { + buf.append('\\'); + buf.append('u'); + if (c <= '\u0fff') { + buf.append('0'); + } + buf.append(Integer.toHexString(c)); + encoded = true; + } + } + } + if (encoded) { + return buf.toString(); + } + return message; + } + + /** + * Checks if logging is enabled. + * @return true if logging is enabled. + */ + public static final boolean isLogEnabled() { + return (logStream != null); + } + + /** + * Logs the specified message. Do not append a newline to parameter + * message. This method does that for you. + * + * Does not write to the message to the Eclipse log + * + * @param message A String containing the message to log. + */ + public static final void log(String message) { + if (logStream != null) { + logStream.println(getCallSiteDescription() + ": " + message); //$NON-NLS-1$ + logStream.flush(); + } + } + + /** + * Writes an error message to the Terminal log and the Eclipse log + * @since 5.2 + */ + public static final void logError(String message) { + logStatus(new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, IStatus.OK, message, null)); + } + + /** + * Writes an exception to the Terminal log and the Eclipse log + */ + public static final void logException(Exception ex) { + if (underTest) { + throw new RuntimeException("Terminal Under Test - examine cause for real failure", ex); //$NON-NLS-1$ + } + logStatus(new Status(IStatus.ERROR, TerminalPlugin.PLUGIN_ID, IStatus.OK, ex.getMessage(), ex)); + } + + /** + * Writes a Status to the Terminal log and the Eclipse log + * @since 5.2 + */ + public static final void logStatus(IStatus status) { + // log in eclipse error log + if (TerminalPlugin.getDefault() != null) { + TerminalPlugin.getDefault().getLog().log(status); + } else { + System.err.println(status); + if (status.getException() != null) { + status.getException().printStackTrace(); + } + } + // Additional Tracing for debug purposes: + // Read my own stack to get the class name, method name, and line number + // of where this method was called + if (logStream != null) { + logStream.println(getCallSiteDescription() + ": " + //$NON-NLS-1$ + status); + if (status.getException() != null) { + status.getException().printStackTrace(logStream); + } + } + } + + /** + * Return a description string of the call site of this logging call for use in logged messages. + * This method will walk the stack to find the first method in the call stack not from the Logger + * class. + */ + private static String getCallSiteDescription() { + Optional stackFrame = walker + .walk(stream -> stream.filter(f -> f.getDeclaringClass() != Logger.class).findFirst()); + int lineNumber = stackFrame.map(StackFrame::getLineNumber).orElse(0); + String className = stackFrame.map(StackFrame::getDeclaringClass).map(Class::getName) + .map(name -> name.substring(name.lastIndexOf('.') + 1)).orElse("UnknownClass"); //$NON-NLS-1$ + String methodName = stackFrame.map(StackFrame::getMethodName).orElse("unknownMethod"); //$NON-NLS-1$ + String locationString = className + "." + methodName + ":" + lineNumber; //$NON-NLS-1$//$NON-NLS-2$ + return locationString; + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/NullSettingsStore.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/NullSettingsStore.java new file mode 100644 index 00000000000..dc7d3b668ec --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/NullSettingsStore.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2015, 2018 Wind River Systems, Inc. and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License 2.0 which accompanies this distribution, and is + * available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +/** + * A settings store implementation doing nothing. + */ +public class NullSettingsStore implements ISettingsStore { + + @Override + public String get(String key) { + return null; + } + + @Override + public String get(String key, String defaultValue) { + return defaultValue; + } + + @Override + public void put(String key, String value) { + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/TerminalConnectorExtension.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/TerminalConnectorExtension.java new file mode 100644 index 00000000000..1db1bbb54eb --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/TerminalConnectorExtension.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Uwe Stieber (Wind River) - [282996] [terminal][api] Add "hidden" attribute to terminal connector extension point + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.RegistryFactory; +import org.eclipse.tm.internal.terminal.connector.TerminalConnector; +import org.eclipse.tm.internal.terminal.provisional.api.provider.TerminalConnectorImpl; + +/** + * A factory to get {@link ITerminalConnector} instances. + * + * @author Michael Scharf + * + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + * + *

            + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *

            + */ +public class TerminalConnectorExtension { + static private ITerminalConnector makeConnector(final IConfigurationElement config) { + String id = config.getAttribute("id"); //$NON-NLS-1$ + if (id == null || id.length() == 0) + id = config.getAttribute("class"); //$NON-NLS-1$ + String name = config.getAttribute("name"); //$NON-NLS-1$ + if (name == null || name.length() == 0) { + name = id; + } + String hidden = config.getAttribute("hidden"); //$NON-NLS-1$ + boolean isHidden = hidden != null ? Boolean.parseBoolean(hidden) : false; + TerminalConnector.Factory factory = () -> (TerminalConnectorImpl) config.createExecutableExtension("class"); //$NON-NLS-1$ + return new TerminalConnector(factory, id, name, isHidden); + } + + /** + * Return a specific terminal connector for a given connector id. The + * terminal connector is not yet instantiated to any real connection. + * + * @param id the id of the terminal connector in the + * org.eclipse.tm.terminal.control.connectors + * extension point + * @return a new ITerminalConnector with id or null if there + * is no extension with that id. + * @since org.eclipse.tm.terminal 2.0 + */ + public static ITerminalConnector makeTerminalConnector(String id) { + IConfigurationElement[] config = RegistryFactory.getRegistry() + .getConfigurationElementsFor("org.eclipse.tm.terminal.control.connectors"); //$NON-NLS-1$ + for (int i = 0; i < config.length; i++) { + if (id.equals(config[i].getAttribute("id"))) { //$NON-NLS-1$ + return makeConnector(config[i]); + } + } + return null; + } + + /** + * Return a list of available terminal connectors (connection types). + * + * The terminal connectors returned are not yet instantiated to any real + * connection. Each terminal connector can connect to one remote system at a + * time. + * + * @return a new list of {@link ITerminalConnector} instances defined in the + * org.eclipse.tm.terminal.control.connectors + * extension point + * @since org.eclipse.tm.terminal 2.0 return value is ITerminalConnector[] + */ + public static ITerminalConnector[] makeTerminalConnectors() { + IConfigurationElement[] config = RegistryFactory.getRegistry() + .getConfigurationElementsFor("org.eclipse.tm.terminal.control.connectors"); //$NON-NLS-1$ + List result = new ArrayList<>(); + for (int i = 0; i < config.length; i++) { + result.add(makeConnector(config[i])); + } + return result.toArray(new ITerminalConnector[result.size()]); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/TerminalState.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/TerminalState.java new file mode 100644 index 00000000000..2304d0a6be1 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/TerminalState.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - fixed copyright headers and beautified + * Michael Scharf (Wind River) - [262996] get rid of TerminalState.OPENED + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api; + +/** + * Represent the sate of a terminal connection. + * In java 1.5 this would be an enum. + * @author Michael Scharf + * + *

            + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will + * work or that it will remain the same. Please do not use this API without + * consulting with the Target Management team. + *

            + */ +public class TerminalState { + /** + * The terminal is not connected. + */ + public final static TerminalState CLOSED = new TerminalState("CLOSED"); //$NON-NLS-1$ + + /** + * The terminal is about to connect. + */ + public final static TerminalState CONNECTING = new TerminalState("CONNECTING..."); //$NON-NLS-1$ + + /** + * The terminal is connected. + */ + public final static TerminalState CONNECTED = new TerminalState("CONNECTED"); //$NON-NLS-1$ + + private final String fState; + + public TerminalState(String state) { + fState = state; + } + + @Override + public String toString() { + return fState; + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/provider/TerminalConnectorImpl.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/provider/TerminalConnectorImpl.java new file mode 100644 index 00000000000..c19296bb725 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/provisional/api/provider/TerminalConnectorImpl.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2008, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - [225853][api] Provide more default functionality in TerminalConnectorImpl + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.provisional.api.provider; + +import java.io.OutputStream; +import java.util.Optional; + +import org.eclipse.tm.internal.terminal.provisional.api.ISettingsStore; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.tm.internal.terminal.provisional.api.TerminalState; + +/** + * Abstract base class for all terminal connector implementations to be + * registered via the org.eclipse.tm.terminal.control.connectors + * extension point. + * + * @since org.eclipse.tm.terminal 2.0 + */ +public abstract class TerminalConnectorImpl { + + /** + * The TerminalControl associated with this connector. + * Required for advertising state changes when needed. + */ + protected ITerminalControl fControl; + + /** + * Initialize this connector. This is called once after the constructor, in + * order to perform any required initializations such as loading required + * native libraries. Any work that may lead to runtime exceptions should be + * done in this method rather than in the constructor. + * + * @throws Exception when the connector fails to initialize (due to missing + * required libraries, for instance). + */ + public void initialize() throws Exception { + } + + /** + * Connect using the current state of the settings. + * + * This method is designed to be overridden by actual implementations, in + * order to open the streams required for communicating with the remote + * side. Extenders must call super.connect(control) as the + * first thing they are doing. + * + * @param control Used to inform the UI about state changes and messages + * from the connection. + */ + public void connect(ITerminalControl control) { + Logger.log("entered."); //$NON-NLS-1$ + fControl = control; + } + + /** + * Disconnect if connected. Else do nothing. Has to set the state of the + * {@link ITerminalControl} when finished disconnecting. + */ + public final void disconnect() { + Logger.log("entered."); //$NON-NLS-1$ + doDisconnect(); + fControl.setState(TerminalState.CLOSED); + } + + /** + * Disconnect if connected. Else do nothing. Clients should override to + * perform any extra work needed for disconnecting. + */ + protected void doDisconnect() { + // Do nothing by default + } + + /** + * @return the terminal to remote stream (bytes written to this stream will + * be sent to the remote site). For the stream in the other direction (remote to + * terminal see {@link ITerminalControl#getRemoteToTerminalOutputStream()} + */ + abstract public OutputStream getTerminalToRemoteStream(); + + /** + * @return A string that represents the settings of the connection. This representation + * may be shown in the status line of the terminal view. + */ + abstract public String getSettingsSummary(); + + /** + * Test if local echo is needed. The default implementation returns + * false. Override to modify this behavior. + * + * @return true if a local echo is needed. TODO:Michael Scharf: this should + * be handed within the connection.... + */ + public boolean isLocalEcho() { + return false; + } + + /** + * Set or reset the settings store to the default values. + */ + public void setDefaultSettings() { + // do nothing by default + } + + /** + * Load the state or settings of this connection. Is typically called before + * {@link #connect(ITerminalControl)}. + * + * Connectors that have nothing to configure do not need to implement this. + * Those terminals that do have configuration need to override this method + * to load settings. + * + * @param store a string based data store. Short keys like "foo" can be used + * to store the state of the connection. + */ + public void load(ISettingsStore store) { + // do nothing by default + } + + /** + * When the view or dialog containing the terminal is closed, the state of + * the connection is saved into the settings store store. + * + * Connectors that have no state or settings to persist do not need to + * override this. Others should override to persist their settings. + * + * @param store the store for persisting settings. + */ + public void save(ISettingsStore store) { + // do nothing by default + } + + /** + * Notify the remote site that the size of the terminal has changed. + * + * Concrete connectors should override this if they have the possibility to + * inform the remote about changed terminal size. + * + * @param newWidth the new width in characters. + * @param newHeight the new height in characters. + */ + public void setTerminalSize(int newWidth, int newHeight) { + } + + /** + * @since 5.2 + */ + public Optional getWorkingDirectory() { + return Optional.empty(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/AbstractTextCanvasModel.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/AbstractTextCanvasModel.java new file mode 100644 index 00000000000..0d0e151a1a8 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/AbstractTextCanvasModel.java @@ -0,0 +1,511 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - [168197] Fix Terminal for CDC-1.1/Foundation-1.1 + * Anton Leherbauer (Wind River) - [219589] Copy an entire line selection + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; +import org.eclipse.tm.terminal.model.TextRange; + +abstract public class AbstractTextCanvasModel implements ITextCanvasModel { + private static final boolean DEBUG_HOVER = TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_HOVER); + protected List fListeners = new ArrayList<>(); + private int fCursorLine; + private int fCursorColumn; + private boolean fShowCursor; + private long fCursorTime; + private boolean fCursorIsEnabled; + private final ITerminalTextDataSnapshot fSnapshot; + private int fLines; + + private int fSelectionStartLine = -1; + private int fSeletionEndLine; + private int fSelectionStartCoumn; + private int fSelectionEndColumn; + private ITerminalTextDataSnapshot fSelectionSnapshot; + private String fCurrentSelection = ""; //$NON-NLS-1$ + private final Point fSelectionAnchor = new Point(0, 0); + /** + * do not update while update is running + */ + boolean fInUpdate; + private int fCols; + + private TextRange fHoverRange = TextRange.EMPTY; + + public AbstractTextCanvasModel(ITerminalTextDataSnapshot snapshot) { + fSnapshot = snapshot; + fLines = fSnapshot.getHeight(); + } + + @Override + public void addCellCanvasModelListener(ITextCanvasModelListener listener) { + fListeners.add(listener); + } + + @Override + public void removeCellCanvasModelListener(ITextCanvasModelListener listener) { + fListeners.remove(listener); + } + + protected void fireCellRangeChanged(int x, int y, int width, int height) { + for (Iterator iter = fListeners.iterator(); iter.hasNext();) { + ITextCanvasModelListener listener = iter.next(); + listener.rangeChanged(x, y, width, height); + } + } + + protected void fireDimensionsChanged(int width, int height) { + for (Iterator iter = fListeners.iterator(); iter.hasNext();) { + ITextCanvasModelListener listener = iter.next(); + listener.dimensionsChanged(width, height); + } + + } + + protected void fireTerminalDataChanged() { + for (Iterator iter = fListeners.iterator(); iter.hasNext();) { + ITextCanvasModelListener listener = iter.next(); + listener.terminalDataChanged(); + } + + } + + @Override + public ITerminalTextDataReadOnly getTerminalText() { + return fSnapshot; + } + + protected ITerminalTextDataSnapshot getSnapshot() { + return fSnapshot; + } + + protected void updateSnapshot() { + if (!fInUpdate && fSnapshot.isOutOfDate()) { + fInUpdate = true; + try { + fSnapshot.updateSnapshot(false); + if (fSnapshot.hasTerminalChanged()) + fireTerminalDataChanged(); + // TODO why does hasDimensionsChanged not work?????? + // if(fSnapshot.hasDimensionsChanged()) + // fireDimensionsChanged(); + if (fLines != fSnapshot.getHeight() || fCols != fSnapshot.getWidth()) { + fireDimensionsChanged(fSnapshot.getWidth(), fSnapshot.getHeight()); + fLines = fSnapshot.getHeight(); + fCols = fSnapshot.getWidth(); + } + int y = fSnapshot.getFirstChangedLine(); + // has any line changed? + if (y < Integer.MAX_VALUE) { + int height = fSnapshot.getLastChangedLine() - y + 1; + fireCellRangeChanged(0, y, fSnapshot.getWidth(), height); + } + + } finally { + fInUpdate = false; + } + } + } + + /** + * must be called from the UI thread + */ + public void update() { + // do the poll.... + updateSnapshot(); + updateSelection(); + updateCursor(); + } + + @Override + public int getCursorColumn() { + return fCursorColumn; + } + + @Override + public int getCursorLine() { + return fCursorLine; + } + + @Override + public boolean isCursorOn() { + return fShowCursor && fCursorIsEnabled; + } + + /** + * should be called regularly to draw an update of the + * blinking cursor + */ + protected void updateCursor() { + if (!fCursorIsEnabled) + return; + int cursorLine = getSnapshot().getCursorLine(); + int cursorColumn = getSnapshot().getCursorColumn(); + // if cursor at the end put it to the end of the + // last line... + if (cursorLine >= getSnapshot().getHeight()) { + cursorLine = getSnapshot().getHeight() - 1; + cursorColumn = getSnapshot().getWidth() - 1; + } + // has the cursor moved? + if (fCursorLine != cursorLine || fCursorColumn != cursorColumn) { + // hide the old cursor! + fShowCursor = false; + // clean the previous cursor + // bug 206363: paint also the char to the left and right of the cursor - see also below + int col = fCursorColumn; + int width = 2; + if (col > 0) { + col--; + width++; + } + fireCellRangeChanged(col, fCursorLine, width, 1); + // the cursor is shown when it moves! + fShowCursor = true; + fCursorTime = System.currentTimeMillis(); + fCursorLine = cursorLine; + fCursorColumn = cursorColumn; + // and draw the new cursor + fireCellRangeChanged(fCursorColumn, fCursorLine, 1, 1); + } else { + long t = System.currentTimeMillis(); + // TODO make the cursor blink time customisable + if (t - fCursorTime > 500) { + fShowCursor = !fShowCursor; + fCursorTime = t; + // on some windows machines, there is some left + // over when updating the cursor . + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206363 + int col = fCursorColumn; + int width = 2; + if (col > 0) { + col--; + width++; + } + fireCellRangeChanged(col, fCursorLine, width, 1); + } + } + } + + @Override + public void setVisibleRectangle(int startLine, int startCol, int height, int width) { + fSnapshot.setInterestWindow(Math.max(0, startLine), Math.max(1, height)); + update(); + } + + protected void showCursor(boolean show) { + fShowCursor = true; + } + + @Override + public void setCursorEnabled(boolean visible) { + fCursorTime = System.currentTimeMillis(); + fShowCursor = visible; + fCursorIsEnabled = visible; + fireCellRangeChanged(fCursorColumn, fCursorLine, 1, 1); + } + + @Override + public boolean isCursorEnabled() { + return fCursorIsEnabled; + } + + @Override + public Point getSelectionEnd() { + if (fSelectionStartLine < 0) + return null; + else + return new Point(fSelectionEndColumn, fSeletionEndLine); + } + + @Override + public Point getSelectionStart() { + if (fSelectionStartLine < 0) + return null; + else + return new Point(fSelectionStartCoumn, fSelectionStartLine); + } + + @Override + public Point getSelectionAnchor() { + if (fSelectionStartLine < 0) + return null; + return new Point(fSelectionAnchor.x, fSelectionAnchor.y); + } + + @Override + public void setSelectionAnchor(Point anchor) { + fSelectionAnchor.x = anchor.x; + fSelectionAnchor.y = anchor.y; + } + + @Override + public void setSelection(int startLine, int endLine, int startColumn, int endColumn) { + // System.err.println(startLine+","+endLine+","+startColumn+","+endColumn); + doSetSelection(startLine, endLine, startColumn, endColumn); + fCurrentSelection = extractSelectedText(); + } + + private void doSetSelection(int startLine, int endLine, int startColumn, int endColumn) { + assert (startLine < 0 || startLine <= endLine); + if (startLine >= 0) { + if (fSelectionSnapshot == null) { + fSelectionSnapshot = fSnapshot.getTerminalTextData().makeSnapshot(); + fSelectionSnapshot.updateSnapshot(true); + } + } else if (fSelectionSnapshot != null) { + fSelectionSnapshot.detach(); + fSelectionSnapshot = null; + } + int oldStart = fSelectionStartLine; + int oldEnd = fSeletionEndLine; + fSelectionStartLine = startLine; + fSeletionEndLine = endLine; + fSelectionStartCoumn = startColumn; + fSelectionEndColumn = endColumn; + if (fSelectionSnapshot != null) { + fSelectionSnapshot.setInterestWindow(0, fSelectionSnapshot.getHeight()); + } + int changedStart; + int changedEnd; + if (oldStart < 0) { + changedStart = fSelectionStartLine; + changedEnd = fSeletionEndLine; + } else if (fSelectionStartLine < 0) { + changedStart = oldStart; + changedEnd = oldEnd; + } else { + changedStart = Math.min(oldStart, fSelectionStartLine); + changedEnd = Math.max(oldEnd, fSeletionEndLine); + } + if (changedStart >= 0) { + fireCellRangeChanged(0, changedStart, fSnapshot.getWidth(), changedEnd - changedStart + 1); + } + } + + @Override + public boolean hasLineSelection(int line) { + if (fSelectionStartLine < 0) + return false; + else + return line >= fSelectionStartLine && line <= fSeletionEndLine; + } + + @Override + public String getSelectedText() { + return fCurrentSelection; + } + + @Override + public boolean hasHoverSelection(int line) { + if (fHoverRange.isEmpty()) { + return false; + } + return fHoverRange.contains(line); + } + + @Override + public Point getHoverSelectionStart() { + if (!fHoverRange.isEmpty()) { + return fHoverRange.getStart(); + } + return null; + } + + @Override + public Point getHoverSelectionEnd() { + // Note - to match behaviour of getSelectionEnd this method + // returns the inclusive end. As the fHoverRange is exclusive + // we need to decrement the end positions before returning them. + if (!fHoverRange.isEmpty()) { + Point end = fHoverRange.getEnd(); + end.x--; + end.y--; + return end; + } + return null; + } + + @Override + public void expandHoverSelectionAt(final int line, final int col) { + if (fHoverRange.contains(col, line)) { + // position is inside current hover range -> no change + return; + } + fHoverRange = TextRange.EMPTY; + if (line < 0 || line > fSnapshot.getHeight() || col < 0) { + return; + } + int row1 = line; + int row2 = line; + while (row1 > 0 && fSnapshot.isWrappedLine(row1 - 1)) + row1--; + while (row2 < fSnapshot.getHeight() && fSnapshot.isWrappedLine(row2)) + row2++; + row2++; + String lineText = ""; //$NON-NLS-1$ + for (int l = row1; l < row2; l++) { + char[] chars = fSnapshot.getChars(l); + if (chars == null) + return; + lineText += String.valueOf(chars); + } + int width = fSnapshot.getWidth(); + int col1 = col + (line - row1) * width; + if (lineText.length() <= col1 || isBoundaryChar(lineText.charAt(col1))) { + return; + } + int wordStart = 0; + int wordEnd = lineText.length(); + for (int c = col1; c >= 1; c--) { + if (isBoundaryChar(lineText.charAt(c - 1))) { + wordStart = c; + break; + } + } + for (int c = col1; c < lineText.length(); c++) { + if (isBoundaryChar(lineText.charAt(c))) { + wordEnd = c; + break; + } + } + if (wordStart < wordEnd) { + fHoverRange = new TextRange(row1 + wordStart / width, row1 + (wordEnd - 1) / width + 1, (wordStart % width), + (wordEnd - 1) % width + 1, lineText.substring(wordStart, wordEnd)); + if (DEBUG_HOVER) { + System.out.format("hover: %s <- [%s,%s][%s,%s]\n", //$NON-NLS-1$ + fHoverRange, col, line, wordStart, wordEnd); + } + } + } + + @Override + public String getHoverSelectionText() { + return fHoverRange.text; + } + + private boolean isBoundaryChar(char c) { + return Character.isWhitespace(c) || (c < '\u0020') || c == '"' || c == '\''; + } + + // helper to sanitize text copied out of a snapshot + private static String scrubLine(String text) { + // get rid of the empty space at the end of the lines + // text=text.replaceAll("\000+$",""); //$NON-NLS-1$//$NON-NLS-2$ + // + int i = text.length() - 1; + while (i >= 0 && text.charAt(i) == '\000') { + i--; + } + text = text.substring(0, i + 1); + // + // null means space + return text.replace('\000', ' '); + } + + /** + * Calculates the currently selected text + * @return the currently selected text + */ + private String extractSelectedText() { + if (fSelectionStartLine < 0 || fSelectionStartCoumn < 0 || fSelectionSnapshot == null) + return ""; //$NON-NLS-1$ + StringBuffer buffer = new StringBuffer(); + for (int line = fSelectionStartLine; line <= fSeletionEndLine; line++) { + String text; + char[] chars = fSelectionSnapshot.getChars(line); + if (chars != null) { + text = new String(chars); + if (line == fSeletionEndLine && fSelectionEndColumn >= 0) + text = text.substring(0, Math.min(fSelectionEndColumn + 1, text.length())); + if (line == fSelectionStartLine) + text = text.substring(Math.min(fSelectionStartCoumn, text.length())); + text = scrubLine(text); + } else { + text = ""; //$NON-NLS-1$ + } + buffer.append(text); + if (line < fSeletionEndLine && !fSelectionSnapshot.isWrappedLine(line)) + buffer.append('\n'); + } + return buffer.toString(); + } + + private void updateSelection() { + if (fSelectionSnapshot != null && fSelectionSnapshot.isOutOfDate()) { + fSelectionSnapshot.updateSnapshot(true); + // has the selection moved? + if (fSelectionSnapshot != null && fSelectionStartLine >= 0 + && fSelectionSnapshot.getScrollWindowSize() > 0) { + int start = fSelectionStartLine + fSelectionSnapshot.getScrollWindowShift(); + int end = fSeletionEndLine + fSelectionSnapshot.getScrollWindowShift(); + if (start < 0) + if (end >= 0) + start = 0; + else + start = -1; + doSetSelection(start, end, fSelectionStartCoumn, fSelectionEndColumn); + } + // check if the content of the selection has changed. If the content has + // changed, clear the selection + if (fCurrentSelection.length() > 0 && fSelectionSnapshot != null + && fSelectionSnapshot.getFirstChangedLine() <= fSeletionEndLine + && fSelectionSnapshot.getLastChangedLine() >= fSelectionStartLine) { + // has the selected text changed? + if (!fCurrentSelection.equals(extractSelectedText())) { + setSelection(-1, -1, -1, -1); + } + } + // update the observed window... + if (fSelectionSnapshot != null) + // todo make -1 to work! + fSelectionSnapshot.setInterestWindow(0, fSelectionSnapshot.getHeight()); + } + } + + @Override + public String getAllText() { + + // Make a snapshot of the whole text data + ITerminalTextDataSnapshot snapshot = fSnapshot.getTerminalTextData().makeSnapshot(); + snapshot.updateSnapshot(true); + snapshot.detach(); + + // Extract the data + StringBuffer sb = new StringBuffer(); + for (int line = 0; line < snapshot.getHeight(); line++) { + char[] chars = snapshot.getChars(line); + String text; + if (chars != null) { + text = scrubLine(new String(chars)); // take care of NULs + } else { + text = ""; //$NON-NLS-1$ null arrays represent empty lines + } + sb.append(text); + // terminate lines except (1) the last one and (2) wrapped lines + if ((line < snapshot.getHeight() - 1) && !snapshot.isWrappedLine(line)) { + sb.append('\n'); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/GridCanvas.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/GridCanvas.java new file mode 100644 index 00000000000..c727d9910f4 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/GridCanvas.java @@ -0,0 +1,233 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [294468] Fix scroller and text line rendering + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.ScrollBar; + +/** + * A Grid based Canvas. The canvas has rows and columns. + * CellPainting is done with the abstract method drawCell + */ +abstract public class GridCanvas extends VirtualCanvas { + /** width of a cell */ + private int fCellWidth; + /** height of a cell */ + private int fCellHeight; + + public GridCanvas(Composite parent, int style) { + super(parent, style); + addListener(SWT.MouseWheel, event -> { + if (getVerticalBar().isVisible()) { + int delta = -fCellHeight * event.count; + scrollYDelta(delta); + } + event.doit = false; + }); + + } + + /** template method paint. + * iterates over all cells in the clipping rectangle and paints them. + */ + @Override + protected void paint(GC gc) { + Rectangle clipping = gc.getClipping(); + if (clipping.width == 0 || clipping.height == 0) + return; + Rectangle clientArea = getScreenRectInVirtualSpace(); + // Beginning coordinates + int xOffset = clientArea.x; + int yOffset = clientArea.y; + int colFirst = virtualXToCell(xOffset + clipping.x); + if (colFirst > getCols()) + colFirst = getCols(); + else if (colFirst < 0) { + colFirst = 0; + } + int rowFirst = virtualYToCell(yOffset + clipping.y); + // End coordinates + int colLast = virtualXToCell(xOffset + clipping.x + clipping.width + fCellWidth); + if (colLast > getCols()) + colLast = getCols(); + int rowLast = virtualYToCell(yOffset + clipping.y + clipping.height + fCellHeight); + if (rowLast > getRows()) + rowLast = getRows(); + // System.out.println(rowFirst+"->"+rowLast+" "+System.currentTimeMillis()); + // draw the cells + for (int row = rowFirst; row <= rowLast; row++) { + int cx = colFirst * fCellWidth - xOffset; + int cy = row * fCellHeight - yOffset; + drawLine(gc, row, cx, cy, colFirst, colLast); + } + paintUnoccupiedSpace(gc, clipping); + } + + /** + * @param gc + * @param row the line to draw + * @param x coordinate on screen + * @param y coordinate on screen + * @param colFirst first column to draw + * @param colLast last column to draw + */ + abstract void drawLine(GC gc, int row, int x, int y, int colFirst, int colLast); + + abstract protected int getRows(); + + abstract protected int getCols(); + + protected void setCellWidth(int cellWidth) { + fCellWidth = cellWidth; + getHorizontalBar().setIncrement(fCellWidth); + } + + public int getCellWidth() { + return fCellWidth; + } + + protected void setCellHeight(int cellHeight) { + fCellHeight = cellHeight; + getVerticalBar().setIncrement(fCellHeight); + } + + public int getCellHeight() { + return fCellHeight; + } + + int virtualXToCell(int x) { + return x / fCellWidth; + } + + int virtualYToCell(int y) { + return y / fCellHeight; + } + + protected Point screenPointToCell(int x, int y) { + x = screenXtoVirtual(x) / fCellWidth; + y = screenYtoVirtual(y) / fCellHeight; + return new Point(x, y); + } + + Point screenPointToCell(Point point) { + return screenPointToCell(point.x, point.y); + } + + protected Point cellToOriginOnScreen(int x, int y) { + x = virtualXtoScreen(fCellWidth * x); + y = virtualYtoScreen(fCellHeight * y); + return new Point(x, y); + } + + Point cellToOriginOnScreen(Point cell) { + return cellToOriginOnScreen(cell.x, cell.y); + } + + Rectangle getCellScreenRect(Point cell) { + return getCellScreenRect(cell.x, cell.y); + } + + Rectangle getCellScreenRect(int x, int y) { + x = fCellWidth * virtualXtoScreen(x); + y = fCellHeight * virtualYtoScreen(y); + return new Rectangle(x, y, fCellWidth, fCellHeight); + } + + protected Rectangle getCellVirtualRect(Point cell) { + return getCellVirtualRect(cell.x, cell.y); + } + + Rectangle getCellVirtualRect(int x, int y) { + x = fCellWidth * x; + y = fCellHeight * y; + return new Rectangle(x, y, fCellWidth, fCellHeight); + } + + @Override + protected void viewRectangleChanged(int x, int y, int width, int height) { + int cellX = virtualXToCell(x); + int cellY = virtualYToCell(y); + // End coordinates + int xE = virtualXToCell(x + width); + // if(xE>getCols()) + // xE=getCols(); + int yE = virtualYToCell(y + height); + // if(yE>getRows()) + // yE=getRows(); + visibleCellRectangleChanged(cellX, cellY, xE - cellX, yE - cellY); + } + + /** + * Called when the viewed part has changed. + * Override when you need this information.... + * Is only called if the values change (well, almost) + * @param x origin of visible cells + * @param y origin of visible cells + * @param width number of cells visible in x direction + * @param height number of cells visible in y direction + */ + protected void visibleCellRectangleChanged(int x, int y, int width, int height) { + } + + @Override + protected void setVirtualExtend(int width, int height) { + int cellHeight = getCellHeight(); + if (cellHeight > 0) { + height -= height % cellHeight; + } + super.setVirtualExtend(width, height); + } + + @Override + protected void setVirtualOrigin(int x, int y) { + int cellHeight = getCellHeight(); + if (cellHeight > 0) { + int remainder = y % cellHeight; + if (remainder < 0) { + y -= (cellHeight + remainder); + } else { + y -= remainder; + } + } + super.setVirtualOrigin(x, y); + } + + @Override + protected void scrollY(ScrollBar vBar) { + int vSelection = vBar.getSelection(); + Rectangle bounds = getVirtualBounds(); + int y = -vSelection; + int cellHeight = getCellHeight(); + if (cellHeight > 0) { + int remainder = y % cellHeight; + if (remainder < 0) { + y -= (cellHeight + remainder); + } else { + y -= remainder; + } + } + int deltaY = y - bounds.y; + if (deltaY != 0) { + scrollSmart(0, deltaY); + setVirtualOrigin(bounds.x, bounds.y += deltaY); + } + if (-bounds.y + getRows() * getCellHeight() >= bounds.height) { + // scrolled to bottom - need to redraw bottom area + Rectangle clientRect = getClientArea(); + redraw(0, clientRect.height - fCellHeight, clientRect.width, fCellHeight, false); + } + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ILinelRenderer.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ILinelRenderer.java new file mode 100644 index 00000000000..a02b67fcfa9 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ILinelRenderer.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [294468] Fix scroller and text line rendering + * Martin Oberhuber (Wind River) - [265352][api] Allow setting fonts programmatically + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import java.util.Map; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.terminal.model.TerminalColor; + +/** + * + */ +public interface ILinelRenderer { + int getCellWidth(); + + int getCellHeight(); + + void drawLine(ITextCanvasModel model, GC gc, int line, int x, int y, int colFirst, int colLast); + + /** + * Update for a font change from the global JFace Registry. + * @deprecated Use {@link #updateFont(String)} + */ + @Deprecated + void onFontChange(); + + /** + * Set a new font + * @param fontName Jface name of the new font + * @since 3.2 + */ + void updateFont(String fontName); + + void updateColors(Map map); + + void setInvertedColors(boolean invert); + + boolean isInvertedColors(); + + /** + * @deprecated use {@link #getDefaultBackgroundColor(Device)} + */ + @Deprecated + Color getDefaultBackgroundColor(); + + Color getDefaultBackgroundColor(Device device); + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModel.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModel.java new file mode 100644 index 00000000000..01beac0789d --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModel.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; + +public interface ITextCanvasModel { + void addCellCanvasModelListener(ITextCanvasModelListener listener); + + void removeCellCanvasModelListener(ITextCanvasModelListener listener); + + ITerminalTextDataReadOnly getTerminalText(); + + /** + * This is is + * @param startLine + * @param startCol + * @param height + * @param width + */ + void setVisibleRectangle(int startLine, int startCol, int height, int width); + + /** + * @return true when the cursor is shown (used for blinking cursors) + */ + boolean isCursorOn(); + + /** + * Show/Hide the cursor. + * @param visible + */ + void setCursorEnabled(boolean visible); + + /** + * @return true if the cursor is shown. + */ + boolean isCursorEnabled(); + + /** + * @return the line of the cursor + */ + int getCursorLine(); + + /** + * @return the column of the cursor + */ + int getCursorColumn(); + + /** + * @return the start of the selection or null if nothing is selected + * {@link Point#x} is the column and {@link Point#y} is the line. + */ + Point getSelectionStart(); + + /** + * @return the end of the selection or null if nothing is selected + * {@link Point#x} is the column and {@link Point#y} is the line. + */ + Point getSelectionEnd(); + + Point getSelectionAnchor(); + + void setSelectionAnchor(Point anchor); + + /** + * Sets the selection. A negative startLine clears the selection. + * @param startLine + * @param endLine + * @param startColumn + * @param endColumn + */ + void setSelection(int startLine, int endLine, int startColumn, int endColumn); + + /** + * @param line + * @return true if line is part of the selection + */ + boolean hasLineSelection(int line); + + String getSelectedText(); + + /** + * Expand the hover selection to the word at the given position. + * + * @param line line + * @param col column + */ + void expandHoverSelectionAt(int line, int col); + + /** + * @param line + * @return true if line is part of the hover selection + */ + boolean hasHoverSelection(int line); + + /** + * Get the text of the current hover selection. + * + * @return the hover selection text, never null. + */ + String getHoverSelectionText(); + + /** + * Get the start of the hover selection. + * + * @return the start of the hover selection or null if nothing is selected + * {@link Point#x} is the column and {@link Point#y} is the line. + * Returns non-null if {@link #hasHoverSelection(int)} returns true + */ + Point getHoverSelectionStart(); + + /** + * Get the end of the hover selection (inclusive). + * + * @return the end of the hover selection or null if nothing is selected + * {@link Point#x} is the column and {@link Point#y} is the line. + * Returns non-null if {@link #hasHoverSelection(int)} returns true + */ + Point getHoverSelectionEnd(); + + /** + * Collect and return all text present in the model. + * + *

            Individual lines of the returned text are separated by '\n'. + * + *

            The method is primarily designed for test automation. + * + * @since 4.4 + */ + String getAllText(); + +} \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModelListener.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModelListener.java new file mode 100644 index 00000000000..4d6251844ca --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/ITextCanvasModelListener.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +/** + */ +public interface ITextCanvasModelListener { + void rangeChanged(int col, int line, int width, int height); + + void dimensionsChanged(int cols, int rows); + + /** + * Called when any text change happened. Used to scroll to the + * end of text in auto scroll mode. This does not get fired + * when the window of interest has changed! + */ + void terminalDataChanged(); +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/PipedInputStream.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/PipedInputStream.java new file mode 100644 index 00000000000..29c00356cbf --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/PipedInputStream.java @@ -0,0 +1,335 @@ +/******************************************************************************* + * Copyright (c) 1996, 2011 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Douglas Lea (Addison Wesley) - [cq:1552] BoundedBufferWithStateTracking adapted to BoundedByteBuffer + * Martin Oberhuber (Wind River) - the waitForAvailable method + * Martin Oberhuber (Wind River) - [208166] Avoid unnecessary arraycopy in BoundedByteBuffer + * Pawel Piech (Wind River) - [333613] "Job found still running" after shutdown + *******************************************************************************/ + +package org.eclipse.tm.internal.terminal.textcanvas; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * The main purpose of this class is to start a runnable in the + * display thread when data is available and to pretend no data + * is available after a given amount of time the runnable is running. + * + */ +public class PipedInputStream extends InputStream { + /** + * The output stream used by the terminal backend to write to the terminal + */ + protected final OutputStream fOutputStream; + /** + * A blocking byte queue. + */ + private final BoundedByteBuffer fQueue; + + /** + * A byte bounded buffer used to synchronize the input and the output stream. + *

            + * Adapted from BoundedBufferWithStateTracking + * http://gee.cs.oswego.edu/dl/cpj/allcode.java + * http://gee.cs.oswego.edu/dl/cpj/ + *

            + * BoundedBufferWithStateTracking is part of the examples for the book + * Concurrent Programming in Java: Design Principles and Patterns by + * Doug Lea (ISBN 0-201-31009-0). Second edition published by + * Addison-Wesley, November 1999. The code is + * Copyright(c) Douglas Lea 1996, 1999 and released to the public domain + * and may be used for any purposes whatsoever. + *

            + * For some reasons a solution based on + * PipedOutputStream/PipedIntputStream + * does work *very* slowly: + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4404700 + *

            + * + */ + private class BoundedByteBuffer { + protected final byte[] fBuffer; // the elements + protected int fPutPos = 0; // circular indices + protected int fTakePos = 0; + protected int fUsedSlots = 0; // the count + private boolean fClosed; + + public BoundedByteBuffer(int capacity) throws IllegalArgumentException { + // make sure we don't deadlock on too small capacity + if (capacity <= 0) + throw new IllegalArgumentException(); + fBuffer = new byte[capacity]; + } + + /** + * @return the bytes available for {@link #read()} + * Must be called with a lock on this! + */ + public int available() { + return fUsedSlots; + } + + /** + * Writes a single byte to the buffer. Blocks if the buffer is full. + * @param b byte to write to the buffer + * @throws InterruptedException when the thread is interrupted while waiting + * for the buffer to become ready + * Must be called with a lock on this! + */ + public void write(byte b) throws InterruptedException { + while (fUsedSlots == fBuffer.length) + // wait until not full + wait(); + + fBuffer[fPutPos] = b; + fPutPos = (fPutPos + 1) % fBuffer.length; // cyclically increment + + if (fUsedSlots++ == 0) // signal if was empty + notifyAll(); + } + + public int getFreeSlots() { + return fBuffer.length - fUsedSlots; + } + + public void write(byte[] b, int off, int len) throws InterruptedException { + assert len <= getFreeSlots(); + while (fUsedSlots == fBuffer.length) + // wait until not full + wait(); + int n = Math.min(len, fBuffer.length - fPutPos); + System.arraycopy(b, off, fBuffer, fPutPos, n); + if (fPutPos + len > fBuffer.length) + System.arraycopy(b, off + n, fBuffer, 0, len - n); + fPutPos = (fPutPos + len) % fBuffer.length; // cyclically increment + boolean wasEmpty = fUsedSlots == 0; + fUsedSlots += len; + if (wasEmpty) // signal if was empty + notifyAll(); + } + + /** + * Read a single byte. Blocks until a byte is available. + * @return a byte from the buffer + * @throws InterruptedException when the thread is interrupted while waiting + * for the buffer to become ready + * Must be called with a lock on this! + */ + public int read() throws InterruptedException { + while (fUsedSlots == 0) { + if (fClosed) + return -1; + // wait until not empty + wait(); + } + byte b = fBuffer[fTakePos]; + fTakePos = (fTakePos + 1) % fBuffer.length; + + if (fUsedSlots-- == fBuffer.length) // signal if was full + notifyAll(); + return b; + } + + public int read(byte[] cbuf, int off, int len) throws InterruptedException { + assert len <= available(); + while (fUsedSlots == 0) { + if (fClosed) + return 0; + // wait until not empty + wait(); + } + int n = Math.min(len, fBuffer.length - fTakePos); + System.arraycopy(fBuffer, fTakePos, cbuf, off, n); + if (fTakePos + len > n) + System.arraycopy(fBuffer, 0, cbuf, off + n, len - n); + fTakePos = (fTakePos + len) % fBuffer.length; + boolean wasFull = fUsedSlots == fBuffer.length; + fUsedSlots -= len; + if (wasFull) + notifyAll(); + + return len; + } + + public void close() { + fClosed = true; + notifyAll(); + } + + public boolean isClosed() { + return fClosed; + } + } + + /** + * An output stream that calls {@link PipedInputStream#textAvailable} + * every time data is written to the stream. The data is written to + * {@link PipedInputStream#fQueue}. + * + */ + class PipedOutputStream extends OutputStream { + @Override + public void write(byte[] b, int off, int len) throws IOException { + try { + synchronized (fQueue) { + if (fQueue.isClosed()) + throw new IOException("Stream is closed!"); //$NON-NLS-1$ + int written = 0; + while (written < len) { + if (fQueue.getFreeSlots() == 0) { + // if no slots available, write one byte and block + // until free slots are available + fQueue.write(b[off + written]); + written++; + } else { + // if slots are available, write as much as + // we can in one junk + int n = Math.min(fQueue.getFreeSlots(), len - written); + fQueue.write(b, off + written, n); + written += n; + } + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + @Override + public void write(int b) throws IOException { + try { + synchronized (fQueue) { + if (fQueue.isClosed()) + throw new IOException("Stream is closed!"); //$NON-NLS-1$ + fQueue.write((byte) b); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + @Override + public void close() throws IOException { + synchronized (fQueue) { + fQueue.close(); + } + } + } + + /** + * @param bufferSize the size of the buffer of the output stream + */ + public PipedInputStream(int bufferSize) { + fOutputStream = new PipedOutputStream(); + fQueue = new BoundedByteBuffer(bufferSize); + } + + /** + * @return the output stream used by the backend to write to the terminal. + */ + public OutputStream getOutputStream() { + return fOutputStream; + } + + /** + * Waits until data is available for reading. + * @param millis see {@link Object#wait(long)} + * @throws InterruptedException when the thread is interrupted while waiting + * for the buffer to become ready + */ + public void waitForAvailable(long millis) throws InterruptedException { + synchronized (fQueue) { + if (fQueue.available() == 0 && !fQueue.fClosed) + fQueue.wait(millis); + } + } + + /** + * Must be called in the Display Thread! + * @return number of characters available for reading. + */ + @Override + public int available() { + synchronized (fQueue) { + return fQueue.available(); + } + } + + /** + * @return the next available byte. Check with {@link #available} + * if characters are available. + */ + @Override + public int read() throws IOException { + try { + synchronized (fQueue) { + return fQueue.read(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return -1; + } + } + + /** + * Closing a PipedInputStream is the same as closing the output stream. + * The stream will allow reading data that's still in the pipe after which it will + * throw an IOException. + */ + @Override + public void close() throws IOException { + synchronized (fQueue) { + fQueue.close(); + } + } + + @Override + public int read(byte[] cbuf, int off, int len) throws IOException { + int n = 0; + if (len == 0) + return 0; + // read as much as we can using a single synchronized statement + try { + synchronized (fQueue) { + // if nothing available, block and read one byte + if (fQueue.available() == 0) { + // block now until at least one byte is available + int c = fQueue.read(); + // are we at the end of stream + if (c == -1) + return -1; + cbuf[off] = (byte) c; + n++; + } + // is there more data available? + if (n < len && fQueue.available() > 0) { + // read at most available() + int nn = Math.min(fQueue.available(), len - n); + // are we at the end of the stream? + if (nn == 0 && fQueue.isClosed()) { + // if no byte was read, return -1 to indicate end of stream + // else return the bytes we read up to now + if (n == 0) + n = -1; + return n; + } + fQueue.read(cbuf, off + n, nn); + n += nn; + } + + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return n; + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/PollingTextCanvasModel.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/PollingTextCanvasModel.java new file mode 100644 index 00000000000..d2fe910e5de --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/PollingTextCanvasModel.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [420928] Terminal widget leaks memory + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import org.eclipse.swt.widgets.Display; +import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; + +/** + * @author Michael.Scharf@scharf-software.com + * + */ +public class PollingTextCanvasModel extends AbstractTextCanvasModel { + private static final int DEFAULT_POLL_INTERVAL = 50; + int fPollInterval = -1; + + /** + * + */ + public PollingTextCanvasModel(ITerminalTextDataSnapshot snapshot) { + super(snapshot); + startPolling(); + } + + public void setUpdateInterval(int t) { + fPollInterval = t; + } + + public void stopPolling() { + // timerExec only dispatches if the delay is >=0 + fPollInterval = -1; + } + + public void startPolling() { + if (fPollInterval < 0) { + fPollInterval = DEFAULT_POLL_INTERVAL; + Display.getDefault().timerExec(fPollInterval, new Runnable() { + @Override + public void run() { + update(); + Display.getDefault().timerExec(fPollInterval, this); + } + }); + } + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/StyleMap.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/StyleMap.java new file mode 100644 index 00000000000..330070cdafb --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/StyleMap.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Michael Scharf (Wind River) - [205260] Terminal does not take the font from the preferences + * Michael Scharf (Wind River) - [209746] There are cases where some colors not displayed correctly + * Michael Scharf (Wind River) - [206328] Terminal does not draw correctly with proportional fonts + * Martin Oberhuber (Wind River) - [247700] Terminal uses ugly fonts in JEE package + * Martin Oberhuber (Wind River) - [335358] Fix Terminal color definition + * Martin Oberhuber (Wind River) - [265352][api] Allow setting fonts programmatically + * Martin Oberhuber (Wind River) - [475422] Fix display on MacOSX Retina + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import java.util.EnumMap; +import java.util.Map; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tm.internal.terminal.preferences.ITerminalConstants; +import org.eclipse.tm.internal.terminal.preferences.TerminalColorPresets; +import org.eclipse.tm.terminal.model.TerminalColor; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * The split between responsibilities of StyleMap and TerminalStyle are not always clear. Generally + * the style parts that are global for a terminal are here, where as in TerminalStyle is about + * a specific range. + */ +public class StyleMap { + + String fFontName = ITerminalConstants.FONT_DEFINITION; + private Point fCharSize; + private final TerminalStyle fDefaultStyle; + private boolean fInvertColors; + private boolean fProportional; + private final int[] fOffsets = new int[256]; + private final Map fColorMap = new EnumMap<>(TerminalColor.class); + + public StyleMap() { + fDefaultStyle = TerminalStyle.getDefaultStyle(); + initFont(); + initColors(); + } + + private void initColors() { + Map map = new EnumMap<>(TerminalColor.class); + TerminalColor[] values = TerminalColor.values(); + for (TerminalColor terminalColor : values) { + RGB rgb = TerminalColorPresets.INSTANCE.getDefaultPreset().getRGB(terminalColor); + map.put(terminalColor, rgb); + } + updateColors(map); + } + + private void initFont() { + updateFont(ITerminalConstants.FONT_DEFINITION); + } + + private RGB getRGB(TerminalColor color) { + return fColorMap.get(color); + } + + public RGB getForegrondRGB(TerminalStyle style) { + style = defaultIfNull(style); + RGB foregroundRGB; + if (style.isReverse()) { + foregroundRGB = style.getBackgroundRGB(); + } else { + foregroundRGB = style.getForegroundRGB(); + } + if (foregroundRGB != null) { + return foregroundRGB; + } + + TerminalColor color; + if (style.isReverse()) { + color = style.getBackgroundTerminalColor(); + } else { + color = style.getForegroundTerminalColor(); + } + + if (color == null) { + color = TerminalColor.FOREGROUND; + } + + color = color.convertColor(fInvertColors, style.isBold()); + return getRGB(color); + } + + public RGB getBackgroundRGB(TerminalStyle style) { + style = defaultIfNull(style); + RGB backgroundRGB; + if (style.isReverse()) { + backgroundRGB = style.getForegroundRGB(); + } else { + backgroundRGB = style.getBackgroundRGB(); + } + if (backgroundRGB != null) { + return backgroundRGB; + } + + TerminalColor color; + if (style.isReverse()) { + color = style.getForegroundTerminalColor(); + } else { + color = style.getBackgroundTerminalColor(); + } + + if (color == null) { + color = TerminalColor.BACKGROUND; + } + + color = color.convertColor(fInvertColors, style.isBold()); + return getRGB(color); + } + + private TerminalStyle defaultIfNull(TerminalStyle style) { + if (style == null) + style = fDefaultStyle; + return style; + } + + public void setInvertedColors(boolean invert) { + fInvertColors = invert; + } + + public boolean isInvertedColors() { + return fInvertColors; + } + + public Font getFont(TerminalStyle style) { + style = defaultIfNull(style); + if (style.isBold()) { + return JFaceResources.getFontRegistry().getBold(fFontName); + } else if (style.isUnderline()) { + return JFaceResources.getFontRegistry().getItalic(fFontName); + + } + return JFaceResources.getFontRegistry().get(fFontName); + } + + public Font getFont() { + return JFaceResources.getFontRegistry().get(fFontName); + } + + public int getFontWidth() { + return fCharSize.x; + } + + public int getFontHeight() { + return fCharSize.y; + } + + /** + * @deprecated Use {@link #updateFont(String)} + */ + @Deprecated + public void updateFont() { + updateFont(ITerminalConstants.FONT_DEFINITION); + } + + /** + * Update the StyleMap for a new font name. + * The font name must be a valid name in the Jface font registry. + * @param fontName Jface name of the new font to use. + * @since 3.2 + */ + public void updateFont(String fontName) { + Display display = Display.getCurrent(); + GC gc = new GC(display); + if (JFaceResources.getFontRegistry().hasValueFor(fontName)) { + fFontName = fontName; + } else { + //fall back to "basic jface text font" + fFontName = "org.eclipse.jface.textfont"; //$NON-NLS-1$ + } + gc.setFont(getFont()); + fCharSize = gc.textExtent("W"); //$NON-NLS-1$ + fProportional = false; + + for (char c = ' '; c <= '~'; c++) { + // consider only the first 128 chars for deciding if a font + // is proportional. Collect char width as a side-effect. + if (measureChar(gc, c, true)) + fProportional = true; + } + if (fProportional) { + // Widest char minus the padding on the left and right: + // Looks much better for small fonts + fCharSize.x -= 2; + // Collect width of the upper characters (for offset calculation) + for (char c = '~' + 1; c < fOffsets.length; c++) { + measureChar(gc, c, false); + } + // Calculate offsets based on each character's width and the bounding box + for (int i = ' '; i < fOffsets.length; i++) { + fOffsets[i] = (fCharSize.x - fOffsets[i]) / 2; + } + } else { + // Non-Proportional: Reset all offsets (eg after font change) + for (int i = 0; i < fOffsets.length; i++) { + fOffsets[i] = 0; + } + String t = "The quick brown Fox jumps over the Lazy Dog."; //$NON-NLS-1$ + Point ext = gc.textExtent(t); + if (ext.x != fCharSize.x * t.length()) { + //Bug 475422: On OSX with Retina display and due to scaling, + //a text many be shorter than the sum of its bounding boxes. + //Because even with fixed width font, bounding box size + //may not be an integer but a fraction eg 6.75 pixels. + // + //Painting in proportional mode ensures that each character + //is painted individually into its proper bounding box, rather + //than using an optimization where Strings would be drawn as + //a whole. This fixes the "fractional bounding box" problem. + fProportional = true; + } + //measure font in boldface, too, and if wider then treat like proportional + gc.setFont(getFont(fDefaultStyle.setBold(true))); + Point charSizeBold = gc.textExtent("W"); //$NON-NLS-1$ + if (fCharSize.x != charSizeBold.x) { + fProportional = true; + } + } + gc.dispose(); + } + + /** + * @param gc + * @param c + * @param updateMax + * @return true if the the font is proportional + */ + private boolean measureChar(GC gc, char c, boolean updateMax) { + boolean proportional = false; + Point ext = gc.textExtent(String.valueOf(c)); + if (ext.x > 0 && ext.y > 0 && (fCharSize.x != ext.x || fCharSize.y != ext.y)) { + proportional = true; + if (updateMax) { + fCharSize.x = Math.max(fCharSize.x, ext.x); + fCharSize.y = Math.max(fCharSize.y, ext.y); + } + } + fOffsets[c] = ext.x; + return proportional; + } + + public boolean isFontProportional() { + return fProportional; + } + + /** + * Return the offset in pixels required to center a given character + * @param c the character to measure + * @return the offset in x direction to center this character + */ + public int getCharOffset(char c) { + if (c >= fOffsets.length) + return 0; + return fOffsets[c]; + } + + public void updateColors(Map colorMap) { + fColorMap.putAll(colorMap); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextCanvas.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextCanvas.java new file mode 100644 index 00000000000..0d7e8bda732 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextCanvas.java @@ -0,0 +1,572 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Michael Scharf (Wind River) - [240098] The cursor should not blink when the terminal is disconnected + * Uwe Stieber (Wind River) - [281328] The very first few characters might be missing in the terminal control if opened and connected programmatically + * Martin Oberhuber (Wind River) - [294327] After logging in, the remote prompt is hidden + * Anton Leherbauer (Wind River) - [294468] Fix scroller and text line rendering + * Uwe Stieber (Wind River) - [205486] Fix ScrollLock always moving to line 1 + * Anton Leherbauer (Wind River) - [219589] Copy an entire line selection + * Anton Leherbauer (Wind River) - [196465] Resizing Terminal changes Scroller location + * Anton Leherbauer (Wind River) - [324608] Terminal has strange scrolling behaviour + * Martin Oberhuber (Wind River) - [265352][api] Allow setting fonts programmatically + * Anton Leherbauer (Wind River) - [434749] UnhandledEventLoopException when copying to clipboard while the selection is empty + * Davy Landman (CWI) - [475267][api] Allow custom mouse listeners + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tm.internal.terminal.control.ITerminalMouseListener; +import org.eclipse.tm.internal.terminal.control.ITerminalMouseListener2; +import org.eclipse.tm.terminal.model.TerminalColor; + +/** + * A cell oriented Canvas. Maintains a list of "cells". + * It can either be vertically or horizontally scrolled. + * The CellRenderer is responsible for painting the cell. + */ +public class TextCanvas extends GridCanvas { + protected final ITextCanvasModel fCellCanvasModel; + /** Renders the cells */ + private final ILinelRenderer fCellRenderer; + private boolean fScrollLock; + private Point fDraggingStart; + private Point fDraggingEnd; + private boolean fHasSelection; + private ResizeListener fResizeListener; + private final List fMouseListeners; + + // The minSize is meant to determine the minimum size of the backing store + // (grid) into which remote data is rendered. If the viewport is smaller + // than that minimum size, the backing store size remains at the minSize, + // and a scrollbar is shown instead. In reality, this has the following + // issues or effects today: + // (a) Bug 281328: For very early data coming in before the widget is + // realized, the minSize determines into what initial grid that is + // rendered. See also @link{#addResizeHandler(ResizeListener)}. + // (b) Bug 294468: Since we have redraw and size computation problems + // with horizontal scrollers, for now the minColumns must be small + // enough to avoid a horizontal scroller appearing in most cases. + // (b) Bug 294327: since we have problems with the vertical scroller + // showing the correct location, minLines must be small enough + // to avoid a vertical scroller or new data may be rendered off-screen. + // As a compromise, we have been working with a 20x4 since the Terminal + // inception, though many users would want a 80x24 minSize and backing + // store. Pros and cons of the small minsize: + // + consistent "remote size==viewport size", vi works as expected + // - dumb terminals which expect 80x24 render garbled on small viewport. + // If bug 294468 were resolved, an 80 wide minSize would be preferrable + // since it allows switching the terminal viewport small/large as needed, + // without destroying the backing store. For a complete solution, + // Bug 196462 tracks the request for a user-defined fixed-widow-size-mode. + private int fMinColumns = 80; + private int fMinLines = 4; + private boolean fCursorEnabled; + private boolean fResizing; + + /** + * Create a new CellCanvas with the given SWT style bits. + * (SWT.H_SCROLL and SWT.V_SCROLL are automatically added). + */ + public TextCanvas(Composite parent, ITextCanvasModel model, int style, ILinelRenderer cellRenderer) { + super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL); + fCellRenderer = cellRenderer; + setCellWidth(fCellRenderer.getCellWidth()); + setCellHeight(fCellRenderer.getCellHeight()); + fCellCanvasModel = model; + fCellCanvasModel.addCellCanvasModelListener(new ITextCanvasModelListener() { + @Override + public void rangeChanged(int col, int line, int width, int height) { + if (isDisposed()) + return; + repaintRange(col, line, width, height); + } + + @Override + public void dimensionsChanged(int cols, int rows) { + if (isDisposed()) + return; + calculateGrid(); + } + + @Override + public void terminalDataChanged() { + if (isDisposed()) + return; + + // scroll to end (unless scroll lock is active) + if (!fResizing) { + calculateGrid(); + scrollToEnd(); + } + } + }); + // let the cursor blink if the text canvas gets the focus... + addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + fCellCanvasModel.setCursorEnabled(fCursorEnabled); + } + + @Override + public void focusLost(FocusEvent e) { + fCellCanvasModel.setCursorEnabled(false); + } + }); + fMouseListeners = new ArrayList<>(); + addMouseListener(new MouseListener() { + @Override + public void mouseDoubleClick(MouseEvent e) { + if (fMouseListeners.size() > 0) { + Point pt = screenPointToCell(e.x, e.y); + if (pt != null) { + for (ITerminalMouseListener l : fMouseListeners) { + if (l instanceof ITerminalMouseListener2) { + ITerminalMouseListener2 l2 = (ITerminalMouseListener2) l; + l2.mouseDoubleClick(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button, + e.stateMask); + } else { + l.mouseDoubleClick(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + } + } + } + } + } + + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 1) { // left button + fDraggingStart = screenPointToCell(e.x, e.y); + fHasSelection = false; + if ((e.stateMask & SWT.SHIFT) != 0) { + Point anchor = fCellCanvasModel.getSelectionAnchor(); + if (anchor != null) + fDraggingStart = anchor; + } else { + fCellCanvasModel.setSelectionAnchor(fDraggingStart); + } + fDraggingEnd = null; + } + if (fMouseListeners.size() > 0) { + Point pt = screenPointToCell(e.x, e.y); + if (pt != null) { + for (ITerminalMouseListener l : fMouseListeners) { + if (l instanceof ITerminalMouseListener2) { + ITerminalMouseListener2 l2 = (ITerminalMouseListener2) l; + l2.mouseDown(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button, e.stateMask); + } else { + l.mouseDown(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + } + } + } + } + } + + @Override + public void mouseUp(MouseEvent e) { + if (e.button == 1) { // left button + updateHasSelection(e); + if (fHasSelection) + setSelection(screenPointToCell(e.x, e.y)); + else + fCellCanvasModel.setSelection(-1, -1, -1, -1); + fDraggingStart = null; + } + if (fMouseListeners.size() > 0) { + Point pt = screenPointToCell(e.x, e.y); + if (pt != null) { + for (ITerminalMouseListener l : fMouseListeners) { + if (l instanceof ITerminalMouseListener2) { + ITerminalMouseListener2 l2 = (ITerminalMouseListener2) l; + l2.mouseUp(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button, e.stateMask); + } else { + l.mouseUp(fCellCanvasModel.getTerminalText(), pt.y, pt.x, e.button); + } + } + } + } + } + }); + addMouseMoveListener(e -> { + if (fDraggingStart != null) { + updateHasSelection(e); + setSelection(screenPointToCell(e.x, e.y)); + fCellCanvasModel.expandHoverSelectionAt(-1, -1); + } else if ((e.stateMask & SWT.MODIFIER_MASK) == SWT.MOD1) { + // highlight (underline) word that would be used by MOD1 + mouse click + Point pt = screenPointToCell(e.x, e.y); + fCellCanvasModel.expandHoverSelectionAt(pt.y, pt.x); + } else { + fCellCanvasModel.expandHoverSelectionAt(-1, -1); + } + redraw(); + }); + serVerticalBarVisible(true); + setHorizontalBarVisible(false); + } + + /** + * The user has to drag the mouse to at least one character to make a selection. + * Once this is done, even a one char selection is OK. + * + * @param e + */ + private void updateHasSelection(MouseEvent e) { + if (fDraggingStart != null) { + Point p = screenPointToCell(e.x, e.y); + if (fDraggingStart.x != p.x || fDraggingStart.y != p.y) + fHasSelection = true; + } + } + + void setSelection(Point p) { + if (fDraggingStart != null && !p.equals(fDraggingEnd)) { + fDraggingEnd = p; + if (compare(p, fDraggingStart) < 0) { + // bug 219589 - make sure selection start coordinates are non-negative + int startColumn = Math.max(0, p.x); + int startRow = Math.max(p.y, 0); + fCellCanvasModel.setSelection(startRow, fDraggingStart.y, startColumn, fDraggingStart.x); + } else { + fCellCanvasModel.setSelection(fDraggingStart.y, p.y, fDraggingStart.x, p.x); + + } + } + } + + int compare(Point p1, Point p2) { + if (p1.equals(p2)) + return 0; + if (p1.y == p2.y) { + if (p1.x > p2.x) + return 1; + else + return -1; + } + if (p1.y > p2.y) { + return 1; + } else { + return -1; + } + } + + public ILinelRenderer getCellRenderer() { + return fCellRenderer; + } + + public int getMinColumns() { + return fMinColumns; + } + + public void setMinColumns(int minColumns) { + fMinColumns = minColumns; + } + + public int getMinLines() { + return fMinLines; + } + + public void setMinLines(int minLines) { + fMinLines = minLines; + } + + protected void onResize(boolean init) { + if (fResizeListener != null) { + Rectangle bonds = getClientArea(); + int cellHeight = getCellHeight(); + int cellWidth = getCellWidth(); + int lines = bonds.height / cellHeight; + int columns = bonds.width / cellWidth; + // when the view is minimised, its size is set to 0 + // we don't sent this to the terminal! + if ((lines > 0 && columns > 0) || init) { + if (columns < fMinColumns) { + if (!isHorizontalBarVisble()) { + setHorizontalBarVisible(true); + bonds = getClientArea(); + lines = bonds.height / cellHeight; + } + columns = fMinColumns; + } else if (columns >= fMinColumns && isHorizontalBarVisble()) { + setHorizontalBarVisible(false); + bonds = getClientArea(); + lines = bonds.height / cellHeight; + columns = bonds.width / cellWidth; + } + if (lines < fMinLines) + lines = fMinLines; + fResizeListener.sizeChanged(lines, columns); + } + } + super.onResize(); + calculateGrid(); + } + + @Override + protected void onResize() { + fResizing = true; + try { + onResize(false); + } finally { + fResizing = false; + } + } + + private void calculateGrid() { + Rectangle virtualBounds = getVirtualBounds(); + setRedraw(false); + try { + setVirtualExtend(getCols() * getCellWidth(), getRows() * getCellHeight()); + getParent().layout(); + if (fResizing) { + // scroll to end if view port was near last line + Rectangle viewRect = getViewRectangle(); + if (virtualBounds.height - (viewRect.y + viewRect.height) < getCellHeight() * 2) + scrollToEnd(); + } + } finally { + setRedraw(true); + } + } + + void scrollToEnd() { + if (!fScrollLock) { + int y = -(getRows() * getCellHeight() - getClientArea().height); + if (y > 0) { + y = 0; + } + Rectangle v = getViewRectangle(); + if (v.y != -y) { + setVirtualOrigin(v.x, y); + } + // make sure the scroll area is correct: + scrollY(getVerticalBar()); + scrollX(getHorizontalBar()); + } + } + + /** + * + * @return true if the cursor should be shown on output.... + */ + public boolean isScrollLock() { + return fScrollLock; + } + + /** + * If set then if the size changes + */ + public void setScrollLock(boolean scrollLock) { + fScrollLock = scrollLock; + } + + protected void repaintRange(int col, int line, int width, int height) { + Point origin = cellToOriginOnScreen(col, line); + Rectangle r = new Rectangle(origin.x, origin.y, width * getCellWidth(), height * getCellHeight()); + repaint(r); + } + + @Override + protected void drawLine(GC gc, int line, int x, int y, int colFirst, int colLast) { + fCellRenderer.drawLine(fCellCanvasModel, gc, line, x, y, colFirst, colLast); + } + + @Override + protected Color getTerminalBackgroundColor(Device device) { + return fCellRenderer.getDefaultBackgroundColor(device); + } + + @Override + @Deprecated + protected Color getTerminalBackgroundColor() { + return fCellRenderer.getDefaultBackgroundColor(); + } + + @Override + protected void visibleCellRectangleChanged(int x, int y, int width, int height) { + fCellCanvasModel.setVisibleRectangle(y, x, height, width); + update(); + } + + @Override + protected int getCols() { + return fCellCanvasModel.getTerminalText().getWidth(); + } + + @Override + protected int getRows() { + return fCellCanvasModel.getTerminalText().getHeight(); + } + + public String getSelectionText() { + // TODO -- create a hasSelectionMethod! + return fCellCanvasModel.getSelectedText(); + } + + public void copy() { + String selectionText = getSelectionText(); + if (selectionText != null && selectionText.length() > 0) { + Clipboard clipboard = new Clipboard(getDisplay()); + clipboard.setContents(new Object[] { selectionText }, new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + } + } + + public void selectAll() { + fCellCanvasModel.setSelection(0, fCellCanvasModel.getTerminalText().getHeight(), 0, + fCellCanvasModel.getTerminalText().getWidth()); + fCellCanvasModel.setSelectionAnchor(new Point(0, 0)); + } + + /** + * @since 4.1 + */ + public void clearSelection() { + fCellCanvasModel.setSelection(-1, -1, -1, -1); + } + + /** + * Collect and return all text present in the widget. + * + *

            Individual lines of the returned text are separated by '\n'. + * + *

            The method is primarily designed for test automation. Tests need + * to check what happens in a terminal (e.g. if and how a CDT Debugger + * Console reacts in a GDB session) and this method allows to read the + * text present in the terminal. + * + * @since 4.4 + */ + public String getAllText() { + return fCellCanvasModel.getAllText(); + } + + public boolean isEmpty() { + return false; + } + + /** + * Gets notified when the visible size of the terminal changes. + * This should update the model! + * + */ + public interface ResizeListener { + void sizeChanged(int lines, int columns); + } + + /** + * @param listener this listener gets notified, when the size of + * the widget changed. It should change the dimensions of the underlying + * terminaldata + */ + public void addResizeHandler(ResizeListener listener) { + if (fResizeListener != null) + throw new IllegalArgumentException("There can be at most one listener at the moment!"); //$NON-NLS-1$ + fResizeListener = listener; + + // Bug 281328: [terminal] The very first few characters might be missing in + // the terminal control if opened and connected programmatically + // + // In case the terminal had not been visible yet or is too small (less than one + // line visible), the terminal should have a minimum size to avoid RuntimeExceptions. + Rectangle bonds = getClientArea(); + if (bonds.height < getCellHeight() || bonds.width < getCellWidth()) { + //Widget not realized yet, or minimized to < 1 item: + //Just tell the listener our min size + fResizeListener.sizeChanged(getMinLines(), getMinColumns()); + } else { + //Widget realized: compute actual size and force telling the listener + onResize(true); + } + } + + /** + * Notify about a change of the global Font Preference. + * @deprecated Use {@link #updateFont(String)} + */ + @Deprecated + public void onFontChange() { + fCellRenderer.onFontChange(); + setCellWidth(fCellRenderer.getCellWidth()); + setCellHeight(fCellRenderer.getCellHeight()); + calculateGrid(); + } + + public void updateFont(String fontName) { + fCellRenderer.updateFont(fontName); + setCellWidth(fCellRenderer.getCellWidth()); + setCellHeight(fCellRenderer.getCellHeight()); + calculateGrid(); + } + + public void updateColors(Map map) { + fCellRenderer.updateColors(map); + redraw(); + } + + public void setInvertedColors(boolean invert) { + fCellRenderer.setInvertedColors(invert); + redraw(); + } + + public boolean isInvertedColors() { + return fCellRenderer.isInvertedColors(); + } + + /** + * @return true if the cursor is enabled (blinking). By default the cursor is not enabled. + */ + public boolean isCursorEnabled() { + return fCursorEnabled; + } + + /** + * @param enabled enabling means that the cursor blinks + */ + public void setCursorEnabled(boolean enabled) { + if (enabled != fCursorEnabled) { + fCursorEnabled = enabled; + fCellCanvasModel.setCursorEnabled(fCursorEnabled); + } + + } + + public void addTerminalMouseListener(final ITerminalMouseListener listener) { + fMouseListeners.add(listener); + } + + public void removeTerminalMouseListener(ITerminalMouseListener listener) { + fMouseListeners.remove(listener); + } + + public String getHoverSelection() { + return fCellCanvasModel.getHoverSelectionText(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextLineRenderer.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextLineRenderer.java new file mode 100644 index 00000000000..845ee499581 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/TextLineRenderer.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Michael Scharf (Wind River) - [205260] Terminal does not take the font from the preferences + * Michael Scharf (Wind River) - [206328] Terminal does not draw correctly with proportional fonts + * Anton Leherbauer (Wind River) - [294468] Fix scroller and text line rendering + * Martin Oberhuber (Wind River) - [265352][api] Allow setting fonts programmatically + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import java.util.Map; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; +import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; +import org.eclipse.tm.terminal.model.LineSegment; +import org.eclipse.tm.terminal.model.TerminalColor; +import org.eclipse.tm.terminal.model.TerminalStyle; + +/** + * + */ +public class TextLineRenderer implements ILinelRenderer { + private static final boolean DEBUG_HOVER = TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_HOVER); + private final ITextCanvasModel fModel; + private final StyleMap fStyleMap; + + public TextLineRenderer(TextCanvas c, ITextCanvasModel model) { + fModel = model; + fStyleMap = new StyleMap(); + } + + @Override + public int getCellWidth() { + return fStyleMap.getFontWidth(); + } + + @Override + public int getCellHeight() { + return fStyleMap.getFontHeight(); + } + + @Override + public void drawLine(ITextCanvasModel model, GC gc, int line, int x, int y, int colFirst, int colLast) { + int width = getCellWidth() * (colLast - colFirst); + int height = getCellHeight(); + if (width <= 0 || height <= 0) { + return; + } + Image buffer = new Image(gc.getDevice(), width, height); + GC doubleBufferGC = new GC(buffer); + if (line < 0 || line >= getTerminalText().getHeight() || colFirst >= getTerminalText().getWidth() + || colFirst - colLast == 0) { + fillBackground(doubleBufferGC, 0, 0, width, height); + } else { + colLast = Math.min(colLast, getTerminalText().getWidth()); + LineSegment[] segments = getTerminalText().getLineSegments(line, colFirst, colLast - colFirst); + for (int i = 0; i < segments.length; i++) { + LineSegment segment = segments[i]; + TerminalStyle style = segment.getStyle(); + setupGC(doubleBufferGC, style); + String text = segment.getText(); + drawText(doubleBufferGC, 0, 0, colFirst, segment.getColumn(), text); + drawCursor(model, doubleBufferGC, line, 0, 0, colFirst); + } + if (fModel.hasHoverSelection(line)) { + if (DEBUG_HOVER) { + System.out.format("hover: %s contains hover selection\n", line); //$NON-NLS-1$ + } + Point hsStart = fModel.getHoverSelectionStart(); + Point hsEnd = fModel.getHoverSelectionEnd(); + int colStart = line == hsStart.y ? hsStart.x : 0; + int colEnd = line == hsEnd.y ? hsEnd.x : getTerminalText().getWidth(); + if (colStart < colEnd) { + RGB defaultFg = fStyleMap.getForegrondRGB(null); + doubleBufferGC.setForeground(new Color(doubleBufferGC.getDevice(), defaultFg)); + drawUnderline(doubleBufferGC, colStart, colEnd); + } + } + if (fModel.hasLineSelection(line)) { + TerminalStyle style = TerminalStyle.getStyle(TerminalColor.SELECTION_FOREGROUND, + TerminalColor.SELECTION_BACKGROUND); + setupGC(doubleBufferGC, style); + Point start = model.getSelectionStart(); + Point end = model.getSelectionEnd(); + char[] chars = model.getTerminalText().getChars(line); + if (chars != null) { + int offset = 0; + if (start.y == line) + offset = start.x; + offset = Math.max(offset, colFirst); + int len; + if (end.y == line) + len = end.x - offset + 1; + else + len = chars.length - offset + 1; + len = Math.min(len, chars.length - offset); + if (len > 0) { + String text = new String(chars, offset, len); + drawText(doubleBufferGC, 0, 0, colFirst, offset, text); + } + } + } + } + gc.drawImage(buffer, x, y); + doubleBufferGC.dispose(); + buffer.dispose(); + } + + private void fillBackground(GC gc, int x, int y, int width, int height) { + Color bg = gc.getBackground(); + gc.setBackground(getDefaultBackgroundColor(gc.getDevice())); + gc.fillRectangle(x, y, width, height); + gc.setBackground(bg); + + } + + @Override + public Color getDefaultBackgroundColor() { + return getDefaultBackgroundColor(Display.getDefault()); + } + + @Override + public Color getDefaultBackgroundColor(Device device) { + // null == default style + RGB backgroundRGB = fStyleMap.getBackgroundRGB(null); + return new Color(device, backgroundRGB); + } + + private void drawCursor(ITextCanvasModel model, GC gc, int row, int x, int y, int colFirst) { + if (!model.isCursorOn()) + return; + int cursorLine = model.getCursorLine(); + + if (row == cursorLine) { + int cursorColumn = model.getCursorColumn(); + if (cursorColumn < getTerminalText().getWidth()) { + TerminalStyle style = getTerminalText().getStyle(row, cursorColumn); + if (style == null) { + // TODO make the cursor color customizable + style = TerminalStyle.getStyle(TerminalColor.FOREGROUND, TerminalColor.BACKGROUND); + } + style = style.setReverse(!style.isReverse()); + setupGC(gc, style); + String text = String.valueOf(getTerminalText().getChar(row, cursorColumn)); + drawText(gc, x, y, colFirst, cursorColumn, text); + } + } + } + + private void drawText(GC gc, int x, int y, int colFirst, int col, String text) { + int offset = (col - colFirst) * getCellWidth(); + if (fStyleMap.isFontProportional()) { + // draw the background + // TODO why does this not work??????? + // gc.fillRectangle(x,y,fStyleMap.getFontWidth()*text.length(),fStyleMap.getFontHeight()); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + int xx = x + offset + i * fStyleMap.getFontWidth(); + // TODO why do I have to draw the background character by character?????? + gc.fillRectangle(xx, y, fStyleMap.getFontWidth(), fStyleMap.getFontHeight()); + if (c != ' ' && c != '\000') { + gc.drawString(String.valueOf(c), fStyleMap.getCharOffset(c) + xx, y, false); + } + } + } else { + text = text.replace('\000', ' '); + gc.drawString(text, x + offset, y, false); + } + } + + /** + * + * @param gc + * @param colStart Starting text column to underline (inclusive) + * @param colEnd Ending text column to underline (inclusive) + */ + private void drawUnderline(GC gc, int colStart, int colEnd) { + int y = getCellHeight() - 1; + int x = getCellWidth() * colStart; + + // x2 is the right side of last column being underlined. + int x2 = (colEnd + 1) * getCellWidth() - 1; + gc.drawLine(x, y, x2, y); + } + + private void setupGC(GC gc, TerminalStyle style) { + RGB foregrondColor = fStyleMap.getForegrondRGB(style); + gc.setForeground(new Color(gc.getDevice(), foregrondColor)); + RGB backgroundColor = fStyleMap.getBackgroundRGB(style); + gc.setBackground(new Color(gc.getDevice(), backgroundColor)); + + Font f = fStyleMap.getFont(style); + if (f != gc.getFont()) { + gc.setFont(f); + } + } + + ITerminalTextDataReadOnly getTerminalText() { + return fModel.getTerminalText(); + } + + /** + * @deprecated Use {@link #updateFont(String)} + */ + @Deprecated + @Override + public void onFontChange() { + fStyleMap.updateFont(); + } + + @Override + public void updateFont(String fontName) { + fStyleMap.updateFont(fontName); + } + + @Override + public void updateColors(Map map) { + fStyleMap.updateColors(map); + } + + @Override + public void setInvertedColors(boolean invert) { + fStyleMap.setInvertedColors(invert); + + } + + @Override + public boolean isInvertedColors() { + return fStyleMap.isInvertedColors(); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/VirtualCanvas.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/VirtualCanvas.java new file mode 100644 index 00000000000..2fc846e7dbc --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/VirtualCanvas.java @@ -0,0 +1,357 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Anton Leherbauer (Wind River) - [294468] Fix scroller and text line rendering + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.textcanvas; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; + +/** + * A Canvas showing a virtual object. + * Virtual: the extent of the total canvas. + * Screen: the visible client area in the screen. + */ +public abstract class VirtualCanvas extends Canvas { + + private final Rectangle fVirtualBounds = new Rectangle(0, 0, 0, 0); + private Rectangle fClientArea; + /** + * prevent infinite loop in {@link #updateScrollbars()} + */ + private boolean fInUpdateScrollbars; + private static boolean fInUpdateScrollbarsLogged; + + public VirtualCanvas(Composite parent, int style) { + super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE); + fClientArea = getClientArea(); + addListener(SWT.Paint, event -> paint(event.gc)); + addListener(SWT.Resize, event -> { + fClientArea = getClientArea(); + onResize(); + }); + getVerticalBar().addListener(SWT.Selection, e -> scrollY((ScrollBar) e.widget)); + getHorizontalBar().addListener(SWT.Selection, e -> scrollX((ScrollBar) e.widget)); + } + + protected void onResize() { + updateViewRectangle(); + } + + protected void scrollX(ScrollBar hBar) { + int hSelection = hBar.getSelection(); + int destX = -hSelection - fVirtualBounds.x; + fVirtualBounds.x = -hSelection; + scrollSmart(destX, 0); + updateViewRectangle(); + } + + protected void scrollXDelta(int delta) { + getHorizontalBar().setSelection(-fVirtualBounds.x + delta); + scrollX(getHorizontalBar()); + } + + protected void scrollY(ScrollBar vBar) { + int vSelection = vBar.getSelection(); + int destY = -vSelection - fVirtualBounds.y; + if (destY != 0) { + fVirtualBounds.y = -vSelection; + scrollSmart(0, destY); + updateViewRectangle(); + } + + } + + protected void scrollYDelta(int delta) { + getVerticalBar().setSelection(-fVirtualBounds.y + delta); + scrollY(getVerticalBar()); + } + + protected void scrollSmart(int deltaX, int deltaY) { + if (deltaX != 0 || deltaY != 0) { + Rectangle rect = getBounds(); + scroll(deltaX, deltaY, 0, 0, rect.width, rect.height, false); + } + } + + /** + * @param rect in virtual space + */ + protected void revealRect(Rectangle rect) { + Rectangle visibleRect = getScreenRectInVirtualSpace(); + // scroll the X part + int deltaX = 0; + if (rect.x < visibleRect.x) { + deltaX = rect.x - visibleRect.x; + } else if (visibleRect.x + visibleRect.width < rect.x + rect.width) { + deltaX = (rect.x + rect.width) - (visibleRect.x + visibleRect.width); + } + if (deltaX != 0) { + getHorizontalBar().setSelection(-fVirtualBounds.x + deltaX); + scrollX(getHorizontalBar()); + } + + // scroll the Y part + int deltaY = 0; + if (rect.y < visibleRect.y) { + deltaY = rect.y - visibleRect.y; + } else if (visibleRect.y + visibleRect.height < rect.y + rect.height) { + deltaY = (rect.y + rect.height) - (visibleRect.y + visibleRect.height); + + } + if (deltaY != 0) { + getVerticalBar().setSelection(-fVirtualBounds.y + deltaY); + scrollY(getVerticalBar()); + } + } + + protected void repaint(Rectangle r) { + if (isDisposed()) + return; + if (inClipping(r, fClientArea)) { + redraw(r.x, r.y, r.width, r.height, true); + update(); + } + } + + /** + * Paint the virtual canvas. + * Override to implement actual paint method. + * @param gc graphics context to paint in + */ + abstract protected void paint(GC gc); + + abstract protected Color getTerminalBackgroundColor(Device device); + + /** + * @deprecated Use {@link #getTerminalBackgroundColor(Device)} + */ + @Deprecated + protected Color getTerminalBackgroundColor() { + // return getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND); + return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + } + + protected void paintUnoccupiedSpace(GC gc, Rectangle clipping) { + int width = fVirtualBounds.width + fVirtualBounds.x; + int height = fVirtualBounds.height + fVirtualBounds.y; + int marginWidth = (clipping.x + clipping.width) - width; + int marginHeight = (clipping.y + clipping.height) - height; + if (marginWidth > 0 || marginHeight > 0) { + Color bg = getBackground(); + gc.setBackground(getTerminalBackgroundColor()); + if (marginWidth > 0) { + gc.fillRectangle(width, clipping.y, marginWidth, clipping.height); + } + if (marginHeight > 0) { + gc.fillRectangle(clipping.x, height, clipping.width, marginHeight); + } + gc.setBackground(bg); + } + } + + /** + * @private + */ + protected boolean inClipping(Rectangle clipping, Rectangle r) { + // TODO check if this is OK in all cases (the <=!) + // + if (r.x + r.width <= clipping.x) + return false; + if (clipping.x + clipping.width <= r.x) + return false; + if (r.y + r.height <= clipping.y) + return false; + if (clipping.y + clipping.height <= r.y) + return false; + + return true; + } + + /** + * @return the screen rect in virtual space (starting with (0,0)) + * of the visible screen. (x,y>=0) + */ + protected Rectangle getScreenRectInVirtualSpace() { + Rectangle r = new Rectangle(fClientArea.x - fVirtualBounds.x, fClientArea.y - fVirtualBounds.y, + fClientArea.width, fClientArea.height); + return r; + } + + /** + * @return the rect in virtual space (starting with (0,0)) + * of the visible screen. (x,y>=0) + */ + protected Rectangle getRectInVirtualSpace(Rectangle r) { + return new Rectangle(r.x - fVirtualBounds.x, r.y - fVirtualBounds.y, r.width, r.height); + } + + /** + * Sets the extent of the virtual display area + * @param width width of the display area + * @param height height of the display area + */ + protected void setVirtualExtend(int width, int height) { + fVirtualBounds.width = width; + fVirtualBounds.height = height; + updateScrollbars(); + updateViewRectangle(); + } + + /** + * sets the scrolling origin. Also sets the scrollbars. + * Does NOT redraw! + * Use negative values (move the virtual origin to the top left + * to see something in the screen (which is located at (0,0)) + * @param x + * @param y + */ + protected void setVirtualOrigin(int x, int y) { + if (fVirtualBounds.x != x || fVirtualBounds.y != y) { + fVirtualBounds.x = x; + fVirtualBounds.y = y; + getHorizontalBar().setSelection(-x); + getVerticalBar().setSelection(-y); + updateViewRectangle(); + } + } + + protected Rectangle getVirtualBounds() { + return cloneRectangle(fVirtualBounds); + } + + /** + * @param x + * @return the virtual coordinate in screen space + */ + protected int virtualXtoScreen(int x) { + return x + fVirtualBounds.x; + } + + protected int virtualYtoScreen(int y) { + return y + fVirtualBounds.y; + } + + protected int screenXtoVirtual(int x) { + return x - fVirtualBounds.x; + } + + protected int screenYtoVirtual(int y) { + return y - fVirtualBounds.y; + } + + /** called when the viewed part is changing */ + private final Rectangle fViewRectangle = new Rectangle(0, 0, 0, 0); + + protected void updateViewRectangle() { + if (fViewRectangle.x == -fVirtualBounds.x && fViewRectangle.y == -fVirtualBounds.y + && fViewRectangle.width == fClientArea.width && fViewRectangle.height == fClientArea.height) + return; + fViewRectangle.x = -fVirtualBounds.x; + fViewRectangle.y = -fVirtualBounds.y; + fViewRectangle.width = fClientArea.width; + fViewRectangle.height = fClientArea.height; + viewRectangleChanged(fViewRectangle.x, fViewRectangle.y, fViewRectangle.width, fViewRectangle.height); + } + + protected Rectangle getViewRectangle() { + return cloneRectangle(fViewRectangle); + } + + private Rectangle cloneRectangle(Rectangle r) { + return new Rectangle(r.x, r.y, r.width, r.height); + } + + /** + * Called when the viewed part has changed. + * Override when you need this information.... + * Is only called if the values change! + * @param x visible in virtual space + * @param y visible in virtual space + * @param width + * @param height + */ + protected void viewRectangleChanged(int x, int y, int width, int height) { + } + + /** + * @private + */ + private void updateScrollbars() { + // don't get into infinite loops.... + if (!fInUpdateScrollbars) { + fInUpdateScrollbars = true; + try { + doUpdateScrollbar(); + } finally { + fInUpdateScrollbars = false; + } + } else { + if (!fInUpdateScrollbarsLogged) { + fInUpdateScrollbarsLogged = true; + TerminalPlugin.getDefault().getLog() + .log(new Status(IStatus.WARNING, TerminalPlugin.PLUGIN_ID, IStatus.OK, + "Unexpected Recursion in terminal", //$NON-NLS-1$ + new RuntimeException())); + } + } + } + + private void doUpdateScrollbar() { + Rectangle clientArea = getClientArea(); + ScrollBar horizontal = getHorizontalBar(); + // even if setVisible was called on the scrollbar, isVisible + // returns false if its parent is not visible. + if (!isVisible() || horizontal.isVisible()) { + horizontal.setPageIncrement(clientArea.width - horizontal.getIncrement()); + int max = fVirtualBounds.width; + horizontal.setMaximum(max); + horizontal.setThumb(clientArea.width); + } + ScrollBar vertical = getVerticalBar(); + // even if setVisible was called on the scrollbar, isVisible + // returns false if its parent is not visible. + if (!isVisible() || vertical.isVisible()) { + vertical.setPageIncrement(clientArea.height - vertical.getIncrement()); + int max = fVirtualBounds.height; + vertical.setMaximum(max); + vertical.setThumb(clientArea.height); + } + } + + protected boolean isVertialBarVisible() { + return getVerticalBar().isVisible(); + } + + protected void serVerticalBarVisible(boolean showVScrollBar) { + ScrollBar vertical = getVerticalBar(); + vertical.setVisible(showVScrollBar); + vertical.setSelection(0); + } + + protected boolean isHorizontalBarVisble() { + return getHorizontalBar().isVisible(); + } + + protected void setHorizontalBarVisible(boolean showHScrollBar) { + ScrollBar horizontal = getHorizontalBar(); + horizontal.setVisible(showHScrollBar); + horizontal.setSelection(0); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextData.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextData.java new file mode 100644 index 00000000000..37a563284b2 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextData.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.terminal.model; + +/** + * A writable matrix of characters and {@link TerminalStyle}. This is intended to be the + * low level representation of the text of a Terminal. Higher layers are + * responsible to fill the text and styles into this representation. + *

            + * Note: Implementations of this interface has to be thread safe. + *

            + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITerminalTextData extends ITerminalTextDataReadOnly { + + /** + * Sets the dimensions of the data. If the dimensions are smaller than the current + * dimensions, the lines will be chopped. If the dimensions are bigger, then + * the new elements will be filled with 0 chars and null Style. + * @param height + * @param width + */ + void setDimensions(int height, int width); + + void setMaxHeight(int height); + + int getMaxHeight(); + + /** + * Set a single character and the associated {@link TerminalStyle}. + * @param line line must be >=0 and < height + * @param column column must be >=0 and < width + * @param c the new character at this position + * @param style the style or null + * @since 5.0 + */ + void setChar(int line, int column, char c, TerminalStyle style); + + /** + * Set an array of characters showing in the same {@link TerminalStyle}. + * @param line line must be >=0 and < height + * @param column column must be >=0 and < width + * @param chars the new characters at this position + * @param style the style or null + * @since 5.0 + */ + void setChars(int line, int column, char[] chars, TerminalStyle style); + + /** + * Set a subrange of an array of characters showing in the same {@link TerminalStyle}. + * @param line line must be >=0 and < height + * @param column column must be >=0 and < width + * @param chars the new characters at this position + * @param start the start index in the chars array + * @param len the number of characters to insert. Characters beyond width are not inserted. + * @param style the style or null + * @since 5.0 + */ + void setChars(int line, int column, char[] chars, int start, int len, TerminalStyle style); + + /** + * Cleans the entire line. + * @param line + */ + void cleanLine(int line); + + /** + * Shifts some lines up or down. The "empty" space is filled with '\000' chars + * and null {@link TerminalStyle} + *

            To illustrate shift, here is some sample data: + *

            +	 * 0 aaaa
            +	 * 1 bbbb
            +	 * 2 cccc
            +	 * 3 dddd
            +	 * 4 eeee
            +	 * 
            + * + * Shift a region of 3 lines up by one line shift(1,3,-1) + *
            +	 * 0 aaaa
            +	 * 1 cccc
            +	 * 2 dddd
            +	 * 3
            +	 * 4 eeee
            +	 * 
            + * + * + * Shift a region of 3 lines down by one line shift(1,3,1) + *
            +	 * 0 aaaa
            +	 * 1
            +	 * 2 bbbb
            +	 * 3 cccc
            +	 * 4 eeee
            +	 * 
            + * @param startLine the start line of the shift + * @param size the number of lines to shift + * @param shift how much scrolling is done. New scrolled area is filled with '\000'. + * Negative number means scroll down, positive scroll up (see example above). + */ + void scroll(int startLine, int size, int shift); + + /**Adds a new line to the terminal. If maxHeigth is reached, the entire terminal + * will be scrolled. Else a line will be added. + */ + void addLine(); + + /** + * Copies the entire source into this and changes the size accordingly + * @param source + */ + void copy(ITerminalTextData source); + + /** + * Copy a sourceLine from source to this at destLine. + * @param source + * @param sourceLine + * @param destLine + */ + void copyLine(ITerminalTextData source, int sourceLine, int destLine); + + /** + * Copy length lines from source starting at sourceLine into this starting at + * destLine. + * @param source + * @param sourceStartLine + * @param destStartLine + * @param length + */ + void copyRange(ITerminalTextData source, int sourceStartLine, int destStartLine, int length); + + void setCursorLine(int line); + + void setCursorColumn(int column); + + /** + * Makes this line a wrapped line which logically continues on next line. + * + * @param line + * @since 3.3 + */ + void setWrappedLine(int line); + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextDataReadOnly.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextDataReadOnly.java new file mode 100644 index 00000000000..af30864e56f --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextDataReadOnly.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend + * Anton Leherbauer (Wind River) - [453393] Add support for copying wrapped lines without line break + *******************************************************************************/ +package org.eclipse.tm.terminal.model; + +/** + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITerminalTextDataReadOnly { + + /** + * @return the width of the terminal + */ + int getWidth(); + + /** + * @return the height of the terminal + */ + int getHeight(); + + /** + * @param line be >=0 and < height + * @param startCol must be >=0 and < width + * @param numberOfCols must be > 0 + * @return a the line segments of the specified range + */ + LineSegment[] getLineSegments(int line, int startCol, int numberOfCols); + + /** + * @param line must be >=0 and < height + * @param column must be >=0 and < width + * @return the character at column,line + */ + char getChar(int line, int column); + + /** + * @param line must be >=0 and < height + * @param column must be >=0 and < width + * @return style at column,line or null + * @since 5.0 + */ + TerminalStyle getStyle(int line, int column); + + /** + * Creates a new instance of {@link ITerminalTextDataSnapshot} that + * can be used to track changes. Make sure to call {@link ITerminalTextDataSnapshot#detach()} + * if you don't need the snapshots anymore. + *

            Note: A new snapshot is empty and needs a call to {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} to + * get its initial values. You might want to setup the snapshot to your needs by calling + * {@link ITerminalTextDataSnapshot#setInterestWindow(int, int)}. + *

            + * @return a new instance of {@link ITerminalTextDataSnapshot} that "listens" to changes of + * this. + */ + public ITerminalTextDataSnapshot makeSnapshot(); + + char[] getChars(int line); + + /** + * @since 5.0 + */ + TerminalStyle[] getStyles(int line); + + /** + * @return the line in which the cursor is at the moment + */ + int getCursorLine(); + + /** + * @return the column at which the cursor is at the moment + */ + int getCursorColumn(); + + /** + * @param line + * @return true if this line got wrapped, ie. logically continues on next line + * @since 3.3 + */ + boolean isWrappedLine(int line); + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextDataSnapshot.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextDataSnapshot.java new file mode 100644 index 00000000000..24752f8fc32 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/ITerminalTextDataSnapshot.java @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend + *******************************************************************************/ +package org.eclipse.tm.terminal.model; + +/** + * This class maintains a snapshot of an instance of {@link ITerminalTextData}. + * While the {@link ITerminalTextData} continues changing, the snapshot remains + * unchanged until the next snapshot is taken by calling + * {@link #updateSnapshot(boolean)}. This is important, because the + * {@link ITerminalTextData} might get modified by another thread. Suppose you + * would want to draw the content of the {@link ITerminalTextData} using the + * following loop: + * + *
            + * for (int line = 0; line < term.getHeight(); line++)
            + * 	for (int column = 0; column < term.getWidth(); column++)
            + * 		drawCharacter(column, line, term.getChar(column, line), term.getStyle(column, line));
            + * 
            + * + * This might fail because the background thread could change the dimensions of + * the {@link ITerminalTextData} while you iterate the loop. One solution would + * be to put a synchronized(term){} statement around the code. This + * has two problems: 1. you would have to know about the internals of the + * synchronisation of {@link ITerminalTextData}. 2. The other thread that + * changes {@link ITerminalTextData} is blocked while the potentially slow + * drawing is done. + *

            + * Solution: Take a snapshot of the terminal and use the snapshot to draw + * the content. There is no danger that the data structure get changed while you + * draw. There are also methods to find out what has changed to minimize the + * number of lines that get redrawn. + *

            + * + *

            + * Drawing optimization: To optimize redrawing of changed lines, this + * class keeps track of lines that have changed since the previous snapshot. + *

            + * + *
            + * // iterate over the potentially changed lines
            + * for (int line = snap.getFirstChangedLine(); line <= snap.getLastChangedLine(); line++)
            + * 	// redraw only if the line has changed
            + * 	if (snap.hasLineChanged(line))
            + * 		for (int column = 0; column < snap.getWidth(); column++)
            + * 			drawCharacter(column, line, snap.getChar(column, line), snap.getStyle(column, line));
            + * 
            + * + *

            + * Scroll optimization: Often new lines are appended at the bottom of the + * terminal and the rest of the lines are scrolled up. In this case all lines + * would be marked as changed. To optimize for this case, + * {@link #updateSnapshot(boolean)} can be called with true for the + * detectScrolling parameter. The object will keep track of + * scrolling. The UI must first handle the scrolling and then use the + * {@link #hasLineChanged(int)} method to determine scrolling: + * + *

            + * // scroll the visible region of the UI <b>before</b> drawing the changed lines.
            + * doUIScrolling(snap.getScrollChangeY(), snap.getScrollChangeN(), snap.getScrollChangeShift());
            + * // iterate over the potentially changed lines
            + * for (int line = snap.getFirstChangedLine(); line <= snap.getFirstChangedLine(); line++)
            + * 	// redraw only if the line has changed
            + * 	if (snap.hasLineChanged(line))
            + * 		for (int column = 0; column < snap.getWidth(); column++)
            + * 			drawCharacter(column, line, snap.getChar(column, line), snap.getStyle(column, line));
            + * 
            + * + *

            + *

            + * Threading Note: This class is not thread safe! All methods have to be + * called by the a same thread, that created the instance by calling + * {@link ITerminalTextDataReadOnly#makeSnapshot()}. + *

            + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITerminalTextDataSnapshot extends ITerminalTextDataReadOnly { + /** + * This listener gets called when the current snapshot + * is out of date. Calling {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} + * will have an effect. Once the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} method is called, + * it will not be called until {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} + * is called and a new snapshot needs to be updated again. + *

            + * A typical terminal view would not update the snapshot immediately + * after the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} has been called. It would introduce a + * delay to update the UI (and the snapshot} 10 or 20 times per second. + * + *

            Make sure you don't spend too much time in this method. + */ + interface SnapshotOutOfDateListener { + /** + * Gets called when the snapshot is out of date. To get the snapshot up to date, + * call {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}. + * @param snapshot The snapshot that is out of date + */ + void snapshotOutOfDate(ITerminalTextDataSnapshot snapshot); + } + + void addListener(SnapshotOutOfDateListener listener); + + void removeListener(SnapshotOutOfDateListener listener); + + /** + * Ends the listening to the {@link ITerminalTextData}. After this + * has been called no new snapshot data is collected. + */ + void detach(); + + /** + * @return true if the data has changed since the previous snapshot. + */ + boolean isOutOfDate(); + + /** + * The window of interest is the region the snapshot should track. + * Changes outside this region are ignored. The change takes effect after + * an update! + * @param startLine -1 means track the end of the data + * @param size number of lines to track. A size of -1 means track all. + */ + void setInterestWindow(int startLine, int size); + + int getInterestWindowStartLine(); + + int getInterestWindowSize(); + + /** + * Create a new snapshot of the {@link ITerminalTextData}. It will efficiently + * copy the data of the {@link ITerminalTextData} into an internal representation. + * The snapshot also keeps track of the changes since the previous snapshot. + *

            With the methods {@link #getFirstChangedLine()}, {@link #getLastChangedLine()} and + * {@link #hasLineChanged(int)} + * you can find out what has changed in the current snapshot since the previous snapshot. + * @param detectScrolling if true the snapshot tries to identify scroll + * changes since the last snapshot. In this case the information about scrolling + * can be retrieved using the following methods: + * {@link #getScrollWindowStartLine()}, {@link #getScrollWindowSize()} and {@link #getScrollWindowShift()} + *
            Note: The method {@link #hasLineChanged(int)} returns changes after the + * scrolling has been applied. + */ + void updateSnapshot(boolean detectScrolling); + + /** + * @return The first line changed in this snapshot compared + * to the previous snapshot. + * + *

            Note: If no line has changed, this + * returns {@link Integer#MAX_VALUE} + * + *

            Note: if {@link #updateSnapshot(boolean)} has been called with true, + * then this does not include lines that only have been scrolled. This is the + * first line that has changed after the scroll has been applied. + */ + int getFirstChangedLine(); + + /** + * @return The last line changed in this snapshot compared + * to the previous snapshot. If the height has changed since the + * last update of the snapshot, then the returned value is within + * the new dimensions. + * + *

            Note: If no line has changed, this returns -1 + * + *

            Note: if {@link #updateSnapshot(boolean)} has been called with true, + * then this does not include lines that only have been scrolled. This is the + * last line that has changed after the scroll has been applied. + * + *

            A typical for loop using this method would look like this (note the <= in the for loop): + *

            +	 * for(int line=snap.{@link #getFirstChangedLine()}; line <= snap.getLastChangedLine(); line++)
            +	 *    if(snap.{@link #hasLineChanged(int) hasLineChanged(line)})
            +	 *       doSomething(line);
            +	 * 
            + */ + int getLastChangedLine(); + + /** + * @param line + * @return true if the line has changed since the previous snapshot + */ + boolean hasLineChanged(int line); + + boolean hasDimensionsChanged(); + + /** + * @return true if the terminal has changed (and not just the + * window of interest) + */ + boolean hasTerminalChanged(); + + /** + * If {@link #updateSnapshot(boolean)} was called with true, then this method + * returns the top of the scroll region. + * @return The first line scrolled in this snapshot compared + * to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}. + */ + int getScrollWindowStartLine(); + + /** + * If {@link #updateSnapshot(boolean)} was called with true, then this method + * returns the size of the scroll region. + * @return The number of lines scrolled in this snapshot compared + * to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)} + * If nothing has changed, 0 is returned. + */ + int getScrollWindowSize(); + + /** + * If {@link #updateSnapshot(boolean)} was called with true, then this method + * returns number of lines moved by the scroll region. + * @return The the scroll shift of this snapshot compared + * to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)} + */ + int getScrollWindowShift(); + + /** + * @return The {@link ITerminalTextData} on that this instance is observing. + */ + ITerminalTextData getTerminalTextData(); + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/LineSegment.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/LineSegment.java new file mode 100644 index 00000000000..548dc705891 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/LineSegment.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.terminal.model; + +public class LineSegment { + private final String fText; + private final int fCol; + private final TerminalStyle fStyle; + + /** + * @since 5.0 + */ + public LineSegment(int col, String text, TerminalStyle style) { + fCol = col; + fText = text; + fStyle = style; + } + + /** + * @since 5.0 + */ + public TerminalStyle getStyle() { + return fStyle; + } + + public String getText() { + return fText; + } + + public int getColumn() { + return fCol; + } + + @Override + public String toString() { + return "LineSegment(" + fCol + ", \"" + fText + "\"," + fStyle + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } +} \ No newline at end of file diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalColor.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalColor.java new file mode 100644 index 00000000000..0bc12ebabdb --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalColor.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (c) 2020 Kichwa Coders Canada Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tm.terminal.model; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.resource.ColorDescriptor; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; + +/** + * Colors that can be used in the Terminal are represented by this class. The enum contains + * the colors with well known names defined by the ANSI Escape Sequences, plus other colors needed + * to render a display (such as Background color). + * + * Rather than name all the colors when using ANSI 8-bit indexed colors, the indexed colors + * can be accessed via the {@link #getIndexedRGBColor(int)} or {@link #getIndexedRGBColor(int)} + * (use {@link #isIndexedTerminalColor(int)} to determine which one is appropriate. + * + * The {@link TerminalStyle} supports any arbitrary color by using {@link RGB} defined colors. + * This class provides the connection between the names exposed to the user in preferences + * and their use in the terminal, along with how colors change when other attributes (such as + * bright and invertColors) are applied to them. + * + * @since 5.0 + */ +public enum TerminalColor { + BLACK, // + RED, // + GREEN, // + YELLOW, // + BLUE, // + MAGENTA, // + CYAN, // + WHITE, // + + BRIGHT_BLACK, // + BRIGHT_RED, // + BRIGHT_GREEN, // + BRIGHT_YELLOW, // + BRIGHT_BLUE, // + BRIGHT_MAGENTA, // + BRIGHT_CYAN, // + BRIGHT_WHITE, // + + FOREGROUND, // + BACKGROUND, // + SELECTION_FOREGROUND, // + SELECTION_BACKGROUND; + + /** + * The first 16-items in the 8-bit lookup table map to the user changeable colors + * above, so this array handles that mapping. + */ + private final static TerminalColor table8bitIndexedTerminalColors[] = new TerminalColor[16]; + + /** + * The rest of the colors in the lookup table (240 colors) are pre-defined by + * the standard. The colors that fill this table were derived from + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit which was more + * digestible and accessible than the underlying ITU and ISO standards. + */ + private final static RGB table8bitIndexedRGB[] = new RGB[256 - 16]; + + /** + * Color to use instead when inverted color is selected + */ + private TerminalColor invertColor; + + /** + * Color to use instead when bright color is selected + */ + private TerminalColor brightColor; + + /** + * Pre-calculate the lookup tables for 8-bit colors, inverses and equivalent brights. + */ + static { + TerminalColor[] values = TerminalColor.values(); + + // 8-bit color lookup tables + { + int index = 0; + for (; index < 16; index++) { + TerminalColor c = values[index]; + table8bitIndexedTerminalColors[index] = c; + } + + int vals[] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff }; + Assert.isTrue(index == 16); + for (int r = 0; r < 6; r++) { + for (int g = 0; g < 6; g++) { + for (int b = 0; b < 6; b++) { + table8bitIndexedRGB[index++ - 16] = new RGB(vals[r], vals[g], vals[b]); + } + } + } + + int greys[] = { 0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e, 0x58, 0x62, 0x6c, 0x76, 0x80, 0x8a, 0x94, + 0x9e, 0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee }; + + Assert.isTrue(index == 232); + for (int g : greys) { + table8bitIndexedRGB[index++ - 16] = new RGB(g, g, g); + } + Assert.isTrue(index == 256); + } + + // bright equivalents + { + // The second set of 8 colors are the bright of the first 8. + for (int i = 0; i < 8; i++) { + values[i].brightColor = values[i + 8]; + } + // The rest of the colors are not brightened + for (int i = 8; i < values.length; i++) { + values[i].brightColor = values[i]; + } + } + + // inverses + { + // by default make all colors invert of themself + for (int i = 0; i < values.length; i++) { + values[i].invertColor = values[i]; + } + // and then mark the colors that are actual inverts + inverts(BLACK, WHITE); + inverts(BRIGHT_BLACK, BRIGHT_WHITE); + inverts(BACKGROUND, FOREGROUND); + } + + } + + private static void inverts(TerminalColor a, TerminalColor b) { + a.invertColor = b; + b.invertColor = a; + } + + /** + * Return a new color for the given color with inversions or brightness attributes applied. + * + * @param invert For invertible colors, return the inverse (typically white <-> black) + * @param bright returns the brighter version of the color if one is available + * @return {@link ColorDescriptor} that a {@link Color} can be made from + * using {@link ColorDescriptor#createColor(org.eclipse.swt.graphics.Device)} + * @throws NullPointerException if there is no current {@link Display} + */ + public TerminalColor convertColor(boolean invert, boolean bright) { + TerminalColor selected = this; + // it doesn't matter which order you apply bright and invert, you get to + // the same color when both are set + if (invert) { + selected = selected.invertColor; + } + if (bright) { + selected = selected.brightColor; + } + return selected; + } + + /** + * Query for whether the 8-bit color index will return a named color, in which case + * {@link #getIndexedTerminalColor(int)} must be called to get the named color. Use + * {@link #convertColor(boolean, boolean)} if this method returns false. + * + * @param index 8-bit index. + * @return true for named colors, false for RGB colors + */ + public static boolean isIndexedTerminalColor(int index) { + Assert.isLegal(index >= 0 && index < 256, "Invalid 8-bit table index out of range 0-255"); //$NON-NLS-1$ + return index < table8bitIndexedTerminalColors.length && index >= 0; + } + + /** + * Return the named color for the given 8-bit index. + * + * @param index 8-bit index in 0-15 range. + * @return named color + */ + public static TerminalColor getIndexedTerminalColor(int index) { + Assert.isLegal(isIndexedTerminalColor(index), "Invalid table index used for ANSI Color"); //$NON-NLS-1$ + return table8bitIndexedTerminalColors[index]; + } + + /** + * Return the RGB color for the given 8-bit index. + * + * @param index 8-bit index in 16-255 range. + * @return RGB color + */ + public static RGB getIndexedRGBColor(int index) { + Assert.isLegal(index >= 16 && index < 256, "Invalid table index used for RGB Color"); //$NON-NLS-1$ + return table8bitIndexedRGB[index - 16]; + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalStyle.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalStyle.java new file mode 100644 index 00000000000..d81d4b8466e --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalStyle.java @@ -0,0 +1,285 @@ +/******************************************************************************* + * Copyright (c) 2007, 2020 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.terminal.model; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; +import org.eclipse.tm.internal.terminal.provisional.api.Logger; + +/** + * @author scharf + * Flyweight + * Threadsafe. + * @since 5.0 + * + */ +// TODO add an Object for user data, use weak map to keep track of styles with associated +// user data +public class TerminalStyle { + private final TerminalColor fForegroundTerminalColor; + private final TerminalColor fBackgroundTerminalColor; + private final RGB fForegroundRGB; + private final RGB fBackgroundRGB; + private final boolean fBold; + private final boolean fBlink; + private final boolean fUnderline; + private final boolean fReverse; + private final static Map fgStyles = Collections + .synchronizedMap(new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + int size = size(); + boolean removeEldest = size >= 1000; + if (TerminalPlugin.isOptionEnabled(Logger.TRACE_DEBUG_LOG_VT100BACKEND)) { + if (removeEldest) { + Logger.log("Removing eldest Style from style cache, size = " + size); //$NON-NLS-1$ + } else { + Logger.log("Leaving eldest Style in style cache, size = " + size); //$NON-NLS-1$ + } + } + return removeEldest; + } + }); + + private TerminalStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, + RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse) { + Assert.isLegal(foregroundTerminalColor == null || foregroundRGB == null, + "Only one of ANSI or RGB colors can be specified as a foreground color"); //$NON-NLS-1$ + Assert.isLegal(backgroundTerminalColor == null || backgroundRGB == null, + "Only one of ANSI or RGB colors can be specified as a background color"); //$NON-NLS-1$ + fForegroundTerminalColor = foregroundTerminalColor; + fBackgroundTerminalColor = backgroundTerminalColor; + fForegroundRGB = foregroundRGB; + fBackgroundRGB = backgroundRGB; + fBold = bold; + fBlink = blink; + fUnderline = underline; + fReverse = reverse; + } + + public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, + RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse) { + TerminalStyle style = new TerminalStyle(foregroundTerminalColor, backgroundTerminalColor, foregroundRGB, + backgroundRGB, bold, blink, underline, reverse); + // If set had a computeIfAbsent we would use a set, instead just store 1-2-1 mapping + return fgStyles.computeIfAbsent(style, (s) -> style); + } + + public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, + boolean bold, boolean blink, boolean underline, boolean reverse) { + return getStyle(foregroundTerminalColor, backgroundTerminalColor, null, null, bold, blink, underline, reverse); + } + + public static TerminalStyle getStyle(RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, + boolean underline, boolean reverse) { + return getStyle(null, null, foregroundRGB, backgroundRGB, bold, blink, underline, reverse); + } + + public static TerminalStyle getDefaultStyle() { + return getStyle(TerminalColor.FOREGROUND, TerminalColor.BACKGROUND); + } + + public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor) { + return getStyle(foregroundTerminalColor, backgroundTerminalColor, null, null, false, false, false, false); + } + + public TerminalStyle setForeground(TerminalColor foregroundTerminalColor) { + return getStyle(foregroundTerminalColor, fBackgroundTerminalColor, null, fBackgroundRGB, fBold, fBlink, + fUnderline, fReverse); + } + + public TerminalStyle setBackground(TerminalColor backgroundTerminalColor) { + return getStyle(fForegroundTerminalColor, backgroundTerminalColor, fForegroundRGB, null, fBold, fBlink, + fUnderline, fReverse); + } + + public TerminalStyle setForeground(RGB foregroundRGB) { + return getStyle(null, fBackgroundTerminalColor, foregroundRGB, fBackgroundRGB, fBold, fBlink, fUnderline, + fReverse); + } + + public TerminalStyle setBackground(RGB backgroundRGB) { + return getStyle(fForegroundTerminalColor, null, fForegroundRGB, backgroundRGB, fBold, fBlink, fUnderline, + fReverse); + } + + public TerminalStyle setForeground(TerminalStyle other) { + return getStyle(other.fForegroundTerminalColor, fBackgroundTerminalColor, other.fForegroundRGB, fBackgroundRGB, + fBold, fBlink, fUnderline, fReverse); + } + + public TerminalStyle setBackground(TerminalStyle other) { + return getStyle(fForegroundTerminalColor, other.fBackgroundTerminalColor, fForegroundRGB, other.fBackgroundRGB, + fBold, fBlink, fUnderline, fReverse); + } + + public TerminalStyle setForeground(int eightBitindexedColor) { + boolean isIndexTerminalColor = TerminalColor.isIndexedTerminalColor(eightBitindexedColor); + if (isIndexTerminalColor) { + TerminalColor foregroundTerminalColor = TerminalColor.getIndexedTerminalColor(eightBitindexedColor); + return getStyle(foregroundTerminalColor, fBackgroundTerminalColor, null, fBackgroundRGB, fBold, fBlink, + fUnderline, fReverse); + } else { + RGB foregroundRGB = TerminalColor.getIndexedRGBColor(eightBitindexedColor); + return getStyle(null, fBackgroundTerminalColor, foregroundRGB, fBackgroundRGB, fBold, fBlink, fUnderline, + fReverse); + } + } + + public TerminalStyle setBackground(int eightBitindexedColor) { + boolean isIndexTerminalColor = TerminalColor.isIndexedTerminalColor(eightBitindexedColor); + if (isIndexTerminalColor) { + TerminalColor backgroundTerminalColor = TerminalColor.getIndexedTerminalColor(eightBitindexedColor); + return getStyle(fForegroundTerminalColor, backgroundTerminalColor, fForegroundRGB, null, fBold, fBlink, + fUnderline, fReverse); + } else { + RGB backgroundRGB = TerminalColor.getIndexedRGBColor(eightBitindexedColor); + return getStyle(fForegroundTerminalColor, null, fForegroundRGB, backgroundRGB, fBold, fBlink, fUnderline, + fReverse); + } + } + + public TerminalStyle setBold(boolean bold) { + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, bold, + fBlink, fUnderline, fReverse); + } + + public TerminalStyle setBlink(boolean blink) { + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, + blink, fUnderline, fReverse); + } + + public TerminalStyle setUnderline(boolean underline) { + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, + fBlink, underline, fReverse); + } + + public TerminalStyle setReverse(boolean reverse) { + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, + fBlink, fUnderline, reverse); + } + + public TerminalColor getForegroundTerminalColor() { + return fForegroundTerminalColor; + } + + public TerminalColor getBackgroundTerminalColor() { + return fBackgroundTerminalColor; + } + + public RGB getForegroundRGB() { + return fForegroundRGB; + } + + public RGB getBackgroundRGB() { + return fBackgroundRGB; + } + + public boolean isBlink() { + return fBlink; + } + + public boolean isBold() { + return fBold; + } + + public boolean isReverse() { + return fReverse; + } + + public boolean isUnderline() { + return fUnderline; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fBackgroundTerminalColor == null) ? 0 : fBackgroundTerminalColor.hashCode()); + result = prime * result + ((fBackgroundRGB == null) ? 0 : fBackgroundRGB.hashCode()); + result = prime * result + (fBlink ? 1231 : 1237); + result = prime * result + (fBold ? 1231 : 1237); + result = prime * result + ((fForegroundTerminalColor == null) ? 0 : fForegroundTerminalColor.hashCode()); + result = prime * result + ((fForegroundRGB == null) ? 0 : fForegroundRGB.hashCode()); + result = prime * result + (fReverse ? 1231 : 1237); + result = prime * result + (fUnderline ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TerminalStyle other = (TerminalStyle) obj; + if (fBackgroundTerminalColor != other.fBackgroundTerminalColor) + return false; + if (fBackgroundRGB == null) { + if (other.fBackgroundRGB != null) + return false; + } else if (!fBackgroundRGB.equals(other.fBackgroundRGB)) + return false; + if (fBlink != other.fBlink) + return false; + if (fBold != other.fBold) + return false; + if (fForegroundTerminalColor != other.fForegroundTerminalColor) + return false; + if (fForegroundRGB == null) { + if (other.fForegroundRGB != null) + return false; + } else if (!fForegroundRGB.equals(other.fForegroundRGB)) + return false; + if (fReverse != other.fReverse) + return false; + if (fUnderline != other.fUnderline) + return false; + return true; + } + + @Override + public String toString() { + StringBuffer result = new StringBuffer(); + result.append("Style(foreground="); //$NON-NLS-1$ + if (fForegroundTerminalColor != null) { + result.append(fForegroundTerminalColor); + } else { + result.append(fForegroundRGB); + } + result.append(", background="); //$NON-NLS-1$ + if (fForegroundTerminalColor != null) { + result.append(fBackgroundTerminalColor); + } else { + result.append(fBackgroundRGB); + } + if (fBlink) + result.append(", blink"); //$NON-NLS-1$ + if (fBold) + result.append(", bold"); //$NON-NLS-1$ + if (fBlink) + result.append(", blink"); //$NON-NLS-1$ + if (fReverse) + result.append(", reverse"); //$NON-NLS-1$ + if (fUnderline) + result.append(", underline"); //$NON-NLS-1$ + result.append(")"); //$NON-NLS-1$ + return result.toString(); + } + +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalTextDataFactory.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalTextDataFactory.java new file mode 100644 index 00000000000..25729aa3426 --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalTextDataFactory.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2007, 2018 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.terminal.model; + +import org.eclipse.tm.internal.terminal.model.SynchronizedTerminalTextData; +import org.eclipse.tm.internal.terminal.model.TerminalTextData; + +public class TerminalTextDataFactory { + static public ITerminalTextData makeTerminalTextData() { + return new SynchronizedTerminalTextData(new TerminalTextData()); + } +} diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TextRange.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TextRange.java new file mode 100644 index 00000000000..b73576f4c3c --- /dev/null +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TextRange.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2021 Fabrizio Iannetti. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.tm.terminal.model; + +import org.eclipse.swt.graphics.Point; + +/** + * Represents a range of text in the terminal. + *

            + * Used, for example, to store location of active hover + * + * @since 5.2 + */ +public final class TextRange { + public final int colStart; + public final int colEnd; + public final int rowStart; + public final int rowEnd; + public final String text; + + public static final TextRange EMPTY = new TextRange(0, 0, 0, 0, ""); //$NON-NLS-1$ + + /** + * Constructor. + * + * @param rowStart start row + * @param rowEnd end row + * @param colStart start column (exclusive) + * @param colEnd end column (exclusive) + * @param text text in the range + */ + public TextRange(int rowStart, int rowEnd, int colStart, int colEnd, String text) { + super(); + this.colStart = colStart; + this.colEnd = colEnd; + this.rowStart = rowStart; + this.rowEnd = rowEnd; + this.text = text; + } + + public boolean contains(int col, int row) { + int colStartInrow = row == rowStart ? colStart : 0; + int colEndInRow = row == rowEnd - 1 ? colEnd : col + 1; + return col >= colStartInrow && col < colEndInRow && row >= rowStart && row < rowEnd; + } + + public boolean contains(int line) { + return line >= rowStart && line < rowEnd; + } + + /** + * Whether the range represents a non-empty (non-zero) amount of text + */ + public boolean isEmpty() { + return !(colEnd > colStart || rowEnd > rowStart); + } + + public Point getStart() { + return new Point(colStart, rowStart); + } + + public Point getEnd() { + return new Point(colEnd, rowEnd); + } + + public int getColStart() { + return colStart; + } + + public int getColEnd() { + return colEnd; + } + + public int getRowStart() { + return rowStart; + } + + public int getRowEnd() { + return rowEnd; + } + + @Override + public String toString() { + return String.format("TextRange (%s,%s)-(%s,%s)-'%s'", //$NON-NLS-1$ + colStart, rowStart, colEnd, rowEnd, text); + } +}