/*
* Copyright (c) 2004 David Flanagan. All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book,
* please visit http://www.davidflanagan.com/javaexamples3.
*/
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 stand-alone 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 move 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
}
}
|