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