/******************************************************************************* * Copyright (c) 2005 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 *******************************************************************************/ package org.eclipse.bpel.common.ui.composite; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.eclipse.bpel.common.ui.CommonUIPlugin; import org.eclipse.bpel.common.ui.Messages; import org.eclipse.bpel.common.ui.Utils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.CoolBarManager; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.ICoolBarManager; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IActionBars2; import org.eclipse.ui.IEditorActionBarContributor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IViewSite; import org.eclipse.ui.internal.EditorActionBuilder; import org.eclipse.ui.internal.EditorManager; import org.eclipse.ui.internal.EditorReference; import org.eclipse.ui.internal.EditorSite; import org.eclipse.ui.internal.WorkbenchPage; import org.eclipse.ui.internal.registry.EditorDescriptor; import org.eclipse.ui.services.IServiceLocator; /** * Handles embedded editor's contributions activation and deactivation. */ public class EditorInViewManager { protected WorkbenchPage page; protected IViewSite fViewSite; protected Map<String,EditorDescriptor> idToDescriptor; protected EditorManager eclipseEditorManager; private static final String EXTPT_EMBEDDED_EDITORS = "embeddedEditors"; //$NON-NLS-1$ private static final String CHILD_EDITOR = "editor"; //$NON-NLS-1$ private static final String ATT_EDITOR_ID = "id"; //$NON-NLS-1$ private static final String ATT_EDITOR_CLASS = "class"; //$NON-NLS-1$ /** * Brand new EditorInView manager. * * @param viewSite */ public EditorInViewManager (IViewSite viewSite) { this.fViewSite = viewSite; idToDescriptor = new HashMap<String,EditorDescriptor>(10); page = (WorkbenchPage) viewSite.getPage(); } /** * Creates and initializes an IEditorPart given its id and input. * @param editorId * @param input * @return the editor part. * @throws CoreException */ public IEditorPart createEditor(String editorId, IEditorInput input) throws CoreException { IEditorPart editor = instantiateEditor(editorId); EditorSite site = createEditorSite(editorId, input, editor); editor.init(site, input); // Force menus to be created. // TODO: The following lines do not seem to be necessary anymore. // Keep them at least until Eclipse 3.0 is released so that if we // face the same problem again we can workaround it. // IMenuManager menuManager = site.getActionBars().getMenuManager(); // menuManager.setVisible(false); // menuManager.setVisible(true); return editor; } /** * Instantiates an editor given its id. The editor id has to be declared * through the <code>embeddedEditors</code> extension point. * * @throws CoreException if the editorId is not found */ protected IEditorPart instantiateEditor (String editorId) throws CoreException { EditorDescriptor descriptor = idToDescriptor.get(editorId); if (descriptor == null) { descriptor = readDescriptor(editorId); if (descriptor == null) { IStatus status = new Status(IStatus.ERROR, CommonUIPlugin.PLUGIN_ID, ICompositeEditorConstants.COULD_NOT_INSTANTIATE_EDITOR, NLS.bind(Messages.CompositeEditorManager_Could_not_find_editor, (new Object[] { editorId })), null); throw new CoreException(status); } } return (IEditorPart) descriptor.getConfigurationElement().createExecutableExtension(ATT_EDITOR_CLASS); } /** * Creates an editor descriptor based on the information defined in the <code>embeddedEditors</code> * extension point for the given editorId. * * @return the editor descriptor or <code>null</code> if there none for the given editorId */ protected EditorDescriptor readDescriptor (String editorId) { for(IConfigurationElement element : Utils.getConfigurationElements(EXTPT_EMBEDDED_EDITORS)) { if (CHILD_EDITOR.equals(element.getName()) == false) { continue; } String extensionId = element.getAttribute(ATT_EDITOR_ID); if (extensionId == null) { // the editor id is mandatory - log error String pluginId = element.getDeclaringExtension().getNamespace(); IStatus status = new Status(IStatus.ERROR, pluginId, ICompositeEditorConstants.MISSING_ATTRIBUTE, Messages.CompositeEditorManager_5, null); CommonUIPlugin.getDefault().getLog().log(status); continue; } if (editorId.equals(extensionId)) { // Use reflection to create and set state on an editor descriptor // This was done to avoid copying the class to this package try { Constructor constuctor = EditorDescriptor.class.getDeclaredConstructor(new Class[0]); constuctor.setAccessible(true); EditorDescriptor descriptor = (EditorDescriptor)constuctor.newInstance(new Object[0]); Method method = descriptor.getClass().getDeclaredMethod("setID", new Class[] {String.class}); method.setAccessible(true); method.invoke(descriptor, new Object[]{editorId}); method = descriptor.getClass().getDeclaredMethod("setConfigurationElement", new Class[] {IConfigurationElement.class}); method.setAccessible(true); method.invoke(descriptor, new Object[]{element}); method = descriptor.getClass().getDeclaredMethod("setName", new Class[] {String.class}); method.setAccessible(true); method.invoke(descriptor, new Object[]{editorId}); idToDescriptor.put(editorId, descriptor); return descriptor; } catch (SecurityException e) { CommonUIPlugin.getDefault().createErrorStatus("Problem creating descriptor for " + editorId, e, true); } catch (IllegalArgumentException e) { CommonUIPlugin.getDefault().createErrorStatus("Problem creating descriptor for " + editorId, e, true); } catch (NoSuchMethodException e) { CommonUIPlugin.getDefault().createErrorStatus("Problem creating descriptor for " + editorId, e, true); } catch (InstantiationException e) { CommonUIPlugin.getDefault().createErrorStatus("Problem creating descriptor for " + editorId, e, true); } catch (IllegalAccessException e) { CommonUIPlugin.getDefault().createErrorStatus("Problem creating descriptor for " + editorId, e, true); } catch (InvocationTargetException e) { CommonUIPlugin.getDefault().createErrorStatus("Problem creating descriptor for " + editorId, e, true); } } } return null; } /** * Creates and populates an editor site for the given editor. */ protected EditorSite createEditorSite(String editorId, IEditorInput input, IEditorPart editor) { EditorDescriptor descriptor = idToDescriptor.get(editorId); IEditorReference ref = new EditorReference(getEditorManager(), input, descriptor); EditorSite site = new EditorInViewSite(fViewSite, ref, editor, page, descriptor); IEditorActionBarContributor contributor = descriptor.createActionBarContributor(); site.setActionBars(createEditorActionBars(descriptor, contributor, site)); return site; } /** * Creates the action bars for the given editor. It also searches for editor * contribution extensions. */ protected CompositeEditorActionBars createEditorActionBars(EditorDescriptor descriptor, IEditorActionBarContributor contributor, EditorSite site) { String type = String.valueOf(System.currentTimeMillis()); // IActionBars viewActionBars = viewSite.getActionBars(); // IActionBars2 parentActionBars = getIActionBars2Wrapper(viewActionBars); CompositeEditorActionBars actionBars = new CompositeEditorActionBars(page, site, type); if (contributor != null) { actionBars.setEditorContributor(contributor); contributor.init(actionBars, page); } // Read action extensions. EditorActionBuilder builder = new EditorActionBuilder(); IEditorActionBarContributor extension = builder.readActionExtensions(descriptor /*WDG , actionBars*/); if (extension != null) { actionBars.setExtensionContributor(extension); extension.init(actionBars, page); } return actionBars; } /** * Creates an IActionBars2 as a wrapper of an IActionBars. */ protected IActionBars2 getIActionBars2Wrapper(final IActionBars actionBars) { return new IActionBars2() { public ICoolBarManager getCoolBarManager() { return new CoolBarManager(); } public void clearGlobalActionHandlers() { actionBars.clearGlobalActionHandlers(); } public IAction getGlobalActionHandler(String actionId) { return actionBars.getGlobalActionHandler(actionId); } public IMenuManager getMenuManager() { // Returns the page menu manager so that the editor // can contribute to any menu (Edit, Navigate, etc...). return page.getActionBars().getMenuManager(); } public IStatusLineManager getStatusLineManager() { return actionBars.getStatusLineManager(); } public IToolBarManager getToolBarManager() { return actionBars.getToolBarManager(); } public void setGlobalActionHandler(String actionId, IAction handler) { actionBars.setGlobalActionHandler(actionId, handler); } public void updateActionBars() { actionBars.updateActionBars(); } public IServiceLocator getServiceLocator() { return actionBars.getServiceLocator(); } }; } /** * Activate contributions. * @param editor */ public void activate(IEditorPart editor) { editor.setFocus(); IEditorSite site = editor.getEditorSite(); CompositeEditorActionBars actionBars = (CompositeEditorActionBars) site.getActionBars(); actionBars.partChanged(editor); actionBars.activate(true); ((EditorInViewSite) site).activate(); } /** * Deactivate contributions. * @param editor */ public void deactivate(IEditorPart editor) { if (editor != null) { CompositeEditorActionBars actionBars = (CompositeEditorActionBars) editor.getEditorSite().getActionBars(); actionBars.deactivate(true); IEditorSite site = editor.getEditorSite(); ((EditorInViewSite) site).deactivate(); } } /** * Creates an embedded editor corresponding to the given editorId. * <p> * It is important to notice that the returned editor will never be * the workbench active part. * <p> * @param editorId the embedded editor id * @param editorComposite the editor's parent composite * @return IEditorPart the embedded editor instance * */ public IEditorPart createEditor(String editorId, IEditorInput input, Composite editorComposite) throws CoreException { final IEditorPart editor = createEditor(editorId, input); Composite composite = new Composite(editorComposite, SWT.NONE); composite.setLayout(new FillLayout()); editor.createPartControl(composite); // This is the fundamental part of our implementation. // Editors are activated when we get an Activate // event from the editor's composite. associate(editor, composite); return editor; } /** * Associates the control with the given editor. When the control * is activated all the editor contributions will be activated * as well. This is useful when the CompositeEditor is not only * composed by embedded editor but also by other controls. If no editor * is associated with these controls the available contributions * (menus, toolbars, status line, etc...) will be the contributions * for the last active editor and might cause confusion to the user. */ protected void associate(final IEditorPart editor, Control control) { control.addListener(SWT.Activate, new Listener() { public void handleEvent(Event event) { activate(editor); } }); control.addListener(SWT.Deactivate, new Listener() { public void handleEvent(Event event) { deactivate(editor); } }); } /** * Removes the given editor from this composite editor. If the given * editor is the current active editor the main editor will be activated * instead. The main editor cannot be disconnected by calling this * method. * * @param editor */ public void disposeEditor(IEditorPart editor) { EditorInViewSite site = (EditorInViewSite) editor.getSite(); CompositeEditorActionBars actionBars = (CompositeEditorActionBars) site.getActionBars(); actionBars.dispose(); site.dispose(); try { editor.dispose(); } catch (Throwable t) { System.out.println("disposeEditor: " + editor); } } /** * TODO: remove this internal access */ public EditorManager getEditorManager() { if (eclipseEditorManager == null) { try { Field field = WorkbenchPage.class.getDeclaredField("editorMgr"); //$NON-NLS-1$ field.setAccessible(true); eclipseEditorManager = (EditorManager) field.get(page); } catch (Exception e) { CommonUIPlugin.log(e); } } return eclipseEditorManager; } }