KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > TextViewerHoverManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jface.text;
12
13
14 import org.eclipse.swt.custom.StyledText;
15 import org.eclipse.swt.events.MouseEvent;
16 import org.eclipse.swt.events.MouseMoveListener;
17 import org.eclipse.swt.graphics.Point;
18 import org.eclipse.swt.graphics.Rectangle;
19 import org.eclipse.swt.widgets.Display;
20
21 import org.eclipse.core.runtime.ILog;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.Platform;
24 import org.eclipse.core.runtime.Status;
25
26
27 /**
28  * This manager controls the layout, content, and visibility of an information
29  * control in reaction to mouse hover events issued by the text widget of a
30  * text viewer. It overrides <code>computeInformation</code>, so that the
31  * computation is performed in a dedicated background thread. This implies
32  * that the used <code>ITextHover</code> objects must be capable of
33  * operating in a non-UI thread.
34  *
35  * @since 2.0
36  */

37 class TextViewerHoverManager extends AbstractHoverInformationControlManager implements IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
38
39
40     /**
41      * Priority of the hovers managed by this manager.
42      * Default value: <code>0</code>;
43      * @since 3.0
44      */

45     public final static int WIDGET_PRIORITY= 0;
46
47
48     /** The text viewer */
49     private TextViewer fTextViewer;
50     /** The hover information computation thread */
51     private Thread JavaDoc fThread;
52     /** The stopper of the computation thread */
53     private ITextListener fStopper;
54     /** Internal monitor */
55     private Object JavaDoc fMutex= new Object JavaDoc();
56     /** The currently shown text hover. */
57     private volatile ITextHover fTextHover;
58     /**
59      * Tells whether the next mouse hover event
60      * should be processed.
61      * @since 3.0
62      */

63     private boolean fProcessMouseHoverEvent= true;
64     /**
65      * Internal mouse move listener.
66      * @since 3.0
67      */

68     private MouseMoveListener fMouseMoveListener;
69     /**
70      * Internal view port listener.
71      * @since 3.0
72      */

73     private IViewportListener fViewportListener;
74
75
76     /**
77      * Creates a new text viewer hover manager specific for the given text viewer.
78      * The manager uses the given information control creator.
79      *
80      * @param textViewer the viewer for which the controller is created
81      * @param creator the information control creator
82      */

83     public TextViewerHoverManager(TextViewer textViewer, IInformationControlCreator creator) {
84         super(creator);
85         fTextViewer= textViewer;
86         fStopper= new ITextListener() {
87             public void textChanged(TextEvent event) {
88                 synchronized (fMutex) {
89                     if (fThread != null) {
90                         fThread.interrupt();
91                         fThread= null;
92                     }
93                 }
94             }
95         };
96         fViewportListener= new IViewportListener() {
97             /*
98              * @see org.eclipse.jface.text.IViewportListener#viewportChanged(int)
99              */

100             public void viewportChanged(int verticalOffset) {
101                 fProcessMouseHoverEvent= false;
102             }
103         };
104         fTextViewer.addViewportListener(fViewportListener);
105         fMouseMoveListener= new MouseMoveListener() {
106             /*
107              * @see MouseMoveListener#mouseMove(MouseEvent)
108              */

109             public void mouseMove(MouseEvent event) {
110                 fProcessMouseHoverEvent= true;
111             }
112         };
113         fTextViewer.getTextWidget().addMouseMoveListener(fMouseMoveListener);
114     }
115
116     /**
117      * Determines all necessary details and delegates the computation into
118      * a background thread.
119      */

120     protected void computeInformation() {
121
122         if (!fProcessMouseHoverEvent) {
123             setInformation(null, null);
124             return;
125         }
126
127         Point location= getHoverEventLocation();
128         int offset= computeOffsetAtLocation(location.x, location.y);
129         if (offset == -1) {
130             setInformation(null, null);
131             return;
132         }
133
134         final ITextHover hover= fTextViewer.getTextHover(offset, getHoverEventStateMask());
135         if (hover == null) {
136             setInformation(null, null);
137             return;
138         }
139
140         final IRegion region= hover.getHoverRegion(fTextViewer, offset);
141         if (region == null) {
142             setInformation(null, null);
143             return;
144         }
145
146         final Rectangle area= computeArea(region);
147         if (area == null || area.isEmpty()) {
148             setInformation(null, null);
149             return;
150         }
151
152         if (fThread != null) {
153             setInformation(null, null);
154             return;
155         }
156
157         fThread= new Thread JavaDoc("Text Viewer Hover Presenter") { //$NON-NLS-1$
158
public void run() {
159                 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=17693
160
boolean hasFinished= false;
161                 try {
162                     if (fThread != null) {
163                         String JavaDoc information;
164                         try {
165                             information= hover.getHoverInfo(fTextViewer, region);
166                         } catch (ArrayIndexOutOfBoundsException JavaDoc x) {
167                             /*
168                              * This code runs in a separate thread which can
169                              * lead to text offsets being out of bounds when
170                              * computing the hover info (see bug 32848).
171                              */

172                             information= null;
173                         }
174
175                         if (hover instanceof ITextHoverExtension)
176                             setCustomInformationControlCreator(((ITextHoverExtension) hover).getHoverControlCreator());
177                         else
178                             setCustomInformationControlCreator(null);
179
180                         setInformation(information, area);
181                         if (information != null)
182                             fTextHover= hover;
183                     } else {
184                         setInformation(null, null);
185                     }
186                     hasFinished= true;
187                 } catch (RuntimeException JavaDoc ex) {
188                     String JavaDoc PLUGIN_ID= "org.eclipse.jface.text"; //$NON-NLS-1$
189
ILog log= Platform.getLog(Platform.getBundle(PLUGIN_ID));
190                     log.log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, "Unexpected runtime error while computing a text hover", ex)); //$NON-NLS-1$
191
} finally {
192                     synchronized (fMutex) {
193                         if (fTextViewer != null)
194                             fTextViewer.removeTextListener(fStopper);
195                         fThread= null;
196                         // https://bugs.eclipse.org/bugs/show_bug.cgi?id=44756
197
if (!hasFinished)
198                             setInformation(null, null);
199                     }
200                 }
201             }
202         };
203
204         fThread.setDaemon(true);
205         fThread.setPriority(Thread.MIN_PRIORITY);
206         synchronized (fMutex) {
207             fTextViewer.addTextListener(fStopper);
208             fThread.start();
209         }
210     }
211
212     /**
213      * As computation is done in the background, this method is
214      * also called in the background thread. Delegates the control
215      * flow back into the UI thread, in order to allow displaying the
216      * information in the information control.
217      */

218     protected void presentInformation() {
219         if (fTextViewer == null)
220             return;
221
222         StyledText textWidget= fTextViewer.getTextWidget();
223         if (textWidget != null && !textWidget.isDisposed()) {
224             Display display= textWidget.getDisplay();
225             if (display == null)
226                 return;
227
228             display.asyncExec(new Runnable JavaDoc() {
229                 public void run() {
230                     doPresentInformation();
231                 }
232             });
233         }
234     }
235
236     /*
237      * @see AbstractInformationControlManager#presentInformation()
238      */

239     protected void doPresentInformation() {
240         super.presentInformation();
241     }
242
243     /**
244      * Computes the document offset underlying the given text widget coordinates.
245      * This method uses a linear search as it cannot make any assumption about
246      * how the document is actually presented in the widget. (Covers cases such
247      * as bidirectional text.)
248      *
249      * @param x the horizontal coordinate inside the text widget
250      * @param y the vertical coordinate inside the text widget
251      * @return the document offset corresponding to the given point
252      */

253     private int computeOffsetAtLocation(int x, int y) {
254
255         try {
256
257             StyledText styledText= fTextViewer.getTextWidget();
258             int widgetOffset= styledText.getOffsetAtLocation(new Point(x, y));
259             Point p= styledText.getLocationAtOffset(widgetOffset);
260             if (p.x > x)
261                 widgetOffset--;
262
263             if (fTextViewer instanceof ITextViewerExtension5) {
264                 ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
265                 return extension.widgetOffset2ModelOffset(widgetOffset);
266             }
267
268             return widgetOffset + fTextViewer._getVisibleRegionOffset();
269
270         } catch (IllegalArgumentException JavaDoc e) {
271             return -1;
272         }
273     }
274
275     /**
276      * Determines graphical area covered by the given text region.
277      *
278      * @param region the region whose graphical extend must be computed
279      * @return the graphical extend of the given region
280      */

281     private Rectangle computeArea(IRegion region) {
282
283         int start= 0;
284         int end= 0;
285         IRegion widgetRegion= modelRange2WidgetRange(region);
286         if (widgetRegion != null) {
287             start= widgetRegion.getOffset();
288             end= start + widgetRegion.getLength();
289         }
290
291         StyledText styledText= fTextViewer.getTextWidget();
292         Rectangle bounds;
293         if (end > 0 && start < end)
294             bounds= styledText.getTextBounds(start, end - 1);
295         else {
296             Point loc= styledText.getLocationAtOffset(start);
297             bounds= new Rectangle(loc.x, loc.y, fTextViewer.getAverageCharWidth(), styledText.getLineHeight(start));
298         }
299         
300         return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
301     }
302
303     /**
304      * Translates a given region of the text viewer's document into
305      * the corresponding region of the viewer's widget.
306      *
307      * @param region the document region
308      * @return the corresponding widget region
309      * @since 2.1
310      */

311     private IRegion modelRange2WidgetRange(IRegion region) {
312         if (fTextViewer instanceof ITextViewerExtension5) {
313             ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
314             return extension.modelRange2WidgetRange(region);
315         }
316
317         IRegion visibleRegion= fTextViewer.getVisibleRegion();
318         int start= region.getOffset() - visibleRegion.getOffset();
319         int end= start + region.getLength();
320         if (end > visibleRegion.getLength())
321             end= visibleRegion.getLength();
322
323         return new Region(start, end - start);
324     }
325
326     /*
327      * @see org.eclipse.jface.text.AbstractInformationControlManager#showInformationControl(org.eclipse.swt.graphics.Rectangle)
328      */

329     protected void showInformationControl(Rectangle subjectArea) {
330         if (fTextViewer != null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY))
331             super.showInformationControl(subjectArea);
332     }
333
334     /*
335      * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
336      */

337     protected void hideInformationControl() {
338         try {
339             fTextHover= null;
340             super.hideInformationControl();
341         } finally {
342             if (fTextViewer != null)
343                 fTextViewer.releaseWidgetToken(this);
344         }
345     }
346
347     /*
348      * @see org.eclipse.jface.text.AbstractInformationControlManager#handleInformationControlDisposed()
349      */

350     protected void handleInformationControlDisposed() {
351         try {
352             super.handleInformationControlDisposed();
353         } finally {
354             if (fTextViewer != null)
355                 fTextViewer.releaseWidgetToken(this);
356         }
357     }
358
359     /*
360      * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner)
361      */

362     public boolean requestWidgetToken(IWidgetTokenOwner owner) {
363         fTextHover= null;
364         super.hideInformationControl();
365         return true;
366     }
367
368     /*
369      * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner, int)
370      * @since 3.0
371      */

372     public boolean requestWidgetToken(IWidgetTokenOwner owner, int priority) {
373         if (priority > WIDGET_PRIORITY) {
374             fTextHover= null;
375             super.hideInformationControl();
376             return true;
377         }
378         return false;
379     }
380
381     /*
382      * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
383      * @since 3.0
384      */

385     public boolean setFocus(IWidgetTokenOwner owner) {
386         return false;
387     }
388
389     /**
390      * Returns the currently shown text hover or <code>null</code> if no text
391      * hover is shown.
392      *
393      * @return the currently shown text hover or <code>null</code>
394      */

395     protected ITextHover getCurrentTextHover() {
396         return fTextHover;
397     }
398
399     /*
400      * @see org.eclipse.jface.text.AbstractHoverInformationControlManager#dispose()
401      * @since 3.0
402      */

403     public void dispose() {
404         if (fTextViewer != null) {
405             fTextViewer.removeViewportListener(fViewportListener);
406             fViewportListener= null;
407
408             StyledText st= fTextViewer.getTextWidget();
409             if (st != null && !st.isDisposed())
410                 st.removeMouseMoveListener(fMouseMoveListener);
411             fMouseMoveListener= null;
412         }
413         super.dispose();
414     }
415 }
416
Popular Tags