Mercurial > projects > dwt-addons
diff dwtx/jface/text/TextViewerHoverManager.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/TextViewerHoverManager.d Sat Aug 23 19:10:48 2008 +0200 @@ -0,0 +1,396 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.text.TextViewerHoverManager; + +import dwt.dwthelper.utils; + + + + +import dwt.custom.StyledText; +import dwt.events.MouseEvent; +import dwt.events.MouseMoveListener; +import dwt.graphics.Point; +import dwt.graphics.Rectangle; +import dwt.widgets.Display; +import dwtx.core.runtime.ILog; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.Platform; +import dwtx.core.runtime.Status; + + +/** + * This manager controls the layout, content, and visibility of an information + * control in reaction to mouse hover events issued by the text widget of a + * text viewer. It overrides <code>computeInformation</code>, so that the + * computation is performed in a dedicated background thread. This implies + * that the used <code>ITextHover</code> objects must be capable of + * operating in a non-UI thread. + * + * @since 2.0 + */ +class TextViewerHoverManager : AbstractHoverInformationControlManager , IWidgetTokenKeeper, IWidgetTokenKeeperExtension { + + + /** + * Priority of the hovers managed by this manager. + * Default value: <code>0</code>; + * @since 3.0 + */ + public final static int WIDGET_PRIORITY= 0; + + + /** The text viewer */ + private TextViewer fTextViewer; + /** The hover information computation thread */ + private Thread fThread; + /** The stopper of the computation thread */ + private ITextListener fStopper; + /** Internal monitor */ + private Object fMutex= new Object(); + /** The currently shown text hover. */ + private volatile ITextHover fTextHover; + /** + * Tells whether the next mouse hover event + * should be processed. + * @since 3.0 + */ + private bool fProcessMouseHoverEvent= true; + /** + * Internal mouse move listener. + * @since 3.0 + */ + private MouseMoveListener fMouseMoveListener; + /** + * Internal view port listener. + * @since 3.0 + */ + private IViewportListener fViewportListener; + + + /** + * Creates a new text viewer hover manager specific for the given text viewer. + * The manager uses the given information control creator. + * + * @param textViewer the viewer for which the controller is created + * @param creator the information control creator + */ + public TextViewerHoverManager(TextViewer textViewer, IInformationControlCreator creator) { + super(creator); + fTextViewer= textViewer; + fStopper= new ITextListener() { + public void textChanged(TextEvent event) { + synchronized (fMutex) { + if (fThread !is null) { + fThread.interrupt(); + fThread= null; + } + } + } + }; + fViewportListener= new IViewportListener() { + /* + * @see dwtx.jface.text.IViewportListener#viewportChanged(int) + */ + public void viewportChanged(int verticalOffset) { + fProcessMouseHoverEvent= false; + } + }; + fTextViewer.addViewportListener(fViewportListener); + fMouseMoveListener= new MouseMoveListener() { + /* + * @see MouseMoveListener#mouseMove(MouseEvent) + */ + public void mouseMove(MouseEvent event) { + fProcessMouseHoverEvent= true; + } + }; + fTextViewer.getTextWidget().addMouseMoveListener(fMouseMoveListener); + } + + /** + * Determines all necessary details and delegates the computation into + * a background thread. + */ + protected void computeInformation() { + + if (!fProcessMouseHoverEvent) { + setInformation(null, null); + return; + } + + Point location= getHoverEventLocation(); + int offset= computeOffsetAtLocation(location.x, location.y); + if (offset is -1) { + setInformation(null, null); + return; + } + + final ITextHover hover= fTextViewer.getTextHover(offset, getHoverEventStateMask()); + if (hover is null) { + setInformation(null, null); + return; + } + + final IRegion region= hover.getHoverRegion(fTextViewer, offset); + if (region is null) { + setInformation(null, null); + return; + } + + final Rectangle area= JFaceTextUtil.computeArea(region, fTextViewer); + if (area is null || area.isEmpty()) { + setInformation(null, null); + return; + } + + if (fThread !is null) { + setInformation(null, null); + return; + } + + fThread= new Thread("Text Viewer Hover Presenter") { //$NON-NLS-1$ + public void run() { + // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17693 + bool hasFinished= false; + try { + if (fThread !is null) { + Object information; + try { + if (hover instanceof ITextHoverExtension2) + information= ((ITextHoverExtension2)hover).getHoverInfo2(fTextViewer, region); + else + information= hover.getHoverInfo(fTextViewer, region); + } catch (ArrayIndexOutOfBoundsException x) { + /* + * This code runs in a separate thread which can + * lead to text offsets being out of bounds when + * computing the hover info (see bug 32848). + */ + information= null; + } + + if (hover instanceof ITextHoverExtension) + setCustomInformationControlCreator(((ITextHoverExtension) hover).getHoverControlCreator()); + else + setCustomInformationControlCreator(null); + + setInformation(information, area); + if (information !is null) + fTextHover= hover; + } else { + setInformation(null, null); + } + hasFinished= true; + } catch (RuntimeException ex) { + String PLUGIN_ID= "dwtx.jface.text"; //$NON-NLS-1$ + ILog log= Platform.getLog(Platform.getBundle(PLUGIN_ID)); + log.log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, "Unexpected runtime error while computing a text hover", ex)); //$NON-NLS-1$ + } finally { + synchronized (fMutex) { + if (fTextViewer !is null) + fTextViewer.removeTextListener(fStopper); + fThread= null; + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=44756 + if (!hasFinished) + setInformation(null, null); + } + } + } + }; + + fThread.setDaemon(true); + fThread.setPriority(Thread.MIN_PRIORITY); + synchronized (fMutex) { + fTextViewer.addTextListener(fStopper); + fThread.start(); + } + } + + /** + * As computation is done in the background, this method is + * also called in the background thread. Delegates the control + * flow back into the UI thread, in order to allow displaying the + * information in the information control. + */ + protected void presentInformation() { + if (fTextViewer is null) + return; + + StyledText textWidget= fTextViewer.getTextWidget(); + if (textWidget !is null && !textWidget.isDisposed()) { + Display display= textWidget.getDisplay(); + if (display is null) + return; + + display.asyncExec(new Runnable() { + public void run() { + doPresentInformation(); + } + }); + } + } + + /* + * @see AbstractInformationControlManager#presentInformation() + */ + protected void doPresentInformation() { + super.presentInformation(); + } + + /** + * Computes the document offset underlying the given text widget coordinates. + * This method uses a linear search as it cannot make any assumption about + * how the document is actually presented in the widget. (Covers cases such + * as bidirectional text.) + * + * @param x the horizontal coordinate inside the text widget + * @param y the vertical coordinate inside the text widget + * @return the document offset corresponding to the given point + */ + private int computeOffsetAtLocation(int x, int y) { + + try { + + StyledText styledText= fTextViewer.getTextWidget(); + int widgetOffset= styledText.getOffsetAtLocation(new Point(x, y)); + Point p= styledText.getLocationAtOffset(widgetOffset); + if (p.x > x) + widgetOffset--; + + if (fTextViewer instanceof ITextViewerExtension5) { + ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer; + return extension.widgetOffset2ModelOffset(widgetOffset); + } + + return widgetOffset + fTextViewer._getVisibleRegionOffset(); + + } catch (IllegalArgumentException e) { + return -1; + } + } + + /* + * @see dwtx.jface.text.AbstractInformationControlManager#showInformationControl(dwt.graphics.Rectangle) + */ + protected void showInformationControl(Rectangle subjectArea) { + if (fTextViewer !is null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY)) + super.showInformationControl(subjectArea); + else + if (DEBUG) + System.out.println("TextViewerHoverManager#showInformationControl(..) did not get widget token"); //$NON-NLS-1$ + } + + /* + * @see dwtx.jface.text.AbstractInformationControlManager#hideInformationControl() + */ + protected void hideInformationControl() { + try { + fTextHover= null; + super.hideInformationControl(); + } finally { + if (fTextViewer !is null) + fTextViewer.releaseWidgetToken(this); + } + } + + /* + * @see dwtx.jface.text.AbstractInformationControlManager#replaceInformationControl(bool) + * @since 3.4 + */ + void replaceInformationControl(bool takeFocus) { + if (fTextViewer !is null) + fTextViewer.releaseWidgetToken(this); + super.replaceInformationControl(takeFocus); + } + + /* + * @see dwtx.jface.text.AbstractInformationControlManager#handleInformationControlDisposed() + */ + protected void handleInformationControlDisposed() { + try { + super.handleInformationControlDisposed(); + } finally { + if (fTextViewer !is null) + fTextViewer.releaseWidgetToken(this); + } + } + + /* + * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner) + */ + public bool requestWidgetToken(IWidgetTokenOwner owner) { + fTextHover= null; + super.hideInformationControl(); + return true; + } + + /* + * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int) + * @since 3.0 + */ + public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) { + if (priority > WIDGET_PRIORITY) { + fTextHover= null; + super.hideInformationControl(); + return true; + } + return false; + } + + /* + * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner) + * @since 3.0 + */ + public bool setFocus(IWidgetTokenOwner owner) { + if (! hasInformationControlReplacer()) + return false; + + IInformationControl iControl= getCurrentInformationControl(); + if (canReplace(iControl)) { + if (cancelReplacingDelay()) + replaceInformationControl(true); + + return true; + } + + return false; + } + + /** + * Returns the currently shown text hover or <code>null</code> if no text + * hover is shown. + * + * @return the currently shown text hover or <code>null</code> + */ + protected ITextHover getCurrentTextHover() { + return fTextHover; + } + + /* + * @see dwtx.jface.text.AbstractHoverInformationControlManager#dispose() + * @since 3.0 + */ + public void dispose() { + if (fTextViewer !is null) { + fTextViewer.removeViewportListener(fViewportListener); + fViewportListener= null; + + StyledText st= fTextViewer.getTextWidget(); + if (st !is null && !st.isDisposed()) + st.removeMouseMoveListener(fMouseMoveListener); + fMouseMoveListener= null; + } + super.dispose(); + } +}