org.eclipse.jface.text.hyperlink.HyperlinkManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.jface.text Show documentation
Show all versions of org.eclipse.jface.text Show documentation
This is org.eclipse.jface.text jar used by Scout SDK
The newest version!
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Steffen Pingel (Tasktop Technologies Inc.) - [navigation] hyperlink decoration is not erased when mouse is moved out of Text widget - https://bugs.eclipse.org/bugs/show_bug.cgi?id=100278
*******************************************************************************/
package org.eclipse.jface.text.hyperlink;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.JFaceTextUtil;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.ISourceViewer;
/**
* Default implementation of a hyperlink manager.
*
* @since 3.1
*/
public class HyperlinkManager implements ITextListener, Listener, KeyListener, MouseListener, MouseMoveListener, FocusListener, MouseTrackListener {
/**
* Text operation code for requesting to open the hyperlink at the caret position.
* @see #openHyperlink()
* @since 3.6
*/
public static final int OPEN_HYPERLINK= ISourceViewer.QUICK_ASSIST + 1;
/**
* Detection strategy.
*/
public static final class DETECTION_STRATEGY {
String fName;
private DETECTION_STRATEGY(String name) {
fName= name;
}
@Override
public String toString() {
return fName;
}
}
/**
* The first detected hyperlink is passed to the
* hyperlink presenter and no further detector
* is consulted.
*/
public static final DETECTION_STRATEGY FIRST= new DETECTION_STRATEGY("first"); //$NON-NLS-1$
/**
* All detected hyperlinks from all detectors are collected
* and passed to the hyperlink presenter.
*
* This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()}
* returns true
.
*
*/
public static final DETECTION_STRATEGY ALL= new DETECTION_STRATEGY("all"); //$NON-NLS-1$
/**
* All detected hyperlinks from all detectors are collected
* and all those with the longest region are passed to the
* hyperlink presenter.
*
* This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()}
* returns true
.
*
*/
public static final DETECTION_STRATEGY LONGEST_REGION_ALL= new DETECTION_STRATEGY("all with same longest region"); //$NON-NLS-1$
/**
* All detected hyperlinks from all detectors are collected
* and form all those with the longest region only the first
* one is passed to the hyperlink presenter.
*/
public static final DETECTION_STRATEGY LONGEST_REGION_FIRST= new DETECTION_STRATEGY("first with longest region"); //$NON-NLS-1$
/** The text viewer on which this hyperlink manager works. */
private ITextViewer fTextViewer;
/** The session is active. */
private boolean fActive;
/** The key modifier mask of the default hyperlink modifier. */
private int fHyperlinkStateMask;
/**
* The active key modifier mask.
* @since 3.3
*/
private int fActiveHyperlinkStateMask;
/** The active hyperlinks. */
private IHyperlink[] fActiveHyperlinks;
/** The hyperlink detectors. */
private IHyperlinkDetector[] fHyperlinkDetectors;
/** The hyperlink presenter. */
private IHyperlinkPresenter fHyperlinkPresenter;
/** The detection strategy. */
private final DETECTION_STRATEGY fDetectionStrategy;
/**
* Creates a new hyperlink manager.
*
* @param detectionStrategy the detection strategy one of {{@link #ALL}, {@link #FIRST}, {@link #LONGEST_REGION_ALL}, {@link #LONGEST_REGION_FIRST}}
*/
public HyperlinkManager(DETECTION_STRATEGY detectionStrategy) {
Assert.isNotNull(detectionStrategy);
fDetectionStrategy= detectionStrategy;
}
/**
* Installs this hyperlink manager with the given arguments.
*
* @param textViewer the text viewer
* @param hyperlinkPresenter the hyperlink presenter
* @param hyperlinkDetectors the array of hyperlink detectors, must not be empty
* @param eventStateMask the SWT event state mask to activate hyperlink mode
*/
public void install(ITextViewer textViewer, IHyperlinkPresenter hyperlinkPresenter, IHyperlinkDetector[] hyperlinkDetectors, int eventStateMask) {
Assert.isNotNull(textViewer);
Assert.isNotNull(hyperlinkPresenter);
fTextViewer= textViewer;
fHyperlinkPresenter= hyperlinkPresenter;
Assert.isLegal(fHyperlinkPresenter.canShowMultipleHyperlinks() || fDetectionStrategy == FIRST || fDetectionStrategy == LONGEST_REGION_FIRST);
setHyperlinkDetectors(hyperlinkDetectors);
setHyperlinkStateMask(eventStateMask);
StyledText text= fTextViewer.getTextWidget();
if (text == null || text.isDisposed())
return;
text.getDisplay().addFilter(SWT.KeyUp, this);
text.addKeyListener(this);
text.addMouseListener(this);
text.addMouseMoveListener(this);
text.addFocusListener(this);
text.addMouseTrackListener(this);
fTextViewer.addTextListener(this);
fHyperlinkPresenter.install(fTextViewer);
}
/**
* Sets the hyperlink detectors for this hyperlink manager.
*
* It is allowed to call this method after this
* hyperlink manger has been installed.
*
*
* @param hyperlinkDetectors and array of hyperlink detectors, must not be empty
*/
public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors) {
Assert.isTrue(hyperlinkDetectors != null && hyperlinkDetectors.length > 0);
if (fHyperlinkDetectors == null)
fHyperlinkDetectors= hyperlinkDetectors;
else {
synchronized (fHyperlinkDetectors) {
fHyperlinkDetectors= hyperlinkDetectors;
}
}
}
/**
* Sets the SWT event state mask which in combination
* with the left mouse button triggers the hyperlink mode.
*
* It is allowed to call this method after this
* hyperlink manger has been installed.
*
*
* Note that {@link IHyperlinkDetectorExtension2}s may specify additional state masks.
*
*
* @param eventStateMask the SWT event state mask to activate hyperlink mode
*/
public void setHyperlinkStateMask(int eventStateMask) {
fHyperlinkStateMask= eventStateMask;
}
/**
* Uninstalls this hyperlink manager.
*/
public void uninstall() {
deactivate();
StyledText text= fTextViewer.getTextWidget();
if (text != null && !text.isDisposed()) {
text.removeKeyListener(this);
text.getDisplay().removeFilter(SWT.KeyUp, this);
text.removeMouseListener(this);
text.removeMouseMoveListener(this);
text.removeFocusListener(this);
text.removeMouseTrackListener(this);
}
fTextViewer.removeTextListener(this);
fHyperlinkPresenter.uninstall();
fHyperlinkPresenter= null;
fTextViewer= null;
fHyperlinkDetectors= null;
}
/**
* Deactivates the currently shown hyperlinks.
*/
protected void deactivate() {
fHyperlinkPresenter.hideHyperlinks();
fActive= false;
}
/**
* Finds hyperlinks at the current offset.
*
* @return the hyperlinks or null
if none.
*/
protected IHyperlink[] findHyperlinks() {
int offset= getCurrentTextOffset();
if (offset == -1)
return null;
IRegion region= new Region(offset, 0);
return findHyperlinks(region);
}
/**
* Returns the hyperlinks in the given region or null
if none.
*
* @param region the selection region
* @return the array of hyperlinks found or null
if none
* @since 3.7
*/
private IHyperlink[] findHyperlinks(IRegion region) {
List allHyperlinks= new ArrayList<>(fHyperlinkDetectors.length * 2);
synchronized (fHyperlinkDetectors) {
for (int i= 0, length= fHyperlinkDetectors.length; i < length; i++) {
IHyperlinkDetector detector= fHyperlinkDetectors[i];
if (detector == null)
continue;
if (detector instanceof IHyperlinkDetectorExtension2) {
int stateMask= ((IHyperlinkDetectorExtension2)detector).getStateMask();
if (stateMask != -1 && stateMask != fActiveHyperlinkStateMask)
continue;
else if (stateMask == -1 && fActiveHyperlinkStateMask != fHyperlinkStateMask)
continue;
} else if (fActiveHyperlinkStateMask != fHyperlinkStateMask)
continue;
boolean canShowMultipleHyperlinks= fHyperlinkPresenter.canShowMultipleHyperlinks();
IHyperlink[] hyperlinks= detector.detectHyperlinks(fTextViewer, region, canShowMultipleHyperlinks);
if (hyperlinks == null)
continue;
Assert.isLegal(hyperlinks.length > 0);
if (fDetectionStrategy == FIRST) {
if (hyperlinks.length == 1)
return hyperlinks;
return new IHyperlink[] {hyperlinks[0]};
}
allHyperlinks.addAll(Arrays.asList(hyperlinks));
}
}
if (allHyperlinks.isEmpty())
return null;
if (fDetectionStrategy != ALL) {
int maxLength= computeLongestHyperlinkLength(allHyperlinks);
Iterator iter= new ArrayList<>(allHyperlinks).iterator();
while (iter.hasNext()) {
IHyperlink hyperlink= iter.next();
if (hyperlink.getHyperlinkRegion().getLength() < maxLength)
allHyperlinks.remove(hyperlink);
}
}
if (fDetectionStrategy == LONGEST_REGION_FIRST)
return new IHyperlink[] {allHyperlinks.get(0)};
return allHyperlinks.toArray(new IHyperlink[allHyperlinks.size()]);
}
/**
* Computes the length of the longest detected hyperlink.
*
* @param hyperlinks the list of hyperlinks
* @return the length of the longest detected
*/
protected int computeLongestHyperlinkLength(List extends IHyperlink> hyperlinks) {
Assert.isLegal(hyperlinks != null && !hyperlinks.isEmpty());
Iterator extends IHyperlink> iter= hyperlinks.iterator();
int length= Integer.MIN_VALUE;
while (iter.hasNext()) {
IRegion region= iter.next().getHyperlinkRegion();
if (region.getLength() < length)
continue;
length= region.getLength();
}
return length;
}
/**
* Returns the offset in the given viewer that corresponds to the current cursor location.
*
* @return the offset in the given viewer that corresponds to the current cursor location.
*/
protected int getCurrentTextOffset() {
return JFaceTextUtil.getOffsetForCursorLocation(fTextViewer);
}
@Override
public void keyPressed(KeyEvent event) {
if (!isRegisteredStateMask((event.keyCode | event.stateMask) & SWT.MODIFIER_MASK)) {
deactivate();
return;
}
fActive= true;
// do not show hyperlink, since that would often be confusing (e.g. when pressing Ctrl+C)
}
@Override
public void keyReleased(KeyEvent event) {
}
@Override
public void mouseDoubleClick(MouseEvent e) {
}
@Override
public void mouseDown(MouseEvent event) {
if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension) {
if (!((IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks())
return;
}
if (!isRegisteredStateMask(event.stateMask)) {
if (fActive)
deactivate();
return;
}
if (event.button != 1) {
deactivate();
return;
}
fActive= true;
fActiveHyperlinkStateMask= event.stateMask & SWT.MODIFIER_MASK;
StyledText text= fTextViewer.getTextWidget();
if (text == null || text.isDisposed()) {
deactivate();
return;
}
if (text.isTextSelected()) {
deactivate();
return;
}
fActiveHyperlinks= findHyperlinks();
showHyperlinks(false);
}
@Override
public void mouseUp(MouseEvent e) {
if (!fActive) {
fActiveHyperlinks= null;
return;
}
if (e.button != 1)
fActiveHyperlinks= null;
deactivate();
if (fActiveHyperlinks != null)
fActiveHyperlinks[0].open();
}
@Override
public void mouseMove(MouseEvent event) {
if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension) {
if (!((IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks())
return;
}
if (!isRegisteredStateMask(event.stateMask)) {
if (fActive)
deactivate();
return;
}
fActive= true;
fActiveHyperlinkStateMask= event.stateMask;
StyledText text= fTextViewer.getTextWidget();
if (text == null || text.isDisposed()) {
deactivate();
return;
}
if ((event.stateMask & SWT.BUTTON1) != 0 && text.isTextSelected()) {
deactivate();
return;
}
fActiveHyperlinks= findHyperlinks();
showHyperlinks(false);
}
/**
* Checks whether the given state mask is registered.
*
* @param stateMask the state mask
* @return true
if a detector is registered for the given state mask
* @since 3.3
*/
private boolean isRegisteredStateMask(int stateMask) {
if (stateMask == fHyperlinkStateMask)
return true;
synchronized (fHyperlinkDetectors) {
for (int i= 0; i < fHyperlinkDetectors.length; i++) {
if (fHyperlinkDetectors[i] instanceof IHyperlinkDetectorExtension2) {
if (stateMask == ((IHyperlinkDetectorExtension2)fHyperlinkDetectors[i]).getStateMask())
return true;
}
}
}
return false;
}
@Override
public void focusGained(FocusEvent e) {}
@Override
public void focusLost(FocusEvent event) {
deactivate();
}
@Override
public void handleEvent(Event event) {
//key up
deactivate();
}
@Override
public void textChanged(TextEvent event) {
if (event.getDocumentEvent() != null)
deactivate();
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public void mouseExit(MouseEvent e) {
if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension) {
if (!((IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks())
return;
}
deactivate();
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public void mouseEnter(MouseEvent e) {
}
/**
* {@inheritDoc}
*
* @since 3.4
*/
@Override
public void mouseHover(MouseEvent e) {
}
/**
* Opens the hyperlink at the current caret location directly if there's only one link, else
* opens the hyperlink control showing all the hyperlinks at that location.
*
* @param takesFocusWhenVisible true
if the control takes focus when visible,
* false
otherwise
*
* @return true
if at least one hyperlink has been found at the caret location,
* false
otherwise
* @since 3.7
*/
private boolean showHyperlinks(boolean takesFocusWhenVisible) {
if (fActiveHyperlinks == null || fActiveHyperlinks.length == 0) {
fHyperlinkPresenter.hideHyperlinks();
return false;
}
if (fActiveHyperlinks.length == 1 && takesFocusWhenVisible) {
fActiveHyperlinks[0].open();
} else {
if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension2)
((IHyperlinkPresenterExtension2)fHyperlinkPresenter).showHyperlinks(fActiveHyperlinks, takesFocusWhenVisible);
else
fHyperlinkPresenter.showHyperlinks(fActiveHyperlinks);
}
return true;
}
/**
* Opens the hyperlink at the caret location or opens a chooser
* if more than one hyperlink is available.
*
* @return true
if at least one hyperlink has been found at the caret location, false
otherwise
* @see #OPEN_HYPERLINK
* @since 3.6
*/
public boolean openHyperlink() {
fActiveHyperlinkStateMask= fHyperlinkStateMask;
if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension) {
if (!((IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks())
return false;
}
ITextSelection sel= (ITextSelection)((TextViewer)fTextViewer).getSelection();
int offset= sel.getOffset();
if (offset == -1)
return false;
IRegion region= new Region(offset, 0);
fActiveHyperlinks= findHyperlinks(region);
return showHyperlinks(true);
}
}