44
44
import org .eclipse .jface .dialogs .IDialogConstants ;
45
45
import org .eclipse .jface .dialogs .IDialogSettings ;
46
46
import org .eclipse .jface .layout .GridDataFactory ;
47
+ import org .eclipse .jface .preference .IPreferenceStore ;
48
+ import org .eclipse .jface .preference .PreferenceConverter ;
47
49
import org .eclipse .jface .resource .JFaceResources ;
48
50
import org .eclipse .jface .text .BadLocationException ;
51
+ import org .eclipse .jface .text .CursorLinePainter ;
49
52
import org .eclipse .jface .text .IDocument ;
53
+ import org .eclipse .jface .text .IPaintPositionManager ;
54
+ import org .eclipse .jface .text .IPainter ;
50
55
import org .eclipse .jface .text .IRegion ;
56
+ import org .eclipse .jface .text .source .CompositeRuler ;
57
+ import org .eclipse .jface .text .source .ISharedTextColors ;
58
+ import org .eclipse .jface .text .source .LineNumberRulerColumn ;
59
+ import org .eclipse .jface .text .source .SourceViewer ;
60
+ import org .eclipse .jface .util .IPropertyChangeListener ;
61
+ import org .eclipse .jface .util .PropertyChangeEvent ;
51
62
import org .eclipse .jface .viewers .ILazyContentProvider ;
52
63
import org .eclipse .jface .viewers .IStructuredContentProvider ;
53
64
import org .eclipse .jface .viewers .IStructuredSelection ;
65
76
import org .eclipse .swt .accessibility .ACC ;
66
77
import org .eclipse .swt .accessibility .AccessibleAdapter ;
67
78
import org .eclipse .swt .accessibility .AccessibleEvent ;
79
+ import org .eclipse .swt .custom .LineBackgroundEvent ;
80
+ import org .eclipse .swt .custom .LineBackgroundListener ;
81
+ import org .eclipse .swt .custom .ST ;
68
82
import org .eclipse .swt .custom .SashForm ;
69
83
import org .eclipse .swt .custom .StyleRange ;
70
84
import org .eclipse .swt .custom .StyledText ;
79
93
import org .eclipse .swt .graphics .Color ;
80
94
import org .eclipse .swt .graphics .Image ;
81
95
import org .eclipse .swt .graphics .Point ;
96
+ import org .eclipse .swt .graphics .RGB ;
82
97
import org .eclipse .swt .graphics .Rectangle ;
98
+ import org .eclipse .swt .layout .FillLayout ;
83
99
import org .eclipse .swt .layout .GridData ;
84
100
import org .eclipse .swt .layout .GridLayout ;
85
101
import org .eclipse .swt .widgets .Button ;
102
+ import org .eclipse .swt .widgets .Canvas ;
86
103
import org .eclipse .swt .widgets .Composite ;
87
104
import org .eclipse .swt .widgets .Control ;
88
105
import org .eclipse .swt .widgets .Display ;
108
125
import org .eclipse .ui .PartInitException ;
109
126
import org .eclipse .ui .PlatformUI ;
110
127
import org .eclipse .ui .dialogs .SelectionStatusDialog ;
128
+ import org .eclipse .ui .editors .text .EditorsUI ;
111
129
import org .eclipse .ui .handlers .IHandlerActivation ;
112
130
import org .eclipse .ui .handlers .IHandlerService ;
113
131
import org .eclipse .ui .internal .IWorkbenchGraphicConstants ;
114
132
import org .eclipse .ui .internal .WorkbenchImages ;
115
133
import org .eclipse .ui .internal .ide .IDEWorkbenchPlugin ;
116
134
import org .eclipse .ui .progress .UIJob ;
135
+ import org .eclipse .ui .texteditor .AbstractDecoratedTextEditorPreferenceConstants ;
136
+ import org .eclipse .ui .texteditor .AbstractTextEditor ;
137
+ import org .eclipse .ui .texteditor .SourceViewerDecorationSupport ;
117
138
import org .osgi .framework .FrameworkUtil ;
118
139
119
140
/**
@@ -352,7 +373,11 @@ public void update(ViewerCell cell) {
352
373
353
374
private QuickTextSearcher searcher ;
354
375
355
- private StyledText details ;
376
+ private SourceViewer viewer ;
377
+ private LineNumberRulerColumn lineNumberColumn ;
378
+ private SourceViewerDecorationSupport sourceViewerDecorationSupport ;
379
+ private FixedLinePainter targetLinePainter ;
380
+ private final IPropertyChangeListener preferenceChangeListener = this ::handlePropertyChangeEvent ;
356
381
357
382
private DocumentFetcher documents ;
358
383
@@ -391,6 +416,7 @@ public QuickSearchDialog(IWorkbenchWindow window) {
391
416
MAX_LINE_LEN = QuickSearchActivator .getDefault ().getPreferences ().getMaxLineLen ();
392
417
MAX_RESULTS = QuickSearchActivator .getDefault ().getPreferences ().getMaxResults ();
393
418
progressJob .setSystem (true );
419
+ EditorsUI .getPreferenceStore ().addPropertyChangeListener (preferenceChangeListener );
394
420
}
395
421
396
422
/*
@@ -915,31 +941,123 @@ protected void dispose() {
915
941
blankImage .dispose ();
916
942
blankImage = null ;
917
943
}
944
+ EditorsUI .getPreferenceStore ().removePropertyChangeListener (preferenceChangeListener );
918
945
}
919
946
920
947
private void createDetailsArea (Composite parent ) {
921
- details = new StyledText (parent , SWT .MULTI +SWT .READ_ONLY +SWT .BORDER +SWT .H_SCROLL +SWT .V_SCROLL );
922
- details .setFont (JFaceResources .getFont (TEXT_FONT ));
948
+ var viewerParent = new Canvas (parent , SWT .BORDER );
949
+ viewerParent .setLayout (new FillLayout ());
950
+
951
+ viewer = new SourceViewer (viewerParent , new CompositeRuler (), SWT .H_SCROLL | SWT .V_SCROLL | SWT .READ_ONLY );
952
+ viewer .getTextWidget ().setFont (JFaceResources .getFont (TEXT_FONT ));
953
+
954
+ lineNumberColumn = new LineNumberRulerColumn ();
955
+ viewer .addVerticalRulerColumn (lineNumberColumn );
956
+ setColors (false );
957
+ createLinesHighlightingDecorations ();
923
958
924
959
list .addSelectionChangedListener (event -> refreshDetails ());
925
- details .addControlListener (new ControlAdapter () {
960
+
961
+ viewer .getTextWidget ().addControlListener (new ControlAdapter () {
926
962
@ Override
927
963
public void controlResized (ControlEvent e ) {
928
964
refreshDetails ();
929
965
}
930
966
});
931
967
}
932
968
969
+ private void setColors (boolean refresh ) {
970
+ RGB background = null ;
971
+ RGB foreground = null ;
972
+ var textWidget = viewer .getTextWidget ();
973
+ var preferenceStore = EditorsUI .getPreferenceStore ();
974
+ ISharedTextColors sharedColors = EditorsUI .getSharedTextColors ();
975
+
976
+ var isUsingSystemBackground = preferenceStore .getBoolean (AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT );
977
+ if (!isUsingSystemBackground ) {
978
+ background = getColorFromStore (preferenceStore , AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND );
979
+ }
980
+ if (background != null ) {
981
+ var color = sharedColors .getColor (background );
982
+ textWidget .setBackground (color );
983
+ lineNumberColumn .setBackground (color );
984
+ } else {
985
+ textWidget .setBackground (null );
986
+ lineNumberColumn .setBackground (null );
987
+ }
988
+
989
+ var isUsingSystemForeground = preferenceStore .getBoolean (AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT );
990
+ if (!isUsingSystemForeground ) {
991
+ foreground = getColorFromStore (preferenceStore , AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND );
992
+ }
993
+ if (foreground != null ) {
994
+ textWidget .setForeground (sharedColors .getColor (foreground ));
995
+ } else {
996
+ textWidget .setForeground (null );
997
+ }
998
+
999
+ var lineNumbersColor = getColorFromStore (EditorsUI .getPreferenceStore (), AbstractDecoratedTextEditorPreferenceConstants .EDITOR_LINE_NUMBER_RULER_COLOR );
1000
+ if (lineNumbersColor == null ) {
1001
+ lineNumbersColor = new RGB (0 , 0 , 0 );
1002
+ }
1003
+ lineNumberColumn .setForeground (sharedColors .getColor (lineNumbersColor ));
1004
+
1005
+ if (refresh ) {
1006
+ textWidget .redraw ();
1007
+ lineNumberColumn .redraw ();
1008
+ }
1009
+ }
1010
+
1011
+
1012
+ private void createLinesHighlightingDecorations () {
1013
+ sourceViewerDecorationSupport = new SourceViewerDecorationSupport (viewer , null , null , EditorsUI .getSharedTextColors ());
1014
+ sourceViewerDecorationSupport .setCursorLinePainterPreferenceKeys (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE , AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE_COLOR );
1015
+ sourceViewerDecorationSupport .install (EditorsUI .getPreferenceStore ());
1016
+ targetLinePainter = new FixedLinePainter ();
1017
+ viewer .addPainter (targetLinePainter );
1018
+ viewer .getTextWidget ().addLineBackgroundListener (targetLinePainter );
1019
+ }
1020
+
1021
+ private void handlePropertyChangeEvent (PropertyChangeEvent event ) {
1022
+ if (viewer == null ) {
1023
+ return ;
1024
+ }
1025
+ var prop = event .getProperty ();
1026
+ if (prop .equals (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_LINE_NUMBER_RULER_COLOR )
1027
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT )
1028
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_BACKGROUND )
1029
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT )
1030
+ || prop .equals (AbstractTextEditor .PREFERENCE_COLOR_FOREGROUND )) {
1031
+ setColors (true );
1032
+ }
1033
+ if (prop .equals (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE )
1034
+ || prop .equals (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_CURRENT_LINE_COLOR )) {
1035
+ targetLinePainter .highlightColor = null ;
1036
+ targetLinePainter .cursorLinePainter = null ;
1037
+ viewer .getTextWidget ().redraw ();
1038
+ }
1039
+ }
1040
+
1041
+ private RGB getColorFromStore (IPreferenceStore store , String key ) {
1042
+ RGB rgb = null ;
1043
+ if (store .contains (key )) {
1044
+ if (store .isDefault (key )) {
1045
+ rgb = PreferenceConverter .getDefaultColor (store , key );
1046
+ } else {
1047
+ rgb = PreferenceConverter .getColor (store , key );
1048
+ }
1049
+ }
1050
+ return rgb ;
1051
+ }
933
1052
934
- // Dumber version just using the a 'raw' StyledText widget.
935
1053
private void refreshDetails () {
936
- if (details !=null && list !=null && !list .getTable ().isDisposed ()) {
1054
+ if (viewer !=null && list !=null && !list .getTable ().isDisposed ()) {
937
1055
if (documents ==null ) {
938
1056
documents = new DocumentFetcher ();
939
1057
}
940
1058
IStructuredSelection sel = (IStructuredSelection ) list .getSelection ();
941
1059
if (sel ==null || sel .isEmpty ()) {
942
- details . setText ( EMPTY_STRING );
1060
+ viewer . setDocument ( null );
943
1061
} else {
944
1062
//Not empty selection
945
1063
final int context = 100 ; // number of lines before and after match to include in preview
@@ -952,35 +1070,56 @@ private void refreshDetails() {
952
1070
int line = item .getLineNumber ()-1 ; //in document lines are 0 based. In search 1 based.
953
1071
int contextStartLine = Math .max (line -(numLines -1 )/2 - context , 0 );
954
1072
int start = document .getLineOffset (contextStartLine );
1073
+ int displayedEndLine = line + numLines /2 ;
955
1074
int end = document .getLength ();
956
- try {
957
- IRegion lineInfo = document .getLineInformation (line + numLines /2 + context );
958
- end = lineInfo .getOffset () + lineInfo .getLength ();
959
- } catch (BadLocationException e ) {
960
- //Presumably line number is past the end of document.
961
- //ignore.
1075
+ if (displayedEndLine + context <= document .getNumberOfLines ()) {
1076
+ try {
1077
+ IRegion lineInfo = document .getLineInformation (displayedEndLine + context );
1078
+ end = lineInfo .getOffset () + lineInfo .getLength ();
1079
+ } catch (BadLocationException e ) {
1080
+ //Presumably line number is past the end of document.
1081
+ //ignore.
1082
+ }
962
1083
}
1084
+ int contextLenght = end -start ;
1085
+
1086
+ viewer .setDocument (document );
1087
+ viewer .setVisibleRegion (start , contextLenght );
1088
+
1089
+ targetLinePainter .setTargetLineOffset (item .getOffset () - start );
1090
+
1091
+ // center target line in the displayed area
1092
+ IRegion rangeEndLineInfo = document .getLineInformation (Math .min (displayedEndLine , document .getNumberOfLines () - 1 ));
1093
+ int rangeStart = document .getLineOffset (Math .max (line - numLines /2 , 0 ));
1094
+ int rangeEnd = rangeEndLineInfo .getOffset () + rangeEndLineInfo .getLength ();
1095
+ viewer .revealRange (rangeStart , rangeEnd - rangeStart );
1096
+
1097
+ var targetLineFirstMatch = getQuery ().findFirst (document .get (item .getOffset (), contextLenght - (item .getOffset () - start )));
1098
+ int targetLineFirstMatchStart = item .getOffset () + targetLineFirstMatch .getOffset ();
1099
+ // sets caret position (also highlights that line)
1100
+ viewer .setSelectedRange (targetLineFirstMatchStart , 0 );
1101
+ // does horizontal scrolling if necessary to reveal 1st occurrence in target line
1102
+ viewer .revealRange (targetLineFirstMatchStart , targetLineFirstMatch .getLength ());
963
1103
964
- StyledString styledString = highlightMatches (document .get (start , end -start ));
965
- details .setText (styledString .getString ());
966
- details .setStyleRanges (styledString .getStyleRanges ());
967
- details .setTopIndex (Math .max (line - contextStartLine - numLines /2 , 0 ));
1104
+ StyledString styledString = highlightMatches (document .get (start , contextLenght ));
1105
+ viewer .getTextWidget ().setStyleRanges (styledString .getStyleRanges ());
968
1106
return ;
969
1107
} catch (BadLocationException e ) {
970
1108
}
971
1109
}
972
1110
}
973
1111
}
974
1112
//empty selection or some error:
975
- details . setText ( EMPTY_STRING );
1113
+ viewer . setDocument ( null );
976
1114
}
977
1115
}
978
1116
979
1117
/**
980
1118
* Computes how many lines of text can be displayed in the details section.
981
1119
*/
982
1120
private int computeLines () {
983
- if (details !=null && !details .isDisposed ()) {
1121
+ StyledText details ;
1122
+ if (viewer !=null && !(details = viewer .getTextWidget ()).isDisposed ()) {
984
1123
int lineHeight = details .getLineHeight ();
985
1124
int areaHeight = details .getClientArea ().height ;
986
1125
return (areaHeight + lineHeight - 1 ) / lineHeight ;
@@ -1003,47 +1142,6 @@ private StyledString highlightMatches(String visibleText) {
1003
1142
return styledText ;
1004
1143
}
1005
1144
1006
- // Version using sourceviewer
1007
- // private void refreshDetails() {
1008
- // if (details!=null && list!=null && !list.getTable().isDisposed()) {
1009
- // if (documents==null) {
1010
- // documents = new DocumentFetcher();
1011
- // }
1012
- // IStructuredSelection sel = (IStructuredSelection) list.getSelection();
1013
- // if (sel!=null && !sel.isEmpty()) {
1014
- // //Not empty selection
1015
- // LineItem item = (LineItem) sel.getFirstElement();
1016
- // IDocument document = documents.getDocument(item.getFile());
1017
- // try {
1018
- // int line = item.getLineNumber()-1; //in document lines are 0 based. In search 1 based.
1019
- // int start = document.getLineOffset(Math.max(line-2, 0));
1020
- // int end = document.getLength();
1021
- // try {
1022
- // end = document.getLineOffset(line+3);
1023
- // } catch (BadLocationException e) {
1024
- // //Presumably line number is past the end of document.
1025
- // //ignore.
1026
- // }
1027
- // details.setDocument(document, start, end-start);
1028
- //
1029
- // String visibleText = document.get(start, end-start);
1030
- // List<TextRange> matches = getQuery().findAll(visibleText);
1031
- // Region visibleRegion = new Region(start, end-start);
1032
- // TextPresentation presentation = new TextPresentation(visibleRegion, 20);
1033
- // presentation.setDefaultStyleRange(new StyleRange(0, document.getLength(), null, null));
1034
- // for (TextRange m : matches) {
1035
- // presentation.addStyleRange(new StyleRange(m.start+start, m.len, null, YELLOW));
1036
- // }
1037
- // details.changeTextPresentation(presentation, true);
1038
- //
1039
- // return;
1040
- // } catch (BadLocationException e) {
1041
- // }
1042
- // }
1043
- // details.setDocument(null);
1044
- // }
1045
- // }
1046
-
1047
1145
/**
1048
1146
* Handle selection in the items list by updating labels of selected and
1049
1147
* unselected items and refresh the details field using the selection.
@@ -1440,4 +1538,67 @@ public QuickTextQuery getQuery() {
1440
1538
return searcher .getQuery ();
1441
1539
}
1442
1540
1541
+ /**
1542
+ * A painter that does 'current line' highlighting (background color) but for single fixed line.
1543
+ * <br><br>
1544
+ * This class piggybacks on {@link CursorLinePainter} instance already added to viewer's widget, which knows what color
1545
+ * to use and does all the necessary repainting. This class only forces the background color to be used also for one
1546
+ * fixed line in addition to current line = line where caret is currently located (done by <code>CursorLinePainter</code>).
1547
+ * Background color for this fixed line never changes and so <code>CursorLinePainter</code>'s repainting code, although not
1548
+ * knowing about this fixed line at all, produces correct visual results - target line always highlighted.
1549
+ *
1550
+ * @see CursorLinePainter
1551
+ */
1552
+ private class FixedLinePainter implements IPainter , LineBackgroundListener {
1553
+
1554
+ private CursorLinePainter cursorLinePainter ;
1555
+ private int lineOffset ;
1556
+ private Color highlightColor ;
1557
+
1558
+ public void setTargetLineOffset (int lineOffset ) {
1559
+ this .lineOffset = lineOffset ;
1560
+ }
1561
+
1562
+ // CursorLinePainter adds itself as line LineBackgroundListener lazily
1563
+ private boolean cursorLinePainterInstalled () {
1564
+ if (cursorLinePainter == null ) {
1565
+ cursorLinePainter = viewer .getTextWidget ().getTypedListeners (ST .LineGetBackground , CursorLinePainter .class ).findFirst ().orElse (null );
1566
+ return cursorLinePainter != null ;
1567
+ }
1568
+ return true ;
1569
+ }
1570
+
1571
+ @ Override
1572
+ public void lineGetBackground (LineBackgroundEvent event ) {
1573
+ if (cursorLinePainterInstalled ()) {
1574
+ cursorLinePainter .lineGetBackground (event );
1575
+ if (event .lineBackground != null ) {
1576
+ // piggyback on CursorLinePainter knowing proper color
1577
+ highlightColor = event .lineBackground ;
1578
+ } else if (lineOffset == event .lineOffset ) { // target line
1579
+ event .lineBackground = highlightColor ;
1580
+ }
1581
+ }
1582
+ }
1583
+
1584
+ @ Override
1585
+ public void dispose () {
1586
+ }
1587
+
1588
+ @ Override
1589
+ public void paint (int reason ) {
1590
+ // no custom painting here, cursorLinePainter (also being registered as IPainter) does all the work
1591
+ // according to background color set in lineGetBackground()
1592
+ }
1593
+
1594
+ @ Override
1595
+ public void deactivate (boolean redraw ) {
1596
+ }
1597
+
1598
+ @ Override
1599
+ public void setPositionManager (IPaintPositionManager manager ) {
1600
+ }
1601
+
1602
+ }
1603
+
1443
1604
}
0 commit comments