Mercurial > projects > dwt-addons
diff dwtx/jface/text/hyperlink/HyperlinkManager.d @ 129:eb30df5ca28b
Added JFace Text sources
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 23 Aug 2008 19:10:48 +0200 |
parents | |
children | c4fb132a086c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/text/hyperlink/HyperlinkManager.d Sat Aug 23 19:10:48 2008 +0200 @@ -0,0 +1,559 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 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 <steffen.pingel@tasktop.com> (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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.text.hyperlink.HyperlinkManager; + +import dwt.dwthelper.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import dwt.DWT; +import dwt.custom.StyledText; +import dwt.events.FocusEvent; +import dwt.events.FocusListener; +import dwt.events.KeyEvent; +import dwt.events.KeyListener; +import dwt.events.MouseEvent; +import dwt.events.MouseListener; +import dwt.events.MouseMoveListener; +import dwt.events.MouseTrackListener; +import dwt.graphics.Point; +import dwt.widgets.Display; +import dwt.widgets.Event; +import dwt.widgets.Listener; +import dwtx.core.runtime.Assert; +import dwtx.jface.text.IRegion; +import dwtx.jface.text.ITextListener; +import dwtx.jface.text.ITextViewer; +import dwtx.jface.text.ITextViewerExtension5; +import dwtx.jface.text.Region; +import dwtx.jface.text.TextEvent; + + +/** + * Default implementation of a hyperlink manager. + * + * @since 3.1 + */ +public class HyperlinkManager : ITextListener, Listener, KeyListener, MouseListener, MouseMoveListener, FocusListener, MouseTrackListener { + + /** + * Detection strategy. + */ + public static final class DETECTION_STRATEGY { + + String fName; + + private DETECTION_STRATEGY(String name) { + fName= name; + } + + /* + * @see java.lang.Object#toString() + */ + 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. + * <p> + * This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()} + * returns <code>true</code>. + * </p> + */ + 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. + * <p> + * This strategy is only allowed if {@link IHyperlinkPresenter#canShowMultipleHyperlinks()} + * returns <code>true</code>. + * </p> + */ + 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 bool fActive; + /** The key modifier mask. */ + 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 DWT 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 is FIRST || fDetectionStrategy is LONGEST_REGION_FIRST); + setHyperlinkDetectors(hyperlinkDetectors); + setHyperlinkStateMask(eventStateMask); + + StyledText text= fTextViewer.getTextWidget(); + if (text is null || text.isDisposed()) + return; + + text.getDisplay().addFilter(DWT.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. + * <p> + * It is allowed to call this method after this + * hyperlink manger has been installed. + * </p> + * + * @param hyperlinkDetectors and array of hyperlink detectors, must not be empty + */ + public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors) { + Assert.isTrue(hyperlinkDetectors !is null && hyperlinkDetectors.length > 0); + if (fHyperlinkDetectors is null) + fHyperlinkDetectors= hyperlinkDetectors; + else { + synchronized (fHyperlinkDetectors) { + fHyperlinkDetectors= hyperlinkDetectors; + } + } + } + + /** + * Sets the DWT event state mask which in combination + * with the left mouse button triggers the hyperlink mode. + * <p> + * It is allowed to call this method after this + * hyperlink manger has been installed. + * </p> + * + * @param eventStateMask the DWT 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 !is null && !text.isDisposed()) { + text.removeKeyListener(this); + text.getDisplay().removeFilter(DWT.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 <code>null</code> if none. + */ + protected IHyperlink[] findHyperlinks() { + int offset= getCurrentTextOffset(); + if (offset is -1) + return null; + + bool canShowMultipleHyperlinks= fHyperlinkPresenter.canShowMultipleHyperlinks(); + IRegion region= new Region(offset, 0); + 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 is null) + continue; + + if (detector instanceof IHyperlinkDetectorExtension2) { + int stateMask= ((IHyperlinkDetectorExtension2)detector).getStateMask(); + if (stateMask !is -1 && stateMask !is fActiveHyperlinkStateMask) + continue; + else if (stateMask is -1 && fActiveHyperlinkStateMask !is fHyperlinkStateMask) + continue; + } else if (fActiveHyperlinkStateMask !is fHyperlinkStateMask) + continue; + + IHyperlink[] hyperlinks= detector.detectHyperlinks(fTextViewer, region, canShowMultipleHyperlinks); + if (hyperlinks is null) + continue; + + Assert.isLegal(hyperlinks.length > 0); + + if (fDetectionStrategy is FIRST) { + if (hyperlinks.length is 1) + return hyperlinks; + return new IHyperlink[] {hyperlinks[0]}; + } + allHyperlinks.addAll(Arrays.asList(hyperlinks)); + } + } + + if (allHyperlinks.isEmpty()) + return null; + + if (fDetectionStrategy !is ALL) { + int maxLength= computeLongestHyperlinkLength(allHyperlinks); + Iterator iter= new ArrayList(allHyperlinks).iterator(); + while (iter.hasNext()) { + IHyperlink hyperlink= (IHyperlink)iter.next(); + if (hyperlink.getHyperlinkRegion().getLength() < maxLength) + allHyperlinks.remove(hyperlink); + } + } + + if (fDetectionStrategy is LONGEST_REGION_FIRST) + return new IHyperlink[] {(IHyperlink)allHyperlinks.get(0)}; + + return (IHyperlink[])allHyperlinks.toArray(new IHyperlink[allHyperlinks.size()]); + + } + + /** + * Computes the length of the longest detected + * hyperlink. + * + * @param hyperlinks + * @return the length of the longest detected + */ + protected int computeLongestHyperlinkLength(List hyperlinks) { + Assert.isLegal(hyperlinks !is null && !hyperlinks.isEmpty()); + Iterator iter= hyperlinks.iterator(); + int length= Integer.MIN_VALUE; + while (iter.hasNext()) { + IRegion region= ((IHyperlink)iter.next()).getHyperlinkRegion(); + if (region.getLength() < length) + continue; + length= region.getLength(); + } + return length; + } + + /** + * Returns the current text offset. + * + * @return the current text offset + */ + protected int getCurrentTextOffset() { + + try { + StyledText text= fTextViewer.getTextWidget(); + if (text is null || text.isDisposed()) + return -1; + + Display display= text.getDisplay(); + Point absolutePosition= display.getCursorLocation(); + Point relativePosition= text.toControl(absolutePosition); + + int widgetOffset= text.getOffsetAtLocation(relativePosition); + Point p= text.getLocationAtOffset(widgetOffset); + if (p.x > relativePosition.x) + widgetOffset--; + + if (fTextViewer instanceof ITextViewerExtension5) { + ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer; + return extension.widgetOffset2ModelOffset(widgetOffset); + } + + return widgetOffset + fTextViewer.getVisibleRegion().getOffset(); + + } catch (IllegalArgumentException e) { + return -1; + } + } + + /* + * @see dwt.events.KeyListener#keyPressed(dwt.events.KeyEvent) + */ + public void keyPressed(KeyEvent event) { + + if (fActive) { + deactivate(); + return; + } + + if (!isRegisteredStateMask(event.keyCode)) { + deactivate(); + return; + } + + fActive= true; + fActiveHyperlinkStateMask= event.keyCode; + +// removed for #25871 (hyperlinks could interact with typing) +// +// ITextViewer viewer= getSourceViewer(); +// if (viewer is null) +// return; +// +// IRegion region= getCurrentTextRegion(viewer); +// if (region is null) +// return; +// +// highlightRegion(viewer, region); +// activateCursor(viewer); + } + + /* + * @see dwt.events.KeyListener#keyReleased(dwt.events.KeyEvent) + */ + public void keyReleased(KeyEvent event) { + } + + /* + * @see dwt.events.MouseListener#mouseDoubleClick(dwt.events.MouseEvent) + */ + public void mouseDoubleClick(MouseEvent e) { + + } + + /* + * @see dwt.events.MouseListener#mouseDown(dwt.events.MouseEvent) + */ + public void mouseDown(MouseEvent event) { + + if (!fActive) + return; + + if (event.stateMask !is fActiveHyperlinkStateMask) { + deactivate(); + return; + } + + if (event.button !is 1) { + deactivate(); + return; + } + } + + /* + * @see dwt.events.MouseListener#mouseUp(dwt.events.MouseEvent) + */ + public void mouseUp(MouseEvent e) { + + if (!fActive) { + fActiveHyperlinks= null; + return; + } + + if (e.button !is 1) + fActiveHyperlinks= null; + + deactivate(); + + if (fActiveHyperlinks !is null) + fActiveHyperlinks[0].open(); + } + + /* + * @see dwt.events.MouseMoveListener#mouseMove(dwt.events.MouseEvent) + */ + 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 is null || text.isDisposed()) { + deactivate(); + return; + } + + if ((event.stateMask & DWT.BUTTON1) !is 0 && text.getSelectionCount() !is 0) { + deactivate(); + return; + } + + fActiveHyperlinks= findHyperlinks(); + if (fActiveHyperlinks is null || fActiveHyperlinks.length is 0) { + fHyperlinkPresenter.hideHyperlinks(); + return; + } + + fHyperlinkPresenter.showHyperlinks(fActiveHyperlinks); + + } + + /** + * Checks whether the given state mask is registered. + * + * @param stateMask + * @return <code>true</code> if a detector is registered for the given state mask + * @since 3.3 + */ + private bool isRegisteredStateMask(int stateMask) { + if (stateMask is fHyperlinkStateMask) + return true; + + synchronized (fHyperlinkDetectors) { + for (int i= 0; i < fHyperlinkDetectors.length; i++) { + if (fHyperlinkDetectors[i] instanceof IHyperlinkDetectorExtension2) { + if (stateMask is ((IHyperlinkDetectorExtension2)fHyperlinkDetectors[i]).getStateMask()) + return true; + } + } + } + return false; + } + + /* + * @see dwt.events.FocusListener#focusGained(dwt.events.FocusEvent) + */ + public void focusGained(FocusEvent e) {} + + /* + * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent) + */ + public void focusLost(FocusEvent event) { + deactivate(); + } + + /* + * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event) + * @since 3.2 + */ + public void handleEvent(Event event) { + //key up + deactivate(); + } + + /* + * @see dwtx.jface.text.ITextListener#textChanged(TextEvent) + * @since 3.2 + */ + public void textChanged(TextEvent event) { + if (event.getDocumentEvent() !is null) + deactivate(); + } + + /** + * {@inheritDoc} + * + * @since 3.4 + */ + public void mouseExit(MouseEvent e) { + if (fHyperlinkPresenter instanceof IHyperlinkPresenterExtension) { + if (!((IHyperlinkPresenterExtension)fHyperlinkPresenter).canHideHyperlinks()) + return; + } + deactivate(); + } + + /** + * {@inheritDoc} + * + * @since 3.4 + */ + public void mouseEnter(MouseEvent e) { + } + + /** + * {@inheritDoc} + * + * @since 3.4 + */ + public void mouseHover(MouseEvent e) { + } + +}