4444import org .eclipse .jface .dialogs .IDialogConstants ;
4545import org .eclipse .jface .dialogs .IDialogSettings ;
4646import org .eclipse .jface .layout .GridDataFactory ;
47+ import org .eclipse .jface .preference .IPreferenceStore ;
48+ import org .eclipse .jface .preference .PreferenceConverter ;
4749import org .eclipse .jface .resource .JFaceResources ;
4850import org .eclipse .jface .text .BadLocationException ;
51+ import org .eclipse .jface .text .CursorLinePainter ;
4952import org .eclipse .jface .text .IDocument ;
53+ import org .eclipse .jface .text .IPaintPositionManager ;
54+ import org .eclipse .jface .text .IPainter ;
5055import 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 ;
5162import org .eclipse .jface .viewers .ILazyContentProvider ;
5263import org .eclipse .jface .viewers .IStructuredContentProvider ;
5364import org .eclipse .jface .viewers .IStructuredSelection ;
6576import org .eclipse .swt .accessibility .ACC ;
6677import org .eclipse .swt .accessibility .AccessibleAdapter ;
6778import 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 ;
6882import org .eclipse .swt .custom .SashForm ;
6983import org .eclipse .swt .custom .StyleRange ;
7084import org .eclipse .swt .custom .StyledText ;
7993import org .eclipse .swt .graphics .Color ;
8094import org .eclipse .swt .graphics .Image ;
8195import org .eclipse .swt .graphics .Point ;
96+ import org .eclipse .swt .graphics .RGB ;
8297import org .eclipse .swt .graphics .Rectangle ;
98+ import org .eclipse .swt .layout .FillLayout ;
8399import org .eclipse .swt .layout .GridData ;
84100import org .eclipse .swt .layout .GridLayout ;
85101import org .eclipse .swt .widgets .Button ;
102+ import org .eclipse .swt .widgets .Canvas ;
86103import org .eclipse .swt .widgets .Composite ;
87104import org .eclipse .swt .widgets .Control ;
88105import org .eclipse .swt .widgets .Display ;
108125import org .eclipse .ui .PartInitException ;
109126import org .eclipse .ui .PlatformUI ;
110127import org .eclipse .ui .dialogs .SelectionStatusDialog ;
128+ import org .eclipse .ui .editors .text .EditorsUI ;
111129import org .eclipse .ui .handlers .IHandlerActivation ;
112130import org .eclipse .ui .handlers .IHandlerService ;
113131import org .eclipse .ui .internal .IWorkbenchGraphicConstants ;
114132import org .eclipse .ui .internal .WorkbenchImages ;
115133import org .eclipse .ui .internal .ide .IDEWorkbenchPlugin ;
116134import 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 ;
117138import org .osgi .framework .FrameworkUtil ;
118139
119140/**
@@ -352,7 +373,11 @@ public void update(ViewerCell cell) {
352373
353374 private QuickTextSearcher searcher ;
354375
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 ;
356381
357382 private DocumentFetcher documents ;
358383
@@ -391,6 +416,7 @@ public QuickSearchDialog(IWorkbenchWindow window) {
391416 MAX_LINE_LEN = QuickSearchActivator .getDefault ().getPreferences ().getMaxLineLen ();
392417 MAX_RESULTS = QuickSearchActivator .getDefault ().getPreferences ().getMaxResults ();
393418 progressJob .setSystem (true );
419+ EditorsUI .getPreferenceStore ().addPropertyChangeListener (preferenceChangeListener );
394420 }
395421
396422 /*
@@ -915,31 +941,123 @@ protected void dispose() {
915941 blankImage .dispose ();
916942 blankImage = null ;
917943 }
944+ EditorsUI .getPreferenceStore ().removePropertyChangeListener (preferenceChangeListener );
918945 }
919946
920947 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 ();
923958
924959 list .addSelectionChangedListener (event -> refreshDetails ());
925- details .addControlListener (new ControlAdapter () {
960+
961+ viewer .getTextWidget ().addControlListener (new ControlAdapter () {
926962 @ Override
927963 public void controlResized (ControlEvent e ) {
928964 refreshDetails ();
929965 }
930966 });
931967 }
932968
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+ }
9331052
934- // Dumber version just using the a 'raw' StyledText widget.
9351053 private void refreshDetails () {
936- if (details !=null && list !=null && !list .getTable ().isDisposed ()) {
1054+ if (viewer !=null && list !=null && !list .getTable ().isDisposed ()) {
9371055 if (documents ==null ) {
9381056 documents = new DocumentFetcher ();
9391057 }
9401058 IStructuredSelection sel = (IStructuredSelection ) list .getSelection ();
9411059 if (sel ==null || sel .isEmpty ()) {
942- details . setText ( EMPTY_STRING );
1060+ viewer . setDocument ( null );
9431061 } else {
9441062 //Not empty selection
9451063 final int context = 100 ; // number of lines before and after match to include in preview
@@ -952,35 +1070,56 @@ private void refreshDetails() {
9521070 int line = item .getLineNumber ()-1 ; //in document lines are 0 based. In search 1 based.
9531071 int contextStartLine = Math .max (line -(numLines -1 )/2 - context , 0 );
9541072 int start = document .getLineOffset (contextStartLine );
1073+ int displayedEndLine = line + numLines /2 ;
9551074 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+ }
9621083 }
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 ());
9631103
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 ());
9681106 return ;
9691107 } catch (BadLocationException e ) {
9701108 }
9711109 }
9721110 }
9731111 }
9741112 //empty selection or some error:
975- details . setText ( EMPTY_STRING );
1113+ viewer . setDocument ( null );
9761114 }
9771115 }
9781116
9791117 /**
9801118 * Computes how many lines of text can be displayed in the details section.
9811119 */
9821120 private int computeLines () {
983- if (details !=null && !details .isDisposed ()) {
1121+ StyledText details ;
1122+ if (viewer !=null && !(details = viewer .getTextWidget ()).isDisposed ()) {
9841123 int lineHeight = details .getLineHeight ();
9851124 int areaHeight = details .getClientArea ().height ;
9861125 return (areaHeight + lineHeight - 1 ) / lineHeight ;
@@ -1003,47 +1142,6 @@ private StyledString highlightMatches(String visibleText) {
10031142 return styledText ;
10041143 }
10051144
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-
10471145 /**
10481146 * Handle selection in the items list by updating labels of selected and
10491147 * unselected items and refresh the details field using the selection.
@@ -1440,4 +1538,67 @@ public QuickTextQuery getQuery() {
14401538 return searcher .getQuery ();
14411539 }
14421540
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+
14431604}
0 commit comments