diff dwtx/jface/text/hyperlink/MultipleHyperlinkPresenter.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/MultipleHyperlinkPresenter.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,742 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.text.hyperlink.MultipleHyperlinkPresenter;
+
+import dwt.dwthelper.utils;
+
+
+import dwt.DWT;
+import dwt.events.KeyAdapter;
+import dwt.events.KeyEvent;
+import dwt.events.KeyListener;
+import dwt.events.MouseAdapter;
+import dwt.events.MouseEvent;
+import dwt.events.MouseMoveListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.events.ShellAdapter;
+import dwt.events.ShellEvent;
+import dwt.graphics.Color;
+import dwt.graphics.Point;
+import dwt.graphics.RGB;
+import dwt.graphics.Rectangle;
+import dwt.layout.GridLayout;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Shell;
+import dwt.widgets.Table;
+import dwt.widgets.TableItem;
+import dwtx.jface.preference.IPreferenceStore;
+import dwtx.jface.text.AbstractInformationControl;
+import dwtx.jface.text.AbstractInformationControlManager;
+import dwtx.jface.text.IInformationControl;
+import dwtx.jface.text.IInformationControlCreator;
+import dwtx.jface.text.IInformationControlExtension2;
+import dwtx.jface.text.IInformationControlExtension3;
+import dwtx.jface.text.IRegion;
+import dwtx.jface.text.ITextHover;
+import dwtx.jface.text.ITextHoverExtension;
+import dwtx.jface.text.ITextViewer;
+import dwtx.jface.text.IWidgetTokenKeeper;
+import dwtx.jface.text.IWidgetTokenKeeperExtension;
+import dwtx.jface.text.IWidgetTokenOwner;
+import dwtx.jface.text.IWidgetTokenOwnerExtension;
+import dwtx.jface.text.JFaceTextUtil;
+import dwtx.jface.text.Region;
+import dwtx.jface.util.Geometry;
+import dwtx.jface.viewers.ColumnLabelProvider;
+import dwtx.jface.viewers.IStructuredContentProvider;
+import dwtx.jface.viewers.TableViewer;
+import dwtx.jface.viewers.Viewer;
+
+
+/**
+ * A hyperlink presenter capable of showing multiple hyperlinks in a hover.
+ * 
+ * @since 3.4
+ */
+public class MultipleHyperlinkPresenter : DefaultHyperlinkPresenter {
+    
+    /**
+     * An information control capable of showing a list of hyperlinks. The hyperlinks can be opened.
+     */
+    private static class LinkListInformationControl : AbstractInformationControl , IInformationControlExtension2 {
+        
+        private static final class LinkContentProvider : IStructuredContentProvider {
+            
+            /*
+             * @see dwtx.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+             */
+            public Object[] getElements(Object inputElement) {
+                return (Object[]) inputElement;
+            }
+            
+            /*
+             * @see dwtx.jface.viewers.IContentProvider#dispose()
+             */
+            public void dispose() {
+            }
+            
+            /*
+             * @see dwtx.jface.viewers.IContentProvider#inputChanged(dwtx.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+             */
+            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+            }
+        }
+        
+        private static final class LinkLabelProvider : ColumnLabelProvider {
+            /*
+             * @see dwtx.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+             */
+            public String getText(Object element) {
+                IHyperlink link= (IHyperlink)element;
+                String text= link.getHyperlinkText();
+                if (text !is null)
+                    return text;
+                return HyperlinkMessages.getString("LinkListInformationControl.unknownLink"); //$NON-NLS-1$
+            }
+        }
+        
+        private final MultipleHyperlinkHoverManager fManager;
+        
+        private IHyperlink[] fInput;
+        private Composite fParent;
+        private Table fTable;
+
+        private Color fForegroundColor;
+        private Color fBackgroundColor;
+        
+        
+        /**
+         * Creates a link list information control with the given shell as parent.
+         *
+         * @param parentShell the parent shell
+         * @param manager the hover manager
+         * @param foregroundColor the foreground color, must not be disposed
+         * @param backgroundColor the background color, must not be disposed
+         */
+        public LinkListInformationControl(Shell parentShell, MultipleHyperlinkHoverManager manager, Color foregroundColor, Color backgroundColor) {
+            super(parentShell, false);
+            fManager= manager;
+            fForegroundColor= foregroundColor;
+            fBackgroundColor= backgroundColor;
+            create();
+        }
+        
+        /*
+         * @see dwtx.jface.text.IInformationControl#setInformation(java.lang.String)
+         */
+        public void setInformation(String information) {
+            //replaced by IInformationControlExtension2#setInput(java.lang.Object)
+        }
+        
+        /*
+         * @see dwtx.jface.text.IInformationControlExtension2#setInput(java.lang.Object)
+         */
+        public void setInput(Object input) {
+            fInput= (IHyperlink[]) input;
+            deferredCreateContent(fParent);
+        }
+        
+        /*
+         * @see dwtx.jface.text.AbstractInformationControl#createContent(dwt.widgets.Composite)
+         */
+        protected void createContent(Composite parent) {
+            fParent= parent;
+            if ("win32".equals(DWT.getPlatform())) { //$NON-NLS-1$
+                GridLayout layout= new GridLayout();
+                layout.marginWidth= 0;
+                layout.marginRight= 4;
+                fParent.setLayout(layout);
+            }
+            fParent.setForeground(fForegroundColor);
+            fParent.setBackground(fBackgroundColor);
+        }
+        
+        /*
+         * @see dwtx.jface.text.AbstractInformationControl#computeSizeHint()
+         */
+        public Point computeSizeHint() {
+            Point preferedSize= getShell().computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
+            
+            Point constraints= getSizeConstraints();
+            if (constraints is null)
+                return preferedSize;
+        
+            if (fTable.getVerticalBar() is null || fTable.getHorizontalBar() is null)
+                return Geometry.min(constraints, preferedSize);
+            
+            int scrollBarWidth= fTable.getVerticalBar().getSize().x;
+            int scrollBarHeight= fTable.getHorizontalBar().getSize().y;
+            
+            int width;
+            if (preferedSize.y - scrollBarHeight <= constraints.y) {
+                width= preferedSize.x - scrollBarWidth;
+                fTable.getVerticalBar().setVisible(false);
+            } else {
+                width= Math.min(preferedSize.x, constraints.x);
+            }
+            
+            int height;
+            if (preferedSize.x - scrollBarWidth <= constraints.x) {
+                height= preferedSize.y - scrollBarHeight;
+                fTable.getHorizontalBar().setVisible(false);
+            } else {
+                height= Math.min(preferedSize.y, constraints.y);
+            }
+            
+            return new Point(width, height);
+        }
+        
+        private void deferredCreateContent(Composite parent) {
+            fTable= new Table(parent, DWT.SINGLE | DWT.FULL_SELECTION);
+            fTable.setLinesVisible(false);
+            fTable.setHeaderVisible(false);
+            fTable.setForeground(fForegroundColor);
+            fTable.setBackground(fBackgroundColor);
+            
+            final TableViewer viewer= new TableViewer(fTable);
+            viewer.setContentProvider(new LinkContentProvider());
+            viewer.setLabelProvider(new LinkLabelProvider());
+            viewer.setInput(fInput);
+            fTable.setSelection(0);
+            
+            registerTableListeners();
+            
+            getShell().addShellListener(new ShellAdapter() {
+                
+                /*
+                 * @see dwt.events.ShellAdapter#shellActivated(dwt.events.ShellEvent)
+                 */
+                public void shellActivated(ShellEvent e) {
+                    if (viewer.getTable().getSelectionCount() is 0) {
+                        viewer.getTable().setSelection(0);
+                    }
+                    
+                    viewer.getTable().setFocus();
+                }
+            });
+        }
+        
+        private void registerTableListeners() {
+            
+            fTable.addMouseMoveListener(new MouseMoveListener() {
+                TableItem fLastItem= null;
+                
+                public void mouseMove(MouseEvent e) {
+                    if (fTable.equals(e.getSource())) {
+                        Object o= fTable.getItem(new Point(e.x, e.y));
+                        if (o instanceof TableItem) {
+                            TableItem item= (TableItem) o;
+                            if (!o.equals(fLastItem)) {
+                                fLastItem= (TableItem) o;
+                                fTable.setSelection(new TableItem[] { fLastItem });
+                            } else if (e.y < fTable.getItemHeight() / 4) {
+                                // Scroll up
+                                int index= fTable.indexOf(item);
+                                if (index > 0) {
+                                    fLastItem= fTable.getItem(index - 1);
+                                    fTable.setSelection(new TableItem[] { fLastItem });
+                                }
+                            } else if (e.y > fTable.getBounds().height - fTable.getItemHeight() / 4) {
+                                // Scroll down
+                                int index= fTable.indexOf(item);
+                                if (index < fTable.getItemCount() - 1) {
+                                    fLastItem= fTable.getItem(index + 1);
+                                    fTable.setSelection(new TableItem[] { fLastItem });
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+            
+            fTable.addSelectionListener(new SelectionAdapter() {
+                public void widgetSelected(SelectionEvent e) {
+                    openSelectedLink();
+                }
+            });
+            
+            fTable.addMouseListener(new MouseAdapter() {
+                public void mouseUp(MouseEvent e) {
+                    if (fTable.getSelectionCount() < 1)
+                        return;
+                    
+                    if (e.button !is 1)
+                        return;
+                    
+                    if (fTable.equals(e.getSource())) {
+                        Object o= fTable.getItem(new Point(e.x, e.y));
+                        TableItem selection= fTable.getSelection()[0];
+                        if (selection.equals(o))
+                            openSelectedLink();
+                    }
+                }
+            });
+            
+            fTable.addKeyListener(new KeyAdapter() {
+                public void keyPressed(KeyEvent e) {
+                    if (e.keyCode is 0x0D) // return
+                        openSelectedLink();
+                }
+            });
+        }
+        
+        /*
+         * @see dwtx.jface.text.IInformationControlExtension#hasContents()
+         */
+        public bool hasContents() {
+            return true;
+        }
+
+        /**
+         * Opens the currently selected link.
+         */
+        private void openSelectedLink() {
+            TableItem selection= fTable.getSelection()[0];
+            IHyperlink link= (IHyperlink)selection.getData();
+            fManager.hideInformationControl();
+            link.open();
+        }
+    }
+    
+    private class MultipleHyperlinkHover : ITextHover, ITextHoverExtension {
+        
+        /**
+         * @see dwtx.jface.text.ITextHover#getHoverInfo(dwtx.jface.text.ITextViewer, dwtx.jface.text.IRegion)
+         * @deprecated
+         */
+        public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
+            return null;
+        }
+        
+        /*
+         * @see dwtx.jface.text.ITextHover#getHoverRegion(dwtx.jface.text.ITextViewer, int)
+         */
+        public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
+            return fSubjectRegion;
+        }
+        
+        /*
+         * @see dwtx.jface.text.ITextHoverExtension2#getHoverInfo2(dwtx.jface.text.ITextViewer, dwtx.jface.text.IRegion)
+         */
+        public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
+            return fHyperlinks;
+        }
+        
+        /*
+         * @see dwtx.jface.text.ITextHoverExtension#getHoverControlCreator()
+         */
+        public IInformationControlCreator getHoverControlCreator() {
+            return new IInformationControlCreator() {
+                public IInformationControl createInformationControl(Shell parent) {
+                    Color foregroundColor= fTextViewer.getTextWidget().getForeground();
+                    Color backgroundColor= fTextViewer.getTextWidget().getBackground();
+                    return new LinkListInformationControl(parent, fManager, foregroundColor, backgroundColor);
+                }
+            };
+        }
+    }
+    
+    private static class MultipleHyperlinkHoverManager : AbstractInformationControlManager , IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
+        
+        private class Closer : IInformationControlCloser, Listener, KeyListener {
+            
+            private Control fSubjectControl;
+            private Display fDisplay;
+            private IInformationControl fControl;
+            private Rectangle fSubjectArea;
+            
+            /*
+             * @see dwtx.jface.text.AbstractInformationControlManager.IInformationControlCloser#setInformationControl(dwtx.jface.text.IInformationControl)
+             */
+            public void setInformationControl(IInformationControl control) {
+                fControl= control;
+            }
+            
+            /*
+             * @see dwtx.jface.text.AbstractInformationControlManager.IInformationControlCloser#setSubjectControl(dwt.widgets.Control)
+             */
+            public void setSubjectControl(Control subject) {
+                fSubjectControl= subject;
+            }
+            
+            /*
+             * @see dwtx.jface.text.AbstractInformationControlManager.IInformationControlCloser#start(dwt.graphics.Rectangle)
+             */
+            public void start(Rectangle subjectArea) {
+                fSubjectArea= subjectArea;
+                
+                fDisplay= fSubjectControl.getDisplay();
+                if (!fDisplay.isDisposed()) {
+                    fDisplay.addFilter(DWT.FocusOut, this);
+                    fDisplay.addFilter(DWT.MouseMove, this);
+                    fTextViewer.getTextWidget().addKeyListener(this);
+                }
+            }
+            
+            /*
+             * @see dwtx.jface.text.AbstractInformationControlManager.IInformationControlCloser#stop()
+             */
+            public void stop() {
+                if (fDisplay !is null && !fDisplay.isDisposed()) {
+                    fDisplay.removeFilter(DWT.FocusOut, this);
+                    fDisplay.removeFilter(DWT.MouseMove, this);
+                    fTextViewer.getTextWidget().removeKeyListener(this);
+                }
+                
+                fSubjectArea= null;
+            }
+            
+            /*
+             * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event)
+             */
+            public void handleEvent(Event event) {
+                switch (event.type) {
+                    case DWT.FocusOut:
+                        if (!fControl.isFocusControl())
+                            disposeInformationControl();
+                        break;
+                    case DWT.MouseMove:
+                        handleMouseMove(event);
+                        break;
+                }
+            }
+            
+            /**
+             * Handle mouse movement events.
+             * 
+             * @param event the event
+             */
+            private void handleMouseMove(Event event) {
+                if (!(event.widget instanceof Control))
+                    return;
+                
+                if (fControl.isFocusControl())
+                    return;
+                
+                Control eventControl= (Control) event.widget;
+                
+                //transform coordinates to subject control:
+                Point mouseLoc= event.display.map(eventControl, fSubjectControl, event.x, event.y);
+                
+                if (fSubjectArea.contains(mouseLoc))
+                    return;
+                
+                if (inKeepUpZone(mouseLoc.x, mouseLoc.y, ((IInformationControlExtension3) fControl).getBounds()))
+                    return;
+                
+                hideInformationControl();
+            }
+            
+            /**
+             * Tests whether a given mouse location is within the keep-up zone.
+             * The hover should not be hidden as long as the mouse stays inside this zone.
+             * 
+             * @param x the x coordinate, relative to the <em>subject control</em>
+             * @param y the y coordinate, relative to the <em>subject control</em>
+             * @param controlBounds the bounds of the current control
+             * 
+             * @return <code>true</code> iff the mouse event occurred in the keep-up zone
+             */
+            private bool inKeepUpZone(int x, int y, Rectangle controlBounds) {
+                //  +-----------+
+                //  |subjectArea|
+                //  +-----------+
+                //  |also keepUp|
+                // ++-----------+-------+
+                // | totalBounds        |
+                // +--------------------+
+                if (fSubjectArea.contains(x, y))
+                    return true;
+                
+                Rectangle iControlBounds= fSubjectControl.getDisplay().map(null, fSubjectControl, controlBounds);
+                Rectangle totalBounds= Geometry.copy(iControlBounds);
+                if (totalBounds.contains(x, y))
+                    return true;
+                
+                int keepUpY= fSubjectArea.y + fSubjectArea.height;
+                Rectangle alsoKeepUp= new Rectangle(fSubjectArea.x, keepUpY, fSubjectArea.width, totalBounds.y - keepUpY);
+                return alsoKeepUp.contains(x, y);
+            }
+            
+            /*
+             * @see dwt.events.KeyListener#keyPressed(dwt.events.KeyEvent)
+             */
+            public void keyPressed(KeyEvent e) {
+            }
+            
+            /*
+             * @see dwt.events.KeyListener#keyReleased(dwt.events.KeyEvent)
+             */
+            public void keyReleased(KeyEvent e) {
+                hideInformationControl();
+            }
+
+        }
+        
+        /**
+         * Priority of the hover managed by this manager.
+         * Default value: One higher then for the hovers
+         * managed by TextViewerHoverManager.
+         */
+        private static final int WIDGET_TOKEN_PRIORITY= 1;
+        
+        private final MultipleHyperlinkHover fHover;
+        private final ITextViewer fTextViewer;
+        private final MultipleHyperlinkPresenter fHyperlinkPresenter;
+        private Closer fCloser;
+        private bool fIsControlVisible;
+
+        
+        /**
+         * Create a new MultipleHyperlinkHoverManager. The MHHM can show and hide
+         * the given MultipleHyperlinkHover inside the given ITextViewer.
+         * 
+         * @param hover the hover to manage
+         * @param viewer the viewer to show the hover in
+         * @param hyperlinkPresenter the hyperlink presenter using this manager to present hyperlinks
+         */
+        public MultipleHyperlinkHoverManager(MultipleHyperlinkHover hover, ITextViewer viewer, MultipleHyperlinkPresenter hyperlinkPresenter) {
+            super(hover.getHoverControlCreator());
+            
+            fHover= hover;
+            fTextViewer= viewer;
+            fHyperlinkPresenter= hyperlinkPresenter;
+            
+            fCloser= new Closer();
+            setCloser(fCloser);
+            fIsControlVisible= false;
+        }
+        
+        /*
+         * @see dwtx.jface.text.AbstractInformationControlManager#computeInformation()
+         */
+        protected void computeInformation() {
+            IRegion region= fHover.getHoverRegion(fTextViewer, -1);
+            if (region is null) {
+                setInformation(null, null);
+                return;
+            }
+            
+            Rectangle area= JFaceTextUtil.computeArea(region, fTextViewer);
+            if (area is null || area.isEmpty()) {
+                setInformation(null, null);
+                return;
+            }
+            
+            Object information= fHover.getHoverInfo2(fTextViewer, region);
+            setCustomInformationControlCreator(fHover.getHoverControlCreator());
+            setInformation(information, area);
+        }
+        
+        /* 
+         * @see dwtx.jface.text.AbstractInformationControlManager#computeInformationControlLocation(dwt.graphics.Rectangle, dwt.graphics.Point)
+         */
+        protected Point computeInformationControlLocation(Rectangle subjectArea, Point controlSize) {
+            Point result= super.computeInformationControlLocation(subjectArea, controlSize);
+            
+            Point cursorLocation= fTextViewer.getTextWidget().getDisplay().getCursorLocation();
+            if (cursorLocation.x <= result.x + controlSize.x)
+                return result;
+            
+            result.x= cursorLocation.x + 20 - controlSize.x;
+            return result;
+        }
+        
+        /*
+         * @see dwtx.jface.text.AbstractInformationControlManager#showInformationControl(dwt.graphics.Rectangle)
+         */
+        protected void showInformationControl(Rectangle subjectArea) {
+            if (fTextViewer instanceof IWidgetTokenOwnerExtension) {
+                if (((IWidgetTokenOwnerExtension) fTextViewer).requestWidgetToken(this, WIDGET_TOKEN_PRIORITY))
+                    super.showInformationControl(subjectArea);
+            } else if (fTextViewer instanceof IWidgetTokenOwner) {
+                if (((IWidgetTokenOwner) fTextViewer).requestWidgetToken(this))
+                    super.showInformationControl(subjectArea);
+            } else {
+                super.showInformationControl(subjectArea);
+            }
+            
+            fIsControlVisible= true;
+        }
+        
+        /*
+         * @see dwtx.jface.text.AbstractInformationControlManager#hideInformationControl()
+         */
+        protected void hideInformationControl() {
+            super.hideInformationControl();
+            
+            if (fTextViewer instanceof IWidgetTokenOwner) {
+                ((IWidgetTokenOwner) fTextViewer).releaseWidgetToken(this);
+            }
+            
+            fIsControlVisible= false;
+            fHyperlinkPresenter.hideHyperlinks();
+        }
+        
+        /*
+         * @see dwtx.jface.text.AbstractInformationControlManager#disposeInformationControl()
+         */
+        public void disposeInformationControl() {
+            super.disposeInformationControl();
+            
+            if (fTextViewer instanceof IWidgetTokenOwner) {
+                ((IWidgetTokenOwner) fTextViewer).releaseWidgetToken(this);
+            }
+            
+            fIsControlVisible= false;
+            fHyperlinkPresenter.hideHyperlinks();
+        }
+        
+        /*
+         * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner)
+         */
+        public bool requestWidgetToken(IWidgetTokenOwner owner) {
+            hideInformationControl();
+            return true;
+        }
+        
+        /*
+         * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int)
+         */
+        public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+            if (priority < WIDGET_TOKEN_PRIORITY)
+                return false;
+            
+            hideInformationControl();
+            return true;
+        }
+        
+        /*
+         * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner)
+         */
+        public bool setFocus(IWidgetTokenOwner owner) {
+            return false;
+        }
+        
+        /**
+         * Returns <code>true</code> if the information control managed by
+         * this manager is visible, <code>false</code> otherwise.
+         * 
+         * @return <code>true</code> if information control is visible
+         */
+        public bool isInformationControlVisible() {
+            return fIsControlVisible;
+        }
+    }
+    
+    private ITextViewer fTextViewer;
+    
+    private IHyperlink[] fHyperlinks;
+    private Region fSubjectRegion;
+    private MultipleHyperlinkHoverManager fManager;
+    
+    /**
+     * Creates a new multiple hyperlink presenter which uses
+     * {@link #HYPERLINK_COLOR} to read the color from the given preference store.
+     *
+     * @param store the preference store
+     */
+    public MultipleHyperlinkPresenter(IPreferenceStore store) {
+        super(store);
+    }
+    
+    /**
+     * Creates a new multiple hyperlink presenter.
+     *
+     * @param color the hyperlink color, to be disposed by the caller
+     */
+    public MultipleHyperlinkPresenter(RGB color) {
+        super(color);
+    }
+    
+    /*
+     * @see dwtx.jface.text.hyperlink.DefaultHyperlinkPresenter#install(dwtx.jface.text.ITextViewer)
+     */
+    public void install(ITextViewer viewer) {
+        super.install(viewer);
+        fTextViewer= viewer;
+        
+        fManager= new MultipleHyperlinkHoverManager(new MultipleHyperlinkHover(), fTextViewer, this);
+        fManager.install(viewer.getTextWidget());
+        fManager.setSizeConstraints(100, 12, false, true);
+    }
+    
+    /*
+     * @see dwtx.jface.text.hyperlink.DefaultHyperlinkPresenter#uninstall()
+     */
+    public void uninstall() {
+        super.uninstall();
+        
+        if (fTextViewer !is null) {
+            fManager.dispose();
+            
+            fTextViewer= null;
+        }
+    }
+    
+    /*
+     * @see dwtx.jface.text.hyperlink.DefaultHyperlinkPresenter#canShowMultipleHyperlinks()
+     */
+    public bool canShowMultipleHyperlinks() {
+        return true;
+    }
+    
+    /*
+     * @see dwtx.jface.text.hyperlink.DefaultHyperlinkPresenter#canHideHyperlinks()
+     */
+    public bool canHideHyperlinks() {
+        return !fManager.isInformationControlVisible();
+    }
+    
+    /*
+     * @see dwtx.jface.text.hyperlink.DefaultHyperlinkPresenter#hideHyperlinks()
+     */
+    public void hideHyperlinks() {
+        super.hideHyperlinks();
+        
+        fHyperlinks= null;
+    }
+    
+    /*
+     * @see dwtx.jface.text.hyperlink.DefaultHyperlinkPresenter#showHyperlinks(dwtx.jface.text.hyperlink.IHyperlink[])
+     */
+    public void showHyperlinks(IHyperlink[] hyperlinks) {
+        super.showHyperlinks(new IHyperlink[] { hyperlinks[0] });
+
+        fSubjectRegion= null;
+        fHyperlinks= hyperlinks;
+        
+        if (hyperlinks.length is 1)
+            return;
+        
+        int start= hyperlinks[0].getHyperlinkRegion().getOffset();
+        int end= start + hyperlinks[0].getHyperlinkRegion().getLength();
+        
+        for (int i= 1; i < hyperlinks.length; i++) {
+            int hstart= hyperlinks[i].getHyperlinkRegion().getOffset();
+            int hend= hstart + hyperlinks[i].getHyperlinkRegion().getLength();
+            
+            start= Math.min(start, hstart);
+            end= Math.max(end, hend);
+        }
+        
+        fSubjectRegion= new Region(start, end - start);
+        
+        fManager.showInformation();
+    }
+}