Thursday, July 25, 2013

Editing TreeViewer's cell with two clicks

With introduction of ColumnViewerEditorActivationStrategy in Eclipse 3.3 there is an ability to control when cell of a viewer gets activated for editing. Unfortunately the events for cell selection is limited to ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION and ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION as far mouse events go. MOUSE_CLICK_SELECTION was not something I wanted to do because I didn't want cell editing to be activated each time selection changes in TreeViewer. I did not want to resort to MOUSE_DOUBLE_CLICK_SELECTION either because something else was suppose to happen on double-click in my case. I needed a way to enable cell editing when user click on a cell once and then again to edit. The solution I found was suggested by David Green (http://greensopinion.blogspot.com/2009/11/treeviewer-two-clicks-to-edit.html). However it did not work in my case. I am not sure exactly why, may be because I was using Nebula's Grid Widget (although it uses the same interfaces as TreeViewer) or custom EditingSupport. I've spend some time figuring out what was the problem, but then decided to take a different approach. Below is the code for my ColumnViewerEditorActivationStrategy implementation.
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.events.MouseEvent;

class SecondClickColumnViewerEditorActivationStrategy extends ColumnViewerEditorActivationStrategy implements ISelectionChangedListener {

 private int counter = 0;

 public SecondClickColumnViewerEditorActivationStrategy(ColumnViewer viewer) {
  super(viewer);
  viewer.addSelectionChangedListener(this);
 }

 @Override
 protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
  counter++;
  
  if(counter == 1){
   return false;
  }
  // this is in case when user clicked on tree node once, but second time activated context menu by clicking on right button,
  // we don't want to activate cell editing in this case
  else if(counter > 1 && isRightMouseClick(event)){
   return false;
  }
  else{
   return true;
  }
 }

 public void selectionChanged(SelectionChangedEvent event) {
  counter = 0;
 }
 
 /**
  * Determine if right mouse button was clicked
  * @param event
  * @return true if right mouse button was clicked, false otherwise.
  */
 private boolean isRightMouseClick(ColumnViewerEditorActivationEvent event){
  return event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION && ((MouseEvent)event.sourceEvent).button == 3;
 }
}
As you can see the editing enablement is controlled by counters. The important thing here is chain of calls between selectionChanged() and isEditorActivationEvent() methods. When user clicks on cell selectionChanged() gets called, and all subsequential clicks are handled by isEditorActivationEvent() method where counter is being incremented and appropriate return value is set. Note use of isRightMouseClick() call in isEditorActivationEvent(), I did not want to activate both context menu on the cell and editing when user clicked on a tree node and then activated context menu by clicking on right mouse button.

Blogger Syntax Highliter