1 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 37 class TextViewerHoverManager extends AbstractHoverInformationControlManager implements IWidgetTokenKeeper, IWidgetTokenKeeperExtension { 38 39 40 45 public final static int WIDGET_PRIORITY= 0; 46 47 48 49 private TextViewer fTextViewer; 50 51 private Thread fThread; 52 53 private ITextListener fStopper; 54 55 private Object fMutex= new Object (); 56 57 private volatile ITextHover fTextHover; 58 63 private boolean fProcessMouseHoverEvent= true; 64 68 private MouseMoveListener fMouseMoveListener; 69 73 private IViewportListener fViewportListener; 74 75 76 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 100 public void viewportChanged(int verticalOffset) { 101 fProcessMouseHoverEvent= false; 102 } 103 }; 104 fTextViewer.addViewportListener(fViewportListener); 105 fMouseMoveListener= new MouseMoveListener() { 106 109 public void mouseMove(MouseEvent event) { 110 fProcessMouseHoverEvent= true; 111 } 112 }; 113 fTextViewer.getTextWidget().addMouseMoveListener(fMouseMoveListener); 114 } 115 116 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 ("Text Viewer Hover Presenter") { public void run() { 159 boolean hasFinished= false; 161 try { 162 if (fThread != null) { 163 String information; 164 try { 165 information= hover.getHoverInfo(fTextViewer, region); 166 } catch (ArrayIndexOutOfBoundsException x) { 167 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 ex) { 188 String PLUGIN_ID= "org.eclipse.jface.text"; 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)); } finally { 192 synchronized (fMutex) { 193 if (fTextViewer != null) 194 fTextViewer.removeTextListener(fStopper); 195 fThread= null; 196 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 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 () { 229 public void run() { 230 doPresentInformation(); 231 } 232 }); 233 } 234 } 235 236 239 protected void doPresentInformation() { 240 super.presentInformation(); 241 } 242 243 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 e) { 271 return -1; 272 } 273 } 274 275 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 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 329 protected void showInformationControl(Rectangle subjectArea) { 330 if (fTextViewer != null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY)) 331 super.showInformationControl(subjectArea); 332 } 333 334 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 350 protected void handleInformationControlDisposed() { 351 try { 352 super.handleInformationControlDisposed(); 353 } finally { 354 if (fTextViewer != null) 355 fTextViewer.releaseWidgetToken(this); 356 } 357 } 358 359 362 public boolean requestWidgetToken(IWidgetTokenOwner owner) { 363 fTextHover= null; 364 super.hideInformationControl(); 365 return true; 366 } 367 368 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 385 public boolean setFocus(IWidgetTokenOwner owner) { 386 return false; 387 } 388 389 395 protected ITextHover getCurrentTextHover() { 396 return fTextHover; 397 } 398 399 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 |