Mercurial > projects > dwt-addons
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(); + } +}