001/* MetalComboBoxUI.java
002   Copyright (C) 2005 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.plaf.metal;
040
041import java.awt.Color;
042import java.awt.Container;
043import java.awt.Dimension;
044import java.awt.Graphics;
045import java.awt.Insets;
046import java.awt.LayoutManager;
047import java.awt.event.MouseEvent;
048import java.beans.PropertyChangeEvent;
049import java.beans.PropertyChangeListener;
050
051import javax.swing.ComboBoxEditor;
052import javax.swing.Icon;
053import javax.swing.JButton;
054import javax.swing.JComboBox;
055import javax.swing.JComponent;
056import javax.swing.plaf.ComponentUI;
057import javax.swing.plaf.basic.BasicComboBoxUI;
058import javax.swing.plaf.basic.BasicComboPopup;
059import javax.swing.plaf.basic.ComboPopup;
060
061
062/**
063 * A UI delegate for the {@link JComboBox} component.
064 */
065public class MetalComboBoxUI extends BasicComboBoxUI
066{
067  /**
068   * A layout manager that arranges the editor component (if active) and the
069   * button that make up the combo box.
070   */
071  public class MetalComboBoxLayoutManager
072    extends BasicComboBoxUI.ComboBoxLayoutManager
073  {
074    /**
075     * Creates a new instance of the layout manager.
076     */
077    public MetalComboBoxLayoutManager()
078    {
079      // Nothing to do here.
080    }
081    
082    /**
083     * Arranges the editor (if visible) and button that comprise the combo
084     * box.
085     * 
086     * @param parent  the parent.
087     */
088    public void layoutContainer(Container parent)
089    {
090      layoutComboBox(parent, this);
091    }
092    
093    /**
094     * Calls the <code>layoutContainer(Container)</code> method in the super 
095     * class.
096     * 
097     * @param parent  the container.
098     */
099    public void superLayout(Container parent)
100    {
101      super.layoutContainer(parent);
102    }
103  }
104  
105  /**
106   * A listener used to handle property changes in the {@link JComboBox} 
107   * component, to ensure that the UI delegate accurately reflects the current
108   * state in the rendering onscreen.
109   */
110  public class MetalPropertyChangeListener
111    extends BasicComboBoxUI.PropertyChangeHandler
112  {
113    /**
114     * Creates a new listener.
115     */
116    public MetalPropertyChangeListener()
117    {
118      // Nothing to do here.
119    }
120    
121    /**
122     * Handles a property change event, updating the UI components as
123     * appropriate.
124     * 
125     * @param e  the event.
126     */
127    public void propertyChange(PropertyChangeEvent e)
128    {
129      super.propertyChange(e);
130      String name = e.getPropertyName();
131      if (name.equals("editable"))
132        editablePropertyChanged(e);
133      else if (name.equals("enabled"))
134        {
135          if (arrowButton instanceof MetalComboBoxButton)
136            {
137              arrowButton.setFocusable(!comboBox.isEditable()
138                                       && comboBox.isEnabled());
139              comboBox.repaint();
140            }
141        }
142      else if (name.equals("background"))
143        {
144          Color c = (Color) e.getNewValue();
145          arrowButton.setBackground(c);
146          listBox.setBackground(c);
147        }
148      else if (name.equals("foreground"))
149        {
150          Color c = (Color) e.getNewValue();
151          arrowButton.setForeground(c);
152          listBox.setForeground(c);
153        }
154    }
155  }
156
157  /**
158   * A popup menu for the combo-box.
159   * 
160   * @see #createPopup()
161   *
162   * @deprecated 1.4
163   */
164  public class MetalComboPopup extends BasicComboPopup
165  {
166    /**
167     * Creates a new popup.
168     * 
169     * @param cBox  the combo box.
170     */
171    public MetalComboPopup(JComboBox cBox)
172    {
173      super(cBox); 
174    }
175    
176    public void delegateFocus(MouseEvent e)
177    {
178      super.delegateFocus(e);
179    }
180  }
181  
182  /**
183   * Constructs a new instance of MetalComboBoxUI.
184   */
185  public MetalComboBoxUI()
186  {
187    super();
188  }
189
190  /**
191   * Returns an instance of MetalComboBoxUI.
192   *
193   * @param component the component for which we return an UI instance
194   *
195   * @return an instance of MetalComboBoxUI
196   */
197  public static ComponentUI createUI(JComponent component)
198  {
199    return new MetalComboBoxUI();
200  }
201  
202  /**
203   * Creates an editor for the combo box.
204   * 
205   * @return An editor.
206   */
207  protected ComboBoxEditor createEditor()
208  {
209    return new MetalComboBoxEditor.UIResource();   
210  }
211  
212  /**
213   * Creates a popup for the combo box.
214   * 
215   * @return A popup.
216   */
217  protected ComboPopup createPopup()
218  {
219    return super.createPopup();
220  }
221  
222  /**
223   * Creates a new button for use in rendering the JComboBox.
224   * 
225   * @return A button.
226   */
227  protected JButton createArrowButton()
228  {
229    JButton button = new MetalComboBoxButton(comboBox, new MetalComboBoxIcon(), 
230            currentValuePane, listBox);  
231    button.setMargin(new Insets(0, 1, 1, 3));
232    return button;
233  }
234  
235  /**
236   * Creates a new property change listener.
237   * 
238   * @return A new property change listener.
239   */
240  public PropertyChangeListener createPropertyChangeListener()
241  {
242    return new MetalPropertyChangeListener();
243  }
244  
245  public void paint(Graphics g, JComponent c)
246  {
247    // do nothing, the button and text field are painted elsewhere
248  }
249  
250  /**
251   * Updates the button and text field to reflect a change in the 'editable'
252   * property.
253   * 
254   * @param e  the event.
255   * 
256   * @deprecated 1.4
257   */
258  protected void editablePropertyChanged(PropertyChangeEvent e)
259  {
260    if (arrowButton instanceof MetalComboBoxButton)
261      {
262        MetalComboBoxButton b = (MetalComboBoxButton) arrowButton;
263        b.setIconOnly(comboBox.isEditable());
264        b.setFocusable(!comboBox.isEditable() && comboBox.isEnabled());
265        comboBox.repaint();
266      }
267  }
268  
269  /**
270   * Creates a new layout manager for the UI delegate.
271   * 
272   * @return A new layout manager.
273   */
274  protected LayoutManager createLayoutManager()
275  {
276    return new MetalComboBoxLayoutManager();
277  }
278  
279  /**
280   * Not used in Classpath.
281   * 
282   * @deprecated 1.4
283   */
284  protected void removeListeners()
285  {
286    // no longer used in JDK 1.4 
287  }
288  
289  /**
290   * Returns the minimum size for the combo.
291   * 
292   * @param c  the component
293   * 
294   * @return The minimum size for the combo box.
295   */
296  public Dimension getMinimumSize(JComponent c)
297  {
298    if (!isMinimumSizeDirty)
299      return new Dimension(cachedMinimumSize);
300
301    Dimension d;
302    if (!comboBox.isEditable() && arrowButton != null
303        && arrowButton instanceof MetalComboBoxButton)
304      {
305        MetalComboBoxButton b = (MetalComboBoxButton) arrowButton;
306        d = getDisplaySize();
307        Insets arrowInsets = b.getInsets();
308        Insets comboInsets = comboBox.getInsets();
309        Icon icon = b.getComboIcon();
310        d.width += comboInsets.left + comboInsets.right;
311        d.width += arrowInsets.left + arrowInsets.right;
312        d.width += arrowInsets.right + icon.getIconWidth();
313        d.height += comboInsets.top + comboInsets.bottom;
314        d.height += arrowInsets.top + arrowInsets.bottom;
315      }
316    else if (comboBox.isEditable() && arrowButton != null && editor != null)
317      {
318        d = super.getMinimumSize(c);
319        Insets arrowMargin = arrowButton.getMargin();
320        d.height += arrowMargin.top + arrowMargin.bottom;
321        d.width += arrowMargin.left + arrowMargin.right;
322      }
323    else
324      {
325        d = super.getMinimumSize(c);
326      }
327    cachedMinimumSize.setSize(d.width, d.height);
328    isMinimumSizeDirty = false;
329    return new Dimension(cachedMinimumSize);
330  }
331  
332  /**
333   * Configures the editor for this combo box.
334   */
335  public void configureEditor()
336  {
337    super.configureEditor();
338    if (popupKeyListener != null)
339      editor.removeKeyListener(popupKeyListener);
340    if (focusListener != null)
341      editor.addFocusListener(focusListener);
342  }
343
344  /**
345   * Unconfigures the editor for this combo box.
346   */
347  public void unconfigureEditor()
348  {
349    super.unconfigureEditor();
350    if (focusListener != null)
351      editor.removeFocusListener(focusListener);
352  }
353  
354  /** 
355   * Lays out the ComboBox
356   */
357  public void layoutComboBox(Container parent,
358                             MetalComboBoxUI.MetalComboBoxLayoutManager manager)
359  {
360    if (comboBox.isEditable())
361      manager.superLayout(parent);
362    else if (arrowButton != null)
363      {
364        Insets comboInsets = comboBox.getInsets();
365        int width = comboBox.getWidth();
366        int height = comboBox.getHeight();
367        arrowButton.setBounds(comboInsets.left, comboInsets.top,
368                              width - (comboInsets.left + comboInsets.right),
369                              height - (comboInsets.top + comboInsets.bottom));
370      }
371  }
372}