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.

Friday, July 12, 2013

Scrolling draw2d ScrollPane using mouse wheel

Today I was working on a project where there was a need to scroll draw2d ScrollPane using only mouse wheel. Why? Because in this particular project there is a tree drawn using draw2d Figures and to the left of it a Nebula Grid. I won't go in details of why that is, but both trees needed to be scrolled using Grid's scroll bar. However, since draw2d tree had no scroll bar visible (by design), I still wanted to give user ability to scroll the tree using mouse wheel since the wheel is pretty much standard on all modern mice. There is no mouse wheel listener for ScrollPane or Viewport, but I found a nice little helper interface MouseWheelHelper. This interface has just one method:
void handleMouseWheelScrolled(Event event);
From there it was just a matter of finding the right variable that stores scroll value:
public class MyEditPart extends TreeEditPart implements MouseWheelHelper{
 private static final int SCROLL_OFFSET = 10;
 ...
        ...
 @Override
 // subtracting or adding values below controls direction of the scrolling
 public void handleMouseWheelScrolled(Event event) {
  pane.scrollVerticalTo(pane.getViewport().getVerticalRangeModel().getValue() - (event.count * SCROLL_OFFSET));
 }
}
That's it. Now when user clicks into the tree and uses scroll wheel the tree with scroll. What's neat is that by changing value of SCROLL_OFFSET you can speed up or slow down the scrolling. Of course this would not work for draw2d only case, because my ScrollPane was inside an EditPart that was already part of gef stack and that's why I could take advantage of MouseWheelHelper interface.

Blogger Syntax Highliter