Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java')
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java300
1 files changed, 300 insertions, 0 deletions
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java
new file mode 100644
index 00000000000..426bdb67db8
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewerHoverManager.java
@@ -0,0 +1,300 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+
+/**
+ * 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 extends AbstractHoverInformationControlManager implements IWidgetTokenKeeper {
+
+ /** 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();
+
+
+ /**
+ * 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 != null) {
+ fThread.interrupt();
+ fThread= null;
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Determines all necessary details and delegates the computation into
+ * a background thread.
+ */
+ protected void computeInformation() {
+
+ Point location= getHoverEventLocation();
+ int offset= computeOffsetAtLocation(location.x, location.y);
+ if (offset == -1) {
+ setInformation(null, null);
+ return;
+ }
+
+ final ITextHover hover= fTextViewer.getTextHover(offset);
+ if (hover == null) {
+ setInformation(null, null);
+ return;
+ }
+
+ final IRegion region= hover.getHoverRegion(fTextViewer, offset);
+ if (region == null) {
+ setInformation(null, null);
+ return;
+ }
+
+ final Rectangle area= computeArea(region);
+ if (area == null || area.isEmpty()) {
+ setInformation(null, null);
+ return;
+ }
+
+ if (fThread != null) {
+ setInformation(null, null);
+ return;
+ }
+
+ fThread= new Thread() {
+ public void run() {
+ // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17693
+ try {
+
+ if (fThread != null) {
+ String information= hover.getHoverInfo(fTextViewer, region);
+ setInformation(information, area);
+ } else {
+ setInformation(null, null);
+ }
+
+ } finally {
+ synchronized (fMutex) {
+ if (fTextViewer != null)
+ fTextViewer.removeTextListener(fStopper);
+ fThread= 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 == null)
+ return;
+
+ StyledText textWidget= fTextViewer.getTextWidget();
+ if (textWidget != null && !textWidget.isDisposed()) {
+ Display display= textWidget.getDisplay();
+ if (display == 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 bidi text.)
+ *
+ * @param x the x coordinate inside the text widget
+ * @param y the y coordinate inside the text widget
+ * @return the document offset corresponding to the given point
+ */
+ private int computeOffsetAtLocation(int x, int y) {
+
+ StyledText styledText= fTextViewer.getTextWidget();
+ IDocument document= fTextViewer.getVisibleDocument();
+
+ if (document == null)
+ return -1;
+
+ int line= (y + styledText.getTopPixel()) / styledText.getLineHeight();
+ int lineCount= document.getNumberOfLines();
+
+ if (line > lineCount - 1)
+ line= lineCount - 1;
+
+ if (line < 0)
+ line= 0;
+
+ try {
+
+ IRegion lineInfo= document.getLineInformation(line);
+ int low= lineInfo.getOffset();
+ int high= low + lineInfo.getLength();
+
+ int lookup= styledText.getLocationAtOffset(low).x;
+ int guess= low;
+ int guessDelta= Math.abs(lookup - x);
+
+ for (int i= low + 1; i < high; i++) {
+ lookup= styledText.getLocationAtOffset(i).x;
+ int delta= Math.abs(lookup - x);
+ if (delta < guessDelta) {
+ guess= i;
+ guessDelta= delta;
+ }
+ }
+
+ return guess + fTextViewer.getVisibleRegionOffset();
+
+ } catch (BadLocationException e) {
+ }
+
+ return -1;
+ }
+
+ /**
+ * Determines graphical area covered by the given text region.
+ *
+ * @param region the region whose graphical extend must be computed
+ * @return the graphical extend of the given region
+ */
+ private Rectangle computeArea(IRegion region) {
+
+ StyledText styledText= fTextViewer.getTextWidget();
+
+ IRegion visibleRegion= fTextViewer.getVisibleRegion();
+ int start= region.getOffset() - visibleRegion.getOffset();
+ int end= start + region.getLength();
+ if (end > visibleRegion.getLength())
+ end= visibleRegion.getLength();
+
+ Point upperLeft= styledText.getLocationAtOffset(start);
+ Point lowerRight= new Point(upperLeft.x, upperLeft.y);
+
+ for (int i= start +1; i < end; i++) {
+
+ Point p= styledText.getLocationAtOffset(i);
+
+ if (upperLeft.x > p.x)
+ upperLeft.x= p.x;
+
+ if (upperLeft.y > p.y)
+ upperLeft.y= p.y;
+
+ if (lowerRight.x < p.x)
+ lowerRight.x= p.x;
+
+ if (lowerRight.y < p.y)
+ lowerRight.y= p.y;
+ }
+
+ lowerRight.x += fTextViewer.getAverageCharWidth();
+ lowerRight.y += styledText.getLineHeight();
+
+ int width= lowerRight.x - upperLeft.x;
+ int height= lowerRight.y - upperLeft.y;
+ return new Rectangle(upperLeft.x, upperLeft.y, width, height);
+ }
+
+ /*
+ * @see AbstractInformationControlManager#showInformationControl(Rectangle)
+ */
+ protected void showInformationControl(Rectangle subjectArea) {
+ if (fTextViewer != null && fTextViewer.requestWidgetToken(this))
+ super.showInformationControl(subjectArea);
+ }
+
+ /*
+ * @see AbstractInformationControlManager#hideInformationControl()
+ */
+ protected void hideInformationControl() {
+ try {
+ super.hideInformationControl();
+ } finally {
+ if (fTextViewer != null)
+ fTextViewer.releaseWidgetToken(this);
+ }
+ }
+
+ /*
+ * @see AbstractInformationControlManager#handleInformationControlDisposed()
+ */
+ protected void handleInformationControlDisposed() {
+ try {
+ super.handleInformationControlDisposed();
+ } finally {
+ if (fTextViewer != null)
+ fTextViewer.releaseWidgetToken(this);
+ }
+ }
+
+ /*
+ * @see IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+ */
+ public boolean requestWidgetToken(IWidgetTokenOwner owner) {
+ super.hideInformationControl();
+ return true;
+ }
+}
+

Back to the top