46
46
import org .eclipse .jface .dialogs .IDialogConstants ;
47
47
import org .eclipse .jface .dialogs .IDialogSettings ;
48
48
import org .eclipse .jface .layout .GridDataFactory ;
49
+ import org .eclipse .jface .preference .IPreferenceStore ;
50
+ import org .eclipse .jface .preference .PreferenceConverter ;
49
51
import org .eclipse .jface .resource .JFaceResources ;
50
52
import org .eclipse .jface .text .BadLocationException ;
53
+ import org .eclipse .jface .text .CursorLinePainter ;
51
54
import org .eclipse .jface .text .IDocument ;
55
+ import org .eclipse .jface .text .IPaintPositionManager ;
56
+ import org .eclipse .jface .text .IPainter ;
52
57
import org .eclipse .jface .text .IRegion ;
58
+ import org .eclipse .jface .text .source .CompositeRuler ;
59
+ import org .eclipse .jface .text .source .ISharedTextColors ;
60
+ import org .eclipse .jface .text .source .LineNumberRulerColumn ;
61
+ import org .eclipse .jface .text .source .SourceViewer ;
62
+ import org .eclipse .jface .util .IPropertyChangeListener ;
63
+ import org .eclipse .jface .util .PropertyChangeEvent ;
53
64
import org .eclipse .jface .viewers .ILazyContentProvider ;
54
65
import org .eclipse .jface .viewers .IStructuredContentProvider ;
55
66
import org .eclipse .jface .viewers .IStructuredSelection ;
67
78
import org .eclipse .swt .accessibility .ACC ;
68
79
import org .eclipse .swt .accessibility .AccessibleAdapter ;
69
80
import org .eclipse .swt .accessibility .AccessibleEvent ;
81
+ import org .eclipse .swt .custom .LineBackgroundEvent ;
82
+ import org .eclipse .swt .custom .LineBackgroundListener ;
83
+ import org .eclipse .swt .custom .ST ;
70
84
import org .eclipse .swt .custom .SashForm ;
71
85
import org .eclipse .swt .custom .StyleRange ;
72
86
import org .eclipse .swt .custom .StyledText ;
81
95
import org .eclipse .swt .graphics .Color ;
82
96
import org .eclipse .swt .graphics .Image ;
83
97
import org .eclipse .swt .graphics .Point ;
98
+ import org .eclipse .swt .graphics .RGB ;
84
99
import org .eclipse .swt .graphics .Rectangle ;
100
+ import org .eclipse .swt .layout .FillLayout ;
85
101
import org .eclipse .swt .layout .GridData ;
86
102
import org .eclipse .swt .layout .GridLayout ;
87
103
import org .eclipse .swt .widgets .Button ;
104
+ import org .eclipse .swt .widgets .Canvas ;
88
105
import org .eclipse .swt .widgets .Combo ;
89
106
import org .eclipse .swt .widgets .Composite ;
90
107
import org .eclipse .swt .widgets .Control ;
111
128
import org .eclipse .ui .PartInitException ;
112
129
import org .eclipse .ui .PlatformUI ;
113
130
import org .eclipse .ui .dialogs .SelectionStatusDialog ;
131
+ import org .eclipse .ui .editors .text .EditorsUI ;
114
132
import org .eclipse .ui .handlers .IHandlerActivation ;
115
133
import org .eclipse .ui .handlers .IHandlerService ;
116
134
import org .eclipse .ui .internal .IWorkbenchGraphicConstants ;
117
135
import org .eclipse .ui .internal .WorkbenchImages ;
118
136
import org .eclipse .ui .internal .ide .IDEWorkbenchPlugin ;
119
137
import org .eclipse .ui .progress .UIJob ;
138
+ import org .eclipse .ui .texteditor .AbstractDecoratedTextEditorPreferenceConstants ;
139
+ import org .eclipse .ui .texteditor .AbstractTextEditor ;
140
+ import org .eclipse .ui .texteditor .SourceViewerDecorationSupport ;
120
141
import org .osgi .framework .FrameworkUtil ;
121
142
122
143
/**
@@ -359,7 +380,10 @@ public void update(ViewerCell cell) {
359
380
360
381
private QuickTextSearcher searcher ;
361
382
362
- private StyledText details ;
383
+ private SourceViewer viewer ;
384
+ private LineNumberRulerColumn lineNumberColumn ;
385
+ private FixedLinePainter targetLinePainter ;
386
+ private final IPropertyChangeListener preferenceChangeListener = this ::handlePropertyChangeEvent ;
363
387
364
388
private DocumentFetcher documents ;
365
389
@@ -398,6 +422,7 @@ public QuickSearchDialog(IWorkbenchWindow window) {
398
422
MAX_LINE_LEN = QuickSearchActivator .getDefault ().getPreferences ().getMaxLineLen ();
399
423
MAX_RESULTS = QuickSearchActivator .getDefault ().getPreferences ().getMaxResults ();
400
424
progressJob .setSystem (true );
425
+ EditorsUI .getPreferenceStore ().addPropertyChangeListener (preferenceChangeListener );
401
426
}
402
427
403
428
/*
@@ -942,31 +967,123 @@ protected void dispose() {
942
967
blankImage .dispose ();
943
968
blankImage = null ;
944
969
}
970
+ EditorsUI .getPreferenceStore ().removePropertyChangeListener (preferenceChangeListener );
945
971
}
946
972
947
973
private void createDetailsArea (Composite parent ) {
948
- details = new StyledText (parent , SWT .MULTI +SWT .READ_ONLY +SWT .BORDER +SWT .H_SCROLL +SWT .V_SCROLL );
949
- details .setFont (JFaceResources .getFont (TEXT_FONT ));
974
+ var viewerParent = new Canvas (parent , SWT .BORDER );
975
+ viewerParent .setLayout (new FillLayout ());
976
+
977
+ viewer = new SourceViewer (viewerParent , new CompositeRuler (), SWT .H_SCROLL | SWT .V_SCROLL | SWT .READ_ONLY );
978
+ viewer .getTextWidget ().setFont (JFaceResources .getFont (TEXT_FONT ));
979
+
980
+ lineNumberColumn = new LineNumberRulerColumn ();
981
+ viewer .addVerticalRulerColumn (lineNumberColumn );
982
+ setColors (false );
983
+ createLinesHighlightingDecorations ();
950
984
951
985
list .addSelectionChangedListener (event -> refreshDetails ());
952
- details .addControlListener (new ControlAdapter () {
986
+
987
+ viewer .getTextWidget ().addControlListener (new ControlAdapter () {
953
988
@ Override
954
989
public void controlResized (ControlEvent e ) {
955
990
refreshDetails ();
956
991
}
957
992
});
958
993
}
959
994
995
+ private void setColors (boolean refresh ) {
996
+ RGB background = null ;
997
+ RGB foreground = null ;
998
+ var textWidget = viewer .getTextWidget ();
999
+ var preferenceStore = EditorsUI .getPreferenceStore ();
1000
+ ISharedTextColors sharedColors = EditorsUI .getSharedTextColors ();
1001
+
1002
+ var isUsingSystemBackground = preferenceStore .getBoolean (AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT );
1003
+ if (!isUsingSystemBackground ) {
1004
+ background = getColorFromStore (preferenceStore , AbstractTextEditor .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 = preferenceStore .getBoolean (AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT );
1016
+ if (!isUsingSystemForeground ) {
1017
+ foreground = getColorFromStore (preferenceStore , AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND );
1018
+ }
1019
+ if (foreground != null ) {
1020
+ textWidget .setForeground (sharedColors .getColor (foreground ));
1021
+ } else {
1022
+ textWidget .setForeground (null );
1023
+ }
1024
+
1025
+ var lineNumbersColor = getColorFromStore (EditorsUI .getPreferenceStore (), AbstractDecoratedTextEditorPreferenceConstants .EDITOR_LINE_NUMBER_RULER_COLOR );
1026
+ if (lineNumbersColor == null ) {
1027
+ lineNumbersColor = new RGB (0 , 0 , 0 );
1028
+ }
1029
+ lineNumberColumn .setForeground (sharedColors .getColor (lineNumbersColor ));
1030
+
1031
+ if (refresh ) {
1032
+ textWidget .redraw ();
1033
+ lineNumberColumn .redraw ();
1034
+ }
1035
+ }
1036
+
1037
+
1038
+ private void createLinesHighlightingDecorations () {
1039
+ var sourceViewerDecorationSupport = new SourceViewerDecorationSupport (viewer , null , null , EditorsUI .getSharedTextColors ());
1040
+ sourceViewerDecorationSupport .setCursorLinePainterPreferenceKeys (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE , AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE_COLOR );
1041
+ sourceViewerDecorationSupport .install (EditorsUI .getPreferenceStore ());
1042
+ targetLinePainter = new FixedLinePainter ();
1043
+ viewer .addPainter (targetLinePainter );
1044
+ viewer .getTextWidget ().addLineBackgroundListener (targetLinePainter );
1045
+ }
1046
+
1047
+ private void handlePropertyChangeEvent (PropertyChangeEvent event ) {
1048
+ if (viewer == null ) {
1049
+ return ;
1050
+ }
1051
+ var prop = event .getProperty ();
1052
+ if (prop .equals (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_LINE_NUMBER_RULER_COLOR )
1053
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT )
1054
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND )
1055
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT )
1056
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND )) {
1057
+ setColors (true );
1058
+ }
1059
+ if (prop .equals (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE )
1060
+ || prop .equals (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE_COLOR )) {
1061
+ targetLinePainter .highlightColor = null ;
1062
+ targetLinePainter .cursorLinePainter = null ;
1063
+ viewer .getTextWidget ().redraw ();
1064
+ }
1065
+ }
1066
+
1067
+ private RGB getColorFromStore (IPreferenceStore store , String key ) {
1068
+ RGB rgb = null ;
1069
+ if (store .contains (key )) {
1070
+ if (store .isDefault (key )) {
1071
+ rgb = PreferenceConverter .getDefaultColor (store , key );
1072
+ } else {
1073
+ rgb = PreferenceConverter .getColor (store , key );
1074
+ }
1075
+ }
1076
+ return rgb ;
1077
+ }
960
1078
961
- // Dumber version just using the a 'raw' StyledText widget.
962
1079
private void refreshDetails () {
963
- if (details !=null && list !=null && !list .getTable ().isDisposed ()) {
1080
+ if (viewer !=null && list !=null && !list .getTable ().isDisposed ()) {
964
1081
if (documents ==null ) {
965
1082
documents = new DocumentFetcher ();
966
1083
}
967
1084
IStructuredSelection sel = (IStructuredSelection ) list .getSelection ();
968
1085
if (sel ==null || sel .isEmpty ()) {
969
- details . setText ( EMPTY_STRING );
1086
+ viewer . setDocument ( null );
970
1087
} else {
971
1088
//Not empty selection
972
1089
final int context = 100 ; // number of lines before and after match to include in preview
@@ -979,35 +1096,56 @@ private void refreshDetails() {
979
1096
int line = item .getLineNumber ()-1 ; //in document lines are 0 based. In search 1 based.
980
1097
int contextStartLine = Math .max (line -(numLines -1 )/2 - context , 0 );
981
1098
int start = document .getLineOffset (contextStartLine );
1099
+ int displayedEndLine = line + numLines /2 ;
982
1100
int end = document .getLength ();
983
- try {
984
- IRegion lineInfo = document .getLineInformation (line + numLines /2 + context );
985
- end = lineInfo .getOffset () + lineInfo .getLength ();
986
- } catch (BadLocationException e ) {
987
- //Presumably line number is past the end of document.
988
- //ignore.
1101
+ if (displayedEndLine + context <= document .getNumberOfLines ()) {
1102
+ try {
1103
+ IRegion lineInfo = document .getLineInformation (displayedEndLine + context );
1104
+ end = lineInfo .getOffset () + lineInfo .getLength ();
1105
+ } catch (BadLocationException e ) {
1106
+ //Presumably line number is past the end of document.
1107
+ //ignore.
1108
+ }
989
1109
}
1110
+ int contextLenght = end -start ;
1111
+
1112
+ viewer .setDocument (document );
1113
+ viewer .setVisibleRegion (start , contextLenght );
1114
+
1115
+ targetLinePainter .setTargetLineOffset (item .getOffset () - start );
1116
+
1117
+ // center target line in the displayed area
1118
+ IRegion rangeEndLineInfo = document .getLineInformation (Math .min (displayedEndLine , document .getNumberOfLines () - 1 ));
1119
+ int rangeStart = document .getLineOffset (Math .max (line - numLines /2 , 0 ));
1120
+ int rangeEnd = rangeEndLineInfo .getOffset () + rangeEndLineInfo .getLength ();
1121
+ viewer .revealRange (rangeStart , rangeEnd - rangeStart );
1122
+
1123
+ var targetLineFirstMatch = getQuery ().findFirst (document .get (item .getOffset (), contextLenght - (item .getOffset () - start )));
1124
+ int targetLineFirstMatchStart = item .getOffset () + targetLineFirstMatch .getOffset ();
1125
+ // sets caret position (also highlights that line)
1126
+ viewer .setSelectedRange (targetLineFirstMatchStart , 0 );
1127
+ // does horizontal scrolling if necessary to reveal 1st occurrence in target line
1128
+ viewer .revealRange (targetLineFirstMatchStart , targetLineFirstMatch .getLength ());
990
1129
991
- StyledString styledString = highlightMatches (document .get (start , end -start ));
992
- details .setText (styledString .getString ());
993
- details .setStyleRanges (styledString .getStyleRanges ());
994
- details .setTopIndex (Math .max (line - contextStartLine - numLines /2 , 0 ));
1130
+ StyledString styledString = highlightMatches (document .get (start , contextLenght ));
1131
+ viewer .getTextWidget ().setStyleRanges (styledString .getStyleRanges ());
995
1132
return ;
996
1133
} catch (BadLocationException e ) {
997
1134
}
998
1135
}
999
1136
}
1000
1137
}
1001
1138
//empty selection or some error:
1002
- details . setText ( EMPTY_STRING );
1139
+ viewer . setDocument ( null );
1003
1140
}
1004
1141
}
1005
1142
1006
1143
/**
1007
1144
* Computes how many lines of text can be displayed in the details section.
1008
1145
*/
1009
1146
private int computeLines () {
1010
- if (details !=null && !details .isDisposed ()) {
1147
+ StyledText details ;
1148
+ if (viewer !=null && !(details = viewer .getTextWidget ()).isDisposed ()) {
1011
1149
int lineHeight = details .getLineHeight ();
1012
1150
int areaHeight = details .getClientArea ().height ;
1013
1151
return (areaHeight + lineHeight - 1 ) / lineHeight ;
@@ -1030,47 +1168,6 @@ private StyledString highlightMatches(String visibleText) {
1030
1168
return styledText ;
1031
1169
}
1032
1170
1033
- // Version using sourceviewer
1034
- // private void refreshDetails() {
1035
- // if (details!=null && list!=null && !list.getTable().isDisposed()) {
1036
- // if (documents==null) {
1037
- // documents = new DocumentFetcher();
1038
- // }
1039
- // IStructuredSelection sel = (IStructuredSelection) list.getSelection();
1040
- // if (sel!=null && !sel.isEmpty()) {
1041
- // //Not empty selection
1042
- // LineItem item = (LineItem) sel.getFirstElement();
1043
- // IDocument document = documents.getDocument(item.getFile());
1044
- // try {
1045
- // int line = item.getLineNumber()-1; //in document lines are 0 based. In search 1 based.
1046
- // int start = document.getLineOffset(Math.max(line-2, 0));
1047
- // int end = document.getLength();
1048
- // try {
1049
- // end = document.getLineOffset(line+3);
1050
- // } catch (BadLocationException e) {
1051
- // //Presumably line number is past the end of document.
1052
- // //ignore.
1053
- // }
1054
- // details.setDocument(document, start, end-start);
1055
- //
1056
- // String visibleText = document.get(start, end-start);
1057
- // List<TextRange> matches = getQuery().findAll(visibleText);
1058
- // Region visibleRegion = new Region(start, end-start);
1059
- // TextPresentation presentation = new TextPresentation(visibleRegion, 20);
1060
- // presentation.setDefaultStyleRange(new StyleRange(0, document.getLength(), null, null));
1061
- // for (TextRange m : matches) {
1062
- // presentation.addStyleRange(new StyleRange(m.start+start, m.len, null, YELLOW));
1063
- // }
1064
- // details.changeTextPresentation(presentation, true);
1065
- //
1066
- // return;
1067
- // } catch (BadLocationException e) {
1068
- // }
1069
- // }
1070
- // details.setDocument(null);
1071
- // }
1072
- // }
1073
-
1074
1171
/**
1075
1172
* Handle selection in the items list by updating labels of selected and
1076
1173
* unselected items and refresh the details field using the selection.
@@ -1467,4 +1564,67 @@ public QuickTextQuery getQuery() {
1467
1564
return searcher .getQuery ();
1468
1565
}
1469
1566
1567
+ /**
1568
+ * A painter that does 'current line' highlighting (background color) but for single fixed line.
1569
+ * <br><br>
1570
+ * This class piggybacks on {@link CursorLinePainter} instance already added to viewer's widget, which knows what color
1571
+ * to use and does all the necessary repainting. This class only forces the background color to be used also for one
1572
+ * fixed line in addition to current line = line where caret is currently located (done by <code>CursorLinePainter</code>).
1573
+ * Background color for this fixed line never changes and so <code>CursorLinePainter</code>'s repainting code, although not
1574
+ * knowing about this fixed line at all, produces correct visual results - target line always highlighted.
1575
+ *
1576
+ * @see CursorLinePainter
1577
+ */
1578
+ private class FixedLinePainter implements IPainter , LineBackgroundListener {
1579
+
1580
+ private CursorLinePainter cursorLinePainter ;
1581
+ private int lineOffset ;
1582
+ private Color highlightColor ;
1583
+
1584
+ public void setTargetLineOffset (int lineOffset ) {
1585
+ this .lineOffset = lineOffset ;
1586
+ }
1587
+
1588
+ // CursorLinePainter adds itself as line LineBackgroundListener lazily
1589
+ private boolean cursorLinePainterInstalled () {
1590
+ if (cursorLinePainter == null ) {
1591
+ cursorLinePainter = viewer .getTextWidget ().getTypedListeners (ST .LineGetBackground , CursorLinePainter .class ).findFirst ().orElse (null );
1592
+ return cursorLinePainter != null ;
1593
+ }
1594
+ return true ;
1595
+ }
1596
+
1597
+ @ Override
1598
+ public void lineGetBackground (LineBackgroundEvent event ) {
1599
+ if (cursorLinePainterInstalled ()) {
1600
+ cursorLinePainter .lineGetBackground (event );
1601
+ if (event .lineBackground != null ) {
1602
+ // piggyback on CursorLinePainter knowing proper color
1603
+ highlightColor = event .lineBackground ;
1604
+ } else if (lineOffset == event .lineOffset ) { // target line
1605
+ event .lineBackground = highlightColor ;
1606
+ }
1607
+ }
1608
+ }
1609
+
1610
+ @ Override
1611
+ public void dispose () {
1612
+ }
1613
+
1614
+ @ Override
1615
+ public void paint (int reason ) {
1616
+ // no custom painting here, cursorLinePainter (also being registered as IPainter) does all the work
1617
+ // according to background color set in lineGetBackground()
1618
+ }
1619
+
1620
+ @ Override
1621
+ public void deactivate (boolean redraw ) {
1622
+ }
1623
+
1624
+ @ Override
1625
+ public void setPositionManager (IPaintPositionManager manager ) {
1626
+ }
1627
+
1628
+ }
1629
+
1470
1630
}
0 commit comments