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; } }