So far, all our user interface components have appeared inside a frame window that was created in the application. This is the most common situation if you write applets that run inside a web browser. But if you write applications, you usually want separate dialog boxes to pop up to give information to or get information from the user. Just as with most windowing systems, AWT distinguishes between modal and modeless dialog boxes. A modal dialog box won't let users interact with the remaining windows of the application until he or she deals with it. You use a modal dialog box when you need information from the user before you can proceed with execution. For example, when the user wants to read a file, a modal file dialog box is the one to pop up. The user must specify a file name before the program can begin the read operation. Only when the user closes the (modal) dialog box can the application proceed. A modeless dialog box lets the user enter information in both the dialog box and the remainder of the application. One example of a modeless dialog is a toolbar. The toolbar can stay in place as long as needed, and the user can interact with both the application window and the toolbar as needed. We start this section with the simplest dialogs modal dialogs with just a single message. Swing has a convenient JOptionPane class that lets you put up a simple dialog without writing any special dialog box code. Next, you see how to write more complex dialogs by implementing your own dialog windows. Finally, you see how to transfer data from your application into a dialog and back. We conclude this section by looking at two standard dialogs: file dialogs and color dialogs. File dialogs are complex, and you definitely want to be familiar with the Swing JFileChooser for this purpose it would be a real challenge to write your own. The JColorChooser dialog is useful when you want users to pick colors. Option DialogsSwing has a set of ready-made simple dialogs that suffice when you need to ask the user for a single piece of information. The JOptionPane has four static methods to show these simple dialogs:
Figure 9-46 shows a typical dialog. As you can see, the dialog has the following components:
Figure 9-46. An option dialogThe input dialog has an additional component for user input. This can be a text field into which the user can type an arbitrary string, or a combo box from which the user can select one item. The exact layout of these dialogs, and the choice of icons for standard message types, depend on the pluggable look and feel. The icon on the left side depends on one of five message types: ERROR_MESSAGE INFORMATION_MESSAGE WARNING_MESSAGE QUESTION_MESSAGE PLAIN_MESSAGE The PLAIN_MESSAGE type has no icon. Each dialog type also has a method that lets you supply your own icon instead. For each dialog type, you can specify a message. This message can be a string, an icon, a user interface component, or any other object. Here is how the message object is displayed:
You can see these options by running the program in Example 9-18. Of course, supplying a message string is by far the most common case. Supplying a Component gives you ultimate flexibility because you can make the paintComponent method draw anything you want. The buttons on the bottom depend on the dialog type and the option type. When calling showMessageDialog and showInputDialog, you get only a standard set of buttons (OK and OK/Cancel, respectively). When calling showConfirmDialog, you can choose among four option types: DEFAULT_OPTION YES_NO_OPTION YES_NO_CANCEL_OPTION OK_CANCEL_OPTION With the showOptionDialog you can specify an arbitrary set of options. You supply an array of objects for the options. Each array element is rendered as follows:
The return values of these functions are as follows:
The showConfirmDialog and showOptionDialog return integers to indicate which button the user chose. For the option dialog, this is simply the index of the chosen option or the value CLOSED_OPTION if the user closed the dialog instead of choosing an option. For the confirmation dialog, the return value can be one of the following: OK_OPTION CANCEL_OPTION YES_OPTION NO_OPTION CLOSED_OPTION This all sounds like a bewildering set of choices, but in practice it is simple:
For example, suppose you want to show the dialog in Figure 9-46. The dialog shows a message and asks the user to confirm or cancel. Thus, it is a confirmation dialog. The icon is a warning icon. The message is a string. The option type is OK_CANCEL_OPTION. Here is the call you would make: int selection = JOptionPane.showConfirmDialog(parent, "Message", "Title", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); if (selection == JOptionPane.OK_OPTION) . . . TIP
The program in Example 9-18 lets you make the selections shown in Figure 9-47. It then shows you the resulting dialog. Example 9-18. OptionDialogTest.java1. import java.awt.*; 2. import java.awt.event.*; 3. import java.awt.geom.*; 4. import java.util.*; 5. import javax.swing.*; 6. import javax.swing.border.*; 7. 8. public class OptionDialogTest 9. { 10. public static void main(String[] args) 11. { 12. OptionDialogFrame frame = new OptionDialogFrame(); 13. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 14. frame.setVisible(true); 15. } 16. } 17. 18. /** 19. A panel with radio buttons inside a titled border. 20. */ 21. class ButtonPanel extends JPanel 22. { 23. /** 24. Constructs a button panel. 25. @param title the title shown in the border 26. @param options an array of radio button labels 27. */ 28. public ButtonPanel(String title, String[] options) 29. { 30. setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title)); 31. setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 32. group = new ButtonGroup(); 33. 34. // make one radio button for each option 35. for (int i = 0; i < options.length; i++) 36. { 37. JRadioButton b = new JRadioButton(options[i]); 38. b.setActionCommand(options[i]); 39. add(b); 40. group.add(b); 41. b.setSelected(i == 0); 42. } 43. } 44. 45. /** 46. Gets the currently selected option. 47. @return the label of the currently selected radio button. 48. */ 49. public String getSelection() 50. { 51. return group.getSelection().getActionCommand(); 52. } 53. 54. private ButtonGroup group; 55. } 56. 57. /** 58. A frame that contains settings for selecting various option 59. dialogs. 60. */ 61. class OptionDialogFrame extends JFrame 62. { 63. public OptionDialogFrame() 64. { 65. setTitle("OptionDialogTest"); 66. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 67. 68. JPanel gridPanel = new JPanel(); 69. gridPanel.setLayout(new GridLayout(2, 3)); 70. 71. typePanel = new ButtonPanel("Type", 72. new String[] 73. { 74. "Message", 75. "Confirm", 76. "Option", 77. "Input" 78. }); 79. 80. messageTypePanel = new ButtonPanel("Message Type", 81. new String[] 82. { 83. "ERROR_MESSAGE", 84. "INFORMATION_MESSAGE", 85. "WARNING_MESSAGE", 86. "QUESTION_MESSAGE", 87. "PLAIN_MESSAGE" 88. }); 89. 90. messagePanel = new ButtonPanel("Message", 91. new String[] 92. { 93. "String", 94. "Icon", 95. "Component", 96. "Other", 97. "Object[]" 98. }); 99. 100. optionTypePanel = new ButtonPanel("Confirm", 101. new String[] 102. { 103. "DEFAULT_OPTION", 104. "YES_NO_OPTION", 105. "YES_NO_CANCEL_OPTION", 106. "OK_CANCEL_OPTION" 107. }); 108. 109. optionsPanel = new ButtonPanel("Option", 110. new String[] 111. { 112. "String[]", 113. "Icon[]", 114. "Object[]" 115. }); 116. 117. inputPanel = new ButtonPanel("Input", 118. new String[] 119. { 120. "Text field", 121. "Combo box" 122. }); 123. 124. gridPanel.add(typePanel); 125. gridPanel.add(messageTypePanel); 126. gridPanel.add(messagePanel); 127. gridPanel.add(optionTypePanel); 128. gridPanel.add(optionsPanel); 129. gridPanel.add(inputPanel); 130. 131. // add a panel with a Show button 132. 133. JPanel showPanel = new JPanel(); 134. JButton showButton = new JButton("Show"); 135. showButton.addActionListener(new ShowAction()); 136. showPanel.add(showButton); 137. 138. add(gridPanel, BorderLayout.CENTER); 139. add(showPanel, BorderLayout.SOUTH); 140. } 141. 142. /** 143. Gets the currently selected message. 144. @return a string, icon, component, or object array, 145. depending on the Message panel selection 146. */ 147. public Object getMessage() 148. { 149. String s = messagePanel.getSelection(); 150. if (s.equals("String")) 151. return messageString; 152. else if (s.equals("Icon")) 153. return messageIcon; 154. else if (s.equals("Component")) 155. return messageComponent; 156. else if (s.equals("Object[]")) 157. return new Object[] 158. { 159. messageString, 160. messageIcon, 161. messageComponent, 162. messageObject 163. }; 164. else if (s.equals("Other")) 165. return messageObject; 166. else return null; 167. } 168. 169. /** 170. Gets the currently selected options. 171. @return an array of strings, icons, or objects, depending 172. on the Option panel selection 173. */ 174. public Object[] getOptions() 175. { 176. String s = optionsPanel.getSelection(); 177. if (s.equals("String[]")) 178. return new String[] { "Yellow", "Blue", "Red" }; 179. else if (s.equals("Icon[]")) 180. return new Icon[] 181. { 182. new ImageIcon("yellow-ball.gif"), 183. new ImageIcon("blue-ball.gif"), 184. new ImageIcon("red-ball.gif") 185. }; 186. else if (s.equals("Object[]")) 187. return new Object[] 188. { 189. messageString, 190. messageIcon, 191. messageComponent, 192. messageObject 193. }; 194. else 195. return null; 196. } 197. 198. /** 199. Gets the selected message or option type 200. @param panel the Message Type or Confirm panel 201. @return the selected XXX_MESSAGE or XXX_OPTION constant 202. from the JOptionPane class 203. */ 204. public int getType(ButtonPanel panel) 205. { 206. String s = panel.getSelection(); 207. try 208. { 209. return JOptionPane.class.getField(s).getInt(null); 210. } 211. catch(Exception e) 212. { 213. return -1; 214. } 215. } 216. 217. /** 218. The action listener for the Show button shows a 219. Confirm, Input, Message, or Option dialog depending 220. on the Type panel selection. 221. */ 222. private class ShowAction implements ActionListener 223. { 224. public void actionPerformed(ActionEvent event) 225. { 226. if (typePanel.getSelection().equals("Confirm")) 227. JOptionPane.showConfirmDialog( 228. OptionDialogFrame.this, 229. getMessage(), 230. "Title", 231. getType(optionTypePanel), 232. getType(messageTypePanel)); 233. else if (typePanel.getSelection().equals("Input")) 234. { 235. if (inputPanel.getSelection().equals("Text field")) 236. JOptionPane.showInputDialog( 237. OptionDialogFrame.this, 238. getMessage(), 239. "Title", 240. getType(messageTypePanel)); 241. else 242. JOptionPane.showInputDialog( 243. OptionDialogFrame.this, 244. getMessage(), 245. "Title", 246. getType(messageTypePanel), 247. null, 248. new String[] { "Yellow", "Blue", "Red" }, 249. "Blue"); 250. } 251. else if (typePanel.getSelection().equals("Message")) 252. JOptionPane.showMessageDialog( 253. OptionDialogFrame.this, 254. getMessage(), 255. "Title", 256. getType(messageTypePanel)); 257. else if (typePanel.getSelection().equals("Option")) 258. JOptionPane.showOptionDialog( 259. OptionDialogFrame.this, 260. getMessage(), 261. "Title", 262. getType(optionTypePanel), 263. getType(messageTypePanel), 264. null, 265. getOptions(), 266. getOptions()[0]); 267. } 268. } 269. 270. public static final int DEFAULT_WIDTH = 600; 271. public static final int DEFAULT_HEIGHT = 400; 272. 273. private ButtonPanel typePanel; 274. private ButtonPanel messagePanel; 275. private ButtonPanel messageTypePanel; 276. private ButtonPanel optionTypePanel; 277. private ButtonPanel optionsPanel; 278. private ButtonPanel inputPanel; 279. 280. private String messageString = "Message"; 281. private Icon messageIcon = new ImageIcon("blue-ball.gif"); 282. private Object messageObject = new Date(); 283. private Component messageComponent = new SamplePanel(); 284. } 285. 286. /** 287. A panel with a painted surface 288. */ 289. 290. class SamplePanel extends JPanel 291. { 292. public void paintComponent(Graphics g) 293. { 294. super.paintComponent(g); 295. Graphics2D g2 = (Graphics2D) g; 296. Rectangle2D rect = new Rectangle2D.Double(0, 0, getWidth() - 1, getHeight() - 1); 297. g2.setPaint(Color.YELLOW); 298. g2.fill(rect); 299. g2.setPaint(Color.BLUE); 300. g2.draw(rect); 301. } 302. 303. public Dimension getMinimumSize() 304. { 305. return new Dimension(10, 10); 306. } 307. } javax.swing.JOptionPane 1.2
Figure 9-47. The OptionDialogTest programCreating DialogsIn the last section, you saw how to use the JOptionPane class to show a simple dialog. In this section, you see how to create such a dialog by hand. Figure 9-48 shows a typical modal dialog box, a program information box that is displayed when the user clicks the About button. Figure 9-48. An About dialog boxTo implement a dialog box, you derive a class from JDialog. This is essentially the same process as deriving the main window for an application from JFrame. More precisely:
Here's an example dialog box: public AboutDialog extends JDialog { public AboutDialog(JFrame owner) { super(owner, "About DialogTest", true); add(new JLabel( "<html><h1><i>Core Java</i></h1><hr>By Cay Horstmann and Gary Cornell</html>"), BorderLayout.CENTER); JPanel panel = new JPanel(); JButton ok = new JButton("Ok"); ok.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { setVisible(false); } }); panel.add(ok); add(panel, BorderLayout.SOUTH); setSize(250, 150); } } As you can see, the constructor adds user interface elements: in this case, labels and a button. It adds a handler to the button and sets the size of the dialog. To display the dialog box, you create a new dialog object and make it visible: JDialog dialog = new AboutDialog(this); dialog.setVisible(true); Actually, in the sample code below, we create the dialog box only once, and we can reuse it whenever the user clicks the About button. if (dialog == null) // first time dialog = new AboutDialog(this); dialog.setVisible(true); When the user clicks the Ok button, the dialog box should close. This is handled in the event handler of the Ok button: ok.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { setVisible(false); } }); When the user closes the dialog by clicking on the Close box, then the dialog is also hidden. Just as with a JFrame, you can override this behavior with the setDefaultCloseOperation method. Example 9-19 is the code for the About dialog box test program. Example 9-19. DialogTest.java1. import java.awt.*; 2. import java.awt.event.*; 3. import javax.swing.*; 4. 5. public class DialogTest 6. { 7. public static void main(String[] args) 8. { 9. DialogFrame frame = new DialogFrame(); 10. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11. frame.setVisible(true); 12. } 13. } 14. 15. /** 16. A frame with a menu whose File->About action shows a dialog. 17. */ 18. class DialogFrame extends JFrame 19. { 20. public DialogFrame() 21. { 22. setTitle("DialogTest"); 23. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 24. 25. // construct a File menu 26. 27. JMenuBar menuBar = new JMenuBar(); 28. setJMenuBar(menuBar); 29. JMenu fileMenu = new JMenu("File"); 30. menuBar.add(fileMenu); 31. 32. // add About and Exit menu items 33. 34. // The About item shows the About dialog 35. 36. JMenuItem aboutItem = new JMenuItem("About"); 37. aboutItem.addActionListener(new 38. ActionListener() 39. { 40. public void actionPerformed(ActionEvent event) 41. { 42. if (dialog == null) // first time 43. dialog = new AboutDialog(DialogFrame.this); 44. dialog.setVisible(true); // pop up dialog 45. } 46. }); 47. fileMenu.add(aboutItem); 48. 49. // The Exit item exits the program 50. 51. JMenuItem exitItem = new JMenuItem("Exit"); 52. exitItem.addActionListener(new 53. ActionListener() 54. { 55. public void actionPerformed(ActionEvent event) 56. { 57. System.exit(0); 58. } 59. }); 60. fileMenu.add(exitItem); 61. } 62. 63. public static final int DEFAULT_WIDTH = 300; 64. public static final int DEFAULT_HEIGHT = 200; 65. 66. private AboutDialog dialog; 67. } 68. 69. /** 70. A sample modal dialog that displays a message and 71. waits for the user to click the Ok button. 72. */ 73. class AboutDialog extends JDialog 74. { 75. public AboutDialog(JFrame owner) 76. { 77. super(owner, "About DialogTest", true); 78. 79. // add HTML label to center 80. 81. add(new JLabel( 82. "<html><h1><i>Core Java</i></h1><hr>By Cay Horstmann and Gary Cornell</html>"), 83. BorderLayout.CENTER); 84. 85. // Ok button closes the dialog 86. 87. JButton ok = new JButton("Ok"); 88. ok.addActionListener(new 89. ActionListener() 90. { 91. public void actionPerformed(ActionEvent event) 92. { 93. setVisible(false); 94. } 95. }); 96. 97. // add Ok button to southern border 98. 99. JPanel panel = new JPanel(); 100. panel.add(ok); 101. add(panel, BorderLayout.SOUTH); 102. 103. setSize(250, 150); 104. } 105. } javax.swing.JDialog 1.2
Data ExchangeThe most common reason to put up a dialog box is to get information from the user. You have already seen how easy it is to make a dialog box object: give it initial data and then call setVisible(true) to display the dialog box on the screen. Now let us see how to transfer data in and out of a dialog box. Consider the dialog box in Figure 9-49 that could be used to obtain a user name and a password to connect to some on-line service. Figure 9-49. Password dialog boxYour dialog box should provide methods to set default data. For example, the PasswordChooser class of the example program has a method, setUser, to place default values into the next fields: public void setUser(User u) { username.setText(u.getName()); } Once you set the defaults (if desired), you show the dialog by calling setVisible(true). The dialog is now displayed. The user then fills in the information and clicks the Ok or Cancel button. The event handlers for both buttons call setVisible(false), which terminates the call to setVisible(true). Alternatively, the user may close the dialog. If you did not install a window listener for the dialog, then the default window closing operation applies: the dialog becomes invisible, which also terminates the call to setVisible(true). The important issue is that the call to setVisible(true) blocks until the user has dismissed the dialog. This makes it easy to implement modal dialogs. You want to know whether the user has accepted or canceled the dialog. Our sample code sets the ok flag to false before showing the dialog. Only the event handler for the Ok button sets the ok flag to TRue. In that case, you can retrieve the user input from the dialog. NOTE
The example program contains another useful improvement. When you construct a JDialog object, you need to specify the owner frame. However, quite often you want to show the same dialog with different owner frames. It is better to pick the owner frame when you are ready to show the dialog, not when you construct the PasswordChooser object. The trick is to have the PasswordChooser extend JPanel instead of JDialog. Build a JDialog object on the fly in the showDialog method: public boolean showDialog(Frame owner, String title) { ok = false; if (dialog == null || dialog.getOwner() != owner) { dialog = new JDialog(owner, true); dialog.add(this); dialog.pack(); } dialog.setTitle(title); dialog.setVisible(true); return ok; } Note that it is safe to have owner equal to null. You can do even better. Sometimes, the owner frame isn't readily available. It is easy enough to compute it from any parent component, like this: Frame owner; if (parent instanceof Frame) owner = (Frame) parent; else owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); We use this enhancement in our sample program. The JOptionPane class also uses this mechanism. Many dialogs have a default button, which is automatically selected if the user presses a trigger key (ENTER in most "look and feel" implementations). The default button is specially marked, often with a thick outline. You set the default button in the root pane of the dialog: dialog.getRootPane().setDefaultButton(okButton); If you follow our suggestion of laying out the dialog in a panel, then you must be careful to set the default button only after you wrapped the panel into a dialog. The panel itself has no root pane. Example 9-20 is the complete code that illustrates the data flow into and out of a dialog box. Example 9-20. DataExchangeTest.java1. import java.awt.*; 2. import java.awt.event.*; 3. import javax.swing.*; 4. 5. public class DataExchangeTest 6. { 7. public static void main(String[] args) 8. { 9. DataExchangeFrame frame = new DataExchangeFrame(); 10. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 11. frame.setVisible(true); 12. } 13. } 14. 15. /** 16. A frame with a menu whose File->Connect action shows a 17. password dialog. 18. */ 19. class DataExchangeFrame extends JFrame 20. { 21. public DataExchangeFrame() 22. { 23. setTitle("DataExchangeTest"); 24. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 25. 26. // construct a File menu 27. 28. JMenuBar mbar = new JMenuBar(); 29. setJMenuBar(mbar); 30. JMenu fileMenu = new JMenu("File"); 31. mbar.add(fileMenu); 32. 33. // add Connect and Exit menu items 34. 35. JMenuItem connectItem = new JMenuItem("Connect"); 36. connectItem.addActionListener(new ConnectAction()); 37. fileMenu.add(connectItem); 38. 39. // The Exit item exits the program 40. 41. JMenuItem exitItem = new JMenuItem("Exit"); 42. exitItem.addActionListener(new 43. ActionListener() 44. { 45. public void actionPerformed(ActionEvent event) 46. { 47. System.exit(0); 48. } 49. }); 50. fileMenu.add(exitItem); 51. 52. textArea = new JTextArea(); 53. add(new JScrollPane(textArea), BorderLayout.CENTER); 54. } 55. 56. public static final int DEFAULT_WIDTH = 300; 57. public static final int DEFAULT_HEIGHT = 200; 58. 59. private PasswordChooser dialog = null; 60. private JTextArea textArea; 61. 62. /** 63. The Connect action pops up the password dialog. 64. */ 65. 66. private class ConnectAction implements ActionListener 67. { 68. public void actionPerformed(ActionEvent event) 69. { 70. // if first time, construct dialog 71. 72. if (dialog == null) 73. dialog = new PasswordChooser(); 74. 75. // set default values 76. dialog.setUser(new User("yourname", null)); 77. 78. // pop up dialog 79. if (dialog.showDialog(DataExchangeFrame.this, "Connect")) 80. { 81. // if accepted, retrieve user input 82. User u = dialog.getUser(); 83. textArea.append( 84. "user name = " + u.getName() 85. + ", password = " + (new String(u.getPassword())) 86. + "\n"); 87. } 88. } 89. } 90. } 91. 92. /** 93. A password chooser that is shown inside a dialog 94. */ 95. class PasswordChooser extends JPanel 96. { 97. public PasswordChooser() 98. { 99. setLayout(new BorderLayout()); 100. 101. // construct a panel with user name and password fields 102. 103. JPanel panel = new JPanel(); 104. panel.setLayout(new GridLayout(2, 2)); 105. panel.add(new JLabel("User name:")); 106. panel.add(username = new JTextField("")); 107. panel.add(new JLabel("Password:")); 108. panel.add(password = new JPasswordField("")); 109. add(panel, BorderLayout.CENTER); 110. 111. // create Ok and Cancel buttons that terminate the dialog 112. 113. okButton = new JButton("Ok"); 114. okButton.addActionListener(new 115. ActionListener() 116. { 117. public void actionPerformed(ActionEvent event) 118. { 119. ok = true; 120. dialog.setVisible(false); 121. } 122. }); 123. 124. JButton cancelButton = new JButton("Cancel"); 125. cancelButton.addActionListener(new 126. ActionListener() 127. { 128. public void actionPerformed(ActionEvent event) 129. { 130. dialog.setVisible(false); 131. } 132. }); 133. 134. // add buttons to southern border 135. 136. JPanel buttonPanel = new JPanel(); 137. buttonPanel.add(okButton); 138. buttonPanel.add(cancelButton); 139. add(buttonPanel, BorderLayout.SOUTH); 140. } 141. 142. /** 143. Sets the dialog defaults. 144. @param u the default user information 145. */ 146. public void setUser(User u) 147. { 148. username.setText(u.getName()); 149. } 150. 151. /** 152. Gets the dialog entries. 153. @return a User object whose state represents 154. the dialog entries 155. */ 156. public User getUser() 157. { 158. return new User(username.getText(), password.getPassword()); 159. } 160. 161. /** 162. Show the chooser panel in a dialog 163. @param parent a component in the owner frame or null 164. @param title the dialog window title 165. */ 166. public boolean showDialog(Component parent, String title) 167. { 168. ok = false; 169. 170. // locate the owner frame 171. 172. Frame owner = null; 173. if (parent instanceof Frame) 174. owner = (Frame) parent; 175. else 176. owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); 177. 178. // if first time, or if owner has changed, make new dialog 179. 180. if (dialog == null || dialog.getOwner() != owner) 181. { 182. dialog = new JDialog(owner, true); 183. dialog.add(this); 184. dialog.getRootPane().setDefaultButton(okButton); 185. dialog.pack(); 186. } 187. 188. // set title and show dialog 189. 190. dialog.setTitle(title); 191. dialog.setVisible(true); 192. return ok; 193. } 194. 195. private JTextField username; 196. private JPasswordField password; 197. private JButton okButton; 198. private boolean ok; 199. private JDialog dialog; 200. } 201. 202. /** 203. A user has a name and password. For security reasons, the 204. password is stored as a char[], not a String. 205. */ 206. class User 207. { 208. public User(String aName, char[] aPassword) 209. { 210. name = aName; 211. password = aPassword; 212. } 213. 214. public String getName() { return name; } 215. public char[] getPassword() { return password; } 216. 217. public void setName(String aName) { name = aName; } 218. public void setPassword(char[] aPassword) { password = aPassword; } 219. 220. private String name; 221. private char[] password; 222. } javax.swing.SwingUtilities 1.2
javax.swing.JComponent 1.2
javax.swing.JRootPane 1.2
javax.swing.JButton 1.2
File DialogsWhen you write an application, you often want to be able to open and save files. A good file dialog box that shows files and directories and lets the user navigate the file system is hard to write, and you definitely don't want to reinvent that wheel. Fortunately, Swing provides a JFileChooser class that allows you to display a file dialog box similar to the one that most native applications use. JFileChooser dialogs are always modal. Note that the JFileChooser class is not a subclass of JDialog. Instead of calling setVisible(true), you call showOpenDialog to display a dialog for opening a file or you call showSaveDialog to display a dialog for saving a file. The button for accepting a file is then automatically labeled Open or Save. You can also supply your own button label with the showDialog method. Figure 9-50 shows an example of the file chooser dialog box. Figure 9-50. File chooser dialog boxHere are the steps needed to put up a file dialog box and recover what the user chooses from the box.
For the most part, these steps are simple. The major difficulty with using a file dialog is to specify a subset of files from which the user should choose. For example, suppose the user should choose a GIF image file. Then, the file chooser should only display files with extension .gif. It should also give the user some kind of feedback that the displayed files are of a particular category, such as "GIF Images." But the situation can be more complex. If the user should choose a JPEG image file, then the extension can be either .jpg or .jpeg. Rather than coming up with a mechanism to codify these complexities, the designers of the file chooser supply a more elegant mechanism: to restrict the displayed files, you supply an object that extends the abstract class javax.swing.filechooser.FileFilter. The file chooser passes each file to the file filter and displays only the files that the file filter accepts. At the time of this writing, only one such subclass is supplied: the default filter that accepts all files. However, it is easy to write ad hoc file filters. You simply implement the two abstract methods of the FileFilter superclass: public boolean accept(File f); public String getDescription(); NOTE
The first method tests whether a file should be accepted. The second method returns a description of the file type that can be displayed in the file chooser dialog. For example, to filter for GIF files, you might use public class GifFilter extends FileFilter { public boolean accept(File f) { return f.getName().toLowerCase().endsWith(".gif") || f.isDirectory(); } public String getDescription() { return "GIF Image"; } } Once you have a file filter object, you use the setFileFilter method of the JFileChooser class to install it into the file chooser object: chooser.setFileFilter(new GifFilter()); In our sample program, we supply a class ExtensionFileFilter, to be used as follows: ExtensionFileFilter filter = new ExtensionFileFilter(); filter.addExtension("jpg"); filter.addExtension("gif"); filter.setDescription("Image files"); The implementation of the ExtensionFileFilter is a straightforward generalization of the GifFilter class. You may want to use that class in your own programs. NOTE
You can install multiple filters to the file chooser by calling chooser.addChoosableFileFilter(new GifFilter()); chooser.addChoosableFileFilter(new JpegFilter()); . . . The user selects a filter from the combo box at the bottom of the file dialog. By default, the "All files" filter is always present in the combo box. This is a good idea, just in case a user of your program needs to select a file with a nonstandard extension. However, if you want to suppress the "All files" filter, call chooser.setAcceptAllFileFilterUsed(false) CAUTION
Finally, you can customize the file chooser by providing special icons and file descriptions for each file that the file chooser displays. You do this by supplying an object of a class extending the FileView class in the javax.swing.filechooser package. This is definitely an advanced technique. Normally, you don't need to supply a file view the pluggable look and feel supplies one for you. But if you want to show different icons for special file types, you can install your own file view. You need to extend the FileView class and implement five methods: Icon getIcon(File f); String getName(File f); String getDescription(File f); String getTypeDescription(File f); Boolean isTraversable(File f); Then you use the setFileView method to install your file view into the file chooser. The file chooser calls your methods for each file or directory that it wants to display. If your method returns null for the icon, name, or description, the file chooser then consults the default file view of the look and feel. That is good, because it means you need to deal only with the file types for which you want to do something different. The file chooser calls the isTraversable method to decide whether to open a directory when a user clicks on it. Note that this method returns a Boolean object, not a boolean value! This seems weird, but it is actually convenient if you aren't interested in deviating from the default file view, just return null. The file chooser will then consult the default file view. In other words, the method returns a Boolean to let you choose among three options: true (Boolean.TRUE), false (Boolean.FALSE), and don't care (null). The example program contains a simple file view class. That class shows a particular icon whenever a file matches a file filter. We use it to display a palette icon for all image files. class FileIconView extends FileView { public FileIconView(FileFilter aFilter, Icon anIcon) { filter = aFilter; icon = anIcon; } public Icon getIcon(File f) { if (!f.isDirectory() && filter.accept(f)) return icon; else return null; } private FileFilter filter; private Icon icon; } CAUTION
You install this file view into your file chooser with the setFileView method: chooser.setFileView(new FileIconView(filter, new ImageIcon("palette.gif"))); The file chooser will then show the palette icon next to all files that pass the filter and use the default file view to show all other files. Naturally, we use the same filter that we set in the file chooser. TIP
Finally, you can customize a file dialog by adding an accessory component. For example, Figure 9-51 shows a preview accessory next to the file list. This accessory displays a thumbnail view of the currently selected file. Figure 9-51. A file dialog with a preview accessoryAn accessory can be any Swing component. In our case, we extend the JLabel class and set its icon to a scaled copy of the graphics image: class ImagePreviewer extends JLabel { public ImagePreviewer(JFileChooser chooser) { setPreferredSize(new Dimension(100, 100)); setBorder(BorderFactory.createEtchedBorder()); } public void loadImage(File f) { ImageIcon icon = new ImageIcon(f.getPath()); if(icon.getIconWidth() > getWidth()) icon = new ImageIcon(icon.getImage().getScaledInstance( getWidth(), -1, Image.SCALE_DEFAULT)); setIcon(icon); repaint(); } } There is just one challenge. We want to update the preview image whenever the user selects a different file. The file chooser uses the "JavaBeans" mechanism of notifying interested listeners whenever one of its properties changes. The selected file is a property that you can monitor by installing a PropertyChangeListener. We discuss this mechanism in greater detail in Volume 2. Here is the code that you need to trap the notifications: chooser.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName() == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY) { File newFile = (File) event.getNewValue() // update the accessory . . . } } }); In our example program, we add this code to the ImagePreviewer constructor. Example 9-21 contains a modification of the ImageViewer program from Chapter 2, in which the file chooser has been enhanced by a custom file view and a preview accessory. Example 9-21. FileChooserTest.java1. import java.awt.*; 2. import java.awt.event.*; 3. import java.awt.image.*; 4. import java.beans.*; 5. import java.util.*; 6. import java.io.*; 7. import javax.swing.*; 8. import javax.swing.filechooser.FileFilter; 9. import javax.swing.filechooser.FileView; 10. 11. public class FileChooserTest 12. { 13. public static void main(String[] args) 14. { 15. ImageViewerFrame frame = new ImageViewerFrame(); 16. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 17. frame.setVisible(true); 18. } 19. } 20. 21. /** 22. A frame that has a menu for loading an image and a display 23. area for the loaded image. 24. */ 25. class ImageViewerFrame extends JFrame 26. { 27. public ImageViewerFrame() 28. { 29. setTitle("FileChooserTest"); 30. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 31. 32. // set up menu bar 33. JMenuBar menuBar = new JMenuBar(); 34. setJMenuBar(menuBar); 35. 36. JMenu menu = new JMenu("File"); 37. menuBar.add(menu); 38. 39. JMenuItem openItem = new JMenuItem("Open"); 40. menu.add(openItem); 41. openItem.addActionListener(new FileOpenListener()); 42. 43. JMenuItem exitItem = new JMenuItem("Exit"); 44. menu.add(exitItem); 45. exitItem.addActionListener(new 46. ActionListener() 47. { 48. public void actionPerformed(ActionEvent event) 49. { 50. System.exit(0); 51. } 52. }); 53. 54. // use a label to display the images 55. label = new JLabel(); 56. add(label); 57. 58. // set up file chooser 59. chooser = new JFileChooser(); 60. 61. // accept all image files ending with .jpg, .jpeg, .gif 62. final ExtensionFileFilter filter = new ExtensionFileFilter(); 63. filter.addExtension("jpg"); 64. filter.addExtension("jpeg"); 65. filter.addExtension("gif"); 66. filter.setDescription("Image files"); 67. chooser.setFileFilter(filter); 68. 69. chooser.setAccessory(new ImagePreviewer(chooser)); 70. 71. chooser.setFileView(new FileIconView(filter, new ImageIcon("palette.gif"))); 72. } 73. 74. /** 75. This is the listener for the File->Open menu item. 76. */ 77. private class FileOpenListener implements ActionListener 78. { 79. public void actionPerformed(ActionEvent event) 80. { 81. chooser.setCurrentDirectory(new File(".")); 82. 83. // show file chooser dialog 84. int result = chooser.showOpenDialog(ImageViewerFrame.this); 85. 86. // if image file accepted, set it as icon of the label 87. if(result == JFileChooser.APPROVE_OPTION) 88. { 89. String name = chooser.getSelectedFile().getPath(); 90. label.setIcon(new ImageIcon(name)); 91. } 92. } 93. } 94. 95. public static final int DEFAULT_WIDTH = 300; 96. public static final int DEFAULT_HEIGHT = 400; 97. 98. private JLabel label; 99. private JFileChooser chooser; 100. } 101. 102. /** 103. This file filter matches all files with a given set of 104. extensions. 105. */ 106. class ExtensionFileFilter extends FileFilter 107. { 108. /** 109. Adds an extension that this file filter recognizes. 110. @param extension a file extension (such as ".txt" or "txt") 111. */ 112. public void addExtension(String extension) 113. { 114. if (!extension.startsWith(".")) 115. extension = "." + extension; 116. extensions.add(extension.toLowerCase()); 117. } 118. 119. /** 120. Sets a description for the file set that this file filter 121. recognizes. 122. @param aDescription a description for the file set 123. */ 124. public void setDescription(String aDescription) 125. { 126. description = aDescription; 127. } 128. 129. /** 130. Returns a description for the file set that this file 131. filter recognizes. 132. @return a description for the file set 133. */ 134. public String getDescription() 135. { 136. return description; 137. } 138. 139. public boolean accept(File f) 140. { 141. if (f.isDirectory()) return true; 142. String name = f.getName().toLowerCase(); 143. 144. // check if the file name ends with any of the extensions 145. for (String extension : extensions) 146. if (name.endsWith(extension)) 147. return true; 148. return false; 149. } 150. 151. private String description = ""; 152. private ArrayList<String> extensions = new ArrayList<String>(); 153. } 154. 155. /** 156. A file view that displays an icon for all files that match 157. a file filter. 158. */ 159. class FileIconView extends FileView 160. { 161. /** 162. Constructs a FileIconView. 163. @param aFilter a file filter--all files that this filter 164. accepts will be shown with the icon. 165. @param anIcon--the icon shown with all accepted files. 166. */ 167. public FileIconView(FileFilter aFilter, Icon anIcon) 168. { 169. filter = aFilter; 170. icon = anIcon; 171. } 172. 173. public Icon getIcon(File f) 174. { 175. if (!f.isDirectory() && filter.accept(f)) 176. return icon; 177. else return null; 178. } 179. 180. private FileFilter filter; 181. private Icon icon; 182. } 183. 184. /** 185. A file chooser accessory that previews images. 186. */ 187. class ImagePreviewer extends JLabel 188. { 189. /** 190. Constructs an ImagePreviewer. 191. @param chooser the file chooser whose property changes 192. trigger an image change in this previewer 193. */ 194. public ImagePreviewer(JFileChooser chooser) 195. { 196. setPreferredSize(new Dimension(100, 100)); 197. setBorder(BorderFactory.createEtchedBorder()); 198. 199. chooser.addPropertyChangeListener(new 200. PropertyChangeListener() 201. { 202. public void propertyChange(PropertyChangeEvent 203. event) 204. { 205. if (event.getPropertyName() == 206. JFileChooser.SELECTED_FILE_CHANGED_PROPERTY) 207. { 208. // the user has selected a new file 209. File f = (File) event.getNewValue(); 210. if (f == null) { setIcon(null); return; } 211. 212. // read the image into an icon 213. ImageIcon icon = new ImageIcon(f.getPath()); 214. 215. // if the icon is too large to fit, scale it 216. if(icon.getIconWidth() > getWidth()) 217. icon = new ImageIcon(icon.getImage().getScaledInstance( 218. getWidth(), -1, Image.SCALE_DEFAULT)); 219. 220. setIcon(icon); 221. } 222. } 223. }); 224. } 225. } javax.swing.JFileChooser 1.2
javax.swing.filechooser.FileFilter 1.2
javax.swing.filechooser.FileView 1.2
Color ChoosersAs you saw in the preceding section, a high-quality file chooser is an intricate user interface component that you definitely do not want to implement yourself. Many user interface toolkits provide other common dialogs: to choose a date/time, currency value, font, color, and so on. The benefit is twofold. Programmers can simply use a high-quality implementation rather than rolling their own. And users have a common experience for these selections. At this point, Swing provides only one additional chooser, the JColorChooser (see Figures 9-52 through 9-54). You use it to let users pick a color value. Like the JFileChooser class, the color chooser is a component, not a dialog, but it contains convenience methods to create dialogs that contain a color chooser component. Figure 9-52. The "swatches" pane of a color chooserFigure 9-54. The RGB pane of a color chooserHere is how you show a modal dialog with a color chooser: Color selectedColor = JColorChooser.showDialog(parent,title, initialColor); Alternatively, you can display a modeless color chooser dialog. You supply
Figure 9-53. The HSB pane of a color chooserHere is how you make a modeless dialog that sets the background color when the user clicks the OK button: chooser = new JColorChooser(); dialog = JColorChooser.createDialog( parent, "Background Color", false /* not modal */, chooser, new ActionListener() // OK button listener { public void actionPerformed(ActionEvent event) { setBackground(chooser.getColor()); } }, null /* no Cancel button listener */); You can do even better than that and give the user immediate feedback of the color selection. To monitor the color selections, you need to obtain the selection model of the chooser and add a change listener:
In this case, there is no benefit to the OK and Cancel buttons that the color chooser dialog provides. You can just add the color chooser component directly into a modeless dialog: dialog = new JDialog(parent, false /* not modal */); dialog.add(chooser); dialog.pack(); The program in Example 9-22 shows the three types of dialogs. If you click on the Modal button, you must select a color before you can do anything else. If you click on the Modeless button, you get a modeless dialog, but the color change only happens when you click the OK button on the dialog. If you click the Immediate button, you get a modeless dialog without buttons. As soon as you pick a different color in the dialog, the background color of the panel is updated. This ends our discussion of user interface components. The material in Chapters 7 through 9 showed you how to implement simple GUIs in Swing. Turn to Volume 2 for more advanced Swing components and sophisticated graphics techniques. Example 9-22. ColorChooserTest.java1. import java.awt.*; 2. import java.awt.event.*; 3. import javax.swing.*; 4. import javax.swing.event.*; 5. 6. public class ColorChooserTest 7. { 8. public static void main(String[] args) 9. { 10. ColorChooserFrame frame = new ColorChooserFrame(); 11. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 12. frame.setVisible(true); 13. } 14. } 15. 16. /** 17. A frame with a color chooser panel 18. */ 19. class ColorChooserFrame extends JFrame 20. { 21. public ColorChooserFrame() 22. { 23. setTitle("ColorChooserTest"); 24. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 25. 26. // add color chooser panel to frame 27. 28. ColorChooserPanel panel = new ColorChooserPanel(); 29. add(panel); 30. } 31. 32. public static final int DEFAULT_WIDTH = 300; 33. public static final int DEFAULT_HEIGHT = 200; 34. } 35. 36. /** 37. A panel with buttons to pop up three types of color choosers 38. */ 39. class ColorChooserPanel extends JPanel 40. { 41. public ColorChooserPanel() 42. { 43. JButton modalButton = new JButton("Modal"); 44. modalButton.addActionListener(new ModalListener()); 45. add(modalButton); 46. 47. JButton modelessButton = new JButton("Modeless"); 48. modelessButton.addActionListener(new ModelessListener()); 49. add(modelessButton); 50. 51. JButton immediateButton = new JButton("Immediate"); 52. immediateButton.addActionListener(new ImmediateListener()); 53. add(immediateButton); 54. } 55. 56. /** 57. This listener pops up a modal color chooser 58. */ 59. private class ModalListener implements ActionListener 60. { 61. public void actionPerformed(ActionEvent event) 62. { 63. Color defaultColor = getBackground(); 64. Color selected = JColorChooser.showDialog( 65. ColorChooserPanel.this, 66. "Set background", 67. defaultColor); 68. if (selected != null) setBackground(selected); 69. } 70. } 71. 72. /** 73. This listener pops up a modeless color chooser. 74. The panel color is changed when the user clicks the OK 75. button. 76. */ 77. private class ModelessListener implements ActionListener 78. { 79. public ModelessListener() 80. { 81. chooser = new JColorChooser(); 82. dialog = JColorChooser.createDialog( 83. ColorChooserPanel.this, 84. "Background Color", 85. false /* not modal */, 86. chooser, 87. new ActionListener() // OK button listener 88. { 89. public void actionPerformed(ActionEvent event) 90. { 91. setBackground(chooser.getColor()); 92. } 93. }, 94. null /* no Cancel button listener */); 95. } 96. 97. public void actionPerformed(ActionEvent event) 98. { 99. chooser.setColor(getBackground()); 100. dialog.setVisible(true); 101. } 102. 103. private JDialog dialog; 104. private JColorChooser chooser; 105. } 106. 107. /** 108. This listener pops up a modeless color chooser. 109. The panel color is changed immediately when the 110. user picks a new color. 111. */ 112. private class ImmediateListener implements ActionListener 113. { 114. public ImmediateListener() 115. { 116. chooser = new JColorChooser(); 117. chooser.getSelectionModel().addChangeListener(new 118. ChangeListener() 119. { 120. public void stateChanged(ChangeEvent event) 121. { 122. setBackground(chooser.getColor()); 123. } 124. }); 125. 126. dialog = new JDialog( 127. (Frame) null, 128. false /* not modal */); 129. dialog.add(chooser); 130. dialog.pack(); 131. } 132. 133. public void actionPerformed(ActionEvent event) 134. { 135. chooser.setColor(getBackground()); 136. dialog.setVisible(true); 137. } 138. 139. private JDialog dialog; 140. private JColorChooser chooser; 141. } 142. } javax.swing.JColorChooser 1.2
|