/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import net.java.sip.communicator.impl.gui.main.chat.conference.*;
import net.java.sip.communicator.service.contactlist.*;
/**
* The main goal of the CListKeySearchListener is to listen for key
* events and to search the ContactList when a key is typed over it. It selects
* the Contact name closest to the typed string.
*
* The time between two button presses is checked to determine whether the user
* makes a new search or a continious search. When user types the same letter
* consecutively the search mechanism selects the next Contact name starting
* with the same letter.
*
* The CListKeySearchListener is added to the MainFrame and
* the ContactListPanel to provide a search functionality over the
* contact list when one of them is focused.
*
* The 'space' key, the '+' and the '-' keys are proccess seperately to provide
* another functionality completely different from the search. When user types
* a '-' or 'space' and there's currently a group selected, the selected group
* is closed. When user types '+' the selected group is opened.
*
* @author Yana Stamcheva
*/
public class CListKeySearchListener implements KeyListener {
private JList contactList;
private char lastTypedKey = KeyEvent.CHAR_UNDEFINED;
private long lastTypedTimestamp = 0;
private StringBuffer keyBuffer = new StringBuffer();
/**
* Creates an instance of CListKeySearchListener for the given
* ContactList.
* @param contactList The contact list.
*/
public CListKeySearchListener(JList contactList) {
this.contactList = contactList;
}
public void keyPressed(KeyEvent e)
{
//Nothing to do if the contact list is empty
if(contactList.getModel().getSize() <= 0)
return;
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_LEFT)
{
closeGroup();
}
else if(keyCode == KeyEvent.VK_RIGHT)
{
openGroup();
}
else if(keyCode == KeyEvent.VK_ENTER)
{
openOrCloseGroup();
}
}
public void keyReleased(KeyEvent e) {
}
/**
* Searches the contact list when any key, different from "space", "+" or
* "-" is typed. Selects the Contact name closest to the typed string. The
* time between two button presses is checked to determine whether the user
* makes a new search or a continuous search. When user types the same
* letter consecutively the search mechanism selects the next Contact name
* starting with the same letter.
*/
public void keyTyped(KeyEvent e) {
//Nothing to do if the contact list is empty
if(contactList.getModel().getSize() <= 0)
return;
long eventTimestamp = e.getWhen();
char keyChar = e.getKeyChar();
if(keyChar == ' ') {
openOrCloseGroup();
}
else if(keyChar == '+') {
openGroup();
}
else if(keyChar == '-') {
closeGroup();
}
else {
if ((lastTypedTimestamp - eventTimestamp) > 1000) {
keyBuffer.delete(0, keyBuffer.length() - 1);
}
this.lastTypedTimestamp = eventTimestamp;
this.keyBuffer.append(keyChar);
boolean selectedSameLetterContact = false;
int selectedIndex = this.contactList.getSelectedIndex();
// Check if there's any selected contact node and get its name.
if (selectedIndex != -1) {
Object selectedObject = this.contactList.getSelectedValue();
if (selectedObject instanceof MetaContact) {
String selectedContactName = ((MetaContact) selectedObject)
.getDisplayName();
if (selectedContactName != null) {
selectedSameLetterContact
= selectedContactName.substring(0, 1)
.equalsIgnoreCase(keyBuffer.toString());
}
}
else if(selectedObject instanceof ConferenceChatContact) {
String selectedContactName =
((ConferenceChatContact) selectedObject).getName();
if (selectedContactName != null) {
selectedSameLetterContact
= selectedContactName.substring(0, 1)
.equalsIgnoreCase(keyBuffer.toString());
}
}
}
// The search starts from the beginning if:
// 1) the newly entered character is different from the last one
// or
// 2) the currently selected contact starts with a different letter
int contactIndex = contactList.getNextMatch(
keyBuffer.toString(),
(lastTypedKey != keyChar || !selectedSameLetterContact)
? 0
: selectedIndex + 1,
Position.Bias.Forward);
int currentlySelectedIndex = this.contactList.getSelectedIndex();
if (currentlySelectedIndex != contactIndex && contactIndex != -1) {
this.contactList.setSelectedIndex(contactIndex);
currentlySelectedIndex = contactList.getSelectedIndex();
}
this.contactList.ensureIndexIsVisible(currentlySelectedIndex);
this.lastTypedKey = keyChar;
}
}
/**
* Closes a group when it's opened.
*/
public void closeGroup() {
Object selectedValue = this.contactList.getSelectedValue();
if (selectedValue instanceof MetaContactGroup) {
MetaContactGroup group = (MetaContactGroup) selectedValue;
ContactListModel model
= (ContactListModel)contactList.getModel();
if (!model.isGroupClosed(group)) {
model.closeGroup(group);
}
}
}
/**
* Opens a group when it's closed.
*/
public void openGroup() {
Object selectedValue = this.contactList.getSelectedValue();
if (selectedValue instanceof MetaContactGroup) {
MetaContactGroup group = (MetaContactGroup) selectedValue;
ContactListModel model
= (ContactListModel) contactList.getModel();
if (model.isGroupClosed(group)) {
model.openGroup(group);
}
}
}
/**
* Opens or closes a group depending the state.
*/
public void openOrCloseGroup()
{
Object selectedValue = this.contactList.getSelectedValue();
if (selectedValue instanceof MetaContactGroup)
{
MetaContactGroup group = (MetaContactGroup) selectedValue;
ContactListModel model
= (ContactListModel) contactList.getModel();
if (model.isGroupClosed(group))
{
model.openGroup(group);
}
else
{
model.closeGroup(group);
}
}
}
}