81 import java.beans.PropertyChangeEvent;
82
83
84 /**
85 * A base class to use in creating a look and feel for Swing.
86 * <p>
87 * Each of the {@code ComponentUI}s provided by {@code
88 * BasicLookAndFeel} derives its behavior from the defaults
89 * table. Unless otherwise noted each of the {@code ComponentUI}
90 * implementations in this package document the set of defaults they
91 * use. Unless otherwise noted the defaults are installed at the time
92 * {@code installUI} is invoked, and follow the recommendations
93 * outlined in {@code LookAndFeel} for installing defaults.
94 * <p>
95 * <strong>Warning:</strong>
96 * Serialized objects of this class will not be compatible with
97 * future Swing releases. The current serialization support is
98 * appropriate for short term storage or RMI between applications running
99 * the same version of Swing. As of 1.4, support for long term storage
100 * of all JavaBeans™
101 * has been added to the <code>java.beans</code> package.
102 * Please see {@link java.beans.XMLEncoder}.
103 *
104 * @author unattributed
105 */
106 @SuppressWarnings("serial") // Same-version serialization only
107 public abstract class BasicLookAndFeel extends LookAndFeel implements Serializable
108 {
109 /**
110 * Whether or not the developer has created a JPopupMenu.
111 */
112 static boolean needsEventHelper;
113
114 /**
115 * Lock used when manipulating clipPlaying.
116 */
117 private transient Object audioLock = new Object();
118 /**
119 * The Clip that is currently playing (set in AudioAction).
120 */
121 private Clip clipPlaying;
203 AccessController.doPrivileged(invocator);
204 invocator = null;
205 }
206
207 if (disposer != null) {
208 // Note that we're likely calling removePropertyChangeListener()
209 // during the course of AppContext.firePropertyChange().
210 // However, EventListenerAggreggate has code to safely modify
211 // the list under such circumstances.
212 context.removePropertyChangeListener(AppContext.GUI_DISPOSED,
213 disposer);
214 disposer = null;
215 }
216 }
217
218 /**
219 * Populates {@code table} with mappings from {@code uiClassID} to the
220 * fully qualified name of the ui class. The value for a
221 * particular {@code uiClassID} is {@code
222 * "javax.swing.plaf.basic.Basic + uiClassID"}. For example, the
223 * value for the {@code uiClassID} {@code TreeUI} is {@code
224 * "javax.swing.plaf.basic.BasicTreeUI"}.
225 *
226 * @param table the {@code UIDefaults} instance the entries are
227 * added to
228 * @throws NullPointerException if {@code table} is {@code null}
229 *
230 * @see javax.swing.LookAndFeel
231 * @see #getDefaults
232 */
233 protected void initClassDefaults(UIDefaults table)
234 {
235 final String basicPackageName = "javax.swing.plaf.basic.";
236 Object[] uiDefaults = {
237 "ButtonUI", basicPackageName + "BasicButtonUI",
238 "CheckBoxUI", basicPackageName + "BasicCheckBoxUI",
239 "ColorChooserUI", basicPackageName + "BasicColorChooserUI",
240 "FormattedTextFieldUI", basicPackageName + "BasicFormattedTextFieldUI",
241 "MenuBarUI", basicPackageName + "BasicMenuBarUI",
242 "MenuUI", basicPackageName + "BasicMenuUI",
243 "MenuItemUI", basicPackageName + "BasicMenuItemUI",
1840 "released ENTER", "release",
1841 "ctrl ENTER", "press",
1842 "ctrl released ENTER", "release"
1843 },
1844 };
1845
1846 table.putDefaults(defaults);
1847 }
1848
1849 static int getFocusAcceleratorKeyMask() {
1850 Toolkit tk = Toolkit.getDefaultToolkit();
1851 if (tk instanceof SunToolkit) {
1852 return ((SunToolkit)tk).getFocusAcceleratorKeyMask();
1853 }
1854 return ActionEvent.ALT_MASK;
1855 }
1856
1857
1858
1859 /**
1860 * Returns the ui that is of type <code>klass</code>, or null if
1861 * one can not be found.
1862 */
1863 static Object getUIOfType(ComponentUI ui, Class<?> klass) {
1864 if (klass.isInstance(ui)) {
1865 return ui;
1866 }
1867 return null;
1868 }
1869
1870 // ********* Auditory Cue support methods and objects *********
1871 // also see the "AuditoryCues" section of the defaults table
1872
1873 /**
1874 * Returns an <code>ActionMap</code> containing the audio actions
1875 * for this look and feel.
1876 * <P>
1877 * The returned <code>ActionMap</code> contains <code>Actions</code> that
1878 * embody the ability to render an auditory cue. These auditory
1879 * cues map onto user and system activities that may be useful
1880 * for an end user to know about (such as a dialog box appearing).
1881 * <P>
1882 * At the appropriate time,
1883 * the {@code ComponentUI} is responsible for obtaining an
1884 * <code>Action</code> out of the <code>ActionMap</code> and passing
1885 * it to <code>playSound</code>.
1886 * <P>
1887 * This method first looks up the {@code ActionMap} from the
1888 * defaults using the key {@code "AuditoryCues.actionMap"}.
1889 * <p>
1890 * If the value is {@code non-null}, it is returned. If the value
1891 * of the default {@code "AuditoryCues.actionMap"} is {@code null}
1892 * and the value of the default {@code "AuditoryCues.cueList"} is
1893 * {@code non-null}, an {@code ActionMapUIResource} is created and
1894 * populated. Population is done by iterating over each of the
1895 * elements of the {@code "AuditoryCues.cueList"} array, and
1896 * invoking {@code createAudioAction()} to create an {@code
1897 * Action} for each element. The resulting {@code Action} is
1898 * placed in the {@code ActionMapUIResource}, using the array
1899 * element as the key. For example, if the {@code
1900 * "AuditoryCues.cueList"} array contains a single-element, {@code
1901 * "audioKey"}, the {@code ActionMapUIResource} is created, then
1902 * populated by way of {@code actionMap.put(cueList[0],
1903 * createAudioAction(cueList[0]))}.
1904 * <p>
1905 * If the value of the default {@code "AuditoryCues.actionMap"} is
2026 */
2027 private void cancelCurrentSound(Clip clip) {
2028 Clip lastClip = null;
2029
2030 synchronized(audioLock) {
2031 if (clip == null || clip == clipPlaying) {
2032 lastClip = clipPlaying;
2033 clipPlaying = null;
2034 }
2035 }
2036
2037 if (lastClip != null) {
2038 lastClip.removeLineListener(this);
2039 lastClip.close();
2040 }
2041 }
2042 }
2043
2044 /**
2045 * Utility method that loads audio bits for the specified
2046 * <code>soundFile</code> filename. If this method is unable to
2047 * build a viable path name from the <code>baseClass</code> and
2048 * <code>soundFile</code> passed into this method, it will
2049 * return <code>null</code>.
2050 *
2051 * @param soundFile the name of the audio file to be retrieved
2052 * from disk
2053 * @return A byte[] with audio data or null
2054 * @since 1.4
2055 */
2056 private byte[] loadAudioData(final String soundFile){
2057 if (soundFile == null) {
2058 return null;
2059 }
2060 /* Copy resource into a byte array. This is
2061 * necessary because several browsers consider
2062 * Class.getResource a security risk since it
2063 * can be used to load additional classes.
2064 * Class.getResourceAsStream just returns raw
2065 * bytes, which we can convert to a sound.
2066 */
2067 byte[] buffer = AccessController.doPrivileged(
2068 new PrivilegedAction<byte[]>() {
2069 public byte[] run() {
2092 }
2093 }
2094 });
2095 if (buffer == null) {
2096 System.err.println(getClass().getName() + "/" +
2097 soundFile + " not found.");
2098 return null;
2099 }
2100 if (buffer.length == 0) {
2101 System.err.println("warning: " + soundFile +
2102 " is zero-length");
2103 return null;
2104 }
2105 return buffer;
2106 }
2107
2108 /**
2109 * If necessary, invokes {@code actionPerformed} on
2110 * {@code audioAction} to play a sound.
2111 * The {@code actionPerformed} method is invoked if the value of
2112 * the {@code "AuditoryCues.playList"} default is a {@code
2113 * non-null} {@code Object[]} containing a {@code String} entry
2114 * equal to the name of the {@code audioAction}.
2115 *
2116 * @param audioAction an Action that knows how to render the audio
2117 * associated with the system or user activity
2118 * that is occurring; a value of {@code null}, is
2119 * ignored
2120 * @throws ClassCastException if {@code audioAction} is {@code non-null}
2121 * and the value of the default {@code "AuditoryCues.playList"}
2122 * is not an {@code Object[]}
2123 * @since 1.4
2124 */
2125 protected void playSound(Action audioAction) {
2126 if (audioAction != null) {
2127 Object[] audioStrings = (Object[])
2128 UIManager.get("AuditoryCues.playList");
2129 if (audioStrings != null) {
2130 // create a HashSet to help us decide to play or not
2131 HashSet<Object> audioCues = new HashSet<Object>();
2132 for (Object audioString : audioStrings) {
2133 audioCues.add(audioString);
|
81 import java.beans.PropertyChangeEvent;
82
83
84 /**
85 * A base class to use in creating a look and feel for Swing.
86 * <p>
87 * Each of the {@code ComponentUI}s provided by {@code
88 * BasicLookAndFeel} derives its behavior from the defaults
89 * table. Unless otherwise noted each of the {@code ComponentUI}
90 * implementations in this package document the set of defaults they
91 * use. Unless otherwise noted the defaults are installed at the time
92 * {@code installUI} is invoked, and follow the recommendations
93 * outlined in {@code LookAndFeel} for installing defaults.
94 * <p>
95 * <strong>Warning:</strong>
96 * Serialized objects of this class will not be compatible with
97 * future Swing releases. The current serialization support is
98 * appropriate for short term storage or RMI between applications running
99 * the same version of Swing. As of 1.4, support for long term storage
100 * of all JavaBeans™
101 * has been added to the {@code java.beans} package.
102 * Please see {@link java.beans.XMLEncoder}.
103 *
104 * @author unattributed
105 */
106 @SuppressWarnings("serial") // Same-version serialization only
107 public abstract class BasicLookAndFeel extends LookAndFeel implements Serializable
108 {
109 /**
110 * Whether or not the developer has created a JPopupMenu.
111 */
112 static boolean needsEventHelper;
113
114 /**
115 * Lock used when manipulating clipPlaying.
116 */
117 private transient Object audioLock = new Object();
118 /**
119 * The Clip that is currently playing (set in AudioAction).
120 */
121 private Clip clipPlaying;
203 AccessController.doPrivileged(invocator);
204 invocator = null;
205 }
206
207 if (disposer != null) {
208 // Note that we're likely calling removePropertyChangeListener()
209 // during the course of AppContext.firePropertyChange().
210 // However, EventListenerAggreggate has code to safely modify
211 // the list under such circumstances.
212 context.removePropertyChangeListener(AppContext.GUI_DISPOSED,
213 disposer);
214 disposer = null;
215 }
216 }
217
218 /**
219 * Populates {@code table} with mappings from {@code uiClassID} to the
220 * fully qualified name of the ui class. The value for a
221 * particular {@code uiClassID} is {@code
222 * "javax.swing.plaf.basic.Basic + uiClassID"}. For example, the
223 * value for the {@code uiClassID TreeUI} is {@code
224 * "javax.swing.plaf.basic.BasicTreeUI"}.
225 *
226 * @param table the {@code UIDefaults} instance the entries are
227 * added to
228 * @throws NullPointerException if {@code table} is {@code null}
229 *
230 * @see javax.swing.LookAndFeel
231 * @see #getDefaults
232 */
233 protected void initClassDefaults(UIDefaults table)
234 {
235 final String basicPackageName = "javax.swing.plaf.basic.";
236 Object[] uiDefaults = {
237 "ButtonUI", basicPackageName + "BasicButtonUI",
238 "CheckBoxUI", basicPackageName + "BasicCheckBoxUI",
239 "ColorChooserUI", basicPackageName + "BasicColorChooserUI",
240 "FormattedTextFieldUI", basicPackageName + "BasicFormattedTextFieldUI",
241 "MenuBarUI", basicPackageName + "BasicMenuBarUI",
242 "MenuUI", basicPackageName + "BasicMenuUI",
243 "MenuItemUI", basicPackageName + "BasicMenuItemUI",
1840 "released ENTER", "release",
1841 "ctrl ENTER", "press",
1842 "ctrl released ENTER", "release"
1843 },
1844 };
1845
1846 table.putDefaults(defaults);
1847 }
1848
1849 static int getFocusAcceleratorKeyMask() {
1850 Toolkit tk = Toolkit.getDefaultToolkit();
1851 if (tk instanceof SunToolkit) {
1852 return ((SunToolkit)tk).getFocusAcceleratorKeyMask();
1853 }
1854 return ActionEvent.ALT_MASK;
1855 }
1856
1857
1858
1859 /**
1860 * Returns the ui that is of type {@code klass}, or null if
1861 * one can not be found.
1862 */
1863 static Object getUIOfType(ComponentUI ui, Class<?> klass) {
1864 if (klass.isInstance(ui)) {
1865 return ui;
1866 }
1867 return null;
1868 }
1869
1870 // ********* Auditory Cue support methods and objects *********
1871 // also see the "AuditoryCues" section of the defaults table
1872
1873 /**
1874 * Returns an {@code ActionMap} containing the audio actions
1875 * for this look and feel.
1876 * <P>
1877 * The returned {@code ActionMap} contains {@code Actions} that
1878 * embody the ability to render an auditory cue. These auditory
1879 * cues map onto user and system activities that may be useful
1880 * for an end user to know about (such as a dialog box appearing).
1881 * <P>
1882 * At the appropriate time,
1883 * the {@code ComponentUI} is responsible for obtaining an
1884 * {@code Action} out of the {@code ActionMap} and passing
1885 * it to {@code playSound}.
1886 * <P>
1887 * This method first looks up the {@code ActionMap} from the
1888 * defaults using the key {@code "AuditoryCues.actionMap"}.
1889 * <p>
1890 * If the value is {@code non-null}, it is returned. If the value
1891 * of the default {@code "AuditoryCues.actionMap"} is {@code null}
1892 * and the value of the default {@code "AuditoryCues.cueList"} is
1893 * {@code non-null}, an {@code ActionMapUIResource} is created and
1894 * populated. Population is done by iterating over each of the
1895 * elements of the {@code "AuditoryCues.cueList"} array, and
1896 * invoking {@code createAudioAction()} to create an {@code
1897 * Action} for each element. The resulting {@code Action} is
1898 * placed in the {@code ActionMapUIResource}, using the array
1899 * element as the key. For example, if the {@code
1900 * "AuditoryCues.cueList"} array contains a single-element, {@code
1901 * "audioKey"}, the {@code ActionMapUIResource} is created, then
1902 * populated by way of {@code actionMap.put(cueList[0],
1903 * createAudioAction(cueList[0]))}.
1904 * <p>
1905 * If the value of the default {@code "AuditoryCues.actionMap"} is
2026 */
2027 private void cancelCurrentSound(Clip clip) {
2028 Clip lastClip = null;
2029
2030 synchronized(audioLock) {
2031 if (clip == null || clip == clipPlaying) {
2032 lastClip = clipPlaying;
2033 clipPlaying = null;
2034 }
2035 }
2036
2037 if (lastClip != null) {
2038 lastClip.removeLineListener(this);
2039 lastClip.close();
2040 }
2041 }
2042 }
2043
2044 /**
2045 * Utility method that loads audio bits for the specified
2046 * {@code soundFile} filename. If this method is unable to
2047 * build a viable path name from the {@code baseClass} and
2048 * {@code soundFile} passed into this method, it will
2049 * return {@code null}.
2050 *
2051 * @param soundFile the name of the audio file to be retrieved
2052 * from disk
2053 * @return A byte[] with audio data or null
2054 * @since 1.4
2055 */
2056 private byte[] loadAudioData(final String soundFile){
2057 if (soundFile == null) {
2058 return null;
2059 }
2060 /* Copy resource into a byte array. This is
2061 * necessary because several browsers consider
2062 * Class.getResource a security risk since it
2063 * can be used to load additional classes.
2064 * Class.getResourceAsStream just returns raw
2065 * bytes, which we can convert to a sound.
2066 */
2067 byte[] buffer = AccessController.doPrivileged(
2068 new PrivilegedAction<byte[]>() {
2069 public byte[] run() {
2092 }
2093 }
2094 });
2095 if (buffer == null) {
2096 System.err.println(getClass().getName() + "/" +
2097 soundFile + " not found.");
2098 return null;
2099 }
2100 if (buffer.length == 0) {
2101 System.err.println("warning: " + soundFile +
2102 " is zero-length");
2103 return null;
2104 }
2105 return buffer;
2106 }
2107
2108 /**
2109 * If necessary, invokes {@code actionPerformed} on
2110 * {@code audioAction} to play a sound.
2111 * The {@code actionPerformed} method is invoked if the value of
2112 * the {@code "AuditoryCues.playList"} default is a
2113 * {@code non-null Object[]} containing a {@code String} entry
2114 * equal to the name of the {@code audioAction}.
2115 *
2116 * @param audioAction an Action that knows how to render the audio
2117 * associated with the system or user activity
2118 * that is occurring; a value of {@code null}, is
2119 * ignored
2120 * @throws ClassCastException if {@code audioAction} is {@code non-null}
2121 * and the value of the default {@code "AuditoryCues.playList"}
2122 * is not an {@code Object[]}
2123 * @since 1.4
2124 */
2125 protected void playSound(Action audioAction) {
2126 if (audioAction != null) {
2127 Object[] audioStrings = (Object[])
2128 UIManager.get("AuditoryCues.playList");
2129 if (audioStrings != null) {
2130 // create a HashSet to help us decide to play or not
2131 HashSet<Object> audioCues = new HashSet<Object>();
2132 for (Object audioString : audioStrings) {
2133 audioCues.add(audioString);
|