--- /dev/null
+package org.simantics.browsing.ui.nattable;\r
+\r
+import java.util.Collection;\r
+import java.util.List;\r
+import java.util.regex.Pattern;\r
+\r
+import org.eclipse.jface.viewers.IStructuredSelection;\r
+import org.eclipse.nebula.widgets.nattable.NatTable;\r
+import org.eclipse.swt.events.KeyAdapter;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.simantics.browsing.ui.GraphExplorer;\r
+import org.simantics.utils.ui.AdaptionUtils;\r
+\r
+/**\r
+ * Selects tree items based on pressed key events.<p>\r
+ * \r
+ * The default implementation of SWT.Tree (Windows?) uses only the the first column when matching the items.<p>\r
+ * \r
+ * This implementation checks all columns. Override <pre>matches(), matchesColumn()</pre> for customized behavior.<p>\r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ */\r
+public class KeyToSelectionAdapter extends KeyAdapter {\r
+\r
+ private static final int KEY_INPUT_DELAY = 500;\r
+ \r
+ private final NatTableGraphExplorer explorer;\r
+\r
+ private String matcher = "";\r
+ private int prevEvent = 0;\r
+ private int columns = 0;\r
+ \r
+ protected Pattern alphaNum;\r
+ \r
+ /**\r
+ * @param contextProvider\r
+ * @param explorer\r
+ */\r
+ public KeyToSelectionAdapter(GraphExplorer explorer) {\r
+ assert explorer != null;\r
+\r
+ this.explorer = (NatTableGraphExplorer)explorer;\r
+ this.alphaNum = Pattern.compile("\\p{Alnum}");\r
+ }\r
+\r
+ @Override\r
+ public void keyPressed(KeyEvent e) {\r
+ if (explorer.isDisposed())\r
+ return;\r
+\r
+ \r
+ if (!alphaNum.matcher(Character.toString(e.character)).matches())\r
+ return;\r
+ // concatenate / replace matcher.\r
+ if ((e.time - prevEvent) > KEY_INPUT_DELAY )\r
+ matcher = "";\r
+ prevEvent = e.time;\r
+ matcher = matcher += Character.toString(e.character);\r
+\r
+ \r
+ //TreeItem item = null;\r
+ NatTable tree = explorer.getControl();\r
+ columns = explorer.getColumns().length;\r
+ \r
+ IStructuredSelection sel = (IStructuredSelection)explorer.getWidgetSelection();\r
+ Collection<RowSelectionItem> selected = AdaptionUtils.adaptToCollection(sel, RowSelectionItem.class);\r
+ \r
+ \r
+ TreeNode item = find(tree, selected);\r
+ \r
+ if (item == null && matcher.length() > 1) {\r
+ matcher = matcher.substring(matcher.length()-1);\r
+ item = find(tree, selected);\r
+ }\r
+ \r
+ if (item != null) {\r
+ explorer.select(item);\r
+ explorer.show(item);\r
+// tree.select(item);\r
+// tree.showItem(item);\r
+ \r
+ \r
+ } \r
+ // without this the default handling would take over.\r
+ e.doit = false;\r
+ }\r
+ \r
+ private TreeNode previous = null;\r
+ private boolean foundPrev = false;\r
+ \r
+ private TreeNode find(NatTable tree, Collection<RowSelectionItem> selected) {\r
+ TreeNode item = null;\r
+\r
+ List<TreeNode> items = explorer.getItems();\r
+ \r
+ if (selected.size() == 0) {\r
+ previous = null;\r
+ foundPrev = true;\r
+ item = findItem(items);\r
+\r
+ } else {\r
+ previous = selected.iterator().next().item;\r
+ foundPrev = false;\r
+ item = findItem(items);\r
+ if (item == null) {\r
+ previous = null;\r
+ foundPrev = true;\r
+ item = findItem(items);\r
+ }\r
+ }\r
+ return item;\r
+ }\r
+ \r
+ private TreeNode findItem(List<TreeNode> items) {\r
+ for (int i = 0; i < items.size(); i++) {\r
+ TreeNode item = items.get(i);\r
+ if (item != previous) {\r
+ if (foundPrev && matches(item, columns, matcher))\r
+ return item;\r
+ \r
+ } else {\r
+ foundPrev = true;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ \r
+ /**\r
+ * \r
+ * @param item\r
+ * @param depth Depth of the item in the tree.\r
+ * @param columns Number of columns.\r
+ * @param string Matching string.\r
+ * @return\r
+ */\r
+ protected boolean matches(TreeNode item, int columns, String matcher) {\r
+ for (int c = 0; c < columns; c++) { \r
+ if (matchesColumn(item, c, matcher)) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ \r
+ /**\r
+ * \r
+ * @param item\r
+ * @param column\r
+ * @param matcher\r
+ * @return\r
+ */\r
+ protected boolean matchesColumn(TreeNode item, int column, String matcher) {\r
+ String text = item.getValueString(column);\r
+ if (text.toLowerCase().startsWith(matcher)) {\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+\r
+}\r