001    /* UIManager.java -- 
002       Copyright (C) 2002, 2003, 2004, 2005, 2006,  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;
040    
041    import gnu.java.lang.CPStringBuilder;
042    
043    import java.awt.Color;
044    import java.awt.Dimension;
045    import java.awt.Font;
046    import java.awt.Insets;
047    import java.beans.PropertyChangeListener;
048    import java.beans.PropertyChangeSupport;
049    import java.io.Serializable;
050    import java.util.Enumeration;
051    import java.util.Locale;
052    
053    import javax.swing.border.Border;
054    import javax.swing.plaf.ComponentUI;
055    import javax.swing.plaf.metal.MetalLookAndFeel;
056    
057    /**
058     * Manages the current {@link LookAndFeel} and any auxiliary {@link LookAndFeel}
059     * instances.
060     */
061    public 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    }