OSDN Git Service

* javax/naming/CompoundName.java (CompoundName): Don't check for
[pf3gnuchains/gcc-fork.git] / libjava / javax / swing / ToolTipManager.java
1 /* ToolTipManager.java --
2    Copyright (C) 2002, 2004 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package javax.swing;
39
40 import java.awt.AWTEvent;
41 import java.awt.Component;
42 import java.awt.Container;
43 import java.awt.Dimension;
44 import java.awt.FlowLayout;
45 import java.awt.Insets;
46 import java.awt.LayoutManager;
47 import java.awt.Panel;
48 import java.awt.Point;
49 import java.awt.Rectangle;
50 import java.awt.event.*;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.awt.event.MouseAdapter;
54 import java.awt.event.MouseEvent;
55 import java.awt.event.MouseMotionListener;
56 import javax.swing.JComponent;
57 import javax.swing.Popup;
58 import javax.swing.PopupFactory;
59 import javax.swing.SwingUtilities;
60 import javax.swing.Timer;
61
62
63 /**
64  * This class is responsible for the registration of JToolTips to Components
65  * and for displaying them when appropriate.
66  */
67 public class ToolTipManager extends MouseAdapter implements MouseMotionListener
68 {
69   /**
70    * This ActionListener is associated with the Timer that listens to whether
71    * the JToolTip can be hidden after four seconds.
72    */
73   protected class stillInsideTimerAction implements ActionListener
74   {
75     /**
76      * This method creates a new stillInsideTimerAction object.
77      */
78     protected stillInsideTimerAction()
79     {
80     }
81
82     /**
83      * This method hides the JToolTip when the Timer has finished.
84      *
85      * @param event The ActionEvent.
86      */
87     public void actionPerformed(ActionEvent event)
88     {
89       hideTip();
90     }
91   }
92
93   /**
94    * This Actionlistener is associated with the Timer that listens to whether
95    * the mouse cursor has re-entered the JComponent in time for an immediate
96    * redisplay of the JToolTip.
97    */
98   protected class outsideTimerAction implements ActionListener
99   {
100     /**
101      * This method creates a new outsideTimerAction object.
102      */
103     protected outsideTimerAction()
104     {
105     }
106
107     /**
108      * This method is called when the Timer that listens to whether the mouse
109      * cursor has re-entered the JComponent has run out.
110      *
111      * @param event The ActionEvent.
112      */
113     public void actionPerformed(ActionEvent event)
114     {
115     }
116   }
117
118   /**
119    * This ActionListener is associated with the Timer that listens to whether
120    * it is time for the JToolTip to be displayed after the mouse has entered
121    * the JComponent.
122    */
123   protected class insideTimerAction implements ActionListener
124   {
125     /**
126      * This method creates a new insideTimerAction object.
127      */
128     protected insideTimerAction()
129     {
130     }
131
132     /**
133      * This method displays the JToolTip when the Mouse has been still for the
134      * delay.
135      *
136      * @param event The ActionEvent.
137      */
138     public void actionPerformed(ActionEvent event)
139     {
140       showTip();
141       if (insideTimer != null)
142         insideTimer.start();
143     }
144   }
145
146   /**
147    * The Timer that determines whether the Mouse has been still long enough
148    * for the JToolTip to be displayed.
149    */
150   Timer enterTimer;
151
152   /**
153    * The Timer that determines whether the Mouse has re-entered the JComponent
154    * quickly enough for the JToolTip to be displayed immediately.
155    */
156   Timer exitTimer;
157
158   /**
159    * The Timer that determines whether the JToolTip has been displayed long
160    * enough for it to be hidden.
161    */
162   Timer insideTimer;
163
164   /** A global enabled setting for the ToolTipManager. */
165   private transient boolean enabled = true;
166
167   /** lightWeightPopupEnabled */
168   protected boolean lightWeightPopupEnabled = true;
169
170   /** heavyWeightPopupEnabled */
171   protected boolean heavyWeightPopupEnabled = false;
172
173   /** The shared instance of the ToolTipManager. */
174   private static ToolTipManager shared;
175
176   /** The current component the tooltip is being displayed for. */
177   private static Component currentComponent;
178
179   /** The current tooltip. */
180   private static JToolTip currentTip;
181
182   /** The last known position of the mouse cursor. */
183   private static Point currentPoint;
184
185   /**
186    * The panel that holds the tooltip when the tooltip is displayed fully
187    * inside the current container.
188    */
189   private static Container containerPanel;
190
191   /**
192    * The window used when the tooltip doesn't fit inside the current
193    * container.
194    */
195   private static JWindow tooltipWindow;
196
197   /**
198    * Creates a new ToolTipManager and sets up the timers.
199    */
200   ToolTipManager()
201   {
202     enterTimer = new Timer(750, new insideTimerAction());
203     enterTimer.setRepeats(false);
204
205     insideTimer = new Timer(4000, new stillInsideTimerAction());
206     insideTimer.setRepeats(false);
207
208     exitTimer = new Timer(500, new outsideTimerAction());
209     exitTimer.setRepeats(false);
210   }
211
212   /**
213    * This method returns the shared instance of ToolTipManager used by all
214    * JComponents.
215    *
216    * @return The shared instance of ToolTipManager.
217    */
218   public static ToolTipManager sharedInstance()
219   {
220     if (shared == null)
221       shared = new ToolTipManager();
222
223     return shared;
224   }
225
226   /**
227    * This method sets whether ToolTips are enabled or disabled for all
228    * JComponents.
229    *
230    * @param enabled Whether ToolTips are enabled or disabled for all
231    *        JComponents.
232    */
233   public void setEnabled(boolean enabled)
234   {
235     if (! enabled)
236       {
237         enterTimer.stop();
238         exitTimer.stop();
239         insideTimer.stop();
240       }
241
242     this.enabled = enabled;
243   }
244
245   /**
246    * This method returns whether ToolTips are enabled.
247    *
248    * @return Whether ToolTips are enabled.
249    */
250   public boolean isEnabled()
251   {
252     return enabled;
253   }
254
255   /**
256    * This method returns whether LightweightToolTips are enabled.
257    *
258    * @return Whether LighweightToolTips are enabled.
259    */
260   public boolean isLightWeightPopupEnabled()
261   {
262     return lightWeightPopupEnabled;
263   }
264
265   /**
266    * This method sets whether LightweightToolTips are enabled. If you mix
267    * Lightweight and Heavyweight components, you must set this to false to
268    * ensure that the ToolTips popup above all other components.
269    *
270    * @param enabled Whether LightweightToolTips will be enabled.
271    */
272   public void setLightWeightPopupEnabled(boolean enabled)
273   {
274     lightWeightPopupEnabled = enabled;
275     heavyWeightPopupEnabled = ! enabled;
276   }
277
278   /**
279    * This method returns the initial delay before the ToolTip is shown when
280    * the mouse enters a Component.
281    *
282    * @return The initial delay before the ToolTip is shown.
283    */
284   public int getInitialDelay()
285   {
286     return enterTimer.getDelay();
287   }
288
289   /**
290    * This method sets the initial delay before the ToolTip is shown when the
291    * mouse enters a Component.
292    *
293    * @param delay The initial delay before the ToolTip is shown.
294    */
295   public void setInitialDelay(int delay)
296   {
297     enterTimer.setDelay(delay);
298   }
299
300   /**
301    * This method returns the time the ToolTip will be shown before being
302    * hidden.
303    *
304    * @return The time the ToolTip will be shown before being hidden.
305    */
306   public int getDismissDelay()
307   {
308     return insideTimer.getDelay();
309   }
310
311   /**
312    * This method sets the time the ToolTip will be shown before being hidden.
313    *
314    * @param delay The time the ToolTip will be shown before being hidden.
315    */
316   public void setDismissDelay(int delay)
317   {
318     insideTimer.setDelay(delay);
319   }
320
321   /**
322    * This method returns the amount of delay where if the mouse re-enters a
323    * Component, the tooltip will be shown immediately.
324    *
325    * @return The reshow delay.
326    */
327   public int getReshowDelay()
328   {
329     return exitTimer.getDelay();
330   }
331
332   /**
333    * This method sets the amount of delay where if the mouse re-enters a
334    * Component, the tooltip will be shown immediately.
335    *
336    * @param delay The reshow delay.
337    */
338   public void setReshowDelay(int delay)
339   {
340     exitTimer.setDelay(delay);
341   }
342
343   /**
344    * This method registers a JComponent with the ToolTipManager.
345    *
346    * @param component The JComponent to register with the ToolTipManager.
347    */
348   public void registerComponent(JComponent component)
349   {
350     component.addMouseListener(this);
351     component.addMouseMotionListener(this);
352   }
353
354   /**
355    * This method unregisters a JComponent with the ToolTipManager.
356    *
357    * @param component The JComponent to unregister with the ToolTipManager.
358    */
359   public void unregisterComponent(JComponent component)
360   {
361     component.removeMouseMotionListener(this);
362     component.removeMouseListener(this);
363   }
364
365   /**
366    * This method is called whenever the mouse enters a JComponent registered
367    * with the ToolTipManager. When the mouse enters within the period of time
368    * specified by the reshow delay, the tooltip will be displayed
369    * immediately. Otherwise, it must wait for the initial delay before
370    * displaying the tooltip.
371    *
372    * @param event The MouseEvent.
373    */
374   public void mouseEntered(MouseEvent event)
375   {
376     if (currentComponent != null
377         && getContentPaneDeepestComponent(event) == currentComponent)
378       return;
379     currentPoint = event.getPoint();
380     currentComponent = (Component) event.getSource();
381
382     if (exitTimer.isRunning())
383       {
384         exitTimer.stop();
385         showTip();
386         insideTimer.start();
387         return;
388       }
389
390     // This should always be stopped unless we have just fake-exited.
391     if (! enterTimer.isRunning())
392       enterTimer.start();
393   }
394
395   /**
396    * This method is called when the mouse exits a JComponent registered with
397    * the ToolTipManager. When the mouse exits, the tooltip should be hidden
398    * immediately.
399    *
400    * @param event The MouseEvent.
401    */
402   public void mouseExited(MouseEvent event)
403   {
404     if (getContentPaneDeepestComponent(event) == currentComponent)
405       return;
406
407     currentPoint = event.getPoint();
408     currentComponent = null;
409     hideTip();
410
411     if (! enterTimer.isRunning() && insideTimer.isRunning())
412       exitTimer.start();
413     if (enterTimer.isRunning())
414       enterTimer.stop();
415     if (insideTimer.isRunning())
416       insideTimer.stop();
417   }
418
419   /**
420    * This method is called when the mouse is pressed on a JComponent
421    * registered with the ToolTipManager. When the mouse is pressed, the
422    * tooltip (if it is shown) must be hidden immediately.
423    *
424    * @param event The MouseEvent.
425    */
426   public void mousePressed(MouseEvent event)
427   {
428     currentPoint = event.getPoint();
429     if (enterTimer.isRunning())
430       enterTimer.restart();
431     else if (insideTimer.isRunning())
432       {
433         insideTimer.stop();
434         hideTip();
435       }
436     currentComponent.invalidate();
437     currentComponent.validate();
438     currentComponent.repaint();
439   }
440
441   /**
442    * This method is called when the mouse is dragged in a JComponent
443    * registered with the ToolTipManager.
444    *
445    * @param event The MouseEvent.
446    */
447   public void mouseDragged(MouseEvent event)
448   {
449     currentPoint = event.getPoint();
450     if (enterTimer.isRunning())
451       enterTimer.restart();
452   }
453
454   /**
455    * This method is called when the mouse is moved in a JComponent registered
456    * with the ToolTipManager.
457    *
458    * @param event The MouseEvent.
459    */
460   public void mouseMoved(MouseEvent event)
461   {
462     currentPoint = event.getPoint();
463     if (currentTip != null)
464       currentTip.setTipText(((JComponent) currentComponent).getToolTipText(event));
465     if (enterTimer.isRunning())
466       enterTimer.restart();
467   }
468
469   /**
470    * This method displays the ToolTip. It can figure out the method needed to
471    * show it as well (whether to display it in heavyweight/lightweight panel
472    * or a window.)
473    */
474   private void showTip()
475   {
476     if (! enabled)
477       return;
478
479     if (currentTip == null
480         || currentTip.getComponent() != currentComponent
481         && currentComponent instanceof JComponent)
482       currentTip = ((JComponent) currentComponent).createToolTip();
483     Point p = currentPoint;
484     Dimension dims = currentTip.getPreferredSize();
485     if (canToolTipFit(currentTip))
486       {
487         JLayeredPane pane = ((JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class,
488                                                                            currentComponent))
489                             .getLayeredPane();
490
491         // This should never happen, but just in case.
492         if (pane == null)
493           return;
494
495         if (containerPanel != null)
496           hideTip();
497         if (isLightWeightPopupEnabled())
498           {
499             containerPanel = new Panel();
500             JRootPane root = new JRootPane();
501             root.getContentPane().add(currentTip);
502             containerPanel.add(root);
503           }
504         else
505           {
506             containerPanel = new JPanel();
507             containerPanel.add(currentTip);
508           }
509         LayoutManager lm = containerPanel.getLayout();
510         if (lm instanceof FlowLayout)
511           {
512             FlowLayout fm = (FlowLayout) lm;
513             fm.setVgap(0);
514             fm.setHgap(0);
515           }
516
517         p = getGoodPoint(p, pane, currentTip, dims);
518
519         pane.add(containerPanel);
520         containerPanel.setBounds(p.x, p.y, dims.width, dims.height);
521         currentTip.setBounds(0, 0, dims.width, dims.height);
522
523         pane.revalidate();
524         pane.repaint();
525       }
526     else
527       {
528         SwingUtilities.convertPointToScreen(p, currentComponent);
529         tooltipWindow = new JWindow();
530         tooltipWindow.getContentPane().add(currentTip);
531         tooltipWindow.setFocusable(false);
532         tooltipWindow.pack();
533         tooltipWindow.setBounds(p.x, p.y, dims.width, dims.height);
534         tooltipWindow.show();
535       }
536     currentTip.setVisible(true);
537   }
538
539   /**
540    * This method hides the ToolTip.
541    */
542   private void hideTip()
543   {
544     if (currentTip == null || ! currentTip.isVisible() || ! enabled)
545       return;
546     currentTip.setVisible(false);
547     if (containerPanel != null)
548       {
549         Container parent = containerPanel.getParent();
550         if (parent == null)
551           return;
552         parent.remove(containerPanel);
553         parent.invalidate();
554         parent.validate();
555         parent.repaint();
556
557         parent = currentTip.getParent();
558         if (parent == null)
559           return;
560         parent.remove(currentTip);
561
562         containerPanel = null;
563       }
564     if (tooltipWindow != null)
565       {
566         tooltipWindow.hide();
567         tooltipWindow.dispose();
568         tooltipWindow = null;
569       }
570   }
571
572   /**
573    * This method returns a point in the LayeredPane where the ToolTip can be
574    * shown. The point returned (if the ToolTip is to be displayed at the
575    * preferred dimensions) will always place the ToolTip inside the
576    * currentComponent if possible.
577    *
578    * @param p The last known good point for the mouse.
579    * @param c The JLayeredPane in the first RootPaneContainer up from the
580    *        currentComponent.
581    * @param tip The ToolTip to display.
582    * @param dims The ToolTip preferred dimensions (can be null).
583    *
584    * @return A good point to place the ToolTip.
585    */
586   private Point getGoodPoint(Point p, JLayeredPane c, JToolTip tip,
587                              Dimension dims)
588   {
589     if (dims == null)
590       dims = tip.getPreferredSize();
591     Rectangle bounds = currentComponent.getBounds();
592     if (p.x + dims.width > bounds.width)
593       p.x = bounds.width - dims.width;
594     if (p.y + dims.height > bounds.height)
595       p.y = bounds.height - dims.height;
596
597     p = SwingUtilities.convertPoint(currentComponent, p, c);
598     return p;
599   }
600
601   /**
602    * This method returns the deepest component in the content pane for the
603    * first RootPaneContainer up from the currentComponent. This method is
604    * used in conjunction with one of the mouseXXX methods.
605    *
606    * @param e The MouseEvent.
607    *
608    * @return The deepest component in the content pane.
609    */
610   private Component getContentPaneDeepestComponent(MouseEvent e)
611   {
612     Component source = (Component) e.getSource();
613     Container parent = (Container) SwingUtilities.getAncestorOfClass(JRootPane.class,
614                                                                      currentComponent);
615     if (parent == null)
616       return null;
617     parent = ((JRootPane) parent).getContentPane();
618     Point p = e.getPoint();
619     p = SwingUtilities.convertPoint(source, p, parent);
620     Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y);
621     return target;
622   }
623
624   /**
625    * This method returns whether the ToolTip can fit in the first
626    * RootPaneContainer up from the currentComponent.
627    *
628    * @param tip The ToolTip.
629    *
630    * @return Whether the ToolTip can fit.
631    */
632   private boolean canToolTipFit(JToolTip tip)
633   {
634     JRootPane root = (JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class,
635                                                                    currentComponent);
636     if (root == null)
637       return false;
638     Dimension pref = tip.getPreferredSize();
639     Dimension rootSize = root.getSize();
640     if (rootSize.width > pref.width && rootSize.height > pref.height)
641       return true;
642     return false;
643   }
644 }