Source for javax.swing.LookAndFeel

   1: /* LookAndFeel.java --
   2:    Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.Font;
  44: import java.awt.Toolkit;
  45: import java.net.URL;
  46: 
  47: import javax.swing.border.Border;
  48: import javax.swing.plaf.ComponentInputMapUIResource;
  49: import javax.swing.plaf.IconUIResource;
  50: import javax.swing.plaf.InputMapUIResource;
  51: import javax.swing.plaf.UIResource;
  52: import javax.swing.plaf.metal.MetalLookAndFeel;
  53: import javax.swing.text.JTextComponent;
  54: 
  55: /**
  56:  * A <i>look-and-feel</i> controls most aspects of the appearance and 
  57:  * operation of user interface components in <code>javax.swing</code>.  A 
  58:  * cross-platform look-and-feel (the {@link MetalLookAndFeel}) is provided.
  59:  * 
  60:  * @see UIManager#getInstalledLookAndFeels()
  61:  * @see UIManager#setLookAndFeel(LookAndFeel)
  62:  */
  63: public abstract class LookAndFeel
  64: {
  65:   /**
  66:    * Creates and returns a look-and-feel specific defaults table.  This method 
  67:    * is called once by {@link UIManager#setLookAndFeel(LookAndFeel)} and 
  68:    * shouldn't be called again (as it creates a large table of defaults).
  69:    *
  70:    * @return The UI defaults.
  71:    */
  72:   public UIDefaults getDefaults()
  73:   {
  74:     return null;
  75:   }
  76: 
  77:   /**
  78:    * Returns a description of the look and feel.
  79:    * 
  80:    * @return A description of the look and feel.
  81:    */
  82:   public abstract String getDescription();
  83: 
  84:   /**
  85:    * Returns the value of <code>Toolkit.getDefaultToolkit()
  86:    * .getDesktopProperty(systemPropertyName)</code>, or 
  87:    * <code>fallbackValue</code> if no such property is defined.
  88:    * 
  89:    * @param systemPropertyName  the system property name.
  90:    * @param fallbackValue  the fallback value.
  91:    * 
  92:    * @return The property value or <code>fallbackValue</code>.
  93:    */
  94:   public static Object getDesktopPropertyValue(String systemPropertyName, 
  95:       Object fallbackValue)
  96:   {
  97:     Object value = Toolkit.getDefaultToolkit().getDesktopProperty(
  98:         systemPropertyName);
  99:     return value != null ? value : fallbackValue;
 100:   }
 101:   
 102:   /**
 103:    * Returns an identifier for the look and feel.
 104:    * 
 105:    * @return An identifier for the look and feel.
 106:    */
 107:   public abstract String getID();
 108: 
 109:   /**
 110:    * Returns the name for the look and feel.
 111:    * 
 112:    * @return The name for the look and feel.
 113:    */
 114:   public abstract String getName();
 115: 
 116:   /**
 117:    * Returns <code>true</code> when the look-and-feel supports window 
 118:    * decorations, and <code>false</code> otherwise. This default implementation
 119:    * always returns <code>false</code> and needs to be overridden when the 
 120:    * derived look-and-feel supports this.
 121:    *
 122:    * @return <code>false</code>.
 123:    *
 124:    * @since 1.4
 125:    */
 126:   public boolean getSupportsWindowDecorations()
 127:   {
 128:     return false;
 129:   }
 130:   
 131:   /**
 132:    * Initializes the look-and-feel.  The 
 133:    * {@link UIManager#setLookAndFeel(LookAndFeel)} method calls this method 
 134:    * before the first call (and typically the only call) to 
 135:    * {@link #getDefaults()}.  This default implementation does nothing, but
 136:    * subclasses can override this behaviour. 
 137:    */
 138:   public void initialize()
 139:   {
 140:     // We do nothing here. This method is meant to be overridden by
 141:     // LookAndFeel implementations.
 142:   }
 143: 
 144:   /**
 145:    * Convenience method for installing a component's default {@link Border} 
 146:    * object on the specified component if either the border is currently 
 147:    * <code>null</code> or already an instance of {@link UIResource}. 
 148:    * 
 149:    * @param c  the component (<code>null</code> not permitted).
 150:    * @param defaultBorderName  the border name (for lookup in the UIDefaults 
 151:    *     table).
 152:    */
 153:   public static void installBorder(JComponent c, String defaultBorderName)
 154:   {
 155:     Border b = c.getBorder();
 156:     if (b == null || b instanceof UIResource)
 157:       c.setBorder(UIManager.getBorder(defaultBorderName));
 158:   }
 159: 
 160:   /**
 161:    * Convenience method for initializing a component's foreground and
 162:    * background color properties with values from the current defaults table.
 163:    * 
 164:    * @param c  the component (<code>null</code> not permitted).
 165:    * @param defaultBgName  the key for the background color in the UIDefaults 
 166:    *     table.
 167:    * @param defaultFgName  the key for the foreground color in the UIDefaults
 168:    *     table.
 169:    */
 170:   public static void installColors(JComponent c, String defaultBgName,
 171:                                    String defaultFgName)
 172:   {
 173:     // Install background.
 174:     Color bg = c.getBackground();
 175:     if (bg == null || bg instanceof UIResource)
 176:       c.setBackground(UIManager.getColor(defaultBgName));
 177: 
 178:     // Install foreground.
 179:     Color fg = c.getForeground();
 180:     if (fg == null || fg instanceof UIResource)
 181:       c.setForeground(UIManager.getColor(defaultFgName));
 182:   }
 183: 
 184:   /**
 185:    * Convenience method for initializing a component's foreground, background
 186:    * and font properties with values from the current defaults table.
 187:    * 
 188:    * @param component  the component (<code>null</code> not permitted).
 189:    * @param defaultBgName  the key for the background color in the UIDefaults 
 190:    *     table.
 191:    * @param defaultFgName  the key for the foreground color in the UIDefaults
 192:    *     table.
 193:    * @param defaultFontName  the key for the font in the UIDefaults table.
 194:    */
 195:   public static void installColorsAndFont(JComponent component,
 196:                                           String defaultBgName,
 197:                                           String defaultFgName,
 198:                                           String defaultFontName)
 199:   {
 200:     // Install colors.
 201:     installColors(component, defaultBgName, defaultFgName);
 202:     // Install font.
 203:     Font f = component.getFont();
 204:     if (f == null || f instanceof UIResource)
 205:       component.setFont(UIManager.getFont(defaultFontName));
 206:   }
 207: 
 208:   /**
 209:    * Returns <code>true</code> if the look-and-feel is the "native" 
 210:    * look-and-feel for the current platform, and <code>false</code> otherwise.
 211:    * A native look-and-feel emulates the appearance and behaviour of the 
 212:    * default windowing system on the host operating system.
 213:    * 
 214:    * @return A flag indicating whether or not this is the native look and feel
 215:    *         for the current platform.
 216:    */
 217:   public abstract boolean isNativeLookAndFeel();
 218: 
 219:   /**
 220:    * Returns <code>true</code> if the look-and-feel is supported on the 
 221:    * current operating system, and <code>false</code> otherwise.  This 
 222:    * mechanism is provided so that it is possible to prevent a look-and-feel
 223:    * from being used on some operating systems (usually for legal, not
 224:    * technical, reasons).
 225:    * 
 226:    * @return A flag indicating whether or not the look-and-feel is supported
 227:    *         on the current platform.
 228:    */
 229:   public abstract boolean isSupportedLookAndFeel();
 230: 
 231:   /**
 232:    * Loads the bindings in keys into retMap. Does not remove existing entries
 233:    * from retMap.  <code>keys</code> describes the InputMap, every even indexed
 234:    * item is either a KeyStroke or a String representing a KeyStroke and every
 235:    * odd indexed item is the Object associated with that KeyStroke in an 
 236:    * ActionMap.
 237:    * 
 238:    * @param retMap the InputMap into which we load bindings
 239:    * @param keys the Object array describing the InputMap as above
 240:    */
 241:   public static void loadKeyBindings(InputMap retMap, Object[] keys)
 242:   {
 243:     if (keys == null)
 244:       return;
 245:     for (int i = 0; i < keys.length - 1; i += 2)
 246:       {
 247:         Object key = keys[i];
 248:         KeyStroke keyStroke;
 249:         if (key instanceof KeyStroke)
 250:           keyStroke = (KeyStroke) key;
 251:         else
 252:           keyStroke = KeyStroke.getKeyStroke((String) key);
 253:         retMap.put(keyStroke, keys[i + 1]);
 254:       }
 255:   }
 256: 
 257:   /**
 258:    * Creates a ComponentInputMap from keys.  
 259:    * <code>keys</code> describes the InputMap, every even indexed
 260:    * item is either a KeyStroke or a String representing a KeyStroke and every
 261:    * odd indexed item is the Object associated with that KeyStroke in an 
 262:    * ActionMap.
 263:    * 
 264:    * @param c the JComponent associated with the ComponentInputMap
 265:    * @param keys the Object array describing the InputMap as above
 266:    * 
 267:    * @return A new input map.
 268:    */
 269:   public static ComponentInputMap makeComponentInputMap(JComponent c,
 270:                             Object[] keys)
 271:   {
 272:     ComponentInputMap retMap = new ComponentInputMapUIResource(c);
 273:     loadKeyBindings(retMap, keys);
 274:     return retMap;
 275:   }
 276: 
 277:   /**
 278:    * Utility method that creates a UIDefaults.LazyValue that creates an
 279:    * ImageIcon UIResource for the specified gifFile filename.
 280:    * 
 281:    * @param baseClass  the base class for accessing the icon resource.
 282:    * @param gifFile  the file name.
 283:    * 
 284:    * @return A {@link UIDefaults.LazyValue} that serves up an 
 285:    *     {@link IconUIResource}.
 286:    */
 287:   public static Object makeIcon(Class<?> baseClass, String gifFile)
 288:   {
 289:     final URL file = baseClass.getResource(gifFile);
 290:     return new UIDefaults.LazyValue() 
 291:       {
 292:         public Object createValue(UIDefaults table)
 293:         {
 294:           return new IconUIResource(new ImageIcon(file));
 295:         }
 296:       };
 297:   }
 298: 
 299:   /**
 300:    * Creates a InputMap from keys. 
 301:    * <code>keys</code> describes the InputMap, every even indexed
 302:    * item is either a KeyStroke or a String representing a KeyStroke and every
 303:    * odd indexed item is the Object associated with that KeyStroke in an 
 304:    * ActionMap.
 305:    * 
 306:    * @param keys the Object array describing the InputMap as above
 307:    * 
 308:    * @return A new input map.
 309:    */
 310:   public static InputMap makeInputMap(Object[] keys)
 311:   {
 312:     InputMap retMap = new InputMapUIResource();
 313:     loadKeyBindings(retMap, keys);
 314:     return retMap;
 315:   }
 316: 
 317:   /**
 318:    * Convenience method for building lists of KeyBindings.
 319:    * <code>keyBindingList</code> is an array of KeyStroke-Action pairs where
 320:    * even indexed elements are KeyStrokes or Strings representing KeyStrokes
 321:    * and odd indexed elements are the associated Actions.
 322:    * 
 323:    * @param keyBindingList the array of KeyStroke-Action pairs
 324:    * @return a JTextComponent.KeyBinding array
 325:    */
 326:   public static JTextComponent.KeyBinding[] makeKeyBindings(
 327:       Object[] keyBindingList)
 328:   {
 329:     JTextComponent.KeyBinding[] retBindings = 
 330:       new JTextComponent.KeyBinding[keyBindingList.length / 2];
 331:     for (int i = 0; i < keyBindingList.length - 1; i += 2)
 332:       {
 333:         KeyStroke stroke;
 334:         if (keyBindingList[i] instanceof KeyStroke)
 335:           stroke = (KeyStroke) keyBindingList[i];
 336:         else
 337:           stroke = KeyStroke.getKeyStroke((String) keyBindingList[i]);
 338:         retBindings[i / 2] = new JTextComponent.KeyBinding(stroke, 
 339:             (String) keyBindingList[i + 1]);
 340:       }
 341:     return retBindings;
 342:   }
 343: 
 344:   /**
 345:    * Invoked when the user attempts an invalid operation. The default 
 346:    * implementation just beeps. Subclasses that wish to change this need to 
 347:    * override this method.
 348:    *
 349:    * @param component the component the error occured in
 350:    */
 351:   public void provideErrorFeedback(Component component)
 352:   {
 353:     Toolkit.getDefaultToolkit().beep();
 354:   }
 355: 
 356:   /**
 357:    * Returns a string that displays and identifies this object's properties.
 358:    *
 359:    * @return string containing the description and class name.
 360:    */
 361:   public String toString()
 362:   {
 363:     return getDescription() + " " + getClass().getName();
 364:   }
 365: 
 366:   /**
 367:    * UIManager.setLookAndFeel calls this method just before we're replaced by
 368:    * a new default look and feel. 
 369:    */
 370:   public void uninitialize()
 371:   {
 372:     // We do nothing here. This method is meant to be overridden by
 373:     // LookAndFeel implementations.
 374:   }
 375: 
 376:   /**
 377:    * Convenience method for un-installing a component's default border on the
 378:    * specified component if the border is currently an instance of UIResource.
 379:    * 
 380:    * @param c  the component (<code>null</code> not permitted).
 381:    */
 382:   public static void uninstallBorder(JComponent c)
 383:   {
 384:     if (c.getBorder() instanceof UIResource)
 385:       c.setBorder(null);
 386:   }
 387: 
 388:   /**
 389:    * This methods installs a UI property if it hasn't already been set by an
 390:    * application. This method is used by UI delegates that install a default
 391:    * value for a property with a primitive type but do not want to override
 392:    * a value that has been set by an application.
 393:    *
 394:    * The supported properties depend on the actual type of the component and
 395:    * are listed in the table below. The supported properties are of course
 396:    * inherited to subclasses.
 397:    *
 398:    * <table>
 399:    * <tr><th>Type</th><th>Supported properties</th></tr>
 400:    * <tr><td><code>JComponent</code></td>
 401:    *     <td><code>opaque, autoscrolls</code></td></tr>
 402:    * <tr><td><code>AbstractButton</code></td>
 403:    *     <td><code>borderPainted, rolloverEnabled, iconTextGap,
 404:    *      contentAreaFilled</code></td></tr>
 405:    * <tr><td><code>JDesktopPane</code></td>
 406:    *     <td><code>dragMode</code></td></tr>
 407:    * <tr><td><code>JSplitPane</code></td>
 408:    *     <td><code>dividerSize, oneTouchExpandable</code></td></tr>
 409:    * <tr><td><code>JTable</code></td>
 410:    *     <td><code>rowHeight</code></td></tr>
 411:    * <tr><td><code>JTree</code></td>
 412:    *     <td><code>rowHeight, scrollsOnExpand, showsRootHandles</code></td></tr>
 413:    * </table>
 414:    *
 415:    * @param c the component to install the property to
 416:    * @param propertyName the name of the property
 417:    * @param value the value of the property
 418:    *
 419:    * @throws IllegalArgumentException if the specified property cannot be set
 420:    *         by this method
 421:    * @throws ClassCastException if the property value does not match the
 422:    *         property type
 423:    * @throws NullPointerException if <code>c</code> or
 424:    *         <code>propertyValue</code> is <code>null</code>
 425:    *
 426:    * @since 1.5
 427:    */
 428:   public static void installProperty(JComponent c, String propertyName,
 429:                                      Object value)
 430:   {
 431:     c.setUIProperty(propertyName, value);
 432:   }
 433: }