Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing

Now that you've seen how to create internal frames and played around with them a bit, let's tackle a slightly larger problem. We want to build an application that can pop up internal frames that you can actually use. This starter application is a web site manager that shows us a list of HTML pages at a site and, for any of those pages, allows us to pop up the page in a separate frame and edit it. We'll keep the main list of HTML pages in one "site" frame that contains a simple list box.

Once you have a site built up with a couple of pages, you can click on any entry in the list, and if the file exists, we'll create a new "page" frame and load the file into a JTextArea object for you to edit. You can modify the text and save the file using the File menu in the page frame.

As a bonus, we'll put those cut, copy, and paste icons to use as well. You can manipulate text in any of the open page frames. The icons work as Action objects by looking at the selected text and insertion point of the active frame. (We alluded to the Action class after our last Toolbar example. We'll demonstrate it here and discuss it thoroughly at the start of the next chapter.) If the active frame is a site frame, nothing happens.

You could certainly add a lot of features to this application and make it a real working program, but we don't want to get mired down in details just yet. (If you want to get really fancy, you could look at some of the editor kits discussed in Chapter 23 and build yourself a real HTML editor.) Figure 2-9 shows the finished application with a couple of open frames.

Figure 2-9. The SiteManager application running on a platform where Metal is the default L&F

We break the code for this application into three separate classes to make discussing it more manageable. The first class handles the real application frame. The constructor handles all of the interface setup work. It sets up the toolbar, as well as the Cut, Copy, and Paste buttons. It uses the default L&F for the platform on which it is run. (You could certainly attach the LnFListener, if you wanted to.) Here's the source code:

// SiteManager.java // import java.awt.*; import java.io.*; import java.util.*; import java.awt.event.*; import javax.swing.*; public class SiteManager extends JFrame { JLayeredPane desktop; Vector popups = new Vector( ); public SiteManager( ) { super("Web Site Manager"); setSize(450, 250); setDefaultCloseOperation(EXIT_ON_CLOSE); Container contentPane = getContentPane( ); JToolBar jtb = new JToolBar( ); jtb.add(new CutAction(this)); jtb.add(new CopyAction(this)); jtb.add(new PasteAction(this)); contentPane.add(jtb, BorderLayout.NORTH); // Add our LayeredPane object for the internal frames. desktop = new JDesktopPane( ); contentPane.add(desktop, BorderLayout.CENTER); addSiteFrame("Sample"); } public static void main(String args[]) { SiteManager mgr = new SiteManager( ); mgr.setVisible(true); }

Notice that since we're finally using Swing's JFrame rather than an AWT Frame, we can replace the cumbersome WindowAdapter, which handles user close requests, with a single call to setDefaultCloseOperation(EXIT_ON_CLOSE).

Now for the creation of the site and page frames. The SiteFrame class and PageFrame class, discussed later in this chapter, extend the JInternalFrame class. These classes handle all of the hard work in getting the frames to look and act correctly. Here, we just need to make the internal frame visible and keep a reference to the frame. By keeping the popups vector around, we could eventually add Save All, Close Site, and other options. For now we just use it to help find the current frame.

// Methods to create our internal frames public void addSiteFrame(String name) { SiteFrame sf = new SiteFrame(name, this); popups.addElement(sf); desktop.add(sf, new Integer(2)); // Keep sites on top for now. sf.setVisible(true); } public void addPageFrame(String name) { PageFrame pf = new PageFrame(name, this); desktop.add(pf, new Integer(1)); pf.setVisible(true); pf.setIconifiable(true); popups.addElement(pf); } public JInternalFrame getCurrentFrame( ) { for (int i = 0; i < popups.size( ); i++) { JInternalFrame currentFrame = (JInternalFrame)popups.elementAt(i); if (currentFrame.isSelected( )) { return currentFrame; } } return null; } }

The getCurrentFrame( ) method runs through a list of all the frames currently open in the site manager and returns the active frame. (Yes, this is a bit inefficient, but we're ignoring that for right now.)

Notice that we're using a JToolBar object in our example. This is a great shortcut if you just want a few buttons along the top (or side or bottom) of your application. A JToolBar can contain almost any kind of component, though it's most often used for buttons. We don't add buttons directly; instead, we add Action objects, which are automatically converted into buttons when placed in a toolbar. The Action interface encapsulates an icon and an actionPerformed( ) method so that you don't have to do lengthy if/else-if testing. When you add an Action to the toolbar, the toolbar displays the Action's icon, and when you click on the icon, the Action's actionPerformed( ) method is called automatically. Here's the code for the CopyAction class:

// CopyAction.java // A simple Action that copies text from a PageFrame object // import java.awt.event.ActionEvent; import javax.swing.*; public class CopyAction extends AbstractAction { SiteManager manager; public CopyAction(SiteManager sm) { super("", new ImageIcon("copy.gif")); manager = sm; } public void actionPerformed(ActionEvent ae) { JInternalFrame currentFrame = manager.getCurrentFrame( ); if (currentFrame == null) { return; } // Can't cut or paste sites if (currentFrame instanceof SiteFrame) { return; } ((PageFrame)currentFrame).copyText( ); } }

The cut and paste action classes work in a similar fashion. (We won't show them here.) Swing provides a large number of pre-built Actions, so you may not even need to write your own. We'll discuss several in Chapter 23. Appendix B lists all the Actions that are provided by Swing's components as well as the key bindings (if any) with which they can be triggered.

Next we need a way to create the site frames. We can set up a separate class that extends the JInternalFrame class and contains the functionality appropriate for the site manager. Namely, we must be able to list available pages in the site and open any of those pages for editing.

We can create a frame that has a listbox as its primary component. This won't be a fancy manager, but it will do what we want. The nice thing about internal frames, from the frame's point of view, is that they look just like regular frames. You can use the constructor to add all of the graphical interface elements and put in event listeners. The only difference with internal frames is that they need to be added to an appropriate desktop pane, but again, that's not a difference we can see here in the code for the individual frames. You can change existing standalone Frame classes to JInternalFrame classes with very little effort:

// SiteFrame.java // A simple extension of the JInternalFrame class that contains a list object. // Elements of the list represent HTML pages for a web site. // import java.awt.*; import javax.swing.*; import javax.swing.event.*; public class SiteFrame extends JInternalFrame { JList nameList; SiteManager parent; // Hardcode the pages of our "site" to keep things simple. String[] pages = {"index.html", "page1.html", "page2.html"}; public SiteFrame(String name, SiteManager sm) { super("Site: " + name, true, true, true); parent = sm; setBounds(50,50,250,100); nameList = new JList(pages); nameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); nameList.addListSelectionListener(new ListSelectionListener( ) { public void valueChanged(ListSelectionEvent lse) { // We know this is the list, so pop up the page. if (!lse.getValueIsAdjusting( )) { parent.addPageFrame((String)nameList.getSelectedValue( )); } } }); Container contentPane = getContentPane( ); contentPane.add(nameList, BorderLayout.CENTER); } }

In the valueChanged( ) method for the ListSelectionListener, we handle the basic functions of the page list. Single-clicking on an entry in the list creates a new PageFrame object for that file. If the file doesn't exist, you get a blank text area for creating the page from scratch. Note that very little error checking is going on here. But you probably have already discovered that robust error checking just gets in the way of having fun, and that's all we're really trying to accomplish with this application.

Now you have the site frame going. The new page frame needs to be able to open the file (if it exists) and display the file for editing. The Cut, Copy, and Paste buttons from our earlier example allow you to move text around in a file and between open files in the application.

Like the site frame, we'll create a subclass of JInternalFrame for our page frame. We can use the constructor for the interface work again, and then allow the text area to manage all of the text display and editing work:

// PageFrame.java // A simple extension of the JInternalFrame class that contains a list object. // Elements of the list represent HTML pages for a web site. // import java.awt.*; import java.io.*; import java.awt.event.*; import javax.swing.*; public class PageFrame extends JInternalFrame { SiteManager parent; String filename; JTextArea ta; public PageFrame(String name, SiteManager sm) { super("Page: " + name, true, true, true, true); parent = sm; setBounds(50,50,300,150); // Use the JFrame's content pane to store our desktop. Container contentPane = getContentPane( ); // Create a text area to display the contents of our file and put it in a // scrollable pane so we can get to all of it. ta = new JTextArea( ); JScrollPane jsp = new JScrollPane(ta); contentPane.add(jsp, BorderLayout.CENTER); // Add a "File->Save" option to the menu bar for this frame. JMenuBar jmb = new JMenuBar( ); JMenu fileMenu = new JMenu("File"); JMenuItem saveItem = new JMenuItem("Save"); saveItem.addActionListener(new ActionListener( ) { public void actionPerformed(ActionEvent ae) { saveContent( ); } }); fileMenu.add(saveItem); jmb.add(fileMenu); setJMenuBar(jmb); // Now get the content, based on the filename that was passed in. filename = name; loadContent( ); } }

Here, we need to add some load and save routines to the PageFrame class for the text areas. You'll learn more about the read( ) and write( ) methods in Chapter 19, but for now, we'll just use them since they provide such a convenient way to read and write text files:

public void loadContent( ) { try { FileReader fr = new FileReader(filename); ta.read(fr, null); fr.close( ); } catch (Exception e) { System.err.println("Could not load page: "+filename); } } public void saveContent( ) { try { FileWriter fw = new FileWriter(filename); ta.write(fw); fw.close( ); } catch(Exception e) { System.err.println("Could not save page: "+filename); } }

To make the cut and paste operations simpler, we'll put in some public access methods to manipulate the text. All three of these routines are built to function regardless of the clipboard implementation you use. We'll use the system clipboard (via some convenience methods found in JTextComponent) for this example, but you could just as easily use your own clipboard, or eventually, Drag and Drop text.

public void cutText( ) { ta.cut( ); } public void copyText( ) { ta.copy( ); } public void pasteText( ) { ta.paste( ); }

Now you can start the program and bring up the individual HTML files by selecting them from the list. Each file has its own internal frame that you can move around, resize, iconify, maximize, and close. You can cut, copy, and paste text between files. You can save edits using menus attached to each pop-up frame. You can even detach the toolbar and let it "float." All this for about 250 lines of code!

Well, now that we've had a bit of fun, it's time to move on to the details. The next chapter plunges into the world of Swing with the JComponent class. Good luck, and have fun!

Категории