Core Java Technologies Tech Tips Tips, Techniques, and Saple Code Welcoe to the Core Java Technologies Tech Tips for March 9, 2004. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platfor, Standard Edition (J2SE). This issue covers: * Working with Swing Look and Feel * Using Soundbanks These tips were developed using Java 2 SDK, Standard Edition, v 1.4.2. This issue of the Core Java Technologies Tech Tips is written by Daniel H. Steinberg, Director of Java Offerings for Di Sum Thinking, Inc, and editor-in-chief for java.net (http://java.net). You can view this issue of the Tech Tips on the Web at http://java.sun.co/developer/JDCTechTips/2004/tt0309.html. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platfors. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WORKING WITH SWING LOOK AND FEEL For the ost part, developers want a Java application to look like a native application on each platfor to which it's deployed. Soe platforms, such as Mac OS X, default to the appropriate look and feel, while others, such as Windows, require that you ake a call like the following to set a particular look and feel: UIManager.setLookAndFeel( UIManager.getSysteLookAndFeelClassName()); In this tip, you will deterine the installed look and feels on your platfor. You will add the newly available GTK+ look and feel, and then create an application where you can change the look and feel with the click of a radio button. Finally, you will learn how to custoize a cross-platform look and feel to give your application a look that is consistent with your branding. Let's begin by generating a list of your installed look and feels. A call to the static getInstalledLookAndFeels() ethod returns an array of objects of type LookAndFeelInfo. You can use getNae() to query the array for the naes of the installed LookAndFeels, or you can use getClassNae() to get the name of each iplementing class. For example, run the following program to display a list of the naes of the installed LookAndFeels: iport javax.swing.UIManager; public class AvailableLaF { public static void ain(String[] args) { UIManager.LookAndFeelInfo[] installed = UIManager.getInstalledLookAndFeels(); for (int i = 0; i < installed.length; i++) { Syste.out.println(installed[i].getName()); } Syste.out.println( "\nThe current look and feel is " + UIManager.getLookAndFeel().getNae()); } } On a achine running the Solaris Operating Environment or on a Windows 2000 coputer, you should see output like this: Metal CDE/Motif Windows The current look and feel is Metal On a Mac running Panther you should see output like this: Mac OS X Metal CDE/Motif The current look and feel is Mac OS X Aqua In J2SE 1.4.2 there is also a cross-platfor look and feel that is based on GTK+ 2.0. Although it does not show up in the list of installed look and feels, you can install it (if it's available). You can use the following ethod to install the cross-platform look and feel: private void installGTK() { try { String GTK = "co.sun.java.swing.plaf.gtk.GTKLookAndFeel"; UIManager.setLookAndFeel(GTK); UIManager.installLookAndFeel("GTK", GTK); } catch (Exception e) { Syste.err.println("Could not install GTK"); } } GTK is available on systes such as Solaris and Mac OS X. On Windows you will see the essage "Could not install GTK". If you are on a syste that supports GTK, and you want to run a Swing-based progra, you can also set the look and feel from the comand line. Here's the command that sets the look and feel (the comand is shown on multiple lines for display purposes). java -Dswing.defaultlaf= co.sun.java.swing.plaf.gtk.GTKLookAndFeel The progra below, ChangingLaF, allows you to change the look and feel using radio buttons. If GTK is available on your syste, the progra adds GTK to the list of your installed look and feels. The progra then creates a radio button for each look and feel. When you run the progra and select a radio button, the user interface changes to that look and feel. The progra does this in response to the actionPerfored() method of the inner class. The following two steps are perfored: UIManager.setLookAndFeel(getText()); SwingUtilities.updateCoponentTreeUI( ChangingLaF.this); The look and feel is changed to the text label of the selected radio button. The progra calls updateComponentTreeUI() to redraw the tree with the given coponent at the root. In this case, it akes a call to the object that is an instance of the enclosing JFrae that is identified by the class name followed by "this". In the current case, the outer class is ChangingLaF so the enclosing instance is identified by ChangingLaF.this. Here is the entire ChangingLaF progra for creating a JFrame with a changeable look and feel. iport javax.swing.JFrame; iport javax.swing.JFileChooser; iport javax.swing.JPanel; iport javax.swing.UIManager; iport javax.swing.JRadioButton; iport javax.swing.ButtonGroup; iport javax.swing.SwingUtilities; iport javax.swing.UnsupportedLookAndFeelException; iport java.awt.BorderLayout; iport java.awt.GridLayout; iport java.awt.event.ActionListener; iport java.awt.event.ActionEvent; public class ChangingLaF extends JFrae { private static ButtonGroup group = new ButtonGroup(); public static void ain(String[] args) { new ChangingLaF().getContentPane(); } ChangingLaF() { JPanel yPanel = new JPanel(); getContentPane().add( yPanel, BorderLayout.SOUTH); setLaFButtons(yPanel); getContentPane().add(new JFileChooser(), BorderLayout.CENTER); pack(); setVisible(true); } private void setLaFButtons(JPanel choices) { installGTK(); UIManager.LookAndFeelInfo[] laf = UIManager.getInstalledLookAndFeels(); choices.setLayout(new GridLayout(laf.length, 1)); for (int i = 0; i < laf.length; i++) { choices.add(new LaFButton(laf[i])); } } private void installGTK() { try { String GTK = "co.sun.java.swing.plaf.gtk.GTKLookAndFeel"; UIManager.setLookAndFeel(GTK); UIManager.installLookAndFeel("GTK", GTK); } catch (Exception e) { Syste.err.println("Could not install GTK"); } } private class LaFButton extends JRadioButton iplements ActionListener { LaFButton(UIManager.LookAndFeelInfo laf) { super(laf.getClassNae()); group.add(this); addActionListener(this); } public void actionPerfored(ActionEvent event) { try { UIManager.setLookAndFeel(getText()); SwingUtilities.updateCoponentTreeUI( ChangingLaF.this); // call yFrame.pack() // to resize frae for laf } catch (IllegalAccessException e) { // insert code to handle this exception } catch (UnsupportedLookAndFeelException e) { // insert code to handle this exception } catch (InstantiationException e) { // insert code to handle this exception } catch (ClassNotFoundException e) { // insert code to handle this exception } } } } Copile and run ChangingLaF. You should be able to easily switch aong the installed look and feels on your machine. With J2SE 1.4.2, you should also be able to take advantage of the new Windows XP look and feel. There ight be times when you want to customize the look and feel. Perhaps you want the syste or user text to use a different font or perhaps you want coponents to render in colors that match your copany's marketing theme. So let's customize a look and feel, and because GTK is not supported on Windows, let's custoize the Metal Thee. The DefaultMetalTheme used in this example is no longer the default in J2SE 1.5. Take a look at the classes in the package javax.swing.plaf.etal. The class MetalLookAndFeel is the Java iplementation of Metal. The subclass DefaultMetalThee can be used to alter some of the basic fonts for controls, enu items, the system, user input, and window titles. It can also be used to alter six basic colors that are split into three priary and three secondary colors. The following saple program, CustomTheme, creates a subclass of DefaultMetalThee. The program sets the colors to override by changing the return values of the accessor ethods. It replaces white with blue, and black with red. The progra uses the setCurrentThee() method in the MetalLookAndFeel class to select this non-standard color thee. iport javax.swing.JFrame; iport javax.swing.JFileChooser; iport javax.swing.plaf.metal.MetalLookAndFeel; iport javax.swing.UIManager; iport javax.swing.UnsupportedLookAndFeelException; iport javax.swing.plaf.metal.MetalLookAndFeel; iport javax.swing.plaf.metal.DefaultMetalTheme; iport javax.swing.plaf.ColorUIResource; iport java.awt.Color; public class CustoTheme { public static void ain(String[] args) throws UnsupportedLookAndFeelException{ UIManager.setLookAndFeel( new MetalLookAndFeel()); MetalLookAndFeel.setCurrentThee( new CustoLaF()); JFrae frame = new JFrame("Metal Theme"); frae.getContentPane().add(new JFileChooser()); frae.pack(); frae.setVisible(true); } static class CustoLaF extends DefaultMetalTheme { protected ColorUIResource getPriary1() { return new ColorUIResource(Color.MAGENTA); } public ColorUIResource getWhite() { return new ColorUIResource(Color.BLUE); } public ColorUIResource getBlack() { return new ColorUIResource(Color.RED); } public ColorUIResource getPriaryControl() { return new ColorUIResource(Color.GREEN); } protected ColorUIResource getSecondary1() { return new ColorUIResource(Color.CYAN); } } } To see the altered thee you need to ensure that the Metal look and feel is the default (on Windows this should be the case). Or you can run the application fro the command line like this. (again, the comand is shown on multiple lines for display purposes). java -Dswing.defaultlaf= javax.swing.plaf.etal.MetalLookAndFeel CustomTheme For ore information about Swing Look and Feel, see the lesson "How to Set the Look and Feel" (http://java.sun.co/docs/books/tutorial/uiswing/misc/plaf.html) in the Java Tutorial. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING SOUNDBANKS The August 5,2003 Tech Tip titled "Producing MIDI sound" (http://java.sun.co/developer/JDCTechTips/2003/tt0805.html#1), showed how to use the javax.sound.idi package to produce the sound of various instruents. The tip noted that as a developer, you are able to hear the usic because a soundbank is autoatically installed with the J2SE SDK. A soundbank represents a set of instruents, and is needed to synthesize sound. End users ight have to install a soundbank separately to hear what you have created. When users install a JRE for J2SE 1.4.1 or later, that have to opt-in to include the soundbank in the installation. This tip describes how to install and use a soundbank. It also shows how to prograatically specify which soundbank you want to use. In this tip, you will query the available soundbanks for inforation and compare the sounds generated by using them. If you have a soundbank installed, look for it in the audio subdirectory of the lib directory inside of the JRE installation. You should find a file naed either soundbank.gm or soundbank-id.gm. You can download additional soundbanks from the Java Sound API: Soundbanks page (http://java.sun.co/products/java-media/sound/soundbanks.html). Let's experient with soundbanks by first downloading the minimal and deluxe zip files on the Soundbanks page. The two zip files are 0.35MB and 4.92MB respectively. Then run the following progra, SynthesizerDemo. The program should play an octave on a synthesized voice instruent, moving up from middle C one half tone at a tie. iport javax.sound.midi.ShortMessage; iport javax.sound.midi.Synthesizer; iport javax.sound.midi.Receiver; iport javax.sound.midi.MidiSystem; iport javax.sound.midi.MidiUnavailableException; iport javax.sound.midi.InvalidMidiDataException; public class SynthesizerDeo { private ShortMessage essage = new ShortMessage(); private Synthesizer synth; private Receiver receiver; public SynthesizerDeo() { try { synth = MidiSyste.getSynthesizer(); synth.open(); receiver = synth.getReceiver(); } catch (MidiUnavailableException e) { e.printStackTrace(); } } public void playNote(int note, int duration) { setShortMessage(ShortMessage.NOTE_ON, note); receiver.send(essage, -1); try { Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } setShortMessage(ShortMessage.NOTE_OFF, note); receiver.send(essage, -1); } public void setInstruent(int instrument) { synth.getChannels()[0].prograChange(instrument); } private void setShortMessage( int onOrOff, int note) { try { essage.setMessage(onOrOff, 0, note, 70); } catch (InvalidMidiDataException e) { e.printStackTrace(); } } public void playOctave(int baseNote) { for (int i = 0; i < 13; i++) { playNote(baseNote + i, 200); } } public static void ain(String[] args) { SynthesizerDeo synth = new SynthesizerDemo(); synth.setInstruent(53); //can set instruent here synth.playOctave(60); } } When you create and initialize your Synthesizer, the default soundbank is loaded. In particular, of the four soundbanks available fro Sun, the highest quality available soundbank will be loaded. In order fro lowest to highest this is soundbank-in.gm (or soundbank.gm), soundbank-mid.gm, and soundbank-deluxe.g. If you install the file soundbank-in.gm in your platform's default location inside the audio directory, and run SynthesizerDeo again you should hear no difference. That's because the pre-installed soundbank.g and soundbank-mid.gm are identical. Run the SynthesizerDeo two more times. Once with soundbank-in.gm as the only available soundbank in the audio directory and once with soundbank-deluxe.g there as well. You should be able to hear the difference and verify with your ear which soundbank is used. Sun's reference iplementation supports soundbanks made for the Beatnik Audio Engine. Beatnik (http://www.beatnik.co) makes available an editor for creating and odifying these soundbanks. Java Sound can be extended with plug-ins in support of other soundbank file forats. Soetimes you might want to programmatically control which soundbank is used. You can use the ethod getSoundbank() in the MidiSyste class to specify a soundbank from a File, a URL, or an InputStrea. You can obtain information about the soundbank by aking calls to the various getters. The following program, SoundbankInfo, shows how. iport java.io.File; iport javax.sound.midi.MidiSystem; iport javax.sound.midi.Soundbank; public class SoundbankInfo { public static void ain(String[] args) { Soundbank bank = null; if (args.length == 1) { File file = new File(args[0]); try { bank = MidiSyste.getSoundbank(file); } catch (Exception e) { Syste.err.println(e.getClass()); Syste.err.println( "Comand line parameter must" + " be a valid path to soundbank file."); Syste.exit(0); } } else { Syste.err.println( "usage:java SoundbankInfo " + ""); Syste.exit(0); } Syste.out.print("The " + bank.getName()); Syste.out.println( " version " + bank.getVersion()); Syste.out.println( "is the " + bank.getDescription()); Syste.out.println("It was created by " + bank.getVendor() + "."); Syste.out.println("It has " + bank.getInstruents().length + " instruents"); } } Pass in the path to the deluxe soundbank as a comand line arguent. For example, depending on where you saved the soundbank, you ight enter the following: java SoundbankInfo soundbank-deluxe.g The response should look soething like this: The Untitled Headspace Soundbank version 1.0.0 is the Soundbank for use with Headspace Mixer. It was created by Sun Microsystes, Headspace Corporation. It contains 189 instruents. If instead you pass in the path to the inimal soundbank, you should see soething like this: The Patches.hsb version 2.4.3 is the Soundbank for use with Headspace Mixer. It was created by Sun Microsystes, Headspace Corporation. It contains 411 instruents. Even though the deluxe soundbank is ore than ten times the size of the inimal soundbank, the minimal soundbank contains inforation for more instruments. You can hear the difference by altering SynthesizerDeo to load different soundbanks before playing the octaves. Here's a progra that does that. iport javax.sound.midi.ShortMessage; iport javax.sound.midi.Synthesizer; iport javax.sound.midi.Receiver; iport javax.sound.midi.MidiSystem; iport javax.sound.midi.MidiUnavailableException; iport javax.sound.midi.InvalidMidiDataException; iport javax.sound.midi.Soundbank; iport java.io.File; iport java.io.IOException; public class SelectSoundbankDeo { private ShortMessage essage = new ShortMessage(); public Synthesizer synth; private Receiver receiver; public SelectSoundbankDeo() { try { synth = MidiSyste.getSynthesizer(); synth.open(); receiver = synth.getReceiver(); } catch (MidiUnavailableException e) { e.printStackTrace(); } } public void playNote(int note, int duration) { setShortMessage(ShortMessage.NOTE_ON, note); receiver.send(essage, -1); try { Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } setShortMessage(ShortMessage.NOTE_OFF, note); receiver.send(essage, -1); } public void setInstruent(int instrument, Soundbank soundbank) { synth.unloadAllInstruents(soundbank); synth.loadAllInstruents(soundbank); synth.getChannels()[0].prograChange(instrument); } private void setShortMessage( int onOrOff, int note) { try { essage.setMessage(onOrOff, 0, note, 70); } catch (InvalidMidiDataException e) { e.printStackTrace(); } } public void playOctave(int baseNote) { for (int i = 0; i < 13; i++) { playNote(baseNote + i, 200); } } public static void ain(String[] args) throws IOException, InvalidMidiDataException { SelectSoundbankDeo synth2 = new SelectSoundbankDeo(); synth2.setInstruent(53, MidiSystem.getSoundbank( new File("soundbank-deluxe.g"))); synth2.playOctave(60); synth2.setInstruent(53, MidiSystem.getSoundbank( new File("soundbank-in.gm"))); synth2.playOctave(60); } } Copile and run SelectSoundbankDemo. You should hear the different voices running through the octave. . . . . . . . . . . . . . . . . . . . . . . . IMPORTANT: Please read our Ters of Use, Privacy, and Licensing policies: http://www.sun.co/share/text/termsofuse.html http://www.sun.co/privacy/ http://developers.sun.co/dispatcher.jsp?uid=6910008 * FEEDBACK Coments? Please enter your feedback on the Tech Tips at: http://developers.sun.co/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platfor, Enterprise Edition (J2EE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platfor, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the JDC Newsletters and Publications page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Update". - To unsubscribe, go to the subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Update". - To use our one-click unsubscribe facility, see the link at the end of this eail: - ARCHIVES You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.co/developer/TechTips/index.html - COPYRIGHT Copyright 2004 Sun Microsystes, Inc. All rights reserved. 4150 Network Circle, Santa Clara, California 95054 USA. This docuent is protected by copyright. For more information, see: http://java.sun.co/jdc/copyright.html Core Java Technologies Tech Tips March 9, 2004 Tradeark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based arks are trademarks or registered tradearks of Sun Microsystems, Inc. in the United States and other countries.