001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.javaeditor;
011:
012: import java.util.ArrayList;
013: import java.util.HashMap;
014: import java.util.List;
015: import java.util.Map;
016:
017: import org.eclipse.core.runtime.Assert;
018:
019: import org.eclipse.swt.SWT;
020: import org.eclipse.swt.custom.BidiSegmentEvent;
021: import org.eclipse.swt.custom.BidiSegmentListener;
022: import org.eclipse.swt.custom.StyleRange;
023: import org.eclipse.swt.custom.StyledText;
024: import org.eclipse.swt.graphics.Color;
025: import org.eclipse.swt.graphics.Point;
026: import org.eclipse.swt.graphics.RGB;
027: import org.eclipse.swt.widgets.Composite;
028: import org.eclipse.swt.widgets.Display;
029:
030: import org.eclipse.jface.preference.IPreferenceStore;
031: import org.eclipse.jface.preference.PreferenceConverter;
032: import org.eclipse.jface.util.IPropertyChangeListener;
033: import org.eclipse.jface.util.PropertyChangeEvent;
034:
035: import org.eclipse.jface.text.BadLocationException;
036: import org.eclipse.jface.text.IDocument;
037: import org.eclipse.jface.text.IRegion;
038: import org.eclipse.jface.text.ITextPresentationListener;
039: import org.eclipse.jface.text.ITypedRegion;
040: import org.eclipse.jface.text.Region;
041: import org.eclipse.jface.text.TextUtilities;
042: import org.eclipse.jface.text.formatter.FormattingContextProperties;
043: import org.eclipse.jface.text.formatter.IFormattingContext;
044: import org.eclipse.jface.text.information.IInformationPresenter;
045: import org.eclipse.jface.text.reconciler.IReconciler;
046: import org.eclipse.jface.text.source.IOverviewRuler;
047: import org.eclipse.jface.text.source.IVerticalRuler;
048: import org.eclipse.jface.text.source.SourceViewerConfiguration;
049: import org.eclipse.jface.text.source.projection.ProjectionViewer;
050:
051: import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
052: import org.eclipse.ui.texteditor.AbstractTextEditor;
053:
054: import org.eclipse.jdt.core.JavaCore;
055:
056: import org.eclipse.jdt.ui.text.IJavaPartitions;
057: import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
058:
059: import org.eclipse.jdt.internal.ui.text.SmartBackspaceManager;
060: import org.eclipse.jdt.internal.ui.text.comment.CommentFormattingContext;
061:
062: public class JavaSourceViewer extends ProjectionViewer implements
063: IPropertyChangeListener {
064:
065: /**
066: * Text operation code for requesting the outline for the current input.
067: */
068: public static final int SHOW_OUTLINE = 51;
069:
070: /**
071: * Text operation code for requesting the outline for the element at the current position.
072: */
073: public static final int OPEN_STRUCTURE = 52;
074:
075: /**
076: * Text operation code for requesting the hierarchy for the current input.
077: */
078: public static final int SHOW_HIERARCHY = 53;
079:
080: private IInformationPresenter fOutlinePresenter;
081: private IInformationPresenter fStructurePresenter;
082: private IInformationPresenter fHierarchyPresenter;
083:
084: /**
085: * This viewer's foreground color.
086: * @since 3.0
087: */
088: private Color fForegroundColor;
089: /**
090: * The viewer's background color.
091: * @since 3.0
092: */
093: private Color fBackgroundColor;
094: /**
095: * This viewer's selection foreground color.
096: * @since 3.0
097: */
098: private Color fSelectionForegroundColor;
099: /**
100: * The viewer's selection background color.
101: * @since 3.0
102: */
103: private Color fSelectionBackgroundColor;
104: /**
105: * The preference store.
106: *
107: * @since 3.0
108: */
109: private IPreferenceStore fPreferenceStore;
110: /**
111: * Is this source viewer configured?
112: *
113: * @since 3.0
114: */
115: private boolean fIsConfigured;
116: /**
117: * The backspace manager of this viewer.
118: *
119: * @since 3.0
120: */
121: private SmartBackspaceManager fBackspaceManager;
122:
123: /**
124: * Whether to delay setting the visual document until the projection has been computed.
125: * <p>
126: * Added for performance optimization.
127: * </p>
128: * @see #prepareDelayedProjection()
129: * @since 3.1
130: */
131: private boolean fIsSetVisibleDocumentDelayed = false;
132:
133: public JavaSourceViewer(Composite parent,
134: IVerticalRuler verticalRuler, IOverviewRuler overviewRuler,
135: boolean showAnnotationsOverview, int styles,
136: IPreferenceStore store) {
137: super (parent, verticalRuler, overviewRuler,
138: 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 map = new HashMap(JavaCore.getOptions());
152: context.setProperty(
153: FormattingContextProperties.CONTEXT_PREFERENCES, map);
154:
155: return context;
156: }
157:
158: /*
159: * @see ITextOperationTarget#doOperation(int)
160: */
161: public void doOperation(int operation) {
162: if (getTextWidget() == null)
163: return;
164:
165: switch (operation) {
166: case SHOW_OUTLINE:
167: if (fOutlinePresenter != null)
168: fOutlinePresenter.showInformation();
169: return;
170: case OPEN_STRUCTURE:
171: if (fStructurePresenter != null)
172: fStructurePresenter.showInformation();
173: return;
174: case SHOW_HIERARCHY:
175: if (fHierarchyPresenter != null)
176: fHierarchyPresenter.showInformation();
177: return;
178: }
179:
180: super .doOperation(operation);
181: }
182:
183: /*
184: * @see ITextOperationTarget#canDoOperation(int)
185: */
186: public boolean canDoOperation(int operation) {
187: if (operation == SHOW_OUTLINE)
188: return fOutlinePresenter != null;
189: if (operation == OPEN_STRUCTURE)
190: return fStructurePresenter != null;
191: if (operation == SHOW_HIERARCHY)
192: return fHierarchyPresenter != null;
193:
194: return super .canDoOperation(operation);
195: }
196:
197: /*
198: * @see ISourceViewer#configure(SourceViewerConfiguration)
199: */
200: public void configure(SourceViewerConfiguration configuration) {
201:
202: /*
203: * Prevent access to colors disposed in unconfigure(), see:
204: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=53641
205: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=86177
206: */
207: StyledText textWidget = getTextWidget();
208: if (textWidget != null && !textWidget.isDisposed()) {
209: Color foregroundColor = textWidget.getForeground();
210: if (foregroundColor != null && foregroundColor.isDisposed())
211: textWidget.setForeground(null);
212: Color backgroundColor = textWidget.getBackground();
213: if (backgroundColor != null && backgroundColor.isDisposed())
214: textWidget.setBackground(null);
215: }
216:
217: super .configure(configuration);
218: if (configuration instanceof JavaSourceViewerConfiguration) {
219: JavaSourceViewerConfiguration javaSVCconfiguration = (JavaSourceViewerConfiguration) configuration;
220: fOutlinePresenter = javaSVCconfiguration
221: .getOutlinePresenter(this , false);
222: if (fOutlinePresenter != null)
223: fOutlinePresenter.install(this );
224:
225: fStructurePresenter = javaSVCconfiguration
226: .getOutlinePresenter(this , true);
227: if (fStructurePresenter != null)
228: fStructurePresenter.install(this );
229:
230: fHierarchyPresenter = javaSVCconfiguration
231: .getHierarchyPresenter(this , true);
232: if (fHierarchyPresenter != null)
233: fHierarchyPresenter.install(this );
234:
235: }
236:
237: if (fPreferenceStore != null) {
238: fPreferenceStore.addPropertyChangeListener(this );
239: initializeViewerColors();
240: }
241:
242: fIsConfigured = true;
243: }
244:
245: protected void initializeViewerColors() {
246: if (fPreferenceStore != null) {
247:
248: StyledText styledText = getTextWidget();
249:
250: // ----------- foreground color --------------------
251: Color color = fPreferenceStore
252: .getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT) ? null
253: : createColor(
254: fPreferenceStore,
255: AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND,
256: styledText.getDisplay());
257: styledText.setForeground(color);
258:
259: if (fForegroundColor != null)
260: fForegroundColor.dispose();
261:
262: fForegroundColor = color;
263:
264: // ---------- background color ----------------------
265: color = fPreferenceStore
266: .getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
267: : createColor(
268: fPreferenceStore,
269: AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND,
270: styledText.getDisplay());
271: styledText.setBackground(color);
272:
273: if (fBackgroundColor != null)
274: fBackgroundColor.dispose();
275:
276: fBackgroundColor = color;
277:
278: // ----------- selection foreground color --------------------
279: color = fPreferenceStore
280: .getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_DEFAULT_COLOR) ? null
281: : createColor(
282: fPreferenceStore,
283: AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_COLOR,
284: styledText.getDisplay());
285: styledText.setSelectionForeground(color);
286:
287: if (fSelectionForegroundColor != null)
288: fSelectionForegroundColor.dispose();
289:
290: fSelectionForegroundColor = color;
291:
292: // ---------- selection background color ----------------------
293: color = fPreferenceStore
294: .getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_DEFAULT_COLOR) ? null
295: : createColor(
296: fPreferenceStore,
297: AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_COLOR,
298: styledText.getDisplay());
299: styledText.setSelectionBackground(color);
300:
301: if (fSelectionBackgroundColor != null)
302: fSelectionBackgroundColor.dispose();
303:
304: fSelectionBackgroundColor = color;
305: }
306: }
307:
308: /**
309: * Creates a color from the information stored in the given preference store.
310: * Returns <code>null</code> if there is no such information available.
311: *
312: * @param store the store to read from
313: * @param key the key used for the lookup in the preference store
314: * @param display the display used create the color
315: * @return the created color according to the specification in the preference store
316: * @since 3.0
317: */
318: private Color createColor(IPreferenceStore store, String key,
319: Display display) {
320:
321: RGB rgb = null;
322:
323: if (store.contains(key)) {
324:
325: if (store.isDefault(key))
326: rgb = PreferenceConverter.getDefaultColor(store, key);
327: else
328: rgb = PreferenceConverter.getColor(store, key);
329:
330: if (rgb != null)
331: return new Color(display, rgb);
332: }
333:
334: return null;
335: }
336:
337: /*
338: * @see org.eclipse.jface.text.source.ISourceViewerExtension2#unconfigure()
339: * @since 3.0
340: */
341: public void unconfigure() {
342: if (fOutlinePresenter != null) {
343: fOutlinePresenter.uninstall();
344: fOutlinePresenter = null;
345: }
346: if (fStructurePresenter != null) {
347: fStructurePresenter.uninstall();
348: fStructurePresenter = null;
349: }
350: if (fHierarchyPresenter != null) {
351: fHierarchyPresenter.uninstall();
352: fHierarchyPresenter = null;
353: }
354: if (fForegroundColor != null) {
355: fForegroundColor.dispose();
356: fForegroundColor = null;
357: }
358: if (fBackgroundColor != null) {
359: fBackgroundColor.dispose();
360: fBackgroundColor = null;
361: }
362:
363: if (fPreferenceStore != null)
364: fPreferenceStore.removePropertyChangeListener(this );
365:
366: super .unconfigure();
367:
368: fIsConfigured = false;
369: }
370:
371: /*
372: * @see org.eclipse.jface.text.source.SourceViewer#rememberSelection()
373: */
374: public Point rememberSelection() {
375: return super .rememberSelection();
376: }
377:
378: /*
379: * @see org.eclipse.jface.text.source.SourceViewer#restoreSelection()
380: */
381: public void restoreSelection() {
382: super .restoreSelection();
383: }
384:
385: /*
386: * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
387: */
388: public void propertyChange(PropertyChangeEvent event) {
389: String property = event.getProperty();
390: if (AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND
391: .equals(property)
392: || AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT
393: .equals(property)
394: || AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND
395: .equals(property)
396: || AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT
397: .equals(property)
398: || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_COLOR
399: .equals(property)
400: || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_DEFAULT_COLOR
401: .equals(property)
402: || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_COLOR
403: .equals(property)
404: || AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_DEFAULT_COLOR
405: .equals(property)) {
406: initializeViewerColors();
407: }
408: }
409:
410: /**
411: * Sets the preference store on this viewer.
412: *
413: * @param store the preference store
414: *
415: * @since 3.0
416: */
417: public void setPreferenceStore(IPreferenceStore store) {
418: if (fIsConfigured && fPreferenceStore != null)
419: fPreferenceStore.removePropertyChangeListener(this );
420:
421: fPreferenceStore = store;
422:
423: if (fIsConfigured && fPreferenceStore != null) {
424: fPreferenceStore.addPropertyChangeListener(this );
425: initializeViewerColors();
426: }
427: }
428:
429: /*
430: * @see org.eclipse.jface.text.source.SourceViewer#createControl(org.eclipse.swt.widgets.Composite, int)
431: */
432: protected void createControl(Composite parent, int styles) {
433:
434: // Use LEFT_TO_RIGHT unless otherwise specified.
435: if ((styles & SWT.RIGHT_TO_LEFT) == 0
436: && (styles & SWT.LEFT_TO_RIGHT) == 0)
437: styles |= SWT.LEFT_TO_RIGHT;
438:
439: super .createControl(parent, styles);
440:
441: fBackspaceManager = new SmartBackspaceManager();
442: fBackspaceManager.install(this );
443:
444: StyledText text = getTextWidget();
445: text.addBidiSegmentListener(new BidiSegmentListener() {
446: public void lineGetSegments(BidiSegmentEvent event) {
447: if (redraws())
448: event.segments = getBidiLineSegments(
449: event.lineOffset, event.lineText);
450: }
451: });
452: }
453:
454: /**
455: * Returns the backspace manager for this viewer.
456: *
457: * @return the backspace manager for this viewer, or <code>null</code> if
458: * there is none
459: * @since 3.0
460: */
461: public SmartBackspaceManager getBackspaceManager() {
462: return fBackspaceManager;
463: }
464:
465: /*
466: * @see org.eclipse.jface.text.source.SourceViewer#handleDispose()
467: */
468: protected void handleDispose() {
469: if (fBackspaceManager != null) {
470: fBackspaceManager.uninstall();
471: fBackspaceManager = null;
472: }
473:
474: super .handleDispose();
475: }
476:
477: /**
478: * Prepends the text presentation listener at the beginning of the viewer's
479: * list of text presentation listeners. If the listener is already registered
480: * with the viewer this call moves the listener to the beginning of
481: * the list.
482: *
483: * @param listener the text presentation listener
484: * @since 3.0
485: */
486: public void prependTextPresentationListener(
487: ITextPresentationListener listener) {
488:
489: Assert.isNotNull(listener);
490:
491: if (fTextPresentationListeners == null)
492: fTextPresentationListeners = new ArrayList();
493:
494: fTextPresentationListeners.remove(listener);
495: fTextPresentationListeners.add(0, listener);
496: }
497:
498: /**
499: * Sets the given reconciler.
500: *
501: * @param reconciler the reconciler
502: * @since 3.0
503: */
504: void setReconciler(IReconciler reconciler) {
505: fReconciler = reconciler;
506: }
507:
508: /**
509: * Returns the reconciler.
510: *
511: * @return the reconciler or <code>null</code> if not set
512: * @since 3.0
513: */
514: IReconciler getReconciler() {
515: return fReconciler;
516: }
517:
518: /**
519: * Returns a segmentation of the given line appropriate for BIDI rendering. The default
520: * implementation returns only the string literals of a java code line as segments.
521: *
522: * @param widgetLineOffset the offset of the line
523: * @param line the content of the line
524: * @return the line's BIDI segmentation
525: */
526: protected int[] getBidiLineSegments(int widgetLineOffset,
527: String line) {
528: if (line != null && line.length() > 0) {
529: int lineOffset = widgetOffset2ModelOffset(widgetLineOffset);
530: try {
531: return getBidiLineSegments(getDocument(), lineOffset);
532: } catch (BadLocationException x) {
533: return null; // don't segment line in this case
534: }
535: }
536: return null;
537: }
538:
539: /**
540: * Returns a segmentation of the line of the given document appropriate for
541: * BIDI rendering. The default implementation returns only the string literals of a java code
542: * line as segments.
543: *
544: * @param document the document
545: * @param lineOffset the offset of the line
546: * @return the line's BIDI segmentation
547: * @throws BadLocationException in case lineOffset is not valid in document
548: */
549: protected static int[] getBidiLineSegments(IDocument document,
550: int lineOffset) throws BadLocationException {
551:
552: if (document == null)
553: return null;
554:
555: IRegion line = document.getLineInformationOfOffset(lineOffset);
556: ITypedRegion[] linePartitioning = TextUtilities
557: .computePartitioning(document,
558: IJavaPartitions.JAVA_PARTITIONING, lineOffset,
559: line.getLength(), false);
560:
561: List segmentation = new ArrayList();
562: for (int i = 0; i < linePartitioning.length; i++) {
563: if (IJavaPartitions.JAVA_STRING.equals(linePartitioning[i]
564: .getType()))
565: segmentation.add(linePartitioning[i]);
566: }
567:
568: if (segmentation.size() == 0)
569: return null;
570:
571: int size = segmentation.size();
572: int[] segments = new int[size * 2 + 1];
573:
574: int j = 0;
575: for (int i = 0; i < size; i++) {
576: ITypedRegion segment = (ITypedRegion) segmentation.get(i);
577:
578: if (i == 0)
579: segments[j++] = 0;
580:
581: int offset = segment.getOffset() - lineOffset;
582: if (offset > segments[j - 1])
583: segments[j++] = offset;
584:
585: if (offset + segment.getLength() >= line.getLength())
586: break;
587:
588: segments[j++] = offset + segment.getLength();
589: }
590:
591: if (j < segments.length) {
592: int[] result = new int[j];
593: System.arraycopy(segments, 0, result, 0, j);
594: segments = result;
595: }
596:
597: return segments;
598: }
599:
600: /**
601: * Delays setting the visual document until after the projection has been computed.
602: * This method must only be called before the document is set on the viewer.
603: * <p>
604: * This is a performance optimization to reduce the computation of
605: * the text presentation triggered by <code>setVisibleDocument(IDocument)</code>.
606: * </p>
607: *
608: * @see #setVisibleDocument(IDocument)
609: * @since 3.1
610: */
611: void prepareDelayedProjection() {
612: Assert.isTrue(!fIsSetVisibleDocumentDelayed);
613: fIsSetVisibleDocumentDelayed = true;
614: }
615:
616: /**
617: * {@inheritDoc}
618: * <p>
619: * This is a performance optimization to reduce the computation of
620: * the text presentation triggered by {@link #setVisibleDocument(IDocument)}
621: * </p>
622: * @see #prepareDelayedProjection()
623: * @since 3.1
624: */
625: protected void setVisibleDocument(IDocument document) {
626: if (fIsSetVisibleDocumentDelayed) {
627: fIsSetVisibleDocumentDelayed = false;
628: IDocument previous = getVisibleDocument();
629: enableProjection(); // will set the visible document if anything is folded
630: IDocument current = getVisibleDocument();
631: // if the visible document was not replaced, continue as usual
632: if (current != null && current != previous)
633: return;
634: }
635:
636: super .setVisibleDocument(document);
637: }
638:
639: /**
640: * {@inheritDoc}
641: * <p>
642: * Performance optimization: since we know at this place
643: * that none of the clients expects the given range to be
644: * untouched we reuse the given range as return value.
645: * </p>
646: */
647: protected StyleRange modelStyleRange2WidgetStyleRange(
648: StyleRange range) {
649: IRegion region = modelRange2WidgetRange(new Region(range.start,
650: range.length));
651: if (region != null) {
652: // don't clone the style range, but simply reuse it.
653: range.start = region.getOffset();
654: range.length = region.getLength();
655: return range;
656: }
657: return null;
658: }
659: }
|