/******************************************************************************* * Copyright (c) 2007, 2011 David Green and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * David Green - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.wikitext.ui.viewer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; import org.eclipse.jface.text.presentation.IPresentationDamager; import org.eclipse.jface.text.presentation.IPresentationReconciler; import org.eclipse.jface.text.presentation.IPresentationRepairer; import org.eclipse.jface.text.presentation.PresentationReconciler; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.DefaultAnnotationHover; import org.eclipse.jface.text.source.IAnnotationHover; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.mylyn.internal.wikitext.ui.WikiTextUiPlugin; import org.eclipse.mylyn.internal.wikitext.ui.viewer.AnnotationHyperlinkDetector; import org.eclipse.mylyn.internal.wikitext.ui.viewer.TextHover; import org.eclipse.mylyn.wikitext.ui.annotation.TitleAnnotation; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.TextEditor; /** * A configuration for use with a {@link HtmlViewer}. * * @author David Green * @since 1.0 */ public class HtmlViewerConfiguration extends AbstractTextSourceViewerConfiguration { private final HtmlViewer viewer; private TextPresentation textPresentation; private boolean disableHyperlinkModifiers = true; private boolean enableSelfContainedIncrementalFind = false; public HtmlViewerConfiguration(HtmlViewer viewer) { super(getDefaultPreferenceStore()); this.viewer = viewer; // filters the platform URL hyperlink detector since the URL hyperlink detection // strategy is defined by the HTML markup. addHyperlinkDetectorDescriptorFilter(new DefaultHyperlinkDetectorDescriptorFilter( "org.eclipse.ui.internal.editors.text.URLHyperlinkDetector")); //$NON-NLS-1$ } private static IPreferenceStore getDefaultPreferenceStore() { // bug# 245759: must work outside of an Eclipse runtime return WikiTextUiPlugin.getDefault() == null ? null : EditorsUI.getPreferenceStore(); } public HtmlViewerConfiguration(HtmlViewer viewer, IPreferenceStore preferenceStore) { super(preferenceStore); this.viewer = viewer; } @Override public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { PresentationReconciler reconciler = new PresentationReconciler(); reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); MarkupViewerDamagerRepairer dr = createMarkupViewerDamagerRepairer(); reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); return reconciler; } /** * returns null, since spell check is not needed * * @since 1.1 */ @Override public IReconciler getReconciler(ISourceViewer sourceViewer) { // no need for a reconciler: no spell check or anything else that can use one return null; } private MarkupViewerDamagerRepairer createMarkupViewerDamagerRepairer() { return new MarkupViewerDamagerRepairer(); } @Override public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) { return new DefaultAnnotationHover() { @Override protected boolean isIncluded(Annotation annotation) { return annotation.getType().equals(TitleAnnotation.TYPE) || isShowInVerticalRuler(annotation); } }; } @Override public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { return new TextHover(sourceViewer); } @Override protected List<IHyperlinkDetector> createCustomHyperlinkDetectors(ISourceViewer sourceViewer) { List<IHyperlinkDetector> detectors = new ArrayList<>(1); AnnotationHyperlinkDetector annotationHyperlinkDetector = createAnnotationHyperlinkDetector(); sourceViewer.getTextWidget().setData(AnnotationHyperlinkDetector.class.getName(), annotationHyperlinkDetector); detectors.add(annotationHyperlinkDetector); return detectors; } /** * @noreference This method is not intended to be referenced by clients. * @since 1.1 */ protected AnnotationHyperlinkDetector createAnnotationHyperlinkDetector() { return new AnnotationHyperlinkDetector(); } protected class MarkupViewerDamagerRepairer implements IPresentationDamager, IPresentationRepairer { private IDocument document; public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, boolean documentPartitioningChanged) { return new Region(0, document.getLength()); } public void setDocument(IDocument document) { this.document = document; } public void createPresentation(TextPresentation presentation, ITypedRegion damage) { TextPresentation viewerPresentation = textPresentation == null ? viewer.getTextPresentation() : textPresentation; presentation.clear(); if (viewerPresentation == null) { return; } StyleRange defaultStyleRange = viewerPresentation.getDefaultStyleRange(); presentation .setDefaultStyleRange((StyleRange) (defaultStyleRange == null ? null : defaultStyleRange.clone())); List<StyleRange> ranges = new ArrayList<>(); Iterator<?> allStyleRangeIterator = viewerPresentation.getAllStyleRangeIterator(); while (allStyleRangeIterator.hasNext()) { StyleRange range = (StyleRange) allStyleRangeIterator.next(); ranges.add((StyleRange) range.clone()); } // fix for bug 237170: presentation must not have any gaps otherwise the hyperlinks won't always get reset // to their non-highlighted state. So detect gaps and fill the gaps with the default presentation style. int start = damage.getOffset(); for (int x = 0; x < ranges.size(); ++x) { StyleRange range = ranges.get(x); if (range.start > start) { StyleRange newRange = defaultStyleRange == null ? new StyleRange() : (StyleRange) defaultStyleRange.clone(); newRange.start = start; newRange.length = range.start - start; ranges.add(++x, newRange); } start = range.start + range.length + 1; } if (start < (damage.getOffset() + damage.getLength())) { StyleRange newRange = defaultStyleRange == null ? new StyleRange() : (StyleRange) defaultStyleRange.clone(); newRange.start = start; newRange.length = (damage.getOffset() + damage.getLength()) - start; ranges.add(newRange); } presentation.replaceStyleRanges(ranges.toArray(new StyleRange[ranges.size()])); } } @SuppressWarnings("unchecked") public void setTextPresentation(TextPresentation textPresentation) { if (textPresentation != null) { TextPresentation textPresentationCopy = new TextPresentation(); textPresentationCopy.setDefaultStyleRange((StyleRange) (textPresentation.getDefaultStyleRange() == null ? null : textPresentation.getDefaultStyleRange().clone())); Iterator<StyleRange> iterator = textPresentation.getAllStyleRangeIterator(); while (iterator.hasNext()) { StyleRange styleRange = iterator.next(); textPresentationCopy.addStyleRange((StyleRange) styleRange.clone()); } textPresentation = textPresentationCopy; } this.textPresentation = textPresentation; } @Override public int getHyperlinkStateMask(ISourceViewer sourceViewer) { if (disableHyperlinkModifiers) { return SWT.NONE; } return super.getHyperlinkStateMask(sourceViewer); } /** * Indicate if hyperlink modifiers are disabled. When disabled (the default) no keyboard modifiers are required to * activate hyperlinks when clicking. * * @see #getHyperlinkStateMask(ISourceViewer) */ public boolean isDisableHyperlinkModifiers() { return disableHyperlinkModifiers; } /** * Indicate if hyperlink modifiers are disabled. When disabled (the default) no keyboard modifiers are required to * activate hyperlinks when clicking. * * @see #getHyperlinkStateMask(ISourceViewer) */ public void setDisableHyperlinkModifiers(boolean disableHyperlinkModifiers) { this.disableHyperlinkModifiers = disableHyperlinkModifiers; } /** * Indicate if incremental find should be supported in a self-contained manner. For use when SourceViewer is not * used in a {@link TextEditor}. Defaults to false. * * @since 1.6 */ public boolean isEnableSelfContainedIncrementalFind() { return enableSelfContainedIncrementalFind; } /** * Indicate if incremental find should be supported in a self-contained manner. For use when SourceViewer is not * used in a {@link TextEditor}. * * @since 1.6 */ public void setEnableSelfContainedIncrementalFind(boolean enableSelfContainedIncrementalFind) { this.enableSelfContainedIncrementalFind = enableSelfContainedIncrementalFind; } }