tags:

views:

838

answers:

2

Here's the situation, I have a jFrame with a tabbed pane and within the tabs I have a couple of jTables and a jTree. I want to be able to chain the selections between the tables and the tree based on whether a user uses a ctrl/shift + click versus a regular click. (If you hold ctrl and click in the first table/tree, it adds to the overall selection, if you use a regular click it clears the selections in the other tables/tree). Currently I'm having an issue with Java's jTree component. I have added a TreeSelectionListener and a MouseListener with a class that implements both interfaces, call it MyBigListener; i.e.

MyBigListener listener = new MyBigListener();
jTree1.addMouseListener( listener );
jTree1.addTreeSelectionListener( listener );

MyBigListener implements TreeSelectionListener, MouseListener {
  private boolean chained = false;
  public synchronized setChained(boolean ch){
    chained = ch;
  }
  public synchronized boolean isChained(){
    return chained
  }
  public void valueChanged(TreeSelectionEvent e){
    if(isChained()){ blah... }
  }

  public void mousePressed(MouseEvent e){
    setChained(e.isControlDown() || e.isShiftDown());
  }
}

My plan was to set a boolean flag if the user uses a ctrl/shift + click that I could check during the valueChanged(TreeSelectionEvent e) implemented by the tree selection listener. I want to be able to process the mouse events before the valueChanged TreeSelectionEvents, but the problem is that I receive the mouse events after the valueChanged treeSelection event. This seems weird to me that I receive the selection change before the mouse pressed event fires, when the selection change is actually initiated by the mouse pressed. (I've already synchronized the boolean flag setting, which ironically helped to highlight the mis-order of events.)

I've already tried alternatives like adding a keyListener, but this doesn't work when the focus is on a separate frame, which goofs me up when a user holds ctrl and then clicks into the jTree causing it to receive both the focus and fire any valueChanged selection events.

Any help would be appreciated, thanks!

--EDIT-- @akf I have separate jTables and jTrees in a tabbed Pane that serve as a summary/control panel for data in a nodal-graph. I'm using these components in the tabbed Pane to do coordinated selection to a graph displayed in a separate jFrame. Individually each table works just fine for its selection as does the jTree. It's coordinating between the panes that's tricky. This works fine so far with jTable components because I fire the new selections as the result of a MouseEvent where I can tell if the shift/ctrl button was down, formulate my new selection, and pass it to the parent frame which coordinates selections between all panes and sends the final selection to the graph. However, the parent needs to know if it needs to chain a selection between panes, or squash the others. With the jTables it's fine again, because I fire selection changes as the result of a mouse click. The jTree is more of a problem because I'm doing some forced selections. If you click on a branch, it forces all leaves into the selection. I needed to implement a TreeSelectionListener to do that, but only get a valueChanged(TreeSelectionEvent) to realized changes. I added a mouseListener to listen for ctrl+clicks and shift+clicks, but apparently the events don't always happen in the same order.. at least so far I receive the valueChanged event before the mousePressed event, so checking to if a ctrl+click happened posts after the selection has already been modified.

Right now, I'm posting a pending selection change and then have the MouseListener grab that and send it up the chain, but if these events aren't guaranteed to happen in the same order, at some point it's going to fail. Implementing a delayer also rubs me the wrong way.

Thanks for the help so far.

--EDIT2-- @ykaganovich

I think overriding the fireValueChanged method is closer to the right way to go about things. Depending on my definition of what actions should cause a "chained" selection to the other components, I'd need to gather some context on what's going on before the valuedChanged method fires. This basically means calling it myself in all cases where I can define what it means by who triggers it. I.e. If a mouse event causes it and ctrl is down then set what I need to set (interpret) then fire. If it's going to change due to a keyboard event, again, set what I need to set, then fire. I don't think the TreeSelectionModel is the way to go, because I still won't know what the circumstances were when the event fired. I think this means I'll need to rewrite parts of the jTree to do this but I guess I'll see how it goes. Thanks.

A: 

You may get the mouse event before or after the tree selection event. Best not to rely on such orders. The reason is that the tree selection event is caused in response to the mouse event. Is that mouse event listener called before or after your listener? Could be either.

This sort of thing is closely involved with the implementation of the PL&F.

Tom Hawtin - tackline
A: 

Don't do it that way, override JTree.fireValueChanged instead.

Try something like this (untested):

class ChainedSelectionEvent extends TreeSelectionEvent {
  ChainedSelectionEvent(TreeSelectionEvent e) {
    super(e.newSource, e.paths, e.areNew, e.oldLeadSelectionPath, e.newLeadSelectionPath);
  }
}

protected void fireValueChanged(TreeSelectionEvent e) {
  if(chained) { // figure out separately
      super.fireValueChanged(new ChainedSelectionEvent(e));
  } else {
      super.fireValueChanged(e);
  }
}

Then check instanceof ChainedSelectionEvent in your listener

EDIT

Actually, I think the right way to do this is to implement your own TreeSelectionModel, and override fireValueChanged there instead. Assuming setSelectionPath(s) methods imply a new selection, and add/removeSelectionPath(s) imply chaining, you could distinguish between the two cleanly. I don't like listening to either keyboard or mouse events explicitly, because there's more than one way to change a selection (e.g. if someone is holding down SHIFT and hitting a down-arrow, you won't get a mouse event).

ykaganovich
I think I'd still run into the same problem here, not being able to determine if the selection should be chained because the events happen out of order. Who controls the calling of fireValueChanged? The tree itself? I didn't want to completely re-write the jTree if I didn't have to, although I already have it pretty torn up adding drag-drop support to it. Hmm... I guess I could over-ride it in my tree, implement a MouseListener there, and only call this method as the result of a mouse event, but then selections for branch collapse/expand gets trickier.. I think I hate jTrees :)
I think this should work. fireValueChanged will be called by JTree in response to selections. I don't think you should ever have to deal with MouseEvents explicitly.
ykaganovich
I need to deal with the MouseEvents as that's the only way I'll know if a user added/removed something while holding down the shift or ctrl button. This is my definition of when to chain something or not. I agree it's not clean, but I could have a KeyListener basically do the same thing to capture those events and respond. For my definition of when to chain or not, I guess I need to capture all the ways a user can modify selection, and get some context as to what's going on before allowing the selection change to fire. Thanks for the help.