Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 315458

Summary: Widget-disposed error during part switch on a stack renderer
Product: [Eclipse Project] e4 Reporter: Brian de Alwis <bsd>
Component: UIAssignee: Project Inbox <e4.ui-inbox>
Status: RESOLVED FIXED QA Contact:
Severity: normal    
Priority: P3    
Version: unspecified   
Target Milestone: 1.0 RC0   
Hardware: Macintosh   
OS: Mac OS X   
Whiteboard:
Attachments:
Description Flags
Workaround
none
Cause ToolControlRenderer to return the tool control widget none

Description Brian de Alwis CLA 2010-06-02 14:49:51 EDT
BuildId: I20100525

I have a part in a stack that contributes an MToolControl.  Switching to this part causes an SWTException (trace below) as the part's toolbar widget has been disposed.

The code in question:

		ToolBar tb;
		MToolBar tbModel = part.getToolbar();
		if (tbModel != null) {
			if (tbModel.getWidget() != null) {
				ToolBar oldTB = (ToolBar) tbModel.getWidget();
				if (oldTB.getParent() instanceof CTabFolder) {	// <--- line 523: oldTB is disposed
					CTabFolder oldCTF = (CTabFolder) oldTB.getParent();
					if (oldCTF.getTopRight() == oldTB)
						oldCTF.setTopRight(null);
				}
				oldTB.setParent(ctf);
				return oldTB;
			}
			tb = (ToolBar) renderer.createGui(tbModel, ctf);
		} else {
			tb = new ToolBar(ctf, SWT.FLAT | SWT.HORIZONTAL);
		}

The toolbar instance is disposed of in StackRenderer.showTab() when switching tabs.  The code above should be checking that the toolbar is not null and that it's not disposed.  It also turns out that PartRenderingEngine#createGui() needs similar code.

I'm a bit puzzled why this only seems to happen with this particular part.

org.eclipse.swt.SWTException: Widget is disposed
	at org.eclipse.swt.SWT.error(SWT.java:4083)
	at org.eclipse.swt.SWT.error(SWT.java:3998)
	at org.eclipse.swt.SWT.error(SWT.java:3969)
	at org.eclipse.swt.widgets.Widget.error(Widget.java:715)
	at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:531)
	at org.eclipse.swt.widgets.Control.getParent(Control.java:1615)
	at org.eclipse.e4.workbench.ui.renderers.swt.StackRenderer.getToolbar(StackRenderer.java:523)
	at org.eclipse.e4.workbench.ui.renderers.swt.StackRenderer.showTab(StackRenderer.java:479)
	at org.eclipse.e4.workbench.ui.renderers.swt.LazyStackRenderer$1.handleEvent(LazyStackRenderer.java:68)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:41)
	at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:188)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:198)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:227)
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:149)
	at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:139)
	at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:78)
	at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:39)
	at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:73)
	at org.eclipse.e4.workbench.ui.internal.UIEventPublisher.notifyChanged(UIEventPublisher.java:58)
	at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:380)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:165)
	at org.eclipse.e4.workbench.ui.renderers.swt.StackRenderer$3.widgetSelected(StackRenderer.java:410)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:234)
Comment 1 Brian de Alwis CLA 2010-06-02 14:51:03 EDT
Created attachment 170859 [details]
Workaround

This patch works around the immediate problem, but I doubt it's the right approach as I thought disposed items should have been removed from the model.
Comment 2 Eric Moffatt CLA 2010-06-02 16:23:15 EDT
Thanks Brian, before I apply this one I'm going to take another stab at why unrendering/removing/re-opening a perspective is giving us headaches. It may well be that there's an underlying issue that, once resolved, will fix this.

If a widget is disposed it's model element is supposed to 'unbind' (which should be setting 'widget' to null), meaning that if 'getWidget()' is non-null it should not be disposed (so the check should be unnecessary)...
Comment 3 Brian de Alwis CLA 2010-06-03 07:28:05 EDT
I dug into this a bit this morning and found the problem: the MToolControl is being bound to the *ToolBar* and not the *tool control*.

You can see this by putting a breakpoint in SWTPartRenderer#bindWidget(MUIElement, Object) conditioned on "widget instanceof org.eclipse.swt.widgets.ToolBar && !(me instanceof org.eclipse.e4.ui.model.application.ui.menu.MToolBar)"

The problem is that ToolControlRenderer#createWidget() is returning the *parentWidget*!

Daemon Thread [Thread-0] (Suspended (entry into method bindWidget in SWTPartRenderer))	
	ToolControlRenderer(SWTPartRenderer).bindWidget(MUIElement, Object) line: 85	
	PartRenderingEngine.createWidget(MUIElement, Object) line: 438	
	PartRenderingEngine.createGui(MUIElement, Object) line: 330	
	PartRenderingEngine.createGui(MUIElement) line: 388	
	ToolBarRenderer(SWTPartRenderer).processContents(MElementContainer<MUIElement>) line: 57	
	PartRenderingEngine.createGui(MUIElement, Object) line: 342	
	StackRenderer.getToolbar(MUIElement) line: 531	
	StackRenderer.showTab(MUIElement) line: 479
Comment 4 Brian de Alwis CLA 2010-06-03 07:35:06 EDT
Created attachment 170933 [details]
Cause ToolControlRenderer to return the tool control widget

The fix is so trivial I committed it.  Fixed in >20100603.
Comment 5 Brian de Alwis CLA 2010-06-03 07:35:41 EDT
Fix committed.
Comment 6 Eric Moffatt CLA 2010-06-03 16:14:54 EDT
Thanks Brian, I thought something like that might be the case (better that than having the while engine spewing disposed widgets...;-).