001    /* MetalComboBoxUI.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.Container;
043    import java.awt.Dimension;
044    import java.awt.Graphics;
045    import java.awt.Insets;
046    import java.awt.LayoutManager;
047    import java.awt.event.MouseEvent;
048    import java.beans.PropertyChangeEvent;
049    import java.beans.PropertyChangeListener;
050    
051    import javax.swing.ComboBoxEditor;
052    import javax.swing.Icon;
053    import javax.swing.JButton;
054    import javax.swing.JComboBox;
055    import javax.swing.JComponent;
056    import javax.swing.plaf.ComponentUI;
057    import javax.swing.plaf.basic.BasicComboBoxUI;
058    import javax.swing.plaf.basic.BasicComboPopup;
059    import javax.swing.plaf.basic.ComboPopup;
060    
061    
062    /**
063     * A UI delegate for the {@link JComboBox} component.
064     */
065    public 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    }