2121package org .eclipse .text .quicksearch .internal .ui ;
2222
2323import static org .eclipse .jface .resource .JFaceResources .TEXT_FONT ;
24+ import static org .eclipse .ui .texteditor .AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE ;
25+ import static org .eclipse .ui .texteditor .AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE_COLOR ;
26+ import static org .eclipse .ui .texteditor .AbstractDecoratedTextEditorPreferenceConstants .EDITOR_LINE_NUMBER_RULER_COLOR ;
27+ import static org .eclipse .ui .texteditor .AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND ;
28+ import static org .eclipse .ui .texteditor .AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT ;
29+ import static org .eclipse .ui .texteditor .AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND ;
30+ import static org .eclipse .ui .texteditor .AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT ;
2431
2532import java .util .ArrayList ;
2633import java .util .Arrays ;
4653import org .eclipse .jface .dialogs .IDialogConstants ;
4754import org .eclipse .jface .dialogs .IDialogSettings ;
4855import org .eclipse .jface .layout .GridDataFactory ;
56+ import org .eclipse .jface .preference .PreferenceConverter ;
4957import org .eclipse .jface .resource .JFaceResources ;
5058import org .eclipse .jface .text .BadLocationException ;
59+ import org .eclipse .jface .text .CursorLinePainter ;
5160import org .eclipse .jface .text .IDocument ;
5261import org .eclipse .jface .text .IRegion ;
62+ import org .eclipse .jface .text .source .CompositeRuler ;
63+ import org .eclipse .jface .text .source .ISharedTextColors ;
64+ import org .eclipse .jface .text .source .LineNumberRulerColumn ;
65+ import org .eclipse .jface .text .source .SourceViewer ;
66+ import org .eclipse .jface .util .IPropertyChangeListener ;
67+ import org .eclipse .jface .util .PropertyChangeEvent ;
5368import org .eclipse .jface .viewers .ILazyContentProvider ;
5469import org .eclipse .jface .viewers .IStructuredContentProvider ;
5570import org .eclipse .jface .viewers .IStructuredSelection ;
6782import org .eclipse .swt .accessibility .ACC ;
6883import org .eclipse .swt .accessibility .AccessibleAdapter ;
6984import org .eclipse .swt .accessibility .AccessibleEvent ;
85+ import org .eclipse .swt .custom .LineBackgroundEvent ;
86+ import org .eclipse .swt .custom .LineBackgroundListener ;
7087import org .eclipse .swt .custom .SashForm ;
7188import org .eclipse .swt .custom .StyleRange ;
7289import org .eclipse .swt .custom .StyledText ;
8198import org .eclipse .swt .graphics .Color ;
8299import org .eclipse .swt .graphics .Image ;
83100import org .eclipse .swt .graphics .Point ;
101+ import org .eclipse .swt .graphics .RGB ;
84102import org .eclipse .swt .graphics .Rectangle ;
103+ import org .eclipse .swt .layout .FillLayout ;
85104import org .eclipse .swt .layout .GridData ;
86105import org .eclipse .swt .layout .GridLayout ;
87106import org .eclipse .swt .widgets .Button ;
107+ import org .eclipse .swt .widgets .Canvas ;
88108import org .eclipse .swt .widgets .Combo ;
89109import org .eclipse .swt .widgets .Composite ;
90110import org .eclipse .swt .widgets .Control ;
111131import org .eclipse .ui .PartInitException ;
112132import org .eclipse .ui .PlatformUI ;
113133import org .eclipse .ui .dialogs .SelectionStatusDialog ;
134+ import org .eclipse .ui .editors .text .EditorsUI ;
114135import org .eclipse .ui .handlers .IHandlerActivation ;
115136import org .eclipse .ui .handlers .IHandlerService ;
116137import org .eclipse .ui .internal .IWorkbenchGraphicConstants ;
117138import org .eclipse .ui .internal .WorkbenchImages ;
118139import org .eclipse .ui .internal .ide .IDEWorkbenchPlugin ;
119140import org .eclipse .ui .progress .UIJob ;
141+ import org .eclipse .ui .texteditor .SourceViewerDecorationSupport ;
120142import org .osgi .framework .FrameworkUtil ;
121143
122144/**
@@ -359,7 +381,10 @@ public void update(ViewerCell cell) {
359381
360382 private QuickTextSearcher searcher ;
361383
362- private StyledText details ;
384+ private SourceViewer viewer ;
385+ private LineNumberRulerColumn lineNumberColumn ;
386+ private FixedLineHighlighter targetLineHighlighter ;
387+ private final IPropertyChangeListener preferenceChangeListener = this ::handlePropertyChangeEvent ;
363388
364389 private DocumentFetcher documents ;
365390
@@ -398,6 +423,7 @@ public QuickSearchDialog(IWorkbenchWindow window) {
398423 MAX_LINE_LEN = QuickSearchActivator .getDefault ().getPreferences ().getMaxLineLen ();
399424 MAX_RESULTS = QuickSearchActivator .getDefault ().getPreferences ().getMaxResults ();
400425 progressJob .setSystem (true );
426+ EditorsUI .getPreferenceStore ().addPropertyChangeListener (preferenceChangeListener );
401427 }
402428
403429 /*
@@ -946,31 +972,124 @@ protected void dispose() {
946972 blankImage .dispose ();
947973 blankImage = null ;
948974 }
975+ EditorsUI .getPreferenceStore ().removePropertyChangeListener (preferenceChangeListener );
949976 }
950977
951978 private void createDetailsArea (Composite parent ) {
952- details = new StyledText (parent , SWT .MULTI +SWT .READ_ONLY +SWT .BORDER +SWT .H_SCROLL +SWT .V_SCROLL );
953- details .setFont (JFaceResources .getFont (TEXT_FONT ));
979+ var viewerParent = new Canvas (parent , SWT .BORDER );
980+ viewerParent .setLayout (new FillLayout ());
981+
982+ viewer = new SourceViewer (viewerParent , new CompositeRuler (), SWT .H_SCROLL | SWT .V_SCROLL | SWT .READ_ONLY );
983+ viewer .getTextWidget ().setFont (JFaceResources .getFont (TEXT_FONT ));
984+ createViewerDecorations ();
954985
955986 list .addSelectionChangedListener (event -> refreshDetails ());
956- details .addControlListener (new ControlAdapter () {
987+
988+ viewer .getTextWidget ().addControlListener (new ControlAdapter () {
957989 @ Override
958990 public void controlResized (ControlEvent e ) {
959991 refreshDetails ();
960992 }
961993 });
962994 }
963995
996+ private void setColors () {
997+ RGB background = null ;
998+ RGB foreground = null ;
999+ var textWidget = viewer .getTextWidget ();
1000+ ISharedTextColors sharedColors = EditorsUI .getSharedTextColors ();
1001+
1002+ var isUsingSystemBackground = EditorsUI .getPreferenceStore ().getBoolean (PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT );
1003+ if (!isUsingSystemBackground ) {
1004+ background = getColorFromStore (PREFERENCE_COLOR_BACKGROUND );
1005+ }
1006+ if (background != null ) {
1007+ var color = sharedColors .getColor (background );
1008+ textWidget .setBackground (color );
1009+ lineNumberColumn .setBackground (color );
1010+ } else {
1011+ textWidget .setBackground (null );
1012+ lineNumberColumn .setBackground (null );
1013+ }
1014+
1015+ var isUsingSystemForeground = EditorsUI .getPreferenceStore ().getBoolean (PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT );
1016+ if (!isUsingSystemForeground ) {
1017+ foreground = getColorFromStore (PREFERENCE_COLOR_FOREGROUND );
1018+ }
1019+ if (foreground != null ) {
1020+ textWidget .setForeground (sharedColors .getColor (foreground ));
1021+ } else {
1022+ textWidget .setForeground (null );
1023+ }
1024+ }
1025+
1026+ private Color getLineNumbersColor () {
1027+ var lineNumbersColor = getColorFromStore (EDITOR_LINE_NUMBER_RULER_COLOR );
1028+ return EditorsUI .getSharedTextColors ().getColor (lineNumbersColor == null ? new RGB (0 , 0 , 0 ) : lineNumbersColor );
1029+ }
1030+
1031+ private Color getTargetLineHighlightColor () {
1032+ RGB background = getColorFromStore (EDITOR_CURRENT_LINE_COLOR );
1033+ ISharedTextColors sharedColors = EditorsUI .getSharedTextColors ();
1034+ return sharedColors .getColor (background );
1035+ }
1036+
1037+ private void createViewerDecorations () {
1038+ lineNumberColumn = new LineNumberRulerColumn ();
1039+ lineNumberColumn .setForeground (getLineNumbersColor ());
1040+ viewer .addVerticalRulerColumn (lineNumberColumn );
1041+
1042+ var sourceViewerDecorationSupport = new SourceViewerDecorationSupport (viewer , null , null , EditorsUI .getSharedTextColors ());
1043+ sourceViewerDecorationSupport .setCursorLinePainterPreferenceKeys (EDITOR_CURRENT_LINE , EDITOR_CURRENT_LINE_COLOR );
1044+ sourceViewerDecorationSupport .install (EditorsUI .getPreferenceStore ());
1045+ targetLineHighlighter = new FixedLineHighlighter ();
1046+ targetLineHighlighter .highlightColor = getTargetLineHighlightColor ();
1047+ viewer .getTextWidget ().addLineBackgroundListener (targetLineHighlighter );
1048+
1049+ setColors ();
1050+ }
1051+
1052+ private void handlePropertyChangeEvent (PropertyChangeEvent event ) {
1053+ if (viewer == null ) {
1054+ return ;
1055+ }
1056+ var prop = event .getProperty ();
1057+ if (PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT .equals (prop )
1058+ || PREFERENCE_COLOR_BACKGROUND .equals (prop )
1059+ || PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT .equals (prop )
1060+ || PREFERENCE_COLOR_FOREGROUND .equals (prop )) {
1061+ setColors ();
1062+ viewer .getTextWidget ().redraw ();
1063+ } else if (EDITOR_LINE_NUMBER_RULER_COLOR .equals (prop )) {
1064+ lineNumberColumn .setForeground (getLineNumbersColor ());
1065+ lineNumberColumn .redraw ();
1066+ } else if (EDITOR_CURRENT_LINE_COLOR .equals (prop )) {
1067+ targetLineHighlighter .highlightColor = getTargetLineHighlightColor ();
1068+ viewer .getTextWidget ().redraw ();
1069+ }
1070+ }
1071+
1072+ private RGB getColorFromStore (String key ) {
1073+ var store = EditorsUI .getPreferenceStore ();
1074+ RGB rgb = null ;
1075+ if (store .contains (key )) {
1076+ if (store .isDefault (key )) {
1077+ rgb = PreferenceConverter .getDefaultColor (store , key );
1078+ } else {
1079+ rgb = PreferenceConverter .getColor (store , key );
1080+ }
1081+ }
1082+ return rgb ;
1083+ }
9641084
965- // Dumber version just using the a 'raw' StyledText widget.
9661085 private void refreshDetails () {
967- if (details !=null && list !=null && !list .getTable ().isDisposed ()) {
1086+ if (viewer !=null && list !=null && !list .getTable ().isDisposed ()) {
9681087 if (documents ==null ) {
9691088 documents = new DocumentFetcher ();
9701089 }
9711090 IStructuredSelection sel = (IStructuredSelection ) list .getSelection ();
9721091 if (sel ==null || sel .isEmpty ()) {
973- details . setText ( EMPTY_STRING );
1092+ viewer . setDocument ( null );
9741093 } else {
9751094 //Not empty selection
9761095 final int context = 100 ; // number of lines before and after match to include in preview
@@ -983,35 +1102,57 @@ private void refreshDetails() {
9831102 int line = item .getLineNumber ()-1 ; //in document lines are 0 based. In search 1 based.
9841103 int contextStartLine = Math .max (line -(numLines -1 )/2 - context , 0 );
9851104 int start = document .getLineOffset (contextStartLine );
1105+ int displayedEndLine = line + numLines /2 ;
9861106 int end = document .getLength ();
987- try {
988- IRegion lineInfo = document .getLineInformation (line + numLines /2 + context );
989- end = lineInfo .getOffset () + lineInfo .getLength ();
990- } catch (BadLocationException e ) {
991- //Presumably line number is past the end of document.
992- //ignore.
1107+ if (displayedEndLine + context <= document .getNumberOfLines ()) {
1108+ try {
1109+ IRegion lineInfo = document .getLineInformation (displayedEndLine + context );
1110+ end = lineInfo .getOffset () + lineInfo .getLength ();
1111+ } catch (BadLocationException e ) {
1112+ //Presumably line number is past the end of document.
1113+ //ignore.
1114+ }
9931115 }
1116+ int contextLenght = end -start ;
1117+
1118+ viewer .setDocument (document );
1119+ viewer .setVisibleRegion (start , contextLenght );
1120+
1121+ targetLineHighlighter .setTargetLineOffset (item .getOffset () - start );
9941122
995- StyledString styledString = highlightMatches (document .get (start , end -start ));
996- details .setText (styledString .getString ());
997- details .setStyleRanges (styledString .getStyleRanges ());
998- details .setTopIndex (Math .max (line - contextStartLine - numLines /2 , 0 ));
1123+ // center target line in the displayed area
1124+ IRegion rangeEndLineInfo = document .getLineInformation (Math .min (displayedEndLine , document .getNumberOfLines () - 1 ));
1125+ int rangeStart = document .getLineOffset (Math .max (line - numLines /2 , 0 ));
1126+ int rangeEnd = rangeEndLineInfo .getOffset () + rangeEndLineInfo .getLength ();
1127+ viewer .revealRange (rangeStart , rangeEnd - rangeStart );
1128+
1129+ var targetLineFirstMatch = getQuery ().findFirst (document .get (item .getOffset (), contextLenght - (item .getOffset () - start )));
1130+ int targetLineFirstMatchStart = item .getOffset () + targetLineFirstMatch .getOffset ();
1131+ // sets caret position
1132+ viewer .setSelectedRange (targetLineFirstMatchStart , 0 );
1133+ // does horizontal scrolling if necessary to reveal 1st occurrence in target line
1134+ viewer .revealRange (targetLineFirstMatchStart , targetLineFirstMatch .getLength ());
1135+
1136+ // above setVisibleRegion() call makes these ranges to be aligned with content of text widget
1137+ StyledString styledString = highlightMatches (document .get (start , contextLenght ));
1138+ viewer .getTextWidget ().setStyleRanges (styledString .getStyleRanges ());
9991139 return ;
10001140 } catch (BadLocationException e ) {
10011141 }
10021142 }
10031143 }
10041144 }
10051145 //empty selection or some error:
1006- details . setText ( EMPTY_STRING );
1146+ viewer . setDocument ( null );
10071147 }
10081148 }
10091149
10101150 /**
10111151 * Computes how many lines of text can be displayed in the details section.
10121152 */
10131153 private int computeLines () {
1014- if (details !=null && !details .isDisposed ()) {
1154+ StyledText details ;
1155+ if (viewer !=null && !(details = viewer .getTextWidget ()).isDisposed ()) {
10151156 int lineHeight = details .getLineHeight ();
10161157 int areaHeight = details .getClientArea ().height ;
10171158 return (areaHeight + lineHeight - 1 ) / lineHeight ;
@@ -1034,47 +1175,6 @@ private StyledString highlightMatches(String visibleText) {
10341175 return styledText ;
10351176 }
10361177
1037- // Version using sourceviewer
1038- // private void refreshDetails() {
1039- // if (details!=null && list!=null && !list.getTable().isDisposed()) {
1040- // if (documents==null) {
1041- // documents = new DocumentFetcher();
1042- // }
1043- // IStructuredSelection sel = (IStructuredSelection) list.getSelection();
1044- // if (sel!=null && !sel.isEmpty()) {
1045- // //Not empty selection
1046- // LineItem item = (LineItem) sel.getFirstElement();
1047- // IDocument document = documents.getDocument(item.getFile());
1048- // try {
1049- // int line = item.getLineNumber()-1; //in document lines are 0 based. In search 1 based.
1050- // int start = document.getLineOffset(Math.max(line-2, 0));
1051- // int end = document.getLength();
1052- // try {
1053- // end = document.getLineOffset(line+3);
1054- // } catch (BadLocationException e) {
1055- // //Presumably line number is past the end of document.
1056- // //ignore.
1057- // }
1058- // details.setDocument(document, start, end-start);
1059- //
1060- // String visibleText = document.get(start, end-start);
1061- // List<TextRange> matches = getQuery().findAll(visibleText);
1062- // Region visibleRegion = new Region(start, end-start);
1063- // TextPresentation presentation = new TextPresentation(visibleRegion, 20);
1064- // presentation.setDefaultStyleRange(new StyleRange(0, document.getLength(), null, null));
1065- // for (TextRange m : matches) {
1066- // presentation.addStyleRange(new StyleRange(m.start+start, m.len, null, YELLOW));
1067- // }
1068- // details.changeTextPresentation(presentation, true);
1069- //
1070- // return;
1071- // } catch (BadLocationException e) {
1072- // }
1073- // }
1074- // details.setDocument(null);
1075- // }
1076- // }
1077-
10781178 /**
10791179 * Handle selection in the items list by updating labels of selected and
10801180 * unselected items and refresh the details field using the selection.
@@ -1471,4 +1571,29 @@ public QuickTextQuery getQuery() {
14711571 return searcher .getQuery ();
14721572 }
14731573
1574+ /**
1575+ * A line background listener that provides the color that is used for current line highlighting (what
1576+ * {@link CursorLinePainter} does) but for single fixed line only and does so always regardless of show current
1577+ * line highlighting on/off preference.
1578+ *
1579+ * @see CursorLinePainter
1580+ */
1581+ private static class FixedLineHighlighter implements LineBackgroundListener {
1582+
1583+ private int lineOffset = -1 ;
1584+ private Color highlightColor ;
1585+
1586+ public void setTargetLineOffset (int lineOffset ) {
1587+ this .lineOffset = lineOffset ;
1588+ }
1589+
1590+ @ Override
1591+ public void lineGetBackground (LineBackgroundEvent event ) {
1592+ if (lineOffset == event .lineOffset ) {
1593+ event .lineBackground = highlightColor ;
1594+ }
1595+ }
1596+
1597+ }
1598+
14741599}
0 commit comments