Change default keybinding for TableViewer - java

By default a JFace TableViewer moves the selection up and down when the user presses the up or the down arrow key. This makes sense most of the time, but in my application the business logic will be different.
How can I override this behavior? I tried adding a KeyListener and it gets called, but the default action still happens: selection changes.

I have figured out I have to use a TableViewerFocusCellManager along with a CellNavigationStrategy. I expected the FocusCellManager to be a property of the TableViewer, but it is not. Apparently the FocusCellManager does the wiring behind the scenes.
CellNavigationStrategy navigationStrategy = new CellNavigationStrategy() {
#Override
public boolean shouldCancelEvent(ColumnViewer viewer, Event event) {
return true;
}
#Override
public boolean isNavigationEvent(ColumnViewer viewer, Event event) {
return false;
}
};
TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tableViewer, new FocusCellOwnerDrawHighlighter(tableViewer), navigationStrategy);

Related

Updating entries of virtual TableViewer on model change

I'm currently recoding a TableViewer to work fully virtual. So far I'm pretty content with the results, but I still have a problem, that all visible elements in the table are refreshed on a fixed timer. The model changes continously though. This means, that if I click on an entry before a periodic update happens, the table loads in the actual value for that position, but leaves all other elements untouched. Since this is how the LazyContentProvider works that is set for the TableViewer this is not much of a problem.
Since my TableViewer is a Live-Viewer of incoming events, with the newest entry shifting all other items one down, I'd like to refresh all visible elements on adding a new event.
I've tried to use TableViewer.refresh() on adding a new item, but that does not seem to do anything.
Since the full code is pretty complex, and part of a bigger piece of code I'll provide a basic representation of the code:
public class MyClass{
public TableViewer liveViewer;
public List<String> myItems=new ArrayList<>();
void init(){
liveViewer = new TableViewer(liveComp, SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL);
liveViewer.setContentProvider(new LiveViewerContentProvider(liveViewer));
liveViewer.setLabelProvider(someLabelProvider);
liveViewer.setUseHashlookup(true);
ClassThatProvidesItems.addListener(new ItemAddedListener(){
#Override
void itemAdded(String item){
myItems.add(0,item);
}
}
}
}
public class LiveViewerContentProvider implements ILazyContentProvider{
private TableViewer viewer;
private List<String> input;
public LiveViewerContentProvider(TableViewer viewer) {
this.viewer = viewer;
}
#Override
public void dispose() {
}
#Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.input = (List<String>) newInput;
}
#Override
public void updateElement(int index) {
viewer.replace(input.get(index), index);
}
}
I'm aware, that an ArrayList is probably not the best choice, for always adding an element at the head, but please ignore it for now. I've tried to perform a liveViewer.refresh(); at the end of the listener callback, but it didn't seem to refresh my elements. What could I do to force a refresh for all visible items on adding a new one?
Thanks in advance.
I've just noticed, that my solution was almost working. The problem was, that the whole code was somewhere within a weird try-catch-block that just silently swallowed Exceptions, and didn't give me the invalid-Thread-access exception that I should have gotten for not performing the liveViewer.refresh within the Display-Thread. Wrapping the line like this fixed the issue:
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
liveViewer.refresh();
}
});

How to make cells in a column un-selectable

I have a JTable in which some columns are uneditable. I do that by overriding the isCellEditable method. I now want to make the cells in these columns un-selectable. if the user is using the tab key to go through the cells, I want to focus to "skip" these uneditable cells. Can you please tell me how this is done? Thanks.
All navigation behaviour is controlled by actions registered in the table's actionMap. So the way to go is to hook into the action that is bound to the tab key, implement a wrapper that invokes that action as often as needed and replace the original action with the wrapper.
A raw code snippet for skipping not-editable cells:
Object actionKey = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.get(KeyStroke.getKeyStroke("TAB"));
final Action traverseAction = table.getActionMap().get(actionKey);
Action wrapper = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
traverseAction.actionPerformed(e);
while(shouldRepeat((JTable) e.getSource())) {
traverseAction.actionPerformed(e);
}
}
private boolean shouldRepeat(JTable source) {
int leadRow = source.getSelectionModel().getLeadSelectionIndex();
int leadColumn = source.getColumnModel().getSelectionModel().getLeadSelectionIndex();
return !source.isCellEditable(leadRow, leadColumn);
}
};
table.getActionMap().put(actionKey, wrapper);

GWTQuery live blur not working

The following code attaches a new element with every subsequent click on the 2nd div. With each adding, the focus is set to the added element, so that it is ready for blur methods (I am setting the tabIndex attribute just for this purpose).
However, on clicking outside the new element, nothing happens, even though there should be a pop-up. Can anyone tell me what's wrong with this code?
public void onModuleLoad() {
VerticalPanel vert = new VerticalPanel();
String foo = "<div id ='foo'>Foo</div>";
$("#bodywrapper").append(foo);
$("#bodywrapper").append("<div id ='boo'>Boo</div>");
$("#boo").click(new Function() {
public boolean f(Event e) {
// Window.alert("foo");
$("<div id ='goo' tabIndex = '1'>Boo</div>").appendTo("#bodywrapper").focus();
return true;
}
});
$("#goo").live("blur", new Function() {
public boolean f(Event e) {
Window.alert("Foo");
return true;
}
});
RootPanel.get().add(vert);
}
}
The blur and focus events don't work with event delegation (live or delegate methods) because they're not bubbling events. JQuery introduce the special events focusout and focusin for this purpose. But GwtQuery doesn't support them yet. Please open an issue there and they will be implemented

Change focus to next component in JTable using TAB

JTable's default behavior is changing focus to next cell and I want to force it to move focus to next component (e.g. JTextField) on TAB key pressed.
I overrided isCellEditable method of DefaultTableModel to always return false:
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
But it still doesn't change focus to next component!
How should I make JTable change focus to next component instead of next cell?
The shift-/tab keys are used by default for transfering focus between components. JTable explicitly requests to handle the shift-/tab internally (by providing sets of focusTraversalKeys which doesn't include those).
Following the general rule (if there's specilized api available for a task, use that instead of rolling your own), the solution is to set traversal keys to again contain them:
Set<AWTKeyStroke> forward = new HashSet<AWTKeyStroke>(
table.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
forward.add(KeyStroke.getKeyStroke("TAB"));
table.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forward);
Set<AWTKeyStroke> backward = new HashSet<AWTKeyStroke>(
table.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
backward.add(KeyStroke.getKeyStroke("shift TAB"));
table.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backward);
If you really want this, you need to change the default behavior of the tables action map.
ActionMap am = table.getActionMap();
am.put("selectPreviousColumnCell", new PreviousFocusHandler());
am.put("selectNextColumnCell", new NextFocusHandler());
Then you need a couple of actions to handle the traversal
public class PreviousFocusHandler extends AbstractAction {
public void actionPerformed(ActionEvent evt) {
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
manager.focusPreviousComponent();
}
}
public class NextFocusHandler extends AbstractAction {
public void actionPerformed(ActionEvent evt) {
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
manager.focusNextComponent();
}
}
Another approach would be to disable the underlying Action...
ActionMap am = table.getActionMap();
am.get("selectPreviousColumnCell").setEnabled(false);
am.get("selectNextColumnCell").setEnabled(false);
(haven't tested this)
The benefit of this approach is can enable/disable the behaviour as you need it without needing to maintain a reference to the old Actions
default (implemented KeyBinding for JTable) is about next cell and from last cell to first,
you can to remove KeyBindings by setting to the null value
To reset to the standard keyboard bindings (typically TAB and SHIFT+TAB), simply specify null for the keystrokes parameter to Component.setFocusTraversalKeys:
table.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
table.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);

In GWT, how to known from a SelectionEvent in a Tree if the Shift button is pressed

I try in GWT to create a Tree with multiple selection for the nodes and ran into a problem similar to this question Shift Key in GWT?.
When a selectionEvent is raised from the Tree, I would like to know if the Shift key is pressed or not.
SelectionHandler<TreeItem> getSelectionHandler() {
return new SelectionHandler<TreeItem>(){
#Override
public void onSelection(SelectionEvent<TreeItem> event) {
// is shift key pressed ?
}
};
}
The solution in the question above cannot apply in this case as the SelectionHandler class does not inherit from DOMEvent and then does not have a getNativeEvent() function.
I tried a dirty solution by adding keyDownEventHandler and keyUpEventHandler to the Tree with a boolean flag but the handlers are only called when the focus is on the tree so this doesn't work.
Is there a simple solution (or just a solution even if it's not simple) ? Thanks.
Edit on aem response :
The solution can work by enclosing the components in a FocusPanel with a keyUp/DownHandler but then I can't add any component needing keyboard input such as TextArea as the "global" handler takes the priority... So it don't really solve my problem.
My suggestion is to create a custom Tree class that temporary store the event and store this event by overriding the onBrowseEvent method. Then you can, in your onSelection method, check if the shift key was pressed by checking this stored event. Since JavaScript is not concurrent it should be no problem using the private variable. The code would be something like this:
public class MyTree extends Tree {
private Event currentEvent;
... constructors...
// Call this method from within the onSelection method to determine if the shift key
// was pressed.
public boolean isShiftPressed() {
return currentEvent != null ? currentEvent.getShiftKey() : false;
}
#Override
public void onBrowserEvent(Event event) {
currentEvent = event;
super.onBrowserEvent(event);
currentEvent = null;
}
}
I'm not sure whether this will work, but it's worth a try:
What about adding key handlers to the root panel containing the Tree, and have them set a boolean indicating whether the Shift key is down? Then the tree's SelectionHandler can check that boolean.
The trouble with this is that the page might contain other widgets that capture the key events, which would make this behavior seem flaky to the user.

Categories