001    /* MetalToolTipUI.java
002       Copyright (C) 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.metal;
040    
041    import java.awt.Color;
042    import java.awt.Dimension;
043    import java.awt.Font;
044    import java.awt.FontMetrics;
045    import java.awt.Graphics;
046    import java.awt.event.InputEvent;
047    import java.awt.event.KeyEvent;
048    
049    import javax.swing.AbstractButton;
050    import javax.swing.JComponent;
051    import javax.swing.JMenuItem;
052    import javax.swing.JToolTip;
053    import javax.swing.KeyStroke;
054    import javax.swing.UIManager;
055    import javax.swing.border.Border;
056    import javax.swing.plaf.ComponentUI;
057    import javax.swing.plaf.UIResource;
058    import javax.swing.plaf.basic.BasicToolTipUI;
059    
060    /**
061     * A UI delegate for the {@link JToolTip} component.
062     */
063    public class MetalToolTipUI
064      extends BasicToolTipUI
065    {
066      /** 
067       * The amount of space between the tool tip text and the accelerator 
068       * description (if visible). 
069       */
070      public static final int padSpaceBetweenStrings = 12;
071    
072      /** The shared UI instance. */
073      private static MetalToolTipUI instance;
074      
075      /** A flag controlling the visibility of the accelerator (if there is one). */
076      private boolean isAcceleratorHidden;
077      
078      /** A string representing the accelerator key for the component. */
079      private String acceleratorString;
080      
081      /** 
082       * The delimiter for the accelerator string.
083       */
084      private String acceleratorDelimiter;
085      
086      /** The font for the accelerator string. */
087      private Font acceleratorFont;
088      
089      /** The color for the accelerator string. */
090      private Color acceleratorForeground;
091      
092      /** The active border. */
093      private Border activeBorder;
094      
095      /** The inactive border. */
096      private Border inactiveBorder;
097      
098      /**
099       * Constructs a new instance of <code>MetalToolTipUI</code>.
100       */
101      public MetalToolTipUI()
102      {
103        super();
104        activeBorder = UIManager.getBorder("ToolTip.border");
105        inactiveBorder = UIManager.getBorder("ToolTip.borderInactive");
106        isAcceleratorHidden = UIManager.getBoolean("ToolTip.hideAccelerator");
107        acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont");
108        acceleratorForeground = UIManager.getColor("MenuItem.acceleratorForeground");
109        acceleratorDelimiter = UIManager.getString("MenuItem.acceleratorDelimiter");
110      }
111    
112      /**
113       * Returns a shared instance of the <code>MetalToolTipUI</code> class.
114       * Although this UI delegate does maintain state information, there is never
115       * more than one tool tip visible, so it is OK to use a shared instance.
116       *
117       * @param component  the component (a {@link JToolTip}).
118       *
119       * @return A shared instance of the <code>MetalToolTipUI</code> class.
120       */
121      public static ComponentUI createUI(JComponent component)
122      {
123        if (instance == null)
124          instance = new MetalToolTipUI();
125        return instance;
126      }
127      
128      /**
129       * Returns a string representing the accelerator key (if there is one) for 
130       * the component that the tool tip belongs to.
131       * 
132       * @return A string representing the accelerator key.
133       */
134      public String getAcceleratorString()
135      {
136        return acceleratorString;   
137      }
138      
139      /**
140       * Installs the UI for the specified component (a {@link JToolTip}).
141       * 
142       * @param c  the {@link JToolTip} component.
143       */
144      public void installUI(JComponent c)
145      {
146        super.installUI(c);
147        Border existingBorder = c.getBorder();
148        if (existingBorder == null || existingBorder instanceof UIResource)
149          {
150            if (c.isEnabled())
151              c.setBorder(activeBorder);
152            else
153              c.setBorder(inactiveBorder);
154          }   
155      }
156      
157      /**
158       * Clears the defaults set in {@link #installUI(JComponent)}.
159       * 
160       * @param c  the component.
161       */
162      public void uninstallUI(JComponent c)
163      {
164        super.uninstallUI(c);
165        if (c.getBorder() instanceof UIResource)
166          c.setBorder(null);
167      }
168      
169      /**
170       * Returns <code>true</code> if the accelerator string is hidden, and
171       * <code>false</code> otherwise.  This setting is controlled by the
172       * <code>ToolTip.hideAccelerator</code> entry in the UI defaults table.
173       *
174       * @return A boolean.
175       */
176      protected boolean isAcceleratorHidden()
177      {
178        return isAcceleratorHidden;
179      }
180      
181      /**
182       * Returns the preferred size for the {@link JToolTip} component.
183       * 
184       * @param c  the component (a {@link JToolTip}).
185       * 
186       * @return The preferred size.
187       */
188      public Dimension getPreferredSize(JComponent c)
189      {
190        Dimension d = super.getPreferredSize(c);
191        String acc = getAcceleratorString();
192        if (acc != null && ! acc.equals(""))
193          {
194            FontMetrics fm = c.getFontMetrics(c.getFont());
195            d.width += fm.stringWidth(acc);
196          }
197        return d;
198      }
199      
200      /**
201       * Paints the tool tip.
202       * 
203       * @param g  the graphics context.
204       * @param c  the {@link JToolTip} component.
205       */
206      public void paint(Graphics g, JComponent c)
207      {
208        super.paint(g, c);
209        // Somehow paint accelerator. Keep care for possible HTML rendering.
210      }
211      
212      /**
213       * Returns a string representing the accelerator for the component, or 
214       * <code>null</code> if the component has no accelerator.
215       * 
216       * @param c  the component.
217       * 
218       * @return A string representing the accelerator (possibly 
219       *         <code>null</code>).
220       */
221      private String fetchAcceleratorString(JComponent c)
222      {
223        String result = null;
224        if (c instanceof JToolTip)
225          {
226            JToolTip toolTip = (JToolTip) c;
227            JComponent component = toolTip.getComponent();
228            KeyStroke ks = null;
229            int mne = 0;
230            if (component instanceof JMenuItem)
231              {
232                JMenuItem item = (JMenuItem) component;
233                ks = item.getAccelerator();
234                if (ks == null)
235                    mne = item.getMnemonic();
236              }
237            else if (component instanceof AbstractButton)
238              {
239                AbstractButton button = (AbstractButton) component;
240                mne = button.getMnemonic();
241              }
242            if (mne > 0)
243              ks = KeyStroke.getKeyStroke(Character.toUpperCase((char) mne), 
244                    InputEvent.ALT_MASK, false);
245            if (ks != null)
246              result = acceleratorToString(ks);
247          }
248        return result;
249      }
250      
251      /**
252       * Returns a string representing an accelerator.
253       * 
254       * @param accelerator  the accelerator (<code>null</code> not permitted).
255       * 
256       * @return A string representing an accelerator.
257       */
258      private String acceleratorToString(KeyStroke accelerator)
259      {
260        // convert keystroke into string format
261        String modifiersText = "";
262        int modifiers = accelerator.getModifiers();
263        char keyChar = accelerator.getKeyChar();
264        int keyCode = accelerator.getKeyCode();
265        
266        if (modifiers != 0)
267          modifiersText = KeyEvent.getKeyModifiersText(modifiers) 
268              + acceleratorDelimiter;
269    
270        if (keyCode == KeyEvent.VK_UNDEFINED)
271          return modifiersText + keyChar;
272        else
273          return modifiersText + KeyEvent.getKeyText(keyCode);
274      }
275    
276    }