Java tutorial
/******************************************************************************* * Copyright (c) 2010, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Joseph Carroll <jdsalingerjr@gmail.com> - Bug 385414 Contributing wizards to toolbar always displays icon and text * Snjezana Peco <snjezana.peco@redhat.com> - Memory leaks in Juno when opening and closing XML Editor - http://bugs.eclipse.org/397909 * Marco Descher <marco@descher.at> - Bug 397677 ******************************************************************************/ package org.eclipse.e4.ui.workbench.renderers.swt; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.eclipse.core.commands.IStateListener; import org.eclipse.core.commands.ParameterizedCommand; import org.eclipse.core.commands.State; import org.eclipse.core.commands.common.NotDefinedException; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.e4.core.commands.ECommandService; import org.eclipse.e4.core.commands.EHandlerService; import org.eclipse.e4.core.contexts.EclipseContextFactory; import org.eclipse.e4.core.contexts.IContextFunction; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.core.services.log.Logger; import org.eclipse.e4.ui.bindings.EBindingService; import org.eclipse.e4.ui.internal.workbench.Activator; import org.eclipse.e4.ui.internal.workbench.ContributionsAnalyzer; import org.eclipse.e4.ui.internal.workbench.Policy; import org.eclipse.e4.ui.internal.workbench.renderers.swt.IUpdateService; import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; import org.eclipse.e4.ui.model.application.commands.MParameter; import org.eclipse.e4.ui.model.application.ui.MContext; import org.eclipse.e4.ui.model.application.ui.MUIElement; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.model.application.ui.menu.ItemType; import org.eclipse.e4.ui.model.application.ui.menu.MHandledItem; import org.eclipse.e4.ui.model.application.ui.menu.MItem; import org.eclipse.e4.ui.model.application.ui.menu.MMenu; import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; import org.eclipse.e4.ui.model.application.ui.menu.MRenderedMenu; import org.eclipse.e4.ui.model.application.ui.menu.MToolItem; import org.eclipse.e4.ui.workbench.IPresentationEngine; import org.eclipse.e4.ui.workbench.IResourceUtilities; import org.eclipse.e4.ui.workbench.modeling.EModelService; import org.eclipse.e4.ui.workbench.swt.util.ISWTResourceUtilities; import org.eclipse.emf.common.util.URI; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.action.IContributionManager; import org.eclipse.jface.action.IMenuCreator; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.bindings.TriggerSequence; import org.eclipse.jface.menus.IMenuStateIds; import org.eclipse.jface.resource.DeviceResourceException; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Widget; public class HandledContributionItem extends ContributionItem { /** * Constant from org.eclipse.ui.handlers.RadioState.PARAMETER_ID */ private static final String ORG_ECLIPSE_UI_COMMANDS_RADIO_STATE_PARAMETER = "org.eclipse.ui.commands.radioStateParameter"; //$NON-NLS-1$ /** * Constant from org.eclipse.ui.handlers.RadioState.STATE_ID */ private static final String ORG_ECLIPSE_UI_COMMANDS_RADIO_STATE = "org.eclipse.ui.commands.radioState"; //$NON-NLS-1$ /** * Constant from org.eclipse.ui.handlers.RegistryToggleState.STATE_ID */ private static final String ORG_ECLIPSE_UI_COMMANDS_TOGGLE_STATE = "org.eclipse.ui.commands.toggleState"; //$NON-NLS-1$ static class RunnableRunner implements ISafeRunnable { private Runnable runnable; public void setRunnable(Runnable r) { runnable = r; } public void handleException(Throwable exception) { // Do not report these exceptions ATM } public void run() throws Exception { runnable.run(); } } public static class ToolItemUpdateTimer implements Runnable { Display display = Display.getCurrent(); RunnableRunner runner = new RunnableRunner(); List<HandledContributionItem> itemsToCheck = new ArrayList<HandledContributionItem>(); List<Runnable> windowRunnables = new ArrayList<Runnable>(); final List<HandledContributionItem> orphanedToolItems = new ArrayList<HandledContributionItem>(); public void addWindowRunnable(Runnable r) { windowRunnables.add(r); } public void removeWindowRunnable(Runnable r) { windowRunnables.remove(r); } void registerItem(HandledContributionItem item) { if (!itemsToCheck.contains(item)) { itemsToCheck.add(item); // Start the timer on the first item registered if (itemsToCheck.size() == 1) display.timerExec(400, this); } } void removeItem(HandledContributionItem item) { itemsToCheck.remove(item); } public void run() { for (final HandledContributionItem hci : itemsToCheck) { // HACK. Remove orphaned entries. See bug 388516. if (hci.model != null && hci.model.getParent() != null) { hci.updateItemEnablement(); } else { orphanedToolItems.add(hci); } } if (!orphanedToolItems.isEmpty()) { itemsToCheck.removeAll(orphanedToolItems); orphanedToolItems.clear(); } if (windowRunnables.size() > 0) { Runnable[] array = new Runnable[windowRunnables.size()]; windowRunnables.toArray(array); for (Runnable r : array) { runner.setRunnable(r); SafeRunner.run(runner); } } // repeat until the list goes empty if (itemsToCheck.size() > 0) display.timerExec(400, this); } } // HACK!! local 'static' timerExec...should move out of this class post 4.1 public static ToolItemUpdateTimer toolItemUpdater = new ToolItemUpdateTimer(); private static final String FORCE_TEXT = "FORCE_TEXT"; //$NON-NLS-1$ private static final String ICON_URI = "iconURI"; //$NON-NLS-1$ private static final String DISABLED_URI = "disabledURI"; //$NON-NLS-1$ private static final String DISPOSABLE_CHECK = "IDisposable"; //$NON-NLS-1$ private static final String WW_SUPPORT = "org.eclipse.ui.IWorkbenchWindow"; //$NON-NLS-1$ private static final String HCI_STATIC_CONTEXT = "HCI-staticContext"; //$NON-NLS-1$ private MHandledItem model; private Widget widget; private Listener menuItemListener; private LocalResourceManager localResourceManager; @Inject @Optional private Logger logger; // We'll only ever log an error during update once to prevent spamming the // log private boolean logged = false; @Inject private ECommandService commandService; @Inject private EModelService modelService; @Inject private EBindingService bindingService; @Inject @Optional private IUpdateService updateService; private Runnable unreferenceRunnable; private ISWTResourceUtilities resUtils = null; private IStateListener stateListener = new IStateListener() { public void handleStateChange(State state, Object oldValue) { updateState(); } }; @Inject void setResourceUtils(IResourceUtilities utils) { resUtils = (ISWTResourceUtilities) utils; } private ISafeRunnable getUpdateRunner() { if (updateRunner == null) { updateRunner = new ISafeRunnable() { public void run() throws Exception { boolean shouldEnable = canExecuteItem(null); if (shouldEnable != model.isEnabled()) { model.setEnabled(shouldEnable); update(); } } public void handleException(Throwable exception) { if (!logged) { logged = true; if (logger != null) { logger.error(exception, "Internal error during tool item enablement updating, this is only logged once per tool item."); //$NON-NLS-1$ } } } }; } return updateRunner; } protected void updateItemEnablement() { if (!(model.getWidget() instanceof ToolItem)) return; ToolItem widget = (ToolItem) model.getWidget(); if (widget == null || widget.isDisposed()) return; SafeRunner.run(getUpdateRunner()); } private IMenuListener menuListener = new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { update(null); } }; private ISafeRunnable updateRunner; private IEclipseContext infoContext; private State styleState; private State toggleState; private State radioState; public void setModel(MHandledItem item) { model = item; setId(model.getElementId()); generateCommand(); if (model.getCommand() == null) logger.error("Element " + model.getElementId() + " invalid, no command defined."); //$NON-NLS-1$ //$NON-NLS-2$ updateVisible(); } /** * */ private void generateCommand() { if (model.getCommand() != null && model.getWbCommand() == null) { String cmdId = model.getCommand().getElementId(); List<MParameter> modelParms = model.getParameters(); Map<String, String> parameters = new HashMap<String, String>(4); for (MParameter mParm : modelParms) { parameters.put(mParm.getName(), mParm.getValue()); } ParameterizedCommand parmCmd = commandService.createCommand(cmdId, parameters); Activator.trace(Policy.DEBUG_MENUS, "command: " + parmCmd, null); //$NON-NLS-1$ model.setWbCommand(parmCmd); styleState = parmCmd.getCommand().getState(IMenuStateIds.STYLE); toggleState = parmCmd.getCommand().getState(ORG_ECLIPSE_UI_COMMANDS_TOGGLE_STATE); radioState = parmCmd.getCommand().getState(ORG_ECLIPSE_UI_COMMANDS_RADIO_STATE); updateState(); if (styleState != null) { styleState.addListener(stateListener); } else if (toggleState != null) { toggleState.addListener(stateListener); } else if (radioState != null) { radioState.addListener(stateListener); } } } private void updateState() { if (styleState != null) { model.setSelected(((Boolean) styleState.getValue()).booleanValue()); } else if (toggleState != null) { model.setSelected(((Boolean) toggleState.getValue()).booleanValue()); } else if (radioState != null && model.getWbCommand() != null) { ParameterizedCommand c = model.getWbCommand(); Object parameter = c.getParameterMap().get(ORG_ECLIPSE_UI_COMMANDS_RADIO_STATE_PARAMETER); String value = (String) radioState.getValue(); model.setSelected(value != null && value.equals(parameter)); } } /* * (non-Javadoc) * * @see * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets * .Menu, int) */ @Override public void fill(Menu menu, int index) { if (model == null) { return; } if (widget != null) { return; } int style = SWT.PUSH; if (model.getType() == ItemType.PUSH) style = SWT.PUSH; else if (model.getType() == ItemType.CHECK) style = SWT.CHECK; else if (model.getType() == ItemType.RADIO) style = SWT.RADIO; MenuItem item = null; if (index >= 0) { item = new MenuItem(menu, style, index); } else { item = new MenuItem(menu, style); } item.setData(this); item.addListener(SWT.Dispose, getItemListener()); item.addListener(SWT.Selection, getItemListener()); item.addListener(SWT.DefaultSelection, getItemListener()); widget = item; model.setWidget(widget); widget.setData(AbstractPartRenderer.OWNING_ME, model); update(null); if (updateService != null) { unreferenceRunnable = updateService.registerElementForUpdate(model.getWbCommand(), model); } } /* * (non-Javadoc) * * @see * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets * .ToolBar, int) */ @Override public void fill(ToolBar parent, int index) { if (model == null) { return; } if (widget != null) { return; } boolean isDropdown = false; if (model instanceof MToolItem) { MMenu menu = ((MToolItem) model).getMenu(); isDropdown = menu != null; } int style = SWT.PUSH; if (isDropdown) style = SWT.DROP_DOWN; else if (model.getType() == ItemType.CHECK) style = SWT.CHECK; else if (model.getType() == ItemType.RADIO) style = SWT.RADIO; ToolItem item = null; if (index >= 0) { item = new ToolItem(parent, style, index); } else { item = new ToolItem(parent, style); } item.setData(this); item.addListener(SWT.Dispose, getItemListener()); item.addListener(SWT.Selection, getItemListener()); item.addListener(SWT.DefaultSelection, getItemListener()); widget = item; model.setWidget(widget); widget.setData(AbstractPartRenderer.OWNING_ME, model); toolItemUpdater.registerItem(this); update(null); hookCheckListener(); if (updateService != null) { unreferenceRunnable = updateService.registerElementForUpdate(model.getWbCommand(), model); } } private void hookCheckListener() { if (model.getType() != ItemType.CHECK) { return; } Object obj = model.getTransientData().get(ItemType.CHECK.toString()); if (obj instanceof IContextFunction) { IEclipseContext context = getContext(model); IEclipseContext staticContext = getStaticContext(null); staticContext.set(MPart.class, context.get(MPart.class)); staticContext.set(WW_SUPPORT, context.get(WW_SUPPORT)); IContextFunction func = (IContextFunction) obj; obj = func.compute(staticContext); if (obj != null) { model.getTransientData().put(DISPOSABLE_CHECK, obj); } } } private void unhookCheckListener() { if (model.getType() != ItemType.CHECK) { return; } final Object obj = model.getTransientData().remove(DISPOSABLE_CHECK); if (obj == null) { return; } ((Runnable) obj).run(); } private void updateVisible() { setVisible((model).isVisible()); final IContributionManager parent = getParent(); if (parent != null) { parent.markDirty(); } } /* * (non-Javadoc) * * @see org.eclipse.jface.action.ContributionItem#update() */ @Override public void update() { update(null); } /* * (non-Javadoc) * * @see org.eclipse.jface.action.ContributionItem#update(java.lang.String) */ @Override public void update(String id) { updateIcons(); if (widget instanceof MenuItem) { updateMenuItem(); } else if (widget instanceof ToolItem) { updateToolItem(); } } private void updateMenuItem() { MenuItem item = (MenuItem) widget; String text = model.getLocalizedLabel(); ParameterizedCommand parmCmd = model.getWbCommand(); String keyBindingText = null; if (parmCmd != null) { if (text == null) { try { text = parmCmd.getName(); } catch (NotDefinedException e) { // we'll just ignore a failure } } if (bindingService != null) { TriggerSequence binding = bindingService.getBestSequenceFor(parmCmd); if (binding != null) keyBindingText = binding.format(); } } if (text != null) { if (model instanceof MMenuElement) { String mnemonics = ((MMenuElement) model).getMnemonics(); if (mnemonics != null) { int idx = text.indexOf(mnemonics); if (idx != -1) { text = text.substring(0, idx) + '&' + text.substring(idx); } } } if (keyBindingText == null) item.setText(text); else item.setText(text + '\t' + keyBindingText); } else { item.setText(""); //$NON-NLS-1$ } item.setSelection(model.isSelected()); item.setEnabled(model.isEnabled()); } private void updateToolItem() { ToolItem item = (ToolItem) widget; final String text = model.getLocalizedLabel(); Image icon = item.getImage(); boolean mode = model.getTags().contains(FORCE_TEXT); if ((icon == null || mode) && text != null) { item.setText(text); } else { item.setText(""); //$NON-NLS-1$ } final String tooltip = getToolTipText(); item.setToolTipText(tooltip); item.setSelection(model.isSelected()); item.setEnabled(model.isEnabled()); } private String getToolTipText() { String text = model.getLocalizedTooltip(); ParameterizedCommand parmCmd = model.getWbCommand(); if (parmCmd == null) { generateCommand(); parmCmd = model.getWbCommand(); } if (parmCmd != null && text == null) { try { text = parmCmd.getName(); } catch (NotDefinedException e) { return null; } } TriggerSequence sequence = bindingService.getBestSequenceFor(parmCmd); if (sequence != null) { text = text + " (" + sequence.format() + ')'; //$NON-NLS-1$ } return text; } private void updateIcons() { if (!(widget instanceof Item)) { return; } Item item = (Item) widget; String iconURI = model.getIconURI() != null ? model.getIconURI() : ""; //$NON-NLS-1$ String disabledURI = getDisabledIconURI(model); Object disabledData = item.getData(DISABLED_URI); if (disabledData == null) disabledData = ""; //$NON-NLS-1$ if (!iconURI.equals(item.getData(ICON_URI)) || !disabledURI.equals(disabledData)) { LocalResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources()); Image iconImage = getImage(iconURI, resourceManager); item.setImage(iconImage); item.setData(ICON_URI, iconURI); if (item instanceof ToolItem) { iconImage = getImage(disabledURI, resourceManager); ((ToolItem) item).setDisabledImage(iconImage); item.setData(DISABLED_URI, disabledURI); } disposeOldImages(); localResourceManager = resourceManager; } } private String getDisabledIconURI(MItem toolItem) { Object obj = toolItem.getTransientData().get(IPresentationEngine.DISABLED_ICON_IMAGE_KEY); return obj instanceof String ? (String) obj : ""; //$NON-NLS-1$ } private Image getImage(String iconURI, LocalResourceManager resourceManager) { Image image = null; if (iconURI != null && iconURI.length() > 0) { ImageDescriptor iconDescriptor = resUtils.imageDescriptorFromURI(URI.createURI(iconURI)); if (iconDescriptor != null) { try { image = resourceManager.createImage(iconDescriptor); } catch (DeviceResourceException e) { iconDescriptor = ImageDescriptor.getMissingImageDescriptor(); image = resourceManager.createImage(iconDescriptor); // as we replaced the failed icon, log the message once. Activator.trace(Policy.DEBUG_MENUS, "failed to create image " + iconURI, e); //$NON-NLS-1$ } } } return image; } private void disposeOldImages() { if (localResourceManager != null) { localResourceManager.dispose(); localResourceManager = null; } } private Listener getItemListener() { if (menuItemListener == null) { menuItemListener = new Listener() { public void handleEvent(Event event) { switch (event.type) { case SWT.Dispose: handleWidgetDispose(event); break; case SWT.DefaultSelection: case SWT.Selection: if (event.widget != null) { handleWidgetSelection(event); } break; } } }; } return menuItemListener; } private void handleWidgetDispose(Event event) { if (event.widget == widget) { if (unreferenceRunnable != null) { unreferenceRunnable.run(); unreferenceRunnable = null; } unhookCheckListener(); toolItemUpdater.removeItem(this); if (infoContext != null) { infoContext.dispose(); infoContext = null; } widget.removeListener(SWT.Selection, getItemListener()); widget.removeListener(SWT.Dispose, getItemListener()); widget.removeListener(SWT.DefaultSelection, getItemListener()); widget = null; model.setWidget(null); disposeOldImages(); } } /* * (non-Javadoc) * * @see org.eclipse.jface.action.ContributionItem#dispose() */ @Override public void dispose() { if (widget != null) { if (unreferenceRunnable != null) { unreferenceRunnable.run(); unreferenceRunnable = null; } ParameterizedCommand command = model.getWbCommand(); if (command != null) { if (styleState != null) { styleState.removeListener(stateListener); styleState = null; } if (toggleState != null) { toggleState.removeListener(stateListener); toggleState = null; } if (radioState != null) { radioState.removeListener(stateListener); radioState = null; } } widget.dispose(); widget = null; model.setWidget(null); } } private void handleWidgetSelection(Event event) { if (widget != null && !widget.isDisposed()) { if (dropdownEvent(event)) { return; } if (model.getType() == ItemType.CHECK || model.getType() == ItemType.RADIO) { boolean selection = false; if (widget instanceof MenuItem) { selection = ((MenuItem) widget).getSelection(); } else if (widget instanceof ToolItem) { selection = ((ToolItem) widget).getSelection(); } model.setSelected(selection); } if (canExecuteItem(event)) { executeItem(event); } } } /** * @param event * @return */ private boolean dropdownEvent(Event event) { if (event.detail == SWT.ARROW && model instanceof MToolItem) { ToolItem ti = (ToolItem) event.widget; MMenu mmenu = ((MToolItem) model).getMenu(); if (mmenu == null) { return false; } Menu menu = getMenu(mmenu, ti); if (menu == null || menu.isDisposed()) { return true; } Rectangle itemBounds = ti.getBounds(); Point displayAt = ti.getParent().toDisplay(itemBounds.x, itemBounds.y + itemBounds.height); menu.setLocation(displayAt); menu.setVisible(true); Display display = menu.getDisplay(); while (!menu.isDisposed() && menu.isVisible()) { if (!display.readAndDispatch()) { display.sleep(); } } return true; } return false; } protected Menu getMenu(final MMenu mmenu, ToolItem toolItem) { Object obj = mmenu.getWidget(); if (obj instanceof Menu && !((Menu) obj).isDisposed()) { return (Menu) obj; } // this is a temporary passthrough of the IMenuCreator if (mmenu instanceof MRenderedMenu) { obj = ((MRenderedMenu) mmenu).getContributionManager(); if (obj instanceof IContextFunction) { final IEclipseContext lclContext = getContext(mmenu); obj = ((IContextFunction) obj).compute(lclContext); ((MRenderedMenu) mmenu).setContributionManager(obj); } if (obj instanceof IMenuCreator) { final IMenuCreator creator = (IMenuCreator) obj; final Menu menu = creator.getMenu(toolItem.getParent().getShell()); if (menu != null) { toolItem.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { if (menu != null && !menu.isDisposed()) { creator.dispose(); ((MRenderedMenu) mmenu).setWidget(null); } } }); // mmenu.setWidget(menu); menu.setData(AbstractPartRenderer.OWNING_ME, menu); return menu; } } } else { final IEclipseContext lclContext = getContext(model); IPresentationEngine engine = lclContext.get(IPresentationEngine.class); obj = engine.createGui(mmenu, toolItem.getParent(), lclContext); if (obj instanceof Menu) { Menu menu = (Menu) obj; // menu.setData(AbstractPartRenderer.OWNING_ME, menu); return menu; } else { System.err.println("Rendering returned " + obj); //$NON-NLS-1$ } } return null; } private IEclipseContext getStaticContext(Event event) { if (infoContext == null) { infoContext = EclipseContextFactory.create(HCI_STATIC_CONTEXT); ContributionsAnalyzer.populateModelInterfaces(model, infoContext, model.getClass().getInterfaces()); } if (event == null) { infoContext.remove(Event.class); } else { infoContext.set(Event.class, event); } return infoContext; } private void executeItem(Event trigger) { ParameterizedCommand cmd = model.getWbCommand(); if (cmd == null) { return; } final IEclipseContext lclContext = getContext(model); EHandlerService service = (EHandlerService) lclContext.get(EHandlerService.class.getName()); final IEclipseContext staticContext = getStaticContext(trigger); service.executeHandler(cmd, staticContext); } private boolean canExecuteItem(Event trigger) { ParameterizedCommand cmd = model.getWbCommand(); if (cmd == null) { return false; } final IEclipseContext lclContext = getContext(model); EHandlerService service = lclContext.get(EHandlerService.class); if (service == null) { return false; } final IEclipseContext staticContext = getStaticContext(trigger); return service.canExecute(cmd, staticContext); } public void setParent(IContributionManager parent) { if (getParent() instanceof IMenuManager) { IMenuManager menuMgr = (IMenuManager) getParent(); menuMgr.removeMenuListener(menuListener); } if (parent instanceof IMenuManager) { IMenuManager menuMgr = (IMenuManager) parent; menuMgr.addMenuListener(menuListener); } super.setParent(parent); } /** * Return a parent context for this part. * * @param element * the part to start searching from * @return the parent's closest context, or global context if none in the * hierarchy */ protected IEclipseContext getContextForParent(MUIElement element) { return modelService.getContainingContext(element); } /** * Return a context for this part. * * @param part * the part to start searching from * @return the closest context, or global context if none in the hierarchy */ protected IEclipseContext getContext(MUIElement part) { if (part instanceof MContext) { return ((MContext) part).getContext(); } return getContextForParent(part); } public Widget getWidget() { return widget; } }