/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* PickListKeyStrokeNavigator.java
* Creation date: (11/09/2001 2:59:39 PM)
* By: Edward Lam
*/
package org.openquark.gems.client;
/**
* Handler for KeyStroke events in a pick list that allows navigation by typing in the first few letters of the
* list item that you want.
* Creation date: (11/09/2001 2:59:39 PM)
* @author Edward Lam
*/
public abstract class PickListKeyStrokeNavigator implements java.awt.KeyEventDispatcher{
/** StringBuilder to hold the typed-in text */
private final StringBuilder typeText = new StringBuilder();
/** Timer to take care of resetting the typed-in text after a certain amount of time */
private final javax.swing.Timer typeTextResetTimer;
/** The amount of time (in ms) before the typed-in text is reset */
private static final int resetTime = 1100;
/** The component that this listener is navigating. */
private final java.awt.Component navigableComponent;
/**
* Handles resetting the text which was typed in after a certain interval.
* Creation date: (11/09/2001 12:18:58 PM)
* @author Edward Lam
*/
private class TypeTextHandler implements java.awt.event.ActionListener {
/**
* Invoked when an action occurs.
* Creation date: (11/09/2001 12:18:58 PM)
* @param e java.awt.event.ActionEvent the relevant key event to handle.
*/
public void actionPerformed(java.awt.event.ActionEvent e) {
resetSearchInfo();
}
}
/**
* Constructor for a PickListKeyStrokeNavigator.
* Creation date: (11/09/2001 11:45:41 AM)
* @param component java.awt.Component - the component that this listener is navigating.
*/
public PickListKeyStrokeNavigator(java.awt.Component component){
if (component == null) {
throw new IllegalArgumentException("Value of argument 'component' cannot be null");
}
navigableComponent = component;
// initialize the type text reset timer
typeTextResetTimer = new javax.swing.Timer(resetTime, new TypeTextHandler());
typeTextResetTimer.setRepeats(false);
}
/**
* Get the String text displayed in the nth row of the pick list.
* @param index the index of the string to return
* @return the text displayed in the nth row
*/
public abstract String getNthRowString(int index);
/**
* Get the number of rows in the pick list.
* Creation date: (11/09/2001 3:03:52 PM)
* @return int the number of rows in the pick list
*/
public abstract int getNumRows();
/**
* Get the index of the row in the pick list which is currently selected.
* Creation date: (11/09/2001 3:03:52 PM)
* @return int the index of the currently selected row
*/
public abstract int getSelectedRowIndex();
/**
* Invoked when a key has been pressed.
* Creation date: (11/09/2001 12:35:40 PM)
* @param e java.awt.event.KeyEvent the relevant key event to handle.
*/
public void keyPressed(java.awt.event.KeyEvent e) {
// reset the typeTextTimer if it's running
if (typeTextResetTimer.isRunning()){
typeTextResetTimer.restart();
}
// reset the search buffer and timer if it's some sort of control character
char charPressed = e.getKeyChar();
if (e.isActionKey() ||
(charPressed != java.awt.event.KeyEvent.CHAR_UNDEFINED && Character.isISOControl(charPressed))) {
resetSearchInfo();
}
}
/**
* Invoked when a key has been typed. This event occurs when a key press is followed by a key release.
* Right now what we do is zip to the row which begins with the text we type.
* Creation date: (10/09/2001 5:55:01 PM)
* @param e java.awt.event.KeyEvent the relevant key event to handle.
*/
public void keyTyped(java.awt.event.KeyEvent e) {
// get the character that was typed, in lower case
char keyTyped = Character.toLowerCase(e.getKeyChar());
// reset the typetext timer
typeTextResetTimer.restart();
// ignore if it's a control character.
if (Character.isISOControl(keyTyped)) {
return;
}
// if the current search string is one character, and the typed in character is the same,
// just look for the next instance of a row string beginning with that character.
// We can do this by just retaining the one character.
// otherwise add the character to the text that's typed in so far
if (!(typeText.length() == 1 && typeText.charAt(0) == keyTyped)){
typeText.append(keyTyped);
}
// the starting row is the most recently selected row, or the next row if there is only one character.
// this also takes care of the case where nothing is selected, since -1 is returned if the leadPath is null
int startRow = getSelectedRowIndex();
if (typeText.length() < 2) {
startRow++;
}
// Convert the search text to a string.
String searchString = typeText.toString();
// Go through the rows, looking for a match.
int numRows = getNumRows();
int rowFound = -1;
for (int i = 0; i < numRows; i++) {
// calculate the row to search
int searchRow = (i + startRow) % numRows;
// Get the text for that row, in lower case
String rowString = getNthRowString(searchRow).toLowerCase();
// Is it a match?
if (rowString.startsWith(searchString)) {
rowFound = searchRow;
break;
}
}
// If we found a match, select it and make sure it's visible
if (rowFound > -1) {
selectRow(rowFound);
}
}
/**
* Move the selection in the pick list to a given row.
* Creation date: (11/09/2001 3:03:52 PM)
* @param index int the index of the row to set selected
*/
public abstract void selectRow(int index);
/**
* This function is called whenever a key event is fired.
* Creation date: (07/08/2002 4:22:00 PM).
* @param evt java.awt.event.KeyEvent - the key event that was fired.
* @return boolean - true if the key event was handled and false otherwise.
*/
public boolean dispatchKeyEvent(java.awt.event.KeyEvent evt) {
// Make sure the navigable component has focus and is enabled so we don't end up stealing
// key events intended for other components.
if (navigableComponent.isFocusOwner() && navigableComponent.isEnabled()) {
// We only care about key pressed or key typed events.
if (evt.getID() == java.awt.event.KeyEvent.KEY_TYPED) {
keyTyped(evt);
// Return true so that no other listeners get this event.
return true;
} else if (evt.getID() == java.awt.event.KeyEvent.KEY_PRESSED){
keyPressed(evt);
// Return false so that other listeners and key bindings get this event.
return false;
}
}
return false;
}
/**
* Prepare the navigator for a new search.
* Clear the buffer and stop the reset timer.
*/
private void resetSearchInfo() {
typeText.setLength(0);
typeTextResetTimer.stop();
}
}