KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > javaeditor > JavaSourceViewer


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.internal.ui.javaeditor;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18
19 import org.eclipse.core.runtime.Assert;
20
21 import org.eclipse.swt.SWT;
22 import org.eclipse.swt.custom.BidiSegmentEvent;
23 import org.eclipse.swt.custom.BidiSegmentListener;
24 import org.eclipse.swt.custom.StyleRange;
25 import org.eclipse.swt.custom.StyledText;
26 import org.eclipse.swt.graphics.Color;
27 import org.eclipse.swt.graphics.Point;
28 import org.eclipse.swt.graphics.RGB;
29 import org.eclipse.swt.widgets.Composite;
30 import org.eclipse.swt.widgets.Display;
31
32 import org.eclipse.jface.preference.IPreferenceStore;
33 import org.eclipse.jface.preference.PreferenceConverter;
34 import org.eclipse.jface.util.IPropertyChangeListener;
35 import org.eclipse.jface.util.PropertyChangeEvent;
36
37 import org.eclipse.jface.text.BadLocationException;
38 import org.eclipse.jface.text.IDocument;
39 import org.eclipse.jface.text.IRegion;
40 import org.eclipse.jface.text.ITextPresentationListener;
41 import org.eclipse.jface.text.ITypedRegion;
42 import org.eclipse.jface.text.Region;
43 import org.eclipse.jface.text.TextUtilities;
44 import org.eclipse.jface.text.formatter.FormattingContextProperties;
45 import org.eclipse.jface.text.formatter.IFormattingContext;
46 import org.eclipse.jface.text.information.IInformationPresenter;
47 import org.eclipse.jface.text.reconciler.IReconciler;
48 import org.eclipse.jface.text.source.IOverviewRuler;
49 import org.eclipse.jface.text.source.IVerticalRuler;
50 import org.eclipse.jface.text.source.SourceViewerConfiguration;
51 import org.eclipse.jface.text.source.projection.ProjectionViewer;
52
53 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
54 import org.eclipse.ui.texteditor.AbstractTextEditor;
55
56 import org.eclipse.jdt.core.JavaCore;
57
58 import org.eclipse.jdt.ui.PreferenceConstants;
59 import org.eclipse.jdt.ui.text.IJavaPartitions;
60 import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
61
62 import org.eclipse.jdt.internal.ui.text.SmartBackspaceManager;
63 import org.eclipse.jdt.internal.ui.text.comment.CommentFormattingContext;
64
65
66
67 public class JavaSourceViewer extends ProjectionViewer implements IPropertyChangeListener {
68
69     /**
70      * Text operation code for requesting the outline for the current input.
71      */

72     public static final int SHOW_OUTLINE= 51;
73
74     /**
75      * Text operation code for requesting the outline for the element at the current position.
76      */

77     public static final int OPEN_STRUCTURE= 52;
78
79     /**
80      * Text operation code for requesting the hierarchy for the current input.
81      */

82     public static final int SHOW_HIERARCHY= 53;
83
84     private IInformationPresenter fOutlinePresenter;
85     private IInformationPresenter fStructurePresenter;
86     private IInformationPresenter fHierarchyPresenter;
87
88     /**
89      * This viewer's foreground color.
90      * @since 3.0
91      */

92     private Color fForegroundColor;
93     /**
94      * The viewer's background color.
95      * @since 3.0
96      */

97     private Color fBackgroundColor;
98     /**
99      * This viewer's selection foreground color.
100      * @since 3.0
101      */

102     private Color fSelectionForegroundColor;
103     /**
104      * The viewer's selection background color.
105      * @since 3.0
106      */

107     private Color fSelectionBackgroundColor;
108     /**
109      * The preference store.
110      *
111      * @since 3.0
112      */

113     private IPreferenceStore fPreferenceStore;
114     /**
115      * Is this source viewer configured?
116      *
117      * @since 3.0
118      */

119     private boolean fIsConfigured;
120     /**
121      * The backspace manager of this viewer.
122      *
123      * @since 3.0
124      */

125     private SmartBackspaceManager fBackspaceManager;
126
127     /**
128      * Whether to delay setting the visual document until the projection has been computed.
129      * <p>
130      * Added for performance optimization.
131      * </p>
132      * @see #prepareDelayedProjection()
133      * @since 3.1
134      */

135     private boolean fIsSetVisibleDocumentDelayed= false;
136
137     public JavaSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles, IPreferenceStore store) {
138         super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles);
139         setPreferenceStore(store);
140     }
141
142     /*
143      * @see org.eclipse.jface.text.source.SourceViewer#createFormattingContext()
144      * @since 3.0
145      */

146     public IFormattingContext createFormattingContext() {
147
148         // it's ok to use instance preferences here as subclasses replace
149
// with project dependent versions (see CompilationUnitEditor.AdaptedSourceViewer)
150
IFormattingContext context= new CommentFormattingContext();
151         Map JavaDoc map= new HashMap JavaDoc(JavaCore.getOptions());
152         context.setProperty(FormattingContextProperties.CONTEXT_PREFERENCES, map);
153
154         return context;
155     }
156
157     /*
158      * @see ITextOperationTarget#doOperation(int)
159      */

160     public void doOperation(int operation) {
161         if (getTextWidget() == null)
162             return;
163
164         switch (operation) {
165             case SHOW_OUTLINE:
166                 if (fOutlinePresenter != null)
167                     fOutlinePresenter.showInformation();
168                 return;
169             case OPEN_STRUCTURE:
170                 if (fStructurePresenter != null)
171                     fStructurePresenter.showInformation();
172                 return;
173             case SHOW_HIERARCHY:
174                 if (fHierarchyPresenter != null)
175                     fHierarchyPresenter.showInformation();
176                 return;
177         }
178
179         super.doOperation(operation);
180     }
181
182     /*
183      * @see ITextOperationTarget#canDoOperation(int)
184      */

185     public boolean canDoOperation(int operation) {
186         if (operation == SHOW_OUTLINE)
187             return fOutlinePresenter != null;
188         if (operation == OPEN_STRUCTURE)
189             return fStructurePresenter != null;
190         if (operation == SHOW_HIERARCHY)
191             return fHierarchyPresenter != null;
192
193         return super.canDoOperation(operation);
194     }
195
196     /*
197      * @see ISourceViewer#configure(SourceViewerConfiguration)
198      */

199     public void configure(SourceViewerConfiguration configuration) {
200
201         /*
202          * Prevent access to colors disposed in unconfigure(), see:
203          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=53641
204          * https://bugs.eclipse.org/bugs/show_bug.cgi?id=86177
205          */

206         StyledText textWidget= getTextWidget();
207         if (textWidget != null && !textWidget.isDisposed()) {
208             Color foregroundColor= textWidget.getForeground();
209             if (foregroundColor != null && foregroundColor.isDisposed())
210                 textWidget.setForeground(null);
211             Color backgroundColor= textWidget.getBackground();
212             if (backgroundColor != null && backgroundColor.isDisposed())
213                 textWidget.setBackground(null);
214         }
215
216         super.configure(configuration);
217         if (configuration instanceof JavaSourceViewerConfiguration) {
218             JavaSourceViewerConfiguration javaSVCconfiguration= (JavaSourceViewerConfiguration)configuration;
219             fOutlinePresenter= javaSVCconfiguration.getOutlinePresenter(this, false);
220             if (fOutlinePresenter != null)
221                 fOutlinePresenter.install(this);
222
223             fStructurePresenter= javaSVCconfiguration.getOutlinePresenter(this, true);
224             if (fStructurePresenter != null)
225                 fStructurePresenter.install(this);
226
227             fHierarchyPresenter= javaSVCconfiguration.getHierarchyPresenter(this, true);
228             if (fHierarchyPresenter != null)
229                 fHierarchyPresenter.install(this);
230
231         }
232
233         if (fPreferenceStore != null) {
234             fPreferenceStore.addPropertyChangeListener(this);
235             initializeViewerColors();
236         }
237
238         fIsConfigured= true;
239     }
240
241
242     protected void initializeViewerColors() {
243         if (fPreferenceStore != null) {
244
245             StyledText styledText= getTextWidget();
246
247             // ----------- foreground color --------------------
248
Color color= fPreferenceStore.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
249             ? null
250             : createColor(fPreferenceStore, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay());
251             styledText.setForeground(color);
252
253             if (fForegroundColor != null)
254                 fForegroundColor.dispose();
255
256             fForegroundColor= color;
257
258             // ---------- background color ----------------------
259
color= fPreferenceStore.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)
260             ? null
261             : createColor(fPreferenceStore, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay());
262             styledText.setBackground(color);
263
264             if (fBackgroundColor != null)
265                 fBackgroundColor.dispose();
266
267             fBackgroundColor= color;
268
269             // ----------- selection foreground color --------------------
270
color= fPreferenceStore.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_DEFAULT_COLOR)
271                 ? null
272                 : createColor(fPreferenceStore, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_COLOR, styledText.getDisplay());
273             styledText.setSelectionForeground(color);
274
275             if (fSelectionForegroundColor != null)
276                 fSelectionForegroundColor.dispose();
277
278             fSelectionForegroundColor= color;
279
280             // ---------- selection background color ----------------------
281
color= fPreferenceStore.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_DEFAULT_COLOR)
282                 ? null
283                 : createColor(fPreferenceStore, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_COLOR, styledText.getDisplay());
284             styledText.setSelectionBackground(color);
285
286             if (fSelectionBackgroundColor != null)
287                 fSelectionBackgroundColor.dispose();
288
289             fSelectionBackgroundColor= color;
290         }
291     }
292
293     /**
294      * Creates a color from the information stored in the given preference store.
295      * Returns <code>null</code> if there is no such information available.
296      *
297      * @param store the store to read from
298      * @param key the key used for the lookup in the preference store
299      * @param display the display used create the color
300      * @return the created color according to the specification in the preference store
301      * @since 3.0
302      */

303     private Color createColor(IPreferenceStore store, String JavaDoc key, Display display) {
304
305         RGB rgb= null;
306
307         if (store.contains(key)) {
308
309             if (store.isDefault(key))
310                 rgb= PreferenceConverter.getDefaultColor(store, key);
311             else
312                 rgb= PreferenceConverter.getColor(store, key);
313
314             if (rgb != null)
315                 return new Color(display, rgb);
316         }
317
318         return null;
319     }
320
321     /*
322      * @see org.eclipse.jface.text.source.ISourceViewerExtension2#unconfigure()
323      * @since 3.0
324      */

325     public void unconfigure() {
326         if (fOutlinePresenter != null) {
327             fOutlinePresenter.uninstall();
328             fOutlinePresenter= null;
329         }
330         if (fStructurePresenter != null) {
331             fStructurePresenter.uninstall();
332             fStructurePresenter= null;
333         }
334         if (fHierarchyPresenter != null) {
335             fHierarchyPresenter.uninstall();
336             fHierarchyPresenter= null;
337         }
338         if (fForegroundColor != null) {
339             fForegroundColor.dispose();
340             fForegroundColor= null;
341         }
342         if (fBackgroundColor != null) {
343             fBackgroundColor.dispose();
344             fBackgroundColor= null;
345         }
346
347         if (fPreferenceStore != null)
348             fPreferenceStore.removePropertyChangeListener(this);
349
350         super.unconfigure();
351
352         fIsConfigured= false;
353     }
354
355     /*
356      * @see org.eclipse.jface.text.source.SourceViewer#rememberSelection()
357      */

358     public Point rememberSelection() {
359         return super.rememberSelection();
360     }
361
362     /*
363      * @see org.eclipse.jface.text.source.SourceViewer#restoreSelection()
364      */

365     public void restoreSelection() {
366         super.restoreSelection();
367     }
368
369     /*
370      * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
371      */

372     public void propertyChange(PropertyChangeEvent event) {
373         String JavaDoc property = event.getProperty();
374         if (AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND.equals(property)
375                 || AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT.equals(property)
376                 || AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND.equals(property)
377                 || AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
378                 || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_COLOR.equals(property)
379                 || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_DEFAULT_COLOR.equals(property)
380                 || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_COLOR.equals(property)
381                 || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_DEFAULT_COLOR.equals(property))
382         {
383             initializeViewerColors();
384         }
385     }
386
387     /**
388      * Sets the preference store on this viewer.
389      *
390      * @param store the preference store
391      *
392      * @since 3.0
393      */

394     public void setPreferenceStore(IPreferenceStore store) {
395         if (fIsConfigured && fPreferenceStore != null)
396             fPreferenceStore.removePropertyChangeListener(this);
397
398         fPreferenceStore= store;
399
400         if (fIsConfigured && fPreferenceStore != null) {
401             fPreferenceStore.addPropertyChangeListener(this);
402             initializeViewerColors();
403         }
404     }
405     
406     /*
407      * @see org.eclipse.jface.text.ITextViewer#resetVisibleRegion()
408      * @since 3.1
409      */

410     public void resetVisibleRegion() {
411         super.resetVisibleRegion();
412         // re-enable folding if ProjectionViewer failed to due so
413
if (fPreferenceStore != null && fPreferenceStore.getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED) && !isProjectionMode())
414             enableProjection();
415     }
416
417     /*
418      * @see org.eclipse.jface.text.source.SourceViewer#createControl(org.eclipse.swt.widgets.Composite, int)
419      */

420     protected void createControl(Composite parent, int styles) {
421
422         // Use LEFT_TO_RIGHT unless otherwise specified.
423
if ((styles & SWT.RIGHT_TO_LEFT) == 0 && (styles & SWT.LEFT_TO_RIGHT) == 0)
424             styles |= SWT.LEFT_TO_RIGHT;
425             
426         super.createControl(parent, styles);
427
428         fBackspaceManager= new SmartBackspaceManager();
429         fBackspaceManager.install(this);
430
431         StyledText text= getTextWidget();
432         text.addBidiSegmentListener(new BidiSegmentListener() {
433             public void lineGetSegments(BidiSegmentEvent event) {
434                 if (redraws())
435                     event.segments= getBidiLineSegments(event.lineOffset, event.lineText);
436             }
437         });
438     }
439
440     /**
441      * Returns the backspace manager for this viewer.
442      *
443      * @return the backspace manager for this viewer, or <code>null</code> if
444      * there is none
445      * @since 3.0
446      */

447     public SmartBackspaceManager getBackspaceManager() {
448         return fBackspaceManager;
449     }
450
451     /*
452      * @see org.eclipse.jface.text.source.SourceViewer#handleDispose()
453      */

454     protected void handleDispose() {
455         if (fBackspaceManager != null) {
456             fBackspaceManager.uninstall();
457             fBackspaceManager= null;
458         }
459
460         super.handleDispose();
461     }
462
463     /**
464      * Prepends the text presentation listener at the beginning of the viewer's
465      * list of text presentation listeners. If the listener is already registered
466      * with the viewer this call moves the listener to the beginning of
467      * the list.
468      *
469      * @param listener the text presentation listener
470      * @since 3.0
471      */

472     public void prependTextPresentationListener(ITextPresentationListener listener) {
473
474         Assert.isNotNull(listener);
475
476         if (fTextPresentationListeners == null)
477             fTextPresentationListeners= new ArrayList JavaDoc();
478
479         fTextPresentationListeners.remove(listener);
480         fTextPresentationListeners.add(0, listener);
481     }
482
483     /**
484      * Sets the given reconciler.
485      *
486      * @param reconciler the reconciler
487      * @since 3.0
488      */

489     void setReconciler(IReconciler reconciler) {
490         fReconciler= reconciler;
491     }
492
493     /**
494      * Returns the reconciler.
495      *
496      * @return the reconciler or <code>null</code> if not set
497      * @since 3.0
498      */

499     IReconciler getReconciler() {
500         return fReconciler;
501     }
502
503     /**
504      * Returns a segmentation of the given line appropriate for BIDI rendering. The default
505      * implementation returns only the string literals of a java code line as segments.
506      *
507      * @param widgetLineOffset the offset of the line
508      * @param line the content of the line
509      * @return the line's BIDI segmentation
510      */

511     protected int[] getBidiLineSegments(int widgetLineOffset, String JavaDoc line) {
512         if (line != null && line.length() > 0) {
513             int lineOffset= widgetOffset2ModelOffset(widgetLineOffset);
514             try {
515                 return getBidiLineSegments(getDocument(), lineOffset);
516             } catch (BadLocationException x) {
517                 return null; // don't segment line in this case
518
}
519         }
520         return null;
521     }
522
523     /**
524      * Returns a segmentation of the line of the given document appropriate for
525      * BIDI rendering. The default implementation returns only the string literals of a java code
526      * line as segments.
527      *
528      * @param document the document
529      * @param lineOffset the offset of the line
530      * @return the line's BIDI segmentation
531      * @throws BadLocationException in case lineOffset is not valid in document
532      */

533     protected static int[] getBidiLineSegments(IDocument document, int lineOffset) throws BadLocationException {
534
535         if (document == null)
536             return null;
537
538         IRegion line= document.getLineInformationOfOffset(lineOffset);
539         ITypedRegion[] linePartitioning= TextUtilities.computePartitioning(document, IJavaPartitions.JAVA_PARTITIONING, lineOffset, line.getLength(), false);
540
541         List JavaDoc segmentation= new ArrayList JavaDoc();
542         for (int i= 0; i < linePartitioning.length; i++) {
543             if (IJavaPartitions.JAVA_STRING.equals(linePartitioning[i].getType()))
544                 segmentation.add(linePartitioning[i]);
545         }
546
547
548         if (segmentation.size() == 0)
549             return null;
550
551         int size= segmentation.size();
552         int[] segments= new int[size * 2 + 1];
553
554         int j= 0;
555         for (int i= 0; i < size; i++) {
556             ITypedRegion segment= (ITypedRegion) segmentation.get(i);
557
558             if (i == 0)
559                 segments[j++]= 0;
560
561             int offset= segment.getOffset() - lineOffset;
562             if (offset > segments[j - 1])
563                 segments[j++]= offset;
564
565             if (offset + segment.getLength() >= line.getLength())
566                 break;
567
568             segments[j++]= offset + segment.getLength();
569         }
570
571         if (j < segments.length) {
572             int[] result= new int[j];
573             System.arraycopy(segments, 0, result, 0, j);
574             segments= result;
575         }
576
577         return segments;
578     }
579
580     /**
581      * Delays setting the visual document until after the projection has been computed.
582      * This method must only be called before the document is set on the viewer.
583      * <p>
584      * This is a performance optimization to reduce the computation of
585      * the text presentation triggered by <code>setVisibleDocument(IDocument)</code>.
586      * </p>
587      *
588      * @see #setVisibleDocument(IDocument)
589      * @since 3.1
590      */

591     void prepareDelayedProjection() {
592         Assert.isTrue(!fIsSetVisibleDocumentDelayed);
593         fIsSetVisibleDocumentDelayed= true;
594     }
595     
596     /**
597      * {@inheritDoc}
598      * <p>
599      * This is a performance optimization to reduce the computation of
600      * the text presentation triggered by {@link #setVisibleDocument(IDocument)}
601      * </p>
602      * @see #prepareDelayedProjection()
603      * @since 3.1
604      */

605     protected void setVisibleDocument(IDocument document) {
606         if (fIsSetVisibleDocumentDelayed) {
607             fIsSetVisibleDocumentDelayed= false;
608             IDocument previous= getVisibleDocument();
609             enableProjection(); // will set the visible document if anything is folded
610
IDocument current= getVisibleDocument();
611             // if the visible document was not replaced, continue as usual
612
if (current != null && current != previous)
613                 return;
614         }
615         
616         super.setVisibleDocument(document);
617     }
618
619     /**
620      * {@inheritDoc}
621      * <p>
622      * Performance optimization: since we know at this place
623      * that none of the clients expects the given range to be
624      * untouched we reuse the given range as return value.
625      * </p>
626      */

627     protected StyleRange modelStyleRange2WidgetStyleRange(StyleRange range) {
628         IRegion region= modelRange2WidgetRange(new Region(range.start, range.length));
629         if (region != null) {
630             // don't clone the style range, but simply reuse it.
631
range.start= region.getOffset();
632             range.length= region.getLength();
633             return range;
634         }
635         return null;
636     }
637 }
638
Popular Tags