/*******************************************************************************
* 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;
}
}