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) {
+    }
+
+}