![]()
Step 4 - Window Management, Visited Links, and Printing
In this step, proper window management is implemented, functionality for changing the color of visited links for better usability is added, and printing support is set up.
Window Management
In this section, the disposeTopLevelContainer( ) method in MyCallBack is implemented to dispose of window objects and control memory usage. It is equally important that the window tell StormBase that it is closing.
The BrowserFrame is added as a WindowListener in its constructor:
addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent event) { BrowserFrame.this.stormBase.closeViewport(viewportId); } });With these changes, StormBase is notified of the destruction and will react by asking its ViewportCallback to call the disposeTopLevelContainer( ) method.
You can keep track of the top level containers that are created by MyCallback using a simple FrameManager:
public Container createTopLevelContainer(Viewport viewport) { BrowserFrame browserFrame = new BrowserFrame(stormBase, viewport); FrameManager.getInstance().put(viewport, browserFrame); browserFrame.setVisible(true); return browserFrame.getPanel(); } public void disposeTopLevelContainer(Viewport viewport) { BrowserFrame browserFrame = (BrowserFrame)FrameManager.getInstance().get(viewport); if (browserFrame != null) { FrameManager.getInstance().remove(viewport); browserFrame.setVisible(false); browserFrame.dispose(); if (FrameManager.getInstance().isEmpty()) { System.exit(0); } } }The FrameManager is kept simple initially. It gets more functionality in a later step:
class FrameManager extends Hashtable { private static FrameManager frameManager; private FrameManager() { // do nothing. } static synchronized FrameManager getInstance() { if (frameManager == null) { frameManager = new FrameManager(); } return frameManager; } }Another import statement is also required:
import java.util.Hashtable;With this new code, the closing of windows is handled correctly.
Keeping Track of Visited Links
To keep track of visited links, the application requires a basic understanding of the DOM. Perhaps the simplest way to keep a record of links is to store the URLs in a Vector object. But you also have to determine when a link is actually visited.
The PropertyChangeEvent contentLoading,request can serve as a trigger. This event has an ice.storm.ContentLoader object as its oldValue, from which you can extract the URL string. This can be put in the Vector and checked whenever a new page is loaded. This is a rather elaborate process. You have to wait for the DOM tree to be built, traverse the possible link elements (anchors), check them against the Vector, and make sure they all know whether they have been visited.
Anchor elements in HTML are mapped to the ice.pilots.html4.DAnchorElement class. This class has a boolean variable visited, which is read during the layout phase. You can find all the anchor elements, read their href attributes, look for them in the Vector, and set the variable.
The following code changes are used in the tutorial code to implement the visited links functionality:
The following import statements provide access to the DOM Java interfaces and the java.util.Vector class:
import ice.pilots.html4.*; import java.util.Vector; import org.w3c.dom.html.HTMLCollection;The following is the new VisitedLinks class:
class VisitedLinks implements PropertyChangeListener { private Vector visitedLinks = new Vector(); public void propertyChange(PropertyChangeEvent event) { Viewport viewport = (Viewport)event.getSource(); String propertyName = event.getPropertyName(); if (propertyName.equals(PropertyConstants.CONTENT_LOADING)) { String newValue = (String)event.getNewValue(); if (newValue.equals(PropertyConstants.REQUEST)) { String location = ((ContentLoader)event.getOldValue()).getLocation(); if (!visitedLinks.contains(location)) { visitedLinks.addElement(location); } } else if (newValue.equals(PropertyConstants.END)) { if (viewport.getPilot() instanceof ThePilot) { HTMLCollection htmlCollection = ((ThePilot)viewport.getPilot()). getDDocument().getLinks(); for (int i = 0; i < htmlCollection.getLength(); i++) { if (htmlCollection.item(i) instanceof DAnchorElement) { DAnchorElement element = (DAnchorElement)htmlCollection.item(i); if (visitedLinks.contains(element.getHref())) { element.setVisited(true); } } } } } } } }The code checks that the pilot is an ice.pilots.html4.ThePilot, as it is for all HTML documents. If it is, you can get its DDocument, a class that implements the org.w3c.dom.html.HTMLDocument interface. It has a method to get all its link elements as an org.w3c.dom.html.HTMLCollection. The elements of this list are then checked to see if they have already been visited.
The remaining task for this step is to register the VisitedLinks instance as a listener with the StormBase in the main( ) method:
public static void main(String[] args) { StormBase stormBase = new StormBase(); String location = "http://www.yahoo.com"; if (args.length > 0) { location = args[0]; } stormBase.setViewportCallback(new MyCallback()); stormBase.addPropertyChangeListener(new VisitedLinks(), null); stormBase.renderContent(location, null, "MyBrowser"); }The second argument, the addPropertyChangeListener method, is null rather than a specific Viewport ID. This ensures that it listens for events from all Viewports. This technique can be used in other contexts. The StormBase registers the listener in a global list, receiving all events.
With the window management and visited links implemented, you have now created a basic but functional browser.
Adding Printing Support
In this step, printing support is added to the browser. The provided reference implementation code is used for creating page setup and print preview dialogs. The code also provides methods for document printing.
Add three buttons to the BrowserFrame with the following code:
private Button pageSetupButton, printPreviewButton, printButton;The following code is the final part of the constructor:
gridBagLayout.setConstraints(pageSetupButton, gridBagConstraints); navigationPanel.add(pageSetupButton); gridBagLayout.setConstraints(printPreviewButton, gridBagConstraints); navigationPanel.add(printPreviewButton); gridBagLayout.setConstraints(printButton, gridBagConstraints); navigationPanel.add(printButton);Print settings are stored in the StormPageFormat object, which can be shared among all instances of the WebBrowser objects.
This line ensures that the class is imported:
import ice.storm.print.StormPageFormat;Add a static instance of the StormPageFormat with the following line:
private static StormPageFormat stormPageFormat = new StormPageFormat();Step 3 of the tutorial introduced the ActionListener interface, which is called whenever one of the new widgets is activated.
These lines ensure that the needed classes are imported:
import ice.ri.common.print.awt.PageSetupDialog; import ice.ri.common.print.awt.PrintPreviewDialog; import ice.ri.common.print.jdk11.PrintHelper11;The following code adds the printing functionality needed by the three print buttons:
public void actionPerformed(ActionEvent event) { Object source = event.getSource(); if (source == backButton) { stormBase.getHistoryManager().goBack(viewportId); ... } else if (source == pageSetupButton){ Viewport view = stormBase.findViewportByName(viewportId); PageSetupDialog pageSetup = new PageSetupDialog(this, view, stormPageFormat); stormPageFormat = pageSetup.showDialog(); } else if (source == printPreviewButton){ Viewport view = stormBase.findViewportByName(viewportId); PrintPreviewDialog pageSetup = new PrintPreviewDialog(this, view, stormPageFormat); stormPageFormat = pageSetup.showDialog(); } else if (source == printButton){ Viewport view = stormBase.findViewportByName(viewportId); PrintHelper11 printHelper11 = new PrintHelper11(view, stormPageFormat); printHelper11.print(this); } }The StormPage object persists page setup settings across dialogs and during the printing of the document. The code for the actionPerformed method does the following:
- Page Setup button
If this button is clicked, a page setup dialog is displayed, which allows the user to interact with the GUI to change StormPageFormat properties. When the dialog is closed, the modified StormPageFormat object is returned.
- Print Preview button
If this button is clicked, a print preview dialog is displayed, which shows how the content will be laid out when printed. This dialog also allows access to the page setup dialog and can directly print the viewport content.
- Print button
If this button is clicked, the viewport's content is printed using the media settings defined by StormPageFormat.
|
Copyright 2005. ICEsoft Technologies, Inc. http://www.icesoft.com |