blob: c2275bf4b3f2b7a0774baaf788ccad019f9ae34f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2012 Tasktop Technologies and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.ui.editors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextPresentationListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension4;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.mylyn.tasks.ui.IHighlightingHyperlink;
import org.eclipse.mylyn.tasks.ui.TaskHyperlink;
import org.eclipse.swt.custom.StyleRange;
/**
* A manager that ensures that all task hyperlinks have the appropriate text presentation. Subclasses may specify logic
* for filtering detected hyperlinks and text decoration.
*
* @author David Green
*/
public abstract class AbstractHyperlinkTextPresentationManager {
/**
* Regions with a lower offset and a shorter length are ordered before other regions.
*/
public static class RegionComparator implements Comparator<IRegion> {
public int compare(IRegion o1, IRegion o2) {
if (o1 == o2) {
return 0;
}
if (o1.getOffset() < o2.getOffset()) {
return -1;
} else if (o1.getOffset() > o2.getOffset()) {
return 1;
} else if (o1.getLength() < o2.getLength()) {
return -1;
} else if (o1.getLength() > o2.getLength()) {
return 1;
} else {
return 0;
}
}
}
private class Support implements ITextPresentationListener {
public void applyTextPresentation(TextPresentation textPresentation) {
StyleRange[] styleRanges = computeStyleRanges(textPresentation.getCoverage());
if (styleRanges != null && styleRanges.length > 0) {
textPresentation.mergeStyleRanges(styleRanges);
}
}
}
private static final RegionComparator REGION_COMPARATOR = new RegionComparator();
private IHyperlinkDetector[] hyperlinkDetectors;
private final ITextPresentationListener textPresentationListener = new Support();
private ITextViewer viewer;
public AbstractHyperlinkTextPresentationManager() {
}
private void addRange(List<StyleRange> ranges, int start, int end) {
// the style range is to be merged with other styles, so only set the bits that are needed
StyleRange styleRange = new StyleRange(start, end - start, null, null);
decorate(styleRange);
ranges.add(styleRange);
}
protected StyleRange[] computeStyleRanges(IRegion extent) {
if (viewer == null || hyperlinkDetectors == null || viewer.getDocument() == null || extent == null) {
return null;
}
List<IRegion> regions = getRegions(extent);
if (regions != null) {
// style ranges may be adjacent but must not overlap, and they must be in order
// of increasing offset.
List<StyleRange> ranges = new ArrayList<StyleRange>(regions.size());
// sort them first to ensure increasing offset
Collections.sort(regions, REGION_COMPARATOR);
// now merge overlapping (and adjacent) ranges
int start = -1;
int end = -1;
for (int x = 0; x < regions.size(); ++x) {
IRegion region = regions.get(x);
if (start == -1) {
start = region.getOffset();
end = region.getOffset() + region.getLength();
} else {
if (region.getOffset() >= end) {
addRange(ranges, start, end);
start = region.getOffset();
end = region.getOffset() + region.getLength();
} else {
end = region.getOffset() + region.getLength();
}
}
}
if (start != -1) {
addRange(ranges, start, end);
}
return ranges.toArray(new StyleRange[ranges.size()]);
}
return null;
}
protected abstract void decorate(StyleRange styleRange);
/**
* The hyperlink detectors that are used to detect {@link TaskHyperlink task hyperlinks}
*/
public IHyperlinkDetector[] getHyperlinkDetector() {
return hyperlinkDetectors;
}
protected List<IRegion> getRegions(IRegion extent) {
List<IRegion> regions = null;
for (IHyperlinkDetector hyperlinkDetector : hyperlinkDetectors) {
IHyperlink[] hyperlinks = hyperlinkDetector.detectHyperlinks(viewer, extent, true);
if (hyperlinks != null && hyperlinks.length > 0) {
for (IHyperlink hyperlink : hyperlinks) {
if (select(hyperlink)) {
if (regions == null) {
regions = new ArrayList<IRegion>();
}
regions.add(hyperlink instanceof IHighlightingHyperlink
? ((IHighlightingHyperlink) hyperlink).getHighlightingRegion()
: hyperlink.getHyperlinkRegion());
}
}
}
}
return regions;
}
public void install(ITextViewer viewer) {
this.viewer = viewer;
if (viewer instanceof ITextViewerExtension4) {
((ITextViewerExtension4) viewer).addTextPresentationListener(textPresentationListener);
}
}
public boolean select(IHyperlink hyperlink) {
return true;
}
/**
* The hyperlink detector that are used to detect {@link TaskHyperlink task hyperlinks}
*/
public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors) {
this.hyperlinkDetectors = hyperlinkDetectors;
}
public void uninstall() {
((ITextViewerExtension4) viewer).removeTextPresentationListener(textPresentationListener);
this.viewer = null;
}
}