Fix column width issues on HiDPI displays. KeyTiSelection fixes.
[simantics/platform.git] / bundles / org.simantics.browsing.ui.nattable / src / org / simantics / browsing / ui / nattable / KeyToSelectionAdapter.java
1 package org.simantics.browsing.ui.nattable;
2
3 import java.util.Collection;
4 import java.util.List;
5 import java.util.regex.Pattern;
6
7 import org.eclipse.jface.viewers.IStructuredSelection;
8 import org.eclipse.nebula.widgets.nattable.NatTable;
9 import org.eclipse.swt.events.KeyAdapter;
10 import org.eclipse.swt.events.KeyEvent;
11 import org.simantics.browsing.ui.GraphExplorer;
12 import org.simantics.utils.ui.AdaptionUtils;
13
14 /**
15  * Selects tree items based on pressed key events.<p>
16  * 
17  * 
18  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
19  *
20  */
21 public class KeyToSelectionAdapter extends KeyAdapter {
22
23         private static final int KEY_INPUT_DELAY = 500;
24         
25     private final NatTableGraphExplorer          explorer;
26
27     private String matcher = "";
28     private int prevEvent = 0;
29     private int columns = 0;
30         
31     protected Pattern alphaNum;
32     
33     /**
34      * @param contextProvider
35      * @param explorer
36      */
37     public KeyToSelectionAdapter(GraphExplorer explorer) {
38         this(explorer,"\\p{Alnum}");
39     }
40     
41     public KeyToSelectionAdapter(GraphExplorer explorer, String pattern) {
42         assert explorer != null;
43
44         this.explorer = (NatTableGraphExplorer)explorer;
45         this.alphaNum = Pattern.compile(pattern);
46     }
47     
48     public boolean acceptKey(char key) {
49         return alphaNum.matcher(Character.toString(key)).matches();
50     }
51     
52     @Override
53     public void keyPressed(KeyEvent e) {
54         if (explorer.isDisposed())
55             return;
56
57          if (!acceptKey(e.character))
58                  return;
59          // concatenate / replace matcher.
60          if ((e.time - prevEvent) > KEY_INPUT_DELAY )
61                  matcher = "";
62          prevEvent = e.time;
63          matcher = matcher += Character.toString(e.character);
64
65          NatTable tree = explorer.getControl();
66          columns = explorer.getColumns().length;
67          
68          IStructuredSelection sel = (IStructuredSelection)explorer.getWidgetSelection();
69          Collection<RowSelectionItem> selected = AdaptionUtils.adaptToCollection(sel, RowSelectionItem.class);
70          
71           
72          TreeNode item = find(tree, selected);
73          
74          if (item == null && matcher.length() > 1) {
75                  matcher = matcher.substring(matcher.length()-1);
76                  item = find(tree, selected);
77          }
78          
79          if (item != null) {
80                  explorer.show(item);
81                  explorer.select(item);
82                  } 
83          // without this the default handling would take over.
84          e.doit = false;
85     }
86     
87     private TreeNode previous = null;
88     private boolean foundPrev = false;
89     
90         private TreeNode find(NatTable tree, Collection<RowSelectionItem> selected) {
91                 TreeNode item = null;
92
93                 List<TreeNode> items = explorer.getItems();
94                 
95                 if (selected.size() == 0) {
96                         previous = null;
97                         foundPrev = true;
98                         item = findItem(items);
99
100                 } else {
101                         previous = selected.iterator().next().item;
102                         foundPrev = false;
103                         item = findItem(items);
104                         if (item == null) {
105                                 previous = null;
106                                 foundPrev = true;
107                                 item = findItem(items);
108                         }
109                 }
110                 return item;
111         }
112     
113     private TreeNode findItem(List<TreeNode> items) {
114         for (int i = 0; i < items.size(); i++) {
115                 TreeNode item = items.get(i);
116                 if (item != previous) {
117                         if (foundPrev && matches(item, columns, matcher))
118                                 return item;
119                         
120                 } else {
121                         foundPrev = true;
122                 }
123         }
124         return null;
125     }
126     
127     
128     /**
129      * 
130      * @param item
131      * @param depth Depth of the item in the tree.
132      * @param columns Number of columns.
133      * @param string Matching string.
134      * @return
135      */
136     protected boolean matches(TreeNode item, int columns, String matcher) {
137         for (int c = 0; c < columns; c++) {     
138                         if (matchesColumn(item, c, matcher)) {
139                                 return true;
140                         }
141                 }
142         return false;
143     }
144     
145     /**
146      * 
147      * @param item
148      * @param column
149      * @param matcher
150      * @return
151      */
152     protected boolean matchesColumn(TreeNode item, int column, String matcher) {
153         String text = item.getValueString(column);
154         if (text == null)
155                 return false;
156                 if (text.toLowerCase().startsWith(matcher)) {
157                         return true;
158                 }
159                 return false;
160     }
161
162 }