Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKai Maetzel2002-09-24 12:39:07 -0400
committerKai Maetzel2002-09-24 12:39:07 -0400
commitdb4ac57b04d3091f1e46fa274fe527444c03a1a0 (patch)
tree5818e06543de8325238c2bb0634e2f79270c9b7f
parent7a653a7e825daf117f57a8457d1d4a2c6648c992 (diff)
downloadeclipse.platform.text-db4ac57b04d3091f1e46fa274fe527444c03a1a0.tar.gz
eclipse.platform.text-db4ac57b04d3091f1e46fa274fe527444c03a1a0.tar.xz
eclipse.platform.text-db4ac57b04d3091f1e46fa274fe527444c03a1a0.zip
First cut of org.eclipse.ui split
-rw-r--r--org.eclipse.jface.text/.classpath10
-rw-r--r--org.eclipse.jface.text/.cvsignore1
-rw-r--r--org.eclipse.jface.text/.project21
-rw-r--r--org.eclipse.jface.text/build.properties4
-rw-r--r--org.eclipse.jface.text/fragment.properties2
-rw-r--r--org.eclipse.jface.text/fragment.xml21
-rw-r--r--org.eclipse.jface.text/scripts/exportplugin.xml28
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java464
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java680
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultAutoIndentStrategy.java85
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultInformationControl.java284
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextDoubleClickStrategy.java218
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultUndoManager.java786
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentAdapter.java338
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentCommand.java77
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoIndentStrategy.java26
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapter.java31
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapterExtension.java39
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IEventConsumer.java31
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java65
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java89
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControl.java142
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreator.java33
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension.java29
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkRegionTarget.java32
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkSelection.java48
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IRewriteTarget.java55
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextDoubleClickStrategy.java27
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHover.java46
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextInputListener.java34
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextListener.java33
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTarget.java95
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTargetExtension.java31
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextSelection.java76
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewer.java432
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension.java101
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManager.java86
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IViewportListener.java24
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeper.java34
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwner.java45
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.java26
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.properties28
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/MarkSelection.java69
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/PropagatingFontFieldEditor.java132
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/TextAttribute.java121
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/TextEvent.java119
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/TextPresentation.java398
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/TextSelection.java175
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java3432
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java300
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java232
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposal.java120
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java603
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java1377
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformation.java86
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationPopup.java626
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationValidator.java53
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/Helper.java26
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposal.java76
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension.java66
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistListener.java29
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistProcessor.java84
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java85
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformation.java57
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationExtension.java29
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationPresenter.java48
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationValidator.java38
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.java26
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java72
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/package.html25
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContentFormatter.java654
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatter.java49
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategy.java51
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/package.html21
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java66
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProvider.java54
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/information/InformationPresenter.java394
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/information/package.html22
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/package.html82
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationDamager.java54
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java70
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationRepairer.java50
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/PresentationReconciler.java465
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/package.html24
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java468
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegion.java86
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegionQueue.java98
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java56
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategy.java54
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategyExtension.java45
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/MonoReconciler.java102
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/Reconciler.java185
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/package.html25
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/BufferedRuleBasedScanner.java119
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/DefaultDamagerRepairer.java213
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/DefaultPartitioner.java518
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/EndOfLineRule.java39
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/ICharacterScanner.java43
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IPartitionTokenScanner.java42
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IPredicateRule.java47
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IRule.java27
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IToken.java47
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/ITokenScanner.java58
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IWhitespaceDetector.java19
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/IWordDetector.java26
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/MultiLineRule.java40
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/NumberRule.java65
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/PatternRule.java215
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedDamagerRepairer.java49
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedPartitionScanner.java109
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedPartitioner.java520
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/RuleBasedScanner.java202
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/SingleLineRule.java41
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/Token.java120
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WhitespaceRule.java50
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WordPatternRule.java101
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/WordRule.java138
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/rules/package.html21
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/Annotation.java118
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationBarHoverManager.java107
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationColumn.java30
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationModel.java324
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationModelEvent.java44
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationRulerColumn.java383
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/CompositeRuler.java719
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationHover.java27
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationModel.java119
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationModelListener.java25
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IAnnotationModelListenerExtension.java30
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/ISourceViewer.java162
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRuler.java74
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerColumn.java74
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerExtension.java39
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/IVerticalRulerInfo.java60
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/JFaceTextMessages.java49
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineNumberRulerColumn.java465
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java519
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewerConfiguration.java245
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/VerticalRuler.java446
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/VisualAnnotationModel.java179
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/package.html26
-rw-r--r--org.eclipse.text/.classpath6
-rw-r--r--org.eclipse.text/.cvsignore1
-rw-r--r--org.eclipse.text/.project17
-rw-r--r--org.eclipse.text/build.properties4
-rw-r--r--org.eclipse.text/plugin.properties2
-rw-r--r--org.eclipse.text/plugin.xml14
-rw-r--r--org.eclipse.text/scripts/exportplugin.xml28
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java1177
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/AbstractLineTracker.java502
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Assert.java165
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/BadLocationException.java30
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/BadPositionCategoryException.java32
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java290
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java39
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentManager.java312
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ConfigurableLineTracker.java58
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/DefaultLineTracker.java74
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/DefaultPositionUpdater.java219
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Document.java71
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/DocumentEvent.java84
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/GapTextStore.java253
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocument.java567
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension.java81
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentListener.java33
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioner.java104
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension.java37
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListener.java27
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension.java33
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ILineTracker.java132
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IPositionUpdater.java33
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IRegion.java32
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ITextStore.java57
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ITypedRegion.java24
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Line.java63
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Position.java181
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Region.java63
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/SequentialRewriteTextStore.java273
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/TextUtilities.java117
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/TypedPosition.java68
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/TypedRegion.java56
-rw-r--r--org.eclipse.ui.editors/.classpath17
-rw-r--r--org.eclipse.ui.editors/.cvsignore1
-rw-r--r--org.eclipse.ui.editors/.project27
-rw-r--r--org.eclipse.ui.editors/build.properties5
-rw-r--r--org.eclipse.ui.editors/extensions/org/eclipse/ui/internal/SystemSummaryDocumentProvider.java310
-rw-r--r--org.eclipse.ui.editors/extensions/org/eclipse/ui/internal/SystemSummaryEditor.java149
-rw-r--r--org.eclipse.ui.editors/plugin.properties2
-rw-r--r--org.eclipse.ui.editors/plugin.xml23
-rw-r--r--org.eclipse.ui.editors/scripts/exportplugin.xml29
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/DefaultEncodingSupport.java219
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/EncodingActionGroup.java383
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java670
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/IEncodingActionsConstants.java68
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/IEncodingActionsDefinitionIds.java68
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/IEncodingActionsHelpContextIds.java71
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/IEncodingSupport.java40
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/IStorageDocumentProvider.java47
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/ITextEditorHelpContextIds.java33
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/StorageDocumentProvider.java352
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextEditor.java322
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextEditorActionContributor.java117
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextEditorMessages.java30
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextEditorMessages.properties100
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextEditorPreferencePage.java118
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/package.html13
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/AddTaskAction.java60
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/SelectMarkerRulerAction.java278
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/SelectMarkerRulerInfoAction.java32
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/SelectRulerAction.java30
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/TaskRulerAction.java76
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/TextEditorMessages.java30
-rw-r--r--org.eclipse.ui.editors/src/org/eclipse/ui/texteditor/TextEditorMessages.properties39
213 files changed, 32687 insertions, 0 deletions
diff --git a/org.eclipse.jface.text/.classpath b/org.eclipse.jface.text/.classpath
new file mode 100644
index 000000000..a72fd369d
--- /dev/null
+++ b/org.eclipse.jface.text/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="/org.eclipse.text"/>
+ <classpathentry kind="src" path="/org.eclipse.jface"/>
+ <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
+ <classpathentry kind="src" path="/org.eclipse.core.runtime"/>
+ <classpathentry kind="src" path="/org.eclipse.swt"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.jface.text/.cvsignore b/org.eclipse.jface.text/.cvsignore
new file mode 100644
index 000000000..ba077a403
--- /dev/null
+++ b/org.eclipse.jface.text/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/org.eclipse.jface.text/.project b/org.eclipse.jface.text/.project
new file mode 100644
index 000000000..85dd82450
--- /dev/null
+++ b/org.eclipse.jface.text/.project
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jface.text</name>
+ <comment></comment>
+ <projects>
+ <project>org.eclipse.core.runtime</project>
+ <project>org.eclipse.jface</project>
+ <project>org.eclipse.swt</project>
+ <project>org.eclipse.text</project>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.jface.text/build.properties b/org.eclipse.jface.text/build.properties
new file mode 100644
index 000000000..4518f3a09
--- /dev/null
+++ b/org.eclipse.jface.text/build.properties
@@ -0,0 +1,4 @@
+source.jfacetext.jar = src/
+bin.includes = fragment.properties,\
+ fragment.xml,\
+ *.jar
diff --git a/org.eclipse.jface.text/fragment.properties b/org.eclipse.jface.text/fragment.properties
new file mode 100644
index 000000000..044b21535
--- /dev/null
+++ b/org.eclipse.jface.text/fragment.properties
@@ -0,0 +1,2 @@
+fragmentName = JFace Text
+providerName = Eclipse.org
diff --git a/org.eclipse.jface.text/fragment.xml b/org.eclipse.jface.text/fragment.xml
new file mode 100644
index 000000000..d41ba8065
--- /dev/null
+++ b/org.eclipse.jface.text/fragment.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fragment
+ id="org.eclipse.jface.text"
+ name="%fragmentName"
+ version="2.1.0"
+ provider-name="%providerName"
+ plugin-id="org.eclipse.jface"
+ plugin-version="2.1.0">
+
+ <runtime>
+ <library name="jfacetext.jar">
+ <export name="*"/>
+ </library>
+ </runtime>
+
+ <requires>
+ <import plugin="org.eclipse.text" export="true"/>
+ </requires>
+
+
+</fragment>
diff --git a/org.eclipse.jface.text/scripts/exportplugin.xml b/org.eclipse.jface.text/scripts/exportplugin.xml
new file mode 100644
index 000000000..ab3d52a4f
--- /dev/null
+++ b/org.eclipse.jface.text/scripts/exportplugin.xml
@@ -0,0 +1,28 @@
+<project name="Export JFace Text" default="export" basedir="..">
+ <target name="init">
+ <tstamp/>
+ <property name="destdir" value="../../plugin-export" />
+ <property name="plugin" value="org.eclipse.jface.text" />
+ <property name="version" value="_2.1.0" />
+ <property name="dest" value="${destdir}/${plugin}${version}" />
+ </target>
+
+ <target name="build" depends="init">
+ <eclipse.incrementalBuild project="${plugin}" kind="incr"/>
+ </target>
+
+ <target name="export" depends="build">
+ <mkdir dir="${destdir}" />
+ <delete dir="${dest}" />
+ <mkdir dir="${dest}" />
+ <jar
+ jarfile="${dest}/jfacetext.jar"
+ basedir="bin"
+ />
+ <copy file="fragment.xml" todir="${dest}"/>
+ <copy file="fragment.properties" todir="${dest}"/>
+ <zip zipfile="${dest}/jfacetextsrc.zip">
+ <fileset dir="src" />
+ </zip>
+ </target>
+</project>
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java
new file mode 100644
index 000000000..48cb94064
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java
@@ -0,0 +1,464 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.jface.util.Assert;
+
+
+/**
+ * An information control manager that shows information on mouse hover events.
+ * The mouse hover events are caught by registering a <code>MouseTrackListener</code>
+ * on the manager's subject control. The manager has by default an information control closer
+ * that closes the information control as soon as the mouse pointer leaves the subject area, the
+ * user presses a key, or the subject control is resized, moved, or deactivated.<p>
+ * When being activated by a mouse hover event, the manager disables itself, until the mouse
+ * leaves the subject area. Thus, the manager is usually still disabled, when the information control
+ * has already been closed by the closer.
+ *
+ * @see org.eclipse.swt.events.MouseTrackListener
+ * @since 2.0
+ */
+abstract public class AbstractHoverInformationControlManager extends AbstractInformationControlManager {
+
+
+ /**
+ * The information control closer for the hover information. Closes the information control as
+ * soon as the mouse pointer leaves the subject area, the user presses a key, or the subject
+ * control is resized or moved.
+ */
+ class Closer extends MouseTrackAdapter
+ implements IInformationControlCloser, MouseListener, MouseMoveListener, ControlListener, KeyListener {
+
+ /** The closer's subject control */
+ private Control fSubjectControl;
+ /** The closer's information control */
+ private IInformationControl fInformationControl;
+ /** The subject area */
+ private Rectangle fSubjectArea;
+ /** Indicates whether this closer is active */
+ private boolean fIsActive= false;
+
+ /**
+ * Creates a new information control closer.
+ */
+ public Closer() {
+ }
+
+ /*
+ * @see IInformationControlCloser#setSubjectControl(Control)
+ */
+ public void setSubjectControl(Control control) {
+ fSubjectControl= control;
+ }
+
+ /*
+ * @see IInformationControlCloser#setHoverControl(IHoverControl)
+ */
+ public void setInformationControl(IInformationControl control) {
+ fInformationControl= control;
+ }
+
+ /*
+ * @see IInformationControlCloser#start(Rectangle)
+ */
+ public void start(Rectangle subjectArea) {
+
+ if (fIsActive)
+ return;
+ fIsActive= true;
+
+ fSubjectArea= subjectArea;
+
+ setEnabled(false);
+
+ if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
+ fSubjectControl.addMouseListener(this);
+ fSubjectControl.addMouseMoveListener(this);
+ fSubjectControl.addMouseTrackListener(this);
+ fSubjectControl.addControlListener(this);
+ fSubjectControl.addKeyListener(this);
+ }
+ }
+
+ /*
+ * @see IInformationControlCloser#stop()
+ */
+ public void stop() {
+ stop(false);
+ }
+
+ /**
+ * Stops the information control and if <code>delayRestart</code> is set
+ * allows restart only after a certain delay.
+ *
+ * @param delayRestart <code>true</code> if restart should be delayed
+ */
+ protected void stop(boolean delayRestart) {
+
+ if (!fIsActive)
+ return;
+ fIsActive= false;
+
+ hideInformationControl();
+
+ if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
+ fSubjectControl.removeMouseListener(this);
+ fSubjectControl.removeMouseMoveListener(this);
+ fSubjectControl.removeMouseTrackListener(this);
+ fSubjectControl.removeControlListener(this);
+ fSubjectControl.removeKeyListener(this);
+ }
+ }
+
+ /*
+ * @see MouseMoveListener#mouseMove
+ */
+ public void mouseMove(MouseEvent event) {
+ if (!fSubjectArea.contains(event.x, event.y))
+ stop();
+ }
+
+ /*
+ * @see MouseListener#mouseUp(MouseEvent)
+ */
+ public void mouseUp(MouseEvent event) {
+ }
+
+ /*
+ * @see MouseListener#mouseDown(MouseEvent)
+ */
+ public void mouseDown(MouseEvent event) {
+ stop();
+ }
+
+ /*
+ * @see MouseListener#mouseDoubleClick(MouseEvent)
+ */
+ public void mouseDoubleClick(MouseEvent event) {
+ stop();
+ }
+
+ /*
+ * @see MouseTrackAdapter#mouseExit(MouseEvent)
+ */
+ public void mouseExit(MouseEvent event) {
+ stop();
+ }
+
+ /*
+ * @see ControlListener#controlResized(ControlEvent)
+ */
+ public void controlResized(ControlEvent event) {
+ stop();
+ }
+
+ /*
+ * @see ControlListener#controlMoved(ControlEvent)
+ */
+ public void controlMoved(ControlEvent event) {
+ stop();
+ }
+
+ /*
+ * @see KeyListener#keyReleased(KeyEvent)
+ */
+ public void keyReleased(KeyEvent event) {
+ }
+
+ /*
+ * @see KeyListener#keyPressed(KeyEvent)
+ */
+ public void keyPressed(KeyEvent event) {
+ stop(true);
+ }
+ };
+
+
+ /**
+ * To be installed on the manager's subject control. Serves two different purposes:
+ * <ul>
+ * <li> start function: initiates the computation of the information to be presented. This happens on
+ * receipt of a mouse hover event and disables the information control manager,
+ * <li> restart function: tracks mouse move and shell activation event to determine when the information
+ * control manager needs to be reactivated.
+ * </ul>
+ */
+ class MouseTracker extends ShellAdapter implements MouseTrackListener, MouseMoveListener {
+
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18393
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19686
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19719
+
+ /** Margin around the original hover event location for coputing the hover area. */
+ private final static int EPSILON= 3;
+
+ /** The area in which the original hover event occurred. */
+ private Rectangle fHoverArea;
+ /** The area for which is computed information is valid. */
+ private Rectangle fSubjectArea;
+ /** The tracker's subject control. */
+ private Control fSubjectControl;
+
+ /** Indicates whether the tracker is computing the start function. */
+ private boolean fIsActive= false;
+ /** Indicates whether the mouse has been lost. */
+ private boolean fMouseLost= false;
+ /** Indicates whether the subject control's shelll has been deactivated. */
+ private boolean fShellDeactivated= false;
+
+ /**
+ * Creates a new mouse tracker.
+ */
+ public MouseTracker() {
+ }
+
+ /**
+ * Sets this mouse tracker's subject area, the area to be tracked in order
+ * to reenable the information control manager.
+ *
+ * @param subjectArea the subject area
+ */
+ public void setSubjectArea(Rectangle subjectArea) {
+ Assert.isNotNull(subjectArea);
+ fSubjectArea= subjectArea;
+ }
+
+ /**
+ * Starts this mouse tracker. The given control becomes this tracker's subject control.
+ * Installs itself as mouse track listener on the subject control.
+ *
+ * @param subjectControl the subject control
+ */
+ public void start(Control subjectControl) {
+ fSubjectControl= subjectControl;
+ if (fSubjectControl != null && !fSubjectControl.isDisposed())
+ fSubjectControl.addMouseTrackListener(this);
+
+ fIsActive= false;
+ fMouseLost= false;
+ fShellDeactivated= false;
+ }
+
+ /**
+ * Stops this mouse tracker. Removes itself as mouse track, mouse move, and
+ * shell listener from the subject control.
+ */
+ public void stop() {
+ if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
+ fSubjectControl.removeMouseTrackListener(this);
+ fSubjectControl.removeMouseMoveListener(this);
+ fSubjectControl.getShell().removeShellListener(this);
+ }
+
+ fMouseLost= false;
+ fShellDeactivated= false;
+ }
+
+ /**
+ * Initiates the computation of the information to be presented. Sets the initial hover area
+ * to a small rectangle around the hover event location. Adds mouse move and shell activation listeners
+ * to track whether the computed information is, after completion, useful for presentation and to
+ * implement the restart function.
+ */
+ public void mouseHover(MouseEvent event) {
+
+ if (fIsActive)
+ return;
+
+ fIsActive= true;
+ fMouseLost= false;
+ fShellDeactivated= false;
+
+ setEnabled(false);
+
+ fHoverEventLocation= new Point(event.x, event.y);
+ fHoverArea= new Rectangle(event.x - EPSILON, event.y - EPSILON, 2 * EPSILON, 2 * EPSILON );
+ if (fHoverArea.x < 0) fHoverArea.x= 0;
+ if (fHoverArea.y < 0) fHoverArea.y= 0;
+ setSubjectArea(fHoverArea);
+
+ if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
+ fSubjectControl.addMouseMoveListener(this);
+ fSubjectControl.getShell().addShellListener(this);
+ }
+
+ doShowInformation();
+ }
+
+ /**
+ * Deactivates this tracker's restart function and enables the information control
+ * manager. Does not have any effect if the tracker is still executing the start function (i.e.
+ * computing the information to be presented.
+ */
+ protected void deactivate() {
+ if (fIsActive)
+ return;
+
+ if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
+ fSubjectControl.removeMouseMoveListener(this);
+ fSubjectControl.getShell().removeShellListener(this);
+ }
+
+ setEnabled(true);
+ }
+
+ /*
+ * @see MouseTrackListener#mouseEnter(MouseEvent)
+ */
+ public void mouseEnter(MouseEvent e) {
+ }
+
+ /*
+ * @see MouseTrackListener#mouseExit(MouseEvent)
+ */
+ public void mouseExit(MouseEvent e) {
+ fMouseLost= true;
+ deactivate();
+ }
+
+ /*
+ * @see MouseMoveListener#mouseMove(MouseEvent)
+ */
+ public void mouseMove(MouseEvent event) {
+ if (!fSubjectArea.contains(event.x, event.y))
+ deactivate();
+ }
+
+ /*
+ * @see ShellListener#shellDeactivated(ShellEvent)
+ */
+ public void shellDeactivated(ShellEvent e) {
+ fShellDeactivated= true;
+ deactivate();
+ }
+
+ /*
+ * @see ShellListener#shellIconified(ShellEvent)
+ */
+ public void shellIconified(ShellEvent e) {
+ fShellDeactivated= true;
+ deactivate();
+ }
+
+ /**
+ * Tells this tracker that the start function processing has been completed.
+ */
+ public void computationCompleted() {
+ fIsActive= false;
+ fMouseLost= false;
+ fShellDeactivated= false;
+ }
+
+ /**
+ * Determines whether the computed information is still useful for presentation.
+ * This is the case, if the shell of the subject control has been deactivated, the mouse
+ * left the subject control, or the mouse moved on, so that it is no longer in the subject
+ * area.
+ *
+ * @return <code>true</code> if information is still useful for presentation, <code>false</code> otherwise
+ */
+ public boolean isMouseLost() {
+
+ if (fMouseLost || fShellDeactivated)
+ return true;
+
+ if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
+ Display display= fSubjectControl.getDisplay();
+ Point p= display.getCursorLocation();
+ p= fSubjectControl.toControl(p);
+ if (!fSubjectArea.contains(p) && !fHoverArea.contains(p))
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ /** The mouse tracker on the subject control */
+ private MouseTracker fMouseTracker= new MouseTracker();
+ /** The remembered hover event location */
+ private Point fHoverEventLocation= new Point(-1, -1);
+
+ /**
+ * Creates a new hover information control manager using the given information control creator.
+ * By default a <code>Closer</code> instance is set as this manager's closer.
+ *
+ * @param creator the information control creator
+ */
+ protected AbstractHoverInformationControlManager(IInformationControlCreator creator) {
+ super(creator);
+ setCloser(new Closer());
+ }
+
+ /*
+ * @see AbstractInformationControlManager#presentInformation()
+ */
+ protected void presentInformation() {
+
+ Rectangle area= getSubjectArea();
+ if (area != null)
+ fMouseTracker.setSubjectArea(area);
+
+ if (fMouseTracker.isMouseLost()) {
+ fMouseTracker.computationCompleted();
+ fMouseTracker.deactivate();
+ } else {
+ fMouseTracker.computationCompleted();
+ super.presentInformation();
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#setEnabled(boolean)
+ */
+ public void setEnabled(boolean enabled) {
+
+ boolean was= isEnabled();
+ super.setEnabled(enabled);
+ boolean is= isEnabled();
+
+ if (was != is) {
+ if (is)
+ fMouseTracker.start(getSubjectControl());
+ else
+ fMouseTracker.stop();
+ }
+ }
+
+ /**
+ * Returns the location at which the most recent mouse hover event
+ * has been issued.
+ *
+ * @return the location of the most recent mouse hover event
+ */
+ protected Point getHoverEventLocation() {
+ return fHoverEventLocation;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java
new file mode 100644
index 000000000..d9d8c54b8
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java
@@ -0,0 +1,680 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.jface.util.Assert;
+
+
+/**
+ * Manages the life cycle, visibility, layout, and contents of an <code>IInformationControl</code>.
+ * This manager can be installed on and uninstalled from a control, refered to as the subject control, i.e.
+ * the one from which the subject of the information to be shown is retrieved. Also a manager can
+ * be enabled or disabled. An installed and enabled manager can be forced to show information in
+ * its information control using <code>showInformation</code>. An information control
+ * manager uses an <code>IInformationControlCloser</code> to define the behavior when
+ * a presented information control must be closed. The disposal of the subject and the information
+ * control are internally handled by the information control manager and are not the responsibility
+ * of the information control closer.
+ *
+ * @since 2.0
+ */
+abstract public class AbstractInformationControlManager {
+
+ /**
+ * Interface of a information control closer. An information control closer
+ * monitors its information control and its subject control and closes
+ * the information control if necessary. <p>
+ * Clients must implement this interface in order to equipe an information
+ * control manager accordingly.
+ */
+ public static interface IInformationControlCloser {
+
+ /**
+ * Sets the closer's subject control. This is the control that parents
+ * the information control and from which the subject of the information
+ * to be shown is retrieved. <p>
+ * Must be called before <code>start</code>. May again be called
+ * between <code>start</code> and <code>stop</code>.
+ *
+ * @param subject the subject control
+ */
+ public void setSubjectControl(Control subject);
+
+ /**
+ * Sets the closer's information control, the one to close if necessary. <p>
+ * Must be called before <code>start</code>. May again be called
+ * between <code>start</code> and <code>stop</code>.
+ *
+ * @param control the information control
+ */
+ public void setInformationControl(IInformationControl control);
+
+ /**
+ * Tells this closer to start monitoring the subject and the information
+ * control. The presented information is considered valid for the given
+ * area of the subject control's display.
+ *
+ * @param subjectArea the area for which the presented information is valid
+ */
+ public void start(Rectangle subjectArea);
+
+ /**
+ * Tells this closer to stop monitoring the subject and the information control.
+ */
+ public void stop();
+ };
+
+
+
+ /**
+ * Constitues entities to enumerate anchors for the layout of the information control.
+ */
+ public static final class Anchor {
+ private Anchor() {
+ };
+ };
+
+ /** Internal anchor list. */
+ private final static Anchor[] ANCHORS= { new Anchor(), new Anchor(), new Anchor(), new Anchor() };
+
+ /** Anchor representing the top of the information area */
+ public final static Anchor ANCHOR_TOP= ANCHORS[0];
+ /** Anchor representing the bottom of the information area */
+ public final static Anchor ANCHOR_BOTTOM= ANCHORS[1];
+ /** Anchor representing the left side of the information area */
+ public final static Anchor ANCHOR_LEFT= ANCHORS[2];
+ /** Anchor representing the right side of the information area */
+ public final static Anchor ANCHOR_RIGHT= ANCHORS[3];
+
+
+
+ /** The subject control of the information control */
+ private Control fSubjectControl;
+
+ /** The display area for which the information to be presented is valid */
+ private Rectangle fSubjectArea;
+
+ /** The information to be presented */
+ private String fInformation;
+
+ /** Indicates whether the information control takes focus when visible */
+ private boolean fTakesFocusWhenVisible= false;
+
+ /** The information control */
+ private IInformationControl fInformationControl;
+
+ /** The information control creator */
+ private IInformationControlCreator fInformationControlCreator;
+
+ /** The information control closer */
+ private IInformationControlCloser fInformationControlCloser;
+
+ /** Indicates that the information control has been disposed */
+ private boolean fDisposed= false;
+
+ /** Indicates the enable state of this manager */
+ private boolean fEnabled= false;
+
+ /** Cached, computed size constraints of the information control in points */
+ private Point fSizeConstraints;
+
+ /** The y margin when laying out the information control */
+ private int fMarginY= 5;
+
+ /** The x margin when laying out the information control */
+ private int fMarginX= 5;
+
+ /** The width contraint of the information control in characters */
+ private int fWidthConstraint= 60;
+
+ /** The height constraint of the information control in characters */
+ private int fHeightConstraint= 6;
+
+ /** Indicates wether the size constraints should be enforced as minimal control size */
+ private boolean fEnforceAsMinimalSize= false;
+
+ /** Indicates whether the size constraints should be enforced as maximal control size */
+ private boolean fEnforceAsMaximalSize= false;
+
+ /** The anchor for laying out the information control in relation to the subject control */
+ private Anchor fAnchor= ANCHOR_BOTTOM;
+
+ /**
+ * A list of anchors used to layout the information control if the original anchor can not
+ * be used because the information control would not fit in the display client area.
+ */
+ private Anchor[] fFallbackAnchors= ANCHORS;
+
+
+ /**
+ * Creates a new information control manager using the given information control creator.
+ * By default the following configuration is given:
+ * <ul>
+ * <li> enabled == false
+ * <li> x-margin == 5 points
+ * <li> y-margin == 5 points
+ * <li> width constraint == 60 characters
+ * <li> height constraint == 6 characters
+ * <li> enforce constraints as minimal size == false
+ * <li> enforce constraints as maximal size == false
+ * <li> layout anchor == ANCHOR_BOTTOM
+ * <li> fallback anchors == { ANCHOR_TOP, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_RIGHT }
+ * <li> takes focus when visible == false
+ * </ul>
+ *
+ * @param creator the information control creator
+ */
+ protected AbstractInformationControlManager(IInformationControlCreator creator) {
+ Assert.isNotNull(creator);
+ fInformationControlCreator= creator;
+ }
+
+ /**
+ * Computes the information to be displayed and the area in which the computed
+ * information is valid. Implementation of this method must finish their computation
+ * by setting the computation results using <code>setInformation</code>.
+ */
+ abstract protected void computeInformation();
+
+ /**
+ * Sets the parameters of the information to be displayed. These are the information itself and
+ * the area for which the given information is valid. This so called subject area is a graphical
+ * region of the information control's subject control. This method calls <code>presentInformation()</code>
+ * to trigger the presentation of the computed information.
+ *
+ * @param information the information
+ * @param subjectArea the subject area
+ */
+ protected final void setInformation(String information, Rectangle subjectArea) {
+ fInformation= information;
+ fSubjectArea= subjectArea;
+ presentInformation();
+ }
+
+ /**
+ * Sets the information control closer for this manager.
+ *
+ * @param closer the information control closer for this manager
+ */
+ protected void setCloser(IInformationControlCloser closer) {
+ fInformationControlCloser= closer;
+ }
+
+ /**
+ * Sets the x- and y- margin to be used when laying out the information control
+ * relative to the subject control.
+ *
+ * @param xMargin the x-margin
+ * @param yMargin the y-Margin
+ */
+ public void setMargins(int xMargin, int yMargin) {
+ fMarginX= xMargin;
+ fMarginY= yMargin;
+ }
+
+ /**
+ * Sets the width- and height constraints of the information control.
+ *
+ * @param widthInChar the width constraint in number of characters
+ * @param heightInChar the height constrain in number of characters
+ * @param enforceAsMinimalSize indicates whether the constraints describe the minimal allowed size of the control
+ * @param enforceAsMaximalSize indicates whether the constraints describe the maximal allowed size of the control
+ */
+ public void setSizeConstraints(int widthInChar, int heightInChar, boolean enforceAsMinimalSize, boolean enforceAsMaximalSize) {
+ fSizeConstraints= null;
+ fWidthConstraint= widthInChar;
+ fHeightConstraint= heightInChar;
+ fEnforceAsMinimalSize= enforceAsMinimalSize;
+ fEnforceAsMaximalSize= enforceAsMaximalSize;
+ }
+
+ /**
+ * Sets the anchor used for laying out the information control relative to the
+ * subject control. E.g, using <code>ANCHOR_TOP</code> indicates that the
+ * information control is position above the area for which the information to
+ * be displayed is valid.
+ *
+ * @param anchor the layout anchor
+ */
+ public void setAnchor(Anchor anchor) {
+ fAnchor= anchor;
+ }
+
+ /**
+ * Sets the sequence of anchors along which the information control is tried to
+ * be laid out until it is fully visible. This fallback is initiated when the information
+ * control does not fit into the client area of the subject control's display.
+ *
+ * @param fallbackAnchors the list of anchors to be tried
+ */
+ public void setFallbackAnchors(Anchor[] fallbackAnchors) {
+ fFallbackAnchors= fallbackAnchors;
+ }
+
+ /**
+ * Tells the manager whether it should set the focus to the information control when made visible.
+ *
+ * @param takesFocus <code>true</code> if information control should take focus when made visible
+ */
+ public void takesFocusWhenVisible(boolean takesFocus) {
+ fTakesFocusWhenVisible= takesFocus;
+ }
+
+ /**
+ * Handles the disposal of the subject control. By default, the information control
+ * is disposed by calling <code>disposeInformationControl</code>. Subclasses may extend
+ * this method.
+ */
+ protected void handleSubjectControlDisposed() {
+ disposeInformationControl();
+ }
+
+ /**
+ * Installs this manager on the given control. The control is now taking the role of
+ * the subject control. This implementation sets the control also as the information
+ * control closer's subject control and automatically enables this manager.
+ *
+ * @param subjectControl the subject control
+ */
+ public void install(Control subjectControl) {
+ fSubjectControl= subjectControl;
+
+ if (fSubjectControl != null) {
+ fSubjectControl.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ handleSubjectControlDisposed();
+ }
+ });
+ }
+
+ if (fInformationControlCloser != null)
+ fInformationControlCloser.setSubjectControl(subjectControl);
+
+ setEnabled(true);
+ }
+
+ /**
+ * Returns the subject control of this manager/information control.
+ *
+ * @return the subject control
+ */
+ protected Control getSubjectControl() {
+ return fSubjectControl;
+ }
+
+ /**
+ * Returns the actual subject area.
+ *
+ * @return the actual subject area
+ */
+ protected Rectangle getSubjectArea() {
+ return fSubjectArea;
+ }
+
+ /**
+ * Sets the enable state of this manager.
+ *
+ * @param enabled the enable state
+ * @deprecated visibility will be changed to protected
+ */
+ public void setEnabled(boolean enabled) {
+ fEnabled= enabled;
+ }
+
+ /**
+ * Returns whether this manager is enabled or not.
+ *
+ * @return <code>true</code> if this manager is enabled otherwise <code>false</code>
+ */
+ protected boolean isEnabled() {
+ return fEnabled;
+ }
+
+ /**
+ * Computes the size constraints of the information control in points based on the
+ * default font of the given subject control as well as the size constraints in character
+ * width.
+ *
+ * @param subjectControl the subject control
+ * @param informationControl the information control whose size constraints are computed
+ * @return the computed size constraints in points
+ */
+ protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
+
+ if (fSizeConstraints == null) {
+
+ if (subjectControl == null)
+ return null;
+
+ GC gc= new GC(subjectControl);
+ gc.setFont(subjectControl.getFont());
+ int width= gc.getFontMetrics().getAverageCharWidth();
+ int height = gc.getFontMetrics().getHeight();
+ gc.dispose();
+
+ fSizeConstraints= new Point (fWidthConstraint * width, fHeightConstraint * height);
+ }
+
+ return fSizeConstraints;
+ }
+
+ /**
+ * Handles the disposal of the information control. By default, the information
+ * control closer is stopped.
+ */
+ protected void handleInformationControlDisposed() {
+ fInformationControl= null;
+ if (fInformationControlCloser != null) {
+ fInformationControlCloser.setInformationControl(null);
+ fInformationControlCloser.stop();
+ }
+ }
+
+ /**
+ * Returns the information control. If the information control has not been created yet,
+ * it is automatically created.
+ *
+ * @return the information control
+ */
+ protected IInformationControl getInformationControl() {
+ if (fInformationControl == null && !fDisposed) {
+
+ fInformationControl= fInformationControlCreator.createInformationControl(fSubjectControl.getShell());
+
+ fInformationControl.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ handleInformationControlDisposed();
+ }
+ });
+
+ if (fInformationControlCloser != null)
+ fInformationControlCloser.setInformationControl(fInformationControl);
+ }
+ return fInformationControl;
+ }
+
+ /**
+ * Computes the display location of the information control. The location is computed
+ * considering the given subject area, the anchor at the subject area, and the
+ * size of the information control. This method does not care about whether the information
+ * control would be completely visible when placed at the result location.
+ *
+ * @param subjectArea the subject area
+ * @param controlSize the size of the information control
+ * @param anchor the anchor at the subject area
+ */
+ protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) {
+
+ int xShift= 0;
+ int yShift= 0;
+
+ if (ANCHOR_BOTTOM == anchor) {
+ xShift= fMarginX;
+ yShift= subjectArea.height + fMarginY;
+ } else if (ANCHOR_RIGHT == anchor) {
+ xShift= fMarginX + subjectArea.width;
+ yShift= fMarginY;
+ } else if (ANCHOR_TOP == anchor) {
+ xShift= fMarginX;
+ yShift= -controlSize.y - fMarginY;
+ } else if (ANCHOR_LEFT == anchor) {
+ xShift= -controlSize.x - fMarginX;
+ yShift= fMarginY;
+ }
+
+ return fSubjectControl.toDisplay(new Point(subjectArea.x + xShift, subjectArea.y + yShift));
+ }
+
+ /**
+ * Checks whether a control of the given size at the given location would be completely visible
+ * in the given display area when laid out by using the given anchor. If not, this method tries
+ * to shift the control orthogonal to the direction given by the anchor to make it visible. If possible
+ * it updates the location.<p>
+ * This method returns <code>true</code> if the potentially updated position results in a
+ * completely visible control, or <code>false</code> otherwise.
+ *
+ *
+ * @param location the location of the control
+ * @param size the size of the control
+ * @param displayArea the display area in which the control should be visible
+ * @param anchor anchor for alying out the control
+ * @return <code>true</code>if the updated location is useful
+ */
+ protected boolean updateLocation(Point location, Point size, Rectangle displayArea, Anchor anchor) {
+
+ int displayLowerRightX= displayArea.x + displayArea.width;
+ int displayLowerRightY= displayArea.y + displayArea.height;
+ int lowerRightX= location.x + size.x;
+ int lowerRightY= location.y + size.y;
+
+ if (ANCHOR_BOTTOM == anchor || ANCHOR_TOP == anchor) {
+
+ if (ANCHOR_BOTTOM == anchor) {
+ if (lowerRightY > displayLowerRightY)
+ return false;
+ } else {
+ if (location.y < displayArea.y)
+ return false;
+ }
+
+ if (lowerRightX > displayLowerRightX)
+ location.x= location.x - (lowerRightX - displayLowerRightX);
+ return true;
+
+ } else if (ANCHOR_RIGHT == anchor || ANCHOR_LEFT == anchor) {
+
+ if (ANCHOR_RIGHT == anchor) {
+ if (lowerRightX > displayLowerRightX)
+ return false;
+ } else {
+ if (location.x < displayArea.x)
+ return false;
+ }
+
+ if (lowerRightY > displayLowerRightY)
+ location.y= location.y - (lowerRightY - displayLowerRightY);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the next fallback anchor from this manager's list of fallback anchors.
+ * If no more fallback anchor is available <code>null</code> is returned.
+ *
+ * @param anchor the current anchor
+ * @return the next fallback anchor or <code>null</code> if no more anchor is available
+ */
+ protected Anchor getNextFallbackAnchor(Anchor anchor) {
+
+ if (anchor == null || fFallbackAnchors == null)
+ return null;
+
+ for (int i= 0; i < fFallbackAnchors.length; i++) {
+ if (fFallbackAnchors[i] == anchor)
+ return fFallbackAnchors[i + 1 == fFallbackAnchors.length ? 0 : i + 1];
+ }
+
+ return null;
+ }
+
+ /**
+ * Computes the location of the information control depending on the
+ * subject area and the size of the information control. This method attempts
+ * to find a location at which the information control lies completely in the display's
+ * client area honoring the manager's default anchor. If this isn't possible using the
+ * default anchor, the fallback anchors are tried out.
+ *
+ * @param subjectArea the information area
+ * @param controlSize the size of the information control
+ * @return the computed location of the information control
+ */
+ protected Point computeInformationControlLocation(Rectangle subjectArea, Point controlSize) {
+
+ Rectangle displayBounds= fSubjectControl.getDisplay().getClientArea();
+
+ Point upperLeft;
+ Anchor testAnchor= fAnchor;
+ do {
+
+ upperLeft= computeLocation(subjectArea, controlSize, testAnchor);
+ if (updateLocation(upperLeft, controlSize, displayBounds, testAnchor))
+ break;
+ testAnchor= getNextFallbackAnchor(testAnchor);
+
+ } while (testAnchor != fAnchor && testAnchor != null);
+
+ return upperLeft;
+ }
+
+ /**
+ * Computes information to be displayed as well as the subject area
+ * and initiates that this information is presented in the information control.
+ * This happens only if this controller is enabled.
+ */
+ public void showInformation() {
+ if (fEnabled)
+ doShowInformation();
+ }
+
+ /**
+ * Computes information to be displayed as well as the subject area
+ * and initiates that this information is presented in the information control.
+ */
+ protected void doShowInformation() {
+ fSubjectArea= null;
+ fInformation= null;
+ computeInformation();
+ }
+
+ /**
+ * Presents the information in the information control or hides the information
+ * control if no information should be presented. The information has previously
+ * been set using <code>setInformation</code>.
+ */
+ protected void presentInformation() {
+ if (fSubjectArea != null && fInformation != null && fInformation.trim().length() > 0)
+ internalShowInformationControl(fSubjectArea, fInformation);
+ else
+ hideInformationControl();
+ }
+
+ /**
+ * Opens the information control with the given information and the specified
+ * subject area. It also activates the information control closer.
+ *
+ * @param subjectArea the information area
+ * @param information the information
+ */
+ private void internalShowInformationControl(Rectangle subjectArea, String information) {
+
+ IInformationControl hoverControl= getInformationControl();
+ if (hoverControl != null) {
+
+ Point sizeConstraints= computeSizeConstraints(fSubjectControl, hoverControl);
+ hoverControl.setSizeConstraints(sizeConstraints.x, sizeConstraints.y);
+ hoverControl.setInformation(information);
+
+ if (hoverControl instanceof IInformationControlExtension) {
+ IInformationControlExtension extension= (IInformationControlExtension) hoverControl;
+ if (!extension.hasContents())
+ return;
+ }
+
+ Point size= hoverControl.computeSizeHint();
+
+ if (fEnforceAsMinimalSize) {
+ if (size.x < sizeConstraints.x)
+ size.x= sizeConstraints.x;
+ if (size.y < sizeConstraints.y)
+ size.y= sizeConstraints.y;
+ }
+
+ if (fEnforceAsMaximalSize) {
+ if (size.x > sizeConstraints.x)
+ size.x= sizeConstraints.x;
+ if (size.y > sizeConstraints.y)
+ size.y= sizeConstraints.y;
+ }
+
+ hoverControl.setSize(size.x, size.y);
+
+ Point location= computeInformationControlLocation(subjectArea, size);
+ hoverControl.setLocation(location);
+
+ showInformationControl(subjectArea);
+ }
+ }
+
+ /**
+ * Hides the information control and stops the information control closer.
+ */
+ protected void hideInformationControl() {
+ if (fInformationControl != null) {
+ fInformationControl.setVisible(false);
+ if (fInformationControlCloser != null)
+ fInformationControlCloser.stop();
+ }
+ }
+
+ /**
+ * Shows the information control and starts the information control closer.
+ * This method may not be called by clients.
+ *
+ * @param subjectArea the information area
+ */
+ protected void showInformationControl(Rectangle subjectArea) {
+ fInformationControl.setVisible(true);
+
+ if (fTakesFocusWhenVisible)
+ fInformationControl.setFocus();
+
+ if (fInformationControlCloser != null)
+ fInformationControlCloser.start(subjectArea);
+ }
+
+ /**
+ * Disposes this manager's information control.
+ */
+ public void disposeInformationControl() {
+ if (fInformationControl != null) {
+ fInformationControl.dispose();
+ handleInformationControlDisposed();
+ }
+ }
+
+ /**
+ * Disposes this manager and if necessary all dependent parts such as
+ * the information control. For symmetry it first disables this manager.
+ */
+ public void dispose() {
+ if (!fDisposed) {
+
+ fDisposed= true;
+
+ setEnabled(false);
+ disposeInformationControl();
+
+ fInformationControlCreator= null;
+ fInformationControlCloser= null;
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultAutoIndentStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultAutoIndentStrategy.java
new file mode 100644
index 000000000..7b63ae3d6
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultAutoIndentStrategy.java
@@ -0,0 +1,85 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+
+/**
+ * Default implementation of <code>IAutoIndentStrategy</code>.
+ * This strategy always copies the indentation of the previous line.
+ */
+public class DefaultAutoIndentStrategy implements IAutoIndentStrategy {
+
+ /**
+ * Creates a new default auto indent strategy which can be installed on
+ * text viewers.
+ */
+ public DefaultAutoIndentStrategy() {
+ }
+
+ /**
+ * Returns the first offset greater than <code>offset</code> and smaller than
+ * <code>end</code> whose character is not a space or tab character. If no such
+ * offset is found, <code>end</code> is returned.
+ *
+ * @param document the document to search in
+ * @param offset the offset at which searching start
+ * @param end the offset at which searching stops
+ * @return the offset in the specifed range whose character is not a space or tab
+ * @exception BadLocationException if position is an invalid range in the given document
+ */
+ protected int findEndOfWhiteSpace(IDocument document, int offset, int end) throws BadLocationException {
+ while (offset < end) {
+ char c= document.getChar(offset);
+ if (c != ' ' && c != '\t') {
+ return offset;
+ }
+ offset++;
+ }
+ return end;
+ }
+
+ /**
+ * Copies the indentation of the previous line.
+ *
+ * @param d the document to work on
+ * @param c the command to deal with
+ */
+ private void autoIndentAfterNewLine(IDocument d, DocumentCommand c) {
+
+ if (c.offset == -1 || d.getLength() == 0)
+ return;
+
+ try {
+ // find start of line
+ int p= (c.offset == d.getLength() ? c.offset - 1 : c.offset);
+ IRegion info= d.getLineInformationOfOffset(p);
+ int start= info.getOffset();
+
+ // find white spaces
+ int end= findEndOfWhiteSpace(d, start, c.offset);
+
+ StringBuffer buf= new StringBuffer(c.text);
+ if (end > start) {
+ // append to input
+ buf.append(d.get(start, end - start));
+ }
+
+ c.text= buf.toString();
+
+ } catch (BadLocationException excp) {
+ // stop work
+ }
+ }
+
+ /*
+ * @see IAutoIndentStrategy#customizeDocumentCommand
+ */
+ public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
+ if (c.length == 0 && c.text != null && TextUtilities.endsWith(d.getLegalLineDelimiters(), c.text) != -1)
+ autoIndentAfterNewLine(d, c);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultInformationControl.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultInformationControl.java
new file mode 100644
index 000000000..e1d2294fb
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultInformationControl.java
@@ -0,0 +1,284 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+
+
+/**
+ * Text based implementation of <code>IInformationControl</code>.
+ * Displays information in a styled text widget. Before displaying, the
+ * information set to this information control is processed by an
+ * <code>IInformationPresenter</code>.
+ *
+ * @since 2.0
+ */
+public class DefaultInformationControl implements IInformationControl, IInformationControlExtension {
+
+ /**
+ * An information presenter determines the style presentation
+ * of information displayed in the default information control.
+ * The interface can be implemented by clients.
+ */
+ public static interface IInformationPresenter {
+
+ /**
+ * Updates the given presentation of the given information and
+ * thereby may manipulate the information to be displayed. The manipulation
+ * could be the extraction of textual encoded style information etc. Returns the
+ * manipulated information.
+ *
+ * @param display the display of the information control
+ * @param hoverInfo the information to be presented
+ * @param presentation the presentation to be updated
+ * @param maxWidth the maximal width in pixels
+ * @param maxHeight the maximal height in pixels
+ *
+ * @return the manipulated information
+ */
+ String updatePresentation(Display display, String hoverInfo, TextPresentation presentation, int maxWidth, int maxHeight);
+ };
+
+
+ /** Border thickness in pixels. */
+ private static final int BORDER= 1;
+ /** Right margin in pixels. */
+ private static final int RIGHT_MARGIN= 3;
+
+ /** The control's shell */
+ private Shell fShell;
+ /** The control's text widget */
+ private StyledText fText;
+ /** The information presenter */
+ private IInformationPresenter fPresenter;
+ /** A cached text presentation */
+ private TextPresentation fPresentation= new TextPresentation();
+ /** The control width constraint */
+ private int fMaxWidth= -1;
+ /** The control height constraint */
+ private int fMaxHeight= -1;
+
+
+
+ /**
+ * Creates a default information control with the given shell as parent. The given
+ * information presenter is used to process the information to be displayed. The given
+ * styles are applied to the created styled text widget.
+ *
+ * @param parent the parent shell
+ * @param presenter the presenter to be used
+ * @param style the additional styles for the styled text widget
+ */
+ public DefaultInformationControl(Shell parent, int style, IInformationPresenter presenter) {
+
+ fShell= new Shell(parent, SWT.NO_FOCUS | SWT.NO_TRIM | SWT.ON_TOP);
+ fText= new StyledText(fShell, SWT.MULTI | SWT.READ_ONLY | style);
+
+ Display display= fShell.getDisplay();
+
+ fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+
+ fText.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+ fText.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+
+ fText.addKeyListener(new KeyListener() {
+
+ public void keyPressed(KeyEvent e) {
+ if (e.character == 0x1B) // ESC
+ fShell.dispose();
+ }
+
+ public void keyReleased(KeyEvent e) {}
+ });
+
+ fPresenter= presenter;
+ }
+
+ /**
+ * Creates a default information control with the given shell as parent.
+ * No information presenter is used to process the information
+ * to be displayed. No additional styles are applied to the styled text widget.
+ *
+ * @param parent the parent shell
+ */
+ public DefaultInformationControl(Shell parent) {
+ this(parent, SWT.NONE, null);
+ }
+
+ /**
+ * Creates a default information control with the given shell as parent. The given
+ * information presenter is used to process the information to be displayed.
+ * No additional styles are applied to the styled text widget.
+ *
+ * @param parent the parent shell
+ * @param presenter the presenter to be used
+ */
+ public DefaultInformationControl(Shell parent, IInformationPresenter presenter) {
+ this(parent, SWT.NONE, presenter);
+ }
+
+ /*
+ * @see IInformationControl#setInformation(String)
+ */
+ public void setInformation(String content) {
+ if (fPresenter == null) {
+ fText.setText(content);
+ } else {
+ fPresentation.clear();
+ content= fPresenter.updatePresentation(fShell.getDisplay(), content, fPresentation, fMaxWidth, fMaxHeight);
+ if (content != null) {
+ fText.setText(content);
+ TextPresentation.applyTextPresentation(fPresentation, fText);
+ } else {
+ fText.setText(""); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /*
+ * @see IInformationControl#setVisible(boolean)
+ */
+ public void setVisible(boolean visible) {
+ fShell.setVisible(visible);
+ }
+
+ /*
+ * @see IInformationControl#dispose()
+ */
+ public void dispose() {
+ if (fShell != null) {
+ if (!fShell.isDisposed())
+ fShell.dispose();
+ fShell= null;
+ fText= null;
+ }
+ }
+
+ /*
+ * @see IInformationControl#setSize(int, int)
+ */
+ public void setSize(int width, int height) {
+ fShell.setSize(width, height);
+
+ width -= BORDER * 2;
+ if (width < 0)
+ width= 0;
+
+ height -= BORDER * 2;
+ if (height < 0)
+ height= 0;
+
+ fText.setSize(width, height);
+ }
+
+ /*
+ * @see IInformationControl#setLocation(Point)
+ */
+ public void setLocation(Point location) {
+ fText.setLocation(BORDER, BORDER);
+ fShell.setLocation(location);
+ }
+
+ /*
+ * @see IInformationControl#setSizeConstraints(int, int)
+ */
+ public void setSizeConstraints(int maxWidth, int maxHeight) {
+ fMaxWidth= maxWidth;
+ fMaxHeight= maxHeight;
+ }
+
+ /*
+ * @see IInformationControl#computeSizeHint()
+ */
+ public Point computeSizeHint() {
+ Point point= fText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+ point.x += RIGHT_MARGIN + BORDER * 2;
+ point.y += BORDER * 2;
+
+ return point;
+ }
+
+ /*
+ * @see IInformationControl#addDisposeListener(DisposeListener)
+ */
+ public void addDisposeListener(DisposeListener listener) {
+ fShell.addDisposeListener(listener);
+ }
+
+ /*
+ * @see IInformationControl#removeDisposeListener(DisposeListener)
+ */
+ public void removeDisposeListener(DisposeListener listener) {
+ fShell.removeDisposeListener(listener);
+ }
+
+ /*
+ * @see IInformationControl#setForegroundColor(Color)
+ */
+ public void setForegroundColor(Color foreground) {
+ fText.setForeground(foreground);
+ }
+
+ /*
+ * @see IInformationControl#setBackgroundColor(Color)
+ */
+ public void setBackgroundColor(Color background) {
+ fText.setBackground(background);
+ }
+
+ /*
+ * @see IInformationControl#isFocusControl()
+ */
+ public boolean isFocusControl() {
+ return fText.isFocusControl();
+ }
+
+ /*
+ * @see IInformationControl#setFocus()
+ */
+ public void setFocus() {
+ fText.setFocus();
+ }
+
+ /*
+ * @see IInformationControl#addFocusListener(FocusListener)
+ */
+ public void addFocusListener(FocusListener listener) {
+ fText.addFocusListener(listener);
+ }
+
+ /*
+ * @see IInformationControl#removeFocusListener(FocusListener)
+ */
+ public void removeFocusListener(FocusListener listener) {
+ fText.removeFocusListener(listener);
+ }
+
+ /*
+ * @see IInformationControlExtension#hasContents()
+ */
+ public boolean hasContents() {
+ return fText.getCharCount() > 0;
+ }
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextDoubleClickStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextDoubleClickStrategy.java
new file mode 100644
index 000000000..a8934267a
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultTextDoubleClickStrategy.java
@@ -0,0 +1,218 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+import java.text.BreakIterator;
+import java.text.CharacterIterator;
+
+
+/**
+ * Standard implementation of <code>ITextDoubleClickStrategy</code>.
+ * Selects words using <code>java.text.BreakIterator</code> for the
+ * default locale. This class is not intended to be subclassed.
+ *
+ * @see java.text.BreakIterator
+ */
+public class DefaultTextDoubleClickStrategy implements ITextDoubleClickStrategy {
+
+
+ /**
+ * Implements a character iterator that works directly on
+ * instances of <code>IDocument</code>. Used to collaborate with
+ * the break iterator.
+ *
+ * @see IDocument
+ * @since 2.0
+ */
+ static class DocumentCharacterIterator implements CharacterIterator {
+
+ /** Document to iterate over. */
+ private IDocument fDocument;
+ /** Start offset of iteration. */
+ private int fOffset= -1;
+ /** Endoffset of iteration. */
+ private int fEndOffset= -1;
+ /** Current offset of iteration. */
+ private int fIndex= -1;
+
+ /** Creates a new document iterator. */
+ public DocumentCharacterIterator() {
+ }
+
+ /**
+ * Configures this document iterator with the document section to be iteratored.
+ *
+ * @param document the document to be iterated
+ * @param iteratorRange the range in the document to be iterated
+ */
+ public void setDocument(IDocument document, IRegion iteratorRange) {
+ fDocument= document;
+ fOffset= iteratorRange.getOffset();
+ fEndOffset= fOffset + iteratorRange.getLength();
+ }
+
+ /*
+ * @see CharacterIterator#first()
+ */
+ public char first() {
+ fIndex= fOffset;
+ return current();
+ }
+
+ /*
+ * @see CharacterIterator#last()
+ */
+ public char last() {
+ fIndex= fOffset < fEndOffset ? fEndOffset -1 : fEndOffset;
+ return current();
+ }
+
+ /*
+ * @see CharacterIterator#current()
+ */
+ public char current() {
+ if (fOffset <= fIndex && fIndex < fEndOffset) {
+ try {
+ return fDocument.getChar(fIndex);
+ } catch (BadLocationException x) {
+ }
+ }
+ return DONE;
+ }
+
+ /*
+ * @see CharacterIterator#next()
+ */
+ public char next() {
+ if (fIndex == fEndOffset -1)
+ return DONE;
+
+ if (fIndex < fEndOffset)
+ ++ fIndex;
+
+ return current();
+ }
+
+ /*
+ * @see CharacterIterator#previous()
+ */
+ public char previous() {
+ if (fIndex == fOffset)
+ return DONE;
+
+ if (fIndex > fOffset)
+ -- fIndex;
+
+ return current();
+ }
+
+ /*
+ * @see CharacterIterator#setIndex(int)
+ */
+ public char setIndex(int index) {
+ fIndex= index;
+ return current();
+ }
+
+ /*
+ * @see CharacterIterator#getBeginIndex()
+ */
+ public int getBeginIndex() {
+ return fOffset;
+ }
+
+ /*
+ * @see CharacterIterator#getEndIndex()
+ */
+ public int getEndIndex() {
+ return fEndOffset;
+ }
+
+ /*
+ * @see CharacterIterator#getIndex()
+ */
+ public int getIndex() {
+ return fIndex;
+ }
+
+ /*
+ * @see CharacterIterator#clone()
+ */
+ public Object clone() {
+ DocumentCharacterIterator i= new DocumentCharacterIterator();
+ i.fDocument= fDocument;
+ i.fIndex= fIndex;
+ i.fOffset= fOffset;
+ i.fEndOffset= fEndOffset;
+ return i;
+ }
+ };
+
+
+ /**
+ * The document character iterator used by this strategy.
+ * @since 2.0
+ */
+ private DocumentCharacterIterator fDocIter= new DocumentCharacterIterator();
+
+
+ /**
+ * Creates a new default text double click strategy.
+ */
+ public DefaultTextDoubleClickStrategy() {
+ super();
+ }
+
+ /*
+ * @see ITextDoubleClickStrategy#doubleClicked
+ */
+ public void doubleClicked(ITextViewer text) {
+
+ int position= text.getSelectedRange().x;
+
+ if (position < 0)
+ return;
+
+ try {
+
+ IDocument document= text.getDocument();
+ IRegion line= document.getLineInformationOfOffset(position);
+ if (position == line.getOffset() + line.getLength())
+ return;
+
+ fDocIter.setDocument(document, line);
+
+ BreakIterator breakIter= BreakIterator.getWordInstance();
+ breakIter.setText(fDocIter);
+
+ int start= breakIter.preceding(position);
+ if (start == BreakIterator.DONE)
+ start= line.getOffset();
+
+ int end= breakIter.following(position);
+ if (end == BreakIterator.DONE)
+ end= line.getOffset() + line.getLength();
+
+ if (breakIter.isBoundary(position)) {
+ if (end - position > position- start)
+ start= position;
+ else
+ end= position;
+ }
+
+ if (start != end)
+ text.setSelectedRange(start, end - start);
+
+ } catch (BadLocationException x) {
+ }
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultUndoManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultUndoManager.java
new file mode 100644
index 000000000..58ca1ea89
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultUndoManager.java
@@ -0,0 +1,786 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+
+
+/**
+ * Standard implementation of <code>IUndoManager</code>.
+ * It registers with the connected text viewer as text listeners and logs all changes.
+ * It also monitors mouse and keyboard activities in order to partition the stream of
+ * text changes into undoable edit commands. <p>
+ * This class is not intended to be subclassed.
+ *
+ * @see ITextViewer
+ * @see ITextListener
+ * @see MouseListener
+ * @see KeyListener
+ */
+public class DefaultUndoManager implements IUndoManager {
+
+ /**
+ * Represents an undoable edit command.
+ */
+ class TextCommand {
+
+ /** The start index of the replaced text */
+ protected int fStart= -1;
+ /** The end index of the replaced text */
+ protected int fEnd= -1;
+ /** The newly inserted text */
+ protected String fText;
+ /** The replaced text */
+ protected String fPreservedText;
+
+ /**
+ * Reinitializes this text command.
+ */
+ protected void reinitialize() {
+ fStart= fEnd= -1;
+ fText= fPreservedText= null;
+ }
+
+ /**
+ * Sets the start and the end index of this command.
+ *
+ * @param start the start index
+ * @param end the end index
+ */
+ protected void set(int start, int end) {
+ fStart= start;
+ fEnd= end;
+ fText= null;
+ fPreservedText= null;
+ }
+
+ /**
+ * Undo the change described by this command.
+ *
+ * @param text the text widget to be modified
+ * @since 2.0
+ */
+ protected void undoTextChange(StyledText text) {
+ text.replaceTextRange(fStart, fText.length(), fPreservedText);
+ }
+
+ /**
+ * Undo the change described by this command. Also selects and
+ * reveals the change.
+ *
+ * @param text the text widget to be modified
+ */
+ protected void undo(StyledText text) {
+
+ undoTextChange(text);
+
+ int length= fPreservedText == null ? 0 : fPreservedText.length();
+ IRegion visible= fTextViewer.getVisibleRegion();
+ int offset= fStart + visible.getOffset();
+ fTextViewer.setSelectedRange(offset, length);
+ fTextViewer.revealRange(offset, length);
+ }
+
+ /**
+ * Redo the change described by this command.
+ *
+ * @param text the text widget to be modified
+ * @since 2.0
+ */
+ protected void redoTextChange(StyledText text) {
+ text.replaceTextRange(fStart, fEnd - fStart, fText);
+ }
+
+ /**
+ * Redo the change described by this command that previously been
+ * rolled back. Also selects and reveals the change.
+ *
+ * @param text the text widget to be modified
+ */
+ protected void redo(StyledText text) {
+
+ redoTextChange(text);
+
+ int length= fText == null ? 0 : fText.length();
+ IRegion visible= fTextViewer.getVisibleRegion();
+ int offset= fStart + visible.getOffset();
+ fTextViewer.setSelectedRange(offset, length);
+ fTextViewer.revealRange(offset, length);
+ }
+
+ /**
+ * Updates the command stack in response to committing
+ * the current change into this command.
+ */
+ protected void updateCommandStack() {
+
+ int length= fCommandStack.size();
+ for (int i= fCommandCounter + 1; i < length; i++)
+ fCommandStack.remove(fCommandCounter + 1);
+
+ fCommandStack.add(this);
+
+ while (fCommandStack.size() > fUndoLevel)
+ fCommandStack.remove(0);
+
+ fCommandCounter= fCommandStack.size() - 1;
+ }
+
+ /**
+ * Creates a new uncommitted text command depending on whether
+ * a compound change is currently being executed.
+ *
+ * @return a new, uncommitted text command or a compound text command
+ */
+ protected TextCommand createCurrent() {
+ return fFoldingIntoCompoundChange ? new CompoundTextCommand() : new TextCommand();
+ }
+
+ /**
+ * Commits the current change into this command.
+ */
+ protected void commit() {
+
+ if (fStart < 0) {
+ reinitialize();
+ } else {
+
+ fText= fTextBuffer.toString();
+ fTextBuffer.setLength(0);
+ fPreservedText= fPreservedTextBuffer.toString();
+ fPreservedTextBuffer.setLength(0);
+
+ updateCommandStack();
+ }
+
+ fCurrent= createCurrent();
+ }
+ };
+
+ /**
+ * Represents an undoable edit command consisting of several
+ * individual edit commands.
+ */
+ class CompoundTextCommand extends TextCommand {
+
+ /** The list of individual commands */
+ private List fCommands= new ArrayList();
+
+ /**
+ * Adds a new individual command to this compound command.
+ *
+ * @param command the command to be added
+ */
+ protected void add(TextCommand command) {
+ fCommands.add(command);
+ }
+
+ /*
+ * @see TextCommand#undo
+ */
+ protected void undo(StyledText text) {
+ ITextViewerExtension extension= null;
+ if (fTextViewer instanceof ITextViewerExtension)
+ extension= (ITextViewerExtension) fTextViewer;
+
+ if (extension != null)
+ extension.setRedraw(false);
+
+ try {
+
+ int size= fCommands.size();
+ if (size > 0) {
+
+ TextCommand c;
+
+ for (int i= size -1; i > 0; --i) {
+ c= (TextCommand) fCommands.get(i);
+ c.undoTextChange(text);
+ }
+
+ c= (TextCommand) fCommands.get(0);
+ c.undo(text);
+ }
+
+ } finally {
+ if (extension != null)
+ extension.setRedraw(true);
+ }
+ }
+
+ /*
+ * @see TextCommand#redo
+ */
+ protected void redo(StyledText text) {
+
+ ITextViewerExtension extension= null;
+ if (fTextViewer instanceof ITextViewerExtension)
+ extension= (ITextViewerExtension) fTextViewer;
+
+ if (extension != null)
+ extension.setRedraw(false);
+
+ try {
+
+ int size= fCommands.size();
+ if (size > 0) {
+
+ TextCommand c;
+
+ for (int i= 0; i < size -1; ++i) {
+ c= (TextCommand) fCommands.get(i);
+ c.redoTextChange(text);
+ }
+
+ c= (TextCommand) fCommands.get(size -1);
+ c.redo(text);
+ }
+
+ } finally {
+ if (extension != null)
+ extension.setRedraw(true);
+ }
+ }
+
+ /*
+ * @see TextCommand#updateCommandStack
+ */
+ protected void updateCommandStack() {
+ TextCommand c= new TextCommand();
+ c.fStart= fStart;
+ c.fEnd= fEnd;
+ c.fText= fText;
+ c.fPreservedText= fPreservedText;
+
+ add(c);
+
+ if (!fFoldingIntoCompoundChange)
+ super.updateCommandStack();
+ }
+
+ /*
+ * @see TextCommand#createCurrent
+ */
+ protected TextCommand createCurrent() {
+
+ if (!fFoldingIntoCompoundChange)
+ return new TextCommand();
+
+ reinitialize();
+ return this;
+ }
+ };
+
+ /**
+ * Represents pretended <code>UndoManager</code> state.
+ */
+ class PretendedUndoManagerState {
+ protected int cmdCounter= -1;
+ protected int stackSize= -1;
+ };
+
+ /**
+ * Internal listener to mouse and key events.
+ */
+ class KeyAndMouseListener implements MouseListener, KeyListener {
+
+ /*
+ * @see MouseListener#mouseDoubleClick
+ */
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+
+ /*
+ * If the right mouse button is pressed, the current editing command is closed
+ * @see MouseListener#mouseDown
+ */
+ public void mouseDown(MouseEvent e) {
+ if (e.button == 1)
+ commit();
+ }
+
+ /*
+ * @see MouseListener#mouseUp
+ */
+ public void mouseUp(MouseEvent e) {
+ }
+
+ /*
+ * @see KeyListener#keyPressed
+ */
+ public void keyReleased(KeyEvent e) {
+ }
+
+ /*
+ * On cursor keys, the current editing command is closed
+ * @see KeyListener#keyPressed
+ */
+ public void keyPressed(KeyEvent e) {
+ switch (e.keyCode) {
+ case SWT.ARROW_UP:
+ case SWT.ARROW_DOWN:
+ case SWT.ARROW_LEFT:
+ case SWT.ARROW_RIGHT:
+ commit();
+ break;
+ }
+ }
+ };
+
+ /**
+ * Internal listener to text changes.
+ */
+ class TextListener implements ITextListener {
+
+ /*
+ * @see ITextListener#textChanged
+ */
+ public void textChanged(TextEvent e) {
+ if (e.getDocumentEvent() != null)
+ processTextEvent(e);
+ }
+ };
+
+ /** Text buffer to collect text which is inserted into the viewer */
+ private StringBuffer fTextBuffer= new StringBuffer();
+ /** Text buffer to collect viewer content which has been replaced */
+ private StringBuffer fPreservedTextBuffer= new StringBuffer();
+ /** Pretended undo manager state */
+ private PretendedUndoManagerState fPretendedState= new PretendedUndoManagerState();
+
+ /** The internal text listener */
+ private ITextListener fTextListener;
+ /** The internal key and mouse event listener */
+ private KeyAndMouseListener fKeyAndMouseListener;
+
+
+ /** Indicates inserting state */
+ private boolean fInserting= false;
+ /** Indicates deleteing state */
+ private boolean fDeleting= false;
+ /** Indicates overwriting state */
+ private boolean fOverwriting= false;
+ /** Indicates whether the current change belongs to a compound change */
+ private boolean fFoldingIntoCompoundChange= false;
+
+ /** The text viewer the undo manager is connected to */
+ private ITextViewer fTextViewer;
+
+ /** Supported undo level */
+ private int fUndoLevel;
+ /** The list of undoable edit commands */
+ private List fCommandStack;
+ /** The currently constructed edit command */
+ private TextCommand fCurrent;
+ /** The last delete edit command */
+ private TextCommand fPreviousDelete;
+ /** Command counter into the edit command stack */
+ private int fCommandCounter= -1;
+
+
+ /**
+ * Creates a new undo manager who remembers the specified number of edit commands.
+ *
+ * @param undoLevel the length of this manager's history
+ */
+ public DefaultUndoManager(int undoLevel) {
+ setMaximalUndoLevel(undoLevel);
+ }
+
+ /*
+ * @see IUndoManager#beginCompoundChange
+ */
+ public void beginCompoundChange() {
+ fFoldingIntoCompoundChange= true;
+ commit();
+ }
+
+ /*
+ * @see IUndoManager#endCompoundChange
+ */
+ public void endCompoundChange() {
+ fFoldingIntoCompoundChange= false;
+ commit();
+ }
+
+ /**
+ * Registers all necessary listeners with the text viewer.
+ */
+ private void addListeners() {
+ StyledText text= fTextViewer.getTextWidget();
+ if (text != null) {
+ fKeyAndMouseListener= new KeyAndMouseListener();
+ text.addMouseListener(fKeyAndMouseListener);
+ text.addKeyListener(fKeyAndMouseListener);
+ listenToTextChanges(true);
+ }
+ }
+
+ /**
+ * Deregister all previously installed listeners from the text viewer.
+ */
+ private void removeListeners() {
+ StyledText text= fTextViewer.getTextWidget();
+ if (text != null && fKeyAndMouseListener != null) {
+ text.removeMouseListener(fKeyAndMouseListener);
+ text.removeKeyListener(fKeyAndMouseListener);
+ listenToTextChanges(false);
+ }
+ }
+
+ /**
+ * Switches the state of whether there is a text listener or not.
+ *
+ * @param listen the state which should be established
+ */
+ private void listenToTextChanges(boolean listen) {
+ if (listen && fTextListener == null) {
+ fTextListener= new TextListener();
+ fTextViewer.addTextListener(fTextListener);
+ } else if (!listen && fTextListener != null) {
+ fTextViewer.removeTextListener(fTextListener);
+ fTextListener= null;
+ }
+ }
+
+ /**
+ * Closes the current editing command and opens a new one.
+ */
+ private void commit() {
+
+ fInserting= false;
+ fDeleting= false;
+ fOverwriting= false;
+ fPreviousDelete.reinitialize();
+
+ fCurrent.commit();
+ }
+
+ /**
+ * Does redo the previously undone editing command.
+ */
+ private void internalRedo() {
+ StyledText text= fTextViewer.getTextWidget();
+ if (text != null) {
+
+ ++fCommandCounter;
+ TextCommand cmd= (TextCommand) fCommandStack.get(fCommandCounter);
+
+ listenToTextChanges(false);
+ cmd.redo(text);
+ listenToTextChanges(true);
+
+ fCurrent= new TextCommand();
+ }
+ }
+
+ /**
+ * Does undo the last editing command.
+ */
+ private void internalUndo() {
+ StyledText text= fTextViewer.getTextWidget();
+ if (text != null) {
+
+ TextCommand cmd= (TextCommand) fCommandStack.get(fCommandCounter);
+ -- fCommandCounter;
+
+ listenToTextChanges(false);
+ cmd.undo(text);
+ listenToTextChanges(true);
+
+ fCurrent= new TextCommand();
+ }
+ }
+
+ /**
+ * Checks whether the given text starts with a line delimiter and
+ * subsequently contains a white space only.
+ *
+ * @param text the text to check
+ */
+ private boolean isWhitespaceText(String text) {
+
+ if (text == null || text.length() == 0)
+ return false;
+
+ String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters();
+ int index= TextUtilities.startsWith(delimiters, text);
+ if (index > -1) {
+ char c;
+ int length= text.length();
+ for (int i= delimiters[index].length(); i < length; i++) {
+ c= text.charAt(i);
+ if (c != ' ' && c != '\t')
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the state the would result if the current editing command would be closed.
+ *
+ * @return the pretended state after closing the current editing command
+ */
+ private PretendedUndoManagerState pretendCommit() {
+ if (fCurrent.fStart < 0) {
+ fPretendedState.stackSize= fCommandStack.size();
+ fPretendedState.cmdCounter= fCommandCounter;
+ } else {
+ int sz= Math.max(fCommandCounter, 0) + 1;
+ if (sz > fUndoLevel)
+ sz -= fUndoLevel;
+ fPretendedState.stackSize= sz;
+ fPretendedState.cmdCounter= sz - 1;
+ }
+ return fPretendedState;
+ }
+
+ /**
+ * Processes the given text event in order to determine editor command.
+ *
+ * @param e the text event
+ */
+ private void processTextEvent(TextEvent e) {
+
+ int start= e.getOffset();
+ int end= e.getOffset() + e.getLength();
+ String newText= e.getText();
+ String oldText= e.getReplacedText();
+
+
+ if (newText == null)
+ newText= ""; //$NON-NLS-1$
+
+ if (oldText == null)
+ oldText= ""; //$NON-NLS-1$
+
+ int length= newText.length();
+ int diff= end - start;
+
+ // normalize verify command
+ if (diff < 0) {
+ int tmp= end;
+ end= start;
+ start= tmp;
+ diff= -diff;
+ }
+
+ if (start == end) {
+ // text will be inserted
+ if ((length == 1) || isWhitespaceText(newText)) {
+ // by typing or model manipulation
+ if (!fInserting || (start != fCurrent.fStart + fTextBuffer.length())) {
+ commit();
+ fInserting= true;
+ }
+ if (fCurrent.fStart < 0)
+ fCurrent.fStart= fCurrent.fEnd= start;
+ if (length > 0)
+ fTextBuffer.append(newText);
+ } else if (length > 0) {
+ // by pasting
+ commit();
+ fCurrent.fStart= fCurrent.fEnd= start;
+ fTextBuffer.append(newText);
+ }
+ } else {
+ if (length == 0) {
+ // text will be deleted by backspace or DEL key or empty clipboard
+ length= oldText.length();
+ String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters();
+
+ if ((length == 1) || TextUtilities.equals(delimiters, oldText) > -1) {
+
+ // whereby selection is empty
+
+ if (fPreviousDelete.fStart == start && fPreviousDelete.fEnd == end) {
+ // repeated DEL
+
+ // correct wrong settings of fCurrent
+ if (fCurrent.fStart == end && fCurrent.fEnd == start) {
+ fCurrent.fStart= start;
+ fCurrent.fEnd= end;
+ }
+ // append to buffer && extend command range
+ fPreservedTextBuffer.append(oldText);
+ ++fCurrent.fEnd;
+
+ } else if (fPreviousDelete.fStart == end) {
+ // repeated backspace
+
+ // insert in buffer and extend command range
+ fPreservedTextBuffer.insert(0, oldText);
+ fCurrent.fStart= start;
+
+ } else {
+ // either DEL or backspace for the first time
+
+ commit();
+ fDeleting= true;
+
+ // as we can not decide whether it was DEL or backspace we initialize for backspace
+ fPreservedTextBuffer.append(oldText);
+ fCurrent.fStart= start;
+ fCurrent.fEnd= end;
+ }
+
+ fPreviousDelete.set(start, end);
+
+ } else if (length > 0) {
+ // whereby selection is not empty
+ commit();
+ fCurrent.fStart= start;
+ fCurrent.fEnd= end;
+ fPreservedTextBuffer.append(oldText);
+ }
+ } else {
+ // text will be replaced
+
+ if (length == 1) {
+ length= oldText.length();
+ String[] delimiters= fTextViewer.getDocument().getLegalLineDelimiters();
+
+ if ((length == 1) || TextUtilities.equals(delimiters, oldText) > -1) {
+ // because of overwrite mode or model manipulation
+ if (!fOverwriting || (start != fCurrent.fStart + fTextBuffer.length())) {
+ commit();
+ fOverwriting= true;
+ }
+
+ if (fCurrent.fStart < 0)
+ fCurrent.fStart= start;
+
+ fCurrent.fEnd= end;
+ fTextBuffer.append(newText);
+ fPreservedTextBuffer.append(oldText);
+ return;
+ }
+ }
+ // because of typing or pasting whereby selection is not empty
+ commit();
+ fCurrent.fStart= start;
+ fCurrent.fEnd= end;
+ fTextBuffer.append(newText);
+ fPreservedTextBuffer.append(oldText);
+ }
+ }
+ }
+
+ /*
+ * @see IUndoManager#setMaximalUndoLevel
+ */
+ public void setMaximalUndoLevel(int undoLevel) {
+ fUndoLevel= undoLevel;
+ }
+
+ /*
+ * @see IUndoManager#connect
+ */
+ public void connect(ITextViewer textViewer) {
+ if (fTextViewer == null) {
+ fTextViewer= textViewer;
+ fCommandStack= new ArrayList();
+ fCurrent= new TextCommand();
+ fPreviousDelete= new TextCommand();
+ addListeners();
+ }
+ }
+
+ /*
+ * @see IUndoManager#disconnect
+ */
+ public void disconnect() {
+ if (fTextViewer != null) {
+
+ removeListeners();
+
+ fCurrent= null;
+ if (fCommandStack != null) {
+ fCommandStack.clear();
+ fCommandStack= null;
+ }
+ fTextBuffer= null;
+ fPreservedTextBuffer= null;
+ fTextViewer= null;
+ }
+ }
+
+ /*
+ * @see IUndoManager#reset
+ */
+ public void reset() {
+ if (fCommandStack != null)
+ fCommandStack.clear();
+ fCommandCounter= -1;
+ if (fCurrent != null)
+ fCurrent.reinitialize();
+ fFoldingIntoCompoundChange= false;
+ fInserting= false;
+ fDeleting= false;
+ fOverwriting= false;
+ fTextBuffer.setLength(0);
+ fPreservedTextBuffer.setLength(0);
+ }
+
+ /*
+ * @see IUndoManager#redoable
+ */
+ public boolean redoable() {
+ if (fCommandStack != null) {
+ PretendedUndoManagerState s= pretendCommit();
+ return (0 <= s.cmdCounter + 1) && (s.cmdCounter + 1 < s.stackSize);
+ }
+ return false;
+ }
+
+ /*
+ * @see IUndoManager#undoable
+ */
+ public boolean undoable() {
+ if (fCommandStack != null) {
+ PretendedUndoManagerState s= pretendCommit();
+ return (0 <= s.cmdCounter) && (s.cmdCounter < s.stackSize);
+ }
+ return false;
+ }
+
+ /*
+ * @see IUndoManager#redo
+ */
+ public void redo() {
+ if (redoable()) {
+ commit();
+ internalRedo();
+ }
+ }
+
+ /*
+ * @see IUndoManager#undo
+ */
+ public void undo() {
+ if (undoable()) {
+ commit();
+ internalUndo();
+ }
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentAdapter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentAdapter.java
new file mode 100644
index 000000000..8ca9b21eb
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentAdapter.java
@@ -0,0 +1,338 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TextChangeListener; import org.eclipse.swt.custom.TextChangedEvent; import org.eclipse.swt.custom.TextChangingEvent; import org.eclipse.jface.util.Assert;
+
+
+/**
+ * Adapts an <code>IDocument</code> to the <code>StyledTextContent</code> interface.
+ */
+class DocumentAdapter implements IDocumentAdapter, IDocumentListener, IDocumentAdapterExtension {
+
+ /** The adapted document. */
+ private IDocument fDocument;
+ /** The registered text change listeners */
+ private List fTextChangeListeners= new ArrayList(1);
+ /**
+ * The remembered document event
+ * @since 2.0
+ */
+ private DocumentEvent fEvent;
+ /** The line delimiter */
+ private String fLineDelimiter= null;
+ /**
+ * Indicates whether this adapter is forwarding document changes
+ * @since 2.0
+ */
+ private boolean fIsForwarding= true;
+ /**
+ * Indicates whether the current document event describes a complete replace.
+ * @since 2.0
+ */
+ private boolean fDocumentContentReplaced= false;
+ /**
+ * Indicates that the line delimiter cache is flushed
+ * @since 2.0
+ */
+ private boolean fInvalidateLineDelimiter;
+
+
+ /**
+ * Creates a new document adapter which is initiallly not connected to
+ * any document.
+ */
+ public DocumentAdapter() {
+ }
+
+ /**
+ * Sets the given document as the document to be adapted.
+ *
+ * @param document the document to be adapted or <code>null</code> if there is no document
+ */
+ public void setDocument(IDocument document) {
+
+ if (fDocument != null)
+ fDocument.removePrenotifiedDocumentListener(this);
+
+ fDocument= document;
+ fLineDelimiter= null;
+
+ if (fDocument != null)
+ fDocument.addPrenotifiedDocumentListener(this);
+ }
+
+ /*
+ * @see StyledTextContent#addTextChangeListener
+ */
+ public void addTextChangeListener(TextChangeListener listener) {
+ Assert.isNotNull(listener);
+ if (! fTextChangeListeners.contains(listener))
+ fTextChangeListeners.add(listener);
+ }
+
+ /*
+ * @see StyledTextContent#removeTextChangeListener
+ */
+ public void removeTextChangeListener(TextChangeListener listener) {
+ Assert.isNotNull(listener);
+ fTextChangeListeners.remove(listener);
+ }
+
+ /*
+ * @see StyledTextContent#getLine(int)
+ */
+ public String getLine(int line) {
+ try {
+ IRegion r= fDocument.getLineInformation(line);
+ return fDocument.get(r.getOffset(), r.getLength());
+ } catch (BadLocationException x) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ return null;
+ }
+ }
+
+ /*
+ * @see StyledTextContent#getLineAtOffset(int)
+ */
+ public int getLineAtOffset(int offset) {
+ try {
+ return fDocument.getLineOfOffset(offset);
+ } catch (BadLocationException x) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ return -1;
+ }
+ }
+
+ /*
+ * @see StyledTextContent#getLineCount()
+ */
+ public int getLineCount() {
+ return fDocument.getNumberOfLines();
+ }
+
+ /*
+ * @see StyledTextContent#getOffsetAtLine(int)
+ */
+ public int getOffsetAtLine(int line) {
+ try {
+ return fDocument.getLineOffset(line);
+ } catch (BadLocationException x) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ return -1;
+ }
+ }
+
+ /*
+ * @see StyledTextContent#getTextRange(int, int)
+ */
+ public String getTextRange(int offset, int length) {
+ try {
+ return fDocument.get(offset, length);
+ } catch (BadLocationException x) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ return null;
+ }
+ }
+
+ /*
+ * @see StyledTextContent#replaceTextRange(int, int, String)
+ */
+ public void replaceTextRange(int pos, int length, String text) {
+ try {
+ fDocument.replace(pos, length, text);
+ } catch (BadLocationException x) {
+ SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ }
+
+ /*
+ * @see StyledTextContent#setText
+ */
+ public void setText(String text) {
+ fDocument.set(text);
+ }
+
+ /*
+ * @see StyledTextContent#getCharCount()
+ */
+ public int getCharCount() {
+ return fDocument.getLength();
+ }
+
+ /*
+ * @see StyledTextContent#getLineDelimiter
+ */
+ public String getLineDelimiter() {
+
+ if (fLineDelimiter == null) {
+
+ try {
+ fLineDelimiter= fDocument.getLineDelimiter(0);
+ } catch (BadLocationException x) {
+ }
+
+ if (fLineDelimiter == null) {
+ /*
+ * Follow up fix for: 1GF5UU0: ITPJUI:WIN2000 - "Organize Imports" in java editor inserts lines in wrong format
+ * The line delimiter must always be a legal document line delimiter.
+ */
+ String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
+ String[] delimiters= fDocument.getLegalLineDelimiters();
+ Assert.isTrue(delimiters.length > 0);
+ for (int i= 0; i < delimiters.length; i++) {
+ if (delimiters[i].equals(sysLineDelimiter)) {
+ fLineDelimiter= sysLineDelimiter;
+ break;
+ }
+ }
+
+ if (fLineDelimiter == null) {
+ // system line delimiter is not a legal document line delimiter
+ fLineDelimiter= delimiters[0];
+ }
+ }
+ }
+
+ return fLineDelimiter;
+ }
+
+ /*
+ * @see IDocumentListener#documentChanged(DocumentEvent)
+ */
+ public void documentChanged(DocumentEvent event) {
+ // check whether the given event is the one which was remembered
+ if (fEvent != null && event == fEvent) {
+
+ // http://dev.eclipse.org/bugs/show_bug.cgi?id=15159
+ if (fDocumentContentReplaced) {
+ fDocumentContentReplaced= false;
+ fireTextSet();
+ } else
+ fireTextChanged();
+
+ if (fInvalidateLineDelimiter) {
+ fLineDelimiter= null;
+ fInvalidateLineDelimiter= false;
+ }
+ }
+ }
+
+ /*
+ * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
+ */
+ public void documentAboutToBeChanged(DocumentEvent event) {
+
+ if (event.getOffset() == 0 && event.getLength() == fDocument.getLength()) {
+ // http://dev.eclipse.org/bugs/show_bug.cgi?id=15159
+ fEvent= event;
+ fInvalidateLineDelimiter= true;
+ fDocumentContentReplaced= true;
+
+ } else {
+
+ try {
+ // invalidate cached line delimiter if first line of document was changed
+ if (event.getOffset() < fDocument.getLineLength(0))
+ fInvalidateLineDelimiter= true;
+ } catch (BadLocationException e) {
+ }
+
+ // Remember the given event
+ fEvent= event;
+ fireTextChanging();
+ }
+ }
+
+ /**
+ * Sends a text changed event to all registered listeners.
+ */
+ private void fireTextChanged() {
+
+ if (!fIsForwarding)
+ return;
+
+ TextChangedEvent event= new TextChangedEvent(this);
+
+ if (fTextChangeListeners != null && fTextChangeListeners.size() > 0) {
+ Iterator e= new ArrayList(fTextChangeListeners).iterator();
+ while (e.hasNext())
+ ((TextChangeListener) e.next()).textChanged(event);
+ }
+ }
+
+ /**
+ * Sends a text set event to all registered listeners.
+ */
+ private void fireTextSet() {
+
+ if (!fIsForwarding)
+ return;
+
+ TextChangedEvent event = new TextChangedEvent(this);
+
+ if (fTextChangeListeners != null && fTextChangeListeners.size() > 0) {
+ Iterator e= new ArrayList(fTextChangeListeners).iterator();
+ while (e.hasNext())
+ ((TextChangeListener) e.next()).textSet(event);
+ }
+ }
+
+ /**
+ * Sends the text changing event to all registered listeners.
+ */
+ private void fireTextChanging() {
+
+ if (!fIsForwarding)
+ return;
+
+ try {
+ IDocument document= fEvent.getDocument();
+ if (document == null)
+ return;
+
+ TextChangingEvent event= new TextChangingEvent(this);
+ event.start= fEvent.fOffset;
+ event.replaceCharCount= fEvent.fLength;
+ event.replaceLineCount= document.getNumberOfLines(fEvent.fOffset, fEvent.fLength) - 1;
+ event.newText= fEvent.fText;
+ event.newCharCount= (fEvent.fText == null ? 0 : fEvent.fText.length());
+ event.newLineCount= (fEvent.fText == null ? 0 : document.computeNumberOfLines(fEvent.fText));
+
+ if (fTextChangeListeners != null && fTextChangeListeners.size() > 0) {
+ Iterator e= new ArrayList(fTextChangeListeners).iterator();
+ while (e.hasNext())
+ ((TextChangeListener) e.next()).textChanging(event);
+ }
+
+ } catch (BadLocationException e) {
+ }
+ }
+
+ /*
+ * @see IDocumentAdapterExtension#resumeForwardingDocumentChanges()
+ * @since 2.0
+ */
+ public void resumeForwardingDocumentChanges() {
+ fIsForwarding= true;
+ fireTextSet();
+ }
+
+ /*
+ * @see IDocumentAdapterExtension#stopForwardingDocumentChanges()
+ * @since 2.0
+ */
+ public void stopForwardingDocumentChanges() {
+ fIsForwarding= false;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentCommand.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentCommand.java
new file mode 100644
index 000000000..a79245254
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/DocumentCommand.java
@@ -0,0 +1,77 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.events.VerifyEvent;
+
+
+/**
+ * Represents a text modification as a document replace command. The text modification is given
+ * as a <code>VerifyEvent</code> and translated into a document replace command relative
+ * to a given offset. A document command can also be used to initialize a given <code>VerifyEvent</code>.
+ */
+public final class DocumentCommand {
+
+ /** Must the command be updated */
+ public boolean doit= false;
+ /** The offset of the command */
+ public int offset;
+ /** The length of the command */
+ public int length;
+ /** The text to be inserted */
+ public String text;
+
+
+ /**
+ * Creates a new document command.
+ */
+ DocumentCommand() {
+ }
+
+ /**
+ * Translates a verify event into a document replace command using the given offset.
+ *
+ * @param event the event to be translated
+ * @param offset the offset used for the translation
+ */
+ void setEvent(VerifyEvent event, int offset) {
+
+ doit= true;
+
+ text= event.text;
+
+ this.offset= event.start;
+ length= event.end - event.start;
+
+ if (length < 0) {
+ this.offset += length;
+ length= -length;
+ }
+
+ this.offset += offset;
+ }
+
+ /**
+ * Fills the given verify event with the replace text and the doit
+ * flag of this document command. Returns whether the document command
+ * covers the same range as the verify event considering the given offset.
+ *
+ * @param event the event to be changed
+ * @param offset to be considered for range comparison
+ * @return <code>true</code> if this command and the event cover the same range
+ */
+ boolean fillEvent(VerifyEvent event, int offset) {
+
+ int start= this.offset - offset;
+
+ event.text= text;
+ event.doit= (start == event.start && start + length == event.end) && doit;
+ return event.doit;
+ }
+}
+
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoIndentStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoIndentStrategy.java
new file mode 100644
index 000000000..c0e48790d
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IAutoIndentStrategy.java
@@ -0,0 +1,26 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * An auto indent strategy can adapt changes that will be applied to
+ * a text viewer's document. The strategy is informed by the text viewer
+ * about each upcoming change in form of a document command. By manipulating
+ * this document command, the strategy can influence in which way the text
+ * viewer's document is changed. Clients may implement this interface or
+ * use the standard implementation <code>DefaultAutoIndentStrategy</code>.
+ */
+public interface IAutoIndentStrategy {
+
+ /**
+ * Allows the strategy to manipulate the document command.
+ *
+ * @param document the document that will be changed
+ * @param command the document command describing the indented change
+ */
+ void customizeDocumentCommand(IDocument document, DocumentCommand command);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapter.java
new file mode 100644
index 000000000..49c6c6a6f
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapter.java
@@ -0,0 +1,31 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.custom.StyledTextContent;
+
+
+/**
+ * Adapts an <code>IDocument</code> to the <code>StyledTextContent</code> interface.
+ * The document adapter is used by <code>TextViewer</code> to translate document changes
+ * into styled text content changes and vice versa.
+ * Clients may implement this interface and override <code>TextViewer.createDocumentAdapter</code>
+ * if they want to intercept the communication between the viewer's text widget and
+ * the viewer's document.
+ *
+ * @see IDocument
+ * @see StyledTextContent
+ */
+public interface IDocumentAdapter extends StyledTextContent {
+
+ /**
+ * Sets the adapters document.
+ *
+ * @param document the document to be adapted
+ */
+ void setDocument(IDocument document);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapterExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapterExtension.java
new file mode 100644
index 000000000..d44d18e6a
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IDocumentAdapterExtension.java
@@ -0,0 +1,39 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+/**
+ * Extension interface for <code>IDocumentAdapter</code>. Introduces the concepts of
+ * batching a series of document changes into one styled text content change. Batching start
+ * when a client calls <code>stopForwardingDocumentChanges</code>. After that call this document
+ * adapter does not send out any styled text content change until
+ * <code>resumeForwardingDocumentChanges</code> is called. Then, it sends out one styled text
+ * content change that covers all changes that have been applied to the document since calling
+ * <code>stopForwardingDocumentChanges</code>.
+ *
+ * @since 2.0
+ */
+public interface IDocumentAdapterExtension {
+
+ /**
+ * Stops forwarding document changes to the styled text.
+ */
+ void stopForwardingDocumentChanges();
+
+ /**
+ * Resumes forwarding document changes to the styled text.
+ * Also forces the styled text to catch up with all the changes
+ * that have been applied since <code>stopTranslatingDocumentChanges</code>
+ * has been called.
+ */
+ void resumeForwardingDocumentChanges();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IEventConsumer.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IEventConsumer.java
new file mode 100644
index 000000000..700704992
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IEventConsumer.java
@@ -0,0 +1,31 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.events.VerifyEvent;
+
+/**
+ * Implementers can register with an text viewer and
+ * receive <code>VerifyEvent</code>s before the text viewer
+ * they are registered with. If the event consumer marks events
+ * as processed by turning their <code>doit</code> field to
+ * <code>false</code> the text viewer subsequently ignores them.
+ * Clients may implement this interface.
+ *
+ * @see ITextViewer
+ * @see org.eclipse.swt.events.VerifyEvent
+ */
+public interface IEventConsumer {
+
+ /**
+ * Processes the given event and marks it as done if it should
+ * be ignored by subsequent receivers.
+ *
+ * @param event the verify event which will be investigated
+ */
+ public void processEvent(VerifyEvent event);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java
new file mode 100644
index 000000000..658369367
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java
@@ -0,0 +1,65 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * Defines the interface for finding and replacing strings.
+ */
+public interface IFindReplaceTarget {
+
+ /**
+ * Returns whether a find operation can be performed.
+ *
+ * @return whether a find operation can be performed
+ */
+ boolean canPerformFind();
+
+ /**
+ * Finds and selects a string starting at the given offset using the specified search
+ * directives.
+ *
+ * @param offset the offset at which searching starts
+ * @param findString the string which should be found
+ * @param searchForward <code>true</code> searches forward, <code>false</code> backwards
+ * @param caseSensitive <code>true</code> performes a case sensitve search, <code>false</code> an insensitive search
+ * @param wholeWord if <code>true</code> only occurences are reported in which the findString stands as a word by itself
+ * @return the position of the specified string, or -1 if the string has not been found
+ */
+ int findAndSelect(int offset, String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord);
+
+ /**
+ * Returns the currently selected range of characters as a offset and length.
+ *
+ * @return the currently selected character range
+ */
+ Point getSelection();
+
+ /**
+ * Returns the currently selected characters as a string.
+ *
+ * @return the currently selected characters
+ */
+ String getSelectionText();
+
+ /**
+ * Returns whether this target can be modified.
+ *
+ * @return <code>true</code> if target can be modified
+ */
+ boolean isEditable();
+
+ /**
+ * Replaces the currently selected range of characters with the given text.
+ * This target must be editable. Otherwise nothing happens.
+ *
+ * @param text the substitution text
+ */
+ void replaceSelection(String text);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java
new file mode 100644
index 000000000..2cc904da7
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java
@@ -0,0 +1,89 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * Extension interface for <code>IFindReplaceTarget</code>. Extends the find replace target with
+ * the concept of searching in a limiting scope and introduces the state of a replace-all mode.
+ *
+ * @since 2.0
+ */
+public interface IFindReplaceTargetExtension {
+
+ /**
+ * Indicates that a session with the target begins.
+ * All calls except <code>beginSession()</code> and <code>endSession()</code> to
+ * <code>IFindReplaceTarget</code> and
+ * <code>IFindReplaceTargetExtension</code> must be embedded within calls to
+ * <code>beginSession()</code> and <code>endSession()</code>.
+ *
+ * @see #endSession()
+ */
+ void beginSession();
+
+ /**
+ * Indicates that a session with the target ends.
+ *
+ * @see #beginSession()
+ */
+ void endSession();
+
+ /**
+ * Returns the find scope of the target, <code>null</code> for global scope.
+ *
+ * @return returns the find scope of the target, may be <code>null</code>
+ */
+ IRegion getScope();
+
+ /**
+ * Sets the find scope of the target to operate on. <code>null</code>
+ * indicates that the global scope should be used.
+ *
+ * @param scope the find scope of the target, may be <code>null</code>
+ */
+ void setScope(IRegion scope);
+
+ /**
+ * Returns the currently selected range of lines as a offset and length.
+ *
+ * @return the currently selected line range
+ */
+ Point getLineSelection();
+
+ /**
+ * Sets a selection.
+ *
+ * @param offset the offset of the selection
+ * @param length the length of the selection
+ */
+ void setSelection(int offset, int length);
+
+ /**
+ * Sets the scope highlight color
+ *
+ * @param color the color of the scope highlight
+ */
+ void setScopeHighlightColor(Color color);
+
+
+ /**
+ * Sets the target's replace-all mode.
+ *
+ * @param replaceAll <code>true</code> if this target should switch into replace-all mode,
+ * <code>false</code> if it should leave the replace-all state
+ */
+ void setReplaceAllMode(boolean replaceAll);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControl.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControl.java
new file mode 100644
index 000000000..5a4518517
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControl.java
@@ -0,0 +1,142 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+
+
+/**
+ * Interface of a control presenting information. The information is given
+ * in textual form. It can either be the content itself or a description
+ * of the content. This specification is left to the implementers of this interface.<p>
+ * The information control may not grap focus when made visible using
+ * <code>setVisible(true)</code>.
+ *
+ * @since 2.0
+ */
+public interface IInformationControl {
+
+ /**
+ * Sets the information to be presented in this information control.
+ *
+ * @param information the information to be presented
+ */
+ void setInformation(String information);
+
+ /**
+ * Sets the information control's size constraints. A constraint value of
+ * <code>-1</code> indicates no constraint. This method is called before
+ * <code>computeSizeHint</code> is called.
+ *
+ * @param maxWidth the maximal width of the control to present the information, or <code>-1</code> for not constraint
+ * @param maxHeight the maximal height of the control to present the information, or <code>-1</code> for not constraint
+ */
+ void setSizeConstraints(int maxWidth, int maxHeight);
+
+ /**
+ * Computes and returns a proposal for the size of this information control depending
+ * on the information to present. The method tries to honor known size constraints but might
+ * returns a size that exceeds them.
+ *
+ * @return the computed size hint
+ */
+ Point computeSizeHint();
+
+ /**
+ * Controls the visibility of this information control.
+ *
+ * @param visible <code>true</code> if the control should be visible
+ */
+ void setVisible(boolean visible);
+
+ /**
+ * Sets the size of this information control.
+ *
+ * @param width the width of the control
+ * @param height the height of the control
+ */
+ void setSize(int width, int height);
+
+ /**
+ * Sets the location of this information control.
+ *
+ * @param location the location
+ */
+ void setLocation(Point location);
+
+ /**
+ * Disposes this information control.
+ */
+ void dispose();
+
+ /**
+ * Adds the given listener to the list of dispose listeners.
+ * If the listener is already registered it is not registered again.
+ *
+ * @param listener the listener to be added
+ */
+ void addDisposeListener(DisposeListener listener);
+
+ /**
+ * Removes the given listeners from the list of dispose listeners.
+ * If the listener is not registered this call has no affect.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeDisposeListener(DisposeListener listener);
+
+ /**
+ * Sets the foreground color of this information control.
+ *
+ * @param foreground the foreground color of this information control
+ */
+ void setForegroundColor(Color foreground);
+
+ /**
+ * Sets the background color of this information control.
+ *
+ * @param background the background color of this information control
+ */
+ void setBackgroundColor(Color background);
+
+ /**
+ * Returns whether this information control has the focus.
+ *
+ * @return <code>true</code> when the information control has the focus otherwise <code>false</code>
+ */
+ boolean isFocusControl();
+
+ /**
+ * Sets the keyboard focus to this information control.
+ */
+ void setFocus();
+
+ /**
+ * Adds the given listener to the list of focus listeners.
+ * If the listener is already registered it is not registered again.
+ *
+ * @param listener the listener to be added
+ */
+ void addFocusListener(FocusListener listener);
+
+ /**
+ * Removes the given listeners from the list of focus listeners.
+ * If the listener is not registered this call has no affect.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeFocusListener(FocusListener listener);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreator.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreator.java
new file mode 100644
index 000000000..be08cefd1
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlCreator.java
@@ -0,0 +1,33 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.widgets.Shell;
+
+
+/**
+ * Interface of a factory of information controls.
+ *
+ * @since 2.0
+ */
+public interface IInformationControlCreator {
+
+ /**
+ * Creates a new information control with the given shell as the control's parent.
+ *
+ * @param parent the parent shell
+ * @return the created information control
+ */
+ IInformationControl createInformationControl(Shell parent);
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension.java
new file mode 100644
index 000000000..077ee59d0
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IInformationControlExtension.java
@@ -0,0 +1,29 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+/**
+ * Extension interface for <code>IInformationControl</code>. As it is the responsibility of
+ * the implementer of <code>IInformationControl</code> to specify whether the information
+ * set is the information itself or a description of the information, only the infomation control
+ * can decide whether there is something that must be displayed.
+ *
+ * @since 2.0
+ */
+public interface IInformationControlExtension {
+
+ /**
+ * Returns whether this information control has contents to be displayed.
+ * @return <code>true</code> if there is contents to be displayed.
+ */
+ boolean hasContents();
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkRegionTarget.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkRegionTarget.java
new file mode 100644
index 000000000..f65f08f26
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkRegionTarget.java
@@ -0,0 +1,32 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+/**
+ * A mark region target to support marked regions as found in emacs.
+ *
+ * @since 2.0
+ */
+public interface IMarkRegionTarget {
+
+ /**
+ * Sets or clears a mark at the current cursor position.
+ *
+ * @param set sets the mark if <code>true</code>, clears otherwise.
+ */
+ void setMarkAtCursor(boolean set);
+
+ /**
+ * Swaps the mark and cursor position if the mark is in visible region.
+ */
+ void swapMarkAndCursor();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkSelection.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkSelection.java
new file mode 100644
index 000000000..91341a8b5
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IMarkSelection.java
@@ -0,0 +1,48 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.jface.viewers.ISelection;
+
+
+/**
+ * A mark selection. Can be returned by text viewers implementing the
+ * <code>IMarkRegionTarget</code> interface.
+ *
+ * @since 2.0
+ */
+public interface IMarkSelection extends ISelection {
+
+ /**
+ * Returns the marked document.
+ *
+ * @return the marked document
+ */
+ IDocument getDocument();
+
+ /**
+ * Returns the mark position. The offset may be <code>-1</code> if there's no marked region.
+ *
+ * @return the mark position or <code>-1</code> if there is no marked region
+ */
+ int getOffset();
+
+ /**
+ * Returns the length of the mark selection. The length may be negative, if the caret
+ * is before the mark position. The length has no meaning if <code>getOffset()</code>
+ * returns <code>-1</code>.
+ *
+ * @return the length of the mark selection. Result is undefined for <code>getOffset == -1</code>
+ */
+ int getLength();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IRewriteTarget.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IRewriteTarget.java
new file mode 100644
index 000000000..559108d91
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IRewriteTarget.java
@@ -0,0 +1,55 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+ /**
+ * A target publishing the required functions to modify a document that is displayed
+ * in the ui, such as in a text viewer. It provides access to the document and control
+ * over the redraw behavior and the batching of document changes into undo commands.
+ *
+ * @see org.eclipse.jface.text.ITextViewer
+ * @see org.eclipse.jface.text.IDocument
+ * @see org.eclipse.jface.text.IUndoManager
+ * @since 2.0
+ */
+public interface IRewriteTarget {
+
+ /**
+ * Returns the document of this target.
+ *
+ * @return the document of this target
+ */
+ IDocument getDocument();
+
+ /**
+ * Disables/enables redrawing of the ui while modifying the target's document.
+ *
+ * @param redraw <code>true</code> if the document's ui presentation should
+ * be updated, <code>false</code> otherwise
+ */
+ void setRedraw(boolean redraw);
+
+ /**
+ * If an undo manager is connected to the document's ui presentation, this
+ * method tells the undo manager to fold all subsequent changes into
+ * one single undo command until <code>endCompoundChange</code> is called.
+ */
+ void beginCompoundChange();
+
+ /**
+ * If an undo manager is connected to the document's ui presentation, this method
+ * tells the undo manager to stop the folding of changes into a single undo command.
+ * After this call, all subsequent changes are considered to be individually undoable.
+ */
+ void endCompoundChange();
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextDoubleClickStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextDoubleClickStrategy.java
new file mode 100644
index 000000000..c77c7dc72
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextDoubleClickStrategy.java
@@ -0,0 +1,27 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * A text double click strategy defines the reaction of a text viewer
+ * to mouse double click events. For that the strategy must be installed
+ * on the text viewer.<p>
+ * Clients may implements this interface or use the standard implementation
+ * <code>DefaultTextDoubleClickStrategy</code>.
+ *
+ * @see ITextViewer
+ * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(MouseEvent)
+ */
+public interface ITextDoubleClickStrategy {
+
+ /**
+ * The mouse has been double clicked on the given text viewer.
+ *
+ * @param viewer the viewer into which has been double clicked
+ */
+ void doubleClicked(ITextViewer viewer);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHover.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHover.java
new file mode 100644
index 000000000..727903192
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextHover.java
@@ -0,0 +1,46 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Provides a hover popup which appears on top of the text viewer with
+ * relevant display information. If the text hover does not provide information
+ * no hover popup is shown. Any implementer of this interface must be capable of
+ * operating in a non-UI thread.<p>
+ * Clients may implement this interface.
+ *
+ * @see ITextViewer
+ */
+public interface ITextHover {
+
+ /**
+ * Returns the text which should be presented if a hover popup is shown
+ * for the specified hover region. The hover region has the same semantics
+ * as the region returned by <code>getHoverRegion</code>. If the returned
+ * string is <code>null</code> or empty no hover popup will be shown.
+ *
+ * @param textViewer the viewer on which the hover popup should be shown
+ * @param hoverRegion the text range in the viewer which is used to determine
+ * the hover display information
+ * @return the hover popup display information
+ */
+ String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion);
+
+ /**
+ * Returns the text region which should serve as the source of information
+ * to compute the hover popup display information. The popup has been requested
+ * for the given offset.<p>
+ * For example, if hover information can be provided on a per method basis in a
+ * source viewer, the offset should be used to find the enclosing method and the
+ * source range of the method should be returned.
+ *
+ * @param textViewer the viewer on which the hover popup should be shown
+ * @param offset the offset for which the hover request has been issued
+ * @return the hover region used to compute the hover display information
+ */
+ IRegion getHoverRegion(ITextViewer textViewer, int offset);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextInputListener.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextInputListener.java
new file mode 100644
index 000000000..016b9ace0
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextInputListener.java
@@ -0,0 +1,34 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Text input listeners registered with an text viewer are informed
+ * if the document serving as the text viewer's model is replaced.
+ * Clients may implement this interface.
+ *
+ * @see ITextViewer
+ * @see IDocument
+ */
+public interface ITextInputListener {
+
+ /**
+ * Called before the input document is replaced.
+ *
+ * @param oldInput the text viewer's previous input document
+ * @param newInput the text viewer's new input document
+ */
+ void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput);
+
+ /**
+ * Called after the input document has been replaced.
+ *
+ * @param oldInput the text viewer's previous input document
+ * @param newInput the text viewer's new input document
+ */
+ void inputDocumentChanged(IDocument oldInput, IDocument newInput);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextListener.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextListener.java
new file mode 100644
index 000000000..ba6006111
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextListener.java
@@ -0,0 +1,33 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Text listeners registered with an text viewer are informed about
+ * all text viewer modifications by means of text events. A text event
+ * describes a change as a replace operation.<p>
+ * The changes described in the event are the changes applied to the text viewer's
+ * widget (i.e. its visual representation) and not those applied to the text viewer's
+ * document. The text event can be asked to return the according document
+ * event. If a text listener receives a text event, it is guaranteed that
+ * both the document and the viewer's visual representation are in sync.<p>
+ * Clients may implement this interface.
+ *
+ * @see ITextViewer
+ * @see TextEvent
+ * @see DocumentEvent
+ */
+public interface ITextListener {
+
+ /**
+ * The visual representation of a text viewer this listener is registered with
+ * has been changed.
+ *
+ * @param event the description of the change
+ */
+ void textChanged(TextEvent event);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTarget.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTarget.java
new file mode 100644
index 000000000..9ae0600c1
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTarget.java
@@ -0,0 +1,95 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Defines the target for a text operation.
+ */
+public interface ITextOperationTarget {
+
+
+ /**
+ * Text operation code for undoing the last edit command.
+ */
+ static final int UNDO= 1;
+
+ /**
+ * Text operation code for redoing the last undone edit command.
+ */
+ static final int REDO= 2;
+
+ /**
+ * Text operation code for moving the selected text to the clipboard.
+ */
+ static final int CUT= 3;
+
+ /**
+ * Text operation code for copying the selected text to the clipboard.
+ */
+ static final int COPY= 4;
+
+ /**
+ * Text operation code for inserting the clipboard content at the
+ * current position.
+ */
+ static final int PASTE= 5;
+
+ /**
+ * Text operation code for deleting the selected text or if selection
+ * is empty the character at the right of the current position.
+ */
+ static final int DELETE= 6;
+
+ /**
+ * Text operation code for selecting the complete text.
+ */
+ static final int SELECT_ALL= 7;
+
+ /**
+ * Text operation code for shifting the selected text block to the right.
+ */
+ static final int SHIFT_RIGHT= 8;
+
+ /**
+ * Text operation code for unshifting the selected text block to the left.
+ */
+ static final int SHIFT_LEFT= 9;
+
+ /**
+ * Text operation code for printing the complete text.
+ */
+ static final int PRINT= 10;
+
+ /**
+ * Text operation code for prefixing the selected text block.
+ */
+ static final int PREFIX= 11;
+
+ /**
+ * Text operation code for removing the prefix from the selected text block.
+ */
+ static final int STRIP_PREFIX= 12;
+
+
+ /**
+ * Returns whether the operation specified by the given operation code
+ * can be performed.
+ *
+ * @param operation the operation code
+ * @return <code>true</code> if the specified operation can be performed
+ */
+ boolean canDoOperation(int operation);
+
+ /**
+ * Performs the operation specified by the operation code on the target.
+ * <code>doOperation</code> must only be called if <code>canDoOperation</code>
+ * returns <code>true</code>.
+ *
+ * @param operation the operation code
+ */
+ void doOperation(int operation);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTargetExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTargetExtension.java
new file mode 100644
index 000000000..e96e370e8
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextOperationTargetExtension.java
@@ -0,0 +1,31 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * Extension interface to <code>ITextOperationTarget</code>. Allows a client to control
+ * the enable state of operations provided by this target.
+ *
+ * @since 2.0
+ */
+public interface ITextOperationTargetExtension {
+
+ /**
+ * Enables/disabled the given text operation.
+ *
+ * @param operation the operation to enable/disable
+ * @param enable <code>true</code> to enable the operation otherwise <code>false</code>
+ */
+ void enableOperation(int operation, boolean enable);
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextSelection.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextSelection.java
new file mode 100644
index 000000000..7967c4d52
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextSelection.java
@@ -0,0 +1,76 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.viewers.ISelection;
+
+
+/**
+ * This interface represents a textual selection. A text selection is
+ * a range of characters. Although a text selection is a snapshot taken
+ * at a particular point in time, it must not copy the line information
+ * and the selected text from the selection provider.<p>
+ * If, for example, the selection provider is a source viewer, and a text
+ * selection is created for the range [5, 10], the line formation for
+ * the 5th character must not be determined and remembered at the point
+ * of creation. It can rather be determined at the point, when
+ * <code>getStartLine</code> is called. If the source viewer range [0, 15]
+ * has been changed in the meantime between the creation of the text
+ * selection object and the invocation of <code>getStartLine</code>, the returned
+ * line number may differ from the line number of the 5th character at the
+ * point of creation of the text selection object.<p> The contract of this
+ * interface is that weak in order to allow for efficient implementations.<p>
+ * Clients may implement this interface or use the default implementation provided
+ * by <code>TextSelection</code>.
+ */
+public interface ITextSelection extends ISelection {
+
+ /**
+ * Returns the offset of the selected text.
+ *
+ * @return the offset of the selected text
+ */
+ int getOffset();
+
+ /**
+ * Returns the length of the selected text.
+ *
+ * @return the length of the selected text
+ */
+ int getLength();
+
+ /**
+ * Returns number of the line containing the offset of the selected text.
+ * If the underlying text has been changed between the creation of this
+ * selection object and the call of this method, the value returned might
+ * differ from what it would have been at the point of creation.
+ *
+ * @return the start line of this selection or -1 if there is no valid line information
+ */
+ int getStartLine();
+
+ /**
+ * Returns the number of the line containing the last character of the selected text.
+ * If the underlying text has been changed between the creation of this
+ * selection object and the call of this method, the value returned might
+ * differ from what it would have been at the point of creation.
+ *
+ * @return the end line of this selection or -1 if there is no valid line information
+ */
+ int getEndLine();
+
+ /**
+ * Returns the selected text.
+ * If the underlying text has been changed between the creation of this
+ * selection object and the call of this method, the value returned might
+ * differ from what it would have been at the point of creation.
+ *
+ * @return the selected text or <code>null</code> if there is no valid text information
+ */
+ String getText();
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewer.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewer.java
new file mode 100644
index 000000000..4cf7376eb
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewer.java
@@ -0,0 +1,432 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.jface.viewers.ISelectionProvider;
+
+
+/**
+ * A text viewer turns a text widget into a document-based text widget.
+ * It supports the following kinds of listeners:
+ * <ul>
+ * <li> viewport listeners to inform about changes of the viewer's viewport
+ * <li> text listeners to inform about changes of the document and the subsequent viewer change
+ * <li> text input listeners to inform about changes of the viewer's input document.
+ * </ul>
+ * A text viewer supports a set of plug-ins which define its behavior:
+ * <ul>
+ * <li> undo manager
+ * <li> double click behavior
+ * <li> auto indentation
+ * <li> text hover
+ * </ul>
+ * Installed plug-ins are not automatically activated. Plug-ins must be activated with the
+ * <code>activatePlugins</code> call. Most plug-ins can be defined per content type.
+ * Content types are derived from the partitioning of the text viewer's input document.<p>
+ * A text viewer also provides the concept of event consumption. Events handled by the
+ * viewer can be filtered and processed by a dynamic event consumer.<p>
+ * A text viewer provides several text editing functions, some of them are configurable,
+ * through a text operation target interface. It also supports a presentation mode
+ * in which it only shows specified sections of its document. The viewer's presentation mode
+ * does not affect any client of the viewer other than text listeners.<p>
+ * Clients may implement this interface or use the standard implementation
+ * <code>TextViewer</code>.
+ *
+ * @see IDocument
+ * @see ITextInputListener
+ * @see IViewportListener
+ * @see ITextListener
+ * @see IEventConsumer
+ */
+public interface ITextViewer {
+
+
+ /* ---------- widget --------- */
+
+ /**
+ * Returns this viewer's SWT control, <code>null</code> if the control is disposed.
+ *
+ * @return the SWT control
+ */
+ StyledText getTextWidget();
+
+
+ /* --------- plugins --------- */
+
+ /**
+ * Sets this viewer's undo manager.
+ *
+ * @param undoManager the new undo manager. <code>null</code> is a valid argument.
+ */
+ void setUndoManager(IUndoManager undoManager);
+
+ /**
+ * Sets this viewer's text double click strategy for the given content type.
+ *
+ * @param strategy the new double click strategy. <code>null</code> is a valid argument.
+ * @param contentType the type for which the strategy is registered
+ */
+ void setTextDoubleClickStrategy(ITextDoubleClickStrategy strategy, String contentType);
+
+ /**
+ * Sets this viewer's auto indent strategy for the given content type.
+ *
+ * @param strategy the new auto indent strategy. <code>null</code> is a valid argument.
+ * @param contentType the type for which the strategy is registered
+ */
+ void setAutoIndentStrategy(IAutoIndentStrategy strategy, String contentType);
+
+ /**
+ * Sets this viewer's text hover for the given content type.
+ *
+ * @param textViewerHover the new hover. <code>null</code> is a valid argument.
+ * @param contentType the type for which the hover is registered
+ */
+ void setTextHover(ITextHover textViewerHover, String contentType);
+
+ /**
+ * Activates the installed plug-ins. If the plug-ins are already activated
+ * this call has no effect.
+ */
+ void activatePlugins();
+
+ /**
+ * Resets the installed plug-ins. If plug-ins change their state or
+ * behavior over the course of time, this method causes them to be set
+ * back to their initial state and behavior. E.g., if an <code>IUndoManager</code>
+ * has been installed on this text viewer, the manager's list of remembered
+ * text editing operations is removed.
+ */
+ void resetPlugins();
+
+
+
+ /* ---------- listeners ------------- */
+
+ /**
+ * Adds the given viewport listener to this viewer. The listener
+ * is informed about all changes to the visible area of this viewer.
+ * If the listener is already registered with this viewer, this call
+ * has no effect.
+ *
+ * @param listener the listener to be added
+ */
+ void addViewportListener(IViewportListener listener);
+
+ /**
+ * Removes the given listener from this viewer's set of viewport listeners.
+ * If the listener is not registered with this viewer, this call has
+ * no effect.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeViewportListener(IViewportListener listener);
+
+ /**
+ * Adds a text listener to this viewer. If the listener is already registered
+ * with this viewer, this call has no effect.
+ *
+ * @param listener the listener to be added
+ */
+ void addTextListener(ITextListener listener);
+
+ /**
+ * Removes the given listener from this viewer's set of text listeners.
+ * If the listener is not registered with this viewer, this call has
+ * no effect.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeTextListener(ITextListener listener);
+
+ /**
+ * Adds a text input listener to this viewer. If the listener is already registered
+ * with this viewer, this call has no effect.
+ *
+ * @param listener the listener to be added
+ */
+ void addTextInputListener(ITextInputListener listener);
+
+ /**
+ * Removes the given listener from this viewer's set of text input listeners.
+ * If the listener is not registered with this viewer, this call has
+ * no effect.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeTextInputListener(ITextInputListener listener);
+
+
+
+ /* -------------- model manipulation ------------- */
+
+ /**
+ * Sets the given document as the text viewer's model and updates the
+ * presentation accordingly. An approriate <code>TextEvent</code> is
+ * issued. This text event does not carry a related document event.
+ *
+ * @param document the viewer's new input document
+ */
+ void setDocument(IDocument document);
+
+ /**
+ * Returns the text viewer's input document.
+ *
+ * @return the viewer's input document
+ */
+ IDocument getDocument();
+
+
+ /* -------------- event handling ----------------- */
+
+ /**
+ * Registers an event consumer with this viewer.
+ *
+ * @param consumer the viewer's event consumer. <code>null</code> is a valid argument.
+ */
+ void setEventConsumer(IEventConsumer consumer);
+
+ /**
+ * Sets the editable mode.
+ *
+ * @param editable the editable mode
+ */
+ void setEditable(boolean editable);
+
+ /**
+ * Returns whether the shown text can be manipulated.
+ *
+ * @return the viewer's editable mode
+ */
+ boolean isEditable();
+
+
+ /* ----------- visible region support ------------- */
+
+ /**
+ * Sets the given document as this viewer's model and
+ * makes the specified region visible in the presentation. An approriate
+ * <code>TextEvent</code> is issued. The text event does not carry a
+ * related document event. This method is a convenience method for
+ * <code>setDocument(document);setVisibleRegion(offset, length)</code>.
+ *
+ * @param document the new input document
+ * @param visibleRegionOffset the offset of the visible region
+ * @param visibleRegionLength the length of the visible region
+ */
+ void setDocument(IDocument document, int visibleRegionOffset, int visibleRegionLength);
+
+ /**
+ * Sets the region of this viewer's document which will be visible in the presentation.
+ *
+ * @param offset the offset of the visible region
+ * @param length the length of the visible region
+ */
+ void setVisibleRegion(int offset, int length);
+
+ /**
+ * Resets the region of this viewer's document which is visible in the presentation.
+ * Afterwards, the whole document is presented again.
+ */
+ void resetVisibleRegion();
+
+ /**
+ * Returns the current visible region of this viewer's document.
+ * The result may differ from the argument passed to <code>setVisibleRegion</code>
+ * if the document has been modified since then.
+ *
+ * @return this viewer's current visible region
+ */
+ IRegion getVisibleRegion();
+
+ /**
+ * Returns whether a given range overlaps with the visible region of this viewer's document.
+ *
+ * @return <code>true</code> if the specified range overlaps with the visible region
+ */
+ boolean overlapsWithVisibleRegion(int offset, int length);
+
+
+
+ /* ------------- presentation manipulation ----------- */
+
+ /**
+ * Applies the color information encoded in the given text presentation.
+ * <code>controlRedraw</code> tells this viewer whether it should take care of
+ * redraw management or not. If, e.g., this call is one in a sequence of multiple
+ * coloring calls, it is more appropriate to explicitly control redrawing at the
+ * beginning and the end of the sequence.
+ *
+ * @param presentation the presentation to be applied to this viewer
+ * @param controlRedraw indicates whether this viewer should manage redraws
+ */
+ void changeTextPresentation(TextPresentation presentation, boolean controlRedraw);
+
+ /**
+ * Marks the currently applied text presentation as invalid. It is the viewer's
+ * responsibility to take any action it can to repair the text presentation.
+ *
+ * @since 2.0
+ */
+ void invalidateTextPresentation();
+
+ /**
+ * Applies the given color to this viewer's selection.
+ *
+ * @param color the color to be applied
+ */
+ void setTextColor(Color color);
+
+ /**
+ * Applies the given color to the specified section of this viewer.
+ * <code>controlRedraw</code> tells this viewer whether it should take care of
+ * redraw management or not.
+ *
+ * @param color the color to be applied
+ * @param offset the offset of the range to be colored
+ * @param length the length of the range to be colored
+ * @param controlRedraw indicates whether this viewer should manage redraws
+ */
+ void setTextColor(Color color, int offset, int length, boolean controlRedraw);
+
+
+ /* --------- target handling and configuration ------------ */
+
+ /**
+ * Returns the text operation target of this viewer.
+ *
+ * @return the text operation target of this viewer
+ */
+ ITextOperationTarget getTextOperationTarget();
+
+ /**
+ * Returns the find/replace operation target of this viewer.
+ *
+ * @return the find/replace operation target of this viewer
+ */
+ IFindReplaceTarget getFindReplaceTarget();
+
+ /**
+ * Sets the string that is used as prefix when lines of the given
+ * content type are prefixed by the prefix text operation.
+ * Sets the strings that are used as prefixes when lines of the given content type
+ * are prefixed using the prefix text operation. The prefixes are considered equivalent.
+ * Inserting a prefix always inserts the defaultPrefixes[0].
+ * Removing a prefix removes all of the specified prefixes.
+ *
+ * @param defaultPrefixes the prefixes to be used
+ * @param contentType the content type for which the prefixes are specified
+ * @since 2.0
+ */
+ void setDefaultPrefixes(String[] defaultPrefixes, String contentType);
+
+ /**
+ * Sets the strings that are used as prefixes when lines of the given content type
+ * are shifted using the shift text operation. The prefixes are considered equivalent.
+ * Thus "\t" and " " can both be used as prefix characters.
+ * Shift right always inserts the indentPrefixes[0].
+ * Shift left removes all of the specified prefixes.
+ *
+ * @param indentPrefixes the prefixes to be used
+ * @param contentType the content type for which the prefixes are specified
+ */
+ void setIndentPrefixes(String[] indentPrefixes, String contentType);
+
+
+
+ /* --------- selection handling -------------- */
+
+ /**
+ * Sets the selection to the specified range.
+ *
+ * @param offset the offset of the selection range
+ * @param length the length of the selection range
+ */
+ void setSelectedRange(int offset, int length);
+
+ /**
+ * Returns the range of the current selection in coordinates of this viewer's document.
+ *
+ * @return the current selection
+ */
+ Point getSelectedRange();
+
+ /**
+ * Returns a selection provider dedicated to this viewer. Subsequent
+ * calls to this method return always the same selection provider.
+ *
+ * @return this viewer's selection provider
+ */
+ ISelectionProvider getSelectionProvider();
+
+
+ /* ------------- appearance manipulation --------------- */
+
+ /**
+ * Ensures that the given range is visible.
+ *
+ * @param offset the offset of the range to be revealed
+ * @param length the length of the range to be revealed
+ */
+ void revealRange(int offset, int length);
+
+ /**
+ * Scrolls the widget so the the given index is the line
+ * with the smallest line number of all visible lines.
+ *
+ * @param index the line which should become the top most line
+ */
+ void setTopIndex(int index);
+
+ /**
+ * Returns the visible line with the smallest line number.
+ *
+ * @return the number of the top most visible line
+ */
+ int getTopIndex();
+
+ /**
+ * Returns the document offset of the upper left corner of this viewer's viewport.
+ *
+ * @return the upper left corner offset
+ */
+ int getTopIndexStartOffset();
+
+ /**
+ * Returns the visible line with the highest line number.
+ *
+ * @return the number of the bottom most line
+ */
+ int getBottomIndex();
+
+ /**
+ * Returns the document offset of the lower right
+ * corner of this viewer's viewport. This is the visible character
+ * with the highest character position. If the content of this viewer
+ * is shorter, the position of the last character of the content is returned.
+ *
+ * @return the lower right corner offset
+ */
+ int getBottomIndexEndOffset();
+
+ /**
+ * Returns the vertical offset of the first visible line.
+ *
+ * @return the vertical offset of the first visible line
+ */
+ int getTopInset();
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension.java
new file mode 100644
index 000000000..ccc6f1314
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/ITextViewerExtension.java
@@ -0,0 +1,101 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.widgets.Control;
+
+
+/**
+ * Extension interface for <code>ITextViewer</code>. Extends <code>ITextViewer</code> with
+ * <ul>
+ * <li> a replacement of the event consumer mechanism (methods dealing with <code>VerifyKeyListener</code>)
+ * <li> access to the control of this viewer
+ * <li> marked region support a la emacs
+ * <li> control of the viewer's redraw behavior (@see #setRedraw)
+ * <li> access to the viewer's rewrite target
+ * </ul>
+ *
+ * @since 2.0
+ */
+public interface ITextViewerExtension {
+
+ /**
+ * Inserts the verify key listener at the beginning of the viewer's
+ * list of verify key listeners. If the listener is already registered
+ * with the viewer this call moves the listener to the beginnng of
+ * the list.
+ *
+ * @param listener the listener to be inserted
+ */
+ void prependVerifyKeyListener(VerifyKeyListener listener);
+
+ /**
+ * Appends a verify key listener to the viewer's list of verify
+ * key listeners. If the listener is already registered with the viewer
+ * this call moves the listener to the end of the list.
+ *
+ * @param listener the listener to be added
+ */
+ void appendVerifyKeyListener(VerifyKeyListener listener);
+
+ /**
+ * Removes the verify key listener from the viewer's list of verify key listeners.
+ * If the listener is not registered with this viewer, this call has no effect.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeVerifyKeyListener(VerifyKeyListener listener);
+
+ /**
+ * Returns the control of this viewer.
+ *
+ * @return the control of this viewer
+ */
+ Control getControl();
+
+ /**
+ * Sets or clears the mark. If offset is <code>-1</code>, the mark is cleared.
+ * If a mark is set and the selection is empty, cut and copy actions performed on this
+ * text viewer peform on the region limited by the positions of the mark and the cursor.
+ *
+ * @param offset the offset of the mark
+ */
+ void setMark(int offset);
+
+ /**
+ * Returns the mark position, <code>-1</code> if mark is not set.
+ *
+ * @return the mark position or <code>-1</code> if no mark is set
+ */
+ int getMark();
+
+ /**
+ * Enables/disables the redrawing of this text viewer. This temporarily disconnects
+ * the viewer from its underlying StyledText widget. While being disconnected only
+ * the viewer's selection may be changed using <code>setSelectedRange</code>.
+ * Any direct manipulation of the widget as well as calls to methods that change the viewer's
+ * presentation state (such as enabling the segmented view) are not allowed.
+ * When redrawing is disabled the viewer does not send out any selection or
+ * view port change notification. When redrawing is enabled again, a selection
+ * change notification is sent out for the selected range and this range is revealed.
+ */
+ void setRedraw(boolean redraw);
+
+ /**
+ * Returns the viewer's rewrite target.
+ *
+ * @return the viewer's rewrite target
+ */
+ IRewriteTarget getRewriteTarget();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManager.java
new file mode 100644
index 000000000..1a372371d
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IUndoManager.java
@@ -0,0 +1,86 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * An undo manager is connected to at most one text viewer.
+ * It monitors the text viewer and keeps a history of the
+ * changes applied to the viewer. The undo manager groups those
+ * changes into user interactions which on an undo request are
+ * rolled back in one atomic change. <p>
+ * Clients may implement this interface or use the standard
+ * implementation <code>DefaultUndoManager</code>.
+ */
+public interface IUndoManager {
+
+ /**
+ * Connects this undo manager to the given text viewer.
+ *
+ * @param viewer the viewer the undo manager is connected to
+ */
+ void connect(ITextViewer viewer);
+
+ /**
+ * Disconnects this undo manager from its text viewer.
+ * If this undo manager hasn't been connected before this
+ * operation has no effect.
+ */
+ void disconnect();
+
+ /**
+ * Signals the undo manager that all subsequent changes until
+ * <code>endCompoundChange</code> is called are to be undone in one piece.
+ */
+ void beginCompoundChange();
+
+ /**
+ * Signals the undo manager that the sequence of changes which started with
+ * <code>beginCompoundChange</code> has been finished. All subsequent changes
+ * are considered to be individually undoable.
+ */
+ void endCompoundChange();
+
+ /**
+ * Resets the history of the undo manager. After that call,
+ * there aren't any undoable or redoable text changes.
+ */
+ void reset();
+
+ /**
+ * The given parameter determines the maximal length of the history
+ * remembered by the undo manager.
+ *
+ * @param undoLevel the length of this undo manager's history
+ */
+ void setMaximalUndoLevel(int undoLevel);
+
+ /**
+ * Returns whether at least one text change can be rolled back.
+ *
+ * @return <code>true</code> if at least one text change can be rolled back
+ */
+ boolean undoable();
+
+ /**
+ * Returns whether at least one text change can be repeated. A text change
+ * can be repeated only if it was executed and rolled back.
+ *
+ * @return <code>true</code> if at least on text change can be repeated
+ */
+ boolean redoable();
+
+ /**
+ * Rolls back the most recently executed text change.
+ */
+ void undo();
+
+ /**
+ * Repeats the most recently rolled back text change.
+ */
+ void redo();
+
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IViewportListener.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IViewportListener.java
new file mode 100644
index 000000000..8677db812
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IViewportListener.java
@@ -0,0 +1,24 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Registered with a text viewer, viewport listeners are
+ * informed about changes of a text viewer's viewport. The view port is that
+ * portion of the viewer's document which is visible in the viewer. <p>
+ * Clients may implement this interface.
+ *
+ * @see ITextViewer
+ */
+public interface IViewportListener {
+
+ /**
+ * Informs about viewport changes. The given vertical position
+ * is the new vertical scrolling offset measured in pixels.
+ */
+ void viewportChanged(int verticalOffset);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeper.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeper.java
new file mode 100644
index 000000000..a11f3668d
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenKeeper.java
@@ -0,0 +1,34 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+/**
+ * A widget token keeper may require a widget token from an
+ * <code>IWidgetTokenOwner</code> and release the token
+ * to the owner after usage. A widget token owner may request
+ * the token from the token keeper. The keeper may deny that.
+ *
+ * @since 2.0
+ */
+public interface IWidgetTokenKeeper {
+
+ /**
+ * The given widget token owner requests the widget token back from
+ * this token keeper. Returns <code>true</code> if the token is released
+ * by this token keeper. Note, the keeper must not call
+ * <code>releaseWidgetToken(IWidgetTokenKeeper)</code> explicitly.
+ *
+ * @param owner the token owner
+ * @return <code>true</code> if token has been released <code>false</code> otherwise
+ */
+ boolean requestWidgetToken(IWidgetTokenOwner owner);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwner.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwner.java
new file mode 100644
index 000000000..6c82bc7fb
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IWidgetTokenOwner.java
@@ -0,0 +1,45 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * A widget token must be aquired in order to display
+ * information in a temporary window. The intent behind this concept is that
+ * only one temporary window should be presented at any moment in time and
+ * also to avoid overlapping temporary windows.
+ *
+ * @since 2.0
+ */
+public interface IWidgetTokenOwner {
+
+ /**
+ * Requests the widget token from this token owner. Returns
+ * <code>true</code> if the token has been aquired or is
+ * already owned by the requester. This method is non-blocking.
+ *
+ * @param requester the token requester
+ * @return <code>true</code> if requester aquires the token,
+ * <code>false</code> otherwise
+ */
+ boolean requestWidgetToken(IWidgetTokenKeeper requester);
+
+ /**
+ * The given token keeper releases the token to this
+ * token owner. If the token has previously not been held
+ * by the given token keeper, nothing happens. This
+ * method is non-blocking.
+ *
+ * @param tokenKeeper the token keeper
+ */
+ void releaseWidgetToken(IWidgetTokenKeeper tokenKeeper);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.java
new file mode 100644
index 000000000..62940e97f
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.java
@@ -0,0 +1,26 @@
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+package org.eclipse.jface.text;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+class JFaceTextMessages {
+
+ private static final String RESOURCE_BUNDLE= "org.eclipse.jface.text.JFaceTextMessages";//$NON-NLS-1$
+
+ private static ResourceBundle fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+ private JFaceTextMessages() {
+ }
+
+ public static String getString(String key) {
+ try {
+ return fgResourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$
+ }
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.properties b/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.properties
new file mode 100644
index 000000000..295194358
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextMessages.properties
@@ -0,0 +1,28 @@
+#########################################
+# (c) Copyright IBM Corp. 2000, 2001.
+# All Rights Reserved.
+#########################################
+
+
+TextViewer.error.bad_location.WidgetCommand.setEvent=TextViewer.WidgetCommand.setEvent: BadLocationException
+TextViewer.error.bad_location.findAndSelect=TextViewer.findAndSelect: BadLocationException
+TextViewer.error.bad_location.getBottomIndex=TextViewer.getBottomIndex: BadLocationException
+TextViewer.error.bad_location.getBottomIndexEndOffset=TextViewer.getBottomIndexEndOffset: BadLocationException
+TextViewer.error.bad_location.getFirstCompleteLineOfRegion=TextViewer.getFirstCompleteLineOfRegion: BadLocationException
+TextViewer.error.bad_location.getTopIndex=TextViewer.getTopIndex: BadLocationException
+TextViewer.error.bad_location.getTopIndexStartOffset=TextViewer.getTopIndexStartOffset: BadLocationException
+TextViewer.error.bad_location.selectContentTypePlugin=TextViewer.selectContentTypePlugin: BadLocationException
+TextViewer.error.bad_location.setTopIndex_1=TextViewer.setTopIndex: BadLocationException
+TextViewer.error.bad_location.setTopIndex_2=TextViewer.setTopIndex: BadLocationException
+TextViewer.error.bad_location.shift_1=TextViewer.shift: BadLocationException
+TextViewer.error.bad_location.shift_2=TextViewer.shift: BadLocationException
+TextViewer.error.bad_location.verifyText=TextViewer.verifyText: BadLocationException
+TextViewer.error.invalid_range=Invalid range argument
+TextViewer.error.invalid_visible_region_1=Invalid visible region argument
+TextViewer.error.invalid_visible_region_2=Invalid visible region argument
+
+InfoPopup.info_delay_timer_name=AdditionalInfo Delay
+
+ContentAssistant.assist_delay_timer_name=AutoAssist Delay
+
+AbstractHoverInformationControlManager.hover.restarter=Hover Restart Delay
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/MarkSelection.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/MarkSelection.java
new file mode 100644
index 000000000..83cb0b5e1
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/MarkSelection.java
@@ -0,0 +1,69 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v0.5
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v05.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * Default implementation of <code>IMarkSelection</code>.
+ * @since 2.0
+ */
+public class MarkSelection implements IMarkSelection {
+
+ /** The marked document. */
+ private final IDocument fDocument;
+ /** The offset of the mark selection. */
+ private final int fOffset;
+ /** The length of the mark selection. */
+ private final int fLength;
+
+ /**
+ * Creates a MarkSelection.
+ *
+ * @param document the marked document
+ * @param offset the offset of the mark
+ * @param length the length of the mark, may be negative if caret before offset
+ */
+ public MarkSelection(IDocument document, int offset, int length) {
+ fDocument= document;
+ fOffset= offset;
+ fLength= length;
+ }
+
+ /*
+ * @see IMarkSelection#getDocument()
+ */
+ public IDocument getDocument() {
+ return fDocument;
+ }
+
+ /*
+ * @see IMarkSelection#getOffset()
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /*
+ * @see IMarkSelection#getLength()
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /*
+ * @see ISelection#isEmpty()
+ */
+ public boolean isEmpty() {
+ return fLength == 0;
+ }
+
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/PropagatingFontFieldEditor.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/PropagatingFontFieldEditor.java
new file mode 100644
index 000000000..e31916724
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/PropagatingFontFieldEditor.java
@@ -0,0 +1,132 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+import org.eclipse.jface.preference.FontFieldEditor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+
+/**
+ * This font field editor implements chaining between a source preference
+ * store and a target preference store. Any time the source preference
+ * store changes, the change is propagated to the target store. Propagation
+ * means that the actual value stored in the source store is set as default
+ * value in the target store. If the target store does not contain a value
+ * other than the default value, the new default value is immediately
+ * effective.
+ *
+ * @see FontFieldEditor
+ * @since 2.0
+ */
+public class PropagatingFontFieldEditor extends FontFieldEditor {
+
+ /** The editor's parent widget */
+ private Composite fParent;
+ /** The representation of the default font choice */
+ private String fDefaultFontLabel;
+
+ /**
+ * Creates a new font field editor with the given parameters.
+ *
+ * @param name the editor's name
+ * @param labelText the text shown as editor description
+ * @param parent the editor's parent widget
+ * @param defaultFontLabel the label shown in the editor value field when the default value should be taken
+ */
+ public PropagatingFontFieldEditor(String name, String labelText, Composite parent, String defaultFontLabel) {
+ super(name, labelText, parent);
+ fParent= parent;
+ fDefaultFontLabel= defaultFontLabel == null ? "" : defaultFontLabel; //$NON-NLS-1$
+ }
+
+ /*
+ * @see FontFieldEditor#doLoad()
+ */
+ protected void doLoad() {
+ if (getPreferenceStore().isDefault(getPreferenceName()))
+ loadDefault();
+ super.doLoad();
+ checkForDefault();
+ }
+
+ /*
+ * @see FontFieldEditor#doLoadDefault()
+ */
+ protected void doLoadDefault() {
+ super.doLoadDefault();
+ checkForDefault();
+ }
+
+ /**
+ * Checks whether this editor presents the default value "inheritated"
+ * from the workbench rather than its own font.
+ */
+ private void checkForDefault() {
+ if (presentsDefaultValue()) {
+ Control c= getValueControl(fParent);
+ if (c instanceof Label)
+ ((Label) c).setText(fDefaultFontLabel);
+ }
+ }
+
+ /**
+ * Propagates the font set in the source store to the
+ * target store using the given keys.
+ *
+ * @param source the store from which to read the text font
+ * @param sourceKey the key under which the font can be found
+ * @param target the store to which to propagate the font
+ * @param targetKey the key under which to store the font
+ */
+ private static void propagateFont(IPreferenceStore source, String sourceKey, IPreferenceStore target, String targetKey) {
+ FontData fd= PreferenceConverter.getFontData(source, sourceKey);
+ if (fd != null) {
+ boolean isDefault= target.isDefault(targetKey); // save old state!
+ PreferenceConverter.setDefault(target, targetKey, fd);
+ if (isDefault) {
+ // restore old state
+ target.setToDefault(targetKey);
+ }
+ }
+ }
+
+ /**
+ * Starts the propagation of the font preference stored in the source preference
+ * store under the source key to the target preference store using the target
+ * preference key.
+ *
+ * @param source the source preference store
+ * @param sourceKey the key to be used in the source preference store
+ * @param target the target preference store
+ * @param targetKey the key to be used in the target preference store
+ */
+ public static void startPropagate(final IPreferenceStore source, final String sourceKey, final IPreferenceStore target, final String targetKey) {
+ source.addPropertyChangeListener(new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (sourceKey.equals(event.getProperty()))
+ propagateFont(source, sourceKey, target, targetKey);
+ }
+ });
+
+ propagateFont(source, sourceKey, target, targetKey);
+ }
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextAttribute.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextAttribute.java
new file mode 100644
index 000000000..1a1e1fff9
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextAttribute.java
@@ -0,0 +1,121 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+
+
+/**
+ * Description of textual attributes such as color and style.
+ * Text attributes are considered value objects.
+ */
+public class TextAttribute {
+
+ /** Foreground color */
+ private Color foreground;
+
+ /** Background color */
+ private Color background;
+
+ /** The text style */
+ private int style;
+
+ /**
+ * Creates a text attribute with the given colors and style.
+ *
+ * @param foreground the foreground color
+ * @param background the background color
+ * @param style the style
+ */
+ public TextAttribute(Color foreground, Color background, int style) {
+ this.foreground= foreground;
+ this.background= background;
+ this.style= style;
+ }
+
+ /**
+ * Creates a text attribute for the given foreground color, no background color and
+ * with the SWT normal style.
+ *
+ * @param foreground the foreground color
+ */
+ public TextAttribute(Color foreground) {
+ this(foreground, null, SWT.NORMAL);
+ }
+
+ /*
+ * @see Object#equals
+ */
+ public boolean equals(Object object) {
+
+ if (object == this)
+ return true;
+
+ if (!(object instanceof TextAttribute))
+ return false;
+
+ TextAttribute a= (TextAttribute) object;
+ return (a.style == style && equals(a.foreground, foreground) && equals(a.background, background));
+ }
+
+ /**
+ * Returns whether the two given objects are equal.
+ *
+ * @param o1 the first object, can be <code>null</code>
+ * @param o2 the second object, can be <code>null</code>
+ * @return <code>true</code> if the given objects are equals
+ * @since 2.0
+ */
+ private boolean equals(Object o1, Object o2) {
+ if (o1 != null)
+ return o1.equals(o2);
+ return (o2 == null);
+ }
+
+ /*
+ * @see Object#hashCode
+ */
+ public int hashCode() {
+ int foregroundHash= foreground == null ? 0 : foreground.hashCode();
+ int backgroundHash= background == null ? 0 : background.hashCode();
+ return (foregroundHash << 24) | (backgroundHash << 16) | style;
+ }
+
+ /**
+ * Returns the attribute's foreground color.
+ *
+ * @return the attribute's foreground color
+ */
+ public Color getForeground() {
+ return foreground;
+ }
+
+ /**
+ * Returns the attribute's background color.
+ *
+ * @return the attribute's background color
+ */
+ public Color getBackground() {
+ return background;
+ }
+
+ /**
+ * Returns the attribute's style.
+ *
+ * @return the attribute's style
+ */
+ public int getStyle() {
+ return style;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextEvent.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextEvent.java
new file mode 100644
index 000000000..22c1d3009
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextEvent.java
@@ -0,0 +1,119 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * This event is sent to implementers of <code>ITextListener</code>. It represents a
+ * change applied to text viewer. The change is specified as a replace command using
+ * offset, length, inserted text, and replaced text. The text viewer issues a text event
+ * after the viewer has been changed either in response to a change of the viewer's document
+ * or when the viewer's visual content has been changed. In the first case, the text event
+ * also carries the original document event. Depending on the viewer's presentation mode,
+ * the text event coordinates are different from the document event's coordinates.
+ * Client's other than text viewer's don't create instances of this class.
+ *
+ * @see ITextListener
+ * @see ITextViewer
+ * @see DocumentEvent
+ */
+public class TextEvent {
+
+ /** Start offset of the change */
+ private int fOffset;
+ /** The length of the change */
+ private int fLength;
+ /** Inserted text */
+ private String fText;
+ /** Replaced text */
+ private String fReplacedText;
+ /** The original document event, may by null */
+ private DocumentEvent fDocumentEvent;
+ /**
+ * The redraw state of the viewer issuing this event
+ * @since 2.0
+ */
+ private boolean fViewerRedrawState;
+
+ /**
+ * Creates a new <code>TextEvent</code> based on the specification.
+ *
+ * @param offset the offset
+ * @param length the length
+ * @param replacedText the replaced text
+ * @param event the associated document event or <code>null</code> if none
+ * @param viewerRedrawState the redraw state of the viewer
+ */
+ protected TextEvent(int offset, int length, String text, String replacedText, DocumentEvent event, boolean viewerRedrawState) {
+ fOffset= offset;
+ fLength= length;
+ fText= text;
+ fReplacedText= replacedText;
+ fDocumentEvent= event;
+ fViewerRedrawState= viewerRedrawState;
+ }
+
+ /**
+ * Returns the offset of the event.
+ *
+ * @return the offset of the event
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /**
+ * Returns the length of the event.
+ *
+ * @return the length of the event
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /**
+ * Returns the text of the event.
+ *
+ * @return the text of the event
+ */
+ public String getText() {
+ return fText;
+ }
+
+ /**
+ * Returns the text replaced by this event.
+ *
+ * @return the text replaced by this event
+ */
+ public String getReplacedText() {
+ return fReplacedText;
+ }
+
+ /**
+ * Returns the corresponding document event that caused the viewer change
+ *
+ * @return the corresponding document event, <code>null</code> if a visual change only
+ */
+ public DocumentEvent getDocumentEvent() {
+ return fDocumentEvent;
+ }
+
+ /**
+ * Returns the viewer's redraw state.
+ *
+ * @return <code>true</code> if the viewer's redraw state is <code>true</code>
+ * @since 2.0
+ */
+ public boolean getViewerRedrawState() {
+ return fViewerRedrawState;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextPresentation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextPresentation.java
new file mode 100644
index 000000000..1c20dbb68
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextPresentation.java
@@ -0,0 +1,398 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+
+
+/**
+ * Describes the presentation styles for a section of an indexed text
+ * such as a document or string. A text presentation defines a default style
+ * for the whole section and in addition style differences for individual
+ * subsections. Text presentations can be narrowed down to a particular
+ * result window. All methods are result window aware, i.e. ranges outside
+ * the result window are always ignored.<p>
+ * All iterators provided by a text presentation assume that they enumerate
+ * non overlapping, consequtive ranges inside the default range. Thus, all
+ * these iterators do not include the default range. The default style range
+ * must be explicitly asked for using <code>getDefaultStyleRange</code>.
+ */
+public class TextPresentation {
+
+ /**
+ * Applies the given presentation to the given text widget. Helper method.
+ *
+ * @param presentation the style information
+ * @param the widget to which to apply the style information
+ * @since 2.0
+ */
+ public static void applyTextPresentation(TextPresentation presentation, StyledText text) {
+
+ StyleRange[] ranges= new StyleRange[presentation.getDenumerableRanges()];
+
+ int i= 0;
+ Iterator e= presentation.getAllStyleRangeIterator();
+ while (e.hasNext())
+ ranges[i++]= (StyleRange) e.next();
+
+ text.setStyleRanges(ranges);
+ }
+
+
+
+
+ /**
+ * Enumerates all the <code>StyleRange</code>s included in the presentation.
+ */
+ class FilterIterator implements Iterator {
+
+ protected int fIndex;
+ protected int fLength;
+ protected boolean fSkipDefaults;
+ protected IRegion fWindow;
+
+ /**
+ * <code>skipDefaults</code> tells the enumeration to skip all those style ranges
+ * which define the same style as the presentation's default style range.
+ */
+ protected FilterIterator(boolean skipDefaults) {
+
+ fSkipDefaults= skipDefaults;
+
+ fWindow= fResultWindow;
+ fIndex= getFirstIndexInWindow(fWindow);
+ fLength= getFirstIndexAfterWindow(fWindow);
+
+ if (fSkipDefaults)
+ computeIndex();
+ }
+
+ /*
+ * @see Iterator#next
+ */
+ public Object next() {
+ try {
+ StyleRange r= (StyleRange) fRanges.get(fIndex++);
+ return createWindowRelativeRange(fWindow, r);
+ } catch (ArrayIndexOutOfBoundsException x) {
+ throw new NoSuchElementException();
+ } finally {
+ if (fSkipDefaults)
+ computeIndex();
+ }
+ }
+
+ /*
+ * @see Iterator#hasNext
+ */
+ public boolean hasNext() {
+ return fIndex < fLength;
+ }
+
+ /*
+ * @see Iterator#remove
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns whether the given object should be skipped.
+ *
+ * @return <code>true</code> if teh object should be skipped by the iterator
+ */
+ protected boolean skip(Object o) {
+ StyleRange r= (StyleRange) o;
+ return r.similarTo(fDefaultRange);
+ }
+
+ /**
+ * Computes the index of the styled range that is the next to be enumerated.
+ */
+ protected void computeIndex() {
+ while (fIndex < fLength && skip(fRanges.get(fIndex)))
+ ++ fIndex;
+ }
+ };
+
+ /** The syle information for the range covered by the whole presentation */
+ private StyleRange fDefaultRange;
+ /** The member ranges of the presentation */
+ private ArrayList fRanges= new ArrayList();
+ /** A clipping region against which the presentation can be clipped when asked for results */
+ private IRegion fResultWindow;
+
+
+ /**
+ * Creates a new empty text presentation.
+ */
+ public TextPresentation() {
+ }
+
+ /**
+ * Sets the result window for this presentation. When dealing with
+ * this presentation all ranges which are outside the result window
+ * are ignored. For example, the size of the presentation is 0
+ * when there is no range inside the window even if there are ranges
+ * outside the window. All methods are aware of the result window.
+ *
+ * @param resultWindow the result window
+ */
+ public void setResultWindow(IRegion resultWindow) {
+ fResultWindow= resultWindow;
+ }
+
+ /**
+ * Set the default style range of this presentation.
+ * The default style range defines the overall area covered
+ * by this presentation and its style information.
+ *
+ * @param range the range decribing the default region
+ */
+ public void setDefaultStyleRange(StyleRange range) {
+ fDefaultRange= range;
+ }
+
+ /**
+ * Returns this presentation's default style range. The returned <code>StyleRange</code>
+ * is relative to the start of the result window.
+ *
+ * @return this presentation's default style range
+ */
+ public StyleRange getDefaultStyleRange() {
+ return createWindowRelativeRange(fResultWindow, fDefaultRange);
+ }
+
+ /**
+ * Add the given range to the presentation. The range must be a
+ * subrange of the presentation's default range.
+ *
+ * @param range the range to be added
+ */
+ public void addStyleRange(StyleRange range) {
+ checkConsistency(range);
+ fRanges.add(range);
+ }
+
+ /**
+ * Checks whether the given range is a subrange of the presentation's
+ * default style range.
+ *
+ * @param range the range to be checked
+ * @exception IllegalArgumentAxception if range is not a subrange of the presentation's default range
+ */
+ private void checkConsistency(StyleRange range) {
+
+ if (range == null)
+ throw new IllegalArgumentException();
+
+ if (fDefaultRange != null) {
+
+ if (range.start < fDefaultRange.start)
+ range.start= fDefaultRange.start;
+
+ int defaultEnd= fDefaultRange.start + fDefaultRange.length;
+ int end= range.start + range.length;
+ if (end > defaultEnd)
+ range.length -= (defaultEnd - end);
+ }
+ }
+
+ /**
+ * Returns the index of the first range which overlaps with the
+ * specified window.
+ *
+ * @param window the window to be used for searching
+ * @return the index of the first range overlapping with the window
+ */
+ private int getFirstIndexInWindow(IRegion window) {
+ int i= 0;
+ if (window != null) {
+ int start= window.getOffset();
+ while (i < fRanges.size()) {
+ StyleRange r= (StyleRange) fRanges.get(i++);
+ if (r.start + r.length > start) {
+ -- i;
+ break;
+ }
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Returns the index of the first range which comes after the specified window and does
+ * not overlap with this window.
+ *
+ * @param window the window to be used for searching
+ * @return the index of the first range behind the window and not overlapping with the window
+ */
+ private int getFirstIndexAfterWindow(IRegion window) {
+ int i= fRanges.size();
+ if (window != null) {
+ int end= window.getOffset() + window.getLength();
+ while (i > 0) {
+ StyleRange r= (StyleRange) fRanges.get(--i);
+ if (r.start < end) {
+ ++ i;
+ break;
+ }
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Returns a style range which is relative to the specified window and
+ * appropriately clipped if necessary. The original style range is not
+ * modified.
+ *
+ * @param window the reference window
+ * @param range the absolute range
+ * @return the window relative range based on the absolute range
+ */
+ private StyleRange createWindowRelativeRange(IRegion window, StyleRange range) {
+ if (window == null || range == null)
+ return range;
+
+ int start= range.start - window.getOffset();
+ if (start < 0)
+ start= 0;
+
+ int rangeEnd= range.start + range.length;
+ int windowEnd= window.getOffset() + window.getLength();
+ int end= (rangeEnd > windowEnd ? windowEnd : rangeEnd);
+ end -= window.getOffset();
+
+ StyleRange newRange= (StyleRange) range.clone();
+ newRange.start= start;
+ newRange.length= end - start;
+ return newRange;
+ }
+
+
+ /**
+ * Returns an iterator which enumerates all style ranged which define a style
+ * different from the presentation's default style range. The default style range
+ * is not enumerated.
+ *
+ * @return a style range interator
+ */
+ public Iterator getNonDefaultStyleRangeIterator() {
+ return new FilterIterator(fDefaultRange != null);
+ }
+
+ /**
+ * Returns an iterator which enumerates all style ranges of this presentation
+ * except the default style range. The returned <code>StyleRange</code>s
+ * are relative to the start of the presentation's result window.
+ *
+ * @return a style range iterator
+ */
+ public Iterator getAllStyleRangeIterator() {
+ return new FilterIterator(false);
+ }
+
+ /**
+ * Returns whether this collection contains any style range including
+ * the default style range.
+ *
+ * @return <code>true</code> if there is no style range in this presentation
+ */
+ public boolean isEmpty() {
+ return (fDefaultRange == null && getDenumerableRanges() == 0);
+ }
+
+ /**
+ * Returns the number of style ranges in the presentation not counting the default
+ * style range.
+ *
+ * @return the number of style ranges in the presentation excluding the default style range
+ */
+ public int getDenumerableRanges() {
+ int size= getFirstIndexAfterWindow(fResultWindow) - getFirstIndexInWindow(fResultWindow);
+ return (size < 0 ? 0 : size);
+ }
+
+ /**
+ * Returns the style range with the smallest offset ignoring the default style range or null
+ * if the presentation is empty.
+ *
+ * @return the style range with the smalled offset different from the default style range
+ */
+ public StyleRange getFirstStyleRange() {
+ try {
+
+ StyleRange range= (StyleRange) fRanges.get(getFirstIndexInWindow(fResultWindow));
+ return createWindowRelativeRange(fResultWindow, range);
+
+ } catch (NoSuchElementException x) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the style range with the highest offset ignoring the default style range.
+ *
+ * @return the style range with the highest offset different from the default style range
+ */
+ public StyleRange getLastStyleRange() {
+ try {
+
+ StyleRange range= (StyleRange) fRanges.get(getFirstIndexAfterWindow(fResultWindow) - 1);
+ return createWindowRelativeRange(fResultWindow, range);
+
+ } catch (NoSuchElementException x) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the coverage of this presentation as clipped by the presentation's
+ * result window.
+ *
+ * @return the coverage of this presentation
+ */
+ public IRegion getCoverage() {
+
+ if (fDefaultRange != null) {
+ StyleRange range= getDefaultStyleRange();
+ return new Region(range.start, range.length);
+ }
+
+ StyleRange first= getFirstStyleRange();
+ StyleRange last= getLastStyleRange();
+
+ if (first == null || last == null)
+ return null;
+
+ return new Region(first.start, last.start - first. start + last.length);
+ }
+
+ /**
+ * Clears this presentation by resetting all applied changes.
+ * @since 2.0
+ */
+ public void clear() {
+ fDefaultRange= null;
+ fResultWindow= null;
+ fRanges.clear();
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextSelection.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextSelection.java
new file mode 100644
index 000000000..a4b60a41e
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextSelection.java
@@ -0,0 +1,175 @@
+package org.eclipse.jface.text;
+
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Standard implementation of <code>ITextSelection</code>.
+ * Makes atvantage of the weak contract of correctness of its
+ * interface. If generated from a selection provider, it only
+ * remembers its offset and length and computes the remaining
+ * information on request.
+ */
+public class TextSelection implements ITextSelection {
+
+ private final static ITextSelection NULL= new TextSelection();
+
+ /**
+ * Returns a shared instance of an empty text selection.
+ */
+ public static ITextSelection emptySelection() {
+ return NULL;
+ }
+
+ /** Document which delivers the data of the selection */
+ private IDocument fDocument;
+ /** Offset of the selection */
+ private int fOffset;
+ /** Length of the selection */
+ private int fLength;
+
+
+ /**
+ * Creates an empty text selection.
+ */
+ private TextSelection() {
+ this(null, -1, -1);
+ }
+
+ /**
+ * Creates a text selection for the given range. This
+ * selection object describes generically a text range and
+ * is intended to be an argument for the <code>setSelection</code>
+ * method of selection providers.
+ *
+ * @param offset the offset of the range
+ * @param length the length of the range
+ */
+ public TextSelection(int offset, int length) {
+ this(null, offset, length);
+ }
+
+ /**
+ * Creates a text selection for the given range of the given document.
+ * This selection object is created by selection providers in responds
+ * <code>getSelection</code>.
+ *
+ * @param document the document whose text range is selected in a viewer
+ * @param offset the offset of the selected range
+ * @param length the length of the selected range
+ */
+ public TextSelection(IDocument document, int offset, int length) {
+ fDocument= document;
+ fOffset= offset;
+ fLength= length;
+ }
+
+ /**
+ * Returns true if the offset and length are smaller than 0.
+ * A selection of length 0, is a valid text selection as it
+ * describes, e.g., the cursor position in a viewer.
+ */
+ /*
+ * @see ISelection#isEmpty
+ */
+ public boolean isEmpty() {
+ return fOffset < 0 || fLength < 0;
+ }
+
+ /*
+ * @see ITextSelection#getOffset
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /*
+ * @see ITextSelection#getLength
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /*
+ * @see ITextSelection#getStartLine
+ */
+ public int getStartLine() {
+
+ try {
+ if (fDocument != null)
+ return fDocument.getLineOfOffset(fOffset);
+ } catch (BadLocationException x) {
+ }
+
+ return -1;
+ }
+
+ /*
+ * @see ITextSelection#getEndLine
+ */
+ public int getEndLine() {
+ try {
+ if (fDocument != null)
+ return fDocument.getLineOfOffset(fOffset + fLength - 1);
+ } catch (BadLocationException x) {
+ }
+
+ return -1;
+ }
+
+ /*
+ * @see ITextSelection#getText
+ */
+ public String getText() {
+ try {
+ if (fDocument != null)
+ return fDocument.get(fOffset, fLength);
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+ /*
+ * @see java.lang.Object#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+
+ TextSelection s= (TextSelection) obj;
+ boolean sameRange= (s.fOffset == fOffset && s.fLength == fLength);
+ if (sameRange) {
+
+ if (s.fDocument == null && fDocument == null)
+ return true;
+ if (s.fDocument == null || fDocument == null)
+ return false;
+
+ try {
+ String sContent= s.fDocument.get(fOffset, fLength);
+ String content= fDocument.get(fOffset, fLength);
+ return sContent.equals(content);
+ } catch (BadLocationException x) {
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ int low= fDocument != null ? fDocument.hashCode() : 0;
+ return (fOffset << 24) | (fLength << 16) | low;
+ }
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java
new file mode 100644
index 000000000..6563014ad
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java
@@ -0,0 +1,3432 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.LineBackgroundEvent;
+import org.eclipse.swt.custom.LineBackgroundListener;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.ST;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+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.events.MouseMoveListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.printing.PrintDialog;
+import org.eclipse.swt.printing.Printer;
+import org.eclipse.swt.printing.PrinterData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.ScrollBar;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.Viewer;
+
+
+
+/**
+ * SWT based implementation of <code>ITextViewer</code>. Once the viewer and its SWT control
+ * have been created the viewer can only indirectly be disposed by disposing its SWT control.<p>
+ * Clients are supposed to instantiate a text viewer and subsequently to communicate with it
+ * exclusively using the <code>ITextViewer</code> interface or any of the implemented extension
+ * interfaces. <p>
+ * A text viewer serves as text operation target. It only partially supports the external control of
+ * the enable state of its text operations. A text viewer is also a widget token owner. Anything that
+ * wants to display an overlay window on top of a text viewer should implement the
+ * <code>IWidgetTokenKeeper</code> interface and participate in the widget token negotiation between
+ * the text viewer and all its potential widget token keepers.<p>
+ * Clients should no subclass this class as it is rather likely that subclasses will be broken by
+ * future releases.
+ *
+ * @see ITextViewer
+ */
+public class TextViewer extends Viewer implements
+ ITextViewer, ITextViewerExtension,
+ ITextOperationTarget, ITextOperationTargetExtension,
+ IWidgetTokenOwner {
+
+ /** Internal flag to indicate the debug state. */
+ public static boolean TRACE_ERRORS= false;
+
+ /**
+ * Represents a replace command that brings the text viewer's text widget
+ * back in sync with text viewer's document after the document has been changed.
+ */
+ protected class WidgetCommand {
+
+ public DocumentEvent event;
+ public int start, length;
+ public String text, preservedText;
+
+ /**
+ * Translates a document event into the presentation coordinates of this text viewer.
+ *
+ * @param e the event to be translated
+ */
+ public void setEvent(DocumentEvent e) {
+
+ event= e;
+
+ start= e.getOffset();
+ length= e.getLength();
+ text= e.getText();
+
+ if (length != 0) {
+ try {
+ preservedText= e.getDocument().get(e.getOffset(), e.getLength());
+ } catch (BadLocationException x) {
+ preservedText= null;
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.WidgetCommand.setEvent")); //$NON-NLS-1$
+ }
+ } else
+ preservedText= null;
+ }
+ };
+
+ /**
+ * Connects a text double click strategy to this viewer's text widget.
+ * Calls the double click strategy when the mouse has been double clicked
+ * inside the text editor.
+ */
+ class TextDoubleClickStrategyConnector extends MouseAdapter {
+
+ /** Internal flag to remember that a double clicked occurred. */
+ private boolean fDoubleClicked= false;
+
+ public TextDoubleClickStrategyConnector() {
+ }
+
+ /*
+ * @see MouseListener#mouseDoubleClick(MouseEvent)
+ */
+ public void mouseDoubleClick(MouseEvent e) {
+ fDoubleClicked= true;
+ }
+
+ /*
+ * @see MouseListener#mouseUp(MouseEvent)
+ */
+ public void mouseUp(MouseEvent e) {
+ if (fDoubleClicked) {
+ fDoubleClicked= false;
+ ITextDoubleClickStrategy s= (ITextDoubleClickStrategy) selectContentTypePlugin(getSelectedRange().x, fDoubleClickStrategies);
+ if (s != null)
+ s.doubleClicked(TextViewer.this);
+ }
+ }
+ };
+
+ /**
+ * Monitors the area of the viewer's document that is visible in the viewer.
+ * If the area might have changed, it informs the text viewer about this
+ * potential change and its origin. The origin is internally used for optimization
+ * purposes.
+ */
+ class ViewportGuard extends MouseAdapter
+ implements ControlListener, KeyListener, MouseMoveListener, SelectionListener {
+
+ /*
+ * @see ControlListener#controlResized(ControlEvent)
+ */
+ public void controlResized(ControlEvent e) {
+ updateViewportListeners(RESIZE);
+ }
+
+ /*
+ * @see ControlListener#controlMoved(ControlEvent)
+ */
+ public void controlMoved(ControlEvent e) {
+ }
+
+ /*
+ * @see KeyListener#keyReleased
+ */
+ public void keyReleased(KeyEvent e) {
+ updateViewportListeners(KEY);
+ }
+
+ /*
+ * @see KeyListener#keyPressed
+ */
+ public void keyPressed(KeyEvent e) {
+ updateViewportListeners(KEY);
+ }
+
+ /*
+ * @see MouseListener#mouseUp
+ */
+ public void mouseUp(MouseEvent e) {
+ if (fTextWidget != null)
+ fTextWidget.removeMouseMoveListener(this);
+ updateViewportListeners(MOUSE_END);
+ }
+
+ /*
+ * @see MouseListener#mouseDown
+ */
+ public void mouseDown(MouseEvent e) {
+ if (fTextWidget != null)
+ fTextWidget.addMouseMoveListener(this);
+ }
+
+ /*
+ * @see MouseMoveListener#mouseMove
+ */
+ public void mouseMove(MouseEvent e) {
+ updateViewportListeners(MOUSE);
+ }
+
+ /*
+ * @see SelectionListener#widgetSelected
+ */
+ public void widgetSelected(SelectionEvent e) {
+ updateViewportListeners(SCROLLER);
+ }
+
+ /*
+ * @see SelectionListener#widgetDefaultSelected
+ */
+ public void widgetDefaultSelected(SelectionEvent e) {}
+ };
+
+ /**
+ * This position updater is used to keep the selection during text shift operations.
+ */
+ static class ShiftPositionUpdater extends DefaultPositionUpdater {
+
+ /**
+ * Creates the position updater for the given category.
+ *
+ * @param category the category this updater takes care of
+ */
+ protected ShiftPositionUpdater(String category) {
+ super(category);
+ }
+
+ /**
+ * If an insertion happens at the selection's start offset,
+ * the position is extended rather than shifted.
+ */
+ protected void adaptToInsert() {
+
+ int myStart= fPosition.offset;
+ int myEnd= fPosition.offset + fPosition.length -1;
+ myEnd= Math.max(myStart, myEnd);
+
+ int yoursStart= fOffset;
+ int yoursEnd= fOffset + fReplaceLength -1;
+ yoursEnd= Math.max(yoursStart, yoursEnd);
+
+ if (myEnd < yoursStart)
+ return;
+
+ if (myStart <= yoursStart) {
+ fPosition.length += fReplaceLength;
+ return;
+ }
+
+ if (myStart > yoursStart)
+ fPosition.offset += fReplaceLength;
+ }
+ };
+
+ /**
+ * Internal document listener.
+ */
+ class DocumentListener implements IDocumentListener {
+
+ /*
+ * @see IDocumentListener#documentAboutToBeChanged
+ */
+ public void documentAboutToBeChanged(DocumentEvent e) {
+ if (e.getDocument() == getVisibleDocument())
+ fWidgetCommand.setEvent(e);
+ }
+
+ /*
+ * @see IDocumentListener#documentChanged
+ */
+ public void documentChanged(final DocumentEvent e) {
+ if (fWidgetCommand.event == e)
+ updateTextListeners(fWidgetCommand);
+ }
+ };
+
+
+ /**
+ * Internal verify listener.
+ */
+ class TextVerifyListener implements VerifyListener {
+
+ /**
+ * Indicates whether verify events are forwarded or ignored.
+ * @since 2.0
+ */
+ private boolean fForward= true;
+
+ /**
+ * Tells the listener to forward received events.
+ *
+ * @param forward <code>true</code> if forwarding should be enabled.
+ * @since 2.0
+ */
+ public void forward(boolean forward) {
+ fForward= forward;
+ }
+
+ /*
+ * @see VerifyListener#verifyText(VerifyEvent)
+ */
+ public void verifyText(VerifyEvent e) {
+ if (fForward)
+ handleVerifyEvent(e);
+ }
+ };
+
+ /**
+ * The viewer's manager reponsible for registered verify key listeners.
+ * Uses batches rather than robust iterators because of performance issues.
+ *
+ * @since 2.0
+ */
+ class VerifyKeyListenersManager implements VerifyKeyListener {
+
+ /**
+ * Represents a batched addListener/removeListener command.
+ */
+ class Batch {
+ /** The index at which to insert the listener. */
+ int index;
+ /** The listener to be inserted. */
+ VerifyKeyListener listener;
+
+ /**
+ * Creates a new batch containing the given listener for the given index.
+ *
+ * @param l the listener to be added
+ * @param i the index at which to insert the listener
+ */
+ public Batch(VerifyKeyListener l, int i) {
+ listener= l;
+ index= i;
+ }
+ };
+
+ /** List of registed verify key listeners. */
+ private List fListeners= new ArrayList();
+ /** List of pending batches. */
+ private List fBatched= new ArrayList();
+ /** The currently active iterator. */
+ private Iterator fIterator;
+
+ /*
+ * @see VerifyKeyListener#verifyKey(VerifyEvent)
+ */
+ public void verifyKey(VerifyEvent event) {
+ if (fListeners.isEmpty())
+ return;
+
+ fIterator= fListeners.iterator();
+ while (fIterator.hasNext() && event.doit) {
+ VerifyKeyListener listener= (VerifyKeyListener) fIterator.next();
+ listener.verifyKey(event);
+ }
+ fIterator= null;
+
+ processBatchedRequests();
+ }
+
+ /**
+ * Processes the pending batched requests.
+ */
+ private void processBatchedRequests() {
+ if (!fBatched.isEmpty()) {
+ Iterator e= fBatched.iterator();
+ while (e.hasNext()) {
+ Batch batch= (Batch) e.next();
+ insertListener(batch.listener, batch.index);
+ }
+ fBatched.clear();
+ }
+ }
+
+ /**
+ * Returns the number of registered verify key listeners.
+ *
+ * @return the number of registered verify key listeners
+ */
+ public int numberOfListeners() {
+ return fListeners.size();
+ }
+
+ /**
+ * Inserts the given listener at the given index or moves it
+ * to that index.
+ *
+ * @param listener the listener to be inserted
+ * @param index the index of the listener or -1 for remove
+ */
+ public void insertListener(VerifyKeyListener listener, int index) {
+
+ if (index == -1) {
+ removeListener(listener);
+ } else if (listener != null) {
+
+ if (fIterator != null) {
+
+ fBatched.add(new Batch(listener, index));
+
+ } else {
+
+ int idx= -1;
+
+ // find index based on identity
+ int size= fListeners.size();
+ for (int i= 0; i < size; i++) {
+ if (listener == fListeners.get(i)) {
+ idx= i;
+ break;
+ }
+ }
+
+ // move or add it
+ if (idx != index) {
+
+ if (idx != -1)
+ fListeners.remove(idx);
+
+ if (index > fListeners.size())
+ fListeners.add(listener);
+ else
+ fListeners.add(index, listener);
+ }
+
+ if (size == 0) // checking old size, i.e. current size == size + 1
+ install();
+ }
+ }
+ }
+
+ /**
+ * Removes the given listener.
+ *
+ * @param listener the listener to be removed
+ */
+ public void removeListener(VerifyKeyListener listener) {
+ if (listener == null)
+ return;
+
+ if (fIterator != null) {
+
+ fBatched.add(new Batch(listener, -1));
+
+ } else {
+
+ int size= fListeners.size();
+ for (int i= 0; i < size; i++) {
+ if (listener == fListeners.get(i)) {
+ fListeners.remove(i);
+ if (size == 1) // checking old size, i.e. current size == size - 1
+ uninstall();
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Installs this manager.
+ */
+ private void install() {
+ StyledText textWidget= getTextWidget();
+ if (textWidget != null && !textWidget.isDisposed())
+ textWidget.addVerifyKeyListener(this);
+ }
+
+ /**
+ * Uninstalls this manager.
+ */
+ private void uninstall() {
+ StyledText textWidget= getTextWidget();
+ if (textWidget != null && !textWidget.isDisposed())
+ textWidget.removeVerifyKeyListener(this);
+ }
+ };
+
+
+ /**
+ * Reification of a range in which a find replace operation is performed. This range is visually
+ * highlighted in the viewer as long as the replace operation is in progress.
+ *
+ * @since 2.0
+ */
+ class FindReplaceRange implements LineBackgroundListener, ITextListener, IPositionUpdater {
+
+ /** Internal name for the position category used to update the range. */
+ private final static String RANGE_CATEGORY= "org.eclipse.jface.text.TextViewer.find.range"; //$NON-NLS-1$
+
+ /** The highlight color of this range. */
+ private Color fHighlightColor;
+ /** The region describing this range's extend. */
+ private IRegion fRange;
+ /** The position used to lively update this range's extent. */
+ private Position fPosition;
+
+ /** Creates a new find/replace range with the given extent.
+ *
+ * @param range the extent of this range
+ */
+ public FindReplaceRange(IRegion range) {
+ setRange(range);
+ }
+
+ /**
+ * Sets the extent of this range.
+ *
+ * @param range the extent of this range
+ */
+ public void setRange(IRegion range) {
+ fPosition= new Position(range.getOffset(), range.getLength());
+ }
+
+ /**
+ * Returns the extent of this range.
+ *
+ * @return the extent of this range
+ */
+ public IRegion getRange() {
+ return new Region(fPosition.getOffset(), fPosition.getLength());
+ }
+
+ /**
+ * Sets the highlight color of this range. Causes the range to be redrawn.
+ *
+ * @param color the highlight color
+ */
+ public void setHighlightColor(Color color) {
+ fHighlightColor= color;
+ paint();
+ }
+
+ /*
+ * @see LineBackgroundListener#lineGetBackground(LineBackgroundEvent)
+ * @since 2.0
+ */
+ public void lineGetBackground(LineBackgroundEvent event) {
+ /* Don't use cached line information because of patched redrawing events. */
+
+ if (fTextWidget != null) {
+ int offset= event.lineOffset + TextViewer.this.getVisibleRegionOffset();
+
+ if (fPosition.includes(offset))
+ event.lineBackground= fHighlightColor;
+ }
+ }
+
+ /**
+ * Installs this range. The range registers itself as background
+ * line painter and text listener. Also, it creates a category with the
+ * viewer's document to maintain its own extent.
+ */
+ public void install() {
+ TextViewer.this.addTextListener(this);
+ fTextWidget.addLineBackgroundListener(this);
+
+ IDocument document= TextViewer.this.getDocument();
+ try {
+ document.addPositionCategory(RANGE_CATEGORY);
+ document.addPosition(RANGE_CATEGORY, fPosition);
+ document.addPositionUpdater(this);
+ } catch (BadPositionCategoryException e) {
+ // should not happen
+ } catch (BadLocationException e) {
+ // should not happen
+ }
+
+ paint();
+ }
+
+ /**
+ * Uninstalls this range.
+ * @see #install()
+ */
+ public void uninstall() {
+
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19612
+
+ IDocument document= TextViewer.this.getDocument();
+ if (document != null) {
+ document.removePositionUpdater(this);
+ document.removePosition(fPosition);
+ }
+
+ if (fTextWidget != null && !fTextWidget.isDisposed())
+ fTextWidget.removeLineBackgroundListener(this);
+
+ TextViewer.this.removeTextListener(this);
+
+ clear();
+ }
+
+ /**
+ * Clears the highlighting of this range.
+ */
+ private void clear() {
+ if (fTextWidget != null && !fTextWidget.isDisposed())
+ fTextWidget.redraw();
+ }
+
+ /**
+ * Paints the highlighting of this range.
+ */
+ private void paint() {
+ int offset= fPosition.getOffset() - TextViewer.this.getVisibleRegionOffset();
+ int length= fPosition.getLength();
+
+ int count= fTextWidget.getCharCount();
+ if (offset + length >= count) {
+ length= count - offset; // clip
+
+ Point upperLeft= fTextWidget.getLocationAtOffset(offset);
+ Point lowerRight= fTextWidget.getLocationAtOffset(offset + length);
+ int width= fTextWidget.getClientArea().width;
+ int height= fTextWidget.getLineHeight() + lowerRight.y - upperLeft.y;
+ fTextWidget.redraw(upperLeft.x, upperLeft.y, width, height, false);
+ }
+
+ fTextWidget.redrawRange(offset, length, true);
+ }
+
+ /*
+ * @see ITextListener#textChanged(TextEvent)
+ * @since 2.0
+ */
+ public void textChanged(TextEvent event) {
+ if (event.getViewerRedrawState())
+ paint();
+ }
+
+ /*
+ * @see IPositionUpdater#update(DocumentEvent)
+ * @since 2.0
+ */
+ public void update(DocumentEvent event) {
+ int offset= event.getOffset();
+ int length= event.getLength();
+ int delta= event.getText().length() - length;
+
+ if (offset < fPosition.getOffset())
+ fPosition.setOffset(fPosition.getOffset() + delta);
+ else if (offset < fPosition.getOffset() + fPosition.getLength())
+ fPosition.setLength(fPosition.getLength() + delta);
+ }
+ };
+
+ /**
+ * This viewer's find/replace target.
+ */
+ class FindReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtension {
+
+ /** The range for this target. */
+ private FindReplaceRange fRange;
+ /** The highlight color of the range of this target. */
+ private Color fScopeHighlightColor;
+ /** The document partitioner remembered in case of a "Replace All". */
+ private IDocumentPartitioner fRememberedPartitioner;
+
+ /*
+ * @see IFindReplaceTarget#getSelectionText()
+ */
+ public String getSelectionText() {
+ Point s= TextViewer.this.getSelectedRange();
+ if (s.x > -1 && s.y > -1) {
+ try {
+ IDocument document= TextViewer.this.getDocument();
+ return document.get(s.x, s.y);
+ } catch (BadLocationException x) {
+ }
+ }
+ return null;
+ }
+
+ /*
+ * @see IFindReplaceTarget#replaceSelection(String)
+ */
+ public void replaceSelection(String text) {
+ Point s= TextViewer.this.getSelectedRange();
+ if (s.x > -1 && s.y > -1) {
+ try {
+ IDocument document= TextViewer.this.getDocument();
+ document.replace(s.x, s.y, text);
+ if (text != null && text.length() > 0)
+ TextViewer.this.setSelectedRange(s.x, text.length());
+ } catch (BadLocationException x) {
+ }
+ }
+ }
+
+ /*
+ * @see IFindReplaceTarget#isEditable()
+ */
+ public boolean isEditable() {
+ return TextViewer.this.isEditable();
+ }
+
+ /*
+ * @see IFindReplaceTarget#getSelection()
+ */
+ public Point getSelection() {
+ Point point= TextViewer.this.getSelectedRange();
+ point.x -= TextViewer.this.getVisibleRegionOffset();
+ return point;
+ }
+
+ /*
+ * @see IFindReplaceTarget#findAndSelect(int, String, boolean, boolean, boolean)
+ */
+ public int findAndSelect(int offset, String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord) {
+ if (offset != -1)
+ offset += TextViewer.this.getVisibleRegionOffset();
+
+ if (fRange != null) {
+ IRegion range= fRange.getRange();
+ offset= TextViewer.this.findAndSelectInRange(offset, findString, searchForward, caseSensitive, wholeWord, range.getOffset(), range.getLength());
+ } else {
+ offset= TextViewer.this.findAndSelect(offset, findString, searchForward, caseSensitive, wholeWord);
+ }
+
+ if (offset != -1)
+ offset -= TextViewer.this.getVisibleRegionOffset();
+
+ return offset;
+ }
+
+ /*
+ * @see IFindReplaceTarget#canPerformFind()
+ */
+ public boolean canPerformFind() {
+ return TextViewer.this.canPerformFind();
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#beginSession()
+ * @since 2.0
+ */
+ public void beginSession() {
+ fRange= null;
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#endSession()
+ * @since 2.0
+ */
+ public void endSession() {
+ if (fRange != null) {
+ fRange.uninstall();
+ fRange= null;
+ }
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#getScope()
+ * @since 2.0
+ */
+ public IRegion getScope() {
+ return fRange == null ? null : fRange.getRange();
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#getLineSelection()
+ * @since 2.0
+ */
+ public Point getLineSelection() {
+ Point point= TextViewer.this.getSelectedRange();
+
+ try {
+ IDocument document= TextViewer.this.getDocument();
+
+ // beginning of line
+ int line= document.getLineOfOffset(point.x);
+ int offset= document.getLineOffset(line);
+
+ // end of line
+ line= document.getLineOfOffset(point.x + point.y);
+ int length= document.getLineOffset(line) + document.getLineLength(line) - offset;
+
+ return new Point(offset, length);
+
+ } catch (BadLocationException e) {
+ // should not happen
+ return null;
+ }
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#setSelection(int, int)
+ * @since 2.0
+ */
+ public void setSelection(int offset, int length) {
+ TextViewer.this.setSelectedRange(offset /*+ TextViewer.this.getVisibleRegionOffset()*/, length);
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#setScope(IRegion)
+ * @since 2.0
+ */
+ public void setScope(IRegion scope) {
+ if (fRange != null)
+ fRange.uninstall();
+
+ if (scope == null) {
+ fRange= null;
+ return;
+ }
+
+ fRange= new FindReplaceRange(scope);
+ fRange.setHighlightColor(fScopeHighlightColor);
+ fRange.install();
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#setScopeHighlightColor(Color)
+ * @since 2.0
+ */
+ public void setScopeHighlightColor(Color color) {
+ if (fRange != null)
+ fRange.setHighlightColor(color);
+ fScopeHighlightColor= color;
+ }
+
+ /*
+ * @see IFindReplaceTargetExtension#setReplaceAllMode(boolean)
+ * @since 2.0
+ */
+ public void setReplaceAllMode(boolean replaceAll) {
+
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18232
+
+ if (replaceAll) {
+
+ TextViewer.this.setRedraw(false);
+ TextViewer.this.startSequentialRewriteMode(false);
+
+ if (fUndoManager != null)
+ fUndoManager.beginCompoundChange();
+
+ IDocument document= TextViewer.this.getDocument();
+ fRememberedPartitioner= document.getDocumentPartitioner();
+ if (fRememberedPartitioner != null) {
+ fRememberedPartitioner.disconnect();
+ document.setDocumentPartitioner(null);
+ }
+
+ } else {
+
+ TextViewer.this.setRedraw(true);
+ TextViewer.this.stopSequentialRewriteMode();
+
+ if (fUndoManager != null)
+ fUndoManager.endCompoundChange();
+
+ if (fRememberedPartitioner != null) {
+ IDocument document= TextViewer.this.getDocument();
+ fRememberedPartitioner.connect(document);
+ document.setDocumentPartitioner(fRememberedPartitioner);
+ }
+ }
+ }
+ };
+
+
+ /**
+ * The viewer's rewrite target.
+ * @since 2.0
+ */
+ class RewriteTarget implements IRewriteTarget {
+
+ /*
+ * @see org.eclipse.jface.text.IRewriteTarget#beginCompoundChange()
+ */
+ public void beginCompoundChange() {
+ if (fUndoManager != null)
+ fUndoManager.beginCompoundChange();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IRewriteTarget#endCompoundChange()
+ */
+ public void endCompoundChange() {
+ if (fUndoManager != null)
+ fUndoManager.endCompoundChange();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IRewriteTarget#getDocument()
+ */
+ public IDocument getDocument() {
+ return TextViewer.this.getDocument();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IRewriteTarget#setRedraw(boolean)
+ */
+ public void setRedraw(boolean redraw) {
+ TextViewer.this.setRedraw(redraw);
+ }
+ };
+
+
+ /** ID for originators of view port changes */
+ protected static final int SCROLLER= 1;
+ protected static final int MOUSE= 2;
+ protected static final int MOUSE_END= 3;
+ protected static final int KEY= 4;
+ protected static final int RESIZE= 5;
+ protected static final int INTERNAL= 6;
+
+ /** Internal name of the position category used selection preservation during shift */
+ protected static final String SHIFTING= "__TextViewer_shifting"; //$NON-NLS-1$
+
+ /** The viewer's text widget */
+ private StyledText fTextWidget;
+ /** The viewer's input document */
+ private IDocument fDocument;
+ /** The viewer's visible document */
+ private IDocument fVisibleDocument;
+ /** The viewer's document adapter */
+ private IDocumentAdapter fDocumentAdapter;
+ /** The child document manager */
+ private ChildDocumentManager fChildDocumentManager;
+ /** The text viewer's double click strategies connector */
+ private TextDoubleClickStrategyConnector fDoubleClickStrategyConnector;
+ /**
+ * The text viewer's hovering controller
+ * @since 2.0
+ */
+ private AbstractHoverInformationControlManager fTextHoverManager;
+ /** The text viewer's viewport guard */
+ private ViewportGuard fViewportGuard;
+ /** Caches the graphical coordinate of the first visible line */
+ private int fTopInset= 0;
+ /** The most recent document modification as widget command */
+ private WidgetCommand fWidgetCommand= new WidgetCommand();
+ /** The SWT control's scrollbars */
+ private ScrollBar fScroller;
+ /** Document listener */
+ private DocumentListener fDocumentListener= new DocumentListener();
+ /** Verify listener */
+ private TextVerifyListener fVerifyListener= new TextVerifyListener();
+ /** The most recent widget modification as document command */
+ private DocumentCommand fDocumentCommand= new DocumentCommand();
+ /** The viewer's find/replace target */
+ private IFindReplaceTarget fFindReplaceTarget;
+ /**
+ * The viewer widget token keeper
+ * @since 2.0
+ */
+ private IWidgetTokenKeeper fWidgetTokenKeeper;
+ /**
+ * The viewer's manager of verify key listeners
+ * @since 2.0
+ */
+ private VerifyKeyListenersManager fVerifyKeyListenersManager= new VerifyKeyListenersManager();
+ /**
+ * The mark position.
+ * @since 2.0
+ */
+ private Position fMarkPosition;
+ /**
+ * The mark position category.
+ * @since 2.0
+ */
+ private final String MARK_POSITION_CATEGORY="__mark_category_" + hashCode();
+ /**
+ * The mark position updater
+ * @since 2.0
+ */
+ private final IPositionUpdater fMarkPositionUpdater= new DefaultPositionUpdater(MARK_POSITION_CATEGORY);
+ /**
+ * The flag indicating the redraw behavior
+ * @since 2.0
+ */
+ private int fRedrawCounter= 0;
+ /**
+ * The selection when working in non-redraw state
+ * @since 2.0
+ */
+ private Point fDocumentSelection;
+ /**
+ * The viewer's rewrite target
+ * @since 2.0
+ */
+ private IRewriteTarget fRewriteTarget;
+
+
+ /** Should the auto indent strategies ignore the next edit operation */
+ protected boolean fIgnoreAutoIndent= false;
+ /** The strings a line is prefixed with on SHIFT_RIGHT and removed from each line on SHIFT_LEFT */
+ protected Map fIndentChars;
+ /** The string a line is prefixed with on PREFIX and removed from each line on STRIP_PREFIX */
+ protected Map fDefaultPrefixChars;
+ /** The text viewer's text double click strategies */
+ protected Map fDoubleClickStrategies;
+ /** The text viewer's undo manager */
+ protected IUndoManager fUndoManager;
+ /** The text viewer's auto indent strategies */
+ protected Map fAutoIndentStrategies;
+ /** The text viewer's text hovers */
+ protected Map fTextHovers;
+ /**
+ * The creator of the text hover control
+ * @since 2.0
+ */
+ protected IInformationControlCreator fHoverControlCreator;
+ /** All registered viewport listeners> */
+ protected List fViewportListeners;
+ /** The last visible vertical position of the top line */
+ protected int fLastTopPixel;
+ /** All registered text listeners */
+ protected List fTextListeners;
+ /** All registered text input listeners */
+ protected List fTextInputListeners;
+ /** The text viewer's event consumer */
+ protected IEventConsumer fEventConsumer;
+ /** Indicates whether the viewer's text presentation should be replaced are modified. */
+ protected boolean fReplaceTextPresentation= false;
+
+
+ //---- Construction and disposal ------------------
+
+
+ /**
+ * Internal use only
+ */
+ protected TextViewer() {
+ }
+
+ /**
+ * Create a new text viewer with the given SWT style bits.
+ * The viewer is ready to use but does not have any plug-in installed.
+ *
+ * @param parent the parent of the viewer's control
+ * @param styles the SWT style bits for the viewer's control
+ */
+ public TextViewer(Composite parent, int styles) {
+ createControl(parent, styles);
+ }
+
+ /**
+ * Factory method to create the text widget to be used as the viewer's text widget.
+ *
+ * @return the text widget to be used
+ */
+ protected StyledText createTextWidget(Composite parent, int styles) {
+ return new StyledText(parent, styles);
+ }
+
+ /**
+ * Factory method to create the document adapter to be used by this viewer.
+ *
+ * @return the document adapter to be used
+ */
+ protected IDocumentAdapter createDocumentAdapter() {
+ return new DocumentAdapter();
+ }
+
+ /**
+ * Creates the viewer's SWT control. The viewer's text widget either is
+ * the control or is a child of the control.
+ *
+ * @param parent the parent of the viewer's control
+ * @param styles the SWT style bits for the viewer's control
+ */
+ protected void createControl(Composite parent, int styles) {
+
+ fTextWidget= createTextWidget(parent, styles);
+ fTextWidget.addDisposeListener(
+ new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ setDocument(null);
+ handleDispose();
+ fTextWidget= null;
+ }
+ }
+ );
+
+ fTextWidget.setFont(parent.getFont());
+ fTextWidget.setDoubleClickEnabled(false);
+
+ /*
+ * Disable SWT Shift+TAB traversal in this viewer
+ * 1GIYQ9K: ITPUI:WINNT - StyledText swallows Shift+TAB
+ */
+ fTextWidget.addTraverseListener(new TraverseListener() {
+ public void keyTraversed(TraverseEvent e) {
+ if ((SWT.SHIFT == e.stateMask) && ('\t' == e.character))
+ e.doit = false;
+ }
+ });
+
+ // where does the first line start
+ fTopInset= -fTextWidget.computeTrim(0, 0, 0, 0).y;
+
+ fVerifyListener.forward(true);
+ fTextWidget.addVerifyListener(fVerifyListener);
+
+ fTextWidget.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent event) {
+ selectionChanged(event.x, event.y - event.x);
+ }
+ public void widgetSelected(SelectionEvent event) {
+ selectionChanged(event.x, event.y - event.x);
+ }
+ });
+
+ initializeViewportUpdate();
+ }
+
+ /*
+ * @see Viewer#getControl
+ */
+ public Control getControl() {
+ return fTextWidget;
+ }
+
+ /*
+ * @see ITextViewer#activatePlugin
+ */
+ public void activatePlugins() {
+
+ if (fDoubleClickStrategies != null && !fDoubleClickStrategies.isEmpty() && fDoubleClickStrategyConnector == null) {
+ fDoubleClickStrategyConnector= new TextDoubleClickStrategyConnector();
+ fTextWidget.addMouseListener(fDoubleClickStrategyConnector);
+ }
+
+ if (fTextHovers != null && !fTextHovers.isEmpty() && fHoverControlCreator != null && fTextHoverManager == null) {
+ fTextHoverManager= new TextViewerHoverManager(this, fHoverControlCreator);
+ fTextHoverManager.install(this.getTextWidget());
+ }
+
+ if (fUndoManager != null) {
+ fUndoManager.connect(this);
+ fUndoManager.reset();
+ }
+ }
+
+ /*
+ * @see ITextViewer#resetPlugins()
+ */
+ public void resetPlugins() {
+ if (fUndoManager != null)
+ fUndoManager.reset();
+ }
+
+ /**
+ * Frees all resources allocated by this viewer. Internally called when the viewer's
+ * control has been disposed.
+ */
+ protected void handleDispose() {
+
+ removeViewPortUpdate();
+ fViewportGuard= null;
+
+ if (fViewportListeners != null) {
+ fViewportListeners.clear();
+ fViewportListeners= null;
+ }
+
+ if (fTextListeners != null) {
+ fTextListeners.clear();
+ fTextListeners= null;
+ }
+
+ if (fAutoIndentStrategies != null) {
+ fAutoIndentStrategies.clear();
+ fAutoIndentStrategies= null;
+ }
+
+ if (fUndoManager != null) {
+ fUndoManager.disconnect();
+ fUndoManager= null;
+ }
+
+ if (fDoubleClickStrategies != null) {
+ fDoubleClickStrategies.clear();
+ fDoubleClickStrategies= null;
+ }
+
+ if (fTextHovers != null) {
+ fTextHovers.clear();
+ fTextHovers= null;
+ }
+
+ fDoubleClickStrategyConnector= null;
+
+ if (fTextHoverManager != null) {
+ fTextHoverManager.dispose();
+ fTextHoverManager= null;
+ }
+
+ if (fDocumentListener != null)
+ fDocumentListener= null;
+
+ if (fVisibleDocument instanceof ChildDocument) {
+ ChildDocument child = (ChildDocument) fVisibleDocument;
+ child.removeDocumentListener(fDocumentListener);
+ getChildDocumentManager().freeChildDocument(child);
+ }
+
+ if (fDocumentAdapter != null) {
+ fDocumentAdapter.setDocument(null);
+ fDocumentAdapter= null;
+ }
+
+ fVisibleDocument= null;
+ fDocument= null;
+ fChildDocumentManager= null;
+ fScroller= null;
+ }
+
+
+ //---- simple getters and setters
+
+ /**
+ * Returns viewer's text widget.
+ */
+ public StyledText getTextWidget() {
+ return fTextWidget;
+ }
+
+ /*
+ * @see ITextViewer#setAutoIndentStrategy
+ */
+ public void setAutoIndentStrategy(IAutoIndentStrategy strategy, String contentType) {
+
+ if (strategy != null) {
+ if (fAutoIndentStrategies == null)
+ fAutoIndentStrategies= new HashMap();
+ fAutoIndentStrategies.put(contentType, strategy);
+ } else if (fAutoIndentStrategies != null)
+ fAutoIndentStrategies.remove(contentType);
+ }
+
+ /*
+ * @see ITextViewer#setEventConsumer
+ */
+ public void setEventConsumer(IEventConsumer consumer) {
+ fEventConsumer= consumer;
+ }
+
+ /*
+ * @see ITextViewer#setIndentPrefixes
+ */
+ public void setIndentPrefixes(String[] indentPrefixes, String contentType) {
+
+ int i= -1;
+ boolean ok= (indentPrefixes != null);
+ while (ok && ++i < indentPrefixes.length)
+ ok= (indentPrefixes[i] != null);
+
+ if (ok) {
+
+ if (fIndentChars == null)
+ fIndentChars= new HashMap();
+
+ fIndentChars.put(contentType, indentPrefixes);
+
+ } else if (fIndentChars != null)
+ fIndentChars.remove(contentType);
+ }
+
+ /*
+ * @see ITextViewer#getTopInset
+ */
+ public int getTopInset() {
+ return fTopInset;
+ }
+
+ /*
+ * @see ITextViewer#isEditable
+ */
+ public boolean isEditable() {
+ if (fTextWidget == null)
+ return false;
+ return fTextWidget.getEditable();
+ }
+
+ /*
+ * @see ITextViewer#setEditable
+ */
+ public void setEditable(boolean editable) {
+ if (fTextWidget != null)
+ fTextWidget.setEditable(editable);
+ }
+
+ /*
+ * @see ITextViewer#setDefaultPrefixes
+ * @since 2.0
+ */
+ public void setDefaultPrefixes(String[] defaultPrefixes, String contentType) {
+
+ if (defaultPrefixes != null && defaultPrefixes.length > 0) {
+ if (fDefaultPrefixChars == null)
+ fDefaultPrefixChars= new HashMap();
+ fDefaultPrefixChars.put(contentType, defaultPrefixes);
+ } else if (fDefaultPrefixChars != null)
+ fDefaultPrefixChars.remove(contentType);
+ }
+
+ /*
+ * @see ITextViewer#setUndoManager
+ */
+ public void setUndoManager(IUndoManager undoManager) {
+ fUndoManager= undoManager;
+ }
+
+ /*
+ * @see ITextViewer#setTextHover
+ */
+ public void setTextHover(ITextHover hover, String contentType) {
+
+ if (hover != null) {
+ if (fTextHovers == null)
+ fTextHovers= new HashMap();
+ fTextHovers.put(contentType, hover);
+ } else if (fTextHovers != null)
+ fTextHovers.remove(contentType);
+ }
+
+ /**
+ * Returns the text hover for a given offset.
+ *
+ * @param offset the offset for which to return the text hover
+ * @return the text hover for the given offset
+ */
+ protected ITextHover getTextHover(int offset) {
+ return (ITextHover) selectContentTypePlugin(offset, fTextHovers);
+ }
+
+ /**
+ * Returns the text hovering controller of this viewer.
+ *
+ * @return the text hovering controller of this viewer
+ * @since 2.0
+ */
+ protected AbstractInformationControlManager getTextHoveringController() {
+ return fTextHoverManager;
+ }
+
+ /**
+ * Sets the creator for the hover controls.
+ *
+ * @param creator the hover control creator
+ * @since 2.0
+ */
+ public void setHoverControlCreator(IInformationControlCreator creator) {
+ fHoverControlCreator= creator;
+ }
+
+ /*
+ * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
+ * @since 2.0
+ */
+ public boolean requestWidgetToken(IWidgetTokenKeeper requester) {
+ if (fTextWidget != null) {
+ if (fWidgetTokenKeeper != null) {
+ if (fWidgetTokenKeeper == requester)
+ return true;
+ if (fWidgetTokenKeeper.requestWidgetToken(this)) {
+ fWidgetTokenKeeper= requester;
+ return true;
+ }
+ } else {
+ fWidgetTokenKeeper= requester;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * @see IWidgetTokenOwner#releaseWidgetToken(IWidgetTokenKeeper)
+ * @since 2.0
+ */
+ public void releaseWidgetToken(IWidgetTokenKeeper tokenKeeper) {
+ if (fWidgetTokenKeeper == tokenKeeper)
+ fWidgetTokenKeeper= null;
+ }
+
+
+ //---- Selection
+
+ /*
+ * @see ITextViewer#getSelectedRange
+ */
+ public Point getSelectedRange() {
+
+ if (!redraws())
+ return new Point(fDocumentSelection.x, fDocumentSelection.y);
+
+ if (fTextWidget != null) {
+ Point p= fTextWidget.getSelectionRange();
+ int offset= getVisibleRegionOffset();
+ return new Point(p.x + offset, p.y);
+ }
+
+ return new Point(-1, -1);
+ }
+
+ /*
+ * @see ITextViewer#setSelectedRange
+ */
+ public void setSelectedRange(int offset, int length) {
+
+ if (!redraws()) {
+ fDocumentSelection.x= offset;
+ fDocumentSelection.y= length;
+ return;
+ }
+
+ if (fTextWidget == null)
+ return;
+
+ int end= offset + length;
+
+ IDocument document= getVisibleDocument();
+ if (document == null)
+ return;
+
+ if (document instanceof ChildDocument) {
+ Position p= ((ChildDocument) document).getParentDocumentRange();
+ if (p.overlapsWith(offset, length)) {
+
+ if (offset < p.getOffset())
+ offset= p.getOffset();
+ offset -= p.getOffset();
+
+ int e= p.getOffset() + p.getLength();
+ if (end > e)
+ end= e;
+ end -= p.getOffset();
+
+ } else
+ return;
+ }
+
+ length= end - offset;
+
+ int[] selectionRange= new int[] { offset, length };
+ validateSelectionRange(selectionRange);
+ if (selectionRange[0] >= 0 && selectionRange[1] >= 0) {
+ fTextWidget.setSelectionRange(selectionRange[0], selectionRange[1]);
+ selectionChanged(selectionRange[0], selectionRange[1]);
+ }
+ }
+
+ /**
+ * Validates and adapts the given selection range if it is not a valid
+ * widget selection. The widget selection is invalid if it starts or ends
+ * inside a multi-character line delimiter. If so, the selection is adapted to
+ * start <b>after</b> the divided line delimiter and to end <b>before</b>
+ * the divided line delimiter. The parameter passed in is changed in-place
+ * when being adapted. An adaptation to <code>[-1, -1]</code> indicates
+ * that the selection range could not be validated.
+ * Subclasses may reimplement this method.
+ *
+ * @param selectionRange selectionRange[0] is the offset, selectionRange[1]
+ * the length of the selection to validate.
+ * @since 2.0
+ */
+ protected void validateSelectionRange(int[] selectionRange) {
+
+ IDocument document= getVisibleDocument();
+ int documentLength= document.getLength();
+
+ int offset= selectionRange[0];
+ int length= selectionRange[1];
+
+
+ if (offset <0)
+ offset= 0;
+
+ if (offset > documentLength)
+ offset= documentLength;
+
+ int delta= (offset + length) - documentLength;
+ if (delta > 0)
+ length -= delta;
+
+ try {
+
+ int lineNumber= document.getLineOfOffset(offset);
+ IRegion lineInformation= document.getLineInformation(lineNumber);
+
+ int lineEnd= lineInformation.getOffset() + lineInformation.getLength();
+ delta= offset - lineEnd;
+ if (delta > 0) {
+ // in the middle of a multi-character line delimiter
+ offset= lineEnd;
+ String delimiter= document.getLineDelimiter(lineNumber);
+ if (delimiter != null)
+ offset += delimiter.length();
+ }
+
+ int end= offset + length;
+ lineInformation= document.getLineInformationOfOffset(end);
+ lineEnd= lineInformation.getOffset() + lineInformation.getLength();
+ delta= end - lineEnd;
+ if (delta > 0) {
+ // in the middle of a multi-character line delimiter
+ length -= delta;
+ }
+
+ } catch (BadLocationException x) {
+ selectionRange[0]= -1;
+ selectionRange[1]= -1;
+ return;
+ }
+
+ selectionRange[0]= offset;
+ selectionRange[1]= length;
+ }
+
+ /*
+ * @see Viewer#setSelection(ISelection)
+ */
+ public void setSelection(ISelection selection, boolean reveal) {
+ if (selection instanceof ITextSelection) {
+ ITextSelection s= (ITextSelection) selection;
+ setSelectedRange(s.getOffset(), s.getLength());
+ if (reveal)
+ revealRange(s.getOffset(), s.getLength());
+ }
+ }
+
+ /*
+ * @see Viewer#getSelection()
+ */
+ public ISelection getSelection() {
+ Point p= getSelectedRange();
+ if (p.x == -1 || p.y == -1)
+ return TextSelection.emptySelection();
+
+ return new TextSelection(getDocument(), p.x, p.y);
+ }
+
+ /*
+ * @see ITextViewer#getSelectionProvider
+ */
+ public ISelectionProvider getSelectionProvider() {
+ return this;
+ }
+
+ /**
+ * Sends out a text selection changed event to all registered listeners.
+ *
+ * @param offset the offset of the newly selected range in the visible document
+ * @param length the length of the newly selected range in the visible document
+ */
+ protected void selectionChanged(int offset, int length) {
+ if (redraws()) {
+ ISelection selection= new TextSelection(getDocument(), getVisibleRegionOffset() + offset, length);
+ SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
+ fireSelectionChanged(event);
+ }
+ }
+
+ /**
+ * Sends out a mark selection changed event to all registered listeners.
+ *
+ * @param offset the offset of the mark selection in the visible document, the offset is <code>-1</code> if the mark was cleared
+ * @param length the length of the mark selection, may be negative if the caret is before the mark.
+ * @since 2.0
+ */
+ protected void markChanged(int offset, int length) {
+ if (redraws()) {
+ if (offset != -1)
+ offset += getVisibleRegionOffset();
+ ISelection selection= new MarkSelection(getDocument(), offset, length);
+ SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
+ fireSelectionChanged(event);
+ }
+ }
+
+
+ //---- Text listeners
+
+ /*
+ * @see ITextViewer#addTextListener
+ */
+ public void addTextListener(ITextListener listener) {
+ if (fTextListeners == null)
+ fTextListeners= new ArrayList();
+
+ if (!fTextListeners.contains(listener))
+ fTextListeners.add(listener);
+ }
+
+ /*
+ * @see ITextViewer#removeTextListener
+ */
+ public void removeTextListener(ITextListener listener) {
+ if (fTextListeners != null) {
+ fTextListeners.remove(listener);
+ if (fTextListeners.size() == 0)
+ fTextListeners= null;
+ }
+ }
+
+ /**
+ * Informs all registered text listeners about the change specified by the
+ * widget command. This method does not use a robust iterator.
+ *
+ * @param cmd the widget command translated into a text event sent to all text listeners
+ */
+ protected void updateTextListeners(WidgetCommand cmd) {
+
+ if (fTextListeners != null) {
+
+ DocumentEvent event= cmd.event;
+ if (event instanceof ChildDocumentEvent)
+ event= ((ChildDocumentEvent) event).getParentEvent();
+
+ TextEvent e= new TextEvent(cmd.start, cmd.length, cmd.text, cmd.preservedText, event, redraws());
+ for (int i= 0; i < fTextListeners.size(); i++) {
+ ITextListener l= (ITextListener) fTextListeners.get(i);
+ l.textChanged(e);
+ }
+ }
+ }
+
+ //---- Text input listeners
+
+ /*
+ * @see ITextViewer#addTextInputListener
+ */
+ public void addTextInputListener(ITextInputListener listener) {
+ if (fTextInputListeners == null)
+ fTextInputListeners= new ArrayList();
+
+ if (!fTextInputListeners.contains(listener))
+ fTextInputListeners.add(listener);
+ }
+
+ /*
+ * @see ITextViewer#removeTextInputListener
+ */
+ public void removeTextInputListener(ITextInputListener listener) {
+ if (fTextInputListeners != null) {
+ fTextInputListeners.remove(listener);
+ if (fTextInputListeners.size() == 0)
+ fTextInputListeners= null;
+ }
+ }
+
+ /**
+ * Informs all registered text input listeners about the forthcoming input change,
+ * This method does not use a robust iterator.
+ *
+ * @param oldInput the old input document
+ * @param newInput the new input document
+ */
+ protected void fireInputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+ if (fTextInputListeners != null) {
+ for (int i= 0; i < fTextInputListeners.size(); i++) {
+ ITextInputListener l= (ITextInputListener) fTextInputListeners.get(i);
+ l.inputDocumentAboutToBeChanged(oldInput, newInput);
+ }
+ }
+ }
+
+ /**
+ * Informs all registered text input listeners about the sucessful input change,
+ * This method does not use a robust iterator.
+ *
+ * @param oldInput the old input document
+ * @param newInput the new input document
+ */
+ protected void fireInputDocumentChanged(IDocument oldInput, IDocument newInput) {
+ if (fTextInputListeners != null) {
+ for (int i= 0; i < fTextInputListeners.size(); i++) {
+ ITextInputListener l= (ITextInputListener) fTextInputListeners.get(i);
+ l.inputDocumentChanged(oldInput, newInput);
+ }
+ }
+ }
+
+ //---- Document
+
+ /*
+ * @see Viewer#getInput
+ */
+ public Object getInput() {
+ return getDocument();
+ }
+
+ /*
+ * @see ITextViewer#getDocument
+ */
+ public IDocument getDocument() {
+ return fDocument;
+ }
+
+ /*
+ * @see Viewer#setInput
+ */
+ public void setInput(Object input) {
+
+ IDocument document= null;
+ if (input instanceof IDocument)
+ document= (IDocument) input;
+
+ setDocument(document);
+ }
+
+ /*
+ * @see ITextViewer#setDocument(IDocument)
+ */
+ public void setDocument(IDocument document) {
+
+ fReplaceTextPresentation= true;
+ fireInputDocumentAboutToBeChanged(fDocument, document);
+
+ IDocument oldDocument= fDocument;
+ fDocument= document;
+
+ setVisibleDocument(fDocument);
+
+ inputChanged(fDocument, oldDocument);
+
+ fireInputDocumentChanged(oldDocument, fDocument);
+ fReplaceTextPresentation= false;
+ }
+
+ /*
+ * @see ITextViewer#setDocument(IDocument, int int)
+ */
+ public void setDocument(IDocument document, int visibleRegionOffset, int visibleRegionLength) {
+
+ fReplaceTextPresentation= true;
+ fireInputDocumentAboutToBeChanged(fDocument, document);
+
+ IDocument oldDocument= fDocument;
+ fDocument= document;
+
+ try {
+ int line= fDocument.getLineOfOffset(visibleRegionOffset);
+ int offset= fDocument.getLineOffset(line);
+ int length= (visibleRegionOffset - offset) + visibleRegionLength;
+ setVisibleDocument(getChildDocumentManager().createChildDocument(fDocument, offset, length));
+ } catch (BadLocationException x) {
+ throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_1")); //$NON-NLS-1$
+ }
+
+ inputChanged(fDocument, oldDocument);
+
+ fireInputDocumentChanged(oldDocument, fDocument);
+ fReplaceTextPresentation= false;
+ }
+
+ //---- Viewports
+
+ /**
+ * Initializes all listeners and structures required to set up viewport listeners.
+ */
+ private void initializeViewportUpdate() {
+
+ if (fViewportGuard != null)
+ return;
+
+ if (fTextWidget != null) {
+
+ fViewportGuard= new ViewportGuard();
+ fLastTopPixel= -1;
+
+ fTextWidget.addKeyListener(fViewportGuard);
+ fTextWidget.addMouseListener(fViewportGuard);
+
+ fScroller= fTextWidget.getVerticalBar();
+ if (fScroller != null)
+ fScroller.addSelectionListener(fViewportGuard);
+ }
+ }
+
+ /**
+ * Removes all listeners and structures required to set up viewport listeners.
+ */
+ private void removeViewPortUpdate() {
+
+ if (fTextWidget != null) {
+
+ fTextWidget.removeKeyListener(fViewportGuard);
+ fTextWidget.removeMouseListener(fViewportGuard);
+
+ if (fScroller != null && !fScroller.isDisposed()) {
+ fScroller.removeSelectionListener(fViewportGuard);
+ fScroller= null;
+ }
+
+ fViewportGuard= null;
+ }
+ }
+
+ /*
+ * @see ITextViewer#addViewportListener
+ */
+ public void addViewportListener(IViewportListener listener) {
+
+ if (fViewportListeners == null) {
+ fViewportListeners= new ArrayList();
+ initializeViewportUpdate();
+ }
+
+ if (!fViewportListeners.contains(listener))
+ fViewportListeners.add(listener);
+ }
+
+ /*
+ * @see ITextViewer#removeViewportListener
+ */
+ public void removeViewportListener(IViewportListener listener) {
+ if (fViewportListeners != null)
+ fViewportListeners.remove(listener);
+ }
+
+ /**
+ * Checks whether the viewport changed and if so informs all registered
+ * listeners about the change.
+ *
+ * @param origin describes under which circumstances this method has been called.
+ *
+ * @see IViewportListener
+ */
+ protected void updateViewportListeners(int origin) {
+
+ if (redraws()) {
+ int topPixel= fTextWidget.getTopPixel();
+ if (topPixel >= 0 && topPixel != fLastTopPixel) {
+ if (fViewportListeners != null) {
+ for (int i= 0; i < fViewportListeners.size(); i++) {
+ IViewportListener l= (IViewportListener) fViewportListeners.get(i);
+ l.viewportChanged(topPixel);
+ }
+ }
+ fLastTopPixel= topPixel;
+ }
+ }
+ }
+
+ //---- scrolling and revealing
+
+ /*
+ * @see ITextViewer#getTopIndex
+ */
+ public int getTopIndex() {
+
+ if (fTextWidget != null) {
+
+ int top= fTextWidget.getTopIndex();
+
+ int offset= getVisibleRegionOffset();
+ if (offset > 0) {
+ try {
+ top += getDocument().getLineOfOffset(offset);
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getTopIndex")); //$NON-NLS-1$
+ return -1;
+ }
+ }
+
+ return top;
+ }
+
+ return -1;
+ }
+
+ /*
+ * @see ITextViewer#setTopIndex
+ */
+ public void setTopIndex(int index) {
+
+ if (fTextWidget != null) {
+
+ int offset= getVisibleRegionOffset();
+ if (offset > 0) {
+ try {
+ index -= getDocument().getLineOfOffset(offset);
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.setTopIndex_1")); //$NON-NLS-1$
+ return;
+ }
+ }
+
+ if (index >= 0) {
+
+ int lines= getVisibleLinesInViewport();
+ if (lines > -1 ) {
+ IDocument d= getVisibleDocument();
+ int last= d.getNumberOfLines() - lines;
+ if (last > 0 && index > last)
+ index= last;
+
+ fTextWidget.setTopIndex(index);
+ updateViewportListeners(INTERNAL);
+
+ } else
+ fTextWidget.setTopIndex(index);
+ }
+ }
+ }
+
+ /**
+ * Returns the viewport height in lines. The actual visible lines can be fewer if the
+ * document is shorter than the viewport.
+ *
+ * @return the viewport height in lines
+ */
+ protected int getVisibleLinesInViewport() {
+ if (fTextWidget != null) {
+ Rectangle clArea= fTextWidget.getClientArea();
+ if (!clArea.isEmpty())
+ return clArea.height / fTextWidget.getLineHeight();
+ }
+ return -1;
+ }
+
+ /*
+ * @see ITextViewer#getBottomIndex
+ */
+ public int getBottomIndex() {
+
+ if (fTextWidget == null)
+ return -1;
+
+ IRegion r= getVisibleRegion();
+
+ try {
+
+ IDocument d= getDocument();
+ int startLine= d.getLineOfOffset(r.getOffset());
+ int endLine= d.getLineOfOffset(r.getOffset() + r.getLength() - 1);
+ int lines= getVisibleLinesInViewport();
+
+ if (startLine + lines < endLine)
+ return getTopIndex() + lines - 1;
+
+ return endLine;
+
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getBottomIndex")); //$NON-NLS-1$
+ }
+
+ return -1;
+ }
+
+ /*
+ * @see ITextViewer#getTopIndexStartOffset
+ */
+ public int getTopIndexStartOffset() {
+
+ if (fTextWidget != null) {
+ int top= fTextWidget.getTopIndex();
+ try {
+ top= getVisibleDocument().getLineOffset(top);
+ return top + getVisibleRegionOffset();
+ } catch (BadLocationException ex) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getTopIndexStartOffset")); //$NON-NLS-1$
+ }
+ }
+
+ return -1;
+ }
+
+ /*
+ * @see ITextViewer#getBottomIndexEndOffset
+ */
+ public int getBottomIndexEndOffset() {
+ try {
+
+ IRegion line= getDocument().getLineInformation(getBottomIndex());
+ int bottomEndOffset= line.getOffset() + line.getLength() - 1;
+
+ IRegion region= getVisibleRegion();
+ int visibleRegionEndOffset= region.getOffset() + region.getLength() - 1;
+ return visibleRegionEndOffset < bottomEndOffset ? visibleRegionEndOffset : bottomEndOffset;
+
+ } catch (BadLocationException ex) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getBottomIndexEndOffset")); //$NON-NLS-1$
+ return getDocument().getLength() - 1;
+ }
+ }
+
+ /*
+ * @see ITextViewer#revealRange
+ */
+ public void revealRange(int start, int length) {
+
+ if (fTextWidget == null || !redraws())
+ return;
+
+ int end= start + length;
+
+ IDocument document= getVisibleDocument();
+ if (document == null)
+ return;
+
+ Position p= (document instanceof ChildDocument)
+ ? ((ChildDocument) document).getParentDocumentRange()
+ : new Position(0, document.getLength());
+
+ if (p.overlapsWith(start, length)) {
+
+ if (start < p.getOffset())
+ start= p.getOffset();
+ start -= p.getOffset();
+
+ int e= p.getOffset() + p.getLength();
+ if (end > e)
+ end= e;
+ end -= p.getOffset();
+
+ } else {
+ // http://dev.eclipse.org/bugs/show_bug.cgi?id=15159
+ start= start < p.getOffset() ? 0 : p.getLength();
+ end= start;
+ }
+
+ internalRevealRange(start, end);
+ }
+
+ /**
+ * Reveals the given range of the visible document.
+ *
+ * @param start the start offset of the range
+ * @param end the end offset of the range
+ */
+ protected void internalRevealRange(int start, int end) {
+
+ try {
+
+ IDocument doc= getVisibleDocument();
+
+ int startLine= doc.getLineOfOffset(start);
+ int endLine= doc.getLineOfOffset(end);
+
+ int top= fTextWidget.getTopIndex();
+ if (top > -1) {
+
+ // scroll vertically
+
+ int lines= getVisibleLinesInViewport();
+ int bottom= top + lines;
+
+ // two lines at the top and the bottom should always be left
+ // if window is smaller than 5 lines, always center position is chosen
+ int bufferZone= 2;
+
+ if (startLine >= top + bufferZone
+ && startLine <= bottom - bufferZone
+ && endLine >= top + bufferZone
+ && endLine <= bottom - bufferZone) {
+
+ // do not scroll at all as it is already visible
+
+ } else {
+
+ int delta= Math.max(0, lines - (endLine - startLine));
+ fTextWidget.setTopIndex(startLine - delta/3);
+ updateViewportListeners(INTERNAL);
+ }
+
+ // scroll horizontally
+
+ if (endLine < startLine) {
+ endLine += startLine;
+ startLine= endLine - startLine;
+ endLine -= startLine;
+ }
+
+ int startPixel= -1;
+ int endPixel= -1;
+
+ if (endLine > startLine) {
+ // reveal the beginning of the range in the start line
+ IRegion line= doc.getLineInformation(startLine);
+ startPixel= getWidthInPixels(line.getOffset(), start - line.getOffset());
+ endPixel= getWidthInPixels(line.getOffset(), line.getLength());
+ } else {
+ int lineStart= doc.getLineOffset(startLine);
+ startPixel= getWidthInPixels(lineStart, start - lineStart);
+ endPixel= getWidthInPixels(lineStart, end - lineStart);
+ }
+
+ int visibleStart= fTextWidget.getHorizontalPixel();
+ int visibleEnd= visibleStart + fTextWidget.getClientArea().width;
+
+ // scroll only if not yet visible
+ if (startPixel < visibleStart || visibleEnd < endPixel) {
+
+ // set buffer zone to 10 pixels
+ bufferZone= 10;
+
+ int newOffset= visibleStart;
+ if (startPixel < visibleStart)
+ newOffset= startPixel;
+ else if (endPixel - startPixel + bufferZone < visibleEnd - visibleStart)
+ newOffset= visibleStart + (endPixel - visibleEnd + bufferZone);
+ else
+ newOffset= startPixel;
+
+ fTextWidget.setHorizontalIndex(newOffset / getAverageCharWidth());
+ }
+
+ }
+ } catch (BadLocationException e) {
+ throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_range")); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Returns the width of the text when being drawed into this viewer's widget.
+ *
+ * @param the string to messure
+ * @return the width of the presentation of the given string
+ * @deprecated use <code>getWidthInPixels(int, int)</code> instead
+ */
+ final protected int getWidthInPixels(String text) {
+ GC gc= new GC(fTextWidget);
+ gc.setFont(fTextWidget.getFont());
+ Point extent= gc.textExtent(text);
+ gc.dispose();
+ return extent.x;
+ }
+
+ /**
+ * Returns the width of the representation of a text range in the
+ * visible region of the viewer's document as drawn in this viewer's
+ * widget.
+ *
+ * @param offset the offset of the text range in the visible region
+ * @param length the length of the text range in the visible region
+ * @return the width of the presentation of the specified text range
+ * @since 2.0
+ */
+ final protected int getWidthInPixels(int offset, int length) {
+
+ Point left= fTextWidget.getLocationAtOffset(offset);
+ Point right= new Point(left.x, left.y);
+
+ int end= offset + length;
+ for (int i= offset +1; i <= end; i++) {
+
+ Point p= fTextWidget.getLocationAtOffset(i);
+
+ if (left.x > p.x)
+ left.x= p.x;
+
+ if (right.x < p.x)
+ right.x= p.x;
+ }
+
+ return right.x - left.x;
+ }
+
+ /**
+ * Returns the average character width of this viewer's widget.
+ *
+ * @return the average character width of this viewer's widget
+ */
+ final protected int getAverageCharWidth() {
+ GC gc= new GC(fTextWidget);
+ gc.setFont(fTextWidget.getFont());
+ int increment= gc.getFontMetrics().getAverageCharWidth();
+ gc.dispose();
+ return increment;
+ }
+
+ /*
+ * @see Viewer#refresh
+ */
+ public void refresh() {
+ setDocument(getDocument());
+ }
+
+ //---- visible range support
+
+ /**
+ * Returns the child document manager
+ *
+ * @return the child document manager
+ */
+ private ChildDocumentManager getChildDocumentManager() {
+ if (fChildDocumentManager == null)
+ fChildDocumentManager= new ChildDocumentManager();
+ return fChildDocumentManager;
+ }
+
+ /**
+ * Invalidates the current presentation by sending an initialization
+ * event to all text listener.
+ * @since 2.0
+ */
+ public final void invalidateTextPresentation() {
+ if (fVisibleDocument != null) {
+ fWidgetCommand.start= 0;
+ fWidgetCommand.length= 0;
+ fWidgetCommand.text= fVisibleDocument.get();
+ fWidgetCommand.event= null;
+ updateTextListeners(fWidgetCommand);
+ }
+ }
+
+ /**
+ * Initializes the text widget with the visual document and
+ * invalidates the overall presentation.
+ */
+ private void initializeWidgetContents() {
+
+ if (fTextWidget != null && fVisibleDocument != null) {
+
+ // set widget content
+ if (fDocumentAdapter == null)
+ fDocumentAdapter= createDocumentAdapter();
+
+ fDocumentAdapter.setDocument(fVisibleDocument);
+ fTextWidget.setContent(fDocumentAdapter);
+
+ // invalidate presentation
+ invalidateTextPresentation();
+ }
+ }
+
+ /**
+ * Sets this viewer's visible document. The visible document represents the
+ * visible region of the viewer's input document.
+ *
+ * @param document the visible document
+ */
+ private void setVisibleDocument(IDocument document) {
+
+ if (fVisibleDocument != null && fDocumentListener != null)
+ fVisibleDocument.removeDocumentListener(fDocumentListener);
+
+ fVisibleDocument= document;
+
+ initializeWidgetContents();
+ resetPlugins();
+
+ if (fVisibleDocument != null && fDocumentListener != null)
+ fVisibleDocument.addDocumentListener(fDocumentListener);
+ }
+
+ /**
+ * Returns the viewer's visible document.
+ *
+ * @return the viewer's visible document
+ */
+ protected IDocument getVisibleDocument() {
+ return fVisibleDocument;
+ }
+
+ /**
+ * Returns the offset of the visible region.
+ *
+ * @return the offset of the visible region
+ */
+ protected int getVisibleRegionOffset() {
+
+ IDocument document= getVisibleDocument();
+ if (document instanceof ChildDocument) {
+ ChildDocument cdoc= (ChildDocument) document;
+ return cdoc.getParentDocumentRange().getOffset();
+ }
+
+ return 0;
+ }
+
+ /*
+ * @see ITextViewer#getVisibleRegion
+ */
+ public IRegion getVisibleRegion() {
+
+ IDocument document= getVisibleDocument();
+ if (document instanceof ChildDocument) {
+ Position p= ((ChildDocument) document).getParentDocumentRange();
+ return new Region(p.getOffset(), p.getLength());
+ }
+
+ return new Region(0, document == null ? 0 : document.getLength());
+ }
+
+ /*
+ * @see ITextViewer#setVisibleRegion
+ */
+ public void setVisibleRegion(int start, int length) {
+
+ IRegion region= getVisibleRegion();
+ if (start == region.getOffset() && length == region.getLength()) {
+ // nothing to change
+ return;
+ }
+
+ ChildDocument child= null;
+ IDocument parent= getVisibleDocument();
+
+ if (parent instanceof ChildDocument) {
+ child= (ChildDocument) parent;
+ parent= child.getParentDocument();
+ }
+
+ try {
+
+ int line= parent.getLineOfOffset(start);
+ int offset= parent.getLineOffset(line);
+ length += (start - offset);
+
+ if (child != null) {
+ child.setParentDocumentRange(offset, length);
+ } else {
+ child= getChildDocumentManager().createChildDocument(parent, offset, length);
+ }
+
+ setVisibleDocument(child);
+
+ } catch (BadLocationException x) {
+ throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_2")); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * @see ITextViewer#resetVisibleRegion
+ */
+ public void resetVisibleRegion() {
+ IDocument document= getVisibleDocument();
+ if (document instanceof ChildDocument) {
+ ChildDocument child = (ChildDocument) document;
+ setVisibleDocument(child.getParentDocument());
+ getChildDocumentManager().freeChildDocument(child);
+ }
+ }
+
+ /*
+ * @see ITextViewer#overlapsWithVisibleRegion
+ */
+ public boolean overlapsWithVisibleRegion(int start, int length) {
+ IDocument document= getVisibleDocument();
+ if (document instanceof ChildDocument) {
+ ChildDocument cdoc= (ChildDocument) document;
+ return cdoc.getParentDocumentRange().overlapsWith(start, length);
+ } else if (document != null) {
+ int size= document.getLength();
+ return (start >= 0 && length >= 0 && start + length <= size);
+ }
+ return false;
+ }
+
+
+
+ //--------------------------------------
+
+ /*
+ * @see ITextViewer#setTextDoubleClickStrategy
+ */
+ public void setTextDoubleClickStrategy(ITextDoubleClickStrategy strategy, String contentType) {
+
+ if (strategy != null) {
+ if (fDoubleClickStrategies == null)
+ fDoubleClickStrategies= new HashMap();
+ fDoubleClickStrategies.put(contentType, strategy);
+ } else if (fDoubleClickStrategies != null)
+ fDoubleClickStrategies.remove(contentType);
+ }
+
+ /**
+ * Selects from the given map the one which is registered under
+ * the content type of the partition in which the given offset is located.
+ *
+ * @param plugins the map from which to choose
+ * @param offset the offset for which to find the plugin
+ * @return the plugin registered under the offset's content type
+ */
+ protected Object selectContentTypePlugin(int offset, Map plugins) {
+ try {
+ return selectContentTypePlugin(getDocument().getContentType(offset), plugins);
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.selectContentTypePlugin")); //$NON-NLS-1$
+ }
+ return null;
+ }
+
+ /**
+ * Selects from the given <code>plugins</code> this one which is registered for
+ * the given content <code>type</code>.
+ */
+ private Object selectContentTypePlugin(String type, Map plugins) {
+
+ if (plugins == null)
+ return null;
+
+ return plugins.get(type);
+ }
+
+ /**
+ * Hook called on receipt of a <code>VerifyEvent</code>. The event has
+ * been translated into a <code>DocumentCommand</code> which can now be
+ * manipulated by interested parties. By default, the hook forwards the command
+ * to the installed <code>IAutoIndentStrategy</code>.
+ *
+ * @param command the document command representing the verify event
+ */
+ protected void customizeDocumentCommand(DocumentCommand command) {
+ if (!fIgnoreAutoIndent) {
+ IAutoIndentStrategy s= (IAutoIndentStrategy) selectContentTypePlugin(command.offset, fAutoIndentStrategies);
+ if (s != null)
+ s.customizeDocumentCommand(getDocument(), command);
+ }
+ fIgnoreAutoIndent= false;
+ }
+
+ /**
+ * @see VerifyListener#verifyText
+ */
+ protected void handleVerifyEvent(VerifyEvent e) {
+
+ if (fEventConsumer != null) {
+ fEventConsumer.processEvent(e);
+ if (!e.doit)
+ return;
+ }
+
+ int offset= getVisibleRegionOffset();
+ fDocumentCommand.setEvent(e, offset);
+ customizeDocumentCommand(fDocumentCommand);
+ if (!fDocumentCommand.fillEvent(e, offset)) {
+ try {
+ fVerifyListener.forward(false);
+ getDocument().replace(fDocumentCommand.offset, fDocumentCommand.length, fDocumentCommand.text);
+ if (fTextWidget != null) {
+ int caretOffset= fDocumentCommand.offset + (fDocumentCommand.text == null ? 0 : fDocumentCommand.text.length()) - offset;
+ fTextWidget.setCaretOffset(caretOffset);
+ }
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.verifyText")); //$NON-NLS-1$
+ } finally {
+ fVerifyListener.forward(true);
+ }
+ }
+ }
+
+ //---- text manipulation
+
+ /**
+ * Returns whether the marked region of this viewer is empty.
+ *
+ * @return <code>true</code> if the marked region of this viewer is empty, otherwise <code>false</code>
+ */
+ private boolean isMarkedRegionEmpty() {
+
+ if (fTextWidget == null)
+ return true;
+
+ IRegion region= getVisibleRegion();
+ int offset= region.getOffset();
+ int length= region.getLength();
+
+ return
+ fMarkPosition == null ||
+ fMarkPosition.isDeleted() ||
+ fMarkPosition.offset < offset ||
+ fMarkPosition.offset > offset + length;
+ }
+
+ /*
+ * @see ITextViewer#canDoOperation
+ */
+ public boolean canDoOperation(int operation) {
+
+ if (fTextWidget == null || !redraws())
+ return false;
+
+ switch (operation) {
+ case CUT:
+ return isEditable() &&(fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty());
+ case COPY:
+ return fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty();
+ case DELETE:
+ case PASTE:
+ return isEditable();
+ case SELECT_ALL:
+ return true;
+ case SHIFT_RIGHT:
+ case SHIFT_LEFT:
+ return isEditable() && fIndentChars != null && areMultipleLinesSelected();
+ case PREFIX:
+ case STRIP_PREFIX:
+ return isEditable() && fDefaultPrefixChars != null;
+ case UNDO:
+ return fUndoManager != null && fUndoManager.undoable();
+ case REDO:
+ return fUndoManager != null && fUndoManager.redoable();
+ case PRINT:
+ return isPrintable();
+ }
+
+ return false;
+ }
+
+ /*
+ * @see ITextViewer#doOperation
+ */
+ public void doOperation(int operation) {
+
+ if (fTextWidget == null || !redraws())
+ return;
+
+ switch (operation) {
+
+ case UNDO:
+ if (fUndoManager != null) {
+ fIgnoreAutoIndent= true;
+ fUndoManager.undo();
+ }
+ break;
+ case REDO:
+ if (fUndoManager != null) {
+ fIgnoreAutoIndent= true;
+ fUndoManager.redo();
+ }
+ break;
+ case CUT:
+ if (fTextWidget.getSelectionCount() == 0)
+ copyMarkedRegion(true);
+ else
+ fTextWidget.cut();
+ break;
+ case COPY:
+ if (fTextWidget.getSelectionCount() == 0)
+ copyMarkedRegion(false);
+ else
+ fTextWidget.copy();
+ break;
+ case PASTE:
+ fIgnoreAutoIndent= true;
+ fTextWidget.paste();
+ break;
+ case DELETE:
+ deleteText();
+ break;
+ case SELECT_ALL:
+ setSelectedRange(getVisibleRegionOffset(), getVisibleDocument().getLength());
+ break;
+ case SHIFT_RIGHT:
+ shift(false, true, false);
+ break;
+ case SHIFT_LEFT:
+ shift(false, false, false);
+ break;
+ case PREFIX:
+ shift(true, true, true);
+ break;
+ case STRIP_PREFIX:
+ shift(true, false, true);
+ break;
+ case PRINT:
+ print();
+ break;
+ }
+ }
+
+ /*
+ * @see ITextOperationTargetExtension#enableOperation(int, boolean)
+ * @since 2.0
+ */
+ public void enableOperation(int operation, boolean enable) {
+ /*
+ * No-op by default.
+ * Will be changed to regularily disable the known operations.
+ */
+ }
+
+ /**
+ * Copies/cuts the marked region.
+ *
+ * @param delete <code>true</code> if the region should be deleted rather than copied.
+ * @since 2.0
+ */
+ private void copyMarkedRegion(boolean delete) {
+
+ if (fTextWidget == null)
+ return;
+
+ IRegion region= getVisibleRegion();
+ int offset= region.getOffset();
+ int length= region.getLength();
+
+ if (fMarkPosition == null || fMarkPosition.isDeleted() ||
+ fMarkPosition.offset < offset || fMarkPosition.offset > offset + length)
+ return;
+
+ int markOffset= fMarkPosition.offset - offset;
+
+ Point selection= fTextWidget.getSelection();
+ if (selection.x <= markOffset)
+ fTextWidget.setSelection(selection.x, markOffset);
+ else
+ fTextWidget.setSelection(markOffset, selection.x);
+
+ if (delete) {
+ fTextWidget.cut();
+ } else {
+ fTextWidget.copy();
+ fTextWidget.setSelection(selection.x); // restore old cursor position
+ }
+ }
+
+ /**
+ * Deletes the current selection. If the selection has the length 0
+ * the selection is automatically extended to the right - either by 1
+ * or by the length of line delimiter if at the end of a line.
+ *
+ * @deprecated use <code>StyledText.invokeAction</code> instead
+ */
+ protected void deleteText() {
+ fTextWidget.invokeAction(ST.DELETE_NEXT);
+ }
+
+ /**
+ * A block is selected if the character preceding the start of the
+ * selection is a new line character.
+ *
+ * @return <code>true</code> if a block is selected
+ */
+ protected boolean isBlockSelected() {
+
+ Point s= getSelectedRange();
+ if (s.y == 0)
+ return false;
+
+ try {
+
+ IDocument document= getDocument();
+ int line= document.getLineOfOffset(s.x);
+ int start= document.getLineOffset(line);
+ return (s.x == start);
+
+ } catch (BadLocationException x) {
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if one line is completely selected or if multiple lines are selected.
+ * Being completely selected means that all characters except the new line characters are
+ * selected.
+ *
+ * @return <code>true</code> if one or multiple lines are selected
+ * @since 2.0
+ */
+ protected boolean areMultipleLinesSelected() {
+ Point s= getSelectedRange();
+ if (s.y == 0)
+ return false;
+
+ try {
+
+ IDocument document= getDocument();
+ int startLine= document.getLineOfOffset(s.x);
+ int endLine= document.getLineOfOffset(s.x + s.y);
+ IRegion line= document.getLineInformation(startLine);
+ return startLine != endLine || (s.x == line.getOffset() && s.y == line.getLength());
+
+ } catch (BadLocationException x) {
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the index of the first line whose start offset is in the given text range.
+ *
+ * @param region the text range in characters where to find the line
+ * @return the first line whose start index is in the given range, -1 if there is no such line
+ */
+ private int getFirstCompleteLineOfRegion(IRegion region) {
+
+ try {
+
+ IDocument d= getDocument();
+
+ int startLine= d.getLineOfOffset(region.getOffset());
+
+ int offset= d.getLineOffset(startLine);
+ if (offset >= region.getOffset())
+ return startLine;
+
+ offset= d.getLineOffset(startLine + 1);
+ return (offset > region.getOffset() + region.getLength() ? -1 : startLine + 1);
+
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getFirstCompleteLineOfRegion")); //$NON-NLS-1$
+ }
+
+ return -1;
+ }
+
+
+ /**
+ * Creates a region describing the text block (something that starts at
+ * the beginning of a line) completely containing the current selection.
+ *
+ * @param selection the selection to use
+ * @return the region describing the text block comprising the given selection
+ * @since 2.0
+ */
+ private IRegion getTextBlockFromSelection(Point selection) {
+
+ try {
+ IDocument document= getDocument();
+ IRegion line= document.getLineInformationOfOffset(selection.x);
+ int length= selection.y == 0 ? line.getLength() : selection.y + (selection.x - line.getOffset());
+ return new Region(line.getOffset(), length);
+
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Shifts a text block to the right or left using the specified set of prefix characters.
+ * The prefixes must start at the beginnig of the line.
+ *
+ * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
+ * @param right says whether to shift to the right or the left
+ *
+ * @deprecated use shift(boolean, boolean, boolean) instead
+ */
+ protected void shift(boolean useDefaultPrefixes, boolean right) {
+ shift(useDefaultPrefixes, right, false);
+ }
+
+ /**
+ * Shifts a text block to the right or left using the specified set of prefix characters.
+ * If white space should be ignored the prefix characters must not be at the beginning of
+ * the line when shifting to the left. There may be whitespace in front of the prefixes.
+ *
+ * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
+ * @param right says whether to shift to the right or the left
+ * @param ignoreWhitespace says whether whitepsace in front of prefixes is allowed
+ * @since 2.0
+ */
+ protected void shift(boolean useDefaultPrefixes, boolean right, boolean ignoreWhitespace) {
+
+ if (fUndoManager != null)
+ fUndoManager.beginCompoundChange();
+
+ setRedraw(false);
+ startSequentialRewriteMode(true);
+
+ IDocument d= getDocument();
+ IDocumentPartitioner partitioner= null;
+
+ try {
+
+ Point selection= getSelectedRange();
+ IRegion block= getTextBlockFromSelection(selection);
+ ITypedRegion[] regions= d.computePartitioning(block.getOffset(), block.getLength());
+
+ int lineCount= 0;
+ int[] lines= new int[regions.length * 2]; // [startline, endline, startline, endline, ...]
+ for (int i= 0, j= 0; i < regions.length; i++, j+= 2) {
+ // start line of region
+ lines[j]= getFirstCompleteLineOfRegion(regions[i]);
+ // end line of region
+ int offset= regions[i].getOffset() + regions[i].getLength() - 1;
+ lines[j + 1]= (lines[j] == -1 ? -1 : d.getLineOfOffset(offset));
+ lineCount += lines[j + 1] - lines[j] + 1;
+ }
+
+ if (lineCount >= 20) {
+ partitioner= d.getDocumentPartitioner();
+ if (partitioner != null) {
+ partitioner.disconnect();
+ d.setDocumentPartitioner(null);
+ }
+ }
+
+ // Remember the selection range.
+ IPositionUpdater positionUpdater= new ShiftPositionUpdater(SHIFTING);
+ Position rememberedSelection= new Position(selection.x, selection.y);
+ d.addPositionCategory(SHIFTING);
+ d.addPositionUpdater(positionUpdater);
+ try {
+ d.addPosition(SHIFTING, rememberedSelection);
+ } catch (BadPositionCategoryException ex) {
+ // should not happen
+ }
+
+ // Perform the shift operation.
+ Map map= (useDefaultPrefixes ? fDefaultPrefixChars : fIndentChars);
+ for (int i= 0, j= 0; i < regions.length; i++, j += 2) {
+ String[] prefixes= (String[]) selectContentTypePlugin(regions[i].getType(), map);
+ if (prefixes != null && prefixes.length > 0 && lines[j] >= 0 && lines[j + 1] >= 0) {
+ if (right)
+ shiftRight(lines[j], lines[j + 1], prefixes[0]);
+ else
+ shiftLeft(lines[j], lines[j + 1], prefixes, ignoreWhitespace);
+ }
+ }
+
+ // Restore the selection.
+ setSelectedRange(rememberedSelection.getOffset(), rememberedSelection.getLength());
+
+ try {
+ d.removePositionUpdater(positionUpdater);
+ d.removePositionCategory(SHIFTING);
+ } catch (BadPositionCategoryException ex) {
+ // should not happen
+ }
+
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.shift_1")); //$NON-NLS-1$
+
+ } finally {
+
+ if (partitioner != null) {
+ partitioner.connect(d);
+ d.setDocumentPartitioner(partitioner);
+ }
+
+ stopSequentialRewriteMode();
+ setRedraw(true);
+
+ if (fUndoManager != null)
+ fUndoManager.endCompoundChange();
+ }
+ }
+
+ /**
+ * Shifts the specified lines to the right inserting the given prefix
+ * at the beginning of each line
+ *
+ * @param prefix the prefix to be inserted
+ * @param startLine the first line to shift
+ * @param endLine the last line to shift
+ * @since 2.0
+ */
+ private void shiftRight(int startLine, int endLine, String prefix) {
+
+ try {
+
+ IDocument d= getDocument();
+ while (startLine <= endLine) {
+ d.replace(d.getLineOffset(startLine++), 0, prefix);
+ }
+
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println("TextViewer.shiftRight: BadLocationException"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Shifts the specified lines to the right or to the left. On shifting to the right
+ * it insert <code>prefixes[0]</code> at the beginning of each line. On shifting to the
+ * left it tests whether each of the specified lines starts with one of the specified
+ * prefixes and if so, removes the prefix.
+ *
+ * @param prefixes the prefixes to be used for shifting
+ * @param right if <code>true</code> shift to the right otherwise to the left
+ * @param startLine the first line to shift
+ * @param endLine the last line to shift
+ * @since 2.0
+ */
+ private void shiftLeft(int startLine, int endLine, String[] prefixes, boolean ignoreWhitespace) {
+
+ IDocument d= getDocument();
+
+ try {
+
+ IRegion[] occurrences= new IRegion[endLine - startLine + 1];
+
+ // find all the first occurrences of prefix in the given lines
+ for (int i= 0; i < occurrences.length; i++) {
+
+ IRegion line= d.getLineInformation(startLine + i);
+ String text= d.get(line.getOffset(), line.getLength());
+
+ int index= -1;
+ int[] found= TextUtilities.indexOf(prefixes, text, 0);
+ if (found[0] != -1) {
+ if (ignoreWhitespace) {
+ String s= d.get(line.getOffset(), found[0]);
+ s= s.trim();
+ if (s.length() == 0)
+ index= line.getOffset() + found[0];
+ } else if (found[0] == 0)
+ index= line.getOffset();
+ }
+
+ if (index > -1) {
+ // remember where prefix is in line, so that it can be removed
+ int length= prefixes[found[1]].length();
+ if (length == 0 && !ignoreWhitespace && line.getLength() > 0) {
+ // found a non-empty line which cannot be shifted
+ return;
+ } else
+ occurrences[i]= new Region(index, length);
+ } else {
+ // found a line which cannot be shifted
+ return;
+ }
+ }
+
+ // ok - change the document
+ int decrement= 0;
+ for (int i= 0; i < occurrences.length; i++) {
+ IRegion r= occurrences[i];
+ d.replace(r.getOffset() - decrement, r.getLength(), ""); //$NON-NLS-1$
+ decrement += r.getLength();
+ }
+
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println("TextViewer.shiftLeft: BadLocationException"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Returns whether the shown text can be printed.
+ *
+ * @return the viewer's printable mode
+ */
+ protected boolean isPrintable() {
+ /*
+ * 1GK7Q10: ITPUI:WIN98 - internal error after invoking print at editor view
+ * Changed from returning true to testing the length of the printer queue
+ */
+ PrinterData[] printerList= Printer.getPrinterList();
+ return (printerList != null && printerList.length > 0);
+ }
+
+ /**
+ * Brings up a print dialog and calls <code>printContents(Printer)</code> which
+ * performs the actual print.
+ *
+ * Subclasses may override.
+ */
+ protected void print() {
+
+ final PrintDialog dialog= new PrintDialog(fTextWidget.getShell(), SWT.PRIMARY_MODAL);
+ final PrinterData data= dialog.open();
+
+ if (data != null) {
+
+ final Printer printer= new Printer(data);
+ final Runnable styledTextPrinter= fTextWidget.print(printer);
+
+ Thread printingThread= new Thread("Printing") { //$NON-NLS-1$
+ public void run() {
+ styledTextPrinter.run();
+ printer.dispose();
+ }
+ };
+ printingThread.start();
+ }
+ }
+
+
+ //------ find support
+
+ /**
+ * @see IFindReplaceTarget#canPerformFind
+ */
+ protected boolean canPerformFind() {
+ IDocument d= getVisibleDocument();
+ return (fTextWidget != null && d != null && d.getLength() > 0);
+ }
+
+ /**
+ * @see IFindReplaceTarget#findAndSelect(int, String, boolean, boolean, boolean)
+ */
+ protected int findAndSelect(int startPosition, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) {
+ if (fTextWidget == null)
+ return -1;
+
+ try {
+ int offset= (startPosition == -1 ? startPosition : startPosition - getVisibleRegionOffset());
+ int pos= getVisibleDocument().search(offset, findString, forwardSearch, caseSensitive, wholeWord);
+ if (pos > -1) {
+ int length= findString.length();
+ if (redraws()) {
+ fTextWidget.setSelectionRange(pos, length);
+ internalRevealRange(pos, pos + length);
+ selectionChanged(pos, length);
+ } else {
+ setSelectedRange(pos + getVisibleRegionOffset(), length);
+ }
+ }
+ return pos + getVisibleRegionOffset();
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
+ }
+
+ return -1;
+ }
+
+ /**
+ * @see IFindReplaceTarget#findAndSelect(int, String, boolean, boolean, boolean)
+ * @since 2.0
+ */
+ private int findAndSelectInRange(int startPosition, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord, int rangeOffset, int rangeLength) {
+ if (fTextWidget == null)
+ return -1;
+
+ try {
+ int offset;
+ if (forwardSearch && (startPosition == -1 || startPosition < rangeOffset)) {
+ offset= rangeOffset;
+ } else if (!forwardSearch && (startPosition == -1 || startPosition > rangeOffset + rangeLength)) {
+ offset= rangeOffset + rangeLength;
+ } else {
+ offset= startPosition;
+ }
+ offset -= getVisibleRegionOffset();
+
+ int pos= getVisibleDocument().search(offset, findString, forwardSearch, caseSensitive, wholeWord);
+
+ int length = findString.length();
+ if (pos != -1 && (pos + getVisibleRegionOffset() < rangeOffset || pos + getVisibleRegionOffset() + length > rangeOffset + rangeLength))
+ pos= -1;
+
+ if (pos > -1) {
+ if (redraws()) {
+ fTextWidget.setSelectionRange(pos, length);
+ internalRevealRange(pos, pos + length);
+ selectionChanged(pos, length);
+ } else {
+ setSelectedRange(pos + getVisibleRegionOffset(), length);
+ }
+ }
+ return pos + getVisibleRegionOffset();
+ } catch (BadLocationException x) {
+ if (TRACE_ERRORS)
+ System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
+ }
+
+ return -1;
+ }
+
+ //---------- text presentation support
+
+ /*
+ * @see ITextViewer#setTextColor
+ */
+ public void setTextColor(Color color) {
+ if (color != null)
+ setTextColor(color, 0, getDocument().getLength(), true);
+ }
+
+ /*
+ * @see ITextViewer#setTextColor
+ */
+ public void setTextColor(Color color, int start, int length, boolean controlRedraw) {
+
+ if (fTextWidget != null) {
+
+ if (controlRedraw)
+ fTextWidget.setRedraw(false);
+
+ StyleRange s= new StyleRange();
+ s.foreground= color;
+ s.start= start;
+ s.length= length;
+
+ fTextWidget.setStyleRange(s);
+
+ if (controlRedraw)
+ fTextWidget.setRedraw(true);
+ }
+ }
+
+ /**
+ * Adds the given presentation to the viewer's style information.
+ *
+ * @param presentation the presentation to be added
+ */
+ private void addPresentation(TextPresentation presentation) {
+
+ StyleRange range= presentation.getDefaultStyleRange();
+ if (range != null) {
+
+ fTextWidget.setStyleRange(range);
+ Iterator e= presentation.getNonDefaultStyleRangeIterator();
+ while (e.hasNext()) {
+ range= (StyleRange) e.next();
+ fTextWidget.setStyleRange(range);
+ }
+
+ } else {
+
+ Iterator e= presentation.getAllStyleRangeIterator();
+
+ // use optimized StyledText
+ StyleRange[] ranges= new StyleRange[presentation.getDenumerableRanges()];
+ for (int i= 0; i < ranges.length; i++)
+ ranges[i]= (StyleRange) e.next();
+
+ IRegion region= presentation.getCoverage();
+ fTextWidget.replaceStyleRanges(region.getOffset(), region.getLength(), ranges);
+ }
+ }
+
+ /**
+ * Returns the visible region if it is not equal to the whole document.
+ * Otherwise returns <code>null</code>.
+ *
+ * @return the viewer's visible region if smaller than input document, otherwise <code>null</code>
+ */
+ protected IRegion internalGetVisibleRegion() {
+
+ IDocument document= getVisibleDocument();
+ if (document instanceof ChildDocument) {
+ Position p= ((ChildDocument) document).getParentDocumentRange();
+ return new Region(p.getOffset(), p.getLength());
+ }
+
+ return null;
+ }
+
+ /*
+ * @see ITextViewer#changeTextPresentation
+ */
+ public void changeTextPresentation(TextPresentation presentation, boolean controlRedraw) {
+
+ if (presentation == null)
+ return;
+
+ presentation.setResultWindow(internalGetVisibleRegion());
+ if (presentation.isEmpty() || fTextWidget == null)
+ return;
+
+ if (controlRedraw)
+ fTextWidget.setRedraw(false);
+
+ if (fReplaceTextPresentation)
+ TextPresentation.applyTextPresentation(presentation, fTextWidget);
+ else
+ addPresentation(presentation);
+
+ if (controlRedraw)
+ fTextWidget.setRedraw(true);
+ }
+
+ /*
+ * @see ITextViewer#getFindReplaceTarget()
+ */
+ public IFindReplaceTarget getFindReplaceTarget() {
+ if (fFindReplaceTarget == null)
+ fFindReplaceTarget= new FindReplaceTarget();
+ return fFindReplaceTarget;
+ }
+
+ /*
+ * @see ITextViewer#getTextOperationTarget()
+ */
+ public ITextOperationTarget getTextOperationTarget() {
+ return this;
+ }
+
+ /*
+ * @see ITextViewerExtension#appendVerifyKeyListener(VerifyKeyListener)
+ * @since 2.0
+ */
+ public void appendVerifyKeyListener(VerifyKeyListener listener) {
+ int index= fVerifyKeyListenersManager.numberOfListeners();
+ fVerifyKeyListenersManager.insertListener(listener, index);
+ }
+
+ /*
+ * @see ITextViewerExtension#prependVerifyKeyListener(VerifyKeyListener)
+ * @since 2.0
+ */
+ public void prependVerifyKeyListener(VerifyKeyListener listener) {
+ fVerifyKeyListenersManager.insertListener(listener, 0);
+
+ }
+
+ /*
+ * @see ITextViewerExtension#removeVerifyKeyListener(VerifyKeyListener)
+ * @since 2.0
+ */
+ public void removeVerifyKeyListener(VerifyKeyListener listener) {
+ fVerifyKeyListenersManager.removeListener(listener);
+ }
+
+ /*
+ * @see ITextViewerExtension#getMark()
+ * @since 2.0
+ */
+ public int getMark() {
+ return fMarkPosition == null || fMarkPosition.isDeleted()
+ ? -1
+ : fMarkPosition.getOffset();
+ }
+
+ /*
+ * @see ITextViewerExtension#setMark(int)
+ * @since 2.0
+ */
+ public void setMark(int offset) {
+
+ // clear
+ if (offset == -1) {
+ if (fMarkPosition != null && !fMarkPosition.isDeleted()) {
+
+ IDocument document= getDocument();
+ if (document != null)
+ document.removePosition(fMarkPosition);
+ }
+
+ fMarkPosition= null;
+
+ markChanged(-1, 0);
+
+ // set
+ } else {
+ if (fMarkPosition == null) {
+
+ IDocument document= getDocument();
+ if (document == null)
+ return;
+
+ if (offset < 0 || offset > document.getLength())
+ return;
+
+ try {
+ Position position= new Position(offset);
+ document.addPosition(MARK_POSITION_CATEGORY, position);
+ fMarkPosition= position;
+
+ } catch (BadLocationException e) {
+ return;
+ } catch (BadPositionCategoryException e) {
+ return;
+ }
+
+ } else {
+
+ IDocument document= getDocument();
+ if (document == null) {
+ fMarkPosition= null;
+ return;
+ }
+
+ if (offset < 0 || offset > document.getLength())
+ return;
+
+ fMarkPosition.setOffset(offset);
+ fMarkPosition.undelete();
+ }
+
+ markChanged(fMarkPosition.offset - getVisibleRegionOffset(), 0);
+ }
+ }
+
+ /*
+ * @see Viewer#inputChanged(Object, Object)
+ * @since 2.0
+ */
+ protected void inputChanged(Object newInput, Object oldInput) {
+
+ IDocument oldDocument= (IDocument) oldInput;
+ if (oldDocument != null) {
+ if (fMarkPosition != null && !fMarkPosition.isDeleted())
+ oldDocument.removePosition(fMarkPosition);
+
+ try {
+ oldDocument.removePositionUpdater(fMarkPositionUpdater);
+ oldDocument.removePositionCategory(MARK_POSITION_CATEGORY);
+
+ } catch (BadPositionCategoryException e) {
+ }
+ }
+
+ fMarkPosition= null;
+
+ super.inputChanged(newInput, oldInput);
+
+ IDocument newDocument= (IDocument) newInput;
+ if (newDocument != null) {
+ newDocument.addPositionCategory(MARK_POSITION_CATEGORY);
+ newDocument.addPositionUpdater(fMarkPositionUpdater);
+ }
+ }
+
+ /**
+ * Informs all text listeners about the change of the viewer's redraw state.
+ * @since 2.0
+ */
+ private void fireRedrawChanged() {
+ fWidgetCommand.start= 0;
+ fWidgetCommand.length= 0;
+ fWidgetCommand.text= null;
+ fWidgetCommand.event= null;
+ updateTextListeners(fWidgetCommand);
+ }
+
+ /**
+ * Enables the redrawing of this text viewer. Subclasses may extend.
+ * @since 2.0
+ */
+ protected void enabledRedrawing() {
+ if (fDocumentAdapter instanceof IDocumentAdapterExtension) {
+ IDocumentAdapterExtension extension= (IDocumentAdapterExtension) fDocumentAdapter;
+ StyledText textWidget= getTextWidget();
+ if (textWidget != null && !textWidget.isDisposed()) {
+ int topPixel= textWidget.getTopPixel();
+ extension.resumeForwardingDocumentChanges();
+ if (topPixel > -1) {
+ try {
+ textWidget.setTopPixel(topPixel);
+ } catch (IllegalArgumentException x) {
+ // changes don't allow for the previous top pixel
+ }
+ }
+ }
+ }
+
+ setSelectedRange(fDocumentSelection.x, fDocumentSelection.y);
+ revealRange(fDocumentSelection.x, fDocumentSelection.y);
+
+ if (fTextWidget != null && !fTextWidget.isDisposed())
+ fTextWidget.setRedraw(true);
+
+ fireRedrawChanged();
+ }
+
+ /**
+ * Disables the redrawing of this text viewer. Subclasses may extend.
+ * @since 2.0
+ */
+ protected void disableRedrawing() {
+
+ fDocumentSelection= getSelectedRange();
+
+ if (fDocumentAdapter instanceof IDocumentAdapterExtension) {
+ IDocumentAdapterExtension extension= (IDocumentAdapterExtension) fDocumentAdapter;
+ extension.stopForwardingDocumentChanges();
+ }
+
+ if (fTextWidget != null && !fTextWidget.isDisposed())
+ fTextWidget.setRedraw(false);
+
+ fireRedrawChanged();
+ }
+
+ /*
+ * @see ITextViewerExtension#setRedraw(boolean)
+ * @since 2.0
+ */
+ public final void setRedraw(boolean redraw) {
+ if (!redraw) {
+ if (fRedrawCounter == 0)
+ disableRedrawing();
+ ++ fRedrawCounter;
+ } else {
+ -- fRedrawCounter;
+ if (fRedrawCounter == 0)
+ enabledRedrawing();
+ }
+ }
+
+ /**
+ * Returns whether this viewer redraws itself.
+ *
+ * @return <code>true</code> if this viewer redraws itself
+ * @since 2.0
+ */
+ protected final boolean redraws() {
+ return fRedrawCounter <= 0;
+ }
+
+ /**
+ * Starts the sequential rewrite mode of the viewer's document.
+ * @since 2.0
+ */
+ protected final void startSequentialRewriteMode(boolean normalized) {
+ IDocument document= getDocument();
+ if (document instanceof IDocumentExtension) {
+ IDocumentExtension extension= (IDocumentExtension) document;
+ extension.startSequentialRewrite(normalized);
+ }
+ }
+
+ /**
+ * Sets the sequential rewrite mode of the viewer's document.
+ * @since 2.0
+ */
+ protected final void stopSequentialRewriteMode() {
+ IDocument document= getDocument();
+ if (document instanceof IDocumentExtension) {
+ IDocumentExtension extension= (IDocumentExtension) document;
+ extension.stopSequentialRewrite();
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ITextViewerExtension#getRewriteTarget()
+ * @since 2.0
+ */
+ public IRewriteTarget getRewriteTarget() {
+ if (fRewriteTarget == null)
+ fRewriteTarget= new RewriteTarget();
+ return fRewriteTarget;
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java
new file mode 100644
index 000000000..426bdb67d
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java
@@ -0,0 +1,300 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+
+/**
+ * This manager controls the layout, content, and visibility of an information
+ * control in reaction to mouse hover events issued by the text widget of a
+ * text viewer. It overrides <code>computeInformation</code>, so that the
+ * computation is performed in a dedicated background thread. This implies
+ * that the used <code>ITextHover</code> objects must be capable of
+ * operating in a non-UI thread.
+ *
+ * @since 2.0
+ */
+class TextViewerHoverManager extends AbstractHoverInformationControlManager implements IWidgetTokenKeeper {
+
+ /** The text viewer */
+ private TextViewer fTextViewer;
+ /** The hover information computation thread */
+ private Thread fThread;
+ /** The stopper of the computation thread */
+ private ITextListener fStopper;
+ /** Internal monitor */
+ private Object fMutex= new Object();
+
+
+ /**
+ * Creates a new text viewer hover manager specific for the given text viewer.
+ * The manager uses the given information control creator.
+ *
+ * @param textViewer the viewer for which the controller is created
+ * @param creator the information control creator
+ */
+ public TextViewerHoverManager(TextViewer textViewer, IInformationControlCreator creator) {
+ super(creator);
+ fTextViewer= textViewer;
+ fStopper= new ITextListener() {
+ public void textChanged(TextEvent event) {
+ synchronized (fMutex) {
+ if (fThread != null) {
+ fThread.interrupt();
+ fThread= null;
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Determines all necessary details and delegates the computation into
+ * a background thread.
+ */
+ protected void computeInformation() {
+
+ Point location= getHoverEventLocation();
+ int offset= computeOffsetAtLocation(location.x, location.y);
+ if (offset == -1) {
+ setInformation(null, null);
+ return;
+ }
+
+ final ITextHover hover= fTextViewer.getTextHover(offset);
+ if (hover == null) {
+ setInformation(null, null);
+ return;
+ }
+
+ final IRegion region= hover.getHoverRegion(fTextViewer, offset);
+ if (region == null) {
+ setInformation(null, null);
+ return;
+ }
+
+ final Rectangle area= computeArea(region);
+ if (area == null || area.isEmpty()) {
+ setInformation(null, null);
+ return;
+ }
+
+ if (fThread != null) {
+ setInformation(null, null);
+ return;
+ }
+
+ fThread= new Thread() {
+ public void run() {
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17693
+ try {
+
+ if (fThread != null) {
+ String information= hover.getHoverInfo(fTextViewer, region);
+ setInformation(information, area);
+ } else {
+ setInformation(null, null);
+ }
+
+ } finally {
+ synchronized (fMutex) {
+ if (fTextViewer != null)
+ fTextViewer.removeTextListener(fStopper);
+ fThread= null;
+ }
+ }
+ }
+ };
+
+ fThread.setDaemon(true);
+ fThread.setPriority(Thread.MIN_PRIORITY);
+ synchronized (fMutex) {
+ fTextViewer.addTextListener(fStopper);
+ fThread.start();
+ }
+ }
+
+ /**
+ * As computation is done in the background, this method is
+ * also called in the background thread. Delegates the control
+ * flow back into the ui thread, in order to allow displaying the
+ * information in the information control.
+ */
+ protected void presentInformation() {
+ if (fTextViewer == null)
+ return;
+
+ StyledText textWidget= fTextViewer.getTextWidget();
+ if (textWidget != null && !textWidget.isDisposed()) {
+ Display display= textWidget.getDisplay();
+ if (display == null)
+ return;
+
+ display.asyncExec(new Runnable() {
+ public void run() {
+ doPresentInformation();
+ }
+ });
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#presentInformation()
+ */
+ protected void doPresentInformation() {
+ super.presentInformation();
+ }
+
+ /**
+ * Computes the document offset underlying the given text widget coordinates.
+ * This method uses a linear search as it cannot make any assumption about
+ * how the document is actually presented in the widget. (Covers cases such
+ * as bidi text.)
+ *
+ * @param x the x coordinate inside the text widget
+ * @param y the y coordinate inside the text widget
+ * @return the document offset corresponding to the given point
+ */
+ private int computeOffsetAtLocation(int x, int y) {
+
+ StyledText styledText= fTextViewer.getTextWidget();
+ IDocument document= fTextViewer.getVisibleDocument();
+
+ if (document == null)
+ return -1;
+
+ int line= (y + styledText.getTopPixel()) / styledText.getLineHeight();
+ int lineCount= document.getNumberOfLines();
+
+ if (line > lineCount - 1)
+ line= lineCount - 1;
+
+ if (line < 0)
+ line= 0;
+
+ try {
+
+ IRegion lineInfo= document.getLineInformation(line);
+ int low= lineInfo.getOffset();
+ int high= low + lineInfo.getLength();
+
+ int lookup= styledText.getLocationAtOffset(low).x;
+ int guess= low;
+ int guessDelta= Math.abs(lookup - x);
+
+ for (int i= low + 1; i < high; i++) {
+ lookup= styledText.getLocationAtOffset(i).x;
+ int delta= Math.abs(lookup - x);
+ if (delta < guessDelta) {
+ guess= i;
+ guessDelta= delta;
+ }
+ }
+
+ return guess + fTextViewer.getVisibleRegionOffset();
+
+ } catch (BadLocationException e) {
+ }
+
+ return -1;
+ }
+
+ /**
+ * Determines graphical area covered by the given text region.
+ *
+ * @param region the region whose graphical extend must be computed
+ * @return the graphical extend of the given region
+ */
+ private Rectangle computeArea(IRegion region) {
+
+ StyledText styledText= fTextViewer.getTextWidget();
+
+ IRegion visibleRegion= fTextViewer.getVisibleRegion();
+ int start= region.getOffset() - visibleRegion.getOffset();
+ int end= start + region.getLength();
+ if (end > visibleRegion.getLength())
+ end= visibleRegion.getLength();
+
+ Point upperLeft= styledText.getLocationAtOffset(start);
+ Point lowerRight= new Point(upperLeft.x, upperLeft.y);
+
+ for (int i= start +1; i < end; i++) {
+
+ Point p= styledText.getLocationAtOffset(i);
+
+ if (upperLeft.x > p.x)
+ upperLeft.x= p.x;
+
+ if (upperLeft.y > p.y)
+ upperLeft.y= p.y;
+
+ if (lowerRight.x < p.x)
+ lowerRight.x= p.x;
+
+ if (lowerRight.y < p.y)
+ lowerRight.y= p.y;
+ }
+
+ lowerRight.x += fTextViewer.getAverageCharWidth();
+ lowerRight.y += styledText.getLineHeight();
+
+ int width= lowerRight.x - upperLeft.x;
+ int height= lowerRight.y - upperLeft.y;
+ return new Rectangle(upperLeft.x, upperLeft.y, width, height);
+ }
+
+ /*
+ * @see AbstractInformationControlManager#showInformationControl(Rectangle)
+ */
+ protected void showInformationControl(Rectangle subjectArea) {
+ if (fTextViewer != null && fTextViewer.requestWidgetToken(this))
+ super.showInformationControl(subjectArea);
+ }
+
+ /*
+ * @see AbstractInformationControlManager#hideInformationControl()
+ */
+ protected void hideInformationControl() {
+ try {
+ super.hideInformationControl();
+ } finally {
+ if (fTextViewer != null)
+ fTextViewer.releaseWidgetToken(this);
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#handleInformationControlDisposed()
+ */
+ protected void handleInformationControlDisposed() {
+ try {
+ super.handleInformationControlDisposed();
+ } finally {
+ if (fTextViewer != null)
+ fTextViewer.releaseWidgetToken(this);
+ }
+ }
+
+ /*
+ * @see IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+ */
+ public boolean requestWidgetToken(IWidgetTokenOwner owner) {
+ super.hideInformationControl();
+ return true;
+ }
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java
new file mode 100644
index 000000000..c1f642abb
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java
@@ -0,0 +1,232 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.util.Assert;
+
+
+
+/**
+ * Displays the additional information available for a completion proposal.
+ *
+ * @since 2.0
+ */
+class AdditionalInfoController extends AbstractInformationControlManager implements Runnable {
+
+ /**
+ * Internal table selection listener.
+ */
+ private class TableSelectionListener implements SelectionListener {
+
+ /*
+ * @see SelectionListener#widgetSelected(SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent e) {
+ handleTableSelectionChanged();
+ }
+
+ /*
+ * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+ };
+
+
+ private Table fProposalTable;
+ private Thread fThread;
+ private boolean fIsReset= false;
+
+ private Object fMutex= new Object();
+ private Object fStartSignal;
+
+ private SelectionListener fSelectionListener= new TableSelectionListener();
+ private int fDelay;
+
+
+ /**
+ * Creates a new additional information controller.
+ *
+ * @param creator the information control creator to be used by this controller
+ * @param delay time in milliseconds after which additional info should be displayed
+ */
+ AdditionalInfoController(IInformationControlCreator creator, int delay) {
+ super(creator);
+ fDelay= delay;
+ setAnchor(ANCHOR_RIGHT);
+ setFallbackAnchors(new Anchor[] { ANCHOR_LEFT, ANCHOR_BOTTOM, ANCHOR_RIGHT });
+ }
+
+ /*
+ * @see AbstractInformationControlManager#install(Control)
+ */
+ public void install(Control control) {
+
+ if (fProposalTable == control) {
+ // already installed
+ return;
+ }
+
+ super.install(control);
+
+ Assert.isTrue(control instanceof Table);
+ fProposalTable= (Table) control;
+ fProposalTable.addSelectionListener(fSelectionListener);
+ fThread= new Thread(this, JFaceTextMessages.getString("InfoPopup.info_delay_timer_name")); //$NON-NLS-1$
+
+ fStartSignal= new Object();
+ synchronized (fStartSignal) {
+ fThread.start();
+ try {
+ // wait until thread is ready
+ fStartSignal.wait();
+ } catch (InterruptedException x) {
+ }
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#disposeInformationControl()
+ */
+ public void disposeInformationControl() {
+
+ if (fThread != null) {
+ fThread.interrupt();
+ fThread= null;
+ }
+
+ if (fProposalTable != null && !fProposalTable.isDisposed()) {
+ fProposalTable.removeSelectionListener(fSelectionListener);
+ fProposalTable= null;
+ }
+
+ super.disposeInformationControl();
+ }
+
+ /*
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ try {
+ while (true) {
+
+ synchronized (fMutex) {
+
+ if (fStartSignal != null) {
+ synchronized (fStartSignal) {
+ fStartSignal.notifyAll();
+ fStartSignal= null;
+ }
+ }
+
+ // Wait for a selection event to occur.
+ fMutex.wait();
+
+ while (true) {
+ fIsReset= false;
+ // Delay before showing the popup.
+ fMutex.wait(fDelay);
+ if (!fIsReset)
+ break;
+ }
+ }
+
+ if (fProposalTable != null && !fProposalTable.isDisposed()) {
+ fProposalTable.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ if (!fIsReset)
+ showInformation();
+ }
+ });
+ }
+
+ }
+ } catch (InterruptedException e) {
+ }
+
+ fThread= null;
+ }
+
+ /**
+ *Handles a change of the line selected in the associated selector.
+ */
+ public void handleTableSelectionChanged() {
+
+ if (fProposalTable != null && !fProposalTable.isDisposed() && fProposalTable.isVisible()) {
+ synchronized (fMutex) {
+ fIsReset= true;
+ fMutex.notifyAll();
+ }
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#computeInformation()
+ */
+ protected void computeInformation() {
+
+ if (fProposalTable == null || fProposalTable.isDisposed())
+ return;
+
+ TableItem[] selection= fProposalTable.getSelection();
+ if (selection != null && selection.length > 0) {
+
+ TableItem item= selection[0];
+
+ // compute information
+ String information= null;
+ Object d= item.getData();
+ if (d instanceof ICompletionProposal) {
+ ICompletionProposal p= (ICompletionProposal) d;
+ information= p.getAdditionalProposalInfo();
+ }
+
+ // compute subject area
+ setMargins(4, -1);
+ Rectangle area= fProposalTable.getBounds();
+ area.x= 0; // subject area is the whole subject control
+ area.y= 0;
+
+ // set information & subject area
+ setInformation(information, area);
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.AbstractInformationControlManager#computeSizeConstraints(Control, IInformationControl)
+ */
+ protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
+ Point sizeConstraint= super.computeSizeConstraints(subjectControl, informationControl);
+ Point size= subjectControl.getSize();
+ if (sizeConstraint.x < size.x)
+ sizeConstraint.x= size.x;
+ if (sizeConstraint.y < size.y)
+ sizeConstraint.y= size.y;
+ return sizeConstraint;
+ }
+}
+
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposal.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposal.java
new file mode 100644
index 000000000..a33cf7f02
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposal.java
@@ -0,0 +1,120 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.util.Assert;
+
+
+
+/**
+ * The standard implementation of the <code>ICompletionProposal</code> interface.
+ */
+public final class CompletionProposal implements ICompletionProposal {
+
+ private String fDisplayString;
+ private String fReplacementString;
+ private int fReplacementOffset;
+ private int fReplacementLength;
+ private int fCursorPosition;
+ private Image fImage;
+ private IContextInformation fContextInformation;
+ private String fAdditionalProposalInfo;
+
+ /**
+ * Creates a new completion proposal based on the provided information. The replacement string is
+ * considered being the display string too. All remaining fields are set to <code>null</code>.
+ *
+ * @param replacementString the actual string to be inserted into the document
+ * @param replacementOffset the offset of the text to be replaced
+ * @param replacementLength the length of the text to be replaced
+ * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+ */
+ public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition) {
+ this(replacementString, replacementOffset, replacementLength, cursorPosition, null, null, null, null);
+ }
+
+ /**
+ * Creates a new completion proposal. All fields are initialized based on the provided information.
+ *
+ * @param replacementString the actual string to be inserted into the document
+ * @param replacementOffset the offset of the text to be replaced
+ * @param replacementLength the length of the text to be replaced
+ * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+ * @param image the image to display for this proposal
+ * @param displayString the string to be displayed for the proposal
+ * @param contentInformation the context information associated with this proposal
+ * @param additionalProposalInfo the additional information associated with this proposal
+ */
+ public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo) {
+ Assert.isNotNull(replacementString);
+ Assert.isTrue(replacementOffset >= 0);
+ Assert.isTrue(replacementLength >= 0);
+ Assert.isTrue(cursorPosition >= 0);
+
+ fReplacementString= replacementString;
+ fReplacementOffset= replacementOffset;
+ fReplacementLength= replacementLength;
+ fCursorPosition= cursorPosition;
+ fImage= image;
+ fDisplayString= displayString;
+ fContextInformation= contextInformation;
+ fAdditionalProposalInfo= additionalProposalInfo;
+ }
+
+ /*
+ * @see ICompletionProposal#apply
+ */
+ public void apply(IDocument document) {
+ try {
+ document.replace(fReplacementOffset, fReplacementLength, fReplacementString);
+ } catch (BadLocationException x) {
+ // ignore
+ }
+ }
+
+ /*
+ * @see ICompletionProposal#getSelection
+ */
+ public Point getSelection(IDocument document) {
+ return new Point(fReplacementOffset + fCursorPosition, 0);
+ }
+
+ /*
+ * @see ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return fContextInformation;
+ }
+
+ /*
+ * @see ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return fImage;
+ }
+
+ /*
+ * @see ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ if (fDisplayString != null)
+ return fDisplayString;
+ return fReplacementString;
+ }
+
+ /*
+ * @see ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return fAdditionalProposalInfo;
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
new file mode 100644
index 000000000..da95fcc30
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
@@ -0,0 +1,603 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+
+
+
+/**
+ * This class is used to present proposals to the user. If additional
+ * information exists for a proposal, then selecting that proposal
+ * will result in the information being displayed in a secondary
+ * window.
+ *
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal
+ */
+class CompletionProposalPopup implements IContentAssistListener {
+
+ private ITextViewer fViewer;
+ private ContentAssistant fContentAssistant;
+ private AdditionalInfoController fAdditionalInfoController;
+
+ private PopupCloser fPopupCloser= new PopupCloser();
+ private Shell fProposalShell;
+ private Table fProposalTable;
+ private boolean fInserting= false;
+
+ private long fInvocationCounter= 0;
+ private ICompletionProposal[] fFilteredProposals;
+ private ICompletionProposal[] fComputedProposals;
+ private int fInvocationOffset;
+ private int fFilterOffset;
+
+ private String fLineDelimiter;
+
+
+ /**
+ * Creates a new completion proposal popup for the given elements.
+ *
+ * @param contentAssistant the content assistant feeding this popup
+ * @param viewer the viewer on top of which this popup appears
+ * @param infoController the info control collaborating with this popup
+ * @since 2.0
+ */
+ public CompletionProposalPopup(ContentAssistant contentAssistant, ITextViewer viewer, AdditionalInfoController infoController) {
+ fContentAssistant= contentAssistant;
+ fViewer= viewer;
+ fAdditionalInfoController= infoController;
+ }
+
+ /**
+ * Computes and presents completion proposals. The flag indicates whether this call has
+ * be made out of an auto activation context.
+ *
+ * @param autoActivated <code>true</code> if auto activation context
+ * @return an error message or <code>null</code> in case of no error
+ */
+ public String showProposals(final boolean autoActivated) {
+ final StyledText styledText= fViewer.getTextWidget();
+ BusyIndicator.showWhile(styledText.getDisplay(), new Runnable() {
+ public void run() {
+
+
+ fInvocationOffset= fViewer.getSelectedRange().x;
+ fComputedProposals= computeProposals(fInvocationOffset);
+
+
+ int count= (fComputedProposals == null ? 0 : fComputedProposals.length);
+ if (count == 0) {
+
+ if (!autoActivated)
+ styledText.getDisplay().beep();
+
+ } else {
+
+ if (count == 1 && !autoActivated && fContentAssistant.isAutoInserting())
+
+ insertProposal(fComputedProposals[0], (char) 0, fInvocationOffset);
+
+ else {
+
+ if (fLineDelimiter == null)
+ fLineDelimiter= styledText.getLineDelimiter();
+
+ createProposalSelector();
+ setProposals(fComputedProposals);
+ displayProposals();
+ }
+ }
+ }
+ });
+
+ return getErrorMessage();
+ }
+
+ /**
+ * Returns the completion proposal available at the given offset of the
+ * viewer's document. Delegates the work to the content assistant.
+ *
+ * @param offset the offset
+ * @return the completion proposals available at this offset
+ */
+ private ICompletionProposal[] computeProposals(int offset) {
+ return fContentAssistant.computeCompletionProposals(fViewer, offset);
+ }
+
+ /**
+ * Returns the error message.
+ *
+ * @return the error message
+ */
+ private String getErrorMessage() {
+ return fContentAssistant.getErrorMessage();
+ }
+
+ /**
+ * Creates the proposal selector.
+ */
+ private void createProposalSelector() {
+ if (Helper.okToUse(fProposalShell))
+ return;
+
+ Control control= fViewer.getTextWidget();
+ fProposalShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE );
+ fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL);
+
+ fProposalTable.setLocation(0, 0);
+ fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+
+ GridLayout layout= new GridLayout();
+ layout.marginWidth= 0;
+ layout.marginHeight= 0;
+ fProposalShell.setLayout(layout);
+
+ GridData data= new GridData(GridData.FILL_BOTH);
+ data.heightHint= fProposalTable.getItemHeight() * 10;
+ data.widthHint= 300;
+ fProposalTable.setLayoutData(data);
+
+ fProposalShell.pack();
+
+ fProposalShell.addControlListener(new ControlListener() {
+
+ public void controlMoved(ControlEvent e) {}
+
+ public void controlResized(ControlEvent e) {
+ // resets the cached resize constraints
+ fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+ }
+ });
+
+
+ fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
+
+ Color c= fContentAssistant.getProposalSelectorBackground();
+ if (c == null)
+ c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+ fProposalTable.setBackground(c);
+
+ c= fContentAssistant.getProposalSelectorForeground();
+ if (c == null)
+ c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+ fProposalTable.setForeground(c);
+
+ fProposalTable.addSelectionListener(new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ selectProposal();
+ }
+ });
+
+ fPopupCloser.install(fContentAssistant, fProposalTable);
+
+ fProposalTable.setHeaderVisible(false);
+ fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset());
+ }
+
+ /**
+ * Returns the proposal selected in the proposal selector.
+ *
+ * @return the selected proposal
+ * @since 2.0
+ */
+ private ICompletionProposal getSelectedProposal() {
+ int i= fProposalTable.getSelectionIndex();
+ if (i < 0 || i >= fFilteredProposals.length)
+ return null;
+ return fFilteredProposals[i];
+ }
+
+ /**
+ * Takes the selected proposal and applies it.
+ * @since 2.0
+ */
+ private void selectProposal() {
+ ICompletionProposal p= getSelectedProposal();
+ hide();
+ if (p != null)
+ insertProposal(p, (char) 0, fViewer.getSelectedRange().x);
+ }
+
+ /**
+ * Applies the given proposal at the given offset. The given character is the
+ * one that triggered the insertion of this proposal.
+ *
+ * @param p the completion proposal
+ * @param trigger the trigger character
+ * @param offset the offset
+ *
+ * @since 2.0
+ */
+ private void insertProposal(ICompletionProposal p, char trigger, int offset) {
+
+ fInserting= true;
+
+ try {
+ IDocument document= fViewer.getDocument();
+
+ if (p instanceof ICompletionProposalExtension) {
+ ICompletionProposalExtension e= (ICompletionProposalExtension) p;
+ e.apply(document, trigger, offset);
+ } else {
+ p.apply(document);
+ }
+
+ Point selection= p.getSelection(document);
+ if (selection != null) {
+ fViewer.setSelectedRange(selection.x, selection.y);
+ fViewer.revealRange(selection.x, selection.y);
+ }
+
+ IContextInformation info= p.getContextInformation();
+ if (info != null) {
+
+ int position;
+ if (p instanceof ICompletionProposalExtension) {
+ ICompletionProposalExtension e= (ICompletionProposalExtension) p;
+ position= e.getContextInformationPosition();
+ } else {
+ if (selection == null)
+ selection= fViewer.getSelectedRange();
+ position= selection.x + selection.y;
+ }
+
+ fContentAssistant.showContextInformation(info, position);
+ }
+
+ } finally {
+ fInserting= false;
+ }
+
+ }
+
+ /**
+ * Returns whether this popup has the focus.
+ * @return <code>true</code> if the popup has the focus
+ */
+ public boolean hasFocus() {
+ if (Helper.okToUse(fProposalShell))
+ return (fProposalShell.isFocusControl() || fProposalTable.isFocusControl());
+
+ return false;
+ }
+
+ /**
+ * Hides this popup.
+ */
+ public void hide() {
+ if (Helper.okToUse(fProposalShell)) {
+
+ fContentAssistant.removeContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR);
+
+ fPopupCloser.uninstall();
+ fProposalShell.setVisible(false);
+ fProposalShell.dispose();
+ fProposalShell= null;
+ }
+
+ fFilteredProposals= null;
+ }
+
+ /**
+ *Returns whether this popup is active. It is active if the propsal selector is visible.
+ * @return <code>true</code> if this popup is active
+ */
+ public boolean isActive() {
+ return fProposalShell != null && !fProposalShell.isDisposed();
+ }
+
+ /**
+ * Initializes the proposal selector with these given proposals.
+ *
+ * @param proposals the proposals
+ */
+ private void setProposals(ICompletionProposal[] proposals) {
+ if (Helper.okToUse(fProposalTable)) {
+
+ fFilteredProposals= proposals;
+
+ fProposalTable.setRedraw(false);
+ fProposalTable.removeAll();
+
+ TableItem item;
+ ICompletionProposal p;
+ for (int i= 0; i < proposals.length; i++) {
+ p= proposals[i];
+ item= new TableItem(fProposalTable, SWT.NULL);
+ if (p.getImage() != null)
+ item.setImage(p.getImage());
+ item.setText(p.getDisplayString());
+ item.setData(p);
+ }
+
+ Point currentLocation= fProposalShell.getLocation();
+ Point newLocation= getLocation();
+ if ((newLocation.x < currentLocation.x && newLocation.y == currentLocation.y) || newLocation.y < currentLocation.y)
+ fProposalShell.setLocation(newLocation);
+
+ selectProposal(0);
+ fProposalTable.setRedraw(true);
+ }
+ }
+
+ /**
+ * Returns the graphical location at which this popup should be made visible.
+ * @return the location of this popup
+ */
+ private Point getLocation() {
+ StyledText text= fViewer.getTextWidget();
+ int caret= text.getCaretOffset();
+ Point p= text.getLocationAtOffset(caret);
+ p= new Point(p.x, p.y + text.getLineHeight());
+ return text.toDisplay(p);
+ }
+
+ /**
+ *Displays this popup and install the additional info controller, so that additional info
+ * is displayed when a proposal is selected and additional info is available.
+ */
+ private void displayProposals() {
+ if (fContentAssistant.addContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR)) {
+ fProposalShell.setVisible(true);
+ if (fAdditionalInfoController != null) {
+ fAdditionalInfoController.install(fProposalTable);
+ fAdditionalInfoController.handleTableSelectionChanged();
+ }
+ }
+ }
+
+ /*
+ * @see IContentAssistListener#verifyKey(VerifyEvent)
+ */
+ public boolean verifyKey(VerifyEvent e) {
+ if (!Helper.okToUse(fProposalShell))
+ return true;
+
+ char key= e.character;
+ if (key == 0) {
+ int newSelection= fProposalTable.getSelectionIndex();
+ int visibleRows= (fProposalTable.getSize().y / fProposalTable.getItemHeight()) - 1;
+ switch (e.keyCode) {
+
+ case SWT.ARROW_LEFT :
+ case SWT.ARROW_RIGHT :
+ filterProposal();
+ return true;
+
+ case SWT.ARROW_UP :
+ newSelection -= 1;
+ if (newSelection < 0)
+ newSelection= fProposalTable.getItemCount() - 1;
+ break;
+
+ case SWT.ARROW_DOWN :
+ newSelection += 1;
+ if (newSelection > fProposalTable.getItemCount() - 1)
+ newSelection= 0;
+ break;
+
+ case SWT.PAGE_DOWN :
+ newSelection += visibleRows;
+ if (newSelection >= fProposalTable.getItemCount())
+ newSelection= fProposalTable.getItemCount() - 1;
+ break;
+
+ case SWT.PAGE_UP :
+ newSelection -= visibleRows;
+ if (newSelection < 0)
+ newSelection= 0;
+ break;
+
+ case SWT.HOME :
+ newSelection= 0;
+ break;
+
+ case SWT.END :
+ newSelection= fProposalTable.getItemCount() - 1;
+ break;
+
+ case SWT.CTRL :
+ case SWT.SHIFT :
+ return true;
+
+ default :
+ hide();
+ return true;
+ }
+
+ selectProposal(newSelection);
+
+ e.doit= false;
+ return false;
+
+ } else {
+
+ switch (key) {
+ case 0x1B : // Esc
+ e.doit= false;
+ hide();
+ break;
+
+ case 0x0D : // Enter
+ e.doit= false;
+ selectProposal();
+ break;
+
+ default:
+
+ if ('\t' == key) {
+ e.doit= false;
+ fProposalShell.setFocus();
+ return false;
+ }
+
+ ICompletionProposal p= getSelectedProposal();
+ if (p instanceof ICompletionProposalExtension) {
+ ICompletionProposalExtension t= (ICompletionProposalExtension) p;
+ char[] triggers= t.getTriggerCharacters();
+ if (contains(triggers, key)) {
+ e.doit= false;
+ hide();
+ insertProposal(p, key, fViewer.getSelectedRange().x);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Selects the entry with the given index in the proposal selector and feeds
+ * the selection to the additional info controller.
+ *
+ * @param index the index in the list
+ * @since 2.0
+ */
+ private void selectProposal(int index) {
+ fProposalTable.setSelection(index);
+ fProposalTable.showSelection();
+ if (fAdditionalInfoController != null)
+ fAdditionalInfoController.handleTableSelectionChanged();
+ }
+
+ /**
+ * Returns whether the given character is contained in the given array of
+ * characters.
+ *
+ * @param characters the list of characters
+ * @param c the character to look for in the list
+ * @return <code>true</code> if character belongs to the list
+ * @since 2.0
+ */
+ private boolean contains(char[] characters, char c) {
+
+ if (characters == null)
+ return false;
+
+ for (int i= 0; i < characters.length; i++) {
+ if (c == characters[i])
+ return true;
+ }
+
+ return false;
+ }
+
+ /*
+ * @see IEventConsumer#processEvent(VerifyEvent)
+ */
+ public void processEvent(VerifyEvent e) {
+ if (!fInserting)
+ filterProposal();
+ }
+
+ /**
+ * Filters the displayed proposal based on the given cursor position and the
+ * offset of the original invocation of the content assistant.
+ */
+ private void filterProposal() {
+ ++ fInvocationCounter;
+ Control control= fViewer.getTextWidget();
+ control.getDisplay().asyncExec(new Runnable() {
+ long fCounter= fInvocationCounter;
+ public void run() {
+
+ if (fCounter != fInvocationCounter) return;
+
+ int offset= fViewer.getSelectedRange().x;
+ ICompletionProposal[] proposals= (offset == -1 ? null : computeFilteredProposals(offset));
+ fFilterOffset= offset;
+
+ if (proposals != null && proposals.length > 0)
+ setProposals(proposals);
+ else
+ hide();
+ }
+ });
+ }
+
+ /**
+ * Computes the subset of already computed propsals that are still valid for
+ * the given offset.
+ *
+ * @param offset the offset
+ * @return the set of filtered proposals
+ * @since 2.0
+ */
+ private ICompletionProposal[] computeFilteredProposals(int offset) {
+
+ if (offset == fInvocationOffset)
+ return fComputedProposals;
+
+ if (offset < fInvocationOffset) {
+ fInvocationOffset= offset;
+ fComputedProposals= computeProposals(fInvocationOffset);
+ return fComputedProposals;
+ }
+
+ ICompletionProposal[] proposals= fComputedProposals;
+ if (offset > fFilterOffset)
+ proposals= fFilteredProposals;
+
+ if (proposals == null)
+ return null;
+
+ IDocument document= fViewer.getDocument();
+ int length= proposals.length;
+ List filtered= new ArrayList(length);
+ for (int i= 0; i < length; i++) {
+ if (proposals[i] instanceof ICompletionProposalExtension) {
+
+ ICompletionProposalExtension p= (ICompletionProposalExtension) proposals[i];
+ if (p.isValidFor(document, offset))
+ filtered.add(p);
+
+ } else {
+ // restore original behavior
+ fInvocationOffset= offset;
+ fComputedProposals= computeProposals(fInvocationOffset);
+ return fComputedProposals;
+ }
+ }
+
+ ICompletionProposal[] p= new ICompletionProposal[filtered.size()];
+ filtered.toArray(p);
+ return p;
+ }
+}
+
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
new file mode 100644
index 000000000..f174ac291
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.java
@@ -0,0 +1,1377 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.custom.VerifyKeyListener;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+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.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IEventConsumer;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.IWidgetTokenKeeper;
+import org.eclipse.jface.text.IWidgetTokenOwner;
+
+import org.eclipse.jface.util.Assert;
+
+
+
+/**
+ * The standard implementation of the <code>IContentAssistant</code> interface.
+ * Usually, clients instantiate this class and configure it before using it.
+ */
+public class ContentAssistant implements IContentAssistant, IWidgetTokenKeeper {
+
+ /**
+ * A generic closer class used to monitor various
+ * interface events in order to determine whether
+ * content-assist should be terminated and all
+ * associated windows closed.
+ */
+ class Closer implements ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {
+
+ /**
+ * Installs this closer on it's viewer's text widget.
+ */
+ protected void install() {
+ Control w= fViewer.getTextWidget();
+ if (Helper.okToUse(w)) {
+
+ Control shell= w.getShell();
+ shell.addControlListener(this);
+
+ w.addMouseListener(this);
+ w.addFocusListener(this);
+
+ /*
+ * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
+ */
+ w.addDisposeListener(this);
+ }
+
+ fViewer.addViewportListener(this);
+ }
+
+ /**
+ * Uninstalls this closer from the viewer's text widget.
+ */
+ protected void uninstall() {
+ Control w= fViewer.getTextWidget();
+ if (Helper.okToUse(w)) {
+
+ Control shell= w.getShell();
+ if (Helper.okToUse(shell))
+ shell.removeControlListener(this);
+
+ w.removeMouseListener(this);
+ w.removeFocusListener(this);
+
+ /*
+ * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
+ */
+ w.removeDisposeListener(this);
+ }
+
+ fViewer.removeViewportListener(this);
+ }
+
+ /*
+ * @see ControlListener#controlResized(ControlEvent)
+ */
+ public void controlResized(ControlEvent e) {
+ hide();
+ }
+
+ /*
+ * @see ControlListener#controlMoved(ControlEvent)
+ */
+ public void controlMoved(ControlEvent e) {
+ hide();
+ }
+
+ /*
+ * @see MouseListener#mouseDown(MouseEvent)
+ */
+ public void mouseDown(MouseEvent e) {
+ hide();
+ }
+
+ /*
+ * @see MouseListener#mouseUp(MouseEvent)
+ */
+ public void mouseUp(MouseEvent e) {
+ }
+
+ /*
+ * @see MouseListener#mouseDoubleClick(MouseEvent)
+ */
+ public void mouseDoubleClick(MouseEvent e) {
+ hide();
+ }
+
+ /*
+ * @see FocusListener#focusGained(FocusEvent)
+ */
+ public void focusGained(FocusEvent e) {
+ }
+
+ /*
+ * @see FocusListener#focusLost(FocusEvent)
+ */
+ public void focusLost(FocusEvent e) {
+ Control control= fViewer.getTextWidget();
+ Display d= control.getDisplay();
+ d.asyncExec(new Runnable() {
+ public void run() {
+ if (!fProposalPopup.hasFocus() && !fContextInfoPopup.hasFocus()) {
+ hide();
+ }
+ }
+ });
+ }
+
+ /*
+ * @seeDisposeListener#widgetDisposed(DisposeEvent)
+ */
+ public void widgetDisposed(DisposeEvent e) {
+ /*
+ * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors
+ */
+ hide();
+ }
+
+ /*
+ * @see IViewportListener#viewportChanged(int)
+ */
+ public void viewportChanged(int topIndex) {
+ hide();
+ }
+
+ /**
+ * Hides any open popups.
+ */
+ protected void hide() {
+ fProposalPopup.hide();
+ fContextInfoPopup.hide();
+ }
+ };
+
+ /**
+ * An implementation of <code>IContentAssistListener</code>, this class is
+ * used to monitor key events in support of automatic activation
+ * of the content assistant. If enabled, the implementation utilizes a
+ * thread to watch for input characters matching the activation
+ * characters specified by the content assist processor, and if
+ * detected, will wait the indicated delay interval before
+ * activating the content assistant.
+ */
+ class AutoAssistListener implements VerifyKeyListener, Runnable {
+
+ private Thread fThread;
+ private boolean fIsReset= false;
+ private Object fMutex= new Object();
+ private int fShowStyle;
+
+ private final static int SHOW_PROPOSALS= 1;
+ private final static int SHOW_CONTEXT_INFO= 2;
+
+ protected AutoAssistListener() {
+ }
+
+ protected void start(int showStyle) {
+ fShowStyle= showStyle;
+ fThread= new Thread(this, JFaceTextMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
+ fThread.start();
+ }
+
+ public void run() {
+ try {
+ while (true) {
+ synchronized (fMutex) {
+ if (fAutoActivationDelay != 0)
+ fMutex.wait(fAutoActivationDelay);
+ if (fIsReset) {
+ fIsReset= false;
+ continue;
+ }
+ }
+ showAssist(fShowStyle);
+ break;
+ }
+ } catch (InterruptedException e) {
+ }
+ fThread= null;
+ }
+
+ protected void reset(int showStyle) {
+ synchronized (fMutex) {
+ fShowStyle= showStyle;
+ fIsReset= true;
+ fMutex.notifyAll();
+ }
+ }
+
+ protected void stop() {
+ if (fThread != null) {
+ fThread.interrupt();
+ }
+ }
+
+ private boolean contains(char[] characters, char character) {
+ if (characters != null) {
+ for (int i= 0; i < characters.length; i++) {
+ if (character == characters[i])
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void verifyKey(VerifyEvent e) {
+
+ int showStyle;
+ int pos= fViewer.getSelectedRange().x;
+ char[] activation= getCompletionProposalAutoActivationCharacters(fViewer.getDocument(), pos);
+
+ if (contains(activation, e.character) && !fProposalPopup.isActive())
+ showStyle= SHOW_PROPOSALS;
+ else {
+ activation= getContextInformationAutoActivationCharacters(fViewer.getDocument(), pos);
+ if (contains(activation, e.character) && !fContextInfoPopup.isActive())
+ showStyle= SHOW_CONTEXT_INFO;
+ else {
+ if (fThread != null && fThread.isAlive())
+ stop();
+ return;
+ }
+ }
+
+ if (fThread != null && fThread.isAlive())
+ reset(showStyle);
+ else
+ start(showStyle);
+ }
+
+ protected void showAssist(final int showStyle) {
+ Control control= fViewer.getTextWidget();
+ Display d= control.getDisplay();
+ if (d != null) {
+ try {
+ d.syncExec(new Runnable() {
+ public void run() {
+ if (showStyle == SHOW_PROPOSALS)
+ fProposalPopup.showProposals(true);
+ else if (showStyle == SHOW_CONTEXT_INFO)
+ fContextInfoPopup.showContextProposals(true);
+ }
+ });
+ } catch (SWTError e) {
+ }
+ }
+ }
+ };
+
+ /**
+ * The laypout manager layouts the various
+ * windows associated with the content assistant based on the
+ * settings of the content assistant.
+ */
+ class LayoutManager implements Listener {
+
+ // Presentation types.
+ public final static int LAYOUT_PROPOSAL_SELECTOR= 0;
+ public final static int LAYOUT_CONTEXT_SELECTOR= 1;
+ public final static int LAYOUT_CONTEXT_INFO_POPUP= 2;
+
+ int fContextType= LAYOUT_CONTEXT_SELECTOR;
+ Shell[] fShells= new Shell[3];
+ Object[] fPopups= new Object[3];
+
+ protected void add(Object popup, Shell shell, int type, int offset) {
+ Assert.isNotNull(popup);
+ Assert.isTrue(shell != null && !shell.isDisposed());
+ checkType(type);
+
+ if (fShells[type] != shell) {
+ if (fShells[type] != null)
+ fShells[type].removeListener(SWT.Dispose, this);
+ shell.addListener(SWT.Dispose, this);
+ fShells[type]= shell;
+ }
+
+ fPopups[type]= popup;
+ if (type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP)
+ fContextType= type;
+
+ layout(type, offset);
+ adjustListeners(type);
+ }
+
+ protected void checkType(int type) {
+ Assert.isTrue(type == LAYOUT_PROPOSAL_SELECTOR ||
+ type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP);
+ }
+
+ public void handleEvent(Event event) {
+ Widget source= event.widget;
+ source.removeListener(SWT.Dispose, this);
+
+ int type= getShellType(source);
+ checkType(type);
+ fShells[type]= null;
+
+ switch (type) {
+ case LAYOUT_PROPOSAL_SELECTOR:
+ if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
+ Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
+ // Restore event notification to the tip popup.
+ addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
+ }
+ break;
+
+ case LAYOUT_CONTEXT_SELECTOR:
+ if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+ if (fProposalPopupOrientation == PROPOSAL_STACKED)
+ layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
+ // Restore event notification to the proposal popup.
+ addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
+ }
+ fContextType= LAYOUT_CONTEXT_INFO_POPUP;
+ break;
+
+ case LAYOUT_CONTEXT_INFO_POPUP:
+ if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+ if (fContextInfoPopupOrientation == CONTEXT_INFO_BELOW)
+ layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
+ }
+ fContextType= LAYOUT_CONTEXT_SELECTOR;
+ break;
+ }
+ }
+
+ protected int getShellType(Widget shell) {
+ for (int i=0; i<fShells.length; i++) {
+ if (fShells[i] == shell)
+ return i;
+ }
+ return -1;
+ }
+
+ protected void layout(int type, int offset) {
+ switch (type) {
+ case LAYOUT_PROPOSAL_SELECTOR:
+ layoutProposalSelector(offset);
+ break;
+ case LAYOUT_CONTEXT_SELECTOR:
+ layoutContextSelector(offset);
+ break;
+ case LAYOUT_CONTEXT_INFO_POPUP:
+ layoutContextInfoPopup(offset);
+ break;
+ }
+ }
+
+ protected void layoutProposalSelector(int offset) {
+ if (fContextType == LAYOUT_CONTEXT_INFO_POPUP &&
+ fContextInfoPopupOrientation == CONTEXT_INFO_BELOW &&
+ Helper.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
+ // Stack proposal selector beneath the tip box.
+ Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+ Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+ shell.setLocation(getStackedLocation(shell, parent));
+ } else if (fContextType != LAYOUT_CONTEXT_SELECTOR ||
+ !Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
+ // There are no other presentations to be concerned with,
+ // so place the proposal selector beneath the cursor line.
+ Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+ shell.setLocation(getBelowLocation(shell, offset));
+ } else {
+ switch (fProposalPopupOrientation) {
+ case PROPOSAL_REMOVE: {
+ // Remove the tip selector and place the
+ // proposal selector beneath the cursor line.
+ fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
+ Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+ shell.setLocation(getBelowLocation(shell, offset));
+ break;
+ }
+ case PROPOSAL_OVERLAY: {
+ // Overlay the tip selector with the proposal selector.
+ Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+ shell.setLocation(getBelowLocation(shell, offset));
+ break;
+ }
+ case PROPOSAL_STACKED: {
+ // Stack the proposal selector beneath the tip selector.
+ Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+ Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
+ shell.setLocation(getStackedLocation(shell, parent));
+ break;
+ }
+ }
+ }
+ }
+
+ protected void layoutContextSelector(int offset) {
+ // Always place the context selector beneath the cursor line.
+ Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
+ shell.setLocation(getBelowLocation(shell, offset));
+
+ if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+ switch (fProposalPopupOrientation) {
+ case PROPOSAL_REMOVE:
+ // Remove the proposal selector.
+ fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
+ break;
+
+ case PROPOSAL_OVERLAY:
+ // The proposal selector has been overlayed by the tip selector.
+ break;
+
+ case PROPOSAL_STACKED: {
+ // Stack the proposal selector beneath the tip selector.
+ shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+ Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
+ shell.setLocation(getStackedLocation(shell, parent));
+ break;
+ }
+ }
+ }
+ }
+
+ protected void layoutContextInfoPopup(int offset) {
+ switch (fContextInfoPopupOrientation) {
+ case CONTEXT_INFO_ABOVE: {
+ // Place the popup above the cursor line.
+ Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+ shell.setLocation(getAboveLocation(shell, offset));
+ break;
+ }
+ case CONTEXT_INFO_BELOW: {
+ // Place the popup beneath the cursor line.
+ Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
+ parent.setLocation(getBelowLocation(parent, offset));
+ if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
+ // Stack the proposal selector beneath the context info popup.
+ Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
+ shell.setLocation(getStackedLocation(shell, parent));
+ }
+ break;
+ }
+ }
+ }
+
+ protected void shiftLeftLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) {
+ if (location.x + shellBounds.width > displayBounds.width)
+ location.x= displayBounds.width - shellBounds.width;
+ }
+
+ protected void shiftDownLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) {
+ if (location.y < displayBounds.y)
+ location.y= displayBounds.y;
+ }
+
+ protected void shiftUpLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) {
+ if (location.y + shellBounds.height > displayBounds.height)
+ location.y= displayBounds.height - shellBounds.height;
+ }
+
+ protected Point getAboveLocation(Shell shell, int offset) {
+ StyledText text= fViewer.getTextWidget();
+ Point location= text.getLocationAtOffset(offset);
+ location= text.toDisplay(location);
+
+ Rectangle shellBounds= shell.getBounds();
+ Rectangle displayBounds= shell.getDisplay().getClientArea();
+
+ location.y=location.y - shellBounds.height;
+
+ shiftLeftLocation(location, shellBounds, displayBounds);
+ shiftDownLocation(location, shellBounds, displayBounds);
+
+ return location;
+ }
+
+ protected Point getBelowLocation(Shell shell, int offset) {
+ StyledText text= fViewer.getTextWidget();
+ Point location= text.getLocationAtOffset(offset);
+ location= text.toDisplay(location);
+
+ Rectangle shellBounds= shell.getBounds();
+ Rectangle displayBounds= shell.getDisplay().getClientArea();
+
+ location.y= location.y + text.getLineHeight();
+ shiftLeftLocation(location, shellBounds, displayBounds);
+ shiftUpLocation(location, shellBounds, displayBounds);
+
+ return location;
+ }
+
+ protected Point getStackedLocation(Shell shell, Shell parent) {
+ Point p= parent.getLocation();
+ Point size= parent.getSize();
+ p.x += size.x / 4;
+ p.y += size.y;
+
+ p= parent.toDisplay(p);
+
+ Rectangle shellBounds= shell.getBounds();
+ Rectangle displayBounds= shell.getDisplay().getClientArea();
+ shiftLeftLocation(p, shellBounds, displayBounds);
+ shiftUpLocation(p, shellBounds, displayBounds);
+
+ return p;
+ }
+
+ protected void adjustListeners(int type) {
+ switch (type) {
+ case LAYOUT_PROPOSAL_SELECTOR:
+ if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
+ Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
+ // Disable event notification to the tip selector.
+ removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
+ break;
+ case LAYOUT_CONTEXT_SELECTOR:
+ if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
+ // Disable event notification to the proposal selector.
+ removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
+ break;
+ case LAYOUT_CONTEXT_INFO_POPUP:
+ break;
+ }
+ }
+ };
+
+ /**
+ * Internal key listener and event consumer.
+ */
+ class InternalListener implements VerifyKeyListener, IEventConsumer {
+
+ /**
+ * Verifies key events by notifying the registered listeners.
+ * Each listener is allowed to indicate that the event has been
+ * handled and should not be further processed.
+ *
+ * @param event the verify event
+ * @see VerifyKeyListener#verifyKey
+ */
+ public void verifyKey(VerifyEvent e) {
+ IContentAssistListener[] listeners= (IContentAssistListener[]) fListeners.clone();
+ for (int i= 0; i < listeners.length; i++) {
+ if (listeners[i] != null) {
+ if (!listeners[i].verifyKey(e) || !e.doit)
+ return;
+ }
+ }
+ }
+
+ /*
+ * @see IEventConsumer#processEvent
+ */
+ public void processEvent(VerifyEvent event) {
+
+ installKeyListener();
+
+ IContentAssistListener[] listeners= (IContentAssistListener[])fListeners.clone();
+ for (int i= 0; i < listeners.length; i++) {
+ if (listeners[i] != null) {
+ listeners[i].processEvent(event);
+ if (!event.doit)
+ return;
+ }
+ }
+ }
+ };
+
+
+ // Content-Assist Listener types
+ final static int CONTEXT_SELECTOR= 0;
+ final static int PROPOSAL_SELECTOR= 1;
+ final static int CONTEXT_INFO_POPUP= 2;
+
+ private IInformationControlCreator fInformationControlCreator;
+ private int fAutoActivationDelay= 500;
+ private boolean fIsAutoActivated= false;
+ private boolean fIsAutoInserting= false;
+ private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
+ private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
+ private Map fProcessors;
+
+ private Color fContextInfoPopupBackground;
+ private Color fContextInfoPopupForeground;
+ private Color fContextSelectorBackground;
+ private Color fContextSelectorForeground;
+ private Color fProposalSelectorBackground;
+ private Color fProposalSelectorForeground;
+
+ private ITextViewer fViewer;
+ private String fLastErrorMessage;
+
+ private Closer fCloser;
+ private LayoutManager fLayoutManager;
+ private AutoAssistListener fAutoAssistListener;
+ private InternalListener fInternalListener;
+ private CompletionProposalPopup fProposalPopup;
+ private ContextInformationPopup fContextInfoPopup;
+
+ private boolean fKeyListenerHooked= false;
+ private IContentAssistListener[] fListeners= new IContentAssistListener[4];
+
+ /**
+ * Creates a new content assistant. The content assistant is not automatically activated,
+ * overlays the completion proposals with context information list if necessary, and
+ * shows the context information above the location at which it was activated. If auto
+ * activation will be enabled, without further configuration steps, this content assistant
+ * is activated after a 500 ms delay.
+ */
+ public ContentAssistant() {
+ }
+
+ /**
+ * Registers a given content assist processor for a particular content type.
+ * If there is already a processor registered for this type, the new processor
+ * is registered instead of the old one.
+ *
+ * @param processor the content assist processor to register, or <code>null</code> to remove an existing one
+ * @param contentType the content type under which to register
+ */
+ public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fProcessors == null)
+ fProcessors= new HashMap();
+
+ if (processor == null)
+ fProcessors.remove(contentType);
+ else
+ fProcessors.put(contentType, processor);
+ }
+
+ /*
+ * @see IContentAssistant#getContentAssistProcessor
+ */
+ public IContentAssistProcessor getContentAssistProcessor(String contentType) {
+ if (fProcessors == null)
+ return null;
+
+ return (IContentAssistProcessor) fProcessors.get(contentType);
+ }
+
+ /**
+ * Enables the content assistant's auto activation mode.
+ *
+ * @param enabled indicates whether auto activation is enabled or not
+ */
+ public void enableAutoActivation(boolean enabled) {
+ fIsAutoActivated= enabled;
+ manageAutoActivation(fIsAutoActivated);
+ }
+
+ /**
+ * Enables the content assistant's auto insertion mode. If enabled,
+ * the content assistant inserts a proposal automatically if it is
+ * the only proposal. In the case of ambiguities, the user must
+ * make the choice.
+ *
+ * @param enabled indicates whether auto insertion is enabled or not
+ * @since 2.0
+ */
+ public void enableAutoInsert(boolean enabled) {
+ fIsAutoInserting= enabled;
+ }
+
+ /**
+ * Returns whether this content assistant is in the auto insertion
+ * mode or not.
+ *
+ * @return <code>true</code> if in auto insertion mode
+ * @since 2.0
+ */
+ boolean isAutoInserting() {
+ return fIsAutoInserting;
+ }
+
+ /**
+ * Installs and uninstall the listeners needed for autoactivation.
+ * @param start <code>true</code> if listeners must be installed,
+ * <code>false</code> if they must be removed
+ * @since 2.0
+ */
+ private void manageAutoActivation(boolean start) {
+ if (start) {
+
+ if (fViewer != null && fAutoAssistListener == null) {
+ fAutoAssistListener= new AutoAssistListener();
+ if (fViewer instanceof ITextViewerExtension) {
+ ITextViewerExtension extension= (ITextViewerExtension) fViewer;
+ extension.appendVerifyKeyListener(fAutoAssistListener);
+ } else {
+ StyledText textWidget= fViewer.getTextWidget();
+ if (Helper.okToUse(textWidget))
+ textWidget.addVerifyKeyListener(fAutoAssistListener);
+ }
+ }
+
+ } else if (fAutoAssistListener != null) {
+
+ if (fViewer instanceof ITextViewerExtension) {
+ ITextViewerExtension extension= (ITextViewerExtension) fViewer;
+ extension.removeVerifyKeyListener(fAutoAssistListener);
+ } else {
+ StyledText textWidget= fViewer.getTextWidget();
+ if (Helper.okToUse(textWidget))
+ textWidget.removeVerifyKeyListener(fAutoAssistListener);
+ }
+
+ fAutoAssistListener= null;
+ }
+ }
+
+ /**
+ * Sets the delay after which the content assistant is automatically invoked
+ * if the cursor is behind an auto activation character.
+ *
+ * @param delay the auto activation delay
+ */
+ public void setAutoActivationDelay(int delay) {
+ fAutoActivationDelay= delay;
+ }
+
+ /**
+ * Sets the proposal popups' orientation.
+ * The following values may be used:
+ * <ul>
+ * <li>PROPOSAL_OVERLAY<p>
+ * proposal popup windows should overlay each other
+ * </li>
+ * <li>PROPOSAL_REMOVE<p>
+ * any currently shown proposal popup should be closed
+ * </li>
+ * <li>PROPOSAL_STACKED<p>
+ * proposal popup windows should be vertical stacked, with no overlap,
+ * beneath the line containing the current cursor location
+ * </li>
+ * </ul>
+ *
+ * @param orientation the popup's orientation
+ */
+ public void setProposalPopupOrientation(int orientation) {
+ fProposalPopupOrientation= orientation;
+ }
+
+ /**
+ * Sets the context information popup's orientation.
+ * The following values may be used:
+ * <ul>
+ * <li>CONTEXT_ABOVE<p>
+ * context information popup should always appear above the line containing
+ * the current cursor location
+ * </li>
+ * <li>CONTEXT_BELOW<p>
+ * context information popup should always appear below the line containing
+ * the current cursor location
+ * </li>
+ * </ul>
+ *
+ * @param orientation the popup's orientation
+ */
+ public void setContextInformationPopupOrientation(int orientation) {
+ fContextInfoPopupOrientation= orientation;
+ }
+
+ /**
+ * Sets the context information popup's background color.
+ *
+ * @param background the background color
+ */
+ public void setContextInformationPopupBackground(Color background) {
+ fContextInfoPopupBackground= background;
+ }
+
+ /**
+ * Returns the background of the context information popup.
+ *
+ * @return the background of the context information popup
+ * @since 2.0
+ */
+ Color getContextInformationPopupBackground() {
+ return fContextInfoPopupBackground;
+ }
+
+ /**
+ * Sets the context information popup's foreground color.
+ *
+ * @param foreground the foreground color
+ * @since 2.0
+ */
+ public void setContextInformationPopupForeground(Color foreground) {
+ fContextInfoPopupForeground= foreground;
+ }
+
+ /**
+ * Returns the foreground of the context information popup.
+ *
+ * @return the foreground of the context information popup
+ * @since 2.0
+ */
+ Color getContextInformationPopupForeground() {
+ return fContextInfoPopupForeground;
+ }
+
+ /**
+ * Sets the proposal selector's background color.
+ *
+ * @param background the background color
+ * @since 2.0
+ */
+ public void setProposalSelectorBackground(Color background) {
+ fProposalSelectorBackground= background;
+ }
+
+ /**
+ * Returns the background of the proposal selector.
+ *
+ * @return the background of the proposal selector
+ * @since 2.0
+ */
+ Color getProposalSelectorBackground() {
+ return fProposalSelectorBackground;
+ }
+
+ /**
+ * Sets the proposal's foreground color.
+ *
+ * @param foreground the foreground color
+ * @since 2.0
+ */
+ public void setProposalSelectorForeground(Color foreground) {
+ fProposalSelectorForeground= foreground;
+ }
+
+ /**
+ * Returns the foreground of the proposal selector.
+ *
+ * @return the foreground of the proposal selector
+ * @since 2.0
+ */
+ Color getProposalSelectorForeground() {
+ return fProposalSelectorForeground;
+ }
+
+ /**
+ * Sets the context selector's background color.
+ *
+ * @param background the background color
+ * @since 2.0
+ */
+ public void setContextSelectorBackground(Color background) {
+ fContextSelectorBackground= background;
+ }
+
+ /**
+ * Returns the background of the context selector.
+ *
+ * @return the background of the context selector
+ * @since 2.0
+ */
+ Color getContextSelectorBackground() {
+ return fContextSelectorBackground;
+ }
+
+ /**
+ * Sets the context selector's foreground color.
+ *
+ * @param foreground the foreground color
+ * @since 2.0
+ */
+ public void setContextSelectorForeground(Color foreground) {
+ fContextSelectorForeground= foreground;
+ }
+
+ /**
+ * Returns the foreground of the context selector.
+ *
+ * @return the foreground of the context selector
+ * @since 2.0
+ */
+ Color getContextSelectorForeground() {
+ return fContextSelectorForeground;
+ }
+
+ /**
+ * Sets the information control creator for the additional information control.
+ *
+ * @param creator the information control creator for the additional information control
+ * @since 2.0
+ */
+ public void setInformationControlCreator(IInformationControlCreator creator) {
+ fInformationControlCreator= creator;
+ }
+
+ /*
+ * @see IContentAssist#install
+ */
+ public void install(ITextViewer textViewer) {
+ Assert.isNotNull(textViewer);
+
+ fViewer= textViewer;
+
+ fLayoutManager= new LayoutManager();
+ fInternalListener= new InternalListener();
+
+ AdditionalInfoController controller= null;
+ if (fInformationControlCreator != null)
+ controller= new AdditionalInfoController(fInformationControlCreator, Math.round(fAutoActivationDelay * 1.5f));
+
+ fContextInfoPopup= new ContextInformationPopup(this, fViewer);
+ fProposalPopup= new CompletionProposalPopup(this, fViewer, controller);
+
+ manageAutoActivation(fIsAutoActivated);
+ }
+
+ /*
+ * @see IContentAssist#uninstall
+ */
+ public void uninstall() {
+
+ if (fProposalPopup != null)
+ fProposalPopup.hide();
+
+ if (fContextInfoPopup != null)
+ fContextInfoPopup.hide();
+
+ manageAutoActivation(false);
+
+ fViewer= null;
+ }
+
+ /**
+ * Adds the given shell of the specified type to the layout.
+ * Valid types are defined by <code>LayoutManager</code>.
+ *
+ * @param popup a content assist popup
+ * @param shell the shell of the content-assist popup
+ * @param type the type of popup
+ * @param visibleOffset the offset at which to layout the popup relative to the offset of the viewer's visible region
+ * @since 2.0
+ */
+ void addToLayout(Object popup, Shell shell, int type, int visibleOffset) {
+ fLayoutManager.add(popup, shell, type, visibleOffset);
+ }
+
+ /**
+ * Layouts the registered popup of the given type relative to the
+ * given offset. The offset is relative to the offset of the viewer's visible region.
+ * Valid types are defined by <code>LayoutManager</code>.
+ *
+ * @param type the type of popup to layout
+ * @param visibleOffset the offset at which to layout relative to the offset of the viewer's visible region
+ * @since 2.0
+ */
+ void layout(int type, int visibleOffset) {
+ fLayoutManager.layout(type, visibleOffset);
+ }
+
+ /**
+ * Notifies the controller that a popup has lost focus.
+ *
+ * @param e the focus event
+ */
+ void popupFocusLost(FocusEvent e) {
+ fCloser.focusLost(e);
+ }
+
+ /**
+ * Returns the offset of the selection relative to the offset of the visible region.
+ *
+ * @return the offset of the selection relative to the offset of the visible region
+ * @since 2.0
+ */
+ int getSelectionOffset() {
+ StyledText text= fViewer.getTextWidget();
+ return text.getSelectionRange().x;
+ }
+
+ /**
+ * Returns whether the widget token could be acquired.
+ * The following are valid listener types:
+ * <ul>
+ * <li>AUTO_ASSIST
+ * <li>CONTEXT_SELECTOR
+ * <li>PROPOSAL_SELECTOR
+ * <li>CONTEXT_INFO_POPUP
+ * <ul>
+ * @param type the listener type for which to acquire
+ * @return <code>true</code> if the widget token could be acquired
+ * @since 2.0
+ */
+ private boolean acquireWidgetToken(int type) {
+ switch (type) {
+ case CONTEXT_SELECTOR:
+ case PROPOSAL_SELECTOR:
+ if (fViewer instanceof IWidgetTokenOwner) {
+ IWidgetTokenOwner owner= (IWidgetTokenOwner) fViewer;
+ return owner.requestWidgetToken(this);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Registers a content assist listener.
+ * The following are valid listener types:
+ * <ul>
+ * <li>AUTO_ASSIST
+ * <li>CONTEXT_SELECTOR
+ * <li>PROPOSAL_SELECTOR
+ * <li>CONTEXT_INFO_POPUP
+ * <ul>
+ * Returns whether the listener could be added successfully. A listener
+ * can not be added if the widget token could not be acquired.
+ *
+ * @param listener the listener to register
+ * @param type the type of listener
+ * @return <code>true</code> if the listener could be added
+ */
+ boolean addContentAssistListener(IContentAssistListener listener, int type) {
+
+ if (acquireWidgetToken(type)) {
+
+ fListeners[type]= listener;
+
+ if (getNumberOfListeners() == 1) {
+ fCloser= new Closer();
+ fCloser.install();
+ fViewer.setEventConsumer(fInternalListener);
+ installKeyListener();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Installs a key listener on the text viewer's widget.
+ */
+ private void installKeyListener() {
+ if (!fKeyListenerHooked) {
+ StyledText text= fViewer.getTextWidget();
+ if (Helper.okToUse(text)) {
+
+ if (fViewer instanceof ITextViewerExtension) {
+ ITextViewerExtension e= (ITextViewerExtension) fViewer;
+ e.prependVerifyKeyListener(fInternalListener);
+ } else {
+ text.addVerifyKeyListener(fInternalListener);
+ }
+
+ fKeyListenerHooked= true;
+ }
+ }
+ }
+
+ /**
+ * Releases the previously acquired widget token if the token
+ * is no longer necessary.
+ * The following are valid listener types:
+ * <ul>
+ * <li>AUTO_ASSIST
+ * <li>CONTEXT_SELECTOR
+ * <li>PROPOSAL_SELECTOR
+ * <li>CONTEXT_INFO_POPUP
+ * <ul>
+ *
+ * @param type the listener type
+ * @since 2.0
+ */
+ private void releaseWidgetToken(int type) {
+ if (fListeners[CONTEXT_SELECTOR] == null && fListeners[PROPOSAL_SELECTOR] == null) {
+ if (fViewer instanceof IWidgetTokenOwner) {
+ IWidgetTokenOwner owner= (IWidgetTokenOwner) fViewer;
+ owner.releaseWidgetToken(this);
+ }
+ }
+ }
+
+ /**
+ * Unregisters a content assist listener.
+ *
+ * @param listener the listener to unregister
+ * @param type the type of listener
+ *
+ * @see #addContentAssistListener
+ */
+ void removeContentAssistListener(IContentAssistListener listener, int type) {
+ fListeners[type]= null;
+
+ if (getNumberOfListeners() == 0) {
+
+ if (fCloser != null) {
+ fCloser.uninstall();
+ fCloser= null;
+ }
+
+ uninstallKeyListener();
+ fViewer.setEventConsumer(null);
+ }
+
+ releaseWidgetToken(type);
+ }
+
+ /**
+ * Uninstall the key listener from the text viewer's widget.
+ */
+ private void uninstallKeyListener() {
+ if (fKeyListenerHooked) {
+ StyledText text= fViewer.getTextWidget();
+ if (Helper.okToUse(text)) {
+
+ if (fViewer instanceof ITextViewerExtension) {
+ ITextViewerExtension e= (ITextViewerExtension) fViewer;
+ e.removeVerifyKeyListener(fInternalListener);
+ } else {
+ text.removeVerifyKeyListener(fInternalListener);
+ }
+
+ fKeyListenerHooked= false;
+ }
+ }
+ }
+
+ /**
+ * Returns the number of listeners.
+ *
+ * @return the number of listeners
+ * @since 2.0
+ */
+ private int getNumberOfListeners() {
+ int count= 0;
+ for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) {
+ if (fListeners[i] != null)
+ ++ count;
+ }
+ return count;
+ }
+
+ /*
+ * @see IContentAssist#showPossibleCompletions
+ */
+ public String showPossibleCompletions() {
+ return fProposalPopup.showProposals(false);
+ }
+
+ /*
+ * @see IContentAssist#showContextInformation
+ */
+ public String showContextInformation() {
+ return fContextInfoPopup.showContextProposals(false);
+ }
+
+ /**
+ * Requests that the specified context information to be shown.
+ *
+ * @param contextInformation the context information to be shown
+ * @param position the position to which the context information refers to
+ * @since 2.0
+ */
+ void showContextInformation(IContextInformation contextInformation, int position) {
+ fContextInfoPopup.showContextInformation(contextInformation, position);
+ }
+
+ /**
+ * Returns the current content assist error message.
+ *
+ * @return an error message or <code>null</code> if no error has occurred
+ */
+ String getErrorMessage() {
+ return fLastErrorMessage;
+ }
+
+ /**
+ * Returns the content assist processor for the content
+ * type of the specified document position.
+ *
+ * @param document the document
+ * @param position a position within the document
+ * @return a content-assist processor or <code>null</code> if none exists
+ */
+ private IContentAssistProcessor getProcessor(IDocument document, int position) {
+ try {
+ String type= document.getContentType(position);
+ return getContentAssistProcessor(type);
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns an array of completion proposals computed based on
+ * the specified document position. The position is used to
+ * determine the appropriate content assist processor to invoke.
+ *
+ * @param viewer the viewer for which to compute the prosposals
+ * @param position a document position
+ * @return an array of completion proposals
+ *
+ * @see IContentAssistProcessor#computeCompletionProposals
+ */
+ ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int position) {
+ fLastErrorMessage= null;
+
+ ICompletionProposal[] result= null;
+
+ IContentAssistProcessor p= getProcessor(viewer.getDocument(), position);
+ if (p != null) {
+ result= p.computeCompletionProposals(viewer, position);
+ fLastErrorMessage= p.getErrorMessage();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns an array of context information objects computed based
+ * on the specified document position. The position is used to determine
+ * the appropriate content assist processor to invoke.
+ *
+ * @param viewer the viewer for which to compute the context information
+ * @param position a document position
+ * @return an array of context information objects
+ *
+ * @see IContentAssistProcessor#computeContextInformation
+ */
+ IContextInformation[] computeContextInformation(ITextViewer viewer, int position) {
+ fLastErrorMessage= null;
+
+ IContextInformation[] result= null;
+
+ IContentAssistProcessor p= getProcessor(viewer.getDocument(), position);
+ if (p != null) {
+ result= p.computeContextInformation(viewer, position);
+ fLastErrorMessage= p.getErrorMessage();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the context information validator that should be used to
+ * determine when the currently displayed context information should
+ * be dismissed. The position is used to determine the appropriate
+ * content assist processor to invoke.
+ *
+ * @param document the document
+ * @param position a document position
+ * @return an validator
+ *
+ * @see IContentAssistProcessor#getContextInformationValidator
+ */
+ IContextInformationValidator getContextInformationValidator(IDocument document, int position) {
+ IContentAssistProcessor p= getProcessor(document, position);
+ if (p != null)
+ return p.getContextInformationValidator();
+ return null;
+ }
+
+ /**
+ * Returns the context information presenter that should be used to
+ * display context information. The position is used to determine the appropriate
+ * content assist processor to invoke.
+ *
+ * @param document the document
+ * @param position a document position
+ * @return a presenter
+ * @since 2.0
+ */
+ IContextInformationPresenter getContextInformationPresenter(IDocument document, int position) {
+ IContextInformationValidator validator= getContextInformationValidator(document, position);
+ if (validator instanceof IContextInformationPresenter)
+ return (IContextInformationPresenter) validator;
+ return null;
+ }
+
+ /**
+ * Returns the characters which when typed by the user should automatically
+ * initiate proposing completions. The position is used to determine the
+ * appropriate content assist processor to invoke.
+ *
+ * @param document the document
+ * @param position a document position
+ * @return the auto activation characters
+ *
+ * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters
+ */
+ private char[] getCompletionProposalAutoActivationCharacters(IDocument document, int position) {
+ IContentAssistProcessor p= getProcessor(document, position);
+ if (p != null)
+ return p.getCompletionProposalAutoActivationCharacters();
+ return null;
+ }
+
+ /**
+ * Returns the characters which when typed by the user should automatically
+ * initiate the presentation of context information. The position is used
+ * to determine the appropriate content assist processor to invoke.
+ *
+ * @param document the document
+ * @param position a document position
+ * @return the auto activation characters
+ *
+ * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters
+ */
+ private char[] getContextInformationAutoActivationCharacters(IDocument document, int position) {
+ IContentAssistProcessor p= getProcessor(document, position);
+ if (p != null)
+ return p.getContextInformationAutoActivationCharacters();
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+ * @since 2.0
+ */
+ public boolean requestWidgetToken(IWidgetTokenOwner owner) {
+ return false;
+ }
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformation.java
new file mode 100644
index 000000000..8dbc83dd1
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformation.java
@@ -0,0 +1,86 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.jface.util.Assert;
+
+
+/**
+ * A default implementation of the <code>IContextInformation</code> interface.
+ */
+public final class ContextInformation implements IContextInformation {
+
+ private String fContextDisplayString;
+ private String fInformationDisplayString;
+ private Image fImage;
+
+ /**
+ * Creates a new context information without an image.
+ *
+ * @param contextDisplayString the string to be used when presenting the context
+ * @param informationDisplayString the string to be displayed when presenting the context information
+ */
+ public ContextInformation(String contextDisplayString, String informationDisplayString) {
+ this(null, contextDisplayString, informationDisplayString);
+ }
+
+ /**
+ * Creates a new context information with an image.
+ *
+ * @param image the image to display when presenting the context information
+ * @param contextDisplayString the string to be used when presenting the context
+ * @param informationDisplayString the string to be displayed when presenting the context information,
+ * may not be <code>null</code>
+ */
+ public ContextInformation(Image image, String contextDisplayString, String informationDisplayString) {
+
+ Assert.isNotNull(informationDisplayString);
+
+ fImage= image;
+ fContextDisplayString= contextDisplayString;
+ fInformationDisplayString= informationDisplayString;
+ }
+
+ /*
+ * @see IContextInformation#equals
+ */
+ public boolean equals(Object object) {
+ if (object instanceof IContextInformation) {
+ IContextInformation contextInformation= (IContextInformation) object;
+ boolean equals= fInformationDisplayString.equalsIgnoreCase(contextInformation.getInformationDisplayString());
+ if (fContextDisplayString != null)
+ equals= equals && fContextDisplayString.equalsIgnoreCase(contextInformation.getContextDisplayString());
+ return equals;
+ }
+ return false;
+ }
+
+ /*
+ * @see IContextInformation#getInformationDisplayString()
+ */
+ public String getInformationDisplayString() {
+ return fInformationDisplayString;
+ }
+
+ /*
+ * @see IContextInformation#getImage()
+ */
+ public Image getImage() {
+ return fImage;
+ }
+
+ /*
+ * @see IContextInformation#getContextDisplayString()
+ */
+ public String getContextDisplayString() {
+ if (fContextDisplayString != null)
+ return fContextDisplayString;
+ return fInformationDisplayString;
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationPopup.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationPopup.java
new file mode 100644
index 000000000..540024c1f
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationPopup.java
@@ -0,0 +1,626 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+ import java.util.Iterator;
+
+import java.util.Stack;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.contentassist.ContentAssistant.LayoutManager;
+
+
+/**
+ * This class is used to present context information to the user.
+ * If multiple contexts are valid at the current cursor location,
+ * a list is presented from which the user may choose one context.
+ * Once the user makes their choice, or if there was only a single
+ * possible context, the context information is shown in a tooltip like popup. <p>
+ * If the tooltip is visible and the user wants to see context information of
+ * a context embedded into the one for which context information is displayed,
+ * context information for the embedded context is shown. As soon as the
+ * cursor leaves the embedded context area, the context information for
+ * the embedding context is shown again.
+ *
+ * @see IContextInformation
+ * @see IContextInformationValidator
+ */
+class ContextInformationPopup implements IContentAssistListener {
+
+
+
+ /**
+ * Represents the state necessary for embedding contexts.
+ * @since 2.0
+ */
+ static class ContextFrame {
+ public int fBeginOffset;
+ public int fOffset;
+ public int fVisibleOffset;
+ public IContextInformation fInformation;
+ public IContextInformationValidator fValidator;
+ public IContextInformationPresenter fPresenter;
+ };
+
+ private ITextViewer fViewer;
+ private ContentAssistant fContentAssistant;
+ private int fListenerCount= 0;
+
+ private PopupCloser fPopupCloser= new PopupCloser();
+ private Shell fContextSelectorShell;
+ private Table fContextSelectorTable;
+ private IContextInformation[] fContextSelectorInput;
+ private String fLineDelimiter= null;
+
+ private Shell fContextInfoPopup;
+ private StyledText fContextInfoText;
+ private TextPresentation fTextPresentation;
+
+ private Stack fContextFrameStack= new Stack();
+
+
+ /**
+ * Creates a new context information popup.
+ *
+ * @param contentAssistant the content assist for computing the context information
+ * @param viewer the viewer on top of which the context information is shown
+ */
+ public ContextInformationPopup(ContentAssistant contentAssistant, ITextViewer viewer) {
+ fContentAssistant= contentAssistant;
+ fViewer= viewer;
+ }
+
+ /**
+ * Shows all possible contexts for the given cursor position of the viewer.
+ *
+ * @param autoActivated <code>true</code> if auto activated
+ * @return a potential error message or <code>null</code> in case of no error
+ */
+ public String showContextProposals(final boolean autoActivated) {
+ final StyledText styledText= fViewer.getTextWidget();
+ BusyIndicator.showWhile(styledText.getDisplay(), new Runnable() {
+ public void run() {
+
+ int position= fViewer.getSelectedRange().x;
+
+ IContextInformation[] contexts= computeContextInformation(position);
+ int count = (contexts == null ? 0 : contexts.length);
+ if (count == 1) {
+
+ // Show context information directly
+ internalShowContextInfo(contexts[0], position);
+
+ } else if (count > 0) {
+ // Precise context must be selected
+
+ if (fLineDelimiter == null)
+ fLineDelimiter= styledText.getLineDelimiter();
+
+ createContextSelector();
+ setContexts(contexts);
+ displayContextSelector();
+ hideContextInfoPopup();
+
+ } else if (!autoActivated) {
+ styledText.getDisplay().beep();
+ }
+ }
+ });
+
+ return getErrorMessage();
+ }
+
+ /**
+ * Displays the given context information for the given offset.
+ *
+ * @param info the context information
+ * @param position the offset
+ * @since 2.0
+ */
+ public void showContextInformation(final IContextInformation info, final int position) {
+ Control control= fViewer.getTextWidget();
+ BusyIndicator.showWhile(control.getDisplay(), new Runnable() {
+ public void run() {
+ internalShowContextInfo(info, position);
+ hideContextSelector();
+ }
+ });
+ }
+
+ /**
+ * Displays the given context information for the given offset.
+ *
+ * @param info the context information
+ * @param position the offset
+ * @since 2.0
+ */
+
+ private void internalShowContextInfo(IContextInformation information, int offset) {
+
+ IContextInformationValidator validator= fContentAssistant.getContextInformationValidator(fViewer.getDocument(), offset);
+
+ if (validator != null) {
+ ContextFrame current= new ContextFrame();
+ current.fInformation= information;
+ current.fBeginOffset= (information instanceof IContextInformationExtension) ? ((IContextInformationExtension) information).getContextInformationPosition() : offset;
+ if (current.fBeginOffset == -1) current.fBeginOffset= offset;
+ current.fOffset= offset;
+ current.fVisibleOffset= fViewer.getTextWidget().getSelectionRange().x - (offset - current.fBeginOffset);
+ current.fValidator= validator;
+ current.fPresenter= fContentAssistant.getContextInformationPresenter(fViewer.getDocument(), offset);
+
+ fContextFrameStack.push(current);
+
+ internalShowContextFrame(current, fContextFrameStack.size() == 1);
+ }
+ }
+
+ /**
+ * Shows the given context frame.
+ *
+ * @param frame the frane to display
+ * @param initial <code>true</code> if this is the first frame to be displayed
+ * @since 2.0
+ */
+ private void internalShowContextFrame(ContextFrame frame, boolean initial) {
+
+ frame.fValidator.install(frame.fInformation, fViewer, frame.fOffset);
+
+ if (frame.fPresenter != null) {
+ if (fTextPresentation == null)
+ fTextPresentation= new TextPresentation();
+ frame.fPresenter.install(frame.fInformation, fViewer, frame.fBeginOffset);
+ frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation);
+ }
+
+ createContextInfoPopup();
+
+ fContextInfoText.setText(frame.fInformation.getInformationDisplayString());
+ if (fTextPresentation != null)
+ TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+ resize();
+
+ if (initial) {
+ if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP)) {
+ fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+ fContextInfoPopup.setVisible(true);
+ }
+ } else {
+ fContentAssistant.layout(ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+ }
+ }
+
+ /**
+ * Computes all possible context information for the given offset.
+ *
+ * @param position the offset
+ * @return all possible context information for the given offset
+ * @since 2.0
+ */
+ private IContextInformation[] computeContextInformation(int position) {
+ return fContentAssistant.computeContextInformation(fViewer, position);
+ }
+
+ /**
+ *Returns the error message generated while computing context information.
+ *
+ * @return the error message
+ */
+ private String getErrorMessage() {
+ return fContentAssistant.getErrorMessage();
+ }
+
+ /**
+ * Creates the context information popup. This is the tooltip like overlay window.
+ */
+ private void createContextInfoPopup() {
+ if (Helper.okToUse(fContextInfoPopup))
+ return;
+
+ Control control= fViewer.getTextWidget();
+ Display display= control.getDisplay();
+
+ fContextInfoPopup= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
+ fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
+
+ fContextInfoText= new StyledText(fContextInfoPopup, SWT.MULTI | SWT.READ_ONLY);
+
+ Color c= fContentAssistant.getContextInformationPopupBackground();
+ if (c == null)
+ c= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+ fContextInfoText.setBackground(c);
+
+ c= fContentAssistant.getContextInformationPopupForeground();
+ if (c == null)
+ c= display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+ fContextInfoText.setForeground(c);
+ }
+
+ /**
+ * Resizes the context information popup.
+ * @since 2.0
+ */
+ private void resize() {
+ Point size= fContextInfoText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+ size.x += 3;
+ fContextInfoText.setSize(size);
+ fContextInfoText.setLocation(1,1);
+ size.x += 2;
+ size.y += 2;
+ fContextInfoPopup.setSize(size);
+ }
+
+ /**
+ *Hides the context information popup.
+ */
+ private void hideContextInfoPopup() {
+
+ if (Helper.okToUse(fContextInfoPopup)) {
+
+ int size= fContextFrameStack.size();
+ if (size > 0) {
+ fContextFrameStack.pop();
+ -- size;
+ }
+
+ if (size > 0) {
+ ContextFrame current= (ContextFrame) fContextFrameStack.peek();
+ internalShowContextFrame(current, false);
+ } else {
+
+ fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP);
+
+ fContextInfoPopup.setVisible(false);
+ fContextInfoPopup.dispose();
+ fContextInfoPopup= null;
+
+ if (fTextPresentation != null) {
+ fTextPresentation.clear();
+ fTextPresentation= null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates the context selector in case the user has the choice between multiple valid contexts
+ * at a given offset.
+ */
+ private void createContextSelector() {
+ if (Helper.okToUse(fContextSelectorShell))
+ return;
+
+ Control control= fViewer.getTextWidget();
+ fContextSelectorShell= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP);
+ fContextSelectorTable= new Table(fContextSelectorShell, SWT.H_SCROLL | SWT.V_SCROLL);
+
+ int height= fContextSelectorTable.getItemHeight() * 10;
+ fContextSelectorShell.setSize(302, height + 2);
+ fContextSelectorTable.setSize(300, height);
+ fContextSelectorTable.setLocation(1, 1);
+
+ fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
+
+ Color c= fContentAssistant.getContextSelectorBackground();
+ if (c == null)
+ c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+ fContextSelectorTable.setBackground(c);
+
+ c= fContentAssistant.getContextSelectorForeground();
+ if (c == null)
+ c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+ fContextSelectorTable.setForeground(c);
+
+ fContextSelectorTable.addSelectionListener(new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ insertSelectedContext();
+ hideContextSelector();
+ }
+ });
+
+ fPopupCloser.install(fContentAssistant, fContextSelectorTable);
+
+ fContextSelectorTable.setHeaderVisible(false);
+ fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset());
+ }
+
+ /**
+ * Causes the context information of the context selected in the context selector
+ * to be displayed in the context information popup.
+ */
+ private void insertSelectedContext() {
+ int i= fContextSelectorTable.getSelectionIndex();
+
+ if (i < 0 || i >= fContextSelectorInput.length)
+ return;
+
+ int position= fViewer.getSelectedRange().x;
+ internalShowContextInfo(fContextSelectorInput[i], position);
+ }
+
+ /**
+ * Sets the contexts in the context selector to the given set.
+ *
+ * @param contexts teh possible contexts
+ */
+ private void setContexts(IContextInformation[] contexts) {
+ if (Helper.okToUse(fContextSelectorTable)) {
+
+ fContextSelectorInput= contexts;
+
+ fContextSelectorTable.setRedraw(false);
+ fContextSelectorTable.removeAll();
+
+ TableItem item;
+ IContextInformation t;
+ for (int i= 0; i < contexts.length; i++) {
+ t= contexts[i];
+ item= new TableItem(fContextSelectorTable, SWT.NULL);
+ if (t.getImage() != null)
+ item.setImage(t.getImage());
+ item.setText(t.getContextDisplayString());
+ }
+
+ fContextSelectorTable.select(0);
+ fContextSelectorTable.setRedraw(true);
+ }
+ }
+
+ /**
+ * Displays the context selector.
+ */
+ private void displayContextSelector() {
+ if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR))
+ fContextSelectorShell.setVisible(true);
+ }
+
+ /**
+ * Hodes the context selector.
+ */
+ private void hideContextSelector() {
+ if (Helper.okToUse(fContextSelectorShell)) {
+ fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR);
+
+ fPopupCloser.uninstall();
+ fContextSelectorShell.setVisible(false);
+ fContextSelectorShell.dispose();
+ fContextSelectorShell= null;
+ }
+ }
+
+ /**
+ *Returns whether the context selector has the focus.
+ * @return <code>true</code> if teh context selector has the focus
+ */
+ public boolean hasFocus() {
+ if (Helper.okToUse(fContextSelectorShell))
+ return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl());
+
+ return false;
+ }
+
+ /**
+ * Hides context selector and context information popup.
+ */
+ public void hide() {
+ hideContextSelector();
+ hideContextInfoPopup();
+ }
+
+ /**
+ * Returns whether this context information popup is active. I.e., either
+ * a context selector or context information is displayed.
+ *
+ * @return <code>true</code> if the context selector is active
+ */
+ public boolean isActive() {
+ return (Helper.okToUse(fContextInfoPopup) || Helper.okToUse(fContextSelectorShell));
+ }
+
+ /*
+ * @see IContentAssistListener#verifyKey(VerifyEvent)
+ */
+ public boolean verifyKey(VerifyEvent e) {
+ if (Helper.okToUse(fContextSelectorShell))
+ return contextSelectorKeyPressed(e);
+ if (Helper.okToUse(fContextInfoPopup))
+ return contextInfoPopupKeyPressed(e);
+ return true;
+ }
+
+ /**
+ * Processes a key stroke in the context selector.
+ *
+ * @param e the verify event describing the key stroke
+ * @return <code>true</code> if processing can be stopped
+ */
+ private boolean contextSelectorKeyPressed(VerifyEvent e) {
+
+ char key= e.character;
+ if (key == 0) {
+
+ int change;
+ int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
+ int selection= fContextSelectorTable.getSelectionIndex();
+
+ switch (e.keyCode) {
+
+ case SWT.ARROW_UP:
+ change= (fContextSelectorTable.getSelectionIndex() > 0 ? -1 : 0);
+ break;
+
+ case SWT.ARROW_DOWN:
+ change= (fContextSelectorTable.getSelectionIndex() < fContextSelectorTable.getItemCount() - 1 ? 1 : 0);
+ break;
+
+ case SWT.PAGE_DOWN :
+ change= visibleRows;
+ if (selection + change >= fContextSelectorTable.getItemCount())
+ change= fContextSelectorTable.getItemCount() - selection;
+ break;
+
+ case SWT.PAGE_UP :
+ change= -visibleRows;
+ if (selection + change < 0)
+ change= -selection;
+ break;
+
+ case SWT.HOME :
+ change= -selection;
+ break;
+
+ case SWT.END :
+ change= fContextSelectorTable.getItemCount() - selection;
+ break;
+
+ case SWT.CTRL:
+ case SWT.SHIFT:
+ return true;
+ default:
+ hideContextSelector();
+ return true;
+ }
+
+ fContextSelectorTable.setSelection(selection + change);
+ fContextSelectorTable.showSelection();
+ e.doit= false;
+ return false;
+
+ } else if ('\t' == key) {
+ // switch focus to selector shell
+ e.doit= false;
+ fContextSelectorShell.setFocus();
+ return false;
+ } else if (key == 0x1B) {
+ // terminate on Esc
+ hideContextSelector();
+ }
+
+ return true;
+ }
+
+ /**
+ * Processes a key stroke while the info popup is up.
+ *
+ * @param e the verify event describing the key stroke
+ * @return <code>true</code> if processing can be stopped
+ */
+ private boolean contextInfoPopupKeyPressed(KeyEvent e) {
+
+ char key= e.character;
+ if (key == 0) {
+
+ switch (e.keyCode) {
+
+ case SWT.ARROW_LEFT:
+ case SWT.ARROW_RIGHT:
+ validateContextInformation();
+ break;
+ case SWT.CTRL:
+ case SWT.SHIFT:
+ break;
+ default:
+ hideContextInfoPopup();
+ break;
+ }
+
+ } else if (key == 0x1B) {
+ // terminate on Esc
+ hideContextInfoPopup();
+ } else {
+ validateContextInformation();
+ }
+ return true;
+ }
+
+ /*
+ * @see IEventConsumer#processEvent(VerifyEvent)
+ */
+ public void processEvent(VerifyEvent event) {
+ if (Helper.okToUse(fContextSelectorShell))
+ contextSelectorProcessEvent(event);
+ if (Helper.okToUse(fContextInfoPopup))
+ contextInfoPopupProcessEvent(event);
+ }
+
+ /**
+ * Processes a key stroke in the context selector.
+ *
+ * @param e the verify event describing the key stroke
+ */
+ private void contextSelectorProcessEvent(VerifyEvent e) {
+
+ if (e.start == e.end && e.text != null && e.text.equals(fLineDelimiter)) {
+ e.doit= false;
+ insertSelectedContext();
+ }
+
+ hideContextSelector();
+ }
+
+ /**
+ * Processes a key stroke while the info popup is up.
+ *
+ * @param e the verify event describing the key stroke
+ */
+ private void contextInfoPopupProcessEvent(VerifyEvent e) {
+ if (e.start != e.end && (e.text == null || e.text.length() == 0))
+ validateContextInformation();
+ }
+
+ /**
+ * Validates the context information for the viewer's actual cursor position.
+ */
+ private void validateContextInformation() {
+ /*
+ * Post the code in the event queue in order to ensure that the
+ * action described by this verify key event has already beed executed.
+ * Otherwise, we'd validate the context information based on the
+ * pre-key-stroke state.
+ */
+ fContextInfoPopup.getDisplay().asyncExec(new Runnable() {
+
+ private ContextFrame fFrame= (ContextFrame) fContextFrameStack.peek();
+
+ public void run() {
+ if (Helper.okToUse(fContextInfoPopup) && fFrame == fContextFrameStack.peek()) {
+ int offset= fViewer.getSelectedRange().x;
+ if (fFrame.fValidator == null || !fFrame.fValidator.isContextInformationValid(offset)) {
+ hideContextInfoPopup();
+ } else if (fFrame.fPresenter != null && fFrame.fPresenter.updatePresentation(offset, fTextPresentation)) {
+ TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+ resize();
+ }
+ }
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationValidator.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationValidator.java
new file mode 100644
index 000000000..79def403c
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContextInformationValidator.java
@@ -0,0 +1,53 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * A default implementation of the <code>IContextInfomationValidator</code> interface.
+ * This implementation determines whether the information is valid by asking the content
+ * assist processor for all context information objects for the current position. If the
+ * currently displayed information is in the result set, the context information is
+ * considered valid.
+ */
+public final class ContextInformationValidator implements IContextInformationValidator {
+
+ private IContentAssistProcessor fProcessor;
+ private IContextInformation fContextInformation;
+ private ITextViewer fViewer;
+
+ /**
+ * Creates a new context information validator which is ready to be installed on
+ * a particular context information.
+ */
+ public ContextInformationValidator(IContentAssistProcessor processor) {
+ fProcessor= processor;
+ }
+
+ /*
+ * @see IContextInformationValidator#install
+ */
+ public void install(IContextInformation contextInformation, ITextViewer viewer, int position) {
+ fContextInformation= contextInformation;
+ fViewer= viewer;
+ }
+
+ /*
+ * @see IContentAssistTipCloser#isContextInformationValid
+ */
+ public boolean isContextInformationValid(int position) {
+ IContextInformation[] infos= fProcessor.computeContextInformation(fViewer, position);
+ if (infos != null && infos.length > 0) {
+ for (int i= 0; i < infos.length; i++)
+ if (fContextInformation.equals(infos[i]))
+ return true;
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/Helper.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/Helper.java
new file mode 100644
index 000000000..7f7468882
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/Helper.java
@@ -0,0 +1,26 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.widgets.Widget;
+
+
+/**
+ * Helper class for testing widget state.
+ */
+class Helper {
+
+ /**
+ * Returns whether the widget is <code>null</code> or disposed.
+ *
+ * @param widget the widget to check
+ * @return <code>true</code> if the widget is neither <code>null</code> nor disposed
+ */
+ public static boolean okToUse(Widget widget) {
+ return (widget != null && !widget.isDisposed());
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposal.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposal.java
new file mode 100644
index 000000000..01c1babda
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposal.java
@@ -0,0 +1,76 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * The interface of completion proposals generated by content assist processors.
+ * A completion proposal contains information used to present the proposed completion
+ * to the user, to insert the completion should the user select it, and to present
+ * context information for the choosen completion once it has been inserted.<p>
+ * The interface can be implemented by clients. By default, clients use
+ * <code>CompletionProposal</code> as the standard implementer of this interface.
+ *
+ * @see IContentAssistProcessor
+ */
+public interface ICompletionProposal {
+
+ /**
+ * Inserts the proposed completion into the given document.
+ *
+ * @param document the document into which to insert the proposed completion
+ */
+ void apply(IDocument document);
+
+ /**
+ * Returns the new selection after the proposal has been applied to
+ * the given document in absolute document coordinates. If it returns
+ * <code>null</code>, no new selection is set.
+ *
+ * @param document the document into which the proposed completion has been inserted
+ * @return the new selection in absolute document coordinates
+ */
+ Point getSelection(IDocument document);
+
+ /**
+ * Returns optional additional information about the proposal.
+ * The additional information will be presented to assist the user
+ * in deciding if the selected proposal is the desired choice.
+ *
+ * @return the additional information or <code>null</code>
+ */
+ String getAdditionalProposalInfo();
+
+ /**
+ * Returns the string to be displayed in the list of completion proposals.
+ *
+ * @return the string to be displayed
+ */
+ String getDisplayString();
+
+ /**
+ * Returns the image to be displayed in the list of completion proposals.
+ * The image would typically be shown to the left of the display string.
+ *
+ * @return the image to be shown or <code>null</code> if no image is desired
+ */
+ Image getImage();
+
+ /**
+ * Returns optional context information associated with this proposal.
+ * The context information will automatically be shown if the proposal
+ * has been applied.
+ *
+ * @return the context information for this proposal or <code>null</code>
+ */
+ IContextInformation getContextInformation();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension.java
new file mode 100644
index 000000000..160e833d6
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ICompletionProposalExtension.java
@@ -0,0 +1,66 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+
+
+import org.eclipse.jface.text.IDocument;
+
+
+/**
+ * Extension interface to <code>ICompletionProposal</code>.
+ * Add the following functions:
+ * <ul>
+ * <li> handling of trigger characters other then ENTER
+ * <li> completion proposal validation for a given offset
+ * <li> freely positionable context information
+ * </ul>
+ * * @since 2.0
+ */
+public interface ICompletionProposalExtension {
+
+ /**
+ * Applies the proposed completion to the given document. The insertion
+ * has been triggered by entering the given character at the given offset.
+ * This method assumes that <code>isValidFor</code> returns
+ * <code>true</code> if called for <code>offset</code>.
+ *
+ * @param document the document into which to insert the proposed completion
+ * @param trigger the trigger to apply the completion
+ * @param offset the offset at which the trigger has been activated
+ */
+ void apply(IDocument document, char trigger, int offset);
+
+ /**
+ * Returns whether this completion proposal is valid for the given
+ * position in the given document.
+ *
+ * @param document the document for which the proposal is tested
+ * @param offset the offset for which the proposal is tested
+ */
+ boolean isValidFor(IDocument document, int offset);
+
+ /**
+ * Returns the characters which trigger the application of this completion proposal.
+ *
+ * @return the completion characters for this completion proposal or <code>null</code>
+ * if no completion other than the new line character is possible
+ */
+ char[] getTriggerCharacters();
+
+ /**
+ * Returns the position to which the computed context information refers to or
+ * <code>-1</code> if no context information can be provided by this completion proposal.
+ *
+ * @return the position to which the context information refers to or <code>-1</code> for no information
+ */
+ int getContextInformationPosition();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistListener.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistListener.java
new file mode 100644
index 000000000..5b3166fc0
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistListener.java
@@ -0,0 +1,29 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.events.VerifyEvent;
+
+import org.eclipse.jface.text.IEventConsumer;
+
+
+
+/**
+ * An interface whereby listeners can not only receive key events,
+ * but can also consume them to prevent subsequent listeners from
+ * processing the event.
+ */
+interface IContentAssistListener extends IEventConsumer {
+
+ /**
+ * Verifies the key event.
+ *
+ * @return <code>true</code> if processing should be continued by additional listeners
+ * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey
+ */
+ public boolean verifyKey(VerifyEvent event);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistProcessor.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistProcessor.java
new file mode 100644
index 000000000..3a0783f80
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistProcessor.java
@@ -0,0 +1,84 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+
+/**
+ * A content assist processor proposes completions and
+ * computes context information for a particular content type.
+ * A content assist processor is an <code>IContentAssistant</code>-plug-ins.
+ * This interface must be implemented by clients. Implementers should be
+ * registered with a content assistant in order to get involved in the
+ * assisting process.
+*/
+public interface IContentAssistProcessor {
+
+ /**
+ * Returns a list of completion proposals based on the
+ * specified location within the document that corresponds
+ * to the current cursor position within the text viewer.
+ *
+ * @param viewer the viewer whose document is used to compute the proposals
+ * @param documentPosition an offset within the document for which completions should be computed
+ * @return an array of completion proposals or <code>null</code> if no proposals are possible
+ */
+ ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset);
+
+ /**
+ * Returns information about possible contexts based on the
+ * specified location within the document that corresponds
+ * to the current cursor position within the text viewer.
+ *
+ * @param viewer the viewer whose document is used to compute the possible contexts
+ * @param documentPosition an offset within the document for which context information should be computed
+ * @return an array of context information objects or <code>null</code> if no context could be found
+ */
+ IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset);
+
+ /**
+ * Returns the characters which when entered by the user should
+ * automatically trigger the presentation of possible completions.
+ *
+ * @return the auto activation characters for completion proposal or <code>null</code>
+ * if no auto activation is desired
+ */
+ char[] getCompletionProposalAutoActivationCharacters();
+
+ /**
+ * Returns the characters which when entered by the user should
+ * automatically trigger the presentation of context information.
+ *
+ * @return the auto activation characters for presenting context information
+ * or <code>null</code> if no auto activation is desired
+ */
+ char[] getContextInformationAutoActivationCharacters();
+
+ /**
+ * Returns the reason why this content assist processor
+ * was unable to produce any completion proposals or context information.
+ *
+ * @return an error message or <code>null</code> if no error occurred
+ */
+ String getErrorMessage();
+
+ /**
+ * Returns a validator used to determine when displayed context information
+ * should be dismissed. May only return <code>null</code> if the processor is
+ * incapable of computing context information. <p>
+ *
+ * Because of http://dev.eclipse.org/bugs/show_bug.cgi?id=13926 the object returned
+ * by this method should also implement <code>IContextInformationPresenter</code>.
+ * @see IContextInformationPresenter
+ *
+ * @return a context information validator, or <code>null</code> if the processor
+ * is incapable of computing context information
+ */
+ IContextInformationValidator getContextInformationValidator();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java
new file mode 100644
index 000000000..74fcd27e9
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContentAssistant.java
@@ -0,0 +1,85 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * An <code>IContentAssistant</code> provides support on interactive content completion.
+ * The content assistant is a <code>ITextViewer</code> add-on. Its
+ * purpose is to propose, display, and insert completions of the content
+ * of the text viewer's document at the viewer's cursor position. In addition
+ * to handle completions, a content assistant can also be requested to provide
+ * context information. Context information is shown in a tooltip like popup.
+ * As it is not always possible to determine the exact context at a given
+ * document offset, a content assistant displays the possible contexts and requests
+ * the user to choose the one whose information should be displayed.<p>
+ * A content assistant has a list of <code>IContentAssistProcessor</code>
+ * objects each of which is registered for a particular document content
+ * type. The content assistant uses the processors to react on the request
+ * of completing documents or presenting context information.<p>
+ * The interface can be implemented by clients. By default, clients use
+ * <code>ContentAssistant</code> as the standard implementer of this interface.
+ *
+ * @see ITextViewer
+ * @see IContentAssistProcessor
+ */
+
+ public interface IContentAssistant {
+
+ //------ proposal popup orientation styles ------------
+ /** The context info list will overlay the list of completion proposals. */
+ public final static int PROPOSAL_OVERLAY= 10;
+ /** The completion proposal list will be removed before the context info list will be shown. */
+ public final static int PROPOSAL_REMOVE= 11;
+ /** The context info list will be presented without hiding or overlapping the completion proposal list. */
+ public final static int PROPOSAL_STACKED= 12;
+
+ //------ context info box orientation styles ----------
+ /** Context info will be shown above the location it has been requested for without hiding the location. */
+ public final static int CONTEXT_INFO_ABOVE= 20;
+ /** Context info will be shown below the location it has been requested for without hiding the location. */
+ public final static int CONTEXT_INFO_BELOW= 21;
+
+
+ /**
+ * Installs content assist support on the given text viewer.
+ *
+ * @param textViewer the text viewer on which content assist will work
+ */
+ void install(ITextViewer textViewer);
+
+ /**
+ * Uninstalls content assist support from the text viewer it has
+ * previously be installed on.
+ */
+ void uninstall();
+
+ /**
+ * Shows all possible completions of the content at the viewer's cursor position.
+ *
+ * @return an optional error message if no proposals can be computed
+ */
+ String showPossibleCompletions();
+
+ /**
+ * Shows context information for the content at the viewer's cursor position.
+ *
+ * @return an optional error message if no context information can be computed
+ */
+ String showContextInformation();
+
+ /**
+ * Returns the content assist processor to be used for the given content type.
+ *
+ * @param contentType the type of the content for which this
+ * content assistant is to be requested
+ * @return an instance content assist processor or
+ * <code>null</code> if none exists for the specified content type
+ */
+ IContentAssistProcessor getContentAssistProcessor(String contentType);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformation.java
new file mode 100644
index 000000000..bbf4efec1
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformation.java
@@ -0,0 +1,57 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.swt.graphics.Image;
+
+
+/**
+ * The inferface of context information presented to the user and
+ * generated by content assist processors.
+ * The interface can be implemented by clients. By default, clients use
+ * <code>ContextInformation</code> as the standard implementer of this interface.
+ *
+ * @see IContentAssistProcessor
+ */
+public interface IContextInformation {
+
+ /**
+ * Returns the string to be displayed in the list of contexts.
+ * This method is used to supply a unique presentation for
+ * situations where the context is ambiguous. These strings are
+ * used to allow the user to select the specific context.
+ *
+ * @return the string to be displayed for the context
+ */
+ String getContextDisplayString();
+
+ /**
+ * Returns the image for this context information.
+ * The image will be shown to the left of the display string.
+ *
+ * @return the image to be shown or <code>null</code> if no image is desired
+ */
+ Image getImage();
+
+ /**
+ * Returns the string to be displayed in the tooltip like information popup.
+ *
+ * @return the string to be displayed
+ */
+ String getInformationDisplayString();
+
+ /**
+ * Compares the given object with this receiver. Two context informations are
+ * equal if there information display strings and their context display strings
+ * are equal.
+ *
+ * @see Object#equals
+ */
+ boolean equals(Object object);
+}
+
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationExtension.java
new file mode 100644
index 000000000..d34e2435c
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationExtension.java
@@ -0,0 +1,29 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+
+
+/**
+ * Extension interface for <code>IContextInformation</code>.
+ * Adds the functionality of freely positionable context information.
+ *
+ * @since 2.0
+ */
+public interface IContextInformationExtension {
+
+ /**
+ * Returns the start offset of the range for which this context information is valid.
+ *
+ * @return the start offset of the range for which this context information is valid
+ */
+ int getContextInformationPosition();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationPresenter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationPresenter.java
new file mode 100644
index 000000000..d0b878310
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationPresenter.java
@@ -0,0 +1,48 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * A context information presenter determines the presentation
+ * of context information depending on a given document position.
+ * The interface can be implemented by clients.
+ *
+ * @since 2.0
+ */
+public interface IContextInformationPresenter {
+
+ /**
+ * Installs this presenter for the given context information.
+ *
+ * @param info the context information which this presenter should style
+ * @param viewer the text viewer on which the information is presented
+ * @param documentPosition the document position for which the information has been computed
+ */
+ void install(IContextInformation info, ITextViewer viewer, int documentPosition);
+
+ /**
+ * Updates the given presentation of the given context information
+ * at the given document position. Returns whether update changed the
+ * presentation.
+ *
+ * @param information the context information to be styled
+ * @param documentPosition the current position within the document
+ * @param presentation the presentation to be updated
+ * @return <code>true</code> if the given presentation has been changed
+ */
+ boolean updatePresentation(int documentPosition, TextPresentation presentation);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationValidator.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationValidator.java
new file mode 100644
index 000000000..661023c92
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/IContextInformationValidator.java
@@ -0,0 +1,38 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * A context information validator is used to determine if
+ * a displayed context information is still valid or should
+ * be dismissed. The interface can be implemented by clients. <p>
+ *
+ * @see IContextInformationPresenter
+ */
+public interface IContextInformationValidator {
+
+ /**
+ * Installs this validator for the given context information.
+ *
+ * @param info the context information which this validator should check
+ * @param viewer the text viewer on which the information is presented
+ * @param documentPosition the document position for which the information has been computed
+ */
+ void install(IContextInformation info, ITextViewer viewer, int documentPosition);
+
+ /**
+ * Returns whether the information this validator is installed on is still valid
+ * at the given document position.
+ *
+ * @param documentPosition the current position within the document
+ * @return <code>true</code> if the information also valid at the given document position
+ */
+ boolean isContextInformationValid(int documentPosition);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.java
new file mode 100644
index 000000000..5b93bed49
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/JFaceTextMessages.java
@@ -0,0 +1,26 @@
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+package org.eclipse.jface.text.contentassist;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+class JFaceTextMessages {
+
+ private static final String RESOURCE_BUNDLE= "org.eclipse.jface.text.JFaceTextMessages";//$NON-NLS-1$
+
+ private static ResourceBundle fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE);
+
+ private JFaceTextMessages() {
+ }
+
+ public static String getString(String key) {
+ try {
+ return fgResourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$
+ }
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java
new file mode 100644
index 000000000..41f264a12
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java
@@ -0,0 +1,72 @@
+package org.eclipse.jface.text.contentassist;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Table;
+
+
+/**
+ * A generic closer class used to monitor various
+ * interface events in order to determine whether
+ * a content assist should be terminated and all
+ * associated windows be closed.
+ */
+class PopupCloser implements FocusListener, SelectionListener {
+
+ private ContentAssistant fContentAssistant;
+ private Table fTable;
+ private ScrollBar fScrollbar;
+ private boolean fScrollbarClicked= false;
+
+ public void install(ContentAssistant contentAssistant, Table table) {
+ fContentAssistant= contentAssistant;
+ fTable= table;
+ if (Helper.okToUse(fTable)) {
+ fTable.addFocusListener(this);
+ fScrollbar= fTable.getVerticalBar();
+ if (fScrollbar != null)
+ fScrollbar.addSelectionListener(this);
+ }
+ }
+
+ public void uninstall() {
+ if (Helper.okToUse(fTable)) {
+ fTable.removeFocusListener(this);
+ if (fScrollbar != null)
+ fScrollbar.removeSelectionListener(this);
+ }
+ }
+
+ // SelectionListener
+ public void widgetSelected(SelectionEvent e) {
+ fScrollbarClicked= true;
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ fScrollbarClicked= true;
+ }
+
+ // FocusListener
+ public void focusGained(FocusEvent e) {
+ }
+
+ public void focusLost(final FocusEvent e) {
+ fScrollbarClicked= false;
+ Display d= fTable.getDisplay();
+ d.asyncExec(new Runnable() {
+ public void run() {
+ if (Helper.okToUse(fTable) && !fTable.isFocusControl() && !fScrollbarClicked)
+ fContentAssistant.popupFocusLost(e);
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/package.html b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/package.html
new file mode 100644
index 000000000..2d94dfb32
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/package.html
@@ -0,0 +1,25 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.51 [en] (WinNT; I) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides a content assist add-on for an <tt>ITextViewer</tt>.
+Content assist supports the user in writing&nbsp; by proposing context
+sensitive completions at a given document position. A completion can also
+be a incomplete in itself and content assist provides means to deal with
+nested completions.
+<h2>
+Package Specification</h2>
+<tt>IContentAssistant</tt> defines the concept of the content assist add-on.
+It collaborates with content type specific completion processors (<tt>IContentAssistProcessor</tt>)
+in order to generate completion proposals (<tt>ICompletionProposal</tt>)
+valid at the current document position. The package provides a default
+implementation <tt>ContentAssistant</tt> which completely defines and implements
+the UI and the control flow for content assist.
+<br>&nbsp;
+</body>
+</html>
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContentFormatter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContentFormatter.java
new file mode 100644
index 000000000..6ac7520ff
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/ContentFormatter.java
@@ -0,0 +1,654 @@
+package org.eclipse.jface.text.formatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DefaultPositionUpdater; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IPositionUpdater; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.TypedPosition; import org.eclipse.jface.util.Assert;
+
+
+/**
+ * Standard implementation of <code>IContentFormatter</code>.
+ * The formatter supports two operation modi: partition aware and
+ * partition unaware. <p>
+ * In the partition aware mode, the formatter determines the
+ * partitioning of the document region to be formatted. For each
+ * partition it determines all document positions which are affected
+ * when text changes are applied to the partition. Those which overlap
+ * with the partition are remembered as character positions. These
+ * character positions are passed over to the formatting strategy
+ * registered for the partition's content type. The formatting strategy
+ * returns a string containing the formatted document partition as well
+ * as the adapted character positions. The formatted partition replaces
+ * the old content of the partition. The remembered document postions
+ * are updated with the adapted character positions. In addition, all
+ * other document positions are accordingly adapted to the formatting
+ * changes.<p>
+ * In the partition unaware mode, the document's partitioning is ignored
+ * and the document is considered consisting of only one partition of
+ * the content type <code>IDocument.DEFAULT_CONTENT_TYPE</code>. The
+ * formatting process is similar to the partition aware mode, with the
+ * exception of having only one partition.<p>
+ * Usually, clients instantiate this class and configure it before using it.
+ *
+ * @see IContentFormatter
+ * @see IDocument
+ * @see ITypedRegion
+ * @see Position
+ */
+public class ContentFormatter implements IContentFormatter {
+
+ /**
+ * Defines a reference to either the offset or the end offset of
+ * a particular position.
+ */
+ static class PositionReference implements Comparable {
+
+ /** The referenced position */
+ protected Position fPosition;
+ /** The reference to either the offset or the end offset */
+ protected boolean fRefersToOffset;
+ /** The original category of the referenced position */
+ protected String fCategory;
+
+ protected PositionReference(Position position, boolean refersToOffset, String category) {
+ fPosition= position;
+ fRefersToOffset= refersToOffset;
+ fCategory= category;
+ }
+
+ /**
+ * Returns the offset of the referenced position.
+ */
+ protected int getOffset() {
+ return fPosition.getOffset();
+ }
+
+ /**
+ * Manipulates the offset of the referenced position.
+ */
+ protected void setOffset(int offset) {
+ fPosition.setOffset(offset);
+ }
+
+ /**
+ * Returns the length of the referenced position.
+ */
+ protected int getLength() {
+ return fPosition.getLength();
+ }
+
+ /**
+ * Manipulates the length of the referenced position.
+ */
+ protected void setLength(int length) {
+ fPosition.setLength(length);
+ }
+
+ /**
+ * Returns whether this reference points to the offset or endoffset
+ * of the references position.
+ */
+ protected boolean refersToOffset() {
+ return fRefersToOffset;
+ }
+
+ /**
+ * Returns the category of the referenced position.
+ */
+ protected String getCategory() {
+ return fCategory;
+ }
+
+ /**
+ * Returns the referenced position.
+ */
+ protected Position getPosition() {
+ return fPosition;
+ }
+
+ /**
+ * Returns the referenced character position
+ */
+ protected int getCharacterPosition() {
+ if (fRefersToOffset)
+ return getOffset();
+ return getOffset() + getLength();
+ }
+
+ /**
+ * @see Comparable#compareTo(Object)
+ */
+ public int compareTo(Object obj) {
+
+ if (obj instanceof PositionReference) {
+ PositionReference r= (PositionReference) obj;
+ return getCharacterPosition() - r.getCharacterPosition();
+ }
+
+ throw new ClassCastException();
+ }
+ };
+
+ /**
+ * The position updater used to adapt all to update the
+ * remembered partitions.
+ *
+ * @see IPositionUpdater
+ * @see DefaultPositionUpdater
+ */
+ class NonDeletingPositionUpdater extends DefaultPositionUpdater {
+
+ protected NonDeletingPositionUpdater(String category) {
+ super(category);
+ }
+
+ /*
+ * @see DefaultPositionUpdater#notDeleted()
+ */
+ protected boolean notDeleted() {
+ return true;
+ }
+ };
+
+ /**
+ * The position updater which runs as first updater on the document's positions.
+ * Used to remove all affected positions from their categories to avoid them
+ * from being regularily updated.
+ *
+ * @see IPositionUpdater
+ */
+ class RemoveAffectedPositions implements IPositionUpdater {
+ /**
+ * @see IPositionUpdater#update(DocumentEvent)
+ */
+ public void update(DocumentEvent event) {
+ removeAffectedPositions(event.getDocument());
+ }
+ };
+
+ /**
+ * The position updater which runs as last updater on the document's positions.
+ * Used to update all affected positions and adding them back to their
+ * original categories.
+ *
+ * @see IPositionUpdater
+ */
+ class UpdateAffectedPositions implements IPositionUpdater {
+
+ private int[] fPositions;
+ private int fOffset;
+
+ public UpdateAffectedPositions(int[] positions, int offset) {
+ fPositions= positions;
+ fOffset= offset;
+ }
+
+ /**
+ * @see IPositionUpdater#update(DocumentEvent)
+ */
+ public void update(DocumentEvent event) {
+ updateAffectedPositions(event.getDocument(), fPositions, fOffset);
+ }
+ };
+
+
+ /** Internal position category used for the formatter partitioning */
+ private final static String PARTITIONING= "__formatter_partitioning"; //$NON-NLS-1$
+
+ /** The map of <code>IFormattingStrategy</code> objects */
+ private Map fStrategies;
+ /** The indicator of whether the formatter operates in partition aware mode or not */
+ private boolean fIsPartitionAware= true;
+
+ /** The partition information managing document position categories */
+ private String[] fPartitionManagingCategories;
+ /** The list of references to offset and end offset of all overlapping positions */
+ private List fOverlappingPositionReferences;
+ /** Position updater used for partitioning positions */
+ private IPositionUpdater fPartitioningUpdater;
+
+
+
+ /**
+ * Creates a new content formatter. The content formatter operates by default
+ * in the partition-aware mode. There are no preconfigured formatting strategies.
+ */
+ public ContentFormatter() {
+ }
+
+ /**
+ * Registers a strategy for a particular content type. If there is already a strategy
+ * registered for this type, the new strategy is registered instead of the old one.
+ * If the given content type is <code>null</code> the given strategy is registered for
+ * all content types as is called only once per formatting session.
+ *
+ * @param strategy the formatting strategy to register, or <code>null</code> to remove an existing one
+ * @param contentType the content type under which to register, or <code>null</code> for all content types
+ */
+ public void setFormattingStrategy(IFormattingStrategy strategy, String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fStrategies == null)
+ fStrategies= new HashMap();
+
+ if (strategy == null)
+ fStrategies.remove(contentType);
+ else
+ fStrategies.put(contentType, strategy);
+ }
+
+ /**
+ * Informs this content formatter about the names of those position categories
+ * which are used to manage the document's partitioning information and thus should
+ * be ignored when this formatter updates positions.
+ *
+ * @param categories the categories to be ignored
+ */
+ public void setPartitionManagingPositionCategories(String[] categories) {
+ fPartitionManagingCategories= categories;
+ }
+
+ /**
+ * Sets the formatter's operation mode.
+ *
+ * @param enable indicates whether the formatting process should be partition ware
+ */
+ public void enablePartitionAwareFormatting(boolean enable) {
+ fIsPartitionAware= enable;
+ }
+
+ /*
+ * @see IContentFormatter#getFormattingStrategy
+ */
+ public IFormattingStrategy getFormattingStrategy(String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fStrategies == null)
+ return null;
+
+ return (IFormattingStrategy) fStrategies.get(contentType);
+ }
+
+ /*
+ * @see IContentFormatter#format
+ */
+ public void format(IDocument document, IRegion region) {
+ if (fIsPartitionAware)
+ formatPartitions(document, region);
+ else
+ formatRegion(document, region);
+ }
+
+ /**
+ * Determines the partitioning of the given region of the document.
+ * Informs for each partition about the start, the process, and the
+ * termination of the formatting session.
+ */
+ private void formatPartitions(IDocument document, IRegion region) {
+
+ addPartitioningUpdater(document);
+
+ try {
+
+ TypedPosition[] ranges= getPartitioning(document, region);
+ if (ranges != null) {
+ start(ranges, getIndentation(document, region.getOffset()));
+ format(document, ranges);
+ stop(ranges);
+ }
+
+ } catch (BadLocationException x) {
+ }
+
+ removePartitioningUpdater(document);
+ }
+
+ /**
+ * Informs for the given region about the start, the process, and
+ * the termination of the formatting session.
+ */
+ private void formatRegion(IDocument document, IRegion region) {
+
+ IFormattingStrategy strategy= getFormattingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
+ if (strategy != null) {
+ strategy.formatterStarts(getIndentation(document, region.getOffset()));
+ format(document, strategy, new TypedPosition(region.getOffset(), region.getLength(), IDocument.DEFAULT_CONTENT_TYPE));
+ strategy.formatterStops();
+ }
+ }
+
+ /**
+ * Returns the partitioning of the given region of the specified document.
+ * As one partition after the other will be formatted and formatting will
+ * probably change the length of the formatted partition, it must be kept
+ * track of the modifications in order to submit the correct partition to all
+ * formatting strategies. For this, all partitions are remembered as positions
+ * in a dedicated position category. (As formatting stratgies might rely on each
+ * other, calling them in reversed order is not an option.)
+ *
+ * @param document the document
+ * @param region the region for which the partitioning must be determined
+ * @return the partitioning of the specified region
+ * @exception BadLocationException of region is invalid in the document
+ */
+ private TypedPosition[] getPartitioning(IDocument document, IRegion region) throws BadLocationException {
+
+ ITypedRegion[] regions= document.computePartitioning(region.getOffset(), region.getLength());
+ TypedPosition[] positions= new TypedPosition[regions.length];
+
+ for (int i= 0; i < regions.length; i++) {
+ positions[i]= new TypedPosition(regions[i]);
+ try {
+ document.addPosition(PARTITIONING, positions[i]);
+ } catch (BadPositionCategoryException x) {
+ // should not happen
+ }
+ }
+
+ return positions;
+ }
+
+ /**
+ * Fires <code>formatterStarts</code> to all formatter strategies
+ * which will be involved in the forthcoming formatting process.
+ *
+ * @param regions the partitioning of the document to be formatted
+ * @param indentation the initial indentation
+ */
+ private void start(TypedPosition[] regions, String indentation) {
+ for (int i= 0; i < regions.length; i++) {
+ IFormattingStrategy s= getFormattingStrategy(regions[i].getType());
+ if (s != null)
+ s.formatterStarts(indentation);
+ }
+ }
+
+ /**
+ * Formats one partition after the other using the formatter strategy registered for
+ * the partition's content type.
+ *
+ * @param document to document to be formatted
+ * @param ranges the partitioning of the document region to be formatted
+ */
+ private void format(final IDocument document, TypedPosition[] ranges) {
+ for (int i= 0; i < ranges.length; i++) {
+ IFormattingStrategy s= getFormattingStrategy(ranges[i].getType());
+ if (s != null) {
+ format(document, s, ranges[i]);
+ }
+ }
+ }
+
+ /**
+ * Formats the given region of the document using the specified formatting
+ * strategy. In order to maintain positions correctly, first all affected
+ * positions determined, after all document listeners have been informed about
+ * the upcoming change, the affected positions are removed to avoid that they
+ * are regularily updated. After all position updaters have run, the affected
+ * positions are updated with the formatter's information and added back to
+ * their categories, right before the first document listener is informed about
+ * that a change happend.
+ *
+ * @param document the document to be formatted
+ * @param strategy the strategy to be used
+ * @param region the region to be formatted
+ */
+ private void format(final IDocument document, IFormattingStrategy strategy, TypedPosition region) {
+ try {
+
+ final int offset= region.getOffset();
+ int length= region.getLength();
+
+ String content= document.get(offset, length);
+ final int[] positions= getAffectedPositions(document, offset, length);
+ String formatted= strategy.format(content, isLineStart(document, offset), getIndentation(document, offset), positions);
+
+ IPositionUpdater first= new RemoveAffectedPositions();
+ document.insertPositionUpdater(first, 0);
+ IPositionUpdater last= new UpdateAffectedPositions(positions, offset);
+ document.addPositionUpdater(last);
+
+ document.replace(offset, length, formatted);
+
+ document.removePositionUpdater(first);
+ document.removePositionUpdater(last);
+
+ } catch (BadLocationException x) {
+ // should not happen
+ }
+ }
+
+ /**
+ * Fires <code>formatterStops</code> to all formatter strategies which were
+ * involved in the formatting process which is about to terminate.
+ *
+ * @param regions the partitioning of the document which has been formatted
+ */
+ private void stop(TypedPosition[] regions) {
+ for (int i= 0; i < regions.length; i++) {
+ IFormattingStrategy s= getFormattingStrategy(regions[i].getType());
+ if (s != null)
+ s.formatterStops();
+ }
+ }
+
+ /**
+ * Installs those updaters which the formatter needs to keep
+ * track of the partitions.
+ *
+ * @param document the document to be formatted
+ */
+ private void addPartitioningUpdater(IDocument document) {
+ fPartitioningUpdater= new NonDeletingPositionUpdater(PARTITIONING);
+ document.addPositionCategory(PARTITIONING);
+ document.addPositionUpdater(fPartitioningUpdater);
+ }
+
+ /**
+ * Removes the formatter's internal position updater and category.
+ *
+ * @param document the document that has been formatted
+ */
+ private void removePartitioningUpdater(IDocument document) {
+
+ try {
+
+ document.removePositionUpdater(fPartitioningUpdater);
+ document.removePositionCategory(PARTITIONING);
+ fPartitioningUpdater= null;
+
+ } catch (BadPositionCategoryException x) {
+ // should not happen
+ }
+ }
+
+ /**
+ * Determines whether the given document position category should be ignored
+ * by this formatter's position updating.
+ *
+ * @param category the category to check
+ * @return <code>true</code> if the category should be ignored, <code>false</code> otherwise
+ */
+ private boolean ignoreCategory(String category) {
+
+ if (PARTITIONING.equals(category))
+ return true;
+
+ if (fPartitionManagingCategories != null) {
+ for (int i= 0; i < fPartitionManagingCategories.length; i++) {
+ if (fPartitionManagingCategories[i].equals(category))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines all embracing, overlapping, and follow up positions
+ * for the given region of the document.
+ *
+ * @param document the document to be formatted
+ * @param offset the offset of the document region to be formatted
+ * @param length the length of the document to be formatted
+ */
+ private void determinePositionsToUpdate(IDocument document, int offset, int length) {
+
+ String[] categories= document.getPositionCategories();
+ if (categories != null) {
+ for (int i= 0; i < categories.length; i++) {
+
+ if (ignoreCategory(categories[i]))
+ continue;
+
+ try {
+
+ Position[] positions= document.getPositions(categories[i]);
+
+ for (int j= 0; j < positions.length; j++) {
+
+ Position p= (Position) positions[j];
+ if (p.overlapsWith(offset, length)) {
+
+ if (offset < p.getOffset())
+ fOverlappingPositionReferences.add(new PositionReference(p, true, categories[i]));
+
+ if (p.getOffset() + p.getLength() < offset + length)
+ fOverlappingPositionReferences.add(new PositionReference(p, false, categories[i]));
+ }
+ }
+
+ } catch (BadPositionCategoryException x) {
+ // can not happen
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns all offset and the end offset of all positions overlapping with the
+ * specified document range.
+ *
+ * @param document the document to be formatted
+ * @param offset the offset of the document region to be formatted
+ * @param length the length of the document to be formatted
+ * @return all character positions of the interleaving positions
+ */
+ private int[] getAffectedPositions(IDocument document, int offset, int length) {
+
+ fOverlappingPositionReferences= new ArrayList();
+
+ determinePositionsToUpdate(document, offset, length);
+
+ Collections.sort(fOverlappingPositionReferences);
+
+ int[] positions= new int[fOverlappingPositionReferences.size()];
+ for (int i= 0; i < positions.length; i++) {
+ PositionReference r= (PositionReference) fOverlappingPositionReferences.get(i);
+ positions[i]= r.getCharacterPosition() - offset;
+ }
+
+ return positions;
+ }
+
+ /**
+ * Removes the affected positions from their categories to avoid
+ * that they are invalidly updated.
+ *
+ * @param document the document
+ */
+ private void removeAffectedPositions(IDocument document) {
+ int size= fOverlappingPositionReferences.size();
+ for (int i= 0; i < size; i++) {
+ PositionReference r= (PositionReference) fOverlappingPositionReferences.get(i);
+ try {
+ document.removePosition(r.getCategory(), r.getPosition());
+ } catch (BadPositionCategoryException x) {
+ // can not happen
+ }
+ }
+ }
+
+ /**
+ * Updates all the overlapping positions. Note, all other positions are
+ * automatically updated by their document position updaters.
+ *
+ * @param document the document to has been formatted
+ * @param positions the adapted character positions to be used to update the document positions
+ * @param offset the offset of the document region that has been formatted
+ */
+ private void updateAffectedPositions(IDocument document, int[] positions, int offset) {
+
+ if (positions.length == 0)
+ return;
+
+ Map added= new HashMap(positions.length * 2);
+
+ for (int i= 0; i < positions.length; i++) {
+
+ PositionReference r= (PositionReference) fOverlappingPositionReferences.get(i);
+
+ if (r.refersToOffset())
+ r.setOffset(offset + positions[i]);
+ else
+ r.setLength((offset + positions[i]) - r.getOffset());
+
+ if (added.get(r.getPosition()) == null) {
+ try {
+ document.addPosition(r.getCategory(), r.getPosition());
+ added.put(r.getPosition(), r.getPosition());
+ } catch (BadPositionCategoryException x) {
+ // can not happen
+ } catch (BadLocationException x) {
+ // should not happen
+ }
+ }
+
+ }
+
+ fOverlappingPositionReferences= null;
+ }
+
+ /**
+ * Returns the indentation of the line of the given offset.
+ *
+ * @param document the document
+ * @param offset the offset
+ * @return the indentation of the line of the offset
+ */
+ private String getIndentation(IDocument document, int offset) {
+
+ try {
+ int start= document.getLineOfOffset(offset);
+ start= document.getLineOffset(start);
+
+ int end= start;
+ char c= document.getChar(end);
+ while ('\t' == c || ' ' == c)
+ c= document.getChar(++end);
+
+ return document.get(start, end - start);
+ } catch (BadLocationException x) {
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Determines whether the offset is the beginning of a line in the given document.
+ *
+ * @param document the document
+ * @param offset the offset
+ * @return <code>true</code> if offset is the beginning of a line
+ * @exception BadLocationException if offset is invalid in document
+ */
+ private boolean isLineStart(IDocument document, int offset) throws BadLocationException {
+ int start= document.getLineOfOffset(offset);
+ start= document.getLineOffset(start);
+ return (start == offset);
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatter.java
new file mode 100644
index 000000000..329e8c92d
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IContentFormatter.java
@@ -0,0 +1,49 @@
+package org.eclipse.jface.text.formatter;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * The interface of a document content formatter. The formatter formats ranges
+ * within documents. The documents are modified by the formatter.<p>
+ * The content formatter is assumed to determine the partitioning of the document
+ * range to be formatted. For each partition, the formatter determines based
+ * on the partition's content type the formatting strategy to be used. Before
+ * the first strategy is activated all strategies are informed about the
+ * start of the formatting process. After that, the formatting strategies are
+ * activated in the sequence defined by the partitioning of the document range to be
+ * formatted. It is assumed that a strategy must be finished before the next strategy
+ * can be activated. After the last strategy has been finished, all strategies are
+ * informed about the termination of the formatting process.<p>
+ * The interface can be implemented by clients. By default, clients use <code>ContentFormatter</code>
+ * as the standard implementer of this interface.
+ *
+ * @see IDocument
+ * @see IFormattingStrategy
+ */
+public interface IContentFormatter {
+
+ /**
+ * Formats the given region of the specified document.The formatter may safely
+ * assume that it is the only subject that modifies the document at this point in time.
+ *
+ * @param document the document to be formatted
+ * @param region the region within the document to be formatted
+ */
+ void format(IDocument document, IRegion region);
+
+ /**
+ * Returns the formatting strategy registered for the given content type.
+ *
+ * @param contentType the content type for which to look up the formatting strategy
+ * @return the formatting strategy for the given content type, or
+ * <code>null</code> if there is no such strategy
+ */
+ IFormattingStrategy getFormattingStrategy(String contentType);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategy.java
new file mode 100644
index 000000000..5568ef785
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/IFormattingStrategy.java
@@ -0,0 +1,51 @@
+package org.eclipse.jface.text.formatter;
+
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.List;
+
+/**
+ * An formatting strategy is assumed to be specialized on formatting text
+ * of a particular content type. Each formatting process calls the strategy's
+ * methods in the following sequence:
+ * <ul>
+ * <li><code>formatterStarts</code>
+ * <li><code>format</code>
+ * <li><code>formatterStops</code>
+ * </ul>
+ * This interface must be implemented by clients. Implementers should be registered with
+ * a content formatter in order get involved in the formatting process.
+ */
+public interface IFormattingStrategy {
+
+ /**
+ * Informs the strategy about the start of a formatting process in which it will
+ * participate.
+ *
+ * @param initialIndentation the indent string of the first line at which the
+ * overall formatting process starts.
+ */
+ void formatterStarts(String initialIndentation);
+
+ /**
+ * Formats the given string. During the formatting process this strategy must update
+ * the given character positions according to the changes applied to the given string.
+ *
+ * @param content the initial string to be formatted
+ * @param isLineStart indicates whether the beginning of content is a line start in its document
+ * @param indentation the indentation string to be used
+ * @param positions the character positions to be updated
+ * @return the formatted string
+ */
+ String format(String content, boolean isLineStart, String indentation, int[] positions);
+
+ /**
+ * Informs the strategy that the formatting process in which it has participated
+ * has been finished.
+ */
+ void formatterStops();
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/package.html b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/package.html
new file mode 100644
index 000000000..33f16a7de
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/formatter/package.html
@@ -0,0 +1,21 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.51 [en] (WinNT; I) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides a text formatter add-on for an <tt>ITextViewer</tt>.
+A text formatter changes the formatting of&nbsp; document region while
+preserving and correctly updating the positions of the document.
+<h2>
+Package Specification</h2>
+<tt>IContentFormatter</tt> defines the concept of a text formatter. It
+collaborates with content type specific formatting stratgies (<tt>IFormattingStrategy</tt>)
+which for a given document region format the subregion with the matching
+content type. The package contains a default implementation of <tt>IContentFormatter</tt>
+(<tt>ContentFormatter</tt>).
+</body>
+</html>
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java
new file mode 100644
index 000000000..74375c750
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationPresenter.java
@@ -0,0 +1,66 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.information;
+
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * An information presenter shows information available at the text viewer's
+ * current document position. An <code>IInformationPresenter</code> is a
+ * <code>ITextViewer</code> add-on.<p>
+ * An information presenters has a list of <code>IInformationProvider</code> objects
+ * each of which is registered for a particular document content type.
+ * The presenter uses the strategy objects to retrieve the information to present.<p>
+ * The interface can be implemented by clients. By default, clients use
+ * <code>InformationPresenter</code> as the standard implementer of this interface.
+ *
+ * @see ITextViewer
+ * @see IInformationProvider
+ * @since 2.0
+ */
+public interface IInformationPresenter {
+
+ /**
+ * Installs the information presenter on the given text viewer. After this method has been
+ * finished, the presenter is operational. I.e., the method <code>showInformation</code>
+ * can be called until <code>uninstall</code> is called.
+ *
+ * @param textViewer the viewer on which the presenter is installed
+ */
+ void install(ITextViewer textViewer);
+
+ /**
+ * Removes the information presenter from the text viewer it has previously been
+ * installed on.
+ */
+ void uninstall();
+
+ /**
+ * Shows information related to the cursor position of the text viewer
+ * this information presenter is installed on.
+ *
+ * @return an optional error message if
+ */
+ void showInformation();
+
+ /**
+ * Returns the information provider to be used for the given content type.
+ *
+ * @param contentType the type of the content for which information will be requested
+ * @return an information provider or
+ * <code>null</code> if none exists for the specified content type
+ */
+ IInformationProvider getInformationProvider(String contentType);
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProvider.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProvider.java
new file mode 100644
index 000000000..e9be43fda
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/IInformationProvider.java
@@ -0,0 +1,54 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.information;
+
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * Provides information related to the content of a text viewer.<p>
+ * Clients may implement this interface.
+ *
+ * @see ITextViewer
+ * @since 2.0
+ */
+public interface IInformationProvider {
+
+ /**
+ * Returns the region of the text viewer's document close to the given
+ * offset that contains a subject about which information can be provided.<p>
+ * For example, if information can be provided on a per code block basis,
+ * the offset should be used to find the enclosing code block and the source
+ * range of the block should be returned.
+ *
+ * @param textViewer the text viewer in which informationhas been requested
+ * @param offset the offset at which information has been requested
+ * @return the region of the text viewer's document containing the information subject
+ */
+ IRegion getSubject(ITextViewer textViewer, int offset);
+
+ /**
+ * Returns the information about the given subject or <code>null</code> if
+ * no information is available. It depends on the concrete configuration in which
+ * format the information is to be provided. For example, information presented
+ * in an information control displaying HTML, should be provided in HTML.
+ *
+ * @param textViewer the viewer in whose document the subject is contained
+ * @param subject the text region constituting the information subject
+ * @return the information about the subject
+ * @see IInformationPresenter
+ */
+ String getInformation(ITextViewer textViewer, IRegion subject);
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/information/InformationPresenter.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/InformationPresenter.java
new file mode 100644
index 000000000..f8f7590c2
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/InformationPresenter.java
@@ -0,0 +1,394 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.information;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.jface.text.AbstractInformationControlManager;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.IViewportListener;
+import org.eclipse.jface.text.IWidgetTokenKeeper;
+import org.eclipse.jface.text.IWidgetTokenOwner;
+
+import org.eclipse.jface.util.Assert;
+
+/**
+ * Standard implementation of <code>IInformationPresenter</code>.
+ * This implementation extends <code>AbstractInformationControlManager</code>.
+ * The information control is made visible on request by calling
+ * <code>showInformation</code>.<p>
+ * Usually, clients instantiate this class and configure it before using it. The configuration
+ * must be consistent: This means the used <code>IInformationControlCreator</code>
+ * must create an information control expecting information in the same format the configured
+ * <code>IInformationProvider</code>s use to encode the information they provide.
+ *
+ * @since 2.0
+ */
+public class InformationPresenter extends AbstractInformationControlManager implements IInformationPresenter, IWidgetTokenKeeper {
+
+ /**
+ * Internal information control closer. Listens to several events issued by its subject control
+ * and closes the information control when necessary.
+ */
+ class Closer implements IInformationControlCloser, ControlListener, MouseListener,
+ FocusListener, IViewportListener, KeyListener {
+
+ /** The subject control */
+ private Control fSubjectControl;
+ /** The information control */
+ private IInformationControl fInformationControl;
+ /** Indicates whether this closer is active */
+ private boolean fIsActive= false;
+
+ /*
+ * @see IInformationControlCloser#setSubjectControl(Control)
+ */
+ public void setSubjectControl(Control control) {
+ fSubjectControl= control;
+ }
+
+ /*
+ * @see IInformationControlCloser#setInformationControl(IInformationControl)
+ */
+ public void setInformationControl(IInformationControl control) {
+ fInformationControl= control;
+ }
+
+ /*
+ * @see IInformationControlCloser#start(Rectangle)
+ */
+ public void start(Rectangle informationArea) {
+
+ if (fIsActive)
+ return;
+ fIsActive= true;
+
+ if (fSubjectControl != null && ! fSubjectControl.isDisposed()) {
+ fSubjectControl.addControlListener(this);
+ fSubjectControl.addMouseListener(this);
+ fSubjectControl.addFocusListener(this);
+ fSubjectControl.addKeyListener(this);
+ }
+
+ if (fInformationControl != null)
+ fInformationControl.addFocusListener(this);
+
+ fTextViewer.addViewportListener(this);
+ }
+
+ /*
+ * @see IInformationControlCloser#stop()
+ */
+ public void stop() {
+
+ if (!fIsActive)
+ return;
+ fIsActive= false;
+
+ fTextViewer.removeViewportListener(this);
+
+ if (fInformationControl != null)
+ fInformationControl.removeFocusListener(this);
+
+ hideInformationControl();
+
+ if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
+ fSubjectControl.removeControlListener(this);
+ fSubjectControl.removeMouseListener(this);
+ fSubjectControl.removeFocusListener(this);
+ fSubjectControl.removeKeyListener(this);
+ }
+ }
+
+ /*
+ * @see ControlListener#controlResized(ControlEvent)
+ */
+ public void controlResized(ControlEvent e) {
+ stop();
+ }
+
+ /*
+ * @see ControlListener#controlMoved(ControlEvent)
+ */
+ public void controlMoved(ControlEvent e) {
+ stop();
+ }
+
+ /*
+ * @see MouseListener#mouseDown(MouseEvent)
+ */
+ public void mouseDown(MouseEvent e) {
+ stop();
+ }
+
+ /*
+ * @see MouseListener#mouseUp(MouseEvent)
+ */
+ public void mouseUp(MouseEvent e) {
+ }
+
+ /*
+ * @see MouseListener#mouseDoubleClick(MouseEvent)
+ */
+ public void mouseDoubleClick(MouseEvent e) {
+ stop();
+ }
+
+ /*
+ * @see FocusListener#focusGained(FocusEvent)
+ */
+ public void focusGained(FocusEvent e) {
+ }
+
+ /*
+ * @see FocusListener#focusLost(FocusEvent)
+ */
+ public void focusLost(FocusEvent e) {
+ Display d= fSubjectControl.getDisplay();
+ d.asyncExec(new Runnable() {
+ public void run() {
+ if ( !fInformationControl.isFocusControl())
+ stop();
+ }
+ });
+ }
+
+ /*
+ * @see IViewportListenerListener#viewportChanged(int)
+ */
+ public void viewportChanged(int topIndex) {
+ stop();
+ }
+
+ /*
+ * @see KeyListener#keyPressed(KeyEvent)
+ */
+ public void keyPressed(KeyEvent e) {
+ stop();
+ }
+
+ /*
+ * @see KeyListener#keyReleased(KeyEvent)
+ */
+ public void keyReleased(KeyEvent e) {
+ }
+ };
+
+
+ /** The text viewer this information presenter works on */
+ private ITextViewer fTextViewer;
+ /** The map of <code>IInformationProvider</code> objects */
+ private Map fProviders;
+
+
+ /**
+ * Creates a new information presenter that uses the given information control creator.
+ * The presenter is not installed on any text viewer yet. By default, an information
+ * control closer is set that closes the information control in the event of key strokes,
+ * resizing, moves, focus changes, mouse clicks, and disposal - all of those applied to
+ * the information control's parent control. Also, the setup ensures that the information
+ * control when made visible will request thel focus.
+ *
+ * @param creator the information control creator to be used
+ */
+ public InformationPresenter(IInformationControlCreator creator) {
+ super(creator);
+ setCloser(new Closer());
+ takesFocusWhenVisible(true);
+ }
+
+ /**
+ * Registers a given information provider for a particular content type.
+ * If there is already a provider registered for this type, the new provider
+ * is registered instead of the old one.
+ *
+ * @param provider the information provider to register, or <code>null</code> to remove an existing one
+ * @param contentType the content type under which to register
+ */
+ public void setInformationProvider(IInformationProvider provider, String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fProviders == null)
+ fProviders= new HashMap();
+
+ if (provider == null)
+ fProviders.remove(contentType);
+ else
+ fProviders.put(contentType, provider);
+ }
+
+ /*
+ * @see IInformationPresenter#getInformationProvider(String)
+ */
+ public IInformationProvider getInformationProvider(String contentType) {
+ if (fProviders == null)
+ return null;
+
+ return (IInformationProvider) fProviders.get(contentType);
+ }
+
+ /*
+ * @see AbstractInformationControlManager#computeInformation()
+ */
+ protected void computeInformation() {
+ int offset= fTextViewer.getSelectedRange().x;
+ if (offset == -1)
+ return;
+
+
+ IInformationProvider provider= null;
+ try {
+ IDocument document= fTextViewer.getDocument();
+ String type= document.getContentType(offset);
+ provider= getInformationProvider(type);
+ } catch (BadLocationException x) {
+ }
+ if (provider == null)
+ return;
+
+ IRegion subject= provider.getSubject(fTextViewer, offset);
+ if (subject == null)
+ return;
+
+ setInformation(provider.getInformation(fTextViewer, subject), computeArea(subject));
+ }
+
+ /**
+ * Determines the graphical area covered by the given text region.
+ *
+ * @param region the region whose graphical extend must be computed
+ * @return the graphical extend of the given region
+ */
+ private Rectangle computeArea(IRegion region) {
+
+ StyledText styledText= fTextViewer.getTextWidget();
+
+ IRegion visible= fTextViewer.getVisibleRegion();
+ int start= region.getOffset() - visible.getOffset();
+ int end= start + region.getLength();
+
+ Point upperLeft= styledText.getLocationAtOffset(start);
+ Point lowerRight= new Point(upperLeft.x, upperLeft.y);
+
+ for (int i= start +1; i < end; i++) {
+
+ Point p= styledText.getLocationAtOffset(i);
+
+ if (upperLeft.x > p.x)
+ upperLeft.x= p.x;
+
+ if (upperLeft.y > p.y)
+ upperLeft.y= p.y;
+
+ if (lowerRight.x < p.x)
+ lowerRight.x= p.x;
+
+ if (lowerRight.y < p.y)
+ lowerRight.y= p.y;
+ }
+
+ GC gc= new GC(styledText);
+ lowerRight.x += gc.getFontMetrics().getAverageCharWidth();
+ lowerRight.y += styledText.getLineHeight();
+ gc.dispose();
+
+ int width= lowerRight.x - upperLeft.x;
+ int height= lowerRight.y - upperLeft.y;
+ return new Rectangle(upperLeft.x, upperLeft.y, width, height);
+ }
+
+ /*
+ * @see IInformationPresenter#install(ITextViewer)
+ */
+ public void install(ITextViewer textViewer) {
+ fTextViewer= textViewer;
+ install(fTextViewer.getTextWidget());
+ }
+
+ /*
+ * @see IInformationPresenter#uninstall()
+ */
+ public void uninstall() {
+ dispose();
+ }
+
+ /*
+ * @see AbstractInformationControlManager#showInformationControl(Rectangle)
+ */
+ protected void showInformationControl(Rectangle subjectArea) {
+ if (fTextViewer instanceof IWidgetTokenOwner) {
+ IWidgetTokenOwner owner= (IWidgetTokenOwner) fTextViewer;
+ if (owner.requestWidgetToken(this))
+ super.showInformationControl(subjectArea);
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#hideInformationControl()
+ */
+ protected void hideInformationControl() {
+ try {
+ super.hideInformationControl();
+ } finally {
+ if (fTextViewer instanceof IWidgetTokenOwner) {
+ IWidgetTokenOwner owner= (IWidgetTokenOwner) fTextViewer;
+ owner.releaseWidgetToken(this);
+ }
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#handleInformationControlDisposed()
+ */
+ protected void handleInformationControlDisposed() {
+ try {
+ super.handleInformationControlDisposed();
+ } finally {
+ if (fTextViewer instanceof IWidgetTokenOwner) {
+ IWidgetTokenOwner owner= (IWidgetTokenOwner) fTextViewer;
+ owner.releaseWidgetToken(this);
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+ */
+ public boolean requestWidgetToken(IWidgetTokenOwner owner) {
+ return false;
+ }
+}
+
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/information/package.html b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/package.html
new file mode 100644
index 000000000..2c20da020
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/information/package.html
@@ -0,0 +1,22 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.76 [en] (Windows NT 5.0; U) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides an information provider add-on for an <tt>ITextViewer</tt>.
+An information provider presents information for a certain subject in a
+specific information control. An information control usually is a floating
+window.
+<h2>
+Package Specification</h2>
+<tt>IInformationPresenter</tt> defines the concept of an information provider.
+It collaborates with content type specific information providers (<tt>IInformationProvider</tt>)
+which determine for a certain offset in a text viewer an information subject
+and the information available about this subject.. The package contains
+a default implementation of <tt>IInformationPresenter</tt> (<tt>InformationPresenter</tt>).
+</body>
+</html>
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/package.html b/org.eclipse.jface.text/src/org/eclipse/jface/text/package.html
new file mode 100644
index 000000000..5561c3748
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/package.html
@@ -0,0 +1,82 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.51 [en] (WinNT; I) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides a framework for creating, manipulating, displaying
+and editing text documents.
+<h2>
+Package Specification</h2>
+The packages is divided into two parts. The first part defines and implements
+the notion of text documents, whereas the second part defines and implements
+a viewer for text documents.
+<h3>
+Text Model</h3>
+<tt>IDocument</tt> is the major text model abstraction. It provides content
+management, position management using position categories, document partition
+management, searching, and change notification.&nbsp; In order to be notified
+about document changes, an object must implements <tt>IDocumentListener</tt>
+and must be registered with the document. Position updating in responds
+to a document change is performed by implementers of <tt>IDocumentPositionUpdater</tt>.
+Partition updating in responds to a document change is performed by implements
+of <tt>IDocumentPartitioner</tt>. In order to be notified about document
+partition changes, objects must implement <tt>IDocumentParititoningListener</tt>
+and must be registered with the document.
+<p>The package contains default implementations for document position updaters
+and for documents. <tt>AbstractDocument</tt> uses <tt>ITextStorage</tt>
+for storing and managing its content and <tt>ILineTracker</tt> to maintain
+a line structure of its content. As defaults a gap text implementation
+of <tt>ITextStore</tt> is provided, together with a line tracker understanding
+the three standard line delimiters ("\r", "\n", "\r\n") and a&nbsp; line
+tracker which can be freely configured to consider any given set of strings
+as valid line delimiters.
+<h3>
+Text Viewer</h3>
+<tt>ITextViewer</tt> defines the concept of a document based, editiable
+viewer. <tt>ITextViewer</tt> offers the following functionality:
+<ul>
+<li>
+present a document</li>
+
+<li>
+event consumption (<tt>IEventConsumer</tt>)</li>
+
+<li>
+viewport tracking and notification (<tt>IIViewportListener</tt>)</li>
+
+<li>
+change notification (<tt>ITextListener</tt>, <tt>ITextInputListener</tt>)</li>
+
+<li>
+listeners (combined view/model notification, input document)</li>
+
+<li>
+standard text editing functions plus text hover support</li>
+
+<li>
+visual region support</li>
+</ul>
+An ITextViewer supports the following plugins
+<ul>
+<li>
+<tt>IUndoManager</tt> for the undo/redo mechanism</li>
+
+<li>
+<tt>IDoubleClickStrategy</tt> for partition type specific behavior on mouse
+double click</li>
+
+<li>
+<tt>IAutoIndentStrategy</tt>&nbsp; for content type specific behavior on
+inserting a line break</li>
+
+<li>
+<tt>ITextHover</tt> for content type specific behavior when overing over
+text</li>
+</ul>
+The package provides default implementations for all these interfaces.
+</body>
+</html>
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationDamager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationDamager.java
new file mode 100644
index 000000000..3f2ba767b
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationDamager.java
@@ -0,0 +1,54 @@
+package org.eclipse.jface.text.presentation;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+
+
+/**
+ * Presentation damagers are used by a presentation reconciler to determine the
+ * region of the document's presentation which must be rebuilt because of the
+ * occurrence of a document change. A presentation damager is assumed to be
+ * specific for a particular document content type. A presentation damager is
+ * expected to return a damage region which is a valid input for a presentation repairer.
+ * I.e. having access to the damage region only the repairer must be able to derive
+ * all the information neede to sucessfully repair this region.<p>
+ * This interface must either be implemented by clients or clients use the rule-based
+ * default implementation <code>RuleBasedDamagerRepairer</code>. Implementers should be
+ * registered with a presentation reconciler in order get involved in the reconciling
+ * process.
+ *
+ * @see IPresentationReconciler
+ * @see IDocument
+ * @see DocumentEvent
+ * @see IPresentationRepairer
+ */
+public interface IPresentationDamager {
+
+ /**
+ * Tells the presentation damager on which document it will work.
+ *
+ * @param document the damager's working document
+ */
+ void setDocument(IDocument document);
+
+ /**
+ * Returns the damage in the document's presentation caused by the given document change.
+ * The damage is restricted to the specified partition for which the presentation damager is
+ * responsible. The damage may also depend on whether the document change also caused changes
+ * of the document's partitioning.
+ *
+ * @param partition the partition inside which the damage must be determined
+ * @param event the event describing the change whose damage must be determined
+ * @param documentPartitioningChange indicates whether the given change changed the document's partitioning
+ * @return the computed damage
+ */
+ IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, boolean documentPartitioningChanged);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java
new file mode 100644
index 000000000..962e4b3ea
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationReconciler.java
@@ -0,0 +1,70 @@
+package org.eclipse.jface.text.presentation;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+/**
+ * An <code>IPresentationReconciler</code> defines and maintains the representation of a
+ * text viewer's document in the presence of changes applied to the document.
+ * An <code>IPresentationReconciler</code> is a <code>ITextViewer</code> add-on.<p>
+ * The presentation reconciler keeps track of changes applied to the text viewer. It sends
+ * each change to presentation damagers which are registered for the content types of the
+ * regions in which the change occurred. The presentation reconciler passes the computed
+ * damage to presentation repairers which construct text presentations. Those text presentation
+ * when applied to the presentation reconciler's text viewer bring the document's presentation
+ * in sync with the document's content and thus repair the damage. A presentation damager
+ * is expected to return damage which is a valid input for a presentation repairer registered
+ * for the same content type as the damager.<p>
+ * A presentation reconciler should always be configured with damager/repairer pairs. I.e.
+ * for each damager there should be a corresponding repairer.<p>
+ * The interface can be implemented by clients. By default, clients use
+ * <code>PresentationReconciler</code> as the standard implementer of this interface.
+ *
+ * @see ITextViewer
+ * @see IPresentationDamager
+ * @see IPresentationRepairer
+ * @see org.eclipse.jface.text.TextPresentation
+ */
+public interface IPresentationReconciler {
+
+ /**
+ * Installs this presentation reconciler on the given text viewer. After
+ * this method has been finished, the reconciler is operational. I.e., it
+ * works without requesting further client actions until <code>uninstall</code>
+ * is called.
+ *
+ * @param textViewer the viewer on which this presentation reconciler is installed
+ */
+ void install(ITextViewer viewer);
+
+ /**
+ * Removes the reconciler from the text viewer it has previously been
+ * installed on.
+ */
+ void uninstall();
+
+ /**
+ * Returns the presentation damager registered with this presentation reconciler
+ * for the specified content type.
+ *
+ * @param contentType the content type for which to determine the damager
+ * @return the presentation damager registered for the given content type, or
+ * <code>null</code> if there is no such strategy
+ */
+ IPresentationDamager getDamager(String contentType);
+
+ /**
+ * Returns the presentation repairer registered with this presentation reconciler
+ * for the specified content type.
+ *
+ * @param contentType the content type for which to determine the repairer
+ * @return the presentation repairer registered for the given content type, or
+ * <code>null</code> if there is no such strategy
+ */
+ IPresentationRepairer getRepairer(String contentType);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationRepairer.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationRepairer.java
new file mode 100644
index 000000000..8217dcf95
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/IPresentationRepairer.java
@@ -0,0 +1,50 @@
+package org.eclipse.jface.text.presentation;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.TextPresentation;
+
+
+/**
+ * Presentation repairers are used by a presentation reconciler
+ * to rebuild a damaged region in a document's presentation. A presentation repairer
+ * is assumed to be specific for a particular document content type. The presentation
+ * repairer gets the region which it should repair and constructs a "repair description"
+ * The presentation repairer adds the individual steps of this sequence into the
+ * text presentation it gets passed in.<p>
+ * This interface must either be implemented by clients or clients use the rule-based
+ * default implementation <code>RuleBasedDamagerRepairer</code>. Implementers should be
+ * registered with a presentation reconciler in order get involved in the reconciling
+ * process.
+ *
+ * @see IPresentationReconciler
+ * @see IDocument
+ * @see org.eclipse.swt.custom.StyleRange
+ * @see TextPresentation
+ */
+public interface IPresentationRepairer {
+
+
+ /**
+ * Tells the presentation repairer on which document it will work.
+ *
+ * @param document the damager's working document
+ */
+ void setDocument(IDocument document);
+
+ /**
+ * Fills the given presentation with the style ranges which when applied to the
+ * presentation reconciler's text viewer repair the presentational damage described by
+ * the given region.
+ *
+ * @param presentation the text presentation to be filled by this repairer
+ * @param damage the damage to be repaired
+ */
+ void createPresentation(TextPresentation presentation, ITypedRegion damage);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/PresentationReconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/PresentationReconciler.java
new file mode 100644
index 000000000..6981d4285
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/PresentationReconciler.java
@@ -0,0 +1,465 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.presentation;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IDocumentPartitioningListener;
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.TypedPosition;
+import org.eclipse.jface.util.Assert;
+
+
+
+/**
+ * Standard implementation of <code>IPresentationReconciler</code>.
+ * This implementation assumes that the tasks performed by its
+ * presentation damagers and repairers are lightweight and of low cost.
+ * This presentation reconciler runs in the UI thread and always repairs
+ * the complete damage caused by a document change rather than just the
+ * portion overlapping with the viewer's viewport.<p>
+ * Usually, clients instantiate this class and configure it before using it.
+ */
+public class PresentationReconciler implements IPresentationReconciler {
+
+ /** Prefix of the name of the position category for tracking damage regions. */
+ protected final static String TRACKED_PARTITION= "__reconciler_tracked_partition"; //$NON-NLS-1$
+
+
+ /**
+ * Internal listener class.
+ */
+ class InternalListener implements
+ ITextInputListener, IDocumentListener, ITextListener,
+ IDocumentPartitioningListener, IDocumentPartitioningListenerExtension {
+
+ /** Set to <code>true</code> if between a document about to be changed and a changed event. */
+ private boolean fDocumentChanging= false;
+
+ /*
+ * @see ITextInputListener#inputDocumentAboutToBeChanged
+ */
+ public void inputDocumentAboutToBeChanged(IDocument oldDocument, IDocument newDocument) {
+ if (oldDocument != null) {
+ try {
+
+ fViewer.removeTextListener(this);
+ oldDocument.removeDocumentListener(this);
+ oldDocument.removeDocumentPartitioningListener(this);
+
+ oldDocument.removePositionUpdater(fPositionUpdater);
+ oldDocument.removePositionCategory(fPositionCategory);
+
+ } catch (BadPositionCategoryException x) {
+ // should not happend for former input documents;
+ }
+ }
+ }
+
+ /*
+ * @see ITextInputListener#inputDocumenChanged
+ */
+ public void inputDocumentChanged(IDocument oldDocument, IDocument newDocument) {
+
+ fDocumentChanging= false;
+
+ if (newDocument != null) {
+
+ newDocument.addPositionCategory(fPositionCategory);
+ newDocument.addPositionUpdater(fPositionUpdater);
+
+ newDocument.addDocumentPartitioningListener(this);
+ newDocument.addDocumentListener(this);
+ fViewer.addTextListener(this);
+
+ setDocumentToDamagers(newDocument);
+ setDocumentToRepairers(newDocument);
+ processDamage(new Region(0, newDocument.getLength()), newDocument);
+ }
+ }
+
+ /*
+ * @see IDocumentPartitioningListener#documentPartitioningChanged
+ */
+ public void documentPartitioningChanged(IDocument document) {
+ if (!fDocumentChanging)
+ processDamage(new Region(0, document.getLength()), document);
+ else
+ fDocumentPartitioningChanged= true;
+ }
+
+ /*
+ * @see IDocumentPartitioningListenerExtension#documentPartitioningChanged
+ * @since 2.0
+ */
+ public void documentPartitioningChanged(IDocument document, IRegion changedRegion) {
+ if (!fDocumentChanging) {
+ processDamage(new Region(changedRegion.getOffset(), changedRegion.getLength()), document);
+ } else {
+ fDocumentPartitioningChanged= true;
+ fChangedDocumentPartitions= changedRegion;
+ }
+ }
+
+ /*
+ * @see IDocumentListener#documentAboutToBeChanged
+ */
+ public void documentAboutToBeChanged(DocumentEvent e) {
+
+ fDocumentChanging= true;
+
+ try {
+ int offset= e.getOffset() + e.getLength();
+ fRememberedPosition= new TypedPosition(e.getDocument().getPartition(offset));
+ e.getDocument().addPosition(fPositionCategory, fRememberedPosition);
+ } catch (BadLocationException x) {
+ // can not happen
+ } catch (BadPositionCategoryException x) {
+ // should not happen on input elements
+ }
+ }
+
+ /*
+ * @see IDocumentListener#documentChanged
+ */
+ public void documentChanged(DocumentEvent e) {
+ try {
+ e.getDocument().removePosition(fPositionCategory, fRememberedPosition);
+ } catch (BadPositionCategoryException x) {
+ // can not happen on input documents
+ }
+
+ fDocumentChanging= false;
+ }
+
+ /*
+ * @see ITextListener#textChanged
+ */
+ public void textChanged(TextEvent e) {
+
+ if (!e.getViewerRedrawState())
+ return;
+
+ DocumentEvent de= e.getDocumentEvent();
+
+ if (de == null) {
+
+ IDocument d= fViewer.getDocument();
+ processDamage(new Region(0, d.getLength()), d);
+
+ } else {
+
+ IRegion damage= getDamage(de);
+ if (damage != null)
+ processDamage(damage, de.getDocument());
+ }
+
+ fDocumentPartitioningChanged= false;
+ fChangedDocumentPartitions= null;
+ }
+ };
+
+ /** The map of presentation damagers. */
+ private Map fDamagers;
+ /** The map of presentation repairers. */
+ private Map fRepairers;
+ /** The target viewer. */
+ private ITextViewer fViewer;
+ /** The internal listener. */
+ private InternalListener fInternalListener= new InternalListener();
+ /** The name of the position category to track damage regions. */
+ private String fPositionCategory;
+ /** The position updated for the damage regions' position category. */
+ private IPositionUpdater fPositionUpdater;
+ /** The positions representing the damage regions. */
+ private TypedPosition fRememberedPosition;
+ /** Flag indicating the receipt of a partitioning changed notification. */
+ private boolean fDocumentPartitioningChanged= false;
+ /** The range covering the changed parititoning. */
+ private IRegion fChangedDocumentPartitions= null;
+
+
+ /**
+ * Creates a new presentation reconciler. There are no damagers or repairers
+ * registered with this reconciler.
+ */
+ public PresentationReconciler() {
+ super();
+ fPositionCategory= TRACKED_PARTITION + hashCode();
+ fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
+ }
+
+ /**
+ * Registers a given presentation damager for a particular content type.
+ * If there is already a damager registered for this type, the new damager
+ * is registered instead of the old one.
+ *
+ * @param damager the presentation damager to register, or <code>null</code> to remove an existing one
+ * @param contentType the content type under which to register
+ */
+ public void setDamager(IPresentationDamager damager, String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fDamagers == null)
+ fDamagers= new HashMap();
+
+ if (damager == null)
+ fDamagers.remove(contentType);
+ else
+ fDamagers.put(contentType, damager);
+ }
+
+ /**
+ * Registers a given presentation repairer for a particular content type.
+ * If there is already a repairer registered for this type, the new repairer
+ * is registered instead of the old one.
+ *
+ * @param repairer the presentation repairer to register, or <code>null</code> to remove an existing one
+ * @param contentType the content type under which to register
+ */
+ public void setRepairer(IPresentationRepairer repairer, String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fRepairers == null)
+ fRepairers= new HashMap();
+
+ if (repairer == null)
+ fRepairers.remove(contentType);
+ else
+ fRepairers.put(contentType, repairer);
+ }
+
+ /*
+ * @see IPresentationReconciler#install
+ */
+ public void install(ITextViewer viewer) {
+ Assert.isNotNull(viewer);
+
+ fViewer= viewer;
+ fViewer.addTextInputListener(fInternalListener);
+ }
+
+ /*
+ * @see IPresentationReconciler#uninstall
+ */
+ public void uninstall() {
+ fViewer.removeTextInputListener(fInternalListener);
+ }
+
+ /*
+ * @see IPresentationReconciler#getDamager
+ */
+ public IPresentationDamager getDamager(String contentType) {
+
+ if (fDamagers == null)
+ return null;
+
+ return (IPresentationDamager) fDamagers.get(contentType);
+ }
+
+ /*
+ * @see IPresentationReconciler#getRepairer
+ */
+ public IPresentationRepairer getRepairer(String contentType) {
+
+ if (fRepairers == null)
+ return null;
+
+ return (IPresentationRepairer) fRepairers.get(contentType);
+ }
+
+ /**
+ * Informs all registed damagers about the document on which they will work.
+ *
+ * @param document the document on which to work
+ */
+ private void setDocumentToDamagers(IDocument document) {
+ if (fDamagers != null) {
+ Iterator e= fDamagers.values().iterator();
+ while (e.hasNext()) {
+ IPresentationDamager damager= (IPresentationDamager) e.next();
+ damager.setDocument(document);
+ }
+ }
+ }
+
+ /**
+ * Informs all registed repairers about the document on which they will work.
+ *
+ * @param document the document on which to work
+ */
+ private void setDocumentToRepairers(IDocument document) {
+ if (fRepairers != null) {
+ Iterator e= fRepairers.values().iterator();
+ while (e.hasNext()) {
+ IPresentationRepairer repairer= (IPresentationRepairer) e.next();
+ repairer.setDocument(document);
+ }
+ }
+ }
+
+ /**
+ * Constructs a "repair description" for the given damage and returns
+ * this description as a text presentation. For this, it queries the
+ * partitioning of the damage region and asks for each partition an
+ * appropriate presentation repairer to construct the "repair description"
+ * for this partition.
+ *
+ * @param damage the damage to be repaired
+ * @param document the document whose presentation must be repaired
+ * @return the presentation repair descritption as text presentation
+ */
+ private TextPresentation createPresentation(IRegion damage, IDocument document) {
+ try {
+
+ TextPresentation presentation= new TextPresentation();
+
+ ITypedRegion[] partitioning= document.computePartitioning(damage.getOffset(), damage.getLength());
+ for (int i= 0; i < partitioning.length; i++) {
+ ITypedRegion r= partitioning[i];
+ IPresentationRepairer repairer= getRepairer(r.getType());
+ if (repairer != null)
+ repairer.createPresentation(presentation, r);
+ }
+
+ return presentation;
+
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Checks for the first and the last affected partition and calls their damagers.
+ * Invalidates everything from the start of the damage for the first partition
+ * until the end of the damage for the last partition.
+ *
+ * @param e the event describing the document change
+ * @return the damaged caused by the change
+ */
+ private IRegion getDamage(DocumentEvent e) {
+
+ IRegion damage= null;
+
+ try {
+
+ ITypedRegion partition= e.getDocument().getPartition(e.getOffset());
+ IPresentationDamager damager= getDamager(partition.getType());
+ if (damager == null)
+ return null;
+
+ IRegion r= damager.getDamageRegion(partition, e, fDocumentPartitioningChanged);
+
+ if (!fDocumentPartitioningChanged) {
+ damage= r;
+ } else {
+
+ int damageEnd= getDamageEndOffset(e);
+
+ int parititionDamageEnd= -1;
+ if (fChangedDocumentPartitions != null)
+ parititionDamageEnd= fChangedDocumentPartitions.getOffset() + fChangedDocumentPartitions.getLength();
+
+ int end= Math.max(damageEnd, parititionDamageEnd);
+
+ damage= end == -1 ? r : new Region(r.getOffset(), end - r.getOffset());
+ }
+
+ } catch (BadLocationException x) {
+ }
+
+ return damage;
+ }
+
+ /**
+ * Returns the end offset of the damage. If a partition has been splitted by
+ * the given document event also the second half of the original
+ * partition must be considered. This is achieved by using the remembered
+ * partition range.
+ *
+ * @param e the event describing the change
+ * @return the damage end offset (excluding)
+ * @exception BadLocationException if method accesses invalid offset
+ */
+ private int getDamageEndOffset(DocumentEvent e) throws BadLocationException {
+
+ IDocument d= e.getDocument();
+
+ int length= 0;
+ if (e.getText() != null) {
+ length= e.getText().length();
+ if (length > 0)
+ -- length;
+ }
+
+ ITypedRegion partition= d.getPartition(e.getOffset() + length);
+ int endOffset= partition.getOffset() + partition.getLength();
+ if (endOffset == e.getOffset())
+ return -1;
+
+ int end= fRememberedPosition.getOffset() + fRememberedPosition.getLength();
+ if (endOffset < end)
+ partition= d.getPartition(end);
+
+ IPresentationDamager damager= getDamager(partition.getType());
+ if (damager == null)
+ return -1;
+
+ IRegion r= damager.getDamageRegion(partition, e, fDocumentPartitioningChanged);
+
+ return r.getOffset() + r.getLength();
+ }
+
+ /**
+ * Processes the given damage.
+ * @param damage the damage to be repaired
+ * @param document the document whose presentation must be repaired
+ */
+ private void processDamage(IRegion damage, IDocument document) {
+ if (damage != null && damage.getLength() > 0) {
+ TextPresentation p= createPresentation(damage, document);
+ if (p != null && !p.isEmpty())
+ applyTextRegionCollection(p);
+ }
+ }
+
+ /**
+ * Applies the given text presentation to the text viewer the presentation
+ * reconciler is installed on.
+ *
+ * @param presentation the text presentation to be applied to the text viewer
+ */
+ private void applyTextRegionCollection(TextPresentation presentation) {
+ fViewer.changeTextPresentation(presentation, false);
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/package.html b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/package.html
new file mode 100644
index 000000000..9574694de
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/presentation/package.html
@@ -0,0 +1,24 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="IBM">
+ <meta name="GENERATOR" content="Mozilla/4.51 [en] (WinNT; I) [Netscape]">
+ <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides a presentation reconciler add-on for an <tt>ITextViewer</tt>.
+A presentation reconciler keep the presentation (styles and colors) in
+sync with the content of the document serving as the <tt>ITextViewer</tt>'s
+input.
+<h2>
+Package Specification</h2>
+<tt>IPresentationReconciler</tt> defines the concept of a presentation
+reconciler. It collaborates with content type specific presentation damagers
+(<tt>IPresentationDamager</tt>) which for a given document change determine
+the region of the presentation which must be rebuild, and content type
+specific presentation repairers (<tt>IPresentationRepairer</tt>) which
+construct for a given damage region the document presentation. The package
+contains a default implementation of <tt>IPresentationReconciler </tt>(<tt>PresentationReconciler</tt>).
+</body>
+</html>
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java
new file mode 100644
index 000000000..36c16479c
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java
@@ -0,0 +1,468 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.reconciler;
+
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.swt.widgets.Listener;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TypedRegion;
+import org.eclipse.jface.util.Assert;
+
+
+
+/**
+ * Abstract implementation of <code>IReconciler</code>. The reconciler
+ * listens to input document changes as well as changes of
+ * the input document of the text viewer it is installed on. Depending on
+ * its configuration it manages the received change notifications in a
+ * queue folding neighboring or overlapping changes together. The reconciler
+ * processes the dirty regions as a background activity after having waited for further
+ * changes for the configured duration of time. A reconciler is started using its
+ * <code>install</code> method. As a first step <code>initialProcess</code> is
+ * executed in the background. Then, the reconciling thread waits for changes that
+ * need to be reconciled. A reconciler can be resumed by calling <code>forceReconciling</code>
+ * independent from the existence of actual changes. This mechanism is for subclasses only.
+ * It is the clients responsibility to stop a reconciler using its <code>uninstall</code>
+ * method. Unstopped reconcilers do not free their resources.<p>
+ * It is subclass responsibility to specify how dirty regions are processed.
+ *
+ * @see IReconciler
+ * @see IDocumentListener
+ * @see ITextInputListener
+ * @see DirtyRegion
+ * @since 2.0
+ */
+abstract public class AbstractReconciler implements IReconciler {
+
+
+ /**
+ * Background thread for the reconciling activity.
+ */
+ class BackgroundThread extends Thread {
+
+ /** Has the reconciler been canceled */
+ private boolean fCanceled= false;
+ /** Has the reconciler been reset */
+ private boolean fReset= false;
+ /** Has a change been applied */
+ private boolean fIsDirty= false;
+ /** Is a reconciling strategy active */
+ private boolean fIsActive= false;
+
+ /**
+ * Creates a new background thread. The thread
+ * runs with minimal priority.
+ *
+ * @param name the thread's name
+ */
+ public BackgroundThread(String name) {
+ super(name);
+ setPriority(Thread.MIN_PRIORITY);
+ setDaemon(true);
+ }
+
+ /**
+ * Returns whether a reconciling strategy is active right now.
+ *
+ * @return <code>true</code> if a activity is active
+ */
+ public boolean isActive() {
+ return fIsActive;
+ }
+
+ /**
+ * Cancels the background thread.
+ */
+ public void cancel() {
+ fCanceled= true;
+ synchronized (fDirtyRegionQueue) {
+ fDirtyRegionQueue.notifyAll();
+ }
+ }
+
+ /**
+ * Reset the background thread as the text viewer has been changed,
+ */
+ public void reset() {
+
+ if (fDelay > 0) {
+
+ synchronized (this) {
+ fIsDirty= true;
+ fReset= true;
+ }
+
+ } else {
+
+ synchronized(this) {
+ fIsDirty= true;
+ }
+
+ synchronized (fDirtyRegionQueue) {
+ fDirtyRegionQueue.notifyAll();
+ }
+ }
+
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19525
+ reconcilerReset();
+
+ }
+
+ /**
+ * The background activity. Waits until there is something in the
+ * queue managing the changes that have been applied to the text viewer.
+ * Removes the first change from the queue and process it.<p>
+ * Calls <code>initialProcess</code> on entrance.
+ */
+ public void run() {
+
+ synchronized (fDirtyRegionQueue) {
+ try {
+ fDirtyRegionQueue.wait(fDelay);
+ } catch (InterruptedException x) {
+ }
+ }
+
+ initialProcess();
+
+ while (!fCanceled) {
+
+ synchronized (fDirtyRegionQueue) {
+ try {
+ fDirtyRegionQueue.wait(fDelay);
+ } catch (InterruptedException x) {
+ }
+ }
+
+ if (fCanceled)
+ break;
+
+ if (!fIsDirty)
+ continue;
+
+ if (fReset) {
+ synchronized (this) {
+ fReset= false;
+ }
+ continue;
+ }
+
+ DirtyRegion r= null;
+ synchronized (fDirtyRegionQueue) {
+ r= fDirtyRegionQueue.removeNextDirtyRegion();
+ }
+
+ fIsActive= true;
+
+ if (fProgressMonitor != null)
+ fProgressMonitor.setCanceled(false);
+
+ process(r);
+
+ synchronized (this) {
+ fIsDirty= false;
+ }
+
+ fIsActive= false;
+ }
+ }
+ };
+
+ /**
+ * Internal document listener and text input listener.
+ */
+ class Listener implements IDocumentListener, ITextInputListener {
+
+ /*
+ * @see IDocumentListener#documentAboutToBeChanged
+ */
+ public void documentAboutToBeChanged(DocumentEvent e) {
+ }
+
+ /*
+ * @see IDocumentListener#documentChanged
+ */
+ public void documentChanged(DocumentEvent e) {
+
+ if (fProgressMonitor != null && fThread.isActive())
+ fProgressMonitor.setCanceled(true);
+
+ if (fIsIncrementalReconciler)
+ createDirtyRegion(e);
+
+ fThread.reset();
+ }
+
+ /*
+ * @see ITextInputListener#inputDocumentAboutToBeChanged
+ */
+ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
+
+ if (oldInput == fDocument) {
+
+ if (fDocument != null)
+ fDocument.removeDocumentListener(this);
+
+ if (fIsIncrementalReconciler) {
+ fDirtyRegionQueue.purgeQueue();
+ if (fDocument != null && fDocument.getLength() > 0) {
+ DocumentEvent e= new DocumentEvent(fDocument, 0, fDocument.getLength(), null);
+ createDirtyRegion(e);
+ }
+ }
+
+ fDocument= null;
+ }
+ }
+
+ /*
+ * @see ITextInputListener#inputDocumentChanged
+ */
+ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
+
+ if (newInput == null)
+ return;
+
+ fDocument= newInput;
+ reconcilerDocumentChanged(fDocument);
+
+ fDocument.addDocumentListener(this);
+
+ forceReconciling();
+ }
+ };
+
+ /** Queue to manage the changes applied to the text viewer */
+ private DirtyRegionQueue fDirtyRegionQueue;
+ /** The background thread */
+ private BackgroundThread fThread;
+ /** Internal document and text input listener */
+ private Listener fListener;
+ /** The background thread delay */
+ private int fDelay= 500;
+ /** Are there incremental reconciling strategies? */
+ private boolean fIsIncrementalReconciler= true;
+ /** The progress monitor used by this reconciler */
+ private IProgressMonitor fProgressMonitor;
+
+ /** The text viewer's document */
+ private IDocument fDocument;
+ /** The text viewer */
+ private ITextViewer fViewer;
+
+
+ /**
+ * Processes a dirty region. If the dirty region is <code>null</code> the whole
+ * document is consider being dirty. The dirty region is partitioned by the
+ * document and each partition is handed over to a reconciling strategy registered
+ * for the partition's content type.
+ *
+ * @param dirtyRegion the dirty region to be processed
+ */
+ abstract protected void process(DirtyRegion dirtyRegion);
+
+ /**
+ * Hook called when the document whose contents should be reconciled
+ * has been changed, i.e., the input document of the text viewer this
+ * reconciler is installed on. Usually, subclasses use this hook to
+ * inform all their reconciling strategies about the change.
+ *
+ * @param newDocument the new reconciler document
+ */
+ abstract protected void reconcilerDocumentChanged(IDocument newDocument);
+
+
+ /**
+ * Creates a new reconciler without configuring it.
+ */
+ protected AbstractReconciler() {
+ super();
+ }
+
+ /**
+ * Tells the reconciler how long it should wait for further text changes before
+ * activating the appropriate reconciling strategies.
+ *
+ * @param delay the duration in milli seconds of a change collection period.
+ */
+ public void setDelay(int delay) {
+ fDelay= delay;
+ }
+
+ /**
+ * Tells the reconciler whether any of the available reconciling strategies
+ * is interested in getting detailed dirty region information or just in the
+ * fact the the document has been changed. In the second case, the reconciling
+ * can not incrementally be pursued.
+ *
+ * @param isIncremental indicates whether this reconciler will be configured with
+ * incremental reconciling strategies
+ *
+ * @see DirtyRegion
+ * @see IReconcilingStrategy
+ */
+ public void setIsIncrementalReconciler(boolean isIncremental) {
+ fIsIncrementalReconciler= isIncremental;
+ }
+
+ /**
+ * Sets the progress monitor of this reconciler.
+ *
+ * @param monitor the monitor to be used
+ */
+ public void setProgressMonitor(IProgressMonitor monitor) {
+ fProgressMonitor= monitor;
+ }
+
+ /**
+ * Returns whether any of the reconciling strategies is interested in
+ * detailed dirty region information.
+ *
+ * @return whether this reconciler is incremental
+ *
+ * @see IReconcilingStrategy
+ */
+ protected boolean isIncrementalReconciler() {
+ return fIsIncrementalReconciler;
+ }
+
+ /**
+ * Returns the input document of the text viewer this reconciler is installed on.
+ *
+ * @return the reconciler document
+ */
+ protected IDocument getDocument() {
+ return fDocument;
+ }
+
+ /**
+ * Returns the text viewer this reconciler is installed on.
+ *
+ * @return the text viewer this reconciler is installed on
+ */
+ protected ITextViewer getTextViewer() {
+ return fViewer;
+ }
+
+ /**
+ * Returns the progress monitor of this reconciler.
+ *
+ * @return the progress monitor of this reconciler
+ */
+ protected IProgressMonitor getProgressMonitor() {
+ return fProgressMonitor;
+ }
+
+ /*
+ * @see IReconciler#install
+ */
+ public void install(ITextViewer textViewer) {
+
+ Assert.isNotNull(textViewer);
+
+ fViewer= textViewer;
+
+ fListener= new Listener();
+ fViewer.addTextInputListener(fListener);
+
+ fDirtyRegionQueue= new DirtyRegionQueue();
+ fThread= new BackgroundThread(getClass().getName());
+ }
+
+ /*
+ * @see IReconciler#uninstall
+ */
+ public void uninstall() {
+ if (fListener != null) {
+
+ fViewer.removeTextInputListener(fListener);
+ if (fDocument != null) fDocument.removeDocumentListener(fListener);
+ fListener= null;
+
+ // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135
+ BackgroundThread bt= fThread;
+ fThread= null;
+ bt.cancel();
+ }
+ }
+
+ /**
+ * Creates a dirty region for a document event and adds it to the queue.
+ *
+ * @param e the document event for which to create a dirty region
+ */
+ private void createDirtyRegion(DocumentEvent e) {
+
+ if (e.getLength() == 0 && e.getText() != null) {
+ // Insert
+ fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getText().length(), DirtyRegion.INSERT, e.getText()));
+
+ } else if (e.getText() == null || e.getText().length() == 0) {
+ // Remove
+ fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getLength(), DirtyRegion.REMOVE, null));
+
+ } else {
+ // Replace (Remove + Insert)
+ fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getLength(), DirtyRegion.REMOVE, null));
+ fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getText().length(), DirtyRegion.INSERT, e.getText()));
+ }
+ }
+
+ /**
+ * This method is called on startup of the background activity. It is called only
+ * once during the life time of the reconciler. Clients may reimplement this method.
+ */
+ protected void initialProcess() {
+ }
+
+ /**
+ * Forces the reconciler to reconcile the structure of the whole document.
+ * Clients may extend this method.
+ */
+ protected void forceReconciling() {
+
+ if (fIsIncrementalReconciler) {
+ DocumentEvent e= new DocumentEvent(fDocument, 0, 0, fDocument.get());
+ createDirtyRegion(e);
+ }
+
+ // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135
+ if (fThread == null)
+ return;
+
+ if (!fThread.isAlive())
+ fThread.start();
+ else
+ fThread.reset();
+ }
+
+ /**
+ * Hook that is called after the reconciler thread has been reset.
+ */
+ protected void reconcilerReset() {
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegion.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegion.java
new file mode 100644
index 000000000..01c57a65a
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegion.java
@@ -0,0 +1,86 @@
+package org.eclipse.jface.text.reconciler;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.text.ITypedRegion;
+
+/**
+ * A dirty region describes a document range which has been changed.
+ */
+public class DirtyRegion implements ITypedRegion {
+
+ /** Definitions of edit types */
+ final static public String INSERT= "__insert"; //$NON-NLS-1$
+ final static public String REMOVE= "__remove"; //$NON-NLS-1$
+
+ /** The region's offset */
+ private int fOffset;
+ /** The region's length */
+ private int fLength;
+ /** Indicates the type of the applied change */
+ private String fType;
+ /** The text which has been inserted */
+ private String fText;
+
+ /**
+ * Creates a new dirty region.
+ *
+ *
+ * @param offset the offset within the document where the change occurred
+ * @param length the length of the text within the document that changed
+ * @param type the type of change that this region represents: <code>INSERT</code> or <code>REMOVE</code>
+ * @param text the substitution text
+ */
+ public DirtyRegion(int offset, int length, String type, String text) {
+ fOffset= offset;
+ fLength= length;
+ fType= type;
+ fText= text;
+ }
+
+ /*
+ * @see ITypedRegion#getOffset()
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /*
+ * @see ITypedRegion#getLength()
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /*
+ * @see ITypedRegion#getType
+ */
+ public String getType() {
+ return fType;
+ }
+
+ /**
+ * Returns the text that changed as part of the region change.
+ *
+ * @return the changed text
+ */
+ public String getText() {
+ return fText;
+ }
+
+ /**
+ * Modify the receiver so that it encompasses the region specified by the dirty region.
+ *
+ * @param dr the dirty region with which to merge
+ */
+ void mergeWith(DirtyRegion dr) {
+ int start= Math.min(fOffset, dr.fOffset);
+ int end= Math.max(fOffset + fLength, dr.fOffset + dr.fLength);
+ fOffset= start;
+ fLength= end - start;
+ fText= (dr.fText == null ? fText : (fText == null) ? dr.fText : fText + dr.fText);
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegionQueue.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegionQueue.java
new file mode 100644
index 000000000..6e4e942ba
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegionQueue.java
@@ -0,0 +1,98 @@
+package org.eclipse.jface.text.reconciler;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+
+
+/**
+ * Queue used by <code>Reconciler</code> to manage dirty regions. When a dirty region
+ * is inserted into the queue, the queue tries to fold it into the neighboring dirty region.
+ *
+ * @see Reconciler
+ * @see DirtyRegion
+ */
+class DirtyRegionQueue {
+
+ /** The list of dirty regions */
+ private List fDirtyRegions= new ArrayList();
+
+ /**
+ * Creates a new empty dirty region.
+ */
+ public DirtyRegionQueue() {
+ super();
+ }
+
+ /**
+ * Adds a dirty region to the end of the dirty-region queue.
+ *
+ * @param dr the dirty region to add
+ */
+ public void addDirtyRegion(DirtyRegion dr) {
+ // If the dirty region being added is directly after the last dirty
+ // region on the queue then merge the two dirty regions together.
+ DirtyRegion lastDR= getLastDirtyRegion();
+ boolean wasMerged= false;
+ if (lastDR != null)
+ if (lastDR.getType() == dr.getType())
+ if (lastDR.getType() == DirtyRegion.INSERT) {
+ if (lastDR.getOffset() + lastDR.getLength() == dr.getOffset()) {
+ lastDR.mergeWith(dr);
+ wasMerged= true;
+ }
+ } else if (lastDR.getType() == DirtyRegion.REMOVE) {
+ if (dr.getOffset() + dr.getLength() == lastDR.getOffset()) {
+ lastDR.mergeWith(dr);
+ wasMerged= true;
+ }
+ }
+
+ if (!wasMerged)
+ // Don't merge- just add the new one onto the queue.
+ fDirtyRegions.add(dr);
+ }
+
+ /**
+ * Returns the last dirty region that was added to the queue.
+ *
+ * @return the last DirtyRegion on the queue
+ */
+ private DirtyRegion getLastDirtyRegion() {
+ int size= fDirtyRegions.size();
+ return (size == 0 ? null : (DirtyRegion) fDirtyRegions.get(size - 1));
+ }
+
+ /**
+ * Returns the number of regions in the queue.
+ *
+ * @return the dirty-region queue-size
+ */
+ public int getSize() {
+ return fDirtyRegions.size();
+ }
+
+ /**
+ * Throws away all entries in the queue.
+ */
+ public void purgeQueue() {
+ fDirtyRegions.clear();
+ }
+
+ /**
+ * Removes and returns the first dirty region in the queue
+ *
+ * @return the next dirty region on the queue
+ */
+ public DirtyRegion removeNextDirtyRegion() {
+ if (fDirtyRegions.size() == 0)
+ return null;
+ DirtyRegion dr= (DirtyRegion) fDirtyRegions.get(0);
+ fDirtyRegions.remove(0);
+ return dr;
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java
new file mode 100644
index 000000000..fa3d8e530
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconciler.java
@@ -0,0 +1,56 @@
+package org.eclipse.jface.text.reconciler;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.text.ITextViewer;
+
+
+
+/**
+ * An <code>IReconciler</code> defines and maintains a model of the content
+ * of the text viewer's document in the presence of changes applied to this
+ * document. An <code>IReconciler</code> is a <code>ITextViewer</code> add-on.<p>
+ * Reconcilers are assumed to be asynchronous, i.e. they allow a certain
+ * temporal window of inconsistency between the document and the model of
+ * the content of this document. <p>
+ * Reconcilers have a list of <code>IReconcilingStrategy</code> objects
+ * each of which is registered for a particular document content type.
+ * The reconciler uses the strategy objects to react on the changes applied
+ * to the text viewer's document.<p>
+ * The interface can be implemented by clients. By default, clients use
+ * <code>Reconciler</code> as the standard implementer of this interface.
+ *
+ * @see ITextViewer
+ * @see IReconcilingStrategy
+ */
+public interface IReconciler {
+
+ /**
+ * Installs the reconciler on the given text viewer. After this method has been
+ * finished, the reconciler is operational. I.e., it works without requesting
+ * further client actions until <code>uninstall</code> is called.
+ *
+ * @param textViewer the viewer on which the reconciler is installed
+ */
+ void install(ITextViewer textViewer);
+
+ /**
+ * Removes the reconciler from the text viewer it has previously been
+ * installed on.
+ */
+ void uninstall();
+
+ /**
+ * Returns the reconciling strategy registered with the reconciler
+ * for the specified content type.
+ *
+ * @param contentType the content type for which to determine the reconciling strategy
+ * @return the reconciling strategy registered for the given content type, or
+ * <code>null</code> if there is no such strategy
+ */
+ IReconcilingStrategy getReconcilingStrategy(String contentType);
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategy.java
new file mode 100644
index 000000000..8292a23ae
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategy.java
@@ -0,0 +1,54 @@
+package org.eclipse.jface.text.reconciler;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+
+/**
+ * A reconciling strategy is used by an reconciler to reconcile a model
+ * based on text of a particular content type. It provides methods for
+ * incremental as well as non-incremental reconciling.<p>
+ * This interface must be implemented by clients. Implementers should be
+ * registered with a reconciler in order get involved in the reconciling
+ * process.
+ */
+public interface IReconcilingStrategy {
+
+ /**
+ * Tells this reconciling strategy on which document it will
+ * work. This method will be called before any other method
+ * and can be called multiple times. The regions passed to the
+ * other methods always refer to the most recent document
+ * passed into this method.
+ *
+ * @param document the document on which this strategy will work
+ */
+ void setDocument(IDocument document);
+
+ /**
+ * Activates incremental reconciling of the specified dirty region.
+ * As a dirty region might span multiple content types, the segment of the
+ * dirty region which should be investigated is also provided to this
+ * reconciling strategy. The given regions refer to the document passed into
+ * the most recent call of <code>setDocument</code>.
+ *
+ * @param dirtyRegion the document region which has been changed
+ * @param subRegion the sub region in the dirty region which should be reconciled
+ */
+ void reconcile(DirtyRegion dirtyRegion, IRegion subRegion);
+
+ /**
+ * Activates non-incremental reconciling. The reconciling strategy is just told
+ * that there are changes and that it should reconcile the given partition of the
+ * document most recently passed into <code>setDocument</code>.
+ *
+ * @param partition the document partition to be reconciled
+ */
+ void reconcile(IRegion partition);
+} \ No newline at end of file
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategyExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategyExtension.java
new file mode 100644
index 000000000..971fb681e
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/IReconcilingStrategyExtension.java
@@ -0,0 +1,45 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.reconciler;
+
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+
+/**
+ * Extension interface for <code>IReconcilingStrategy</code>.
+ * The new functions are:
+ * <ul>
+ * <li> usage of a progress monitor
+ * <li> initial reconciling step: If a reconciler runs as periodic activity in the background, this
+ * methods offers the reconciler a chance for initializing its startegies and achieving a
+ * reconciled state before the periodic activity starts.
+ * </ul>
+ *
+ * @since 2.0
+ */
+public interface IReconcilingStrategyExtension {
+
+ /**
+ * Tells this reconciling strategy with which progress monitor
+ * it will work. This method will be called before any other
+ * method and can be called multiple times.
+ *
+ * @param monitor the progress monitor with which this strategy will work
+ */
+ void setProgressMonitor(IProgressMonitor monitor);
+
+ /**
+ * Called only once in the life time of this reconciling strategy.
+ */
+ void initialReconcile();
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/MonoReconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/MonoReconciler.java
new file mode 100644
index 000000000..2fd0a670b
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/MonoReconciler.java
@@ -0,0 +1,102 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.reconciler;
+
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.util.Assert;
+
+/**
+ * Standard implementation of <code>IReconciler</code>. The reconciler
+ * is configured with a single <code>IReconcilingStrategy</code> that is
+ * used independ from where a dirty region is located in the reconciler's
+ * document. <p>
+ * Usually, clients instantiate this class and configure it before using it.
+ *
+ * @see IReconciler
+ * @see org.eclipse.jface.text.IDocumentListener
+ * @see org.eclipse.jface.text.ITextInputListener
+ * @see DirtyRegion
+ * @since 2.0
+ */
+public class MonoReconciler extends AbstractReconciler {
+
+ /** The reconciling strategy */
+ private IReconcilingStrategy fStrategy;
+
+
+ /**
+ * Creates a new reconciler that uses the same reconciling strategy to
+ * reconcile its document independent of the type of the document's contents.
+ *
+ * @param strategy the reconciling strategy to be used
+ * @param isIncremental the indication whether strategy is incremental or not
+ */
+ public MonoReconciler(IReconcilingStrategy strategy, boolean isIncremental) {
+ super();
+
+ Assert.isNotNull(strategy);
+
+ fStrategy= strategy;
+ setIsIncrementalReconciler(isIncremental);
+ }
+
+ /*
+ * @see IReconciler#getReconcilingStrategy
+ */
+ public IReconcilingStrategy getReconcilingStrategy(String contentType) {
+ Assert.isNotNull(contentType);
+ return fStrategy;
+ }
+
+ /*
+ * @see AbstractReconciler#process(DirtyRegion)
+ */
+ protected void process(DirtyRegion dirtyRegion) {
+
+ if(dirtyRegion != null)
+ fStrategy.reconcile(dirtyRegion, dirtyRegion);
+ else
+ fStrategy.reconcile(new Region(0, getDocument().getLength()));
+ }
+
+ /*
+ * @see AbstractReconciler#reconcilerDocumentChanged(IDocument)
+ */
+ protected void reconcilerDocumentChanged(IDocument document) {
+ fStrategy.setDocument(document);
+ }
+
+ /*
+ * @see AbstractReconciler#setProgressMonitor(IProgressMonitor)
+ */
+ public void setProgressMonitor(IProgressMonitor monitor) {
+ super.setProgressMonitor(monitor);
+ if (fStrategy instanceof IReconcilingStrategyExtension) {
+ IReconcilingStrategyExtension extension= (IReconcilingStrategyExtension) fStrategy;
+ extension.setProgressMonitor(monitor);
+ }
+ }
+
+ /*
+ * @see AbstractReconciler#initialProcess()
+ */
+ protected void initialProcess() {
+ if (fStrategy instanceof IReconcilingStrategyExtension) {
+ IReconcilingStrategyExtension extension= (IReconcilingStrategyExtension) fStrategy;
+ extension.initialReconcile();
+ }
+ }
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/Reconciler.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/Reconciler.java
new file mode 100644
index 000000000..cd8a38580
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/Reconciler.java
@@ -0,0 +1,185 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.reconciler;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TypedRegion;
+import org.eclipse.jface.util.Assert;
+
+/**
+ * Standard implementation of <code>IReconciler</code>. The reconciler
+ * is configured with a set of reconciling strategies each of which is
+ * responsible for a particular content type. <p>
+ * Usually, clients instantiate this class and configure it before using it.
+ *
+ * @see IReconciler
+ * @see org.eclipse.jface.text.IDocumentListener
+ * @see org.eclipse.jface.text.ITextInputListener
+ * @see DirtyRegion
+ */
+public class Reconciler extends AbstractReconciler {
+
+ /** The map of reconciling strategies */
+ private Map fStrategies;
+
+ /**
+ * Creates a new reconciler with the following configuration: it is
+ * an incremental reconciler with a standard delay of 500 ms. There
+ * are no predefined reconciling strategies.
+ */
+ public Reconciler() {
+ super();
+ }
+
+ /**
+ * Registers a given reconciling strategy for a particular content type.
+ * If there is already a strategy registered for this type, the new strategy
+ * is registered instead of the old one.
+ *
+ * @param strategy the reconciling strategy to register, or <code>null</code> to remove an existing one
+ * @param contentType the content type under which to register
+ */
+ public void setReconcilingStrategy(IReconcilingStrategy strategy, String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fStrategies == null)
+ fStrategies= new HashMap();
+
+ if (strategy == null)
+ fStrategies.remove(contentType);
+ else {
+ fStrategies.put(contentType, strategy);
+ if (strategy instanceof IReconcilingStrategyExtension && getProgressMonitor() == null) {
+ IReconcilingStrategyExtension extension= (IReconcilingStrategyExtension) strategy;
+ extension.setProgressMonitor(getProgressMonitor());
+ }
+ }
+ }
+
+ /*
+ * @see IReconciler#getReconcilingStrategy
+ */
+ public IReconcilingStrategy getReconcilingStrategy(String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fStrategies == null)
+ return null;
+
+ return (IReconcilingStrategy) fStrategies.get(contentType);
+ }
+
+ /**
+ * Processes a dirty region. If the dirty region is <code>null</code> the whole
+ * document is consider being dirty. The dirty region is partitioned by the
+ * document and each partition is handed over to a reconciling strategy registered
+ * for the partition's content type.
+ *
+ * @param dirtyRegion the dirty region to be processed
+ * @see AbstractReconciler#process(DirtyRegion)
+ */
+ protected void process(DirtyRegion dirtyRegion) {
+
+ IRegion region= dirtyRegion;
+
+ if (region == null)
+ region= new Region(0, getDocument().getLength());
+
+ ITypedRegion[] regions= null;
+ try {
+ regions= getDocument().computePartitioning(region.getOffset(), region.getLength());
+ } catch (BadLocationException x) {
+ regions= new TypedRegion[0];
+ }
+
+ for (int i= 0; i < regions.length; i++) {
+ ITypedRegion r= regions[i];
+ IReconcilingStrategy s= getReconcilingStrategy(r.getType());
+ if (s == null)
+ continue;
+
+ if(dirtyRegion != null)
+ s.reconcile(dirtyRegion, r);
+ else
+ s.reconcile(r);
+ }
+ }
+
+ /*
+ * @see AbstractReconciler#reconcilerDocumentChanged(IDocument)
+ * @since 2.0
+ */
+ protected void reconcilerDocumentChanged(IDocument document) {
+ if (fStrategies != null) {
+ Iterator e= fStrategies.values().iterator();
+ while (e.hasNext()) {
+ IReconcilingStrategy strategy= (IReconcilingStrategy) e.next();
+ strategy.setDocument(document);
+ }
+ }
+ }
+
+ /*
+ * @see AbstractReconciler#setProgressMonitor(IProgressMonitor)
+ * @since 2.0
+ */
+ public void setProgressMonitor(IProgressMonitor monitor) {
+ super.setProgressMonitor(monitor);
+
+ if (fStrategies != null) {
+ Iterator e= fStrategies.values().iterator();
+ while (e.hasNext()) {
+ IReconcilingS