001/* UIManager.java -- 
002   Copyright (C) 2002, 2003, 2004, 2005, 2006,  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;
040
041import gnu.java.lang.CPStringBuilder;
042
043import java.awt.Color;
044import java.awt.Dimension;
045import java.awt.Font;
046import java.awt.Insets;
047import java.beans.PropertyChangeListener;
048import java.beans.PropertyChangeSupport;
049import java.io.Serializable;
050import java.util.Enumeration;
051import java.util.Locale;
052
053import javax.swing.border.Border;
054import javax.swing.plaf.ComponentUI;
055import javax.swing.plaf.metal.MetalLookAndFeel;
056
057/**
058 * Manages the current {@link LookAndFeel} and any auxiliary {@link LookAndFeel}
059 * instances.
060 */
061public class UIManager implements Serializable
062{
063  /**
064   * Represents the basic information about a {@link LookAndFeel} (LAF), so 
065   * that a list of installed LAFs can be presented without actually loading 
066   * the LAF class(es).
067   */
068  public static class LookAndFeelInfo
069  {
070    String name, clazz;
071        
072    /**
073     * Creates a new instance.
074     * 
075     * @param name  the look and feel name.
076     * @param clazz  the look and feel class name.
077     */
078    public LookAndFeelInfo(String name, 
079                           String clazz)
080    {
081      this.name  = name;
082      this.clazz = clazz;
083    }
084
085    /**
086     * Returns the name of the look and feel.
087     * 
088     * @return The name of the look and feel.
089     */
090    public String getName()
091    {
092      return name;
093    }
094    
095    /**
096     * Returns the fully qualified class name for the {@link LookAndFeel}.
097     * 
098     * @return The fully qualified class name for the {@link LookAndFeel}.
099     */
100    public String getClassName()
101    {
102      return clazz;
103    }
104
105    /**
106     * Returns a String representation of the LookAndFeelInfo object.
107     *
108     * @return a String representation of the LookAndFeelInfo object
109     */
110    public String toString()
111    {
112      CPStringBuilder s = new CPStringBuilder();
113      s.append(getClass().getName());
114      s.append('[');
115      s.append(getName());
116      s.append(' ');
117      s.append(getClassName());
118      s.append(']');
119      return s.toString();
120    }
121  }
122
123  /**
124   * A UIDefaults subclass that multiplexes between itself and a 'fallback'
125   * UIDefaults instance. This is used to protect the L&F UIDefaults from beeing
126   * overwritten by applications.
127   */
128  private static class MultiplexUIDefaults
129    extends UIDefaults
130  {
131    private class MultiplexEnumeration
132      implements Enumeration
133    {
134      Enumeration[] enums;
135      int i;
136      MultiplexEnumeration(Enumeration e1, Enumeration e2)
137      {
138        enums = new Enumeration[]{ e1, e2 };
139        i = 0;
140      }
141
142      public boolean hasMoreElements()
143      {
144        return enums[i].hasMoreElements() || i < enums.length - 1;
145      }
146
147      public Object nextElement()
148      {
149        Object val = enums[i].nextElement();
150        if (! enums[i].hasMoreElements() && i < enums.length - 1)
151          i++;
152        return val;
153      }
154        
155    }
156
157    UIDefaults fallback;
158
159    /**
160     * Creates a new <code>MultiplexUIDefaults</code> instance with 
161     * <code>d</code> as the fallback defaults.
162     * 
163     * @param d  the fallback defaults (<code>null</code> not permitted).
164     */
165    MultiplexUIDefaults(UIDefaults d)
166    {
167      if (d == null) 
168        throw new NullPointerException();
169      fallback = d;
170    }
171
172    public Object get(Object key)
173    {
174      Object val = super.get(key);
175      if (val == null)
176        val = fallback.get(key);
177      return val;
178    }
179
180    public Object get(Object key, Locale l)
181    {
182      Object val = super.get(key, l);
183      if (val == null)
184        val = fallback.get(key, l);
185      return val;
186    }
187
188    public Object remove(Object key)
189    {
190      Object val = super.remove(key);
191      if (val == null)
192        val = fallback.remove(key);
193      return val;
194    }
195
196    public int size()
197    {
198      return super.size() + fallback.size();
199    }
200
201    public Enumeration keys()
202    {
203      return new MultiplexEnumeration(super.keys(), fallback.keys());
204    }
205
206    public Enumeration elements()
207    {
208      return new MultiplexEnumeration(super.elements(), fallback.elements());
209    }
210  }
211
212  private static final long serialVersionUID = -5547433830339189365L;
213
214  /** The installed look and feel(s). */
215  static LookAndFeelInfo [] installed = {
216    new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"),
217    new LookAndFeelInfo("GNU", "gnu.javax.swing.plaf.gnu.GNULookAndFeel")
218  };
219
220  /** The installed auxiliary look and feels. */
221  static LookAndFeel[] auxLookAndFeels;
222  
223  /** The current look and feel. */
224  static LookAndFeel currentLookAndFeel;
225  
226  static MultiplexUIDefaults currentUIDefaults;
227
228  static UIDefaults lookAndFeelDefaults;
229
230  /** Property change listener mechanism. */
231  static PropertyChangeSupport listeners
232      = new PropertyChangeSupport(UIManager.class);
233
234  static
235  {
236    String defaultlaf = System.getProperty("swing.defaultlaf");
237    try 
238      {
239        if (defaultlaf != null)
240          {
241            setLookAndFeel(defaultlaf);
242          }
243        else
244          {
245            setLookAndFeel(new MetalLookAndFeel());
246          }
247      }
248    catch (Exception ex)
249      {
250        System.err.println("cannot initialize Look and Feel: " + defaultlaf);
251        System.err.println("error: " + ex.toString());
252        ex.printStackTrace();
253        System.err.println("falling back to Metal Look and Feel");
254        try
255          {
256            setLookAndFeel(new MetalLookAndFeel());
257          }
258        catch (Exception ex2)
259        {
260          throw (Error) new AssertionError("There must be no problem installing"
261                                           + " the MetalLookAndFeel.")
262                                           .initCause(ex2);
263        }
264      }
265  }
266
267  /**
268   * Creates a new instance of the <code>UIManager</code>.  There is no need
269   * to construct an instance of this class, since all methods are static.
270   */
271  public UIManager()
272  {
273    // Do nothing here.
274  }
275
276  /**
277   * Add a <code>PropertyChangeListener</code> to the listener list.
278   *
279   * @param listener the listener to add
280   */
281  public static void addPropertyChangeListener(PropertyChangeListener listener)
282  {
283    listeners.addPropertyChangeListener(listener);
284  }
285
286  /**
287   * Remove a <code>PropertyChangeListener</code> from the listener list.
288   *
289   * @param listener the listener to remove
290   */
291  public static void removePropertyChangeListener(PropertyChangeListener 
292          listener)
293  {
294    listeners.removePropertyChangeListener(listener);
295  }
296
297  /**
298   * Returns an array of all added <code>PropertyChangeListener</code> objects.
299   *
300   * @return an array of listeners
301   *
302   * @since 1.4
303   */
304  public static PropertyChangeListener[] getPropertyChangeListeners()
305  {
306    return listeners.getPropertyChangeListeners();
307  }
308
309  /**
310   * Add a {@link LookAndFeel} to the list of auxiliary look and feels.
311   * 
312   * @param laf  the auxiliary look and feel (<code>null</code> not permitted).
313   * 
314   * @throws NullPointerException if <code>laf</code> is <code>null</code>.
315   * 
316   * @see #getAuxiliaryLookAndFeels()
317   */
318  public static void addAuxiliaryLookAndFeel(LookAndFeel laf)
319  {
320    if (laf == null)
321      throw new NullPointerException("Null 'laf' argument.");
322    if (auxLookAndFeels == null)
323      {
324        auxLookAndFeels = new LookAndFeel[1];
325        auxLookAndFeels[0] = laf;
326        return;
327      }
328        
329    LookAndFeel[] temp = new LookAndFeel[auxLookAndFeels.length + 1];
330    System.arraycopy(auxLookAndFeels, 0, temp, 0, auxLookAndFeels.length);                       
331    auxLookAndFeels = temp;
332    auxLookAndFeels[auxLookAndFeels.length - 1] = laf;
333  }
334    
335  /**
336   * Removes a {@link LookAndFeel} (LAF) from the list of auxiliary LAFs.
337   * 
338   * @param laf  the LAF to remove.
339   * 
340   * @return <code>true</code> if the LAF was removed, and <code>false</code>
341   *         otherwise.
342   */
343  public static boolean removeAuxiliaryLookAndFeel(LookAndFeel laf)
344  {
345    if (auxLookAndFeels == null)
346      return false;
347    int count = auxLookAndFeels.length;
348    if (count == 1 && auxLookAndFeels[0] == laf)
349      {
350        auxLookAndFeels = null;
351        return true;
352      }
353    for (int i = 0; i < count; i++)
354      {
355        if (auxLookAndFeels[i] == laf)
356          {
357            LookAndFeel[] temp = new LookAndFeel[auxLookAndFeels.length - 1];
358            if (i == 0)
359              {
360                System.arraycopy(auxLookAndFeels, 1, temp, 0, count - 1);  
361              }
362            else if (i == count - 1)
363              {
364                System.arraycopy(auxLookAndFeels, 0, temp, 0, count - 1);
365              }
366            else 
367              {
368                System.arraycopy(auxLookAndFeels, 0, temp, 0, i);
369                System.arraycopy(auxLookAndFeels, i + 1, temp, i, 
370                        count - i - 1);
371              }
372            auxLookAndFeels = temp;
373            return true;
374          }             
375      }
376    return false;
377  }
378
379  /**
380   * Returns an array (possibly <code>null</code>) containing the auxiliary
381   * {@link LookAndFeel}s that are in use.  These are used by the 
382   * {@link javax.swing.plaf.multi.MultiLookAndFeel} class.
383   * 
384   * @return The auxiliary look and feels (possibly <code>null</code>).
385   * 
386   * @see #addAuxiliaryLookAndFeel(LookAndFeel)
387   */
388  public static LookAndFeel[] getAuxiliaryLookAndFeels()
389  {
390    return auxLookAndFeels;
391  }
392
393  /**
394   * Returns an object from the {@link UIDefaults} table for the current
395   * {@link LookAndFeel}.
396   * 
397   * @param key  the key.
398   * 
399   * @return The object.
400   */
401  public static Object get(Object key)
402  {
403    return getDefaults().get(key);
404  }
405
406  /**
407   * Returns an object from the {@link UIDefaults} table for the current
408   * {@link LookAndFeel}.
409   * 
410   * @param key  the key.
411   * 
412   * @return The object.
413   * 
414   * @since 1.4
415   */
416  public static Object get(Object key, Locale locale)
417  {
418    return getDefaults().get(key, locale);
419  }
420
421  /**
422   * Returns a boolean value from the defaults table.  If there is no value
423   * for the specified key, or the value is not an instance of {@link Boolean},
424   * this method returns <code>false</code>.
425   * 
426   * @param key  the key (<code>null</code> not permitted).
427   *
428   * @return The boolean value associated with the specified key.
429   * 
430   * @throws NullPointerException if <code>key</code> is <code>null</code>.
431   * 
432   * @since 1.4
433   */
434  public static boolean getBoolean(Object key)
435  {
436    Object value = get(key);
437    if (value instanceof Boolean) 
438      return ((Boolean) value).booleanValue();
439    return false;
440  }
441  
442  /**
443   * Returns a boolean value from the defaults table.  If there is no value
444   * for the specified key, or the value is not an instance of {@link Boolean},
445   * this method returns <code>false</code>.
446   * 
447   * @param key  the key (<code>null</code> not permitted).
448   * @param locale  the locale.
449   *
450   * @return The boolean value associated with the specified key.
451   * 
452   * @throws NullPointerException if <code>key</code> is <code>null</code>.
453   * 
454   * @since 1.4
455   */
456  public static boolean getBoolean(Object key, Locale locale)
457  {
458    Object value = get(key, locale);
459    if (value instanceof Boolean) 
460      return ((Boolean) value).booleanValue();
461    return false;
462  }
463    
464  /**
465   * Returns a border from the defaults table. 
466   * 
467   * @param key  the key (<code>null</code> not permitted).
468   * 
469   * @return The border associated with the given key, or <code>null</code>.
470   * 
471   * @throws NullPointerException if <code>key</code> is <code>null</code>.
472   */
473  public static Border getBorder(Object key)
474  {
475    Object value = get(key);
476    if (value instanceof Border) 
477      return (Border) value;
478    return null;
479  }
480    
481  /**
482   * Returns a border from the defaults table. 
483   * 
484   * @param key  the key (<code>null</code> not permitted).
485   * @param locale  the locale.
486   * 
487   * @return The border associated with the given key, or <code>null</code>.
488   * 
489   * @throws NullPointerException if <code>key</code> is <code>null</code>.
490   *
491   * @since 1.4
492   */
493  public static Border getBorder(Object key, Locale locale)
494  {
495    Object value = get(key, locale);
496    if (value instanceof Border) 
497      return (Border) value;
498    return null;
499  }
500    
501  /**
502   * Returns a drawing color from the defaults table. 
503   * 
504   * @param key  the key (<code>null</code> not permitted).
505   * 
506   * @return The color associated with the given key, or <code>null</code>.
507   * 
508   * @throws NullPointerException if <code>key</code> is <code>null</code>.
509   */
510  public static Color getColor(Object key)
511  {
512    Object value = get(key);
513    if (value instanceof Color) 
514      return (Color) value;
515    return null;
516  }
517
518  /**
519   * Returns a drawing color from the defaults table. 
520   * 
521   * @param key  the key (<code>null</code> not permitted).
522   * @param locale  the locale.
523   * 
524   * @return The color associated with the given key, or <code>null</code>.
525   * 
526   * @throws NullPointerException if <code>key</code> is <code>null</code>.
527   * 
528   * @since 1.4
529   */
530  public static Color getColor(Object key, Locale locale)
531  {
532    Object value = get(key, locale);
533    if (value instanceof Color) 
534      return (Color) value;
535    return null;
536  }
537
538  /**
539   * The fully qualified class name of the cross platform (Metal) look and feel.
540   * This string can be passed to Class.forName()
541   * 
542   * @return <code>"javax.swing.plaf.metal.MetalLookAndFeel"</code>
543   */
544  public static String getCrossPlatformLookAndFeelClassName()
545  {     
546    return "javax.swing.plaf.metal.MetalLookAndFeel";
547  }
548
549  /**
550   * Returns the default values for this look and feel. 
551   * 
552   * @return The {@link UIDefaults} for the current {@link LookAndFeel}.
553   */
554  public static UIDefaults getDefaults()
555  {
556    if (currentUIDefaults == null)
557      currentUIDefaults = new MultiplexUIDefaults(new UIDefaults());
558    return currentUIDefaults;
559  }
560
561  /**
562   * Returns a dimension from the defaults table. 
563   * 
564   * @param key  the key (<code>null</code> not permitted).
565   * 
566   * @return The color associated with the given key, or <code>null</code>.
567   * 
568   * @throws NullPointerException if <code>key</code> is <code>null</code>.
569   */
570  public static Dimension getDimension(Object key)
571  {
572    Object value = get(key);
573    if (value instanceof Dimension) 
574      return (Dimension) value;
575    return null;
576  }
577
578  /**
579   * Returns a dimension from the defaults table. 
580   * 
581   * @param key  the key (<code>null</code> not permitted).
582   * @param locale  the locale.
583   * 
584   * @return The color associated with the given key, or <code>null</code>.
585   * 
586   * @throws NullPointerException if <code>key</code> is <code>null</code>.
587   * @since 1.4
588   */
589  public static Dimension getDimension(Object key, Locale locale)
590  {
591    Object value = get(key, locale);
592    if (value instanceof Dimension) 
593      return (Dimension) value;
594    return null;
595  }
596
597  /**
598   * Retrieves a font from the defaults table of the current
599   * LookAndFeel.
600   *
601   * @param key an Object that specifies the font. Typically,
602   *        this is a String such as
603   *        <code>TitledBorder.font</code>.
604   *        
605   * @return The font associated with the given key, or <code>null</code>.
606   * 
607   * @throws NullPointerException if <code>key</code> is <code>null</code>.
608   */
609  public static Font getFont(Object key)
610  {
611    Object value = get(key);
612    if (value instanceof Font) 
613      return (Font) value;
614    return null;
615  }
616
617  /**
618   * Retrieves a font from the defaults table of the current
619   * LookAndFeel.
620   *
621   * @param key an Object that specifies the font. Typically,
622   *        this is a String such as
623   *        <code>TitledBorder.font</code>.
624   * @param locale  the locale.
625   *        
626   * @return The font associated with the given key, or <code>null</code>.
627   * 
628   * @throws NullPointerException if <code>key</code> is <code>null</code>.
629   *        
630   * @since 1.4
631   */
632  public static Font getFont(Object key, Locale locale)
633  {
634    Object value = get(key, locale);
635    if (value instanceof Font) 
636      return (Font) value;
637    return null;
638  }
639
640  /**
641   * Returns an icon from the defaults table. 
642   * 
643   * @param key  the key (<code>null</code> not permitted).
644   * 
645   * @return The icon associated with the given key, or <code>null</code>.
646   * 
647   * @throws NullPointerException if <code>key</code> is <code>null</code>.
648   */
649  public static Icon getIcon(Object key)
650  {
651    Object value = get(key);
652    if (value instanceof Icon) 
653      return (Icon) value;
654    return null;
655  }
656  
657  /**
658   * Returns an icon from the defaults table. 
659   * 
660   * @param key  the key (<code>null</code> not permitted).
661   * @param locale  the locale.
662   * 
663   * @return The icon associated with the given key, or <code>null</code>.
664   * 
665   * @throws NullPointerException if <code>key</code> is <code>null</code>.
666   * @since 1.4
667   */
668  public static Icon getIcon(Object key, Locale locale)
669  {
670    Object value = get(key, locale);
671    if (value instanceof Icon) 
672      return (Icon) value;
673    return null;
674  }
675  
676  /**
677   * Returns an Insets object from the defaults table.
678   * 
679   * @param key  the key (<code>null</code> not permitted).
680   * 
681   * @return The insets associated with the given key, or <code>null</code>.
682   * 
683   * @throws NullPointerException if <code>key</code> is <code>null</code>.   
684   */
685  public static Insets getInsets(Object key)
686  {
687    Object o = get(key);
688    if (o instanceof Insets)
689      return (Insets) o;
690    else
691      return null;
692  }
693
694  /**
695   * Returns an Insets object from the defaults table.
696   * 
697   * @param key  the key (<code>null</code> not permitted).
698   * @param locale  the locale.
699   * 
700   * @return The insets associated with the given key, or <code>null</code>.
701   * 
702   * @throws NullPointerException if <code>key</code> is <code>null</code>.   
703   * @since 1.4
704   */
705  public static Insets getInsets(Object key, Locale locale)
706  {
707    Object o = get(key, locale);
708    if (o instanceof Insets)
709      return (Insets) o;
710    else
711      return null;
712  }
713
714  /**
715   * Returns an array containing information about the {@link LookAndFeel}s
716   * that are installed.
717   * 
718   * @return A list of the look and feels that are available (installed).
719   */
720  public static LookAndFeelInfo[] getInstalledLookAndFeels()
721  {
722    return installed;
723  }
724
725  /**
726   * Returns the integer value of the {@link Integer} associated with the
727   * given key.  If there is no value, or the value is not an instance of
728   * {@link Integer}, this method returns 0.
729   * 
730   * @param key  the key (<code>null</code> not permitted).
731   * 
732   * @return The integer value associated with the given key, or 0.
733   */
734  public static int getInt(Object key)
735  {
736    Object x = get(key);
737    if (x instanceof Integer)
738      return ((Integer) x).intValue();
739    return 0;
740  }
741
742  /**
743   * Returns the integer value of the {@link Integer} associated with the
744   * given key.  If there is no value, or the value is not an instance of
745   * {@link Integer}, this method returns 0.
746   * 
747   * @param key  the key (<code>null</code> not permitted).
748   * @param locale  the locale.
749   * 
750   * @return The integer value associated with the given key, or 0.
751   * 
752   * @since 1.4
753   */
754  public static int getInt(Object key, Locale locale)
755  {
756    Object x = get(key, locale);
757    if (x instanceof Integer)
758      return ((Integer) x).intValue();
759    return 0;
760  }
761
762  /**
763   * Returns the current look and feel (which may be <code>null</code>).
764   * 
765   * @return The current look and feel.
766   * 
767   * @see #setLookAndFeel(LookAndFeel)
768   */
769  public static LookAndFeel getLookAndFeel()
770  {
771    return currentLookAndFeel;
772  }
773
774  /**
775   * Returns the <code>UIDefaults</code> table of the currently active
776   * look and feel.
777   * 
778   * @return The {@link UIDefaults} for the current {@link LookAndFeel}.
779   */
780  public static UIDefaults getLookAndFeelDefaults()
781  {
782    return lookAndFeelDefaults;
783  }
784
785  /**
786   * Returns the {@link String} associated with the given key.  If the value 
787   * is not a {@link String}, this method returns <code>null</code>.
788   * 
789   * @param key  the key (<code>null</code> not permitted).
790   * 
791   * @return The string associated with the given key, or <code>null</code>.
792   */
793  public static String getString(Object key)
794  {
795    Object s = get(key);
796    if (s instanceof String)
797      return (String) s;
798    return null;
799  }
800  
801  /**
802   * Returns the {@link String} associated with the given key.  If the value 
803   * is not a {@link String}, this method returns <code>null</code>.
804   * 
805   * @param key  the key (<code>null</code> not permitted).
806   * @param locale  the locale.
807   * 
808   * @return The string associated with the given key, or <code>null</code>.
809   * 
810   * @since 1.4
811   */
812  public static String getString(Object key, Locale locale)
813  {
814    Object s = get(key, locale);
815    if (s instanceof String)
816      return (String) s;
817    return null;
818  }
819  
820  /**
821   * Returns the name of the {@link LookAndFeel} class that implements the
822   * native systems look and feel if there is one, otherwise the name
823   * of the default cross platform LookAndFeel class.
824   * 
825   * @return The fully qualified class name for the system look and feel.
826   * 
827   * @see #getCrossPlatformLookAndFeelClassName()
828   */
829  public static String getSystemLookAndFeelClassName()
830  {
831    return getCrossPlatformLookAndFeelClassName();
832  }
833
834  /**
835   * Returns UI delegate from the current {@link LookAndFeel} that renders the 
836   * target component.
837   * 
838   * @param target  the target component.
839   */
840  public static ComponentUI getUI(JComponent target)
841  {
842    return getDefaults().getUI(target);
843  }
844
845  /**
846   * Creates a new look and feel and adds it to the current array.
847   * 
848   * @param name  the look and feel name.
849   * @param className  the fully qualified name of the class that implements the
850   *                   look and feel.
851   */
852  public static void installLookAndFeel(String name, String className)
853  {
854    installLookAndFeel(new LookAndFeelInfo(name, className));
855  }
856
857  /**
858   * Adds the specified look and feel to the current array and then calls
859   * setInstalledLookAndFeels(javax.swing.UIManager.LookAndFeelInfo[]).
860   */
861  public static void installLookAndFeel(LookAndFeelInfo info)
862  {
863    LookAndFeelInfo[] newInstalled = new LookAndFeelInfo[installed.length + 1];
864    System.arraycopy(installed, 0, newInstalled, 0, installed.length);
865    newInstalled[newInstalled.length - 1] = info;
866    setInstalledLookAndFeels(newInstalled);
867  }
868
869  /**
870   * Stores an object in the defaults table.
871   * 
872   * @param key  the key.
873   * @param value  the value.
874   */
875  public static Object put(Object key, Object value)
876  {
877    return getDefaults().put(key, value);
878  }
879
880  /**
881   * Replaces the current array of installed LookAndFeelInfos.
882   */
883  public static void setInstalledLookAndFeels(UIManager.LookAndFeelInfo[] infos)
884  {
885    installed = infos;
886  }
887  
888  /**
889   * Sets the current {@link LookAndFeel}.
890   * 
891   * @param newLookAndFeel  the new look and feel (<code>null</code> permitted).
892   * 
893   * @throws UnsupportedLookAndFeelException if the look and feel is not 
894   *         supported on the current platform.
895   * 
896   * @see LookAndFeel#isSupportedLookAndFeel()
897   */
898  public static void setLookAndFeel(LookAndFeel newLookAndFeel)
899    throws UnsupportedLookAndFeelException
900  {
901    if (newLookAndFeel != null && ! newLookAndFeel.isSupportedLookAndFeel())
902      throw new UnsupportedLookAndFeelException(newLookAndFeel.getName()
903                                         + " not supported on this platform");
904    LookAndFeel oldLookAndFeel = currentLookAndFeel;
905    if (oldLookAndFeel != null)
906      oldLookAndFeel.uninitialize();
907
908    // Set the current default look and feel using a LookAndFeel object. 
909    currentLookAndFeel = newLookAndFeel;
910    if (newLookAndFeel != null)
911      {
912        newLookAndFeel.initialize();
913        lookAndFeelDefaults = newLookAndFeel.getDefaults();
914        if (currentUIDefaults == null)
915          currentUIDefaults =
916            new MultiplexUIDefaults(lookAndFeelDefaults);
917        else
918          currentUIDefaults.fallback = lookAndFeelDefaults;
919      }
920    else
921      {
922        currentUIDefaults = null;    
923      }
924    listeners.firePropertyChange("lookAndFeel", oldLookAndFeel, newLookAndFeel);
925    //revalidate();
926    //repaint();
927  }
928
929  /**
930   * Set the current default look and feel using a class name.
931   * 
932   * @param className  the look and feel class name.
933   * 
934   * @throws UnsupportedLookAndFeelException if the look and feel is not 
935   *         supported on the current platform.
936   * 
937   * @see LookAndFeel#isSupportedLookAndFeel()
938   */
939  public static void setLookAndFeel(String className)
940    throws ClassNotFoundException, InstantiationException, IllegalAccessException,
941    UnsupportedLookAndFeelException
942  {
943    Class c = Class.forName(className, true,
944                            Thread.currentThread().getContextClassLoader());
945    LookAndFeel a = (LookAndFeel) c.newInstance(); // throws class-cast-exception
946    setLookAndFeel(a);
947  }
948}