package org.simantics.browsing.ui.swt;
import java.util.regex.Pattern;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.simantics.browsing.ui.GraphExplorer;
/**
* Selects tree items based on pressed key events.
*
* The default implementation of SWT.Tree (Windows?) uses only the the first column when matching the items.
*
* This implementation checks all columns. Override
matches(), matchesColumn()
for customized behavior.
*
* @author Marko Luukkainen
*
*/
public class KeyToSelectionAdapter extends KeyAdapter {
private static final int KEY_INPUT_DELAY = 500;
private final GraphExplorer explorer;
private String matcher = "";
private int prevEvent = 0;
private int columns = 0;
protected Pattern alphaNum;
/**
* @param contextProvider
* @param explorer
*/
public KeyToSelectionAdapter(GraphExplorer explorer) {
assert explorer != null;
this.explorer = explorer;
this.alphaNum = Pattern.compile("\\p{Alnum}");
}
@Override
public void keyPressed(KeyEvent e) {
if (explorer.isDisposed())
return;
if (!alphaNum.matcher(Character.toString(e.character)).matches())
return;
// concatenate / replace matcher.
if ((e.time - prevEvent) > KEY_INPUT_DELAY )
matcher = "";
prevEvent = e.time;
matcher = matcher += Character.toString(e.character);
TreeItem item = null;
Tree tree = explorer.getControl();
columns = tree.getColumnCount();
TreeItem[] selected = tree.getSelection();
item = find(tree, selected);
if (item == null && matcher.length() > 1) {
matcher = matcher.substring(matcher.length()-1);
item = find(tree, selected);
}
if (item != null) {
tree.select(item);
tree.showItem(item);
}
// without this the default handling would take over.
e.doit = false;
}
private TreeItem previous = null;
private boolean foundPrev = false;
private TreeItem find(Tree tree, TreeItem[] selected) {
TreeItem item = null;
TreeItem items[] = tree.getItems();
if (selected.length == 0) {
previous = null;
foundPrev = true;
item = findItem(items, 0);
} else {
previous = selected[0];
foundPrev = false;
item = findItem(items, 0);
if (item == null) {
previous = null;
foundPrev = true;
item = findItem(items, 0);
}
}
return item;
}
private TreeItem findItem(TreeItem items[], int depth) {
for (int i = 0; i < items.length; i++) {
TreeItem item = items[i];
if (item != previous) {
if (foundPrev && matches(item, depth, columns, matcher))
return item;
} else {
foundPrev = true;
}
TreeItem childItem = findItem(item.getItems(),depth+1);
if (childItem != null)
return childItem;
}
return null;
}
/**
*
* @param item
* @param depth Depth of the item in the tree.
* @param columns Number of columns.
* @param string Matching string.
* @return
*/
protected boolean matches(TreeItem item, int depth, int columns, String matcher) {
for (int c = 0; c < columns; c++) {
if (matchesColumn(item, c, matcher)) {
return true;
}
}
return false;
}
/**
*
* @param item
* @param column
* @param matcher
* @return
*/
protected boolean matchesColumn(TreeItem item, int column, String matcher) {
String text = item.getText(column);
if (text.toLowerCase().startsWith(matcher)) {
return true;
}
return false;
}
}