KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > window > ToolTip


1 /*******************************************************************************
2  * Copyright (c) 2006, 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  * Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jface.window;
13
14 import java.util.HashMap JavaDoc;
15
16 import org.eclipse.jface.viewers.ColumnViewer;
17 import org.eclipse.jface.viewers.ViewerCell;
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.events.DisposeEvent;
20 import org.eclipse.swt.events.DisposeListener;
21 import org.eclipse.swt.graphics.Point;
22 import org.eclipse.swt.graphics.Rectangle;
23 import org.eclipse.swt.layout.FillLayout;
24 import org.eclipse.swt.widgets.Composite;
25 import org.eclipse.swt.widgets.Control;
26 import org.eclipse.swt.widgets.Event;
27 import org.eclipse.swt.widgets.Listener;
28 import org.eclipse.swt.widgets.Monitor;
29 import org.eclipse.swt.widgets.Shell;
30
31 /**
32  * This class gives implementors to provide customized tooltips for any control.
33  *
34  * @since 3.3
35  */

36 public abstract class ToolTip {
37     private Control control;
38
39     private int xShift = 3;
40
41     private int yShift = 0;
42
43     private int popupDelay = 0;
44
45     private int hideDelay = 0;
46
47     private ToolTipOwnerControlListener listener;
48
49     private HashMap JavaDoc data;
50
51     // Ensure that only one tooltip is active in time
52
private static Shell CURRENT_TOOLTIP;
53
54     /**
55      * Recreate the tooltip on every mouse move
56      */

57     public static final int RECREATE = 1;
58
59     /**
60      * Don't recreate the tooltip as long the mouse doesn't leave the area
61      * triggering the Tooltip creation
62      */

63     public static final int NO_RECREATE = 1 << 1;
64
65     private TooltipHideListener hideListener = new TooltipHideListener();
66
67     private boolean hideOnMouseDown = true;
68
69     private boolean respectDisplayBounds = true;
70
71     private boolean respectMonitorBounds = true;
72
73     private int style;
74
75     private Object JavaDoc currentArea;
76
77     /**
78      * Create new instance which add TooltipSupport to the widget
79      *
80      * @param control
81      * the control on whose action the tooltip is shown
82      */

83     public ToolTip(Control control) {
84         this(control, RECREATE, false);
85     }
86
87     /**
88      * @param control
89      * the control to which the tooltip is bound
90      * @param style
91      * style passed to control tooltip behaviour
92      *
93      * @param manualActivation
94      * <code>true</code> if the activation is done manually using
95      * {@link #show(Point)}
96      * @see #RECREATE
97      * @see #NO_RECREATE
98      */

99     public ToolTip(Control control, int style, boolean manualActivation) {
100         this.control = control;
101         this.style = style;
102         this.control.addDisposeListener(new DisposeListener() {
103
104             public void widgetDisposed(DisposeEvent e) {
105                 deactivate();
106             }
107
108         });
109
110         this.listener = new ToolTipOwnerControlListener();
111
112         if (!manualActivation) {
113             activate();
114         }
115     }
116
117     /**
118      * Restore arbitary data under the given key
119      *
120      * @param key
121      * the key
122      * @param value
123      * the value
124      */

125     public void setData(String JavaDoc key, Object JavaDoc value) {
126         if (data == null) {
127             data = new HashMap JavaDoc();
128         }
129         data.put(key, value);
130     }
131
132     /**
133      * Get the data restored under the key
134      *
135      * @param key
136      * the key
137      * @return data or <code>null</code> if no entry is restored under the key
138      */

139     public Object JavaDoc getData(String JavaDoc key) {
140         if (data != null) {
141             return data.get(key);
142         }
143         return null;
144     }
145
146     /**
147      * Set the shift (from the mouse position triggered the event) used to
148      * display the tooltip. By default the tooltip is shifted 3 pixels to the
149      * left
150      *
151      * @param p
152      * the new shift
153      */

154     public void setShift(Point p) {
155         xShift = p.x;
156         yShift = p.y;
157     }
158
159     /**
160      * Activate tooltip support for this control
161      */

162     public void activate() {
163         deactivate();
164         control.addListener(SWT.Dispose, listener);
165         control.addListener(SWT.MouseHover, listener);
166         control.addListener(SWT.MouseMove, listener);
167         control.addListener(SWT.MouseExit, listener);
168         control.addListener(SWT.MouseDown, listener);
169     }
170
171     /**
172      * Deactivate tooltip support for the underlying control
173      */

174     public void deactivate() {
175         control.removeListener(SWT.Dispose, listener);
176         control.removeListener(SWT.MouseHover, listener);
177         control.removeListener(SWT.MouseMove, listener);
178         control.removeListener(SWT.MouseExit, listener);
179         control.removeListener(SWT.MouseDown, listener);
180     }
181
182     /**
183      * Return whther the tooltip respects bounds of the display.
184      *
185      * @return <code>true</code> if the tooltip respects bounds of the display
186      */

187     public boolean isRespectDisplayBounds() {
188         return respectDisplayBounds;
189     }
190
191     /**
192      * Set to <code>false</code> if display bounds should not be respected or
193      * to <code>true</code> if the tooltip is should repositioned to not
194      * overlap the display bounds.
195      * <p>
196      * Default is <code>true</code>
197      * </p>
198      *
199      * @param respectDisplayBounds
200      */

201     public void setRespectDisplayBounds(boolean respectDisplayBounds) {
202         this.respectDisplayBounds = respectDisplayBounds;
203     }
204
205     /**
206      * Return whther the tooltip respects bounds of the monitor.
207      *
208      * @return <code>true</code> if tooltip respects the bounds of the monitor
209      */

210     public boolean isRespectMonitorBounds() {
211         return respectMonitorBounds;
212     }
213
214     /**
215      * Set to <code>false</code> if monitor bounds should not be respected or
216      * to <code>true</code> if the tooltip is should repositioned to not
217      * overlap the monitors bounds. The monitor the tooltip belongs to is the
218      * same is control's monitor the tooltip is shown for.
219      * <p>
220      * Default is <code>true</code>
221      * </p>
222      *
223      * @param respectMonitorBounds
224      */

225     public void setRespectMonitorBounds(boolean respectMonitorBounds) {
226         this.respectMonitorBounds = respectMonitorBounds;
227     }
228
229     /**
230      * Should the tooltip displayed because of the given event.
231      * <p>
232      * <b>Subclasses may overwrite this to get custom behaviour</b>
233      * </p>
234      *
235      * @param event
236      * the event
237      * @return <code>true</code> if tooltip should be displayed
238      */

239     protected boolean shouldCreateToolTip(Event event) {
240         if ((style & NO_RECREATE) != 0) {
241             Object JavaDoc tmp = getToolTipArea(event);
242
243             // No new area close the current tooltip
244
if (tmp == null) {
245                 hide();
246                 return false;
247             }
248
249             boolean rv = !tmp.equals(currentArea);
250             return rv;
251         }
252
253         return true;
254     }
255
256     /**
257      * This method is called before the tooltip is hidden
258      *
259      * @param event
260      * the event trying to hide the tooltip
261      * @return <code>true</code> if the tooltip should be hidden
262      */

263     private boolean shouldHideToolTip(Event event) {
264         if (event != null && event.type == SWT.MouseMove
265                 && (style & NO_RECREATE) != 0) {
266             Object JavaDoc tmp = getToolTipArea(event);
267
268             // No new area close the current tooltip
269
if (tmp == null) {
270                 hide();
271                 return false;
272             }
273
274             boolean rv = !tmp.equals(currentArea);
275             return rv;
276         }
277
278         return true;
279     }
280
281     /**
282      * This method is called to check for which area the tooltip is
283      * created/hidden for. In case of {@link #NO_RECREATE} this is used to
284      * decide if the tooltip is hidden recreated.
285      *
286      * <code>By the default it is the widget the tooltip is created for but could be any object. To decide if
287      * the area changed the {@link Object#equals(Object)} method is used.</code>
288      *
289      * @param event
290      * the event
291      * @return the area responsible for the tooltip creation or
292      * <code>null</code> this could be any object describing the area
293      * (e.g. the {@link Control} onto which the tooltip is bound to, a part of
294      * this area e.g. for {@link ColumnViewer} this could be a
295      * {@link ViewerCell})
296      */

297     protected Object JavaDoc getToolTipArea(Event event) {
298         return control;
299     }
300
301     /**
302      * Start up the tooltip programmatically
303      *
304      * @param location
305      * the location relative to the control the tooltip is shown
306      */

307     public void show(Point location) {
308         Event event = new Event();
309         event.x = location.x;
310         event.y = location.y;
311         event.widget = control;
312         toolTipCreate(event);
313     }
314
315     private Shell toolTipCreate(final Event event) {
316         if (shouldCreateToolTip(event)) {
317             Shell shell = new Shell(control.getShell(), SWT.ON_TOP | SWT.TOOL
318                     | SWT.NO_FOCUS);
319             shell.setLayout(new FillLayout());
320
321             toolTipOpen(shell, event);
322
323             return shell;
324         }
325
326         return null;
327     }
328
329     private void toolTipShow(Shell tip, Event event) {
330         if (!tip.isDisposed()) {
331             currentArea = getToolTipArea(event);
332             createToolTipContentArea(event, tip);
333             if (isHideOnMouseDown()) {
334                 toolTipHookBothRecursively(tip);
335             } else {
336                 toolTipHookByTypeRecursively(tip, true, SWT.MouseExit);
337             }
338
339             tip.pack();
340             tip.setLocation(fixupDisplayBounds(tip.getSize(), getLocation(tip
341                     .getSize(), event)));
342             tip.setVisible(true);
343         }
344     }
345
346     private Point fixupDisplayBounds(Point tipSize, Point location) {
347         if (respectDisplayBounds || respectMonitorBounds) {
348             Rectangle bounds;
349             Point rightBounds = new Point(tipSize.x + location.x, tipSize.y
350                     + location.y);
351
352             Monitor[] ms = control.getDisplay().getMonitors();
353
354             if (respectMonitorBounds && ms.length > 1) {
355                 // By default present in the monitor of the control
356
bounds = control.getMonitor().getBounds();
357                 Point p = new Point(location.x, location.y);
358
359                 // Search on which monitor the event occurred
360
Rectangle tmp;
361                 for (int i = 0; i < ms.length; i++) {
362                     tmp = ms[i].getBounds();
363                     if (tmp.contains(p)) {
364                         bounds = tmp;
365                         break;
366                     }
367                 }
368
369             } else {
370                 bounds = control.getDisplay().getBounds();
371             }
372
373             if (!(bounds.contains(location) && bounds.contains(rightBounds))) {
374                 if (rightBounds.x > bounds.width) {
375                     location.x -= rightBounds.x - bounds.width;
376                 }
377
378                 if (rightBounds.y > bounds.height) {
379                     location.y -= rightBounds.y - bounds.height;
380                 }
381
382                 if (location.x < bounds.x) {
383                     location.x = bounds.x;
384                 }
385
386                 if (location.y < bounds.y) {
387                     location.y = bounds.y;
388                 }
389             }
390         }
391
392         return location;
393     }
394
395     /**
396      * Get the display relative location where the tooltip is displayed.
397      * Subclasses may overwrite to implement custom positioning.
398      *
399      * @param tipSize
400      * the size of the tooltip to be shown
401      * @param event
402      * the event triggered showing the tooltip
403      * @return the absolute position on the display
404      */

405     public Point getLocation(Point tipSize, Event event) {
406         return control.toDisplay(event.x + xShift, event.y + yShift);
407     }
408
409     private void toolTipHide(Shell tip, Event event) {
410         if (tip != null && !tip.isDisposed() && shouldHideToolTip(event)) {
411             currentArea = null;
412             tip.dispose();
413             CURRENT_TOOLTIP = null;
414             afterHideToolTip(event);
415         }
416     }
417
418     private void toolTipOpen(final Shell shell, final Event event) {
419         // Ensure that only one Tooltip is shown in time
420
if (CURRENT_TOOLTIP != null) {
421             toolTipHide(CURRENT_TOOLTIP, null);
422         }
423
424         CURRENT_TOOLTIP = shell;
425
426         if (popupDelay > 0) {
427             control.getDisplay().timerExec(popupDelay, new Runnable JavaDoc() {
428                 public void run() {
429                     toolTipShow(shell, event);
430                 }
431             });
432         } else {
433             toolTipShow(CURRENT_TOOLTIP, event);
434         }
435
436         if (hideDelay > 0) {
437             control.getDisplay().timerExec(popupDelay + hideDelay,
438                     new Runnable JavaDoc() {
439
440                         public void run() {
441                             toolTipHide(shell, null);
442                         }
443                     });
444         }
445     }
446
447     private void toolTipHookByTypeRecursively(Control c, boolean add, int type) {
448         if (add) {
449             c.addListener(type, hideListener);
450         } else {
451             c.removeListener(type, hideListener);
452         }
453
454         if (c instanceof Composite) {
455             Control[] children = ((Composite) c).getChildren();
456             for (int i = 0; i < children.length; i++) {
457                 toolTipHookByTypeRecursively(children[i], add, type);
458             }
459         }
460     }
461
462     private void toolTipHookBothRecursively(Control c) {
463         c.addListener(SWT.MouseDown, hideListener);
464         c.addListener(SWT.MouseExit, hideListener);
465
466         if (c instanceof Composite) {
467             Control[] children = ((Composite) c).getChildren();
468             for (int i = 0; i < children.length; i++) {
469                 toolTipHookBothRecursively(children[i]);
470             }
471         }
472     }
473
474     /**
475      * Creates the content area of the the tooltip.
476      *
477      * @param event
478      * the event that triggered the activation of the tooltip
479      * @param parent
480      * the parent of the content area
481      * @return the content area created
482      */

483     protected abstract Composite createToolTipContentArea(Event event,
484             Composite parent);
485
486     /**
487      * This method is called after a Tooltip is hidden.
488      * <p>
489      * <b>Subclasses may override to clean up requested system resources</b>
490      * </p>
491      *
492      * @param event
493      * event triggered the hiding action (may be <code>null</code>
494      * if event wasn't triggered by user actions directly)
495      */

496     protected void afterHideToolTip(Event event) {
497
498     }
499
500     /**
501      * Set the hide delay.
502      *
503      * @param hideDelay
504      * the delay before the tooltip is hidden. If <code>0</code>
505      * the tooltip is shown until user moves to other item
506      */

507     public void setHideDelay(int hideDelay) {
508         this.hideDelay = hideDelay;
509     }
510
511     /**
512      * Set the popup delay.
513      *
514      * @param popupDelay
515      * the delay before the tooltip is shown to the user. If
516      * <code>0</code> the tooltip is shown immediately
517      */

518     public void setPopupDelay(int popupDelay) {
519         this.popupDelay = popupDelay;
520     }
521
522     /**
523      * Return if hiding on mouse down is set.
524      *
525      * @return <code>true</code> if hiding on mouse down in the tool tip is on
526      */

527     public boolean isHideOnMouseDown() {
528         return hideOnMouseDown;
529     }
530
531     /**
532      * If you don't want the tool tip to be hidden when the user clicks inside
533      * the tool tip set this to <code>false</code>. You maybe also need to
534      * hide the tool tip yourself depending on what you do after clicking in the
535      * tooltip (e.g. if you open a new {@link Shell})
536      *
537      * @param hideOnMouseDown
538      * flag to indicate of tooltip is hidden automatically on mouse
539      * down inside the tool tip
540      */

541     public void setHideOnMouseDown(final boolean hideOnMouseDown) {
542         // Only needed if there's currently a tooltip active
543
if (CURRENT_TOOLTIP != null && !CURRENT_TOOLTIP.isDisposed()) {
544             // Only change if value really changed
545
if (hideOnMouseDown != this.hideOnMouseDown) {
546                 control.getDisplay().syncExec(new Runnable JavaDoc() {
547
548                     public void run() {
549                         if (CURRENT_TOOLTIP != null
550                                 && CURRENT_TOOLTIP.isDisposed()) {
551                             toolTipHookByTypeRecursively(CURRENT_TOOLTIP,
552                                     hideOnMouseDown, SWT.MouseDown);
553                         }
554                     }
555
556                 });
557             }
558         }
559
560         this.hideOnMouseDown = hideOnMouseDown;
561     }
562
563     /**
564      * Hide the currently active tool tip
565      */

566     public void hide() {
567         toolTipHide(CURRENT_TOOLTIP, null);
568     }
569
570     private class ToolTipOwnerControlListener implements Listener {
571         public void handleEvent(Event event) {
572             switch (event.type) {
573             case SWT.Dispose:
574             case SWT.KeyDown:
575             case SWT.MouseDown:
576             case SWT.MouseMove:
577                 toolTipHide(CURRENT_TOOLTIP, event);
578                 break;
579             case SWT.MouseHover:
580                 toolTipCreate(event);
581                 break;
582             case SWT.MouseExit:
583                 /*
584                  * Check if the mouse exit happend because we move over the
585                  * tooltip
586                  */

587                 if (CURRENT_TOOLTIP != null && !CURRENT_TOOLTIP.isDisposed()) {
588                     if (CURRENT_TOOLTIP.getBounds().contains(
589                             control.toDisplay(event.x, event.y))) {
590                         break;
591                     }
592                 }
593
594                 toolTipHide(CURRENT_TOOLTIP, event);
595                 break;
596             }
597         }
598     }
599
600     private class TooltipHideListener implements Listener {
601         public void handleEvent(Event event) {
602             if (event.widget instanceof Control) {
603
604                 Control c = (Control) event.widget;
605                 Shell shell = c.getShell();
606
607                 switch (event.type) {
608                 case SWT.MouseDown:
609                     if (isHideOnMouseDown()) {
610                         toolTipHide(shell, event);
611                     }
612                     break;
613                 case SWT.MouseExit:
614                     /*
615                      * Give some insets to ensure we get exit informations from
616                      * a wider area ;-)
617                      */

618                     Rectangle rect = shell.getBounds();
619                     rect.x += 5;
620                     rect.y += 5;
621                     rect.width -= 10;
622                     rect.height -= 10;
623
624                     if (!rect.contains(c.getDisplay().getCursorLocation())) {
625                         toolTipHide(shell, event);
626                     }
627
628                     break;
629                 }
630             }
631         }
632     }
633 }
634
Popular Tags