comparison org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children 6f068362a363
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
1 /*******************************************************************************
2 * Copyright (c) 2000, 2008 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.jface.text.AbstractHoverInformationControlManager;
14
15 import org.eclipse.jface.text.IDocumentPartitioningListener; // packageimport
16 import org.eclipse.jface.text.DefaultTextHover; // packageimport
17 import org.eclipse.jface.text.AbstractInformationControl; // packageimport
18 import org.eclipse.jface.text.TextUtilities; // packageimport
19 import org.eclipse.jface.text.IInformationControlCreatorExtension; // packageimport
20 import org.eclipse.jface.text.AbstractInformationControlManager; // packageimport
21 import org.eclipse.jface.text.ITextViewerExtension2; // packageimport
22 import org.eclipse.jface.text.IDocumentPartitioner; // packageimport
23 import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
24 import org.eclipse.jface.text.ITextSelection; // packageimport
25 import org.eclipse.jface.text.Document; // packageimport
26 import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
27 import org.eclipse.jface.text.ITextListener; // packageimport
28 import org.eclipse.jface.text.BadPartitioningException; // packageimport
29 import org.eclipse.jface.text.ITextViewerExtension5; // packageimport
30 import org.eclipse.jface.text.IDocumentPartitionerExtension3; // packageimport
31 import org.eclipse.jface.text.IUndoManager; // packageimport
32 import org.eclipse.jface.text.ITextHoverExtension2; // packageimport
33 import org.eclipse.jface.text.IRepairableDocument; // packageimport
34 import org.eclipse.jface.text.IRewriteTarget; // packageimport
35 import org.eclipse.jface.text.DefaultPositionUpdater; // packageimport
36 import org.eclipse.jface.text.RewriteSessionEditProcessor; // packageimport
37 import org.eclipse.jface.text.TextViewerHoverManager; // packageimport
38 import org.eclipse.jface.text.DocumentRewriteSession; // packageimport
39 import org.eclipse.jface.text.TextViewer; // packageimport
40 import org.eclipse.jface.text.ITextViewerExtension8; // packageimport
41 import org.eclipse.jface.text.RegExMessages; // packageimport
42 import org.eclipse.jface.text.IDelayedInputChangeProvider; // packageimport
43 import org.eclipse.jface.text.ITextOperationTargetExtension; // packageimport
44 import org.eclipse.jface.text.IWidgetTokenOwner; // packageimport
45 import org.eclipse.jface.text.IViewportListener; // packageimport
46 import org.eclipse.jface.text.GapTextStore; // packageimport
47 import org.eclipse.jface.text.MarkSelection; // packageimport
48 import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; // packageimport
49 import org.eclipse.jface.text.IDocumentAdapterExtension; // packageimport
50 import org.eclipse.jface.text.IInformationControlExtension; // packageimport
51 import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
52 import org.eclipse.jface.text.DefaultDocumentAdapter; // packageimport
53 import org.eclipse.jface.text.ITextViewerExtension3; // packageimport
54 import org.eclipse.jface.text.IInformationControlCreator; // packageimport
55 import org.eclipse.jface.text.TypedRegion; // packageimport
56 import org.eclipse.jface.text.ISynchronizable; // packageimport
57 import org.eclipse.jface.text.IMarkRegionTarget; // packageimport
58 import org.eclipse.jface.text.TextViewerUndoManager; // packageimport
59 import org.eclipse.jface.text.IRegion; // packageimport
60 import org.eclipse.jface.text.IInformationControlExtension2; // packageimport
61 import org.eclipse.jface.text.IDocumentExtension4; // packageimport
62 import org.eclipse.jface.text.IDocumentExtension2; // packageimport
63 import org.eclipse.jface.text.IDocumentPartitionerExtension2; // packageimport
64 import org.eclipse.jface.text.DefaultInformationControl; // packageimport
65 import org.eclipse.jface.text.IWidgetTokenOwnerExtension; // packageimport
66 import org.eclipse.jface.text.DocumentClone; // packageimport
67 import org.eclipse.jface.text.DefaultUndoManager; // packageimport
68 import org.eclipse.jface.text.IFindReplaceTarget; // packageimport
69 import org.eclipse.jface.text.IAutoEditStrategy; // packageimport
70 import org.eclipse.jface.text.ILineTrackerExtension; // packageimport
71 import org.eclipse.jface.text.IUndoManagerExtension; // packageimport
72 import org.eclipse.jface.text.TextSelection; // packageimport
73 import org.eclipse.jface.text.DefaultAutoIndentStrategy; // packageimport
74 import org.eclipse.jface.text.IAutoIndentStrategy; // packageimport
75 import org.eclipse.jface.text.IPainter; // packageimport
76 import org.eclipse.jface.text.IInformationControl; // packageimport
77 import org.eclipse.jface.text.IInformationControlExtension3; // packageimport
78 import org.eclipse.jface.text.ITextViewerExtension6; // packageimport
79 import org.eclipse.jface.text.IInformationControlExtension4; // packageimport
80 import org.eclipse.jface.text.DefaultLineTracker; // packageimport
81 import org.eclipse.jface.text.IDocumentInformationMappingExtension; // packageimport
82 import org.eclipse.jface.text.IRepairableDocumentExtension; // packageimport
83 import org.eclipse.jface.text.ITextHover; // packageimport
84 import org.eclipse.jface.text.FindReplaceDocumentAdapter; // packageimport
85 import org.eclipse.jface.text.ILineTracker; // packageimport
86 import org.eclipse.jface.text.Line; // packageimport
87 import org.eclipse.jface.text.ITextViewerExtension; // packageimport
88 import org.eclipse.jface.text.IDocumentAdapter; // packageimport
89 import org.eclipse.jface.text.TextEvent; // packageimport
90 import org.eclipse.jface.text.BadLocationException; // packageimport
91 import org.eclipse.jface.text.AbstractDocument; // packageimport
92 import org.eclipse.jface.text.AbstractLineTracker; // packageimport
93 import org.eclipse.jface.text.TreeLineTracker; // packageimport
94 import org.eclipse.jface.text.ITextPresentationListener; // packageimport
95 import org.eclipse.jface.text.Region; // packageimport
96 import org.eclipse.jface.text.ITextViewer; // packageimport
97 import org.eclipse.jface.text.IDocumentInformationMapping; // packageimport
98 import org.eclipse.jface.text.MarginPainter; // packageimport
99 import org.eclipse.jface.text.IPaintPositionManager; // packageimport
100 import org.eclipse.jface.text.TextPresentation; // packageimport
101 import org.eclipse.jface.text.IFindReplaceTargetExtension; // packageimport
102 import org.eclipse.jface.text.ISlaveDocumentManagerExtension; // packageimport
103 import org.eclipse.jface.text.ISelectionValidator; // packageimport
104 import org.eclipse.jface.text.IDocumentExtension; // packageimport
105 import org.eclipse.jface.text.PropagatingFontFieldEditor; // packageimport
106 import org.eclipse.jface.text.ConfigurableLineTracker; // packageimport
107 import org.eclipse.jface.text.SlaveDocumentEvent; // packageimport
108 import org.eclipse.jface.text.IDocumentListener; // packageimport
109 import org.eclipse.jface.text.PaintManager; // packageimport
110 import org.eclipse.jface.text.IFindReplaceTargetExtension3; // packageimport
111 import org.eclipse.jface.text.ITextDoubleClickStrategy; // packageimport
112 import org.eclipse.jface.text.IDocumentExtension3; // packageimport
113 import org.eclipse.jface.text.Position; // packageimport
114 import org.eclipse.jface.text.TextMessages; // packageimport
115 import org.eclipse.jface.text.CopyOnWriteTextStore; // packageimport
116 import org.eclipse.jface.text.WhitespaceCharacterPainter; // packageimport
117 import org.eclipse.jface.text.IPositionUpdater; // packageimport
118 import org.eclipse.jface.text.DefaultTextDoubleClickStrategy; // packageimport
119 import org.eclipse.jface.text.ListLineTracker; // packageimport
120 import org.eclipse.jface.text.ITextInputListener; // packageimport
121 import org.eclipse.jface.text.BadPositionCategoryException; // packageimport
122 import org.eclipse.jface.text.IWidgetTokenKeeperExtension; // packageimport
123 import org.eclipse.jface.text.IInputChangedListener; // packageimport
124 import org.eclipse.jface.text.ITextOperationTarget; // packageimport
125 import org.eclipse.jface.text.IDocumentInformationMappingExtension2; // packageimport
126 import org.eclipse.jface.text.ITextViewerExtension7; // packageimport
127 import org.eclipse.jface.text.IInformationControlExtension5; // packageimport
128 import org.eclipse.jface.text.IDocumentRewriteSessionListener; // packageimport
129 import org.eclipse.jface.text.JFaceTextUtil; // packageimport
130 import org.eclipse.jface.text.AbstractReusableInformationControlCreator; // packageimport
131 import org.eclipse.jface.text.TabsToSpacesConverter; // packageimport
132 import org.eclipse.jface.text.CursorLinePainter; // packageimport
133 import org.eclipse.jface.text.ITextHoverExtension; // packageimport
134 import org.eclipse.jface.text.IEventConsumer; // packageimport
135 import org.eclipse.jface.text.IDocument; // packageimport
136 import org.eclipse.jface.text.IWidgetTokenKeeper; // packageimport
137 import org.eclipse.jface.text.DocumentCommand; // packageimport
138 import org.eclipse.jface.text.TypedPosition; // packageimport
139 import org.eclipse.jface.text.IEditingSupportRegistry; // packageimport
140 import org.eclipse.jface.text.IDocumentPartitionerExtension; // packageimport
141 import org.eclipse.jface.text.IEditingSupport; // packageimport
142 import org.eclipse.jface.text.IMarkSelection; // packageimport
143 import org.eclipse.jface.text.ISlaveDocumentManager; // packageimport
144 import org.eclipse.jface.text.DocumentEvent; // packageimport
145 import org.eclipse.jface.text.DocumentPartitioningChangedEvent; // packageimport
146 import org.eclipse.jface.text.ITextStore; // packageimport
147 import org.eclipse.jface.text.JFaceTextMessages; // packageimport
148 import org.eclipse.jface.text.DocumentRewriteSessionEvent; // packageimport
149 import org.eclipse.jface.text.SequentialRewriteTextStore; // packageimport
150 import org.eclipse.jface.text.DocumentRewriteSessionType; // packageimport
151 import org.eclipse.jface.text.TextAttribute; // packageimport
152 import org.eclipse.jface.text.ITextViewerExtension4; // packageimport
153 import org.eclipse.jface.text.ITypedRegion; // packageimport
154
155 import java.lang.all;
156 import java.util.Set;
157 import tango.text.convert.Format;
158
159 import org.eclipse.swt.SWT;
160 import org.eclipse.swt.events.ControlEvent;
161 import org.eclipse.swt.events.ControlListener;
162 import org.eclipse.swt.events.KeyEvent;
163 import org.eclipse.swt.events.KeyListener;
164 import org.eclipse.swt.events.MouseEvent;
165 import org.eclipse.swt.events.MouseListener;
166 import org.eclipse.swt.events.MouseMoveListener;
167 import org.eclipse.swt.events.MouseTrackListener;
168 import org.eclipse.swt.events.SelectionEvent;
169 import org.eclipse.swt.events.SelectionListener;
170 import org.eclipse.swt.events.ShellAdapter;
171 import org.eclipse.swt.events.ShellEvent;
172 import org.eclipse.swt.graphics.Point;
173 import org.eclipse.swt.graphics.Rectangle;
174 import org.eclipse.swt.widgets.Control;
175 import org.eclipse.swt.widgets.Display;
176 import org.eclipse.swt.widgets.Event;
177 import org.eclipse.swt.widgets.Listener;
178 import org.eclipse.swt.widgets.ScrollBar;
179 import org.eclipse.swt.widgets.Scrollable;
180 import org.eclipse.core.runtime.Assert;
181 import org.eclipse.core.runtime.IProgressMonitor;
182 import org.eclipse.core.runtime.IStatus;
183 import org.eclipse.core.runtime.Status;
184 import org.eclipse.core.runtime.jobs.Job;
185 import org.eclipse.jface.internal.text.DelayedInputChangeListener;
186 import org.eclipse.jface.internal.text.InformationControlReplacer;
187 import org.eclipse.jface.internal.text.InternalAccessor;
188 import org.eclipse.jface.text.ITextViewerExtension8;
189 import org.eclipse.jface.text.source.AnnotationBarHoverManager;
190 import org.eclipse.jface.util.Geometry;
191
192
193 /**
194 * An information control manager that shows information in response to mouse
195 * hover events. The mouse hover events are caught by registering a
196 * {@link org.eclipse.swt.events.MouseTrackListener} on the manager's subject
197 * control. The manager has by default an information control closer that closes
198 * the information control as soon as the mouse pointer leaves the subject area,
199 * the user presses a key, or the subject control is resized, moved, or
200 * deactivated.
201 * <p>
202 * When being activated by a mouse hover event, the manager disables itself,
203 * until the mouse leaves the subject area. Thus, the manager is usually still
204 * disabled, when the information control has already been closed by the closer.
205 *
206 * @see org.eclipse.swt.events.MouseTrackListener
207 * @since 2.0
208 */
209 abstract public class AbstractHoverInformationControlManager : AbstractInformationControlManager {
210
211 /**
212 * The information control closer for the hover information. Closes the information control as
213 * soon as the mouse pointer leaves the subject area (unless "move into hover" is enabled),
214 * a mouse button is pressed, the user presses a key, or the subject control is resized, moved, or loses focus.
215 */
216 class Closer : IInformationControlCloser, MouseListener, MouseMoveListener, ControlListener, KeyListener, SelectionListener, Listener {
217
218 /** The closer's subject control */
219 private Control fSubjectControl;
220 /** The subject area */
221 private Rectangle fSubjectArea;
222 /** Indicates whether this closer is active */
223 private bool fIsActive= false;
224 /**
225 * The cached display.
226 * @since 3.1
227 */
228 private Display fDisplay;
229
230
231 /**
232 * Creates a new information control closer.
233 */
234 public this() {
235 }
236
237 /*
238 * @see IInformationControlCloser#setSubjectControl(Control)
239 */
240 public void setSubjectControl(Control control) {
241 fSubjectControl= control;
242 }
243
244 /*
245 * @see IInformationControlCloser#setHoverControl(IHoverControl)
246 */
247 public void setInformationControl(IInformationControl control) {
248 // NOTE: we use getCurrentInformationControl() from the outer class
249 }
250
251 /*
252 * @see IInformationControlCloser#start(Rectangle)
253 */
254 public void start(Rectangle subjectArea) {
255
256 if (fIsActive)
257 return;
258 fIsActive= true;
259 fWaitForMouseUp= false;
260
261 fSubjectArea= subjectArea;
262
263 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
264 fSubjectControl.addMouseListener(this);
265 fSubjectControl.addMouseMoveListener(this);
266 fSubjectControl.addControlListener(this);
267 fSubjectControl.addKeyListener(this);
268 if ( cast(Scrollable)fSubjectControl ) {
269 Scrollable scrollable= cast(Scrollable) fSubjectControl;
270 ScrollBar vBar= scrollable.getVerticalBar();
271 if (vBar !is null)
272 vBar.addSelectionListener(this);
273 ScrollBar hBar= scrollable.getHorizontalBar();
274 if (hBar !is null)
275 hBar.addSelectionListener(this);
276 }
277
278 fDisplay= fSubjectControl.getDisplay();
279 if (!fDisplay.isDisposed()) {
280 fDisplay.addFilter(SWT.Activate, this);
281 fDisplay.addFilter(SWT.MouseWheel, this);
282
283 fDisplay.addFilter(SWT.FocusOut, this);
284
285 fDisplay.addFilter(SWT.MouseDown, this);
286 fDisplay.addFilter(SWT.MouseUp, this);
287
288 fDisplay.addFilter(SWT.MouseMove, this);
289 fDisplay.addFilter(SWT.MouseEnter, this);
290 fDisplay.addFilter(SWT.MouseExit, this);
291 }
292 }
293 }
294
295 /*
296 * @see IInformationControlCloser#stop()
297 */
298 public void stop() {
299 if (!fIsActive)
300 return;
301
302 fIsActive= false;
303
304 if (DEBUG)
305 System.out_.println("AbstractHoverInformationControlManager.Closer stopped"); //$NON-NLS-1$
306
307 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
308 fSubjectControl.removeMouseListener(this);
309 fSubjectControl.removeMouseMoveListener(this);
310 fSubjectControl.removeControlListener(this);
311 fSubjectControl.removeKeyListener(this);
312 if ( cast(Scrollable)fSubjectControl ) {
313 Scrollable scrollable= cast(Scrollable) fSubjectControl;
314 ScrollBar vBar= scrollable.getVerticalBar();
315 if (vBar !is null)
316 vBar.removeSelectionListener(this);
317 ScrollBar hBar= scrollable.getHorizontalBar();
318 if (hBar !is null)
319 hBar.removeSelectionListener(this);
320 }
321 }
322
323 if (fDisplay !is null && !fDisplay.isDisposed()) {
324 fDisplay.removeFilter(SWT.Activate, this);
325 fDisplay.removeFilter(SWT.MouseWheel, this);
326
327 fDisplay.removeFilter(SWT.FocusOut, this);
328
329 fDisplay.removeFilter(SWT.MouseDown, this);
330 fDisplay.removeFilter(SWT.MouseUp, this);
331
332 fDisplay.removeFilter(SWT.MouseMove, this);
333 fDisplay.removeFilter(SWT.MouseEnter, this);
334 fDisplay.removeFilter(SWT.MouseExit, this);
335 }
336 fDisplay= null;
337 }
338
339 /*
340 * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
341 */
342 public void mouseMove(MouseEvent event) {
343 if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(getCurrentInformationControl())) {
344 if (!fSubjectArea.contains(event.x, event.y)) {
345 hideInformationControl();
346 }
347
348 } else if (getCurrentInformationControl() !is null && !getCurrentInformationControl().isFocusControl()) {
349 if (!inKeepUpZone(event.x, event.y, fSubjectControl, fSubjectArea, true)) {
350 hideInformationControl();
351 }
352 }
353 }
354
355 /*
356 * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
357 */
358 public void mouseUp(MouseEvent event) {
359 }
360
361 /*
362 * @see MouseListener#mouseDown(MouseEvent)
363 */
364 public void mouseDown(MouseEvent event) {
365 hideInformationControl();
366 }
367
368 /*
369 * @see MouseListener#mouseDoubleClick(MouseEvent)
370 */
371 public void mouseDoubleClick(MouseEvent event) {
372 hideInformationControl();
373 }
374
375 /*
376 * @see ControlListener#controlResized(ControlEvent)
377 */
378 public void controlResized(ControlEvent event) {
379 hideInformationControl();
380 }
381
382 /*
383 * @see ControlListener#controlMoved(ControlEvent)
384 */
385 public void controlMoved(ControlEvent event) {
386 hideInformationControl();
387 }
388
389 /*
390 * @see KeyListener#keyReleased(KeyEvent)
391 */
392 public void keyReleased(KeyEvent event) {
393 }
394
395 /*
396 * @see KeyListener#keyPressed(KeyEvent)
397 */
398 public void keyPressed(KeyEvent event) {
399 hideInformationControl();
400 }
401
402 /*
403 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
404 */
405 public void widgetSelected(SelectionEvent e) {
406 hideInformationControl();
407 }
408
409 /*
410 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
411 */
412 public void widgetDefaultSelected(SelectionEvent e) {
413 }
414
415 /*
416 * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
417 * @since 3.1
418 */
419 public void handleEvent(Event event) {
420 switch (event.type) {
421 case SWT.Activate:
422 case SWT.MouseWheel:
423 if (!hasInformationControlReplacer())
424 hideInformationControl();
425 else if (!isReplaceInProgress()) {
426 IInformationControl infoControl= getCurrentInformationControl();
427 // During isReplaceInProgress(), events can come from the replacing information control
428 if ( cast(Control)event.widget && cast(IInformationControlExtension5)infoControl ) {
429 Control control= cast(Control) event.widget;
430 IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) infoControl;
431 if (!(iControl5.containsControl(control)))
432 hideInformationControl();
433 else if (event.type is SWT.MouseWheel && cancelReplacingDelay())
434 replaceInformationControl(false);
435 } else if (infoControl !is null && infoControl.isFocusControl() && cancelReplacingDelay()) {
436 replaceInformationControl(true);
437 }
438 }
439 break;
440
441 case SWT.MouseUp:
442 case SWT.MouseDown:
443 if (!hasInformationControlReplacer())
444 hideInformationControl();
445 else if (!isReplaceInProgress()) {
446 IInformationControl infoControl= getCurrentInformationControl();
447 if ( cast(Control)event.widget && cast(IInformationControlExtension5)infoControl ) {
448 Control control= cast(Control) event.widget;
449 final IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) infoControl;
450 if (!(iControl5.containsControl(control))) {
451 hideInformationControl();
452 } else if (cancelReplacingDelay()) {
453 if (event.type is SWT.MouseUp) {
454 stop(); // avoid that someone else replaces the info control before the async is exec'd
455 if ( cast(IDelayedInputChangeProvider)infoControl ) {
456 final IDelayedInputChangeProvider delayedICP= cast(IDelayedInputChangeProvider) infoControl;
457 final IInputChangedListener inputChangeListener= new DelayedInputChangeListener(delayedICP, getInformationControlReplacer());
458 delayedICP.setDelayedInputChangeListener(inputChangeListener);
459 // cancel automatic input updating after a small timeout:
460 control.getShell().getDisplay().timerExec(1000, new class() Runnable {
461 public void run() {
462 delayedICP.setDelayedInputChangeListener(null);
463 }
464 });
465 }
466
467 // XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=212392 :
468 control.getShell().getDisplay().asyncExec(new class() Runnable {
469 public void run() {
470 replaceInformationControl(true);
471 }
472 });
473 } else {
474 fWaitForMouseUp= true;
475 }
476 }
477 } else {
478 handleMouseMove(event);
479 }
480 }
481 break;
482
483 case SWT.FocusOut:
484 IInformationControl iControl= getCurrentInformationControl();
485 if (iControl !is null && ! iControl.isFocusControl())
486 hideInformationControl();
487 break;
488
489 case SWT.MouseMove:
490 case SWT.MouseEnter:
491 case SWT.MouseExit:
492 handleMouseMove(event);
493 break;
494 default:
495 }
496 }
497
498 /**
499 * Handle mouse movement events.
500 *
501 * @param event the event
502 * @since 3.4
503 */
504 private void handleMouseMove(Event event) {
505 // if (DEBUG)
506 // System.out_.println("AbstractHoverInformationControl.Closer.handleMouseMove():" + event); //$NON-NLS-1$
507
508 if (!( cast(Control)event.widget ))
509 return;
510 Control eventControl= cast(Control) event.widget;
511
512 //transform coordinates to subject control:
513 Point mouseLoc= event.display.map(eventControl, fSubjectControl, event.x, event.y);
514
515 if (fSubjectArea.contains(mouseLoc))
516 return;
517
518 IInformationControl iControl= getCurrentInformationControl();
519 if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(iControl)) {
520 if ( cast(AnnotationBarHoverManager)this.outer ) {
521 if (getInternalAccessor().getAllowMouseExit())
522 return;
523 }
524 hideInformationControl();
525 return;
526 }
527
528 IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) iControl;
529 Rectangle controlBounds= iControl3.getBounds();
530 if (controlBounds !is null) {
531 Rectangle tooltipBounds= event.display.map(null, eventControl, controlBounds);
532 if (tooltipBounds.contains(event.x, event.y)) {
533 if (!isReplaceInProgress() && event.type !is SWT.MouseExit)
534 startReplaceInformationControl(event.display);
535 return;
536 }
537 cancelReplacingDelay();
538 }
539
540 if (!fSubjectControl.getBounds().contains(mouseLoc)) {
541 /*
542 * Use inKeepUpZone() to make sure it also works when the hover is
543 * completely outside of the subject control.
544 */
545 if (!inKeepUpZone(mouseLoc.x, mouseLoc.y, fSubjectControl, fSubjectArea, true)) {
546 hideInformationControl();
547 return;
548 }
549 }
550 }
551 }
552
553 /**
554 * To be installed on the manager's subject control. Serves two different purposes:
555 * <ul>
556 * <li> start function: initiates the computation of the information to be presented. This happens on
557 * receipt of a mouse hover event and disables the information control manager,
558 * <li> restart function: tracks mouse move and shell activation event to determine when the information
559 * control manager needs to be reactivated.
560 * </ul>
561 */
562 class MouseTracker : ShellAdapter , MouseTrackListener, MouseMoveListener {
563
564 /** Margin around the original hover event location for computing the hover area. */
565 private const static int EPSILON= 3;
566
567 /** The area in which the original hover event occurred. */
568 private Rectangle fHoverArea;
569 /** The area for which is computed information is valid. */
570 private Rectangle fSubjectArea;
571 /** The tracker's subject control. */
572 private Control fSubjectControl;
573
574 /** Indicates whether the tracker is in restart mode ignoring hover events. */
575 private bool fIsInRestartMode= false;
576 /** Indicates whether the tracker is computing the information to be presented. */
577 private bool fIsComputing= false;
578 /** Indicates whether the mouse has been lost. */
579 private bool fMouseLostWhileComputing= false;
580 /** Indicates whether the subject control's shell has been deactivated. */
581 private bool fShellDeactivatedWhileComputing= false;
582
583 /**
584 * Creates a new mouse tracker.
585 */
586 public this() {
587 }
588
589 /**
590 * Sets this mouse tracker's subject area, the area to be tracked in order
591 * to re-enable the information control manager.
592 *
593 * @param subjectArea the subject area
594 */
595 public void setSubjectArea(Rectangle subjectArea) {
596 Assert.isNotNull(subjectArea);
597 fSubjectArea= subjectArea;
598 }
599
600 /**
601 * Starts this mouse tracker. The given control becomes this tracker's subject control.
602 * Installs itself as mouse track listener on the subject control.
603 *
604 * @param subjectControl the subject control
605 */
606 public void start(Control subjectControl) {
607 fSubjectControl= subjectControl;
608 if (fSubjectControl !is null && !fSubjectControl.isDisposed())
609 fSubjectControl.addMouseTrackListener(this);
610
611 fIsInRestartMode= false;
612 fIsComputing= false;
613 fMouseLostWhileComputing= false;
614 fShellDeactivatedWhileComputing= false;
615 }
616
617 /**
618 * Stops this mouse tracker. Removes itself as mouse track, mouse move, and
619 * shell listener from the subject control.
620 */
621 public void stop() {
622 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
623 fSubjectControl.removeMouseTrackListener(this);
624 fSubjectControl.removeMouseMoveListener(this);
625 fSubjectControl.getShell().removeShellListener(this);
626 }
627 }
628
629 /**
630 * Initiates the computation of the information to be presented. Sets the initial hover area
631 * to a small rectangle around the hover event location. Adds mouse move and shell activation listeners
632 * to track whether the computed information is, after completion, useful for presentation and to
633 * implement the restart function.
634 *
635 * @param event the mouse hover event
636 */
637 public void mouseHover(MouseEvent event) {
638 if (fIsComputing || fIsInRestartMode ||
639 (fSubjectControl !is null && !fSubjectControl.isDisposed() && fSubjectControl.getShell() !is fSubjectControl.getShell().getDisplay().getActiveShell())) {
640 if (DEBUG)
641 System.out_.println(Format("AbstractHoverInformationControlManager...mouseHover: @ {}/{} : hover cancelled: fIsComputing= {}, fIsInRestartMode= {}", event.x, event.y, fIsComputing, fIsInRestartMode)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
642 return;
643 }
644
645 fIsInRestartMode= true;
646 fIsComputing= true;
647 fMouseLostWhileComputing= false;
648 fShellDeactivatedWhileComputing= false;
649
650 fHoverEventStateMask= event.stateMask;
651 fHoverEvent= event;
652 fHoverArea= new Rectangle(event.x - EPSILON, event.y - EPSILON, 2 * EPSILON, 2 * EPSILON );
653 if (fHoverArea.x < 0)
654 fHoverArea.x= 0;
655 if (fHoverArea.y < 0)
656 fHoverArea.y= 0;
657 setSubjectArea(fHoverArea);
658
659 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
660 fSubjectControl.addMouseMoveListener(this);
661 fSubjectControl.getShell().addShellListener(this);
662 }
663 doShowInformation();
664 }
665
666 /**
667 * Deactivates this tracker's restart function and enables the information control
668 * manager. Does not have any effect if the tracker is still executing the start function (i.e.
669 * computing the information to be presented.
670 */
671 protected void deactivate() {
672 if (fIsComputing)
673 return;
674
675 fIsInRestartMode= false;
676 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
677 fSubjectControl.removeMouseMoveListener(this);
678 fSubjectControl.getShell().removeShellListener(this);
679 }
680 }
681
682 /*
683 * @see MouseTrackListener#mouseEnter(MouseEvent)
684 */
685 public void mouseEnter(MouseEvent e) {
686 }
687
688 /*
689 * @see MouseTrackListener#mouseExit(MouseEvent)
690 */
691 public void mouseExit(MouseEvent e) {
692 if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(getCurrentInformationControl()) || !inKeepUpZone(e.x, e.y, fSubjectControl, fSubjectArea, false)) {
693 fMouseLostWhileComputing= true;
694 deactivate();
695 }
696 }
697
698 /*
699 * @see MouseMoveListener#mouseMove(MouseEvent)
700 */
701 public void mouseMove(MouseEvent event) {
702 if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(getCurrentInformationControl())) {
703 if (!fSubjectArea.contains(event.x, event.y))
704 deactivate();
705 } else {
706 if (!inKeepUpZone(event.x, event.y, fSubjectControl, fSubjectArea, false))
707 deactivate();
708 }
709 }
710
711 /*
712 * @see ShellListener#shellDeactivated(ShellEvent)
713 */
714 public void shellDeactivated(ShellEvent e) {
715 fShellDeactivatedWhileComputing= true;
716 deactivate();
717 }
718
719 /*
720 * @see ShellListener#shellIconified(ShellEvent)
721 */
722 public void shellIconified(ShellEvent e) {
723 fShellDeactivatedWhileComputing= true;
724 deactivate();
725 }
726
727 /**
728 * Tells this tracker that the start function processing has been completed.
729 */
730 public void computationCompleted() {
731 fIsComputing= false;
732 fMouseLostWhileComputing= false;
733 fShellDeactivatedWhileComputing= false;
734 }
735
736 /**
737 * Determines whether the computed information is still useful for presentation.
738 * This is not the case, if the shell of the subject control has been deactivated, the mouse
739 * left the subject control, or the mouse moved on, so that it is no longer in the subject
740 * area.
741 *
742 * @return <code>true</code> if information is still useful for presentation, <code>false</code> otherwise
743 */
744 public bool isMouseLost() {
745
746 if (fMouseLostWhileComputing || fShellDeactivatedWhileComputing)
747 return true;
748
749 if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
750 Display display= fSubjectControl.getDisplay();
751 Point p= display.getCursorLocation();
752 p= fSubjectControl.toControl(p);
753 if (!fSubjectArea.contains(p) && !fHoverArea.contains(p))
754 return true;
755 }
756
757 return false;
758 }
759 }
760
761 /**
762 * The delay in {@link ITextViewerExtension8.EnrichMode#AFTER_DELAY} mode after which
763 * the hover is enriched when the mouse has stopped moving inside the hover.
764 * @since 3.4
765 */
766 private static const long HOVER_AUTO_REPLACING_DELAY= 200;
767
768 /** The mouse tracker on the subject control */
769 private MouseTracker fMouseTracker;
770 /**
771 * The remembered hover event.
772 * @since 3.0
773 */
774 private MouseEvent fHoverEvent= null;
775 /** The remembered hover event state mask of the keyboard modifiers */
776 private int fHoverEventStateMask= 0;
777 /**
778 * The thread that delays replacing of the hover information control.
779 * To be accessed in the UI thread only!
780 *
781 * @since 3.4
782 */
783 private Job fReplacingDelayJob;
784
785 /**
786 * The {@link ITextViewerExtension8.EnrichMode}, may be <code>null</code>.
787 * @since 3.4
788 */
789 private ITextViewerExtension8_EnrichMode fEnrichMode;
790
791 /**
792 * Indicates whether we have received a MouseDown event and are waiting for a MouseUp
793 * (and don't replace the information control until that happened).
794 * @since 3.4
795 */
796 private bool fWaitForMouseUp= false;
797
798 /**
799 * Creates a new hover information control manager using the given information control creator.
800 * By default a <code>Closer</code> instance is set as this manager's closer.
801 *
802 * @param creator the information control creator
803 */
804 protected this(IInformationControlCreator creator) {
805 fMouseTracker= new MouseTracker();
806 super(creator);
807 setCloser(new Closer());
808 setHoverEnrichMode(ITextViewerExtension8_EnrichMode.AFTER_DELAY);
809 }
810
811 /**
812 * Tests whether a given mouse location is within the keep-up zone.
813 * The hover should not be hidden as long as the mouse stays inside this zone.
814 *
815 * @param x the x coordinate, relative to the <em>subject control</em>
816 * @param y the y coordinate, relative to the <em>subject control</em>
817 * @param subjectControl the subject control
818 * @param subjectArea the area for which the presented information is valid
819 * @param blowUp If <code>true</code>, then calculate for the closer, i.e. blow up the keepUp area.
820 * If <code>false</code>, then use tight bounds for hover detection.
821 *
822 * @return <code>true</code> iff the mouse event occurred in the keep-up zone
823 * @since 3.4
824 */
825 private bool inKeepUpZone(int x, int y, Control subjectControl, Rectangle subjectArea, bool blowUp) {
826 if (subjectArea.contains(x, y))
827 return true;
828
829 IInformationControl iControl= getCurrentInformationControl();
830 if (( cast(IInformationControlExtension5)iControl && !(cast(IInformationControlExtension5) iControl).isVisible())) {
831 iControl= null;
832 if (getInformationControlReplacer() !is null) {
833 iControl= getInformationControlReplacer().getCurrentInformationControl2();
834 if (( cast(IInformationControlExtension5)iControl && !(cast(IInformationControlExtension5) iControl).isVisible())) {
835 return false;
836 }
837 }
838 }
839 if ( cast(IInformationControlExtension3)iControl ) {
840 IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) iControl;
841
842 Rectangle iControlBounds= subjectControl.getDisplay().map(null, subjectControl, iControl3.getBounds());
843 Rectangle totalBounds= Geometry.copy(iControlBounds);
844 if (blowUp && isReplaceInProgress()) {
845 //Problem: blown up iControl overlaps rest of subjectArea's line
846 // solution for now: only blow up for keep up (closer), but not for further hover detection
847 int margin= getInformationControlReplacer().getKeepUpMargin();
848 Geometry.expand(totalBounds, margin, margin, margin, margin);
849 }
850
851 if (!blowUp) {
852 if (iControlBounds.contains(x, y))
853 return true;
854
855 if (subjectArea.y + subjectArea.height < iControlBounds.y) {
856 // special case for hover events: subjectArea totally above iControl:
857 // +-----------+
858 // |subjectArea|
859 // +-----------+
860 // |also keepUp|
861 // ++-----------+-------+
862 // | InformationControl |
863 // +--------------------+
864 if (subjectArea.y + subjectArea.height <= y && y <= totalBounds.y) {
865 // is vertically between subject area and iControl
866 if (subjectArea.x <= x && x <= subjectArea.x + subjectArea.width) {
867 // is below subject area (in a vertical projection)
868 return true;
869 }
870 // FIXME: cases when subjectArea extends to left or right of iControl?
871 }
872 return false;
873
874 } else if (iControlBounds.x + iControlBounds.width < subjectArea.x) {
875 // special case for hover events (e.g. in overview ruler): iControl totally left of subjectArea
876 // +--------------------+-----------+
877 // | | +-----------+
878 // | InformationControl |also keepUp|subjectArea|
879 // | | +-----------+
880 // +--------------------+-----------+
881 if (iControlBounds.x + iControlBounds.width <= x && x <= subjectArea.x) {
882 // is horizontally between iControl and subject area
883 if (iControlBounds.y <= y && y <= iControlBounds.y + iControlBounds.height) {
884 // is to the right of iControl (in a horizontal projection)
885 return true;
886 }
887 }
888 return false;
889
890 } else if (subjectArea.x + subjectArea.width < iControlBounds.x) {
891 // special case for hover events (e.g. in annotation ruler): subjectArea totally left of iControl
892 // +-----------+--------------------+
893 // +-----------+ | |
894 // |subjectArea|also keepUp| InformationControl |
895 // +-----------+ | |
896 // +-----------+--------------------+
897 if (subjectArea.x + subjectArea.width <= x && x <= iControlBounds.x) {
898 // is horizontally between subject area and iControl
899 if (iControlBounds.y <= y && y <= iControlBounds.y + iControlBounds.height) {
900 // is to the left of iControl (in a horizontal projection)
901 return true;
902 }
903 }
904 return false;
905 }
906 }
907
908 // FIXME: should maybe use convex hull, not bounding box
909 totalBounds.add(subjectArea);
910 if (totalBounds.contains(x, y))
911 return true;
912 }
913 return false;
914 }
915
916 /**
917 * Tests whether the given information control allows the mouse to be moved
918 * into it.
919 *
920 * @param iControl information control or <code>null</code> if none
921 * @return <code>true</code> if information control allows mouse move into
922 * control, <code>false</code> otherwise
923 */
924 bool canMoveIntoInformationControl(IInformationControl iControl) {
925 return fEnrichMode !is null && canReplace(iControl);
926 }
927
928 /*
929 * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl()
930 */
931 protected void hideInformationControl() {
932 cancelReplacingDelay();
933 super.hideInformationControl();
934 }
935
936 /**
937 * Sets the hover enrich mode. Only applicable when an information
938 * control replacer has been set with
939 * {@link #setInformationControlReplacer(InformationControlReplacer)} .
940 *
941 * @param mode the enrich mode
942 * @since 3.4
943 * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode)
944 */
945 void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode) {
946 fEnrichMode= mode;
947 }
948
949 /*
950 * @see org.eclipse.jface.text.AbstractInformationControlManager#replaceInformationControl(bool)
951 */
952 void replaceInformationControl(bool takeFocus) {
953 fWaitForMouseUp= false;
954 super.replaceInformationControl(takeFocus);
955 }
956
957 /**
958 * Cancels the replacing delay job.
959 * @return <code>true</code> iff canceling was successful, <code>false</code> if replacing has already started
960 */
961 bool cancelReplacingDelay() {
962 fWaitForMouseUp= false;
963 if (fReplacingDelayJob !is null && fReplacingDelayJob.getState() !is Job.RUNNING) {
964 bool cancelled= fReplacingDelayJob.cancel();
965 fReplacingDelayJob= null;
966 // if (DEBUG)
967 // System.out_.println("AbstractHoverInformationControlManager.cancelReplacingDelay(): cancelled=" + cancelled); //$NON-NLS-1$
968 return cancelled;
969 }
970 // if (DEBUG)
971 // System.out_.println("AbstractHoverInformationControlManager.cancelReplacingDelay(): not delayed"); //$NON-NLS-1$
972 return true;
973 }
974
975 /**
976 * Starts replacing the information control, considering the current
977 * {@link ITextViewerExtension8.EnrichMode}.
978 * If set to {@link ITextViewerExtension8.EnrichMode#AFTER_DELAY}, this
979 * method cancels previous requests and restarts the delay timer.
980 *
981 * @param display the display to be used for the call to
982 * {@link #replaceInformationControl(bool)} in the UI thread
983 */
984 private void startReplaceInformationControl(Display display) {
985 if (fEnrichMode is ITextViewerExtension8_EnrichMode.ON_CLICK)
986 return;
987
988 if (fReplacingDelayJob !is null) {
989 if (fReplacingDelayJob.getState() !is Job.RUNNING) {
990 if (fReplacingDelayJob.cancel()) {
991 if (fEnrichMode is ITextViewerExtension8_EnrichMode.IMMEDIATELY) {
992 fReplacingDelayJob= null;
993 if (! fWaitForMouseUp)
994 replaceInformationControl(false);
995 } else {
996 // if (DEBUG)
997 // System.out_.println("AbstractHoverInformationControlManager.startReplaceInformationControl(): rescheduled"); //$NON-NLS-1$
998 fReplacingDelayJob.schedule(HOVER_AUTO_REPLACING_DELAY);
999 }
1000 }
1001 }
1002 return;
1003 }
1004
1005 fReplacingDelayJob= new class("AbstractHoverInformationControlManager Replace Delayer", display) Job { //$NON-NLS-1$
1006 Display display_;
1007 this( String str, Display b){
1008 super(str);
1009 display_=b;
1010 }
1011 public IStatus run(IProgressMonitor monitor) {
1012 if (monitor.isCanceled() || display_.isDisposed()) {
1013 return Status.CANCEL_STATUS;
1014 }
1015 display_.syncExec(dgRunnable( (IProgressMonitor monitor_) {
1016 fReplacingDelayJob= null;
1017 if (monitor_.isCanceled())
1018 return;
1019 if (! fWaitForMouseUp)
1020 replaceInformationControl(false);
1021 }, monitor ));
1022 return Status.OK_STATUS;
1023 }
1024 };
1025 fReplacingDelayJob.setSystem(true);
1026 fReplacingDelayJob.setPriority(Job.INTERACTIVE);
1027 // if (DEBUG)
1028 // System.out_.println("AbstractHoverInformationControlManager.startReplaceInformationControl(): scheduled"); //$NON-NLS-1$
1029 fReplacingDelayJob.schedule(HOVER_AUTO_REPLACING_DELAY);
1030 }
1031
1032 /*
1033 * @see org.eclipse.jface.text.AbstractInformationControlManager#presentInformation()
1034 */
1035 protected void presentInformation() {
1036 if (fMouseTracker is null) {
1037 super.presentInformation();
1038 return;
1039 }
1040
1041 Rectangle area= getSubjectArea();
1042 if (area !is null)
1043 fMouseTracker.setSubjectArea(area);
1044
1045 if (fMouseTracker.isMouseLost()) {
1046 fMouseTracker.computationCompleted();
1047 fMouseTracker.deactivate();
1048 } else {
1049 fMouseTracker.computationCompleted();
1050 super.presentInformation();
1051 }
1052 }
1053
1054 /**
1055 * {@inheritDoc}
1056 * @deprecated visibility will be changed to protected
1057 */
1058 public void setEnabled(bool enabled) {
1059
1060 bool was= isEnabled();
1061 super.setEnabled(enabled);
1062 bool is_= isEnabled();
1063
1064 if (was !is is_ && fMouseTracker !is null) {
1065 if (is_)
1066 fMouseTracker.start(getSubjectControl());
1067 else
1068 fMouseTracker.stop();
1069 }
1070 }
1071
1072 /**
1073 * Disposes this manager's information control.
1074 */
1075 public void dispose() {
1076 if (fMouseTracker !is null) {
1077 fMouseTracker.stop();
1078 fMouseTracker.fSubjectControl= null;
1079 fMouseTracker= null;
1080 }
1081 super.dispose();
1082 }
1083
1084 /**
1085 * Returns the location at which the most recent mouse hover event
1086 * has been issued.
1087 *
1088 * @return the location of the most recent mouse hover event
1089 */
1090 protected Point getHoverEventLocation() {
1091 return fHoverEvent !is null ? new Point(fHoverEvent.x, fHoverEvent.y) : new Point(-1, -1);
1092 }
1093 package Point getHoverEventLocation_package() {
1094 return getHoverEventLocation();
1095 }
1096
1097 /**
1098 * Returns the most recent mouse hover event.
1099 *
1100 * @return the most recent mouse hover event or <code>null</code>
1101 * @since 3.0
1102 */
1103 protected MouseEvent getHoverEvent() {
1104 return fHoverEvent;
1105 }
1106
1107 /**
1108 * Returns the SWT event state of the most recent mouse hover event.
1109 *
1110 * @return the SWT event state of the most recent mouse hover event
1111 */
1112 protected int getHoverEventStateMask() {
1113 return fHoverEventStateMask;
1114 }
1115
1116 /**
1117 * Returns an adapter that gives access to internal methods.
1118 * <p>
1119 * <strong>Note:</strong> This method is not intended to be referenced or overridden by clients.</p>
1120 *
1121 * @return the replaceable information control accessor
1122 * @since 3.4
1123 * @noreference This method is not intended to be referenced by clients.
1124 * @nooverride This method is not intended to be re-implemented or extended by clients.
1125 */
1126 public InternalAccessor getInternalAccessor() {
1127 return new MyInternalAccessor2(this);
1128 }
1129 static class MyInternalAccessor2 : MyInternalAccessor {
1130 AbstractHoverInformationControlManager outer_;
1131 this( AbstractHoverInformationControlManager a ){
1132 outer_=a;
1133 super(a);
1134 }
1135 public void setHoverEnrichMode(ITextViewerExtension8_EnrichMode mode) {
1136 outer_.setHoverEnrichMode(mode);
1137 }
1138 }
1139
1140 }