/*
* File: Chap2Solutions.java
* Tim Balls : Feb 2001
*
* Solutions to questions 1-3 of Chapter 2 (more or less!)
*
* The JComboBox generates ActionEvents when return is pressed and the
* Component has focus. It is necessary to add the item to the list that
* forms the "model" of the JComboBox - checking to see that it not already
* in the list.
* The display part can be updated (without affecting the storage list) by
* making use of another internal "model" - the JComboBoxEditor that is
* responsible for the entry line. Its item can be set and retrieved.
*
* The JComboBox also sends ItemEvents (to an ItemListener) whenever items
* are selected or deselected. Trapping an appropriate event allows list
* selection to change the displayed page.
*/
import javax.swing.*;
import javax.swing.event.*;
import java.net.*;
import java.io.IOException;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
class Ch2Sol123 extends JFrame
{ public static void main( String[] args )
{ if( args.length == 0 )
{ new Ch2Sol123( "NoURL" );
}
else
{ new Ch2Sol123( args[0] );
}
}
private JEditorPane html = new JEditorPane();
/**/ private JComboBox location;
private JLabel statusLine = new JLabel( "", JLabel.LEFT );
private JButton back = new JButton( "<<" );
private JButton forward = new JButton( ">>" );
private HistoryList historyList = new HistoryList();
public Ch2Sol123( String site )
{ setSize( 550, 600 );
setTitle( "Chapter Two Solutions" );
addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent e )
{ System.exit( 0 );
}
});
// Build more interesting interface
/**/ location = new JComboBox();
/**/ location.setEditable( true );
/**/ location.getEditor().setItem( site );
JPanel topLine = new JPanel();
topLine.setLayout( new FlowLayout( FlowLayout.LEFT ) );
topLine.add( back );
topLine.add( forward );
topLine.add( new JLabel( "URL:" ) );
topLine.add( location );
// for the JFrame:
this.getContentPane().setLayout( new BorderLayout() );
this.getContentPane().add( topLine, BorderLayout.NORTH );
this.getContentPane().add( statusLine, BorderLayout.SOUTH );
if( !site.equals( "NoURL" ) )
{ if( loadURL( site ) )
{ try
{ historyList.add( new URL( site ) );
}
catch( Exception ex ) {} // it must work as loadURL returned true.
location.addItem( site );
}
}
JScrollPane scroller = new JScrollPane();
JViewport viewport = scroller.getViewport();
viewport.add( html );
this.getContentPane().add( scroller, BorderLayout.CENTER );
html.setEditable( false ); // to allow links to be activated
setVisible( true );
/**/ // Event handlers - new code but much has simply been moved here
// First pick up <return> pressed in editable section of the
// JComboBox. Need to add the new item to the JComboBox list.
location.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{ String newSite = location.getEditor().getItem().toString();
if( loadURL( newSite ) )
{ try
{ historyList.add( new URL( newSite ) );
}
catch( Exception ex ) {} // it must work as loadURL returned true.
int index = -1;
for( int item = 0; item < location.getItemCount(); item++ )
{ if( location.getItemAt( item ).equals( newSite ) )
{ index = item;
}
}
if( index == -1 ) // new item, not in list, so put at front
{ location.insertItemAt( newSite, 0 );
}
}
}
});
// This one is needed to capture an item picked from the list
location.addItemListener( new ItemListener() {
public void itemStateChanged( ItemEvent e )
{ if( e.getStateChange() == ItemEvent.SELECTED )
{ boolean b = loadURL( e.getItem().toString() ); // must work!
}
}
});
// history list buttons:
back.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{ historyList.back();
loadURL( historyList.getCurrent().toString() );
}
});
forward.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{ historyList.forward();
loadURL( historyList.getCurrent().toString() );
}
});
/**/ // hyperlinkListener moved to here
html.addHyperlinkListener( new HyperlinkListener() {
public void hyperlinkUpdate( HyperlinkEvent e )
{ if( e.getEventType() == HyperlinkEvent.EventType.ACTIVATED )
{ if( loadURL( e.getURL().toString() ) )
{ historyList.add( e.getURL() );
}
}
if( e.getEventType() == HyperlinkEvent.EventType.ENTERED )
{ statusLine.setText( "" + e.getURL() );
}
if( e.getEventType() == HyperlinkEvent.EventType.EXITED )
{ statusLine.setText( "" );
}
}
});
/**/ // end of event handlers
} // end of Constructor (easily missed!)
// Attempt to load the html JEditorPane from the URL at "site"
// modified to return an indication of success
// but NOT to update the HistoryList
/**/ private boolean loadURL( String site )
{ URL url = null;
try
{ url = new URL( site );
}
catch( MalformedURLException e )
{ error( "Invalid URL specified" );
return false;
}
try
{ html.setPage( url ); // load page
}
catch( IOException e )
{ error( "Can't load url: " + url );
return false;
}
/**/ // set it as the currently displayed item in the JComboBox
/**/ // but DO NOT add it to the list at this point
/**/ location.getEditor().setItem( site );
return true;
}
/**/ // Another way to handle errors - create a special method
// It could write to the status line - in this case it uses
// the predfined dialogs in the JOptionPane class
private void error( String message )
{ JOptionPane.showMessageDialog( this,
message,
"Error",
JOptionPane.ERROR_MESSAGE);
}
/**/
/**/ // No changes to the HistoryList class
/* Encapsulate the behaviour of a history list for URLs
Enable/Disable the arrow controls so that
illegal operations are impossible
This class is (too) strongly coupled to its outer class
Maintain position in list, ensure that adding a new item
deletes any that would have followed (becomes new tail)
*/
private class HistoryList
{ private ArrayList history = new ArrayList();
private int current = 0;
public HistoryList()
{ back.setEnabled( false );
forward.setEnabled( false );
}
public void add( URL url )
{ if( history.size() > (current+1) )
{ history.subList( current+1, history.size() ).clear();
}
history.add( url );
current = history.size()-1;
if( current != 0 )
{ back.setEnabled( true );
}
forward.setEnabled( false );
}
/* Decrement the current pointer
PreCondition: must not be at start of list
*/
public void back()
{ current--;
if( current == 0 )
{ back.setEnabled( false );
}
if( current < (history.size()-1) )
{ forward.setEnabled( true );
}
}
/* Increment the current pointer
PreCondition: must not be at end of list
*/
public void forward()
{ current++;
if( current > 0 )
{ back.setEnabled( true );
}
if( current == (history.size()-1) )
{ forward.setEnabled( false );
}
}
/* Return the URL access by the current pointer
*/
public URL getCurrent()
{ return (URL)(history.get( current ));
}
}
}
|