-
Notifications
You must be signed in to change notification settings - Fork 0
/
WebBrowser.java
358 lines (312 loc) · 15.7 KB
/
WebBrowser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
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.
// 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
}
}