11.11 A Simple Web Browser

The two previous examples have shown us the powerful JTable and JTree components. A third powerful Swing component is javax.swing.text.JTextComponent and its various subclasses, which include JTextField, JTextArea, and JEditorPane. JEditorPane is a particularly interesting component that makes it easy to display (or edit) HTML text.

As an aside, it is worth noting here that you do not have to create a JEditorPane to display static HTML text. In Java 1.2.2 and later, the JLabel, JButton, and other similar components can all display multiline, multifont formatted HTML labels. The trick is to begin the label with the string "<html>". This tells the component to treat the rest of the label string as formatted HTML text and display it (using an internal JTextComponent) in that way. You can experiment with the feature using the ShowBean program; use it to create a JButton component and set the text property to a value that begins with "<html>".

Example 11-21 is a listing of WebBrowser.java, a JFrame subclass that implements the simple web browser shown in Figure 11-18. The WebBrowser class uses the power of the java.net.URL class to download HTML documents from the Web and the JEditorPane component to display the contents of those documents. Although defined as a reusable component, the WebBrowser class includes a main( ) method so that it can be run as a standalone application.

Figure 11-18. The WebBrowser component
figs/jex3_1118.gif

Example 11-21 is intended as a demonstration of the power of the JEditorPane component. The truth is, however, that using JEditorPane is quite trivial: simply pass a URL to the setPage( ) method or a string of HTML text to the setText( ) method. So, when you study the code for this example, don't focus too much on the JEditorPane. You should instead look at WebBrowser as an example of pulling together many Swing components and programming techniques to create a fairly substantial GUI. Points of interest include the enabling and disabling of Action objects and the use of the JFileChooser component. The example also uses a JLabel as an application message line, with a javax.swing.Timer that performs a simple text-based animation in that message line.

Another thing to notice about this example is that it demonstrates several other example classes that are developed later in this chapter. GUIResourceBundle, which is developed in Example 11-22, is the primary one. This class allows common GUI resources (such as colors and fonts) to be read from textual descriptions stored in a properties file, which therefore allows the resources to be customized and localized. When GUIResourceBundle is extended with ResourceParser implementations, it can parse more complex "resources," such as entire JMenuBar and JToolBar components. WebBrowser defers the creation of its menus and toolbars to GUIResourceBundle.

The WebBrowser class uses the default Metal look-and-feel, but it allows the user to select a "theme" (a color and font combination) for use within that look-and-feel. This functionality is provided by the ThemeManager class, which is developed in Example 11-28.

Example 11-21. WebBrowser.java
package je3.gui; import java.awt.*;                 // LayoutManager stuff import javax.swing.*;              // Swing components import java.awt.event.*;           // AWT event handlers import javax.swing.event.*;        // Swing event handlers import java.beans.*;               // JavaBeans event handlers import java.io.*;                  // Input/output import java.net.*;                 // Networking with URLs import java.util.*;                // Hashtables and other utilities // Import this class by name.  JFileChooser uses it, and its name conflicts // with java.io.FileFilter import javax.swing.filechooser.FileFilter;   /**  * This class implements a simple web browser using the HTML  * display capabilities of the JEditorPane component.  **/ public class WebBrowser extends JFrame     implements HyperlinkListener, PropertyChangeListener {     /**      * A simple main( ) method that allows the WebBrowser class to be used      * as a standalone application.      **/     public static void main(String[  ] args) throws IOException {         // End the program when there are no more open browser windows         WebBrowser.setExitWhenLastWindowClosed(true);         WebBrowser browser = new WebBrowser( );  // Create a browser window         browser.setSize(800, 600);              // Set its size         browser.setVisible(true);               // Make it visible.         // Tell the browser what to display.  This method is defined below.         browser.displayPage((args.length > 0) ? args[0] : browser.getHome( ));     }          // This class uses GUIResourceBundle to create its menubar and toolbar     // This static initializer performs one-time registration of the     // required ResourceParser classes.     static {         GUIResourceBundle.registerResourceParser(new MenuBarParser( ));         GUIResourceBundle.registerResourceParser(new MenuParser( ));         GUIResourceBundle.registerResourceParser(new ActionParser( ));         GUIResourceBundle.registerResourceParser(new CommandParser( ));         GUIResourceBundle.registerResourceParser(new ToolBarParser( ));     }     // These are the Swing components that the browser uses     JEditorPane textPane;      // Where the HTML is displayed     JLabel messageLine;        // Displays one-line messages     JTextField urlField;       // Displays and edits the current URL     JFileChooser fileChooser;  // Allows the user to select a local file     // These are Actions that are used in the menubar and toolbar.     // We obtain explicit references to them from the GUIResourceBundle     // so we can enable and disable them.     Action backAction, forwardAction;     // These fields are used to maintain the browsing history of the window     java.util.List history = new ArrayList( );  // The history list     int currentHistoryPage = -1;               // Current location in it     public static final int MAX_HISTORY = 50;  // Trim list when over this size     // These static fields control the behavior of the close( ) action     static int numBrowserWindows = 0;     static boolean exitWhenLastWindowClosed = false;     // This is where the "home( )" method takes us.  See also setHome( )     String home = "http://www.davidflanagan.com";  // A default value     /** Create and initialize a new WebBrowser window */     public WebBrowser( ) {         super("WebBrowser");              // Chain to JFrame constructor         textPane = new JEditorPane( );     // Create HTML window         textPane.setEditable(false);      // Don't allow the user to edit it         // Register action listeners.  The first is to handle hyperlinks.         // The second is to receive property change notifications, which tell         // us when a document is done loading.  This class implements these         // EventListener interfaces, and the methods are defined below         textPane.addHyperlinkListener(this);          textPane.addPropertyChangeListener(this);         // Put the text pane in a JScrollPane in the center of the window         this.getContentPane( ).add(new JScrollPane(textPane),                                   BorderLayout.CENTER);         // Now create a message line and place it at the bottom of the window         messageLine = new JLabel(" ");         this.getContentPane( ).add(messageLine, BorderLayout.SOUTH);         // Read the file WebBrowserResources.properties (and any localized         // variants appropriate for the current Locale) to create a         // GUIResourceBundle from which we'll get our menubar and toolbar.         GUIResourceBundle resources =             new GUIResourceBundle(this,"je3.gui." +                                   "WebBrowserResources");         // Read a menubar from the resource bundle and display it         JMenuBar menubar = (JMenuBar) resources.getResource("menubar",                                                             JMenuBar.class);         this.setJMenuBar(menubar);         // Read a toolbar from the resource bundle.  Don't display it yet.         JToolBar toolbar =              (JToolBar) resources.getResource("toolbar", JToolBar.class);         // Create a text field that the user can enter a URL in.         // Set up an action listener to respond to the ENTER key in that field         urlField = new JTextField( );         urlField.addActionListener(new ActionListener( ) {                 public void actionPerformed(ActionEvent e) {                     displayPage(urlField.getText( ));                 }             });         // Add the URL field and a label for it to the end of the toolbar         toolbar.add(new JLabel("         URL:"));         toolbar.add(urlField);         // And add the toolbar to the top of the window         this.getContentPane( ).add(toolbar, BorderLayout.NORTH);         // Read cached copies of two Action objects from the resource bundle         // These actions are used by the menubar and toolbar, and enabling and         // disabling them enables and disables the menu and toolbar items.         backAction = (Action)resources.getResource("action.back",Action.class);         forwardAction =             (Action)resources.getResource("action.forward", Action.class);         // Start off with both actions disabled         backAction.setEnabled(false);         forwardAction.setEnabled(false);                  // Create a ThemeManager for this frame,          // and add a Theme menu to the menubar         ThemeManager themes = new ThemeManager(this, resources);         menubar.add(themes.getThemeMenu( ));         // Keep track of how many web browser windows are open         WebBrowser.numBrowserWindows++;     }     /** Set the static property that controls the behavior of close( ) */     public static void setExitWhenLastWindowClosed(boolean b) {         exitWhenLastWindowClosed = b;     }     /** These are accessor methods for the home property. */     public void setHome(String home) { this.home = home; }     public String getHome( ) { return home; }     /**      * This internal method attempts to load and display the specified URL.      * It is called from various places throughout the class.      **/     boolean visit(URL url) {         try {             String href = url.toString( );             // Start animating.  Animation is stopped in propertyChanged( )             startAnimation("Loading " + href + "...");               textPane.setPage(url);   // Load and display the URL              this.setTitle(href);     // Display URL in window titlebar             urlField.setText(href);  // Display URL in text input field             return true;             // Return success         }         catch (IOException ex) {     // If page loading fails             stopAnimation( );             messageLine.setText("Can't load page: " + ex.getMessage( ));             return false;            // Return failure         }     }     /**      * Ask the browser to display the specified URL, and put it in the      * history list.      **/     public void displayPage(URL url) {         if (visit(url)) {    // go to the specified url, and if we succeed:             history.add(url);       // Add the url to the history list             int numentries = history.size( );             if (numentries > MAX_HISTORY+10) {  // Trim history when too large                 history = history.subList(numentries-MAX_HISTORY, numentries);                 numentries = MAX_HISTORY;             }             currentHistoryPage = numentries-1;  // Set current history page             // If we can go back, then enable the Back action             if (currentHistoryPage > 0) backAction.setEnabled(true);         }     }     /** Like displayPage(URL), but takes a string instead */     public void displayPage(String href) {         try {             displayPage(new URL(href));         }         catch (MalformedURLException ex) {             messageLine.setText("Bad URL: " + href);         }     }     /** Allow the user to choose a local file, and display it */     public void openPage( ) {         // Lazy creation: don't create the JFileChooser until it is needed         if (fileChooser == null) {             fileChooser = new JFileChooser( );             // This javax.swing.filechooser.FileFilter displays only HTML files             FileFilter filter = new FileFilter( ) {                     public boolean accept(File f) {                         String fn = f.getName( );                         if (fn.endsWith(".html") || fn.endsWith(".htm"))                             return true;                         else return false;                     }                     public String getDescription( ) { return "HTML Files"; }                 };             fileChooser.setFileFilter(filter);             fileChooser.addChoosableFileFilter(filter);         }         // Ask the user to choose a file.         int result = fileChooser.showOpenDialog(this);         if (result == JFileChooser.APPROVE_OPTION) {             // If they didn't click "Cancel", then try to display the file.             File selectedFile = fileChooser.getSelectedFile( );             String url = "file://" + selectedFile.getAbsolutePath( );             displayPage(url);         }     }     /** Go back to the previously displayed page. */     public void back( ) {         if (currentHistoryPage > 0)  // go back, if we can             visit((URL)history.get(--currentHistoryPage));         // Enable or disable actions as appropriate         backAction.setEnabled((currentHistoryPage > 0));         forwardAction.setEnabled((currentHistoryPage < history.size( )-1));     }     /** Go forward to the next page in the history list */     public void forward( ) {         if (currentHistoryPage < history.size( )-1)  // go forward, if we can             visit((URL)history.get(++currentHistoryPage));         // Enable or disable actions as appropriate         backAction.setEnabled((currentHistoryPage > 0));         forwardAction.setEnabled((currentHistoryPage < history.size( )-1));     }     /** Reload the current page in the history list */     public void reload( ) {         if (currentHistoryPage != -1) {             // We can't reload the current document, so display a blank page             textPane.setDocument(new javax.swing.text.html.HTMLDocument( ));             // Now re-visit the current URL             visit((URL)history.get(currentHistoryPage));         }     }     /** Display the page specified by the "home" property */     public void home( ) { displayPage(getHome( )); }     /** Open a new browser window */     public void newBrowser( ) {         WebBrowser b = new WebBrowser( );         b.setSize(this.getWidth( ), this.getHeight( ));         b.setVisible(true);     }     /**      * Close this browser window.  If this was the only open window,      * and exitWhenLastBrowserClosed is true, then exit the VM      **/     public void close( ) {         this.setVisible(false);             // Hide the window         this.dispose( );                     // Destroy the window         synchronized(WebBrowser.class) {    // Synchronize for thread-safety             WebBrowser.numBrowserWindows--; // There is one window fewer now             if ((numBrowserWindows==0) && exitWhenLastWindowClosed)                 System.exit(0);             // Exit if it was the last one         }     }             /**      * Exit the VM.  If confirm is true, ask the user if they are sure.      * Note that showConfirmDialog( ) displays a dialog, waits for the user,      * and returns the user's response (i.e. the button the user selected).      **/     public void exit(boolean confirm) {         if (!confirm ||             (JOptionPane.showConfirmDialog(this,  // dialog parent                  /* message to display */  "Are you sure you want to quit?",                  /* dialog title */        "Really Quit?",                  /* dialog buttons */      JOptionPane.YES_NO_OPTION) ==               JOptionPane.YES_OPTION))  // If Yes button was clicked             System.exit(0);     }     /**       * This method implements HyperlinkListener.  It is invoked when the user      * clicks on a hyperlink or moves the mouse onto or off of a link      **/     public void hyperlinkUpdate(HyperlinkEvent e) {         HyperlinkEvent.EventType type = e.getEventType( );  // what happened?         if (type == HyperlinkEvent.EventType.ACTIVATED) {     // Click!             displayPage(e.getURL( ));   // Follow the link; display new page         }         else if (type == HyperlinkEvent.EventType.ENTERED) {  // Mouse over!             // When mouse goes over a link, display it in the message line             messageLine.setText(e.getURL( ).toString( ));           }         else if (type == HyperlinkEvent.EventType.EXITED) {   // Mouse out!             messageLine.setText(" ");  // Clear the message line         }     }     /**      * This method implements java.beans.PropertyChangeListener.  It is       * invoked whenever a bound property changes in the JEditorPane object.      * The property we are interested in is the "page" property, because it      * tells us when a page has finished loading.      **/     public void propertyChange(PropertyChangeEvent e) {         if (e.getPropertyName( ).equals("page")) // If the page property changed             stopAnimation( );              // Then stop the loading... animation     }     /**      * The fields and methods below implement a simple animation in the      * web browser message line; they are used to provide user feedback      * while web pages are loading.      **/     String animationMessage;  // The "loading..." message to display     int animationFrame = 0;   // What "frame" of the animation are we on     String[  ] animationFrames = new String[  ] {  // The content of each "frame"         "-", "\\", "|", "/", "-", "\\", "|", "/",          ",", ".", "o", "0", "O", "#", "*", "+"     };     /** This object calls the animate( ) method 8 times a second */     javax.swing.Timer animator =         new javax.swing.Timer(125, new ActionListener( ) {                 public void actionPerformed(ActionEvent e) { animate( ); }             });     /** Display the next frame. Called by the animator timer */     void animate( ) {         String frame = animationFrames[animationFrame++];    // Get next frame         messageLine.setText(animationMessage + " " + frame); // Update msgline         animationFrame = animationFrame % animationFrames.length;     }     /** Start the animation.  Called by the visit( ) method. */     void startAnimation(String msg) {         animationMessage = msg;     // Save the message to display         animationFrame = 0;         // Start with frame 0 of the animation         animator.start( );           // Tell the timer to start firing.     }     /** Stop the animation.  Called by propertyChanged( ) method. */     void stopAnimation( ) {                animator.stop( );            // Tell the timer to stop firing events         messageLine.setText(" ");   // Clear the message line     } }


Java Examples in a Nutshell
Java Examples in a Nutshell, 3rd Edition
ISBN: 0596006209
EAN: 2147483647
Year: 2003
Pages: 285

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net