import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class JumpToWidget extends Applet implements KeyListener,FocusListener
{
private String namesString;
private String valuesString;
private Vector namesVector;
private Vector lowercaseNamesVector;
private Vector valuesVector;
private Vector optionsValuesVector;
private Vector optionsNamesVector;
private java.awt.List optionsList;
private java.awt.TextField tf;
private int isCaseSensitive;
/**
* Initializes the user interface and private variables.
*/
public void init()
{
int numOptions;
// create user interface using the grid bag layout
GridBagLayout layout = new GridBagLayout();
this.setLayout(layout);
GridBagConstraints constraints = new GridBagConstraints();
// prepare grid bag constraints for text field
constraints.weightx = 100;
constraints.weighty = 0;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.NORTHEAST;
// the text field
tf = new TextField("",25);
tf.addKeyListener(this);
layout.setConstraints(tf, constraints);
this.add(tf);
optionsList = new java.awt.List(30, false);
// prepare grid bag constraints for list
constraints.gridy = 1;
constraints.weighty = 100;
constraints.fill = GridBagConstraints.BOTH;
constraints.anchor = GridBagConstraints.CENTER;
layout.setConstraints(optionsList, constraints);
// add the list and calculate the number of rows that
// are actually visible
this.add(optionsList);
this.validate();
Dimension dim = new Dimension();
dim = optionsList.getSize();
Dimension dim4 = new Dimension();
Dimension dim5 = new Dimension();
dim4 = optionsList.getPreferredSize(4);
dim5 = optionsList.getPreferredSize(5);
numOptions = (dim.height/(dim5.height - dim4.height)) - 1;
if(numOptions <= 0)
{
numOptions = 1;
}
// remove and re-add the list so that it's number of rows
// is the same as the number that are visible
this.remove(optionsList);
this.validate();
optionsList = new java.awt.List(numOptions, false);
optionsList.addFocusListener(this);
optionsList.setFont(new Font("SansSerif",java.awt.Font.PLAIN,11));
layout.setConstraints(optionsList, constraints);
this.add(optionsList);
// initialize private variables
namesVector = new Vector();
lowercaseNamesVector = new Vector();
valuesVector = new Vector();
optionsValuesVector = new Vector();
optionsNamesVector = new Vector();
// set the case sensitivity
setCaseSensitivity(getParameter("casesensitivity"));
// read the parameters from the html page
loadNamesAndValues(getParameter("names"), getParameter("values"));
}
/**
* Returns the value of the selected option.
* @return null
if no option is selected, otherwise
* the value of the currently selected option
*/
public String getSelectedValue()
{
if(optionsList.getSelectedIndex() != -1)
{
return (String)optionsValuesVector.elementAt(optionsList.getSelectedIndex());
}
else
{
return null;
}
}
/**
* Returns the name of the selected option.
* @return null
if no option is selected, otherwise
* the visible name of the currently selected option
*/
public String getSelectedName()
{
if(optionsList.getSelectedItem() != null)
{
return (String)optionsList.getSelectedItem();
}
else
{
return null;
}
}
public void setCaseSensitivity(String strCaseSensitivity)
{
strCaseSensitivity = strCaseSensitivity.toLowerCase();
if(strCaseSensitivity.compareTo("yes") == 0
|| strCaseSensitivity.compareTo("y") == 0
|| strCaseSensitivity.compareTo("1") == 0
|| strCaseSensitivity.compareTo("true") == 0 )
{
isCaseSensitive = 1;
}
else
{
isCaseSensitive = 0;
}
}
/**
* Reloads the name and value strings into the internal vectors
* and refreshes the options list. This must be called after
* the names and values fields are changed. If the names and values
* don't agree in number, then an error is printed in the text field.
* @see JumpToWidget#namesString
* @see JumpToWidget#valuesString
*/
public void loadNamesAndValues(String strNames, String strValues)
{
this.namesString = new String(strNames);
this.valuesString = new String(strValues);
int i;
int countNames;
int countValues;
StringTokenizer st;
String tempString;
// clear the data vectors and options list
namesVector.removeAllElements();
valuesVector.removeAllElements();
optionsNamesVector.removeAllElements();
optionsValuesVector.removeAllElements();
lowercaseNamesVector.removeAllElements();
optionsList.removeAll();
// get the names into an array
st = new StringTokenizer(this.namesString, "|");
countNames = 0;
while (st.hasMoreTokens()) {
tempString = new String(st.nextToken());
namesVector.addElement(tempString);
optionsNamesVector.addElement(tempString);
if(isCaseSensitive == 0)
{
lowercaseNamesVector.addElement(tempString.toLowerCase());
}
else
{
lowercaseNamesVector.addElement(tempString);
}
countNames++;
}
// get the values into an array
st = new StringTokenizer(valuesString, "|");
countValues = 0;
while (st.hasMoreTokens()) {
tempString = new String(st.nextToken());
valuesVector.addElement(tempString);
optionsValuesVector.addElement(tempString);
countValues++;
}
// if the number of names doesn't match the number of values
// add an error to the option box.
if(countNames != countValues)
{
// if there are names, but no values, then
if(countValues == 0 && countNames != 0)
{
// load the names as values
for(i = 0; i < namesVector.size(); i++)
{
valuesVector.addElement(namesVector.elementAt(i));
optionsValuesVector.addElement(namesVector.elementAt(i));
countValues++;
}
}
else
{
// otherwise, print error
tf.setText("ERROR: number of values doesn't match number of names");
}
}
else
{
// reset the text field
tf.setText("");
}
optionsList.setVisible(false);
// only add the visible options because adding options is really slow
for(i = 0; i < optionsList.getRows() && i < optionsNamesVector.size(); i++)
{
optionsList.add( (String)optionsNamesVector.elementAt(i) );
}
// select the first item
if(optionsList.getItemCount() > 0)
{
optionsList.select(0);
}
optionsList.setVisible(true);
}
/**
* When the list gains focus, then we must populate
* the list with the remaining options which match
* the substring.
*/
public void focusGained(java.awt.event.FocusEvent fe)
{
int i;
int selectedIndex;
// only reload if the number of options displayed is
// less than the actual number of options
if(optionsList.getItemCount() < optionsNamesVector.size())
{
// rememember the index of the selected item
selectedIndex = optionsList.getSelectedIndex();
// remove the options
optionsList.removeAll();
// show loading message
optionsList.add("Loading...");
// set temporarily invisible
optionsList.setVisible(false);
// set the display to the new names
for(i = 0; i < optionsNamesVector.size(); i++)
{
optionsList.add( (String)optionsNamesVector.elementAt(i) );
}
// show the options list
optionsList.setVisible(true);
// remove the loading message
optionsList.remove(0);
// reselect the selected item
optionsList.select(selectedIndex);
// make sure focus stays on the list box
optionsList.requestFocus();
}
}
/**
* Does nothing
*/
public void focusLost(java.awt.event.FocusEvent fe)
{
// do nothing
}
/**
* When key is released on the text field, then
* the internal options vectors are reloaded and the options
* list is updated to show only the options that contain
* the substrings in the text field. To improve performance,
* only first few visible options are added back to the options list.
*/
public void keyReleased(KeyEvent e)
{
int i, j, bAddOption;
String compareString, tempString;
Vector compareVector;
StringTokenizer st;
if(e.getKeyCode() == e.VK_TAB || e.getKeyCode() == e.VK_DOWN)
{
// if tab is pressed, transfer control to the list box
optionsList.requestFocus();
}
else
{
// remove all the options
optionsList.removeAll();
optionsNamesVector.removeAllElements();
optionsValuesVector.removeAllElements();
// disable redrawing temporarily
optionsList.setVisible(false);
// set the compare string
if(isCaseSensitive == 0)
{
compareString = new String(tf.getText().toLowerCase());
}
else
{
compareString = new String(tf.getText());
}
// create the compare vector
compareVector = new Vector();
st = new StringTokenizer(compareString, " ");
i = 0;
while (st.hasMoreTokens()) {
tempString = new String(st.nextToken());
compareVector.addElement(tempString);
i++;
}
// if not tokens found, then the add only the
// full compare string
if(i == 0)
{
compareVector.addElement(compareString);
}
// add back those that contain the new substring
for(i = 0; i < namesVector.size(); i++)
{
bAddOption = 1;
for(j = 0; j < compareVector.size(); j++)
{
if(((String)lowercaseNamesVector.elementAt(i)).indexOf((String)compareVector.elementAt(j)) == -1)
{
bAddOption = 0;
}
}
// only add if all substrings were in the name
if(bAddOption == 1)
{
// add the option to the options list
//optionsList.add( (String)namesVector.elementAt(i) );
optionsNamesVector.addElement( (String)namesVector.elementAt(i) );
// add the corresponding value
optionsValuesVector.addElement( (String)valuesVector.elementAt(i) );
}
}
// only add the visible options because adding options is really slow
for(i = 0; i < optionsList.getRows() && i < optionsNamesVector.size(); i++)
{
optionsList.add( (String)optionsNamesVector.elementAt(i) );
}
// select the first item
if(optionsList.getItemCount() > 0)
{
optionsList.select(0);
}
// reenable redrawing
optionsList.setVisible(true);
} // end of else statment
}
/**
* Do nothing.
*/
public void keyTyped(KeyEvent e) { }
/**
* Do nothing.
*/
public void keyPressed(KeyEvent e) {}
/*
// test timing function
public void testOptionsSpeed()
{
long startTime, endTime;
Long testLong = new Long(0);
int i = 0, countOptions;
Vector testVector = new Vector();
countOptions = optionsList.getItemCount();
startTime = System.currentTimeMillis();
optionsList.removeAll();
endTime = System.currentTimeMillis();
System.out.println("removed "+countOptions+" options in "+((endTime-startTime))+" milliseconds.");
countOptions = 10000;
startTime = System.currentTimeMillis();
optionsList.setVisible(false);
for(i = 0; i < countOptions; i++)
{
optionsList.add( "test string: "+i);
}
optionsList.setVisible(true);
endTime = System.currentTimeMillis();
System.out.println("added "+countOptions+" options in "+((endTime-startTime))+" milliseconds.");
}
*/
}