| /******************************************************************************* |
| * Copyright (c) 2008, 2009 BestSolution.at 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: |
| * Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation |
| * IBM Corporation - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.e4.workbench.ui.internal; |
| |
| import java.io.IOException; |
| import java.net.URLConnection; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import org.eclipse.core.commands.Category; |
| import org.eclipse.core.commands.IParameter; |
| import org.eclipse.core.commands.ParameterizedCommand; |
| import org.eclipse.core.commands.contexts.ContextManager; |
| import org.eclipse.core.internal.runtime.PlatformURLPluginConnection; |
| import org.eclipse.core.runtime.IAdapterManager; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.RegistryFactory; |
| import org.eclipse.e4.core.commands.ECommandService; |
| import org.eclipse.e4.core.commands.EHandlerService; |
| import org.eclipse.e4.core.services.IContributionFactory; |
| import org.eclipse.e4.core.services.Logger; |
| import org.eclipse.e4.core.services.context.ContextChangeEvent; |
| import org.eclipse.e4.core.services.context.EclipseContextFactory; |
| import org.eclipse.e4.core.services.context.IEclipseContext; |
| import org.eclipse.e4.core.services.context.IRunAndTrack; |
| import org.eclipse.e4.core.services.context.spi.ContextFunction; |
| import org.eclipse.e4.core.services.context.spi.ContextInjectionFactory; |
| import org.eclipse.e4.core.services.context.spi.IContextConstants; |
| import org.eclipse.e4.ui.bindings.EBindingService; |
| import org.eclipse.e4.ui.bindings.TriggerSequence; |
| import org.eclipse.e4.ui.internal.services.ActiveContextsFunction; |
| import org.eclipse.e4.ui.model.application.MApplication; |
| import org.eclipse.e4.ui.model.application.MApplicationElement; |
| import org.eclipse.e4.ui.model.application.MApplicationFactory; |
| import org.eclipse.e4.ui.model.application.MBindingContainer; |
| import org.eclipse.e4.ui.model.application.MCommand; |
| import org.eclipse.e4.ui.model.application.MCommandParameter; |
| import org.eclipse.e4.ui.model.application.MContext; |
| import org.eclipse.e4.ui.model.application.MElementContainer; |
| import org.eclipse.e4.ui.model.application.MHandler; |
| import org.eclipse.e4.ui.model.application.MHandlerContainer; |
| import org.eclipse.e4.ui.model.application.MKeyBinding; |
| import org.eclipse.e4.ui.model.application.MPSCElement; |
| import org.eclipse.e4.ui.model.application.MParameter; |
| import org.eclipse.e4.ui.model.application.MPart; |
| import org.eclipse.e4.ui.model.application.MUIElement; |
| import org.eclipse.e4.ui.model.application.MWindow; |
| import org.eclipse.e4.ui.services.IServiceConstants; |
| import org.eclipse.e4.ui.services.IStylingEngine; |
| import org.eclipse.e4.ui.services.events.IEventBroker; |
| import org.eclipse.e4.workbench.modeling.EPartService; |
| import org.eclipse.e4.workbench.ui.IExceptionHandler; |
| import org.eclipse.e4.workbench.ui.IPresentationEngine; |
| import org.eclipse.e4.workbench.ui.IWorkbench; |
| import org.eclipse.e4.workbench.ui.IWorkbenchWindowHandler; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.URIConverter; |
| import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; |
| import org.eclipse.osgi.service.datalocation.Location; |
| import org.osgi.framework.Bundle; |
| import org.osgi.service.packageadmin.PackageAdmin; |
| |
| public class Workbench implements IWorkbench { |
| public static final String LOCAL_ACTIVE_SHELL = "localActiveShell"; //$NON-NLS-1$ |
| public static final String ID = "org.eclipse.e4.workbench.fakedWBWindow"; //$NON-NLS-1$ |
| private MApplication workbench; |
| private static final boolean saveAndRestore = true; |
| private IWorkbenchWindowHandler windowHandler; |
| |
| public IEclipseContext getContext() { |
| return workbenchContext; |
| } |
| |
| // UI Construction... |
| private IPresentationEngine renderer; |
| private int rv; |
| |
| private ExceptionHandler exceptionHandler; |
| private IEclipseContext workbenchContext; |
| private ReflectionContributionFactory contributionFactory; |
| private String renderingEngineURI; |
| private ResourceHandler handler; |
| private Location instanceLocation; |
| |
| public Workbench(Location instanceLocation, IExtensionRegistry registry, |
| PackageAdmin packageAdmin, IEclipseContext applicationContext, |
| IWorkbenchWindowHandler windowHandler, String renderingEngineURI) { |
| // System.err.println("NEw Workbenc"); //$NON-NLS-1$ |
| this.windowHandler = windowHandler; |
| this.renderingEngineURI = renderingEngineURI; |
| this.instanceLocation = instanceLocation; |
| |
| exceptionHandler = new ExceptionHandler(); |
| contributionFactory = new ReflectionContributionFactory(registry); |
| |
| workbenchContext = createWorkbenchContext(applicationContext, registry, exceptionHandler, |
| contributionFactory); |
| workbenchContext.set(Workbench.class.getName(), this); |
| workbenchContext.set(IWorkbench.class.getName(), this); |
| workbenchContext.set(IExtensionRegistry.class.getName(), registry); |
| workbenchContext.set(IContributionFactory.class.getName(), contributionFactory); |
| workbenchContext.set(IEclipseContext.class.getName(), workbenchContext); |
| } |
| |
| public void setWorkbenchModel(MApplication model) { |
| workbench = model; |
| init(); |
| } |
| |
| // this could be it |
| // and that's all, folks |
| |
| public void setWorkbenchModelURI(URI workbenchXmiURI) { |
| createWorkbenchModel(workbenchXmiURI); |
| } |
| |
| public static IEclipseContext createWorkbenchContext(final IEclipseContext applicationContext, |
| IExtensionRegistry registry, IExceptionHandler exceptionHandler, |
| IContributionFactory contributionFactory) { |
| Activator |
| .trace( |
| Policy.DEBUG_CONTEXTS, |
| "createWorkbenchContext: initialize the workbench context with needed services", null); //$NON-NLS-1$ |
| final IEclipseContext mainContext = EclipseContextFactory.create(applicationContext, |
| UISchedulerStrategy.getInstance()); |
| mainContext.set(Logger.class.getName(), ContextInjectionFactory.inject( |
| new WorkbenchLogger(), mainContext)); |
| mainContext.set(IContextConstants.DEBUG_STRING, "WorkbenchContext"); //$NON-NLS-1$ |
| |
| // setup for commands and handlers |
| if (contributionFactory != null) { |
| mainContext.set(IContributionFactory.class.getName(), contributionFactory); |
| } |
| mainContext.set(ContextManager.class.getName(), new ContextManager()); |
| |
| mainContext.set(IServiceConstants.ACTIVE_CONTEXTS, new ActiveContextsFunction()); |
| mainContext.set(IServiceConstants.ACTIVE_PART, new ActivePartLookupFunction()); |
| mainContext.runAndTrack(new IRunAndTrack() { |
| public boolean notify(ContextChangeEvent event) { |
| Object o = mainContext.get(IServiceConstants.ACTIVE_PART); |
| if (o instanceof MPart) { |
| mainContext.set(IServiceConstants.ACTIVE_PART_ID, ((MPart) o).getId()); |
| } |
| return true; |
| } |
| |
| /* |
| * For debugging purposes only |
| */ |
| @Override |
| public String toString() { |
| return IServiceConstants.ACTIVE_PART_ID; |
| } |
| }, null); |
| // EHandlerService comes from a ContextFunction |
| // EContextService comes from a ContextFunction |
| mainContext.set(IExceptionHandler.class.getName(), exceptionHandler); |
| mainContext.set(IExtensionRegistry.class.getName(), registry); |
| mainContext.set(IServiceConstants.INPUT, new ContextFunction() { |
| public Object compute(IEclipseContext context, Object[] arguments) { |
| Class adapterType = null; |
| if (arguments.length > 0 && arguments[0] instanceof Class) { |
| adapterType = (Class) arguments[0]; |
| } |
| Object newInput = null; |
| Object newValue = context.get(IServiceConstants.SELECTION); |
| if (adapterType == null || adapterType.isInstance(newValue)) { |
| newInput = newValue; |
| } else if (newValue != null && adapterType != null) { |
| IAdapterManager adapters = (IAdapterManager) applicationContext |
| .get(IAdapterManager.class.getName()); |
| if (adapters != null) { |
| Object adapted = adapters.loadAdapter(newValue, adapterType.getName()); |
| if (adapted != null) { |
| newInput = adapted; |
| } |
| } |
| } |
| return newInput; |
| } |
| }); |
| mainContext.set(IServiceConstants.ACTIVE_SHELL, new ActiveChildLookupFunction( |
| IServiceConstants.ACTIVE_SHELL, LOCAL_ACTIVE_SHELL)); |
| |
| initializeNullStyling(mainContext); |
| |
| return mainContext; |
| } |
| |
| private MApplication createWorkbenchModel(URI applicationDefinitionInstance) { |
| handler = new ResourceHandler(instanceLocation, applicationDefinitionInstance, |
| saveAndRestore, (Logger) getContext().get(Logger.class.getName())); |
| |
| long restoreLastModified = handler.getLastStoreDatetime(); |
| long lastApplicationModification = getLastApplicationModification(applicationDefinitionInstance); |
| |
| boolean restore = restoreLastModified > lastApplicationModification; |
| |
| Resource resource; |
| if (restore) { |
| resource = handler.loadRestoredModel(); |
| workbench = (MApplication) resource.getContents().get(0); |
| } else { |
| resource = handler.loadBaseModel(); |
| MApplication app = (MApplication) resource.getContents().get(0); |
| |
| final EList<MWindow> windows = app.getChildren(); |
| for (MWindow window : windows) { |
| processPartContributions(resource, window); |
| } |
| |
| workbench = app; |
| } |
| |
| init(); |
| |
| return workbench; |
| } |
| |
| private long getLastApplicationModification(URI applicationDefinitionInstance) { |
| long appLastModified = 0L; |
| ResourceSetImpl resourceSetImpl = new ResourceSetImpl(); |
| |
| Map<String, ?> attributes = resourceSetImpl.getURIConverter().getAttributes( |
| applicationDefinitionInstance, |
| Collections.singletonMap(URIConverter.OPTION_REQUESTED_ATTRIBUTES, Collections |
| .singleton(URIConverter.ATTRIBUTE_TIME_STAMP))); |
| |
| Object timestamp = attributes.get(URIConverter.ATTRIBUTE_TIME_STAMP); |
| if (timestamp instanceof Long) { |
| appLastModified = ((Long) timestamp).longValue(); |
| } else if (applicationDefinitionInstance.isPlatformPlugin()) { |
| try { |
| java.net.URL url = new java.net.URL(applicationDefinitionInstance.toString()); |
| Object[] obj = PlatformURLPluginConnection.parse(url.getFile().trim(), url); |
| Bundle b = (Bundle) obj[0]; |
| URLConnection openConnection = b.getResource((String) obj[1]).openConnection(); |
| appLastModified = openConnection.getLastModified(); |
| } catch (Exception e) { |
| // ignore |
| } |
| } |
| |
| return appLastModified; |
| } |
| |
| private void processPartContributions(Resource resource, MWindow mWindow) { |
| IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| String extId = "org.eclipse.e4.workbench.parts"; //$NON-NLS-1$ |
| IConfigurationElement[] parts = registry.getConfigurationElementsFor(extId); |
| |
| for (int i = 0; i < parts.length; i++) { |
| MPart part = MApplicationFactory.eINSTANCE.createPart(); |
| part.setLabel(parts[i].getAttribute("label")); //$NON-NLS-1$ |
| part.setIconURI("platform:/plugin/" //$NON-NLS-1$ |
| + parts[i].getContributor().getName() + "/" //$NON-NLS-1$ |
| + parts[i].getAttribute("icon")); //$NON-NLS-1$ |
| part.setURI("platform:/plugin/" //$NON-NLS-1$ |
| + parts[i].getContributor().getName() + "/" //$NON-NLS-1$ |
| + parts[i].getAttribute("class")); //$NON-NLS-1$ |
| String parentId = parts[i].getAttribute("parentId"); //$NON-NLS-1$ |
| |
| Object parent = findObject(resource.getAllContents(), parentId); |
| if (parent instanceof MElementContainer<?>) { |
| ((MElementContainer<MPSCElement>) parent).getChildren().add(part); |
| } |
| } |
| |
| } |
| |
| private EObject findObject(TreeIterator<EObject> it, String id) { |
| while (it.hasNext()) { |
| EObject el = it.next(); |
| if (el instanceof MApplicationElement) { |
| if (el.eResource().getURIFragment(el).equals(id)) { |
| return el; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private void init() { |
| Activator.trace(Policy.DEBUG_WORKBENCH, "init() workbench", null); //$NON-NLS-1$ |
| // Capture the MApplication into the context |
| workbenchContext.set(MApplication.class.getName(), workbench); |
| workbench.setContext(workbenchContext); |
| |
| // fill in commands |
| Activator.trace(Policy.DEBUG_CMDS, "Initialize service from model", null); //$NON-NLS-1$ |
| ECommandService cs = (ECommandService) workbenchContext |
| .get(ECommandService.class.getName()); |
| Category cat = cs |
| .defineCategory(MApplication.class.getName(), "Application Category", null); //$NON-NLS-1$ |
| EList<MCommand> commands = workbench.getCommands(); |
| for (MCommand cmd : commands) { |
| IParameter[] parms = null; |
| String id = cmd.getId(); |
| String name = cmd.getCommandName(); |
| EList<MCommandParameter> modelParms = cmd.getParameters(); |
| if (modelParms != null && !modelParms.isEmpty()) { |
| ArrayList<Parameter> parmList = new ArrayList<Parameter>(); |
| for (MCommandParameter cmdParm : modelParms) { |
| parmList.add(new Parameter(cmdParm.getId(), cmdParm.getName(), null, null, |
| cmdParm.isOptional())); |
| } |
| parms = parmList.toArray(new Parameter[parmList.size()]); |
| } |
| cs.defineCommand(id, name, null, cat, parms); |
| } |
| |
| // take care of generating the contexts. |
| EList<MWindow> windows = workbench.getChildren(); |
| for (MWindow window : windows) { |
| initializeContext(workbenchContext, window); |
| } |
| |
| processHierarchy(workbench); |
| // Hook the global notifications |
| ((Notifier) workbench).eAdapters().add(new UIEventPublisher(workbench.getContext())); |
| } |
| |
| /** |
| * Create the context chain. It both creates the chain for the current model, and adds eAdapters |
| * so it can add new contexts when new model items are added. |
| * |
| * @param parentContext |
| * The parent context |
| * @param contextModel |
| * needs a context created |
| */ |
| public static IEclipseContext initializeContext(IEclipseContext parentContext, |
| MContext contextModel) { |
| final IEclipseContext context; |
| if (contextModel.getContext() != null) { |
| context = contextModel.getContext(); |
| } else { |
| context = EclipseContextFactory |
| .create(parentContext, UISchedulerStrategy.getInstance()); |
| context.set(IContextConstants.DEBUG_STRING, "PartContext(" + contextModel + ')'); //$NON-NLS-1$ |
| } |
| |
| Activator.trace(Policy.DEBUG_CONTEXTS, "initializeContext(" //$NON-NLS-1$ |
| + parentContext.toString() + ", " + contextModel + ")", null); //$NON-NLS-1$ //$NON-NLS-2$ |
| // fill in the interfaces, so MContributedPart.class.getName() will |
| // return the model element, for example. |
| final Class[] interfaces = contextModel.getClass().getInterfaces(); |
| for (Class intf : interfaces) { |
| Activator.trace(Policy.DEBUG_CONTEXTS, "Adding " + intf.getName() + " for " //$NON-NLS-1$ //$NON-NLS-2$ |
| + contextModel.getClass().getName(), null); |
| context.set(intf.getName(), contextModel); |
| } |
| |
| // declares modifiable variables from the model |
| EList<String> containedProperties = contextModel.getVariables(); |
| for (String name : containedProperties) { |
| context.declareModifiable(name); |
| } |
| |
| contextModel.setContext(context); |
| return context; |
| } |
| |
| /** |
| * Should be called prior to running the e4 workench. |
| */ |
| public void createUIFromModel() { |
| EList<MWindow> windows = workbench.getChildren(); |
| for (MWindow wbw : windows) { |
| createGUI(wbw); |
| } |
| } |
| |
| public int run() { |
| Activator.trace(Policy.DEBUG_WORKBENCH, "running event loop", null); //$NON-NLS-1$ |
| windowHandler.runEvenLoop(workbench.getChildren().get(0).getWidget()); |
| |
| if (handler != null && saveAndRestore && workbench != null) { |
| try { |
| Activator.trace(Policy.DEBUG_WORKBENCH, "Saving workbench: " //$NON-NLS-1$ |
| + ((EObject) workbench).eResource().getURI(), null); |
| handler.save(); |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| return rv; |
| } |
| |
| public static void processHierarchy(Object me) { |
| if (me instanceof MApplication) { |
| MContext contextAware = (MContext) me; |
| IEclipseContext c = contextAware.getContext(); |
| IEventBroker broker = (IEventBroker) c.get(IEventBroker.class.getName()); |
| PartServiceImpl.addListener(broker); |
| } |
| |
| if (me instanceof MHandlerContainer) { |
| MContext contextModel = (MContext) me; |
| MHandlerContainer container = (MHandlerContainer) contextModel; |
| IEclipseContext context = contextModel.getContext(); |
| if (context != null) { |
| IContributionFactory cf = (IContributionFactory) context |
| .get(IContributionFactory.class.getName()); |
| EHandlerService hs = (EHandlerService) context.get(EHandlerService.class.getName()); |
| EList<MHandler> handlers = container.getHandlers(); |
| for (MHandler handler : handlers) { |
| String commandId = handler.getCommand().getId(); |
| if (handler.getObject() == null) { |
| handler.setObject(cf.create(handler.getURI(), context)); |
| } |
| hs.activateHandler(commandId, handler.getObject()); |
| } |
| } |
| } |
| if (me instanceof MBindingContainer) { |
| MContext contextModel = (MContext) me; |
| MBindingContainer container = (MBindingContainer) me; |
| IEclipseContext context = contextModel.getContext(); |
| if (context != null) { |
| ECommandService cs = (ECommandService) context.get(ECommandService.class.getName()); |
| EBindingService bs = (EBindingService) context.get(EBindingService.class.getName()); |
| EList<MKeyBinding> bindings = container.getBindings(); |
| for (MKeyBinding binding : bindings) { |
| Map<String, Object> parameters = null; |
| EList<MParameter> modelParms = binding.getParameters(); |
| if (modelParms != null && !modelParms.isEmpty()) { |
| parameters = new HashMap<String, Object>(); |
| for (MParameter mParm : modelParms) { |
| parameters.put(mParm.getTag(), mParm.getValue()); |
| } |
| } |
| ParameterizedCommand cmd = cs.createCommand(binding.getCommand().getId(), |
| parameters); |
| TriggerSequence sequence = bs.createSequence(binding.getKeySequence()); |
| if (cmd == null || sequence == null) { |
| System.err.println("Failed to handle binding: " + binding); //$NON-NLS-1$ |
| } else { |
| bs.activateBinding(sequence, cmd); |
| } |
| } |
| } |
| } |
| if (me instanceof MWindow) { |
| MContext contextAware = (MContext) me; |
| IEclipseContext c = contextAware.getContext(); |
| if (c != null) { |
| c.set(EPartService.PART_SERVICE_ROOT, me); |
| } |
| } |
| if (me instanceof MElementContainer<?>) { |
| EList children = ((MElementContainer) me).getChildren(); |
| Iterator i = children.iterator(); |
| while (i.hasNext()) { |
| MUIElement e = (MUIElement) i.next(); |
| processHierarchy(e); |
| } |
| } |
| } |
| |
| public void createGUI(MUIElement uiRoot) { |
| if (renderer == null) { |
| Object newEngine = contributionFactory.create(renderingEngineURI, workbenchContext); |
| if (newEngine != null) { |
| renderer = (IPresentationEngine) newEngine; |
| } |
| } |
| |
| renderer.createGui(uiRoot); |
| if (uiRoot instanceof MWindow) { |
| MWindow wbw = (MWindow) uiRoot; |
| Object appWindow = wbw.getWidget(); |
| rv = 0; |
| |
| // TODO get access to IApplicationContext to call |
| // applicationRunning() |
| // Platform.endSplash(); |
| |
| // A position of 0 is not possible on OS-X because then the |
| // title-bar is |
| // hidden |
| // below the MMenu-Bar |
| windowHandler.setBounds(appWindow, wbw.getX(), wbw.getY(), wbw.getWidth(), wbw |
| .getHeight()); |
| windowHandler.layout(appWindow); |
| |
| windowHandler.open(appWindow); |
| |
| // NMH: how do we do this now ? |
| // processHandlers(wbw); |
| } |
| } |
| |
| public boolean close() { |
| EList<MWindow> windows = workbench.getChildren(); |
| for (MWindow window : windows) { |
| windowHandler.dispose(window.getWidget()); |
| } |
| return true; |
| } |
| |
| // public Display getDisplay() { |
| // return appWindow.getDisplay(); |
| // } |
| |
| public void closeWindow(MWindow workbenchWindow) { |
| windowHandler.close(workbenchWindow.getWidget()); |
| } |
| |
| public Object getWindow() { |
| return workbench.getChildren().get(0).getWidget(); |
| } |
| |
| /* |
| * For use when there is no real styling engine present. Has no behaviour but conforms to |
| * IStylingEngine API. |
| * |
| * @param appContext |
| */ |
| private static void initializeNullStyling(IEclipseContext appContext) { |
| appContext.set(IStylingEngine.SERVICE_NAME, new IStylingEngine() { |
| public void setClassname(Object widget, String classname) { |
| } |
| |
| public void setId(Object widget, String id) { |
| } |
| |
| public void style(Object widget) { |
| } |
| }); |
| } |
| |
| public MApplication getModel() { |
| return workbench; |
| } |
| } |