abm (14) amp (18) ascape (6) biomed (6) business (22) butterflyzer (9) dharma (12) eclipse (62) emf (7) graphics (10) ip (8) java (35) life (5) osx (13) science (13) web (6) xpand (5)

Friday, December 11, 2009

Adding GEF Zoom Support to a View Part

Just a quicky here..

I like to use views instead of editors for most of my GEF viewers. They are a lot of advantages to views -- the most significant one for me is that you can put them wherever you darn want to. But they're also seem much more amenable to programmatic control and interaction, which is crucial to AMP and other science applications. Generally, it's pretty easy to use view parts instead of edit parts for GEF, but there are a few gotchas, and this is one I recently encountered.

Normally, to add a combo box for zooming to a GEF edit part you just need to add an adapter to the Editor and then contribute that to the toolbar. See this tip for more on that. So that's what I did to add zoom support to the GEF 2D views in AGF. First, just like in the editor part case, you need to provide the zoomer. You could just add something like this to your ViewPart:


@Override
public Object getAdapter(Class type) {
...
if (type == ZoomManager.class) {
return ((ScalableFreeformRootEditPart) getViewer().getRootEditPart()).getZoomManager();
}
...
}


Then I just added the following code to my ViewPart as well:

    @Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
IToolBarManager toolBar = getViewSite().getActionBars().getToolBarManager();
ZoomComboContributionItem item = new ZoomComboContributionItem(getViewSite().getPage());
...


Seemed to work just fine, but then I noticed that the values for all of the combo boxes ended up being the same. If I changed a value in one combo box, it would update to all of the others. I traced the issue to this code in ZoomComboContributionItem:


public ZoomComboContributionItem(IPartService partService, String[] initStrings) {
super(GEFActionConstants.ZOOM_TOOLBAR_WIDGET);
this.initStrings = initStrings;
service = partService;
Assert.isNotNull(partService);
partService.addPartListener(partListener = new IPartListener() {
public void partActivated(IWorkbenchPart part) {
setZoomManager((ZoomManager) part.getAdapter(ZoomManager.class));
}
public void partBroughtToTop(IWorkbenchPart p) { }
public void partClosed(IWorkbenchPart p) { }
public void partDeactivated(IWorkbenchPart p) { }
public void partOpened(IWorkbenchPart p) { }
});
}


The problem here is that the code never checks to see if the zoom combo contribution is actually for the provided part or not! Basically, it is assuming that the Zoom contribution is a singleton. That of course works just fine for the editor case, but not when we have multiple zoomers as in the view part usage. The way that the class is written makes it difficult to change this behavior. It would be nice if the part listener was added in an method that we could override. Or we could just override getZoomManager() but that won't work either because the class itself uses the zoomManager field instead. A good reminder to all of us to pay attention to reuse even for seemingly single purpose classes like this. Anyway, I found a reasonable hack that works. We have the inverse situation here -- we know that a given zoom contribution item will have one and only one view attached to it. So we simply override setZoomManager to prevent it form setting anything else! (We could check avoid this except for the first case, but there doesn't seem to be any complelling reason to do so.)


ZoomComboContributionItem item = new ZoomComboContributionItem(getViewSite().getPage()) {
/**
* @param zm
* @see org.eclipse.gef.ui.actions.ZoomComboContributionItem#setZoomManager(org.eclipse.gef.editparts.ZoomManager)
*/
public void setZoomManager(ZoomManager zm) {
super.setZoomManager(getZoomer());
}
};


Here is a little helper method to get the zoom manager:


public ZoomManager getZoomer() {
if (rootEditPart instanceof ScalableFreeformRootEditPart) {
return ((ScalableFreeformRootEditPart) rootEditPart).getZoomManager();
} else if (rootEditPart instanceof ScalableRootEditPart) {
return ((ScalableRootEditPart) rootEditPart).getZoomManager();
}
return null;
}


BTW, you shouldn't need the adapter anymore, unless you're using it for something else. As I said, this is a bit of a hack, but it works.

Popular Posts

Recent Tweets

    follow me on Twitter