KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > fieldassist > ControlDecoration


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  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jface.fieldassist;
12
13 import org.eclipse.core.runtime.ListenerList;
14 import org.eclipse.swt.SWT;
15 import org.eclipse.swt.events.DisposeEvent;
16 import org.eclipse.swt.events.DisposeListener;
17 import org.eclipse.swt.events.FocusEvent;
18 import org.eclipse.swt.events.FocusListener;
19 import org.eclipse.swt.events.MenuDetectEvent;
20 import org.eclipse.swt.events.MenuDetectListener;
21 import org.eclipse.swt.events.MouseAdapter;
22 import org.eclipse.swt.events.MouseEvent;
23 import org.eclipse.swt.events.MouseMoveListener;
24 import org.eclipse.swt.events.MouseTrackListener;
25 import org.eclipse.swt.events.PaintEvent;
26 import org.eclipse.swt.events.PaintListener;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.events.SelectionListener;
29 import org.eclipse.swt.graphics.GC;
30 import org.eclipse.swt.graphics.Image;
31 import org.eclipse.swt.graphics.Point;
32 import org.eclipse.swt.graphics.Rectangle;
33 import org.eclipse.swt.graphics.Region;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.swt.widgets.Display;
37 import org.eclipse.swt.widgets.Event;
38 import org.eclipse.swt.widgets.Listener;
39 import org.eclipse.swt.widgets.Shell;
40 import org.eclipse.swt.widgets.Widget;
41
42 /**
43  * ControlDecoration renders an image decoration near a control. It allows
44  * clients to specify an image and a position for the image relative to the
45  * control. A ControlDecoration may be assigned description text, which can
46  * optionally be shown when the user hovers over the image. Clients can decorate
47  * any kind of control.
48  * <p>
49  * Decoration images always appear on the left or right side of the field, never
50  * above or below it. Decorations can be positioned at the top, center, or
51  * bottom of either side of the control. Future implementations may provide
52  * additional positioning options for decorations.
53  * <p>
54  * ControlDecoration renders the image adjacent to the specified (already
55  * created) control, with no guarantee that it won't be clipped or otherwise
56  * obscured or overlapped by adjacent controls, including another
57  * ControlDecoration placed in the same location. Clients should ensure that
58  * there is adequate space adjacent to the control to show the decoration
59  * properly.
60  * <p>
61  * Clients using ControlDecoration should typically ensure that enough margin
62  * space is reserved for a decoration by altering the layout data margins,
63  * although this is not assumed or required by the ControlDecoration
64  * implementation.
65  * <p>
66  * This class is intended to be instantiated and used by clients. It is not
67  * intended to be subclassed by clients.
68  *
69  * @since 3.3
70  *
71  * @see FieldDecoration
72  * @see FieldDecorationRegistry
73  */

74 public class ControlDecoration {
75     /**
76      * Debug flag for tracing
77      */

78     private static boolean DEBUG = false;
79
80     /**
81      * Cached platform flags for dealing with platform-specific issues.
82      */

83     private static boolean CARBON = "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
84

85     /**
86      * The associated control
87      */

88     private Control control;
89
90     /**
91      * The composite on which to render the decoration and hook mouse events, or
92      * null if we are hooking all parent composites.
93      */

94     private Composite composite;
95
96     /**
97      * The associated image.
98      */

99     private Image image;
100
101     /**
102      * The associated description text.
103      */

104     private String JavaDoc descriptionText;
105     /**
106      * The position of the decoration.
107      */

108     private int position;
109
110     /**
111      * The decoration's visibility flag
112      */

113     private boolean visible = true;
114
115     /**
116      * Boolean indicating whether the decoration should only be shown when the
117      * control has focus
118      */

119     private boolean showOnlyOnFocus = false;
120
121     /**
122      * Boolean indicating whether the decoration should show its description
123      * text in a hover when the user hovers over the decoration.
124      */

125     private boolean showHover = true;
126
127     /**
128      * Margin width used between the decorator and the control.
129      */

130     private int marginWidth = 0;
131
132     /**
133      * Registered selection listeners.
134      */

135     private ListenerList selectionListeners = new ListenerList();
136
137     /**
138      * Registered menu detect listeners.
139      */

140     private ListenerList menuDetectListeners = new ListenerList();
141
142     /**
143      * The focus listener
144      */

145     private FocusListener focusListener;
146
147     /**
148      * The dispose listener
149      */

150     private DisposeListener disposeListener;
151
152     /**
153      * The paint listener installed for drawing the decoration
154      */

155     private PaintListener paintListener;
156
157     /**
158      * The mouse listener installed for tracking the hover
159      */

160     private MouseTrackListener mouseTrackListener;
161
162     /**
163      * The mouse move listener installed for tracking the hover
164      */

165     private MouseMoveListener mouseMoveListener;
166
167     /**
168      * The untyped listener installed for notifying external listeners
169      */

170     private Listener compositeListener;
171
172     /**
173      * Control that we last installed a move listener on. We only want one at a
174      * time.
175      */

176     private Control moveListeningTarget = null;
177
178     /**
179      * Debug counter used to match add and remove listeners
180      */

181     private int listenerInstalls = 0;
182
183     /**
184      * The current rectangle used for tracking mouse moves
185      */

186     private Rectangle decorationRectangle;
187
188     /**
189      * An internal flag tracking whether we have focus. We use this rather than
190      * isFocusControl() so that we can set the flag as soon as we get the focus
191      * callback, rather than having to do an asyncExec in the middle of a focus
192      * callback to ensure that isFocusControl() represents the outcome of the
193      * event.
194      */

195     private boolean hasFocus = false;
196
197     /**
198      * The hover used for showing description text
199      */

200     private Hover hover;
201
202     /**
203      * The hover used to show a decoration image's description.
204      */

205     class Hover {
206         private static final String JavaDoc EMPTY = ""; //$NON-NLS-1$
207

208         /**
209          * Offset of info hover arrow from the left or right side.
210          */

211         private int hao = 10;
212
213         /**
214          * Width of info hover arrow.
215          */

216         private int haw = 8;
217
218         /**
219          * Height of info hover arrow.
220          */

221         private int hah = 10;
222
223         /**
224          * Margin around info hover text.
225          */

226         private int hm = 2;
227
228         /**
229          * This info hover's shell.
230          */

231         Shell hoverShell;
232
233         /**
234          * The info hover text.
235          */

236         String JavaDoc text = EMPTY;
237
238         /**
239          * The region used to manage the shell shape
240          */

241         Region region;
242
243         /**
244          * Boolean indicating whether the last computed polygon location had an
245          * arrow on left. (true if left, false if right).
246          */

247         boolean arrowOnLeft = true;
248
249         /*
250          * Create a hover parented by the specified shell.
251          */

252         Hover(Shell parent) {
253             final Display display = parent.getDisplay();
254             hoverShell = new Shell(parent, SWT.NO_TRIM | SWT.ON_TOP
255                     | SWT.NO_FOCUS);
256             hoverShell.setBackground(display
257                     .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
258             hoverShell.setForeground(display
259                     .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
260             hoverShell.addPaintListener(new PaintListener() {
261                 public void paintControl(PaintEvent pe) {
262                     pe.gc.drawText(text, hm, hm);
263                     if (!CARBON) {
264                         pe.gc.drawPolygon(getPolygon(true));
265                     }
266                 }
267             });
268             hoverShell.addMouseListener(new MouseAdapter() {
269                 public void mouseDown(MouseEvent e) {
270                     hideHover();
271                 }
272             });
273         }
274
275         /*
276          * Compute a polygon that represents a hover with an arrow pointer. If
277          * border is true, compute the polygon inset by 1-pixel border. Consult
278          * the arrowOnLeft flag to determine which side the arrow is on.
279          */

280         int[] getPolygon(boolean border) {
281             Point e = getExtent();
282             int b = border ? 1 : 0;
283             if (arrowOnLeft) {
284                 return new int[] { 0, 0, e.x - b, 0, e.x - b, e.y - b,
285                         hao + haw, e.y - b, hao + haw / 2, e.y + hah - b, hao,
286                         e.y - b, 0, e.y - b, 0, 0 };
287             }
288             return new int[] { 0, 0, e.x - b, 0, e.x - b, e.y - b,
289                     e.x - hao - b, e.y - b, e.x - hao - haw / 2, e.y + hah - b,
290                     e.x - hao - haw, e.y - b, 0, e.y - b, 0, 0 };
291         }
292
293         /*
294          * Dispose the hover, it is no longer needed. Dispose any resources
295          * allocated by the hover.
296          */

297         void dispose() {
298             if (!hoverShell.isDisposed()) {
299                 hoverShell.dispose();
300             }
301             if (region != null) {
302                 region.dispose();
303             }
304         }
305
306         /*
307          * Set the visibility of the hover.
308          */

309         void setVisible(boolean visible) {
310             if (visible) {
311                 if (!hoverShell.isVisible()) {
312                     hoverShell.setVisible(true);
313                 }
314             } else {
315                 if (hoverShell.isVisible()) {
316                     hoverShell.setVisible(false);
317                 }
318             }
319         }
320
321         /*
322          * Set the text of the hover to the specified text. Recompute the size
323          * and location of the hover to hover near the decoration rectangle,
324          * pointing the arrow toward the target control.
325          */

326         void setText(String JavaDoc t, Rectangle decorationRectangle,
327                 Control targetControl) {
328             if (t == null) {
329                 t = EMPTY;
330             }
331             if (!t.equals(text)) {
332                 Point oldSize = getExtent();
333                 text = t;
334                 hoverShell.redraw();
335                 Point newSize = getExtent();
336                 if (!oldSize.equals(newSize)) {
337                     // set a flag that indicates the direction of arrow
338
arrowOnLeft = decorationRectangle.x <= targetControl
339                             .getLocation().x;
340                     setNewShape();
341                 }
342             }
343
344             Point extent = getExtent();
345             int y = -extent.y - hah + 1;
346             int x = arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw / 2;
347
348             hoverShell.setLocation(control.getParent().toDisplay(
349                     decorationRectangle.x + x, decorationRectangle.y + y));
350         }
351
352         /*
353          * Return whether or not the hover (shell) is visible.
354          */

355         boolean isVisible() {
356             return hoverShell.isVisible();
357         }
358
359         /*
360          * Compute the extent of the hover for the current text.
361          */

362         Point getExtent() {
363             GC gc = new GC(hoverShell);
364             Point e = gc.textExtent(text);
365             gc.dispose();
366             e.x += hm * 2;
367             e.y += hm * 2;
368             return e;
369         }
370
371         /*
372          * Compute a new shape for the hover shell.
373          */

374         void setNewShape() {
375             Region oldRegion = region;
376             region = new Region();
377             region.add(getPolygon(false));
378             hoverShell.setRegion(region);
379             if (oldRegion != null) {
380                 oldRegion.dispose();
381             }
382
383         }
384     }
385
386     /**
387      * Construct a ControlDecoration for decorating the specified control at the
388      * specified position relative to the control. Render the decoration on top
389      * of any Control that happens to appear at the specified location.
390      * <p>
391      * SWT constants are used to specify the position of the decoration relative
392      * to the control. The position should include style bits describing both
393      * the vertical and horizontal orientation. <code>SWT.LEFT</code> and
394      * <code>SWT.RIGHT</code> describe the horizontal placement of the
395      * decoration relative to the control, and the constants
396      * <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and
397      * <code>SWT.BOTTOM</code> describe the vertical alignment of the
398      * decoration relative to the control. Decorations always appear on either
399      * the left or right side of the control, never above or below it. For
400      * example, a decoration appearing on the left side of the field, at the
401      * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are
402      * specified, the control decoration will be positioned to the left and
403      * center of the control (<code>SWT.LEFT | SWT.CENTER</code>).
404      * </p>
405      *
406      * @param control
407      * the control to be decorated
408      * @param position
409      * bit-wise or of position constants (<code>SWT.TOP</code>,
410      * <code>SWT.BOTTOM</code>, <code>SWT.LEFT</code>,
411      * <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>).
412      */

413     public ControlDecoration(Control control, int position) {
414         this(control, position, null);
415
416     }
417
418     /**
419      * Construct a ControlDecoration for decorating the specified control at the
420      * specified position relative to the control. Render the decoration only on
421      * the specified Composite or its children. The decoration will be clipped
422      * if it does not appear within the visible bounds of the composite or its
423      * child composites.
424      * <p>
425      * SWT constants are used to specify the position of the decoration relative
426      * to the control. The position should include style bits describing both
427      * the vertical and horizontal orientation. <code>SWT.LEFT</code> and
428      * <code>SWT.RIGHT</code> describe the horizontal placement of the
429      * decoration relative to the control, and the constants
430      * <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and
431      * <code>SWT.BOTTOM</code> describe the vertical alignment of the
432      * decoration relative to the control. Decorations always appear on either
433      * the left or right side of the control, never above or below it. For
434      * example, a decoration appearing on the left side of the field, at the
435      * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are
436      * specified, the control decoration will be positioned to the left and
437      * center of the control (<code>SWT.LEFT | SWT.CENTER</code>).
438      * </p>
439      *
440      * @param control
441      * the control to be decorated
442      * @param position
443      * bit-wise or of position constants (<code>SWT.TOP</code>,
444      * <code>SWT.BOTTOM</code>, <code>SWT.LEFT</code>,
445      * <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>).
446      * @param composite
447      * The SWT composite within which the decoration should be
448      * rendered. The decoration will be clipped to this composite,
449      * but it may be rendered on a child of the composite. The
450      * decoration will not be visible if the specified composite or
451      * its child composites are not visible in the space relative to
452      * the control, where the decoration is to be rendered. If this
453      * value is <code>null</code>, then the decoration will be
454      * rendered on whichever composite (or composites) are located in
455      * the specified position.
456      */

457     public ControlDecoration(Control control, int position, Composite composite) {
458         this.position = position;
459         this.control = control;
460         this.composite = composite;
461
462         addControlListeners();
463
464     }
465
466     /**
467      * Adds the listener to the collection of listeners who will be notified
468      * when the platform-specific context menu trigger has occurred, by sending
469      * it one of the messages defined in the <code>MenuDetectListener</code>
470      * interface.
471      * <p>
472      * The <code>widget</code> field in the SelectionEvent will contain the
473      * Composite on which the decoration is rendered that received the click.
474      * The <code>x</code> and <code>y</code> fields will be in coordinates
475      * relative to the display. The <code>data</code> field will contain the
476      * decoration that received the event.
477      * </p>
478      *
479      * @param listener
480      * the listener which should be notified
481      *
482      * @see org.eclipse.swt.events.MenuDetectListener
483      * @see org.eclipse.swt.events.MenuDetectEvent
484      * @see #removeMenuDetectListener
485      */

486     public void addMenuDetectListener(MenuDetectListener listener) {
487         menuDetectListeners.add(listener);
488     }
489
490     /**
491      * Removes the listener from the collection of listeners who will be
492      * notified when the platform-specific context menu trigger has occurred.
493      *
494      * @param listener
495      * the listener which should no longer be notified. This message
496      * has no effect if the listener was not previously added to the
497      * receiver.
498      *
499      * @see org.eclipse.swt.events.MenuDetectListener
500      * @see #addMenuDetectListener
501      */

502     public void removeMenuDetectListener(MenuDetectListener listener) {
503         menuDetectListeners.remove(listener);
504     }
505
506     /**
507      * Adds the listener to the collection of listeners who will be notified
508      * when the decoration is selected, by sending it one of the messages
509      * defined in the <code>SelectionListener</code> interface.
510      * <p>
511      * <code>widgetSelected</code> is called when the decoration is selected
512      * (by mouse click). <code>widgetDefaultSelected</code> is called when the
513      * decoration is double-clicked.
514      * </p>
515      * <p>
516      * The <code>widget</code> field in the SelectionEvent will contain the
517      * Composite on which the decoration is rendered that received the click.
518      * The <code>x</code> and <code>y</code> fields will be in coordinates
519      * relative to that widget. The <code>data</code> field will contain the
520      * decoration that received the event.
521      * </p>
522      *
523      * @param listener
524      * the listener which should be notified
525      *
526      * @see org.eclipse.swt.events.SelectionListener
527      * @see org.eclipse.swt.events.SelectionEvent
528      * @see #removeSelectionListener
529      */

530     public void addSelectionListener(SelectionListener listener) {
531         selectionListeners.add(listener);
532     }
533
534     /**
535      * Removes the listener from the collection of listeners who will be
536      * notified when the decoration is selected.
537      *
538      * @param listener
539      * the listener which should no longer be notified. This message
540      * has no effect if the listener was not previously added to the
541      * receiver.
542      *
543      * @see org.eclipse.swt.events.SelectionListener
544      * @see #addSelectionListener
545      */

546     public void removeSelectionListener(SelectionListener listener) {
547         selectionListeners.remove(listener);
548     }
549
550     /**
551      * Dispose this ControlDecoration. Unhook any listeners that have been
552      * installed on the target control. This method has no effect if the
553      * receiver is already disposed.
554      */

555     public void dispose() {
556         if (control == null) {
557             return;
558         }
559         if (hover != null) {
560             hover.dispose();
561             hover = null;
562         }
563         removeControlListeners();
564         control = null;
565     }
566
567     /**
568      * Get the control that is decorated by the receiver.
569      *
570      * @return the Control decorated by the receiver. May be <code>null</code>
571      * if the control has been uninstalled.
572      */

573     public Control getControl() {
574         return control;
575     }
576
577     /**
578      * Add any listeners needed on the target control and on the composite where
579      * the decoration is to be rendered.
580      */

581     private void addControlListeners() {
582         disposeListener = new DisposeListener() {
583             public void widgetDisposed(DisposeEvent event) {
584                 dispose();
585             }
586         };
587         printAddListener(control, "DISPOSE"); //$NON-NLS-1$
588
control.addDisposeListener(disposeListener);
589
590         focusListener = new FocusListener() {
591             public void focusGained(FocusEvent event) {
592                 hasFocus = true;
593                 if (showOnlyOnFocus) {
594                     update();
595                 }
596             }
597
598             public void focusLost(FocusEvent event) {
599                 hasFocus = false;
600                 if (showOnlyOnFocus) {
601                     update();
602                 }
603             }
604         };
605         printAddListener(control, "FOCUS"); //$NON-NLS-1$
606
control.addFocusListener(focusListener);
607
608         // Listener for painting the decoration
609
paintListener = new PaintListener() {
610             public void paintControl(PaintEvent event) {
611                 Control control = (Control) event.widget;
612                 Rectangle rect = getDecorationRectangle(control);
613                 if (shouldShowDecoration()) {
614                     event.gc.drawImage(getImage(), rect.x, rect.y);
615                 }
616             }
617         };
618
619         // Listener for tracking the end of a hover. Only installed
620
// after a hover begins.
621
mouseMoveListener = new MouseMoveListener() {
622             public void mouseMove(MouseEvent event) {
623                 if (showHover) {
624                     if (!decorationRectangle.contains(event.x, event.y)) {
625                         hideHover();
626                         // No need to listen any longer
627
printRemoveListener(event.widget, "MOUSEMOVE"); //$NON-NLS-1$
628
((Control) event.widget)
629                                 .removeMouseMoveListener(mouseMoveListener);
630                         moveListeningTarget = null;
631                     }
632                 }
633             }
634         };
635
636         // Listener for tracking the beginning of a hover. Always installed.
637
mouseTrackListener = new MouseTrackListener() {
638             public void mouseExit(MouseEvent event) {
639                 // Just in case we didn't catch it before.
640
Control target = (Control) event.widget;
641                 if (target == moveListeningTarget) {
642                     printRemoveListener(target, "MOUSEMOVE"); //$NON-NLS-1$
643
target.removeMouseMoveListener(mouseMoveListener);
644                     moveListeningTarget = null;
645                 }
646                 hideHover();
647             }
648
649             public void mouseHover(MouseEvent event) {
650                 if (showHover) {
651                     decorationRectangle = getDecorationRectangle((Control) event.widget);
652                     if (decorationRectangle.contains(event.x, event.y)) {
653                         showHoverText(getDescriptionText());
654                         Control target = (Control) event.widget;
655                         if (moveListeningTarget == null) {
656                             printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$
657
target.addMouseMoveListener(mouseMoveListener);
658                             moveListeningTarget = target;
659                         } else if (target != moveListeningTarget) {
660                             printRemoveListener(moveListeningTarget,
661                                     "MOUSEMOVE"); //$NON-NLS-1$
662
moveListeningTarget
663                                     .removeMouseMoveListener(mouseMoveListener);
664                             printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$
665
target.addMouseMoveListener(mouseMoveListener);
666                             moveListeningTarget = target;
667                         } else {
668                             // It is already installed on this control.
669
}
670                     }
671                 }
672             }
673
674             public void mouseEnter(MouseEvent event) {
675                 // Nothing to do until a hover occurs.
676
}
677         };
678
679         compositeListener = new Listener() {
680             public void handleEvent(Event event) {
681                 // Don't forward events if decoration is not showing
682
if (!visible) {
683                     return;
684                 }
685                 // Notify listeners if any are registered.
686
switch (event.type) {
687                 case SWT.MouseDown:
688                     if (!selectionListeners.isEmpty())
689                         notifySelectionListeners(event);
690                     break;
691                 case SWT.MouseDoubleClick:
692                     if (!selectionListeners.isEmpty())
693                         notifySelectionListeners(event);
694                     break;
695                 case SWT.MenuDetect:
696                     if (!menuDetectListeners.isEmpty())
697                         notifyMenuDetectListeners(event);
698                     break;
699                 }
700             }
701         };
702
703         // We do not know which parent in the control hierarchy
704
// is providing the decoration space, so hook all the way up, until
705
// the shell or the specified parent composite is reached.
706
Composite c = control.getParent();
707         while (c != null) {
708             installCompositeListeners(c);
709             if (composite != null && composite == c) {
710                 // We just installed on the specified composite, so stop.
711
c = null;
712             } else if (c instanceof Shell) {
713                 // We just installed on a shell, so don't go further
714
c = null;
715             } else {
716                 c = c.getParent();
717             }
718         }
719         // force a redraw of the decoration area so our paint listener
720
// is notified.
721
update();
722     }
723
724     /*
725      * Install the listeners used to paint and track mouse events on the
726      * composite.
727      */

728     private void installCompositeListeners(Composite c) {
729         if (!c.isDisposed()) {
730             printAddListener(c, "PAINT"); //$NON-NLS-1$
731
c.addPaintListener(paintListener);
732             printAddListener(c, "MOUSETRACK"); //$NON-NLS-1$
733
c.addMouseTrackListener(mouseTrackListener);
734             printAddListener(c, "SWT.MenuDetect"); //$NON-NLS-1$
735
c.addListener(SWT.MenuDetect, compositeListener);
736             printAddListener(c, "SWT.MouseDown"); //$NON-NLS-1$
737
c.addListener(SWT.MouseDown, compositeListener);
738             printAddListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$
739
c.addListener(SWT.MouseDoubleClick, compositeListener);
740         }
741     }
742
743     /*
744      * Remove the listeners used to paint and track mouse events on the
745      * composite.
746      */

747     private void removeCompositeListeners(Composite c) {
748         if (!c.isDisposed()) {
749             printRemoveListener(c, "PAINT"); //$NON-NLS-1$
750
c.removePaintListener(paintListener);
751             printRemoveListener(c, "MOUSETRACK"); //$NON-NLS-1$
752
c.removeMouseTrackListener(mouseTrackListener);
753             printRemoveListener(c, "SWT.MenuDetect"); //$NON-NLS-1$
754
c.removeListener(SWT.MenuDetect, compositeListener);
755             printRemoveListener(c, "SWT.MouseDown"); //$NON-NLS-1$
756
c.removeListener(SWT.MouseDown, compositeListener);
757             printRemoveListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$
758
c.removeListener(SWT.MouseDoubleClick, compositeListener);
759         }
760     }
761
762     private void notifySelectionListeners(Event event) {
763         if (!(event.widget instanceof Control)) {
764             return;
765         }
766         if (getDecorationRectangle((Control) event.widget).contains(event.x,
767                 event.y)) {
768             SelectionEvent clientEvent = new SelectionEvent(event);
769             clientEvent.data = this;
770             if (getImage() != null) {
771                 clientEvent.height = getImage().getBounds().height;
772                 clientEvent.width = getImage().getBounds().width;
773             }
774             Object JavaDoc[] listeners;
775             switch (event.type) {
776             case SWT.MouseDoubleClick:
777                 if (event.button == 1) {
778                     listeners = selectionListeners.getListeners();
779                     for (int i = 0; i < listeners.length; i++) {
780                         ((SelectionListener) listeners[i])
781                                 .widgetDefaultSelected(clientEvent);
782                     }
783                 }
784                 break;
785             case SWT.MouseDown:
786                 if (event.button == 1) {
787                     listeners = selectionListeners.getListeners();
788                     for (int i = 0; i < listeners.length; i++) {
789                         ((SelectionListener) listeners[i])
790                                 .widgetSelected(clientEvent);
791                     }
792                 }
793                 break;
794             }
795         }
796     }
797
798     private void notifyMenuDetectListeners(Event event) {
799         if (getDecorationRectangle(null).contains(event.x, event.y)) {
800             MenuDetectEvent clientEvent = new MenuDetectEvent(event);
801             clientEvent.data = this;
802             Object JavaDoc[] listeners = menuDetectListeners.getListeners();
803             for (int i = 0; i < listeners.length; i++) {
804                 ((MenuDetectListener) listeners[i]).menuDetected(clientEvent);
805
806             }
807         }
808     }
809
810     /**
811      * Show the specified text using the same hover dialog as is used to show
812      * decorator descriptions. When {@link #setShowHover(boolean)} has been set
813      * to <code>true</code>, a decoration's description text will be shown in
814      * an info hover over the field's control whenever the mouse hovers over the
815      * decoration. This method can be used to show a decoration's description
816      * text at other times (such as when the control receives focus), or to show
817      * other text associated with the field.
818      *
819      * @param text
820      * the text to be shown in the info hover, or <code>null</code>
821      * if no text should be shown.
822      */

823     public void showHoverText(String JavaDoc text) {
824         if (control == null) {
825             return;
826         }
827         showHoverText(text, control);
828     }
829
830     /**
831      * Hide any hover popups that are currently showing on the control. When
832      * {@link #setShowHover(boolean)} has been set to <code>true</code>, a
833      * decoration's description text will be shown in an info hover over the
834      * field's control as long as the mouse hovers over the decoration, and will
835      * be hidden when the mouse exits the decoration. This method can be used to
836      * hide a hover, whether it was shown explicitly using
837      * {@link #showHoverText(String)}, or was showing because the user was
838      * hovering in the decoration.
839      * <p>
840      * This message has no effect if there is no current hover.
841      *
842      */

843     public void hideHover() {
844         if (hover != null) {
845             hover.setVisible(false);
846         }
847     }
848
849     /**
850      * Show the control decoration. This message has no effect if the decoration
851      * is already showing. If {@link #setShowOnlyOnFocus(boolean)} is set to
852      * <code>true</code>, the decoration will only be shown if the control
853      * has focus.
854      */

855     public void show() {
856         if (!visible) {
857             visible = true;
858             update();
859         }
860     }
861
862     /**
863      * Hide the control decoration. This message has no effect if the decoration
864      * is already hidden.
865      */

866     public void hide() {
867         if (visible) {
868             visible = false;
869             update();
870         }
871     }
872
873     /**
874      * Get the description text that may be shown in a hover for this
875      * decoration.
876      *
877      * @return the text to be shown as a description for the decoration, or
878      * <code>null</code> if none has been set.
879      */

880     public String JavaDoc getDescriptionText() {
881         return descriptionText;
882     }
883
884     /**
885      * Set the image shown in this control decoration. Update the rendered
886      * decoration.
887      *
888      * @param text
889      * the text to be shown as a description for the decoration, or
890      * <code>null</code> if none has been set.
891      */

892     public void setDescriptionText(String JavaDoc text) {
893         this.descriptionText = text;
894         update();
895     }
896
897     /**
898      * Get the image shown in this control decoration.
899      *
900      * @return the image to be shown adjacent to the control, or
901      * <code>null</code> if one has not been set.
902      */

903     public Image getImage() {
904         return image;
905     }
906
907     /**
908      * Set the image shown in this control decoration. Update the rendered
909      * decoration.
910      *
911      * @param image
912      * the image to be shown adjacent to the control
913      */

914     public void setImage(Image image) {
915         this.image = image;
916         update();
917     }
918
919     /**
920      * Get the boolean that controls whether the decoration is shown only when
921      * the control has focus. The default value of this setting is
922      * <code>false</code>.
923      *
924      * @return <code>true</code> if the decoration should only be shown when
925      * the control has focus, and <code>false</code> if it should
926      * always be shown. Note that if the control is not capable of
927      * receiving focus (<code>SWT.NO_FOCUS</code>), then the
928      * decoration will never show when this value is <code>true</code>.
929      */

930     public boolean getShowOnlyOnFocus() {
931         return showOnlyOnFocus;
932     }
933
934     /**
935      * Set the boolean that controls whether the decoration is shown only when
936      * the control has focus. The default value of this setting is
937      * <code>false</code>.
938      *
939      * @param showOnlyOnFocus
940      * <code>true</code> if the decoration should only be shown
941      * when the control has focus, and <code>false</code> if it
942      * should always be shown. Note that if the control is not
943      * capable of receiving focus (<code>SWT.NO_FOCUS</code>),
944      * then the decoration will never show when this value is
945      * <code>true</code>.
946      */

947     public void setShowOnlyOnFocus(boolean showOnlyOnFocus) {
948         this.showOnlyOnFocus = showOnlyOnFocus;
949         update();
950     }
951
952     /**
953      * Get the boolean that controls whether the decoration's description text
954      * should be shown in a hover when the user hovers over the decoration. The
955      * default value of this setting is <code>true</code>.
956      *
957      * @return <code>true</code> if a hover popup containing the decoration's
958      * description text should be shown when the user hovers over the
959      * decoration, and <code>false</code> if a hover should not be
960      * shown.
961      */

962     public boolean getShowHover() {
963         return showHover;
964     }
965
966     /**
967      * Set the boolean that controls whether the decoration's description text
968      * should be shown in a hover when the user hovers over the decoration. The
969      * default value of this setting is <code>true</code>.
970      *
971      * @param showHover
972      * <code>true</code> if a hover popup containing the
973      * decoration's description text should be shown when the user
974      * hovers over the decoration, and <code>false</code> if a
975      * hover should not be shown.
976      */

977     public void setShowHover(boolean showHover) {
978         this.showHover = showHover;
979         update();
980     }
981
982     /**
983      * Get the margin width in pixels that should be used between the decorator
984      * and the horizontal edge of the control. The default value of this setting
985      * is <code>0</code>.
986      *
987      * @return the number of pixels that should be reserved between the
988      * horizontal edge of the control and the adjacent edge of the
989      * decoration.
990      */

991     public int getMarginWidth() {
992         return marginWidth;
993     }
994
995     /**
996      * Set the margin width in pixels that should be used between the decorator
997      * and the horizontal edge of the control. The default value of this setting
998      * is <code>0</code>.
999      *
1000     * @param marginWidth
1001     * the number of pixels that should be reserved between the
1002     * horizontal edge of the control and the adjacent edge of the
1003     * decoration.
1004     */

1005    public void setMarginWidth(int marginWidth) {
1006        this.marginWidth = marginWidth;
1007        update();
1008    }
1009
1010    /**
1011     * Something has changed, requiring redraw. Redraw the decoration and update
1012     * the hover text if appropriate.
1013     */

1014    protected void update() {
1015        if (control == null || control.isDisposed()) {
1016            return;
1017        }
1018        Rectangle rect = getDecorationRectangle(control.getShell());
1019        // Redraw this rectangle in all children
1020
control.getShell()
1021                .redraw(rect.x, rect.y, rect.width, rect.height, true);
1022        control.getShell().update();
1023        if (hover != null && getDescriptionText() != null) {
1024            hover.setText(getDescriptionText(), getDecorationRectangle(control
1025                    .getParent()), control);
1026        }
1027    }
1028
1029    /*
1030     * Show the specified text in the hover, positioning the hover near the
1031     * specified control.
1032     */

1033    private void showHoverText(String JavaDoc text, Control hoverNear) {
1034        // If we aren't to show a hover, don't do anything.
1035
if (!showHover) {
1036            return;
1037        }
1038        // If there is no text, don't do anything.
1039
if (text == null) {
1040            hideHover();
1041            return;
1042        }
1043
1044        // If there is no control, nothing to do
1045
if (control == null) {
1046            return;
1047        }
1048        // Create the hover if it's not showing
1049
if (hover == null) {
1050            hover = new Hover(hoverNear.getShell());
1051        }
1052        hover.setText(text, getDecorationRectangle(control.getParent()),
1053                control);
1054        hover.setVisible(true);
1055    }
1056
1057    /*
1058     * Remove any listeners installed on the controls.
1059     */

1060    private void removeControlListeners() {
1061        if (control == null) {
1062            return;
1063        }
1064        printRemoveListener(control, "FOCUS"); //$NON-NLS-1$
1065
control.removeFocusListener(focusListener);
1066        focusListener = null;
1067
1068        printRemoveListener(control, "DISPOSE"); //$NON-NLS-1$
1069
control.removeDisposeListener(disposeListener);
1070        disposeListener = null;
1071
1072        Composite c = control.getParent();
1073        while (c != null) {
1074            removeCompositeListeners(c);
1075            if (composite != null && composite == c) {
1076                // We previously installed listeners only to the specified
1077
// composite, so stop.
1078
c = null;
1079            } else if (c instanceof Shell) {
1080                // We previously installed listeners only up to the first Shell
1081
// encountered, so stop.
1082
c = null;
1083            } else {
1084                c = c.getParent();
1085            }
1086        }
1087        paintListener = null;
1088        mouseTrackListener = null;
1089        compositeListener = null;
1090
1091        // We may have a remaining mouse move listener installed
1092
if (moveListeningTarget != null) {
1093            printRemoveListener(moveListeningTarget, "MOUSEMOVE"); //$NON-NLS-1$
1094
moveListeningTarget.removeMouseMoveListener(mouseMoveListener);
1095            moveListeningTarget = null;
1096            mouseMoveListener = null;
1097        }
1098        if (DEBUG) {
1099            if (listenerInstalls > 0) {
1100                System.out.println("LISTENER LEAK>>>CHECK TRACE ABOVE"); //$NON-NLS-1$
1101
} else if (listenerInstalls < 0) {
1102                System.out
1103                        .println("REMOVED UNREGISTERED LISTENERS>>>CHECK TRACE ABOVE"); //$NON-NLS-1$
1104
} else {
1105                System.out.println("ALL INSTALLED LISTENERS WERE REMOVED."); //$NON-NLS-1$
1106
}
1107        }
1108    }
1109
1110    /**
1111     * Return the rectangle in which the decoration should be rendered, in
1112     * coordinates relative to the specified control. If the specified control
1113     * is null, return the rectangle in display coordinates.
1114     *
1115     * @param targetControl
1116     * the control whose coordinates should be used
1117     * @return the rectangle in which the decoration should be rendered
1118     */

1119    protected Rectangle getDecorationRectangle(Control targetControl) {
1120        if (getImage() == null || control == null) {
1121            return new Rectangle(0, 0, 0, 0);
1122        }
1123        // Compute the bounds first relative to the control's parent.
1124
Rectangle imageBounds = getImage().getBounds();
1125        Rectangle controlBounds = control.getBounds();
1126        int x, y;
1127        // Compute x
1128
if ((position & SWT.RIGHT) == SWT.RIGHT) {
1129            x = controlBounds.x + controlBounds.width + marginWidth;
1130        } else {
1131            // default is left
1132
x = controlBounds.x - imageBounds.width - marginWidth;
1133        }
1134        // Compute y
1135
if ((position & SWT.TOP) == SWT.TOP) {
1136            y = controlBounds.y;
1137        } else if ((position & SWT.BOTTOM) == SWT.BOTTOM) {
1138            y = controlBounds.y + control.getBounds().height
1139                    - imageBounds.height;
1140        } else {
1141            // default is center
1142
y = controlBounds.y
1143                    + (control.getBounds().height - imageBounds.height) / 2;
1144        }
1145
1146        // Now convert to coordinates relative to the target control.
1147
Point globalPoint = control.getParent().toDisplay(x, y);
1148        Point targetPoint;
1149        if (targetControl == null) {
1150            targetPoint = globalPoint;
1151        } else {
1152            targetPoint = targetControl.toControl(globalPoint);
1153        }
1154        return new Rectangle(targetPoint.x, targetPoint.y, imageBounds.width,
1155                imageBounds.height);
1156    }
1157
1158    /*
1159     * Return true if the decoration should be shown, false if it should not.
1160     */

1161    private boolean shouldShowDecoration() {
1162        if (!visible) {
1163            return false;
1164        }
1165        if (control == null || control.isDisposed() || getImage() == null) {
1166            return false;
1167        }
1168
1169        if (!control.isVisible()) {
1170            return false;
1171        }
1172        if (showOnlyOnFocus) {
1173            return hasFocus;
1174        }
1175        return true;
1176    }
1177
1178    /*
1179     * If in debug mode, print info about adding the specified listener.
1180     */

1181    private void printAddListener(Widget widget, String JavaDoc listenerType) {
1182        listenerInstalls++;
1183        if (DEBUG) {
1184            System.out
1185                    .println("Added listener>>>" + listenerType + " to>>>" + widget); //$NON-NLS-1$//$NON-NLS-2$
1186
}
1187    }
1188
1189    /*
1190     * If in debug mode, print info about adding the specified listener.
1191     */

1192    private void printRemoveListener(Widget widget, String JavaDoc listenerType) {
1193        listenerInstalls--;
1194        if (DEBUG) {
1195            System.out
1196                    .println("Removed listener>>>" + listenerType + " from>>>" + widget); //$NON-NLS-1$//$NON-NLS-2$
1197
}
1198    }
1199}
1200
Popular Tags