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.



Hello Miles Parker.
ReplyDeleteFirst: Thanks alot for this fantastic blog :)
Second:
I have tried to fullfil this "toturial", but when i try to override the method getAdapter and insert the code:
[code]
if (type == ZoomManager.class) {
return ((ScalableFreeformRootEditPart) getViewer().getRootEditPart()).getZoomManager();
}
[/code]
Then it does not regonize the getViewer() method. (It says that it is undefined).
So therefor I cannot reach the rootEditPart :S
My view is extending the org.eclipse.ui.part.ViewPart class so this should be okay?, but it doesn't work.
I hope you could fine some time to help me :)
Thanks alot.
Regards Mikkel Jonassen
Hi Mikkel,
ReplyDeleteThe first case is if I remember correctly from an example of a common usage pattern for IViewPart. In that case, you implement IViewPart and then create the viewer in createPartControl. So you may or may not have done that -- you could just use the field of course. But as I mention down below, you shouldn't actually have to worry about defining the adapter at all now. That's because you'll be setting up the zoom in each views toolbar rather than relying on the editor manager to handle that. Hope that helps and let me know if you're still stuck.
-Miles
I see.. So I should only change the method createPartControl when using this "hack".
ReplyDeleteI still have a problem though. Since I can't figure out how to make an instance of the variable rootEditPart. Since I can't get the rootEditPart without the getViewer method.
In the EditParts classes the getViewer method exist, but this is a normal ViewPart that I am extending, so it doesn't exist here.
what do you suggest to retrieve the rootEditPart object, so that I can use it?
Thanks alot for your time and kindness :D
-Mikkel
I will clarify my problem to make sure that I am not doing something wrong.
ReplyDeleteInsted of implementing IViewPart I just extend ViewPart in order to get the default functionality, but then my problem is the rootEditPart in the helper method getZoomer().
Do I need to create a rootEditPart myself? in that case how? Or should I retrieve it with the getViewer method like it is done in the getAdapter (which I don't use now). The problem is that there is no such method getViewer() neither in ViewPart or in the IViewPart. This method only exist in an EditPart.
So my problem in short. how do I get the rootEditPart, so the variabel/field can be used?
example:
RootEditPart rep = new RootEditPart()??
or
RootEditPart rep = method..getRootEditPart??
Some line like this would be greate to make an instantiation of the rootEditPart field.
No problem. The description above is a little sketchy. I realized I should have just given the actual implementation -- for more details look at:
ReplyDeletehttp://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.amp/org.eclipse.amp.agf/plugins/org.eclipse.amp.agf.core/src/org/eclipse/amp/agf/gef/GEFViewPart.java?view=markup&revision=1.5&root=Modeling_Project
http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.amp/org.eclipse.amp.agf/plugins/org.eclipse.amp.agf.core/src/org/eclipse/amp/agf/gef/EditPartViewPart.java?view=markup&revision=1.5&root=Modeling_Project
You've got the right strategy -- it would be pretty heinous to try to implement your own IViewPart.I also found that the GEF root edit part stuff was a little hard to puzzle out. The code above is probably more complicated then you need, but you can see that we a) create the viewer in createPartControl, and then create the rootEditPart in the setInput code. I can't actually remember why it is neccesary to do that in the input call -- I think because the edit part expects to actually have some content. As you can see, we adapt for the EditPart in the ViewPart as well but again, I'm not sure that that is needed.
I hope this helps -- I wish I had a more clean isolated example, but this one was designed for extension throughout the AMP graphics code. I end up using Edit Parts even for some non-GEF Edit views simply because once you have all that stuff written it is much easier to integrate any othr kind of graphics view!
Thanks alot for your example I will now investigate them closely :)
ReplyDeleteThe last question, do you have any idea on how this would work to zoom on SWT widgets like Buttons etc.?
Once again thanks. :)