diff options
Diffstat (limited to 'plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org')
124 files changed, 16940 insertions, 0 deletions
diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/Activator.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/Activator.java new file mode 100644 index 00000000000..a744cf9db8d --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/Activator.java @@ -0,0 +1,99 @@ +/***************************************************************************** + * Copyright (c) 2011, 2016 CEA LIST, Christian W. Damus, 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: + * Francois Le Fevre (CEA LIST) francois.le-fevre@cea.fr - Initial API and implementation + * Christian W. Damus = bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui; + +import org.eclipse.papyrus.infra.core.log.LogHelper; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.services.spi.IContextualServiceRegistryTracker; +import org.eclipse.papyrus.infra.tools.spi.IExecutorServiceFactory; +import org.eclipse.papyrus.infra.ui.util.UIUtil; +import org.eclipse.papyrus.infra.ui.util.WorkbenchPartHelper; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + /** + * The plug-in ID + */ + public static final String PLUGIN_ID = "org.eclipse.papyrus.infra.ui"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The plug-in's logger + */ + public static LogHelper log; + + private ServiceRegistration<IExecutorServiceFactory> executorFactoryReg; + private ServiceRegistration<IContextualServiceRegistryTracker> serviceRegistryTrackerReg; + + /** + * The constructor + */ + public Activator() { + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + log = new LogHelper(this); + + IExecutorServiceFactory executorFactory = () -> UIUtil.createUIExecutor(Display.getDefault()); + executorFactoryReg = context.registerService(IExecutorServiceFactory.class, executorFactory, null); + + IContextualServiceRegistryTracker serviceRegistryTracker = () -> { + ServicesRegistry result = null; + IEditorPart editor = WorkbenchPartHelper.getCurrentActiveEditorPart(); + if (editor != null) { + result = editor.getAdapter(ServicesRegistry.class); + } + return result; + }; + serviceRegistryTrackerReg = context.registerService(IContextualServiceRegistryTracker.class, serviceRegistryTracker, null); + } + + @Override + public void stop(BundleContext context) throws Exception { + if (serviceRegistryTrackerReg != null) { + serviceRegistryTrackerReg.unregister(); + serviceRegistryTrackerReg = null; + } + if (executorFactoryReg != null) { + executorFactoryReg.unregister(); + executorFactoryReg = null; + } + + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/command/AbstractCommandHandler.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/command/AbstractCommandHandler.java new file mode 100644 index 00000000000..9145151e8a0 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/command/AbstractCommandHandler.java @@ -0,0 +1,189 @@ +/***************************************************************************** + * Copyright (c) 2010, 2016 CEA LIST, Christian W. Damus, 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: + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation + * Vincent Lorenzo (CEA-LIST) vincent.lorenzo@cea.fr + * Christian W. Damus (CEA) - Refactoring package/profile import/apply UI for CDO + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.command; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.emf.utils.EMFHelper; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.util.ServiceUtilsForHandlers; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * <pre> + * + * This abstract command handler manages: + * - current selection in order to build a list of the selected {@link EObject} + * - execute the command (returned by children) in Papyrus {@link TransactionalEditingDomain} + * - calculate the command enablement and visibility regarding the command executability + * (the command is now shown in menu if not executable). + * + * </pre> + */ +public abstract class AbstractCommandHandler extends AbstractPapyrusHandler { + + private List<?> selection = Collections.EMPTY_LIST; + + /** + * Returns the command to execute (to be implemented + * in children implementing this class) + * + * @param context + * the command evaluation context + * + * @return the command to execute + */ + protected abstract Command getCommand(IEvaluationContext context); + + protected Command getCommand(ExecutionEvent event) { + Command result = null; + + Object context = event.getApplicationContext(); + if (context instanceof IEvaluationContext) { + result = getCommand((IEvaluationContext) context); + } else { + throw new IllegalArgumentException("No evaluation context in execution event: " + event); //$NON-NLS-1$ + } + + return result; + } + + protected List<?> getSelection() { + return selection; + } + + /** + * <pre> + * Get the selected element, the first selected element if several are selected or null + * if no selection or the selection is not an {@link EObject}. + * + * @return selected {@link EObject} or null + * </pre> + * + */ + protected EObject getSelectedElement() { + EObject eObject = null; + + // Get current selection + List<?> selection = getSelection(); + + // Treat non-null selected object (try to adapt and return EObject) + if (!selection.isEmpty()) { + + // Get first element if the selection is an IStructuredSelection + Object first = selection.get(0); + + EObject businessObject = EMFHelper.getEObject(first); + if (businessObject != null) { + eObject = businessObject; + } + } + + return eObject; + } + + /** + * <pre> + * Parse current selection and extract the list of {@link EObject} from + * this selection. + * + * This also tries to adapt selected element into {@link EObject} + * (for example to get the {@link EObject} from a selection in the ModelExplorer). + * + * @return a list of currently selected {@link EObject} + * </pre> + * + */ + protected List<EObject> getSelectedElements() { + + List<EObject> selectedEObjects = new ArrayList<EObject>(); + + // Get current selection + Collection<?> selection = getSelection(); + + // Treat non-null selected object (try to adapt and return EObject) + if (!selection.isEmpty()) { + + // Parse current selection + for (Object current : selection) { + // Adapt current selection to EObject + EObject selectedEObject = EMFHelper.getEObject(current); + if (selectedEObject != null) { + // we avoid to add null element in the list! + selectedEObjects.add(selectedEObject); + } + } + } + + return selectedEObjects; + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + try { + ISelection selection = HandlerUtil.getCurrentSelection(event); + this.selection = (selection instanceof IStructuredSelection) ? ((IStructuredSelection) selection).toList() : Collections.EMPTY_LIST; + + ServiceUtilsForHandlers.getInstance().getTransactionalEditingDomain(event).getCommandStack().execute(getCommand(event)); + } catch (ServiceException e) { + Activator.log.error("Unexpected error while executing command.", e); //$NON-NLS-1$ + } finally { + // clear the selection + this.selection = Collections.EMPTY_LIST; + } + + return null; + } + + protected boolean computeEnabled(IEvaluationContext context) { + boolean result = false; + + Command command = getCommand(context); + if (command != null) { + result = command.canExecute(); + command.dispose(); + } + + return result; + } + + @Override + public void setEnabled(Object evaluationContext) { + if (evaluationContext instanceof IEvaluationContext) { + IEvaluationContext context = (IEvaluationContext) evaluationContext; + + Object selection = ((IEvaluationContext) evaluationContext).getDefaultVariable(); + if (selection instanceof Collection<?>) { + this.selection = (selection instanceof List<?>) ? (List<?>) selection : new java.util.ArrayList<Object>((Collection<?>) selection); + setBaseEnabled(computeEnabled(context)); + this.selection = Collections.EMPTY_LIST; + } + } + super.setEnabled(evaluationContext); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/command/AbstractPapyrusHandler.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/command/AbstractPapyrusHandler.java new file mode 100644 index 00000000000..d77ab4240be --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/command/AbstractPapyrusHandler.java @@ -0,0 +1,217 @@ +/***************************************************************************** + * Copyright (c) 2011, 2016 CEA LIST, Christian W. Damus, 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.command; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.core.runtime.Platform; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.emf.utils.EMFHelper; +import org.eclipse.papyrus.infra.ui.util.ServiceUtilsForHandlers; +import org.eclipse.papyrus.infra.ui.util.ServiceUtilsForIEvaluationContext; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * This provides facilities to get the TransactionEditingDomain and the PageManager from + * the current Papyrus editor or view context. + */ +public abstract class AbstractPapyrusHandler extends AbstractHandler { + + /** + * Obtains the transactional editing domain associated with the Papyrus Editor or View + * that is the context of the specified {@code execution}. + * + * @param execution + * an execution event + * + * @return the editing domain, or {@code null} if there is none (such as when the Papyrus Editor is closing) + */ + protected TransactionalEditingDomain getEditingDomain(ExecutionEvent execution) { + TransactionalEditingDomain result = null; + + try { + result = ServiceUtilsForHandlers.getInstance().getTransactionalEditingDomain(execution); + } catch (ServiceException e) { + // The wrong kind of editor/view is active or the Papyrus Editor is shutting down. + // These are both normal conditions + } + + return result; + } + + /** + * Obtains the transactional editing domain associated with the Papyrus Editor or View + * that has the specified evaluation {@code context}. + * + * @param context + * an evaluation context for a command's enablement or other computation + * + * @return the editing domain, or {@code null} if there is none (such as when the Papyrus Editor is closing) + */ + protected TransactionalEditingDomain getEditingDomain(IEvaluationContext context) { + TransactionalEditingDomain result = null; + + try { + result = ServiceUtilsForIEvaluationContext.getInstance().getTransactionalEditingDomain(context); + } catch (ServiceException e) { + // The wrong kind of editor/view is active or the Papyrus Editor is shutting down. + // These are both normal conditions + } + + return result; + } + + /** + * Obtains the page manager associated with the Papyrus Editor or View + * that is the context of the specified {@code execution}. + * + * @param execution + * an execution event + * + * @return the page manager, or {@code null} if there is none (such as when the Papyrus Editor is closing) + */ + protected IPageManager getPageManager(ExecutionEvent execution) { + IPageManager result = null; + + try { + result = ServiceUtilsForHandlers.getInstance().getIPageManager(execution); + } catch (ServiceException e) { + // The wrong kind of editor/view is active or the Papyrus Editor is shutting down. + // These are both normal conditions + } + + return result; + } + + /** + * Obtains the page manager associated with the Papyrus Editor or View + * that has the specified evaluation {@code context}. + * + * @param context + * an evaluation context for a command's enablement or other computation + * + * @return the page manager, or {@code null} if there is none (such as when the Papyrus Editor is closing) + */ + protected IPageManager getPageManager(IEvaluationContext context) { + IPageManager result = null; + + try { + result = ServiceUtilsForIEvaluationContext.getInstance().getIPageManager(context); + } catch (ServiceException e) { + // The wrong kind of editor/view is active or the Papyrus Editor is shutting down. + // These are both normal conditions + } + + return result; + } + + /** + * Adapt the specified object to the requested type, if possible. + * Return null if the object can't be adapted. + * + * @param object + * @param expectedClassType + * @return The adapted object, or null. + */ + @SuppressWarnings("unchecked") + private <T> T adapt(Object object, Class<T> expectedClassType) { + + + EObject eobject = EMFHelper.getEObject(object); + + if (eobject != null && expectedClassType.isInstance(eobject)) { + return (T) eobject; + } + + + + // Try global mechanism + { + T ele = Platform.getAdapterManager().getAdapter(object, expectedClassType); + if (ele != null) { + return ele; + } + // Try as EObject if the expectedClasType is sub-type of EObject. + if (EObject.class.isAssignableFrom(expectedClassType)) { + // to EObject + eobject = Platform.getAdapterManager().getAdapter(object, EObject.class); + + if (eobject != null && expectedClassType.isInstance(eobject)) { + + return (T) eobject; + } + } + } + // Can't be adapted + return null; + + } + + /** + * Filter the list, and only retain objects that can be adapted to the specified type + * + * @param objects + * @param class1 + * @return + */ + private <T> List<T> getAllElementAdaptedToType(List<Object> list, Class<T> expectedClassType) { + + List<T> res = new ArrayList<T>(); + + for (Object cur : list) { + + T adapted = adapt(cur, expectedClassType); + if (adapted != null) { + res.add(adapted); + } + } + return res; + } + + /** + * Get all selected element of the specified type. + * + * @param expectedType + * @return + * @throws ExecutionException + */ + @SuppressWarnings("unchecked") + protected <T> List<T> getCurrentSelectionAdaptedToType(ExecutionEvent event, Class<T> expectedType) throws ExecutionException { + + // Get selection from the workbench + ISelection selection = HandlerUtil.getCurrentSelectionChecked(event); + + // Get the selected objects according to the type of the selected + if (selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + return getAllElementAdaptedToType(structuredSelection.toList(), expectedType); + } else if (selection instanceof TreeSelection) { + TreeSelection treeSelection = (TreeSelection) selection; + return getAllElementAdaptedToType(treeSelection.toList(), expectedType); + + } + return null; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/ContentOutlineRegistry.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/ContentOutlineRegistry.java new file mode 100644 index 00000000000..1fb84925184 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/ContentOutlineRegistry.java @@ -0,0 +1,263 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.contentoutline; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.Platform; +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.papyrus.infra.core.extension.BadClassNameException; +import org.eclipse.papyrus.infra.core.extension.NotFoundException; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.extension.diagrameditor.EditorDescriptorExtensionFactory; +import org.osgi.framework.Bundle; + +public class ContentOutlineRegistry { + + /** ID of the editor extension (schema filename) */ + public static final String EDITOR_EXTENSION_ID = "papyrusContentOutline"; + + private static String classAttributeName = "class"; + + private static String actionBarContributorIdPropertyName = "actionBarContributorId"; + + /** Namespace where to look for the extension points. */ + protected String extensionPointNamespace; + + /** + * The selected content outline. + */ + protected IPapyrusContentOutlinePage contentOutline; + + /** + * Associated editor. + */ + private IMultiDiagramEditor multiEditor; + + /** + * Constructor. defaultContext, input and site are explicitly required in + * order be sure that they are initialized. The multiEditor should be + * initialized. In particular, getEditorSite(), getEditorInput() and + * getDefaultContext() should return initialized values. + * + * @param multiEditor + * @param defaultContext + * @param input + * @param site + * @param extensionPointNamespace + */ + public ContentOutlineRegistry(IMultiDiagramEditor multiEditor, String extensionPointNamespace) { + this.multiEditor = multiEditor; + this.extensionPointNamespace = extensionPointNamespace; + } + + /** + * Returns the single instance of the content outline. Creates one if + * necessary. + * + * @return the contentOutline the single instance of the content outline + * @throws BackboneException + * exception thrown when the outline can not be created. + */ + public IPapyrusContentOutlinePage getContentOutline() throws BackboneException { + if (contentOutline == null) { + createContentOutline(); + } + return contentOutline; + } + + /** + * Return the {@link ContentOutlineDescriptor} with the highest priority. + * + * @return + * @throws BackboneException + * @throws NotFoundException + * If no ContentOutline can be found in extensions + */ + private ContentOutlineDescriptor getContentOutlineDescriptor() throws BackboneException { + IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor(extensionPointNamespace, EDITOR_EXTENSION_ID); + ContentOutlineDescriptor found = null; + + // look for the one with the highest priority + for (IConfigurationElement ele : configElements) { + ContentOutlineDescriptor desc = new ContentOutlineDescriptor(ele); + if (desc.isHigher(found)) { + found = desc; + } + } + + // Instanciate the object + if (found == null) { + throw new NotFoundException("No ContentOutline registered."); //$NON-NLS-1$ + } + + return found; + + } + + /** + * Creates the content outline from the selected extension. + * + * @throws BackboneException + * exception thrown when the outline can not be created. + */ + private void createContentOutline() throws BackboneException { + + ContentOutlineDescriptor found = getContentOutlineDescriptor(); + // Instanciate the object + if (found != null) { + contentOutline = found.createContentOutlinePage(); + } + } + + /** + * Inner Descriptor for content outline. This class load data from Eclipse + * extension mechanism TODO Change the parent class. It is here just to have + * quick code. + */ + protected class ContentOutlineDescriptor extends EditorDescriptorExtensionFactory { + + private int priority; + + private String className; + + private String actionBarContributorID; + + private IConfigurationElement element; + + /** + * Instance is created when requested. + */ + protected IPapyrusContentOutlinePage instance = null; + + /** + * Create a descriptor backuped by the config element. + */ + protected ContentOutlineDescriptor(IConfigurationElement element) throws BackboneException { + String tagName = "contentoutline"; + checkTagName(element, tagName); + this.className = element.getAttribute(classAttributeName); + this.actionBarContributorID = element.getAttribute(actionBarContributorIdPropertyName); + try { + this.priority = Integer.parseInt(element.getAttribute("priority")); + } catch (NumberFormatException e) { + this.priority = 0; + } + + this.element = element; + // check parameters + if (className == null) { + throw new BadClassNameException("Class name must be set", "contentoutline", classAttributeName); //$NON-NLS-1$ //$NON-NLS-2$ + } + + } + + /** + * Compare priority. The highest priority win. + */ + public boolean isHigher(ContentOutlineDescriptor found) { + if (found == null) { + return true; + } + return this.getPriority() > found.getPriority(); + } + + /** + * Return the higher value of the descriptor. This value is used to + * order the contentOutline. The highest priority win. + */ + private int getPriority() { + return priority; + } + + /** + * @return the actionBarContributorID + */ + public String getActionBarContributorID() { + return actionBarContributorID; + } + + /** + * Returns the content outline page instance (lazy initialization) + * + * @return the context outline page + * @throws BackboneException + * exception thrown when a problem occurs. + */ + protected IPapyrusContentOutlinePage getContentOutline() throws BackboneException { + if (instance == null) { + instance = createContentOutlinePage(); + } + return instance; + } + + /** + * Create the class corresponding to the class attribute. + */ + private Class<IPapyrusContentOutlinePage> loadClass() throws BadClassNameException { + if (className == null || className.length() == 0) { + throw new BadClassNameException("Classname should be set.", "contentoutline", classAttributeName); //$NON-NLS-1$ //$NON-NLS-2$ + } + Class<IPapyrusContentOutlinePage> factoryClass; + try { + factoryClass = (Class<IPapyrusContentOutlinePage>) Class.forName(className); + } catch (ClassNotFoundException e) { + // try another way + try { + String declaringID = element.getContributor().getName(); + Bundle bundle = Platform.getBundle(declaringID); + factoryClass = (Class<IPapyrusContentOutlinePage>) bundle.loadClass(className); + } catch (ClassNotFoundException e1) { + throw new BadClassNameException("", "contentoutline", classAttributeName, e1); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + return factoryClass; + } + + /** + * create the outlinepage by calling constructor without parameter and + * then call init method + * + * @return the outline. + * @throws BackboneException + */ + protected IPapyrusContentOutlinePage createContentOutlinePage() throws BackboneException { + if (false) { + System.out.println("Not yet"); // FIXME : no syso + return null; + } + try { + IPapyrusContentOutlinePage outline = loadClass().newInstance(); + outline.init(multiEditor); + return outline; + + } catch (SecurityException e) { + // Lets propagate. This is an implementation problem that should + // be solved by programmer. + throw new RuntimeException(e); + } + + catch (InstantiationException e) { + // Lets propagate. This is an implementation problem that should + // be solved by programmer. + // throw new RuntimeException(e); + } catch (IllegalAccessException e) { + // Lets propagate. This is an implementation problem that should + // be solved by programmer. + throw new RuntimeException(e); + } + return null; + } + + } // end class +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/IPapyrusContentOutlinePage.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/IPapyrusContentOutlinePage.java new file mode 100644 index 00000000000..8ce4fd3985f --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/IPapyrusContentOutlinePage.java @@ -0,0 +1,34 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.contentoutline; + +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; + +/** + * Extends the original interface to add the init method. + */ +public interface IPapyrusContentOutlinePage extends org.eclipse.ui.views.contentoutline.IContentOutlinePage { + + /** + * Init the content outline. + * + * @param multiEditor + * the multiEditor is used to access to the context + * @throws BackboneException + * during research of the associated context. + */ + void init(IMultiDiagramEditor multiEditor) throws BackboneException; + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/NestedEditorDelegatedOutlinePage.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/NestedEditorDelegatedOutlinePage.java new file mode 100644 index 00000000000..833f0ef89fb --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/contentoutline/NestedEditorDelegatedOutlinePage.java @@ -0,0 +1,1098 @@ +/***************************************************************************** + * Copyright (c) 2013, 2014 CEA LIST and other. + * + * 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: + * Remi Schnekenburger (CEA LIST) - Initial API and implementation + * Christian W. Damus (CEA) - bug 437217 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.contentoutline; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.papyrus.infra.core.Activator; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IEditorPage; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IPage; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageLifeCycleEventsListener; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.utils.AdapterUtils; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.editor.IReloadableEditor; +import org.eclipse.papyrus.infra.ui.editor.reload.EditorReloadEvent; +import org.eclipse.papyrus.infra.ui.editor.reload.IEditorReloadListener; +import org.eclipse.papyrus.infra.ui.editor.reload.IReloadContextProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.SubActionBars; +import org.eclipse.ui.part.IPageBookViewPage; +import org.eclipse.ui.part.IPageSite; +import org.eclipse.ui.part.Page; +import org.eclipse.ui.part.PageBook; +import org.eclipse.ui.part.PageSite; +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; + +import com.google.common.collect.Lists; + +/** + * Page for Papyrus outline when active nested editor is a GMF editor + */ +public class NestedEditorDelegatedOutlinePage extends Page implements IPapyrusContentOutlinePage, IPageLifeCycleEventsListener, IEditorReloadListener { + + /** The editor for which I am a slave. */ + private IMultiDiagramEditor multiEditor; + + /** Sash window container to listen for page changes inside the same editor */ + private ISashWindowsContainer sashWindowsContainer; + + /** Page book in which all outline controls of nested editors will be stored and displayed one by one */ + private PageBook sashEditorPageBook; + + /** + * Map from papyrus pages (representing nested editors) to outline page records (key type: <code>org.eclipse.papyrus.infra.core.sasheditor.editor.IPage</code>; + * value type: <code>OutlinePageRec</code>). + */ + private Map<IPage, OutlinePageRec> mapIPapyrusPageToOutlineRec = new HashMap<IPage, OutlinePageRec>(); + + /** + * The page rec which provided the current page or <code>null</code> + */ + private OutlinePageRec activeRec; + + /** + * Default page rec that displays a simple message + */ + private OutlinePageRec defaultPageRec; + + /** + * {@inheritDoc} + */ + @Override + public void init(IMultiDiagramEditor multiEditor) { + this.multiEditor = multiEditor; + + internalInit(multiEditor); + + IReloadableEditor.Adapter.getAdapter(multiEditor).addEditorReloadListener(this); + } + + private void internalInit(IMultiDiagramEditor multiEditor) { + sashWindowsContainer = (ISashWindowsContainer) multiEditor.getAdapter(ISashWindowsContainer.class); + sashWindowsContainer.addPageLifeCycleListener(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void init(IPageSite pageSite) { + IViewSite viewSite = getViewSite(pageSite); + + DelegatedPageSite delegatedPageSite = new DelegatedPageSite(viewSite, this); + super.init(delegatedPageSite); + } + + /** + * /** + * The <code>PageBookView</code> implementation of this <code>IWorkbenchPart</code> method cleans up all the pages. Subclasses + * may extend. + */ + @Override + public void dispose() { + if (multiEditor != null) { + IReloadableEditor.Adapter.getAdapter(multiEditor).removeEditorReloadListener(this); + } + + internalDispose(); + + multiEditor = null; + + // Run super. + super.dispose(); + } + + private void internalDispose() { + // Deref all of the pages. + activeRec = null; + if (defaultPageRec != null) { + // check for null since the default page may not have + // been created (ex. perspective never visible) + defaultPageRec.contentOutlinePage.dispose(); + defaultPageRec.dispose(); + defaultPageRec = null; + } + + java.util.List<OutlinePageRec> records = new ArrayList<NestedEditorDelegatedOutlinePage.OutlinePageRec>(mapIPapyrusPageToOutlineRec.values()); + Iterator<OutlinePageRec> itr = records.iterator(); + while (itr.hasNext()) { + OutlinePageRec rec = itr.next(); + removePage(rec); + } + + // remove listener and all refs to editor + sashWindowsContainer.removePageLifeCycleListener(this); + } + + /** + * Refreshes the global actions for the active page. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected void refreshGlobalActionHandlers() { + // Clear old actions. + IActionBars bars = getSite().getActionBars(); + bars.clearGlobalActionHandlers(); + + // Set new actions. + Map newActionHandlers = ((SubActionBars) activeRec.getPageSite().getActionBars()).getGlobalActionHandlers(); + if (newActionHandlers != null) { + Set<?> keys = newActionHandlers.entrySet(); + Iterator<?> iter = keys.iterator(); + while (iter.hasNext()) { + Map.Entry<String, IAction> entry = (Map.Entry) iter.next(); + bars.setGlobalActionHandler(entry.getKey(), entry.getValue()); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + // nothing here + } + + /** + * {@inheritDoc} + */ + @Override + public ISelection getSelection() { + // nothing here + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + // nothing here + } + + /** + * {@inheritDoc} + */ + @Override + public void setSelection(ISelection selection) { + // nothing here + } + + /** + * {@inheritDoc} + */ + @Override + public void createControl(Composite parent) { + sashEditorPageBook = new PageBook(parent, SWT.BORDER); + + createContents(); + } + + protected void createContents() { + // Create the default page rec. + IContentOutlinePage defaultPage = createDefaultPage(sashEditorPageBook); + defaultPageRec = new OutlinePageRec(null, defaultPage); + preparePage(defaultPageRec); + + // Show the initial active page or the default page + IPage activePage = sashWindowsContainer.getActiveSashWindowsPage(); + if (activePage != null) { + OutlinePageRec rec = getOutlinePageRec(activePage); + if (rec == null) { + rec = createPage(activePage); + } + + // Show the page, if it was successfully created + if (rec != null) { + showOutlinePageRec(rec); + } else { + showOutlinePageRec(defaultPageRec); + } + } else { + showOutlinePageRec(defaultPageRec); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + return sashEditorPageBook; + } + + /** + * {@inheritDoc} + */ + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + // nothing here + } + + /** + * {@inheritDoc} + */ + @Override + public void pageChanged(IPage newPage) { + // throw new UnsupportedOperationException("pageChanged not implemented " + newPage); + } + + /** + * {@inheritDoc} + */ + @Override + public void pageOpened(IPage page) { + // Activator.log.debug("Opened"); + // create the new Outline + // Create a page for the part. + OutlinePageRec rec = getOutlinePageRec(page); + if (rec == null) { + rec = createPage(page); + } + + // Show the page, if it was successfully created + if (rec != null) { + showOutlinePageRec(rec); + } else { + showOutlinePageRec(defaultPageRec); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void pageClosed(IPage papyrusPage) { + // Activator.log.debug("Closed"); + // Update the active part. + if (activeRec != null && activeRec.papyrusPage == papyrusPage) { + showOutlinePageRec(defaultPageRec); + } + + // Find and remove the part page. + OutlinePageRec rec = getOutlinePageRec(papyrusPage); + if (rec != null) { + removePage(rec); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void pageActivated(IPage page) { + // Activator.log.debug("Activated"); + // Create a page for the partm, if necessary. + OutlinePageRec rec = getOutlinePageRec(page, true); + + // Show the page, if it was successfully created + if (rec != null) { + showOutlinePageRec(rec); + } else { + showOutlinePageRec(defaultPageRec); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void pageDeactivated(IPage page) { + // throw new UnsupportedOperationException("pageDeactivated not implemented " + page); + } + + /** + * {@inheritDoc} + */ + @Override + public void pageAboutToBeOpened(IPage page) { + // throw new UnsupportedOperationException("pageAboutToBeOpened not implemented "+page); + } + + /** + * {@inheritDoc} + */ + @Override + public void pageAboutToBeClosed(IPage page) { + // throw new UnsupportedOperationException("pageAboutToBeClosed not implemented " + page); + } + + @Override + public void editorAboutToReload(EditorReloadEvent event) { + event.putContext(new OutlineContext()); + + internalDispose(); + } + + @Override + public void editorReloaded(EditorReloadEvent event) { + internalInit(event.getEditor()); + createContents(); + + ((OutlineContext) event.getContext()).restore(); + } + + // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // MAINLY INSPIRED FROM PAGE BOOK VIEW + // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Creates and returns the default page for this view. + * + * @param book + * the pagebook control + * @return the default page + */ + protected IContentOutlinePage createDefaultPage(PageBook book) { + MessageOutlinePage page = new MessageOutlinePage(); + initPage(page); + page.createControl(book); + return page; + } + + /** + * Creates an outline record for a given papyrus Page. Adds it to the pagebook but does not show it. + * + * @param page + * The nested editor we are created an outline. + * @return the created outline page record + */ + protected OutlinePageRec createPage(IPage papyrusPage) { + OutlinePageRec rec = doCreatePage(papyrusPage); + if (rec != null) { + mapIPapyrusPageToOutlineRec.put(papyrusPage, rec); + preparePage(rec); + } + return rec; + } + + /** + * Prepares the page in the given page rec for use in this view. + * + * @param rec + */ + protected void preparePage(OutlinePageRec rec) { + IPageSite site = null; + + if (!doesPageExist(rec.contentOutlinePage)) { + if (rec.contentOutlinePage instanceof IPageBookViewPage) { + site = ((IPageBookViewPage) rec.contentOutlinePage).getSite(); + rec.setPageSite(site); + } + } + } + + /** + * Initializes the given page with a page site. + * <p> + * Subclasses should call this method after the page is created but before creating its controls. + * </p> + * <p> + * Subclasses may override + * </p> + * + * @param page + * The page to initialize + */ + protected void initPage(IPageBookViewPage page) { + try { + IPageSite site = super.getSite(); + // try to create a specific page site for this page + page.init(new PageSite(getViewSite(site))); + } catch (PartInitException e) { + Activator.log.error(e); + } + } + + /** + * @param site + * the page site from which parent view site is retrieved + * @return the retrieved page site + */ + protected static IViewSite getViewSite(IPageSite site) { + if (site instanceof IViewSite) { + return ((IViewSite) site); + } + // no way to get the IViewSite from the page site. + if (site instanceof PageSite) { + try { + Field parentSiteField = PageSite.class.getDeclaredField("parentSite"); + parentSiteField.setAccessible(true); + Object parentSite = parentSiteField.get(site); + if (parentSite instanceof IViewSite) { + return ((IViewSite) parentSite); + } + } catch (SecurityException e) { + Activator.log.error(e); + } catch (NoSuchFieldException e) { + Activator.log.error(e); + } catch (IllegalArgumentException e) { + Activator.log.error(e); + } catch (IllegalAccessException e) { + Activator.log.error(e); + } + } + return null; + } + + /* + * (non-Javadoc) + * Method declared on PageBookView. + */ + protected OutlinePageRec doCreatePage(IPage papyrusPage) { + // Try to get an outline page. + if (papyrusPage instanceof IEditorPage) { + IEditorPart part = ((IEditorPage) papyrusPage).getIEditorPart(); + Object obj = getAdapter(part, IContentOutlinePage.class, false); + if (obj instanceof IContentOutlinePage) { + IContentOutlinePage page = (IContentOutlinePage) obj; + if (page instanceof IPageBookViewPage) { + initPage((IPageBookViewPage) page); + } + page.createControl(getPageBook()); + return new OutlinePageRec(papyrusPage, page); + } + } + + // There is no content outline + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public DelegatedPageSite getSite() { + return (DelegatedPageSite) super.getSite(); + } + + /* + * (non-Javadoc) + * Method declared on PageBookView. + */ + protected void doDestroyPage(IPage papyrusPage, OutlinePageRec rec) { + IContentOutlinePage contentOutlinePage = rec.contentOutlinePage; + contentOutlinePage.dispose(); + rec.dispose(); + } + + protected Collection<OutlinePageRec> getAllPages() { + return mapIPapyrusPageToOutlineRec.values(); + } + + /** + * Returns true if the page has already been created. + * + * @param page + * the page to test + * @return true if this page has already been created. + */ + protected boolean doesPageExist(IContentOutlinePage page) { + return mapIPapyrusPageToOutlineRec.containsKey(page); + } + + /** + * Returns the papyrus page which contributed the current outline page to this view. + * + * @return the page which contributed the current outline page or <code>null</code> if no part contributed the current page + */ + protected IPage getCurrentContributingPage() { + if (activeRec == null) { + return null; + } + return activeRec.papyrusPage; + } + + /** + * Returns the currently visible outline page for this view or <code>null</code> if no page is currently visible. + * + * @return the currently visible page + */ + public IContentOutlinePage getCurrentOutlinePage() { + if (activeRec == null) { + return null; + } + return activeRec.contentOutlinePage; + } + + /** + * Returns the view site for the given page of this view. + * + * @param page + * the page + * @return the corresponding site, or <code>null</code> if not found + */ + protected IPageSite getPageSite(IPage page) { + OutlinePageRec rec = getOutlinePageRec(page); + if (rec != null) { + return rec.getPageSite(); + } + return null; + } + + /** + * Returns the default page for this view. + * + * @return the default page + */ + public IContentOutlinePage getDefaultOutlinePage() { + return defaultPageRec.contentOutlinePage; + } + + /** + * Returns the pagebook control for this view. + * + * @return the pagebook control, or <code>null</code> if not initialized + */ + protected PageBook getPageBook() { + return sashEditorPageBook; + } + + /** + * Returns the page record for the given part. + * + * @param part + * the part + * @return the corresponding page record, or <code>null</code> if not + * found + */ + protected OutlinePageRec getOutlinePageRec(IPage papyrusPage) { + return mapIPapyrusPageToOutlineRec.get(papyrusPage); + } + + OutlinePageRec getOutlinePageRec(IPage papyrusPage, boolean create) { + OutlinePageRec result = getOutlinePageRec(papyrusPage); + if (result == null) { + result = createPage(papyrusPage); + } + return result; + } + + /** + * Returns the page record for the given page of this view. + * + * @param page + * the page + * @return the corresponding page record, or <code>null</code> if not + * found + */ + protected OutlinePageRec getPageRec(IContentOutlinePage contentOutlinePage) { + Iterator<OutlinePageRec> itr = mapIPapyrusPageToOutlineRec.values().iterator(); + while (itr.hasNext()) { + OutlinePageRec rec = itr.next(); + if (rec.contentOutlinePage == contentOutlinePage) { + return rec; + } + } + return null; + } + + /** + * Removes a page record. + * + * @param rec + * the page record to remove + */ + protected void removePage(OutlinePageRec rec) { + mapIPapyrusPageToOutlineRec.remove(rec.papyrusPage); + + Control control = rec.contentOutlinePage.getControl(); + if (control != null && !control.isDisposed()) { + // Dispose the page's control so pages don't have to do this in their dispose method. + // The page's control is a child of this view's control so if this view is closed, the page's control will already be disposed. + control.dispose(); + } + + // Do this before destroying the page, otherwise we won't be able to retrieve the page site (it will be null) + IPageSite site = rec.getPageSite(); + if (site instanceof PageSite) { // test null pointer and PageSite + ((SubActionBars) ((PageSite) site).getActionBars()).deactivate(); + ((SubActionBars) ((PageSite) site).getActionBars()).dispose(); + } + + // Free the page + doDestroyPage(rec.papyrusPage, rec); + } + + /* + * (non-Javadoc) Method declared on IWorkbenchPart. + */ + @Override + public void setFocus() { + // first set focus on the page book, in case the page + // doesn't properly handle setFocus + if (sashEditorPageBook != null) { + sashEditorPageBook.setFocus(); + } + // then set focus on the page, if any + if (activeRec != null) { + activeRec.contentOutlinePage.setFocus(); + } + } + + /** + * Shows page contained in the given page record in this view. The page + * record must be one from this pagebook view. + * <p> + * The <code>PageBookView</code> implementation of this method asks the pagebook control to show the given page's control, and records that the given page is now current. Subclasses may extend. + * </p> + * + * @param pageRec + * the page record containing the page to show + */ + protected void showOutlinePageRec(OutlinePageRec pageRec) { + // If already showing do nothing + if (activeRec == pageRec) { + return; + } + // If the page is the same, just set activeRec to pageRec + if (activeRec != null && pageRec != null && activeRec.contentOutlinePage == pageRec.contentOutlinePage) { + activeRec = pageRec; + return; + } + + activeRec = pageRec; + Control pageControl = activeRec.contentOutlinePage.getControl(); + if (pageControl != null && !pageControl.isDisposed()) { + PageSite pageSite = (PageSite) activeRec.getPageSite(); + // Verify that the page control is not disposed + // If we are closing, it may have already been disposed + sashEditorPageBook.showPage(pageControl); + getSite().setActivePageSite(pageSite); + } + + } + + /** + * If it is possible to adapt the given object to the given type, this + * returns the adapter. Performs the following checks: + * + * <ol> + * <li>Returns <code>sourceObject</code> if it is an instance of the adapter type.</li> + * <li>If sourceObject implements IAdaptable, it is queried for adapters.</li> + * <li>If sourceObject is not an instance of PlatformObject (which would have already done so), the adapter manager is queried for adapters</li> + * </ol> + * + * Otherwise returns null. + * + * @param sourceObject + * object to adapt, or null + * @param adapter + * type to adapt to + * @param activatePlugins + * true if IAdapterManager.loadAdapter should be used (may trigger plugin activation) + * @return a representation of sourceObject that is assignable to the + * adapter type, or null if no such representation exists + */ + public static Object getAdapter(Object sourceObject, Class<?> adapter, boolean activatePlugins) { + Assert.isNotNull(adapter); + if (sourceObject == null) { + return null; + } + if (adapter.isInstance(sourceObject)) { + return sourceObject; + } + + if (sourceObject instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) sourceObject; + + Object result = adaptable.getAdapter(adapter); + if (result != null) { + // Sanity-check + Assert.isTrue(adapter.isInstance(result)); + return result; + } + } + + if (!(sourceObject instanceof PlatformObject)) { + Object result; + if (activatePlugins) { + result = Platform.getAdapterManager().loadAdapter(sourceObject, adapter.getName()); + } else { + result = Platform.getAdapterManager().getAdapter(sourceObject, adapter); + } + if (result != null) { + return result; + } + } + + return null; + } + + /** + * A data structure used to store the information about the editor outline page within the papyrus outline page. + */ + protected static class OutlinePageRec { + + public Object subActionBars; + + /** papyrus page: current editor opened as nested editor */ + public IPage papyrusPage; + + /** outline page recorded for the given papyrus page */ + public IContentOutlinePage contentOutlinePage; + + /** page site for the recorded outline page */ + public IPageSite pageSite; + + /** + * Creates a new page record initialized to the given papyrus page and outline page. + * + * @param papyrusPage + * @param contentOutlinePage + */ + public OutlinePageRec(IPage papyrusPage, IContentOutlinePage contentOutlinePage) { + this.papyrusPage = papyrusPage; + this.contentOutlinePage = contentOutlinePage; + } + + /** + * Sets the page site + * + * @param pageSite + * the page site for the recorded content outline page + */ + public void setPageSite(IPageSite pageSite) { + this.pageSite = pageSite; + } + + /** + * Sets the page site + * + * @param pageSite + * the page site for the recorded content outline page + */ + public IPageSite getPageSite() { + return this.pageSite; + } + + /** + * Disposes of this page record by <code>null</code>ing its fields. + */ + public void dispose() { + papyrusPage = null; + contentOutlinePage = null; + pageSite = null; + } + } + + protected static class DelegatedPageSite extends PageSite { + + /** Page site of the active page in the book */ + protected PageSite activePageSite; + + private NestedEditorDelegatedOutlinePage nestedEditorDelegatedOutlinePage; + + /** + * Constructor. + * + * @param parentViewSite + * @param nestedEditorDelegatedOutlinePage + */ + public DelegatedPageSite(IViewSite parentViewSite, NestedEditorDelegatedOutlinePage nestedEditorDelegatedOutlinePage) { + super(parentViewSite); + this.nestedEditorDelegatedOutlinePage = nestedEditorDelegatedOutlinePage; + } + + /** + * Sets the active page site + * + * @param activePageSite + * the activePageSite to set + */ + public void setActivePageSite(PageSite activePageSite) { + // remove the contribution of the previous active page site + if (this.activePageSite != null) { + // update the action bars for the current page + getActionBars().deactivate(); + getActionBars().clearGlobalActionHandlers(); + getActionBars().updateActionBars(); + + activePageSite.deactivate(); + + } + this.activePageSite = activePageSite; + if (this.activePageSite != null) { + activePageSite.activate(); + // update the action bars for the current page + getActionBars().activate(); + getActionBars().updateActionBars(); + } + } + + /** + * Returns the active page site + * + * @return the active Page Site + */ + public PageSite getActivePageSite() { + return activePageSite; + } + + /** + * {@inheritDoc} + */ + @Override + public SubActionBars getActionBars() { + if (activePageSite != null) { + return (SubActionBars) activePageSite.getActionBars(); + } + return (SubActionBars) super.getActionBars(); + } + + /** + * {@inheritDoc} + */ + @Override + public void deactivate() { + // deactivate the action bars of the current active page + if (activePageSite != null) { + activePageSite.deactivate(); + } + + // deactivate all subcontributions + for (OutlinePageRec rec : nestedEditorDelegatedOutlinePage.getAllPages()) { + IPageSite site = rec.getPageSite(); + IActionBars bars = site.getActionBars(); + if (bars instanceof SubActionBars) { + SubActionBars subActionBars = (SubActionBars) bars; + subActionBars.deactivate(); + subActionBars.clearGlobalActionHandlers(); + subActionBars.updateActionBars(); + } + } + super.deactivate(); + } + + /** + * {@inheritDoc} + */ + @Override + public void activate() { + + // here, we have to desactivate all contributions of all pages of this delegating service. + // when the page site is activated, the pagebookview has already tried to update some action bars, even if it should not + // so we recompute all the active contributions items here, after desactivating all the contributions. + for (OutlinePageRec rec : nestedEditorDelegatedOutlinePage.getAllPages()) { + IPageSite site = rec.getPageSite(); + IActionBars bars = site.getActionBars(); + if (bars instanceof SubActionBars) { + SubActionBars subActionBars = (SubActionBars) bars; + subActionBars.deactivate(); + subActionBars.clearGlobalActionHandlers(); + subActionBars.updateActionBars(); + } + } + if (this.activePageSite != null) { + activePageSite.activate(); + // update the action bars for the current page + getActionBars().activate(); + getActionBars().updateActionBars(); + } + super.activate(); + } + } + + protected static class MessageOutlinePage implements IContentOutlinePage, IPageBookViewPage { + + private Text label; + + private IPageSite site; + + /** + * {@inheritDoc} + */ + @Override + public void createControl(Composite parent) { + label = new Text(parent, SWT.NONE); + label.setText("No outline for this editor"); + } + + /** + * {@inheritDoc} + */ + @Override + public void dispose() { + if (label != null && label.isDisposed()) { + label.dispose(); + label = null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + return label; + } + + /** + * {@inheritDoc} + */ + @Override + public void setActionBars(IActionBars actionBars) { + } + + /** + * {@inheritDoc} + */ + @Override + public void setFocus() { + if (label != null && label.isDisposed()) { + label.setFocus(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + // nothing here + } + + /** + * {@inheritDoc} + */ + @Override + public ISelection getSelection() { + // nothing here + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + // nothing here + } + + /** + * {@inheritDoc} + */ + @Override + public void setSelection(ISelection selection) { + // nothing here + } + + /** + * {@inheritDoc} + */ + @Override + public IPageSite getSite() { + return site; + } + + /** + * {@inheritDoc} + */ + @Override + public void init(IPageSite site) throws PartInitException { + this.site = site; + } + + } + + private class OutlineContext { + + private List<PageContext> pages = Lists.newArrayListWithCapacity(mapIPapyrusPageToOutlineRec.size()); + + OutlineContext() { + for (OutlinePageRec next : mapIPapyrusPageToOutlineRec.values()) { + pages.add(new PageContext(next)); + } + } + + public void restore() { + for (PageContext next : pages) { + next.restore(); + } + } + + // + // Nested types + // + + private class PageContext { + + final URI diagramToken; + + final Object context; + + PageContext(OutlinePageRec outlinePage) { + Object diagram = outlinePage.papyrusPage.getRawModel(); + diagramToken = (diagram instanceof EObject) ? EcoreUtil.getURI((EObject) diagram) : null; + + // Can only sensibly manage restoring the state of the page if we can find it again + if (diagramToken == null) { + context = null; + } else { + IReloadContextProvider provider = AdapterUtils.adapt(outlinePage.contentOutlinePage, IReloadContextProvider.class, null); + context = (provider == null) ? null : provider.createReloadContext(); + } + } + + void restore() { + if (diagramToken != null) { + try { + ModelSet modelSet = multiEditor.getServicesRegistry().getService(ModelSet.class); + + Object diagram = modelSet.getEObject(diagramToken, true); + if (diagram != null) { + IPage page = sashWindowsContainer.lookupModelPage(diagram); + if (page != null) { + OutlinePageRec outlinePage = getOutlinePageRec(page, true); + if ((outlinePage != null) && (context != null)) { + // Restore it. We know it adapts if it provided the reload state in the first place + AdapterUtils.adapt(outlinePage.contentOutlinePage, IReloadContextProvider.class, null).restore(context); + } + } + } + } catch (ServiceException e) { + Activator.log.error(e); + } + } + } + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/AbstractStringValueConverter.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/AbstractStringValueConverter.java new file mode 100644 index 00000000000..02e1f24f92c --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/AbstractStringValueConverter.java @@ -0,0 +1,86 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.converter; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.messages.Messages; + +/** + * Abstract class for String value Container + * + * @author VL222926 + * + */ +public abstract class AbstractStringValueConverter implements IStringValueConverter { + + protected static final String THE_STRING_X_IS_NOT_VALID_TO_CREATE_Y = Messages.AbstractStringValueConverter_TheStringXIsNotValidToCreateY; + + protected static final String THE_FEATURE_X_CANT_BE_RESOLVED = Messages.AbstractStringValueConverter_TheFeatureXCantBeResolved; + + protected static final String THE_STRING_VALUE_X_CANT_BE_RESOLVED = Messages.AbstractStringValueConverter_TheStringValueXCantBeResolved; + + protected static final String SOME_STRING_ARE_NOT_VALID_TO_CREATE_X = Messages.AbstractStringValueConverter_SomeStringsAreNotValidToCreateY; + + protected static final String SOME_STRING_CANT_BE_RESOLVED_TO_FIND_X = Messages.AbstractStringValueConverter_SomeStringsCantBeResolvedToFindY; + + protected static final String NO_X_REPRESENTED_BY_Y_HAVE_BEEN_FOUND = Messages.AbstractStringValueConverter_NoXReprensentedByYHaveBeenFound; + + private ConvertedValueContainer<?> result; + + /** + * + * @see org.eclipse.papyrus.infra.ui.converter.IStringValueConverter#deduceValueFromString(java.lang.Object, java.lang.String) + * + * @param type + * @param valueAsString + * @return + */ + @Override + public final ConvertedValueContainer<?> deduceValueFromString(final Object type, final String valueAsString) { + result = doDeduceValueFromString(type, valueAsString); + if (result == null) { + final IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(THE_STRING_VALUE_X_CANT_BE_RESOLVED, valueAsString)); + result = new ConvertedValueContainer<Object>(null, status); + } + return result; + } + + /** + * + * @return + * the converted value, you should call deduceValueFromString before to call this method + */ + public final ConvertedValueContainer<?> getConvertedValue() { + if (this.result == null) { + throw new IllegalStateException("You should call deduceValueFromString before to call this method"); //$NON-NLS-1$ + } + return this.result; + } + + /** + * + * @param type + * the type of the object + * @param valueAsString + * the string to resolve + * @return + * a {@link ConvertedValueContainer} with the resolved values and a status + */ + protected abstract ConvertedValueContainer<?> doDeduceValueFromString(final Object type, final String valueAsString); + + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/ConvertedValueContainer.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/ConvertedValueContainer.java new file mode 100644 index 00000000000..657091fb03c --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/ConvertedValueContainer.java @@ -0,0 +1,75 @@ +/***************************************************************************** + * Copyright (c) 2012 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.converter; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; + + +/** + * + * This class allows to store the value created for a pasted String AND a result status associated to this pasted String + * + * @param <T> + */ + +public class ConvertedValueContainer<T> { + + /** + * this field is used when the pasted value is monovalued + */ + private final T value; + + /** + * the resulting status of the parsing + */ + private final IStatus status; + + /** + * + * Constructor. + * + * @param realValue + * a monovalued Value (can be <code>null</code>) + * @param realListValue + * a collection value (can be <code>null</code>) + * @param status + * a status (can be <code>null</code>) + */ + public ConvertedValueContainer(final T realValue, final IStatus status) { + this.value = realValue; + this.status = status; + Assert.isNotNull(status); + } + + + /** + * + * @return + * the status of the conversion + */ + public final IStatus getStatus() { + return this.status; + } + + /** + * + * @return + * the value + */ + public final T getConvertedValue() { + return this.value; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/EMFStringValueConverter.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/EMFStringValueConverter.java new file mode 100644 index 00000000000..2847e7180fa --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/EMFStringValueConverter.java @@ -0,0 +1,373 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.converter; + +import java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.util.Enumerator; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EEnumLiteral; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.osgi.util.NLS; +import org.eclipse.papyrus.infra.emf.utils.EMFContants; +import org.eclipse.papyrus.infra.tools.util.BooleanHelper; +import org.eclipse.papyrus.infra.tools.util.TypesConstants; +import org.eclipse.papyrus.infra.ui.Activator; + +/** + * Value solver for EMF + * + * WARNING : incomplete implementations + * + * @author vl222926 + * + */ + +public class EMFStringValueConverter extends AbstractStringValueConverter { + + + + /** + * Context used for the resolution of the string + */ + private EObject resolutionContext; + + /** + * The separator used for multivalue + */ + protected final String multiValueSeparator; + + /** + * + * Constructor. + * + * @param resolutionContext + * the context used for the resolution of the string + */ + public EMFStringValueConverter(final EObject resolutionContext, final String multiValueSeparator) { + this.resolutionContext = resolutionContext; + this.multiValueSeparator = multiValueSeparator; + } + + + /** + * + * @return + * the context to use for the resolution + */ + public EObject getResolutionContext() { + return resolutionContext; + } + + /** + * + * @see org.eclipse.ui.services.IDisposable#dispose() + * + */ + @Override + public void dispose() { + this.resolutionContext = null; + } + + /** + * + * @param resolutionContext + * the table context + * @param feature + * the feature + * @param valueAsString + * the pasted string for this feature + * @return + * the value for the pasted string or <code>null</code> if not found + */ + @Override + protected ConvertedValueContainer<?> doDeduceValueFromString(final Object feature, final String valueAsString) { + final EClassifier featureType = getFeatureType(feature); + if (feature instanceof EStructuralFeature) { + return deduceValueFromString(feature, featureType, valueAsString); + } + return null; + } + + /** + * + * @param feature + * @param featureType + * @param valueAsString + * @return + */ + protected ConvertedValueContainer<?> deduceValueFromString(final Object feature, final EClassifier featureType, final String valueAsString) { + ConvertedValueContainer<?> realValue = null; + // if(feature instanceof EStructuralFeature) { + final int upperbound = getFeatureUpperBound(feature); + boolean isMany = (upperbound > 1 || upperbound == -1); + if (featureType instanceof EDataType) { + if (featureType instanceof EEnum) { + realValue = deduceEEnumLiteralValue((EEnum) featureType, isMany, valueAsString); + } + final String typeName = featureType.getName(); + if (TypesConstants.STRING.equals(typeName) || EMFContants.ESTRING.equals(typeName)) { + realValue = deduceStringValue(isMany, valueAsString); + } else if (EMFContants.EBOOLEAN.equals(typeName) || TypesConstants.BOOLEAN.equals(typeName)) { + realValue = deduceBooleanValue(isMany, valueAsString); + } else if (EMFContants.EINT.equals(typeName) || TypesConstants.INTEGER.equals(typeName)) { + realValue = deduceIntValue(isMany, valueAsString); + } else if (EMFContants.EDOUBLE.equals(typeName)) { + realValue = deduceDoubleValue(isMany, valueAsString); + } + } else if (featureType instanceof EClass) { + realValue = deduceEObjectValue(getResolutionContext(), feature, (EClass) featureType, isMany, valueAsString); + } + return realValue; + } + + protected int getFeatureUpperBound(final Object feature) { + return ((EStructuralFeature) feature).getUpperBound(); + } + + + + /** + * + * @param resolutionContext + * the context used for the resolution + * @param feature + * the feature + * @param featureType + * the type of the feature + * @param isMany + * <code>true</code> if the feature isMany + * @param valueAsString + * the string value to resolve + * @return + * a value container referencing the eobject represented by the string + * @throws StringValueSolverException + */ + protected ConvertedValueContainer<?> deduceEObjectValue(EObject resolutionContext, Object feature, EClass featureType, boolean isMany, String valueAsString) { + if (valueAsString == null || valueAsString.equals("")) { + return new ConvertedValueContainer<EObject>(null, Status.OK_STATUS); + } + final IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(THE_STRING_VALUE_X_CANT_BE_RESOLVED, valueAsString)); + return new ConvertedValueContainer<EObject>(null, status); + } + + /** + * + * @param feature + * an object representing a feature + * @return + * the type of the feature + */ + protected EClassifier getFeatureType(final Object feature) { + final EClassifier featureType; + if (feature instanceof EStructuralFeature) { + return ((EStructuralFeature) feature).getEType(); + } else { + featureType = null; + } + return featureType; + } + + /** + * + * @param eenum + * the enumeration + * @param isMany + * <code>true</code> if the feature is many + * @param valueAsString + * the value to convert + * @return + * the converted value + */ + protected ConvertedValueContainer<?> deduceEEnumLiteralValue(final EEnum eenum, final boolean isMany, final String valueAsString) { + ConvertedValueContainer<?> returnedValue = null; + IStatus iStatus = Status.OK_STATUS; + final Collection<String> unresolvedValues = new ArrayList<String>(); + if (isMany) { + final Collection<EEnumLiteral> values = new ArrayList<EEnumLiteral>(); + for (final String str : valueAsString.split(this.multiValueSeparator)) { + final EEnumLiteral literal = eenum.getEEnumLiteral(str); + if (literal != null) { + values.add(literal); + } else { + unresolvedValues.add(str); + } + } + if (!unresolvedValues.isEmpty()) { + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(SOME_STRING_ARE_NOT_VALID_TO_CREATE_X, EMFContants.EENUM_LITERAL), unresolvedValues); + } + returnedValue = new MultiConvertedValueContainer<EEnumLiteral>(values, iStatus); + } else { + final EEnumLiteral literal = eenum.getEEnumLiteral(valueAsString); + if (literal != null) { + // returnedValue = new ConvertedValueContainer<EEnumLiteral>(literal, iStatus); + // fix a bug on enumerator + returnedValue = new ConvertedValueContainer<Enumerator>(literal.getInstance(), iStatus); + } else { + unresolvedValues.add(valueAsString); + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(THE_STRING_X_IS_NOT_VALID_TO_CREATE_Y, valueAsString, EMFContants.EENUM_LITERAL), unresolvedValues); + returnedValue = new ConvertedValueContainer<Boolean>(null, iStatus); + } + } + return returnedValue; + } + + /** + * + * @param isMany + * <code>true</code> if the feature isMany + * @param valueAsString + * the value to parse + * @return + * the result of the parsing + */ + protected ConvertedValueContainer<?> deduceBooleanValue(final boolean isMany, final String valueAsString) { + ConvertedValueContainer<?> returnedValue = null; + IStatus iStatus = Status.OK_STATUS; + final Collection<String> unresolvedValues = new ArrayList<String>(); + if (isMany) { + final Collection<Boolean> values = new ArrayList<Boolean>(); + for (final String str : valueAsString.split(this.multiValueSeparator)) { + if (BooleanHelper.isBoolean(str)) { + values.add(Boolean.valueOf(valueAsString)); + } else { + unresolvedValues.add(str); + } + } + if (!unresolvedValues.isEmpty()) { + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(SOME_STRING_ARE_NOT_VALID_TO_CREATE_X, TypesConstants.BOOLEAN), unresolvedValues); + } + returnedValue = new MultiConvertedValueContainer<Boolean>(values, iStatus); + } else { + if (BooleanHelper.isBoolean(valueAsString)) { + returnedValue = new ConvertedValueContainer<Boolean>(Boolean.valueOf(valueAsString), iStatus); + } else { + unresolvedValues.add(valueAsString); + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(THE_STRING_X_IS_NOT_VALID_TO_CREATE_Y, valueAsString, TypesConstants.BOOLEAN), unresolvedValues); + returnedValue = new ConvertedValueContainer<Boolean>(null, iStatus); + } + } + return returnedValue; + } + + /** + * + * @param isMany + * <code>true</code> if the feature isMany + * @param valueAsString + * the value to parse + * @return + * the result of the parsing + */ + protected ConvertedValueContainer<?> deduceDoubleValue(final boolean isMany, final String valueAsString) { + ConvertedValueContainer<?> returnedValue = null; + IStatus iStatus = Status.OK_STATUS; + final Collection<String> unresolvedValues = new ArrayList<String>(); + if (isMany) { + final Collection<Double> values = new ArrayList<Double>(); + for (final String str : valueAsString.split(this.multiValueSeparator)) { + final Double value = Double.valueOf(str); + if (value != null) { + values.add(value); + } else { + unresolvedValues.add(str); + } + } + if (!unresolvedValues.isEmpty()) { + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(SOME_STRING_ARE_NOT_VALID_TO_CREATE_X, TypesConstants.DOUBLE), unresolvedValues); + } + returnedValue = new MultiConvertedValueContainer<Double>(values, iStatus); + } else { + try { + returnedValue = new ConvertedValueContainer<Double>(Double.valueOf(valueAsString), iStatus); + } catch (final NumberFormatException e) { + unresolvedValues.add(valueAsString); + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(THE_STRING_X_IS_NOT_VALID_TO_CREATE_Y, valueAsString, TypesConstants.DOUBLE), unresolvedValues); + returnedValue = new ConvertedValueContainer<Boolean>(null, iStatus); + } + } + return returnedValue; + } + + /** + * + * @param isMany + * <code>true</code> if the feature isMany + * @param valueAsString + * the value to parse + * @return + * the result of the parsing + */ + protected ConvertedValueContainer<?> deduceIntValue(final boolean isMany, final String valueAsString) { + ConvertedValueContainer<?> returnedValue = null; + IStatus iStatus = Status.OK_STATUS; + final Collection<String> unresolvedValues = new ArrayList<String>(); + if (isMany) { + final Collection<Integer> values = new ArrayList<Integer>(); + for (final String str : valueAsString.split(this.multiValueSeparator)) { + try { + values.add(Integer.valueOf(str)); + } catch (final NumberFormatException e) { + unresolvedValues.add(str); + } + } + if (!unresolvedValues.isEmpty()) { + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(SOME_STRING_ARE_NOT_VALID_TO_CREATE_X, TypesConstants.INTEGER), unresolvedValues); + } + returnedValue = new MultiConvertedValueContainer<Integer>(values, iStatus); + } else { + try { + returnedValue = new ConvertedValueContainer<Integer>(Integer.valueOf(valueAsString), iStatus); + } catch (final NumberFormatException e) { + unresolvedValues.add(valueAsString); + iStatus = new StringValueConverterStatus(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(THE_STRING_X_IS_NOT_VALID_TO_CREATE_Y, valueAsString, TypesConstants.INTEGER), unresolvedValues); + returnedValue = new ConvertedValueContainer<Boolean>(null, iStatus); + } + } + return returnedValue; + } + + + /** + * + * @param isMany + * <code>true</code> if the feature is many + * @param valueAsString + * the value as string + * @return + * the value container with the real value(s) + */ + protected ConvertedValueContainer<?> deduceStringValue(final boolean isMany, final String valueAsString) { + ConvertedValueContainer<?> returnedValue = null; + final IStatus iStatus = Status.OK_STATUS; + if (isMany) { + final Collection<String> values = new ArrayList<String>(); + for (final String str : valueAsString.split(this.multiValueSeparator)) { + values.add(str); + } + returnedValue = new MultiConvertedValueContainer<String>(values, iStatus); + } else { + returnedValue = new ConvertedValueContainer<String>(valueAsString, iStatus); + } + return returnedValue; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/IStringValueConverter.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/IStringValueConverter.java new file mode 100644 index 00000000000..5ce086d2dd7 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/IStringValueConverter.java @@ -0,0 +1,36 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.converter; + +import org.eclipse.ui.services.IDisposable; + +/** + * Common interface for string converter + * + * @author VL222926 + * + */ +public interface IStringValueConverter extends IDisposable { + + /** + * + * @param type + * an object representing the type of the in which we want to convert the string + * @param valueAsString + * the value represented by a string + * @return + * a {@link ConvertedValueContainer} + */ + public ConvertedValueContainer<?> deduceValueFromString(final Object type, final String valueAsString); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/MultiConvertedValueContainer.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/MultiConvertedValueContainer.java new file mode 100644 index 00000000000..4324d0191fa --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/MultiConvertedValueContainer.java @@ -0,0 +1,39 @@ +/***************************************************************************** + * Copyright (c) 2012 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.converter; + +import java.util.Collection; + +import org.eclipse.core.runtime.IStatus; + +/** + * + * This class allows to store the value created for a pasted String AND a result status associated to this pasted String + * + * @param <T> + */ +public class MultiConvertedValueContainer<T> extends ConvertedValueContainer<Collection<T>> { + + + /** + * + * Constructor. + * + * @param realValue + * @param status + */ + public MultiConvertedValueContainer(final Collection<T> realValue, final IStatus status) { + super(realValue, status); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/StringValueConverterStatus.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/StringValueConverterStatus.java new file mode 100644 index 00000000000..979bc06460c --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/converter/StringValueConverterStatus.java @@ -0,0 +1,60 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.converter; + +import java.util.Collection; + +import org.eclipse.core.runtime.Status; + +/** + * This status is used y the String Value solvers + * + * @author vl222926 + * + */ +public class StringValueConverterStatus extends Status { + + /** + * the list of the uresolved strings + */ + private Collection<String> unresolvedString; + + /** + * + * Constructor. + * + * @param severity + * the severity of the status + * @param pluginId + * the plugin id providing this status + * @param message + * the message for this status + * @param unresolvedString + * the list of the unresolved string + */ + public StringValueConverterStatus(int severity, String pluginId, String message, Collection<String> unresolvedString) { + super(severity, pluginId, message); + this.unresolvedString = unresolvedString; + } + + + /** + * + * @return + * the list of the unresolved string + */ + public final Collection<String> getUnresolvedString() { + return unresolvedString; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/dnd/PapyrusTransfer.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/dnd/PapyrusTransfer.java new file mode 100644 index 00000000000..a31e0562b90 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/dnd/PapyrusTransfer.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2000, 2015 IBM Corporation, Christian W. Damus, 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 + * Christian W. Damus - adapted from GEF for bug 469188 + *******************************************************************************/ +package org.eclipse.papyrus.infra.ui.dnd; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; + +import org.eclipse.swt.dnd.ByteArrayTransfer; +import org.eclipse.swt.dnd.TransferData; + +/** + * A local transfer carrying a single object being dragged. Subclasses should maintain a + * single instance of their Transfer and provide a static method to obtain that + * instance. + */ +public abstract class PapyrusTransfer<T> extends ByteArrayTransfer { + + private final Class<? extends T> objectType; + private final String typeName; + private final int typeID; + + private Reference<T> object; + private long startTime; + + protected PapyrusTransfer(Class<? extends T> objectType) { + super(); + + this.objectType = objectType; + + typeName = String.format("%s:%x:%x", getClass().getSimpleName(), hashCode(), System.currentTimeMillis()); + typeID = registerType(typeName); + } + + @Override + public final int hashCode() { + return System.identityHashCode(this); + } + + /** + * The data object is not converted to bytes. It is held onto in a field. + * Instead, a checksum is written out to prevent unwanted drags across + * mulitple running copies of Eclipse. + * + * @see org.eclipse.swt.dnd.Transfer#javaToNative(Object, TransferData) + */ + @Override + public void javaToNative(Object object, TransferData transferData) { + setObject(objectType.cast(object)); + startTime = System.currentTimeMillis(); + if (transferData != null) + super.javaToNative(String.valueOf(startTime).getBytes(), + transferData); + } + + /** + * The data object is not converted to bytes. It is held onto in a field. + * Instead, a checksum is written out to prevent unwanted drags across + * mulitple running. copies of Eclipse. + * + * @see org.eclipse.swt.dnd.Transfer#nativeToJava(TransferData) + */ + @Override + public Object nativeToJava(TransferData transferData) { + byte bytes[] = (byte[]) super.nativeToJava(transferData); + if (bytes == null) { + return null; + } + long startTime = Long.parseLong(new String(bytes)); + return (this.startTime == startTime) ? getObject() : null; + } + + /** + * Obtains the object being dragged. + */ + public T getObject() { + return (object == null) ? null : objectType.cast(object.get()); + } + + /** + * Sets the object being dragged. + */ + public void setObject(T object) { + this.object = new WeakReference<>(object); + } + + @Override + protected int[] getTypeIds() { + return new int[] { typeID }; + } + + @Override + protected String[] getTypeNames() { + return new String[] { typeName }; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/ContentProviderServiceFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/ContentProviderServiceFactory.java new file mode 100644 index 00000000000..49557e36432 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/ContentProviderServiceFactory.java @@ -0,0 +1,68 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.DiSashModelManager; +import org.eclipse.papyrus.infra.core.services.IServiceFactory; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; + +/** + * A service factory to create the {@link ISashWindowsContentProvider} service. + * This service depends on {@link DiSashModelMngrServiceFactory}. + * + * @author cedric dumoulin + * + */ +public class ContentProviderServiceFactory implements IServiceFactory { + + /** + * The sashModelMangr. + */ + private DiSashModelManager sashModelMngr; + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + * @throws ServiceException + */ + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + // Get required services + sashModelMngr = servicesRegistry.getService(DiSashModelManager.class); + + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + * + * @throws ServiceException + */ + @Override + public void startService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#disposeService() + * + * @throws ServiceException + */ + @Override + public void disposeService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IServiceFactory#createServiceInstance() + * + * @return + * @throws ServiceException + */ + @Override + public Object createServiceInstance() throws ServiceException { + return sashModelMngr.getISashWindowsContentProvider(); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/CoreMultiDiagramEditor.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/CoreMultiDiagramEditor.java new file mode 100644 index 00000000000..bbfb16f40a6 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/CoreMultiDiagramEditor.java @@ -0,0 +1,1244 @@ +/***************************************************************************** + * Copyright (c) 2008, 2016 LIFL, CEA LIST, Christian W. Damus, 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * Christian W. Damus (CEA) - manage models by URI, not IFile (CDO) + * Christian W. Damus (CEA) - bug 410346 + * Christian W. Damus (CEA) - bug 431953 (pre-requisite refactoring of ModelSet service start-up) + * Christian W. Damus (CEA) - bug 437217 + * Christian W. Damus - bugs 469464, 469188, 485220 + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.editor; + +import static org.eclipse.papyrus.infra.core.Activator.log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.core.commands.operations.IUndoContext; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.common.ui.URIEditorInput; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.emf.edit.domain.IEditingDomainProvider; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.emf.edit.provider.IItemLabelProvider; +import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.osgi.util.NLS; +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.papyrus.infra.core.language.ILanguageChangeListener; +import org.eclipse.papyrus.infra.core.language.ILanguageService; +import org.eclipse.papyrus.infra.core.language.LanguageChangeEvent; +import org.eclipse.papyrus.infra.core.resource.ModelMultiException; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IContentChangedListener; +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.DiSashModelManager; +import org.eclipse.papyrus.infra.core.sasheditor.editor.AbstractMultiPageSashEditor; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager; +import org.eclipse.papyrus.infra.core.services.ExtensionServicesRegistry; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServiceMultiException; +import org.eclipse.papyrus.infra.core.services.ServiceStartKind; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.ServiceUtils; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.contentoutline.ContentOutlineRegistry; +import org.eclipse.papyrus.infra.ui.editor.IReloadableEditor.DirtyPolicy; +import org.eclipse.papyrus.infra.ui.editor.reload.EditorReloadEvent; +import org.eclipse.papyrus.infra.ui.editor.reload.IEditorReloadListener; +import org.eclipse.papyrus.infra.ui.lifecycleevents.DoSaveEvent; +import org.eclipse.papyrus.infra.ui.lifecycleevents.IEditorInputChangedListener; +import org.eclipse.papyrus.infra.ui.lifecycleevents.ISaveAndDirtyService; +import org.eclipse.papyrus.infra.ui.multidiagram.actionbarcontributor.ActionBarContributorRegistry; +import org.eclipse.papyrus.infra.ui.multidiagram.actionbarcontributor.CoreComposedActionBarContributor; +import org.eclipse.papyrus.infra.ui.services.EditorLifecycleManager; +import org.eclipse.papyrus.infra.ui.services.internal.EditorLifecycleManagerImpl; +import org.eclipse.papyrus.infra.ui.services.internal.InternalEditorLifecycleManager; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorActionBarContributor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IGotoMarker; +import org.eclipse.ui.part.FileEditorInput; +import org.eclipse.ui.progress.UIJob; +import org.eclipse.ui.statushandlers.StatusManager; +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; +import org.eclipse.ui.views.properties.IPropertySheetPage; +import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +/** + * Multi diagram editor allowing to plug various kind of editors. Editors are + * registered with the help of the Eclipse extension mechanism. This + * implementation allows to register editors and context separately. An editor + * should specify which context it need to run. This multi diagram editor allows + * to show editor side by side in one or more sash windows. + * + * The real implementation for the generic type T of SashMultiPageEditorPart is + * actually di2.Diagram + * + * @author cedric dumoulin + * @author <a href="mailto:jerome.benois@obeo.fr">Jerome Benois</a> + * @author <a href="mailto:thomas.szadel@atosorigin.com">Thomas Szadel</a> + * Refactoring. + * + * TODO : remove GMF dependency ! + */ +public class CoreMultiDiagramEditor extends AbstractMultiPageSashEditor implements IMultiDiagramEditor, ITabbedPropertySheetPageContributor, IGotoMarker, IEditingDomainProvider { + + /** ContentOutline registry */ + private ContentOutlineRegistry contentOutlineRegistry; + + /** Services registry. Used to get registered services */ + private ServicesRegistry servicesRegistry; + + /** + * ActionBarContributor Registry. Allows to get an ActionBar by its Id. The + * registry is initialized from the Eclipse extension mechanism. + */ + private ActionBarContributorRegistry actionBarContributorRegistry; + + /** SashModelMngr to add pages */ + protected DiSashModelManager sashModelMngr; + + /** + * Service used to maintain the dirty state and to perform save and saveAs. + */ + protected ISaveAndDirtyService saveAndDirtyService; + + private final List<IPropertySheetPage> propertiesPages = new LinkedList<IPropertySheetPage>(); + + private final List<Runnable> closeActions = new ArrayList<>(); + + /** + * Listener on {@link ISaveAndDirtyService#addInputChangedListener(IEditorInputChangedListener)} + */ + private static class EditorInputChangedListener implements IEditorInputChangedListener { + + private CoreMultiDiagramEditor editor; + + public EditorInputChangedListener(CoreMultiDiagramEditor editor) { + this.editor = editor; + } + + /** + * This method is called when the editor input is changed from the + * ISaveAndDirtyService. + * + * @see org.eclipse.papyrus.infra.ui.lifecycleevents.IEditorInputChangedListener#editorInputChanged(org.eclipse.ui.part.FileEditorInput) + * + * @param fileEditorInput + */ + @Override + public void editorInputChanged(FileEditorInput fileEditorInput) { + // Change the editor input. + editor.setInputWithNotify(fileEditorInput); + editor.setPartName(fileEditorInput.getName()); + } + + /** + * The isDirty flag has changed, reflect its new value + * + * @see org.eclipse.papyrus.infra.ui.lifecycleevents.IEditorInputChangedListener#isDirtyChanged() + * + */ + @Override + public void isDirtyChanged() { + + // Run it in async way. + editor.getSite().getShell().getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + // editor can be null if this object has been finalized, but + // still queued in the asyncExec queue. + // This can happen if the editor is disposed, but some run still in + // the exec queue. + // When the method is executed asynchronously, the object is already finalized, and so + // editor is null. + if (editor == null) { + return; + } + editor.firePropertyChange(IEditorPart.PROP_DIRTY); + } + }); + } + + public void dispose() { + this.editor = null; + } + } + + protected EditorInputChangedListener editorInputChangedListener; + + private TransactionalEditingDomain transactionalEditingDomain; + + /** + * Object managing models lifeCycle. + */ + protected ModelSet resourceSet; + + /** + * Cached event that can be reused. + */ + protected DoSaveEvent lifeCycleEvent; + + private class ContentChangedListener implements IContentChangedListener { + + /** + * Called when the content is changed. RefreshTabs. + */ + @Override + public void contentChanged(ContentEvent event) { + scheduleRefresh(); + } + } + + /** + * A listener on model change events. + */ + private ContentChangedListener contentChangedListener; + + /** + * Undo context used to have the same undo context in all Papyrus related + * views and editors. TODO : move away, use a version independent of GMF, + * add a listener that will add the context to all commands modifying + * attached Resources (==> linked to ModelSet ?) + */ + private IUndoContext undoContext; + + /** + * Editor reload listeners. + */ + private CopyOnWriteArrayList<IEditorReloadListener> reloadListeners = new CopyOnWriteArrayList<IEditorReloadListener>(); + + /** + * A pending reload operation (awaiting next activation of the editor). + */ + private final AtomicReference<DeferredReload> pendingReload = new AtomicReference<DeferredReload>(); + + public CoreMultiDiagramEditor() { + super(); + + addSelfReloadListener(); + } + + /** + * Get the contentOutlineRegistry. Create it if needed. + * + * @return the contentOutlineRegistry + */ + protected ContentOutlineRegistry getContentOutlineRegistry() { + if (contentOutlineRegistry == null) { + createContentOutlineRegistry(); + } + + return contentOutlineRegistry; + } + + /** + * Create the contentOutlineRegistry. + */ + private void createContentOutlineRegistry() { + contentOutlineRegistry = new ContentOutlineRegistry(this, Activator.PLUGIN_ID); + } + + /** + * Returns the service registry associated to the editor. + * + * @return the servicesRegistry The registry. + */ + @Override + public ServicesRegistry getServicesRegistry() { + if (servicesRegistry == null) { + servicesRegistry = createServicesRegistry(); + } + return servicesRegistry; + } + + /** + * Create the ServicesRegistry. + * + * @return + */ + private ServicesRegistry createServicesRegistry() { + // Create Services Registry + try { + ServicesRegistry servicesRegistry = new ExtensionServicesRegistry(org.eclipse.papyrus.infra.core.Activator.PLUGIN_ID); + // servicesRegistry.startRegistry(); + return servicesRegistry; + } catch (ServiceException e) { + // Show log and error + log.error(e.getMessage(), e); + } + return null; + } + + /** + * Do nothing as we create the provider before any calls to this method. + * Should not be called by subclasses. + * + * @see org.eclipse.papyrus.infra.core.sasheditor.editor.AbstractMultiPageSashEditor#createPageProvider() + */ + @Override + protected ISashWindowsContentProvider createPageProvider() { + throw new UnsupportedOperationException("Not implemented. Should not be called as the ContentProvider is already initialized."); + } + + /** + * Create the pageContentProvider. + * + * Removed since 0.10.0 + * + * @param pageFactory + * @param diResource + * Resource used to load/save the SashModel. + * + * + */ + // protected ISashWindowsContentProvider createPageProvider(IPageModelFactory pageFactory, Resource diResource, TransactionalEditingDomain editingDomain) { + // + // sashModelMngr = new TransactionalDiSashModelMngr(pageFactory, diResource, editingDomain); + // + // ISashWindowsContentProvider pageProvider = sashModelMngr.getISashWindowsContentProvider(); + // + // return pageProvider; + // } + + /** + * Get The {@link IPageMngr} used to add, open, remove or close a diagram in + * the SashWindow. This method is available as soon as the {@link CoreMultiDiagramEditor#init(IEditorSite, IEditorInput)} method is + * called. + * + * @return + */ + protected IPageManager getIPageManager() throws IllegalStateException { + try { + return sashModelMngr.getIPageManager(); + } catch (Exception e) { + throw new IllegalStateException("Method should be called after CoreMultiDiagramEditor#init(IEditorSite, IEditorInput) is called"); + } + } + + /** + * Get the ActionBarContributorRegistry. Creates it if necessary. + * + * @return + */ + protected ActionBarContributorRegistry getActionBarContributorRegistry() { + if (actionBarContributorRegistry != null) { + return actionBarContributorRegistry; + } + + // Try to got it from CoreComposedActionBarContributor + // Get it from the contributor. + IEditorActionBarContributor contributor = getEditorSite().getActionBarContributor(); + if (contributor instanceof CoreComposedActionBarContributor) { + log.debug(getClass().getSimpleName() + " - ActionBarContributorRegistry loaded from CoreComposedActionBarContributor."); + return ((CoreComposedActionBarContributor) contributor).getActionBarContributorRegistry(); + } else { + // Create a registry. + log.debug(getClass().getSimpleName() + " - create an ActionBarContributorRegistry."); + return createActionBarContributorRegistry(); + } + + } + + /** + * Create the ActionBarContributorRegistry. + * + * @return + */ + private ActionBarContributorRegistry createActionBarContributorRegistry() { + return new ActionBarContributorRegistry(Activator.PLUGIN_ID); + } + + /** + * + * + * @param adapter + * + * @return + */ + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + + if (ServicesRegistry.class == adapter) { + return getServicesRegistry(); + } + + if (IPageManager.class == adapter) { + return getIPageManager(); + } + + if (IPropertySheetPage.class == adapter) { + // Do not test if tabbedPropertySheetPage is null before calling new + // this is managed by Eclipse which only call current method when + // necessary + return getPropertySheetPage(); + } + + // Add a viewer + if (IContentOutlinePage.class == adapter) { + try { + ContentOutlineRegistry outlineRegistry = getContentOutlineRegistry(); + if (outlineRegistry == null) { + return null; + } + IContentOutlinePage contentOutline = outlineRegistry.getContentOutline(); + if (contentOutline != null) { + return contentOutline; + } + } catch (BackboneException e) { + // Ignore: There is not registered outline. + } + } + + if (EditingDomain.class == adapter || TransactionalEditingDomain.class == adapter) { + return transactionalEditingDomain; + } + + /* + * Return context used for undo/redo. All papyrus views should use this + * context. The prefer way to get this is to use undoContext = + * servicesRegistry.getService(IUndoContext.class); + */ + if (IUndoContext.class == adapter) { + return undoContext; + } + + // EMF requirements + if (IEditingDomainProvider.class == adapter) { + return this; + } + + if (adapter == ISelection.class) { + return getSite().getSelectionProvider().getSelection(); + } + + if (adapter == IReloadableEditor.class) { + return createReloadAdapter(); + } + + return super.getAdapter(adapter); + } + + /** + * Init the editor. + */ + @Override + public void init(IEditorSite site, IEditorInput input) throws PartInitException { + // Init super + super.init(site, input); + + // Set editor name + setPartName(input.getName()); + + initContents(); + } + + @Override + public void createPartControl(Composite parent) { + super.createPartControl(parent); + + // Fire the PreDisplay event synchronously, so that listeners can continue + // setting up the UI before the contents are actually rendered fully + getLifecycleManager().firePreDisplay(this); + + // Fire the PostDisplay event asynchronously, to leave time to the Eclipse + // framework to actually display the contents of the editor + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + // Because we are asynchronous, the editor may already have been disposed + // (Especially in the case of tests running in the UI Thread) + if (servicesRegistry == null) { + return; + } + getLifecycleManager().firePostDisplay(CoreMultiDiagramEditor.this); + } + }); + + } + + protected void loadModelAndServices() throws PartInitException { + // Create ServicesRegistry and register services + servicesRegistry = createServicesRegistry(); + + // Add itself as a service + servicesRegistry.add(IMultiDiagramEditor.class, 1, this); + + // Create lifeCycle event provider and the event that is used when the editor fire a save event. + // lifeCycleEventsProvider = new LifeCycleEventsProvider(); + // lifeCycleEvent = new DoSaveEvent(servicesRegistry, this); + // servicesRegistry.add(ILifeCycleEventsProvider.class, 1, lifeCycleEventsProvider); + + // register services + servicesRegistry.add(ActionBarContributorRegistry.class, 1, getActionBarContributorRegistry()); + // servicesRegistry.add(TransactionalEditingDomain.class, 1, transactionalEditingDomain); + // servicesRegistry.add(DiResourceSet.class, 1, resourceSet); + + // Create and initalize editor icons service + // PageIconsRegistry pageIconsRegistry = new PageIconsRegistry(); + // PluggableEditorFactoryReader editorReader = new PluggableEditorFactoryReader(Activator.PLUGIN_ID); + // editorReader.populate(pageIconsRegistry); + // servicesRegistry.add(IPageIconsRegistry.class, 1, pageIconsRegistry); + + + // Create PageModelRegistry requested by content provider. + // Also populate it from extensions. + // PageModelFactoryRegistry pageModelRegistry = new PageModelFactoryRegistry(); + // editorReader.populate(pageModelRegistry, servicesRegistry); + + // TODO : create appropriate Resource for the contentProvider, and pass it here. + // This will allow to remove the old sash stuff. + // setContentProvider(createPageProvider(pageModelRegistry, resourceSet.getDiResource(), transactionalEditingDomain)); + // servicesRegistry.add(ISashWindowsContentProvider.class, 1, getContentProvider()); + // servicesRegistry.add(IPageMngr.class, 1, getIPageMngr()); + + // register a basic label provider + // adapter factory used by EMF objects + AdapterFactory factory = null; + try { + EditingDomain domain = ServiceUtils.getInstance().getTransactionalEditingDomain(servicesRegistry); + if (domain instanceof AdapterFactoryEditingDomain) { + // Use the adapter factory already provided by this editing domain + factory = ((AdapterFactoryEditingDomain) domain).getAdapterFactory(); + } + } catch (ServiceException e) { + // OK, there's no editing domain. That's fine + } + + if (factory == null) { + // Must create a new adapter factory + factory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE); + } + + /** label provider for EMF objects */ + ILabelProvider labelProvider = new AdapterFactoryLabelProvider(factory) { + + /** + * This implements {@link ILabelProvider}.getText by forwarding it + * to an object that implements {@link IItemLabelProvider#getText + * IItemLabelProvider.getText} + */ + @Override + public String getText(Object object) { + // Get the adapter from the factory. + // + IItemLabelProvider itemLabelProvider = (IItemLabelProvider) adapterFactory.adapt(object, IItemLabelProvider.class); + if (object instanceof EObject) { + if (((EObject) object).eIsProxy()) { + return "Proxy - " + object; + } + } + return itemLabelProvider != null ? itemLabelProvider.getText(object) : object == null ? "" : object.toString(); + } + }; + servicesRegistry.add(ILabelProvider.class, 1, labelProvider); + + EditorLifecycleManager lifecycleManager = new EditorLifecycleManagerImpl(); + servicesRegistry.add(EditorLifecycleManager.class, 1, lifecycleManager, ServiceStartKind.LAZY); + + // Start servicesRegistry + URI uri; + IEditorInput input = getEditorInput(); + if (input instanceof IFileEditorInput) { + uri = URI.createPlatformResourceURI(((IFileEditorInput) input).getFile().getFullPath().toString(), true); + } else if (input instanceof URIEditorInput) { + uri = ((URIEditorInput) input).getURI(); + } else { + uri = URI.createURI(((IURIEditorInput) input).getURI().toString()); + } + + try { + // Start the ModelSet first, and load if from the specified File. + // Also start me so that I may be retrieved from the registry by other services + List<Class<?>> servicesToStart = new ArrayList<Class<?>>(1); + servicesToStart.add(ModelSet.class); + servicesToStart.add(IMultiDiagramEditor.class); + + servicesRegistry.startServicesByClassKeys(servicesToStart); + + resourceSet = servicesRegistry.getService(ModelSet.class); + resourceSet.loadModels(uri); + + // start remaining services + servicesRegistry.startRegistry(); + } catch (ModelMultiException e) { + try { + // with the ModelMultiException it is still possible to open the + // editors that's why the service registry is still started + servicesRegistry.startRegistry(); + warnUser(e); + } catch (ServiceException e1) { + log.error(e); + // throw new PartInitException("could not initialize services", e); //$NON-NLS-1$ + } + } catch (ServiceException e) { + log.error(e); + // throw new PartInitException("could not initialize services", e); + } + + + // Get required services + + try { + transactionalEditingDomain = servicesRegistry.getService(TransactionalEditingDomain.class); + sashModelMngr = servicesRegistry.getService(DiSashModelManager.class); + + saveAndDirtyService = servicesRegistry.getService(ISaveAndDirtyService.class); + undoContext = servicesRegistry.getService(IUndoContext.class); + + servicesRegistry.getService(ILanguageService.class).addLanguageChangeListener(createLanguageChangeListener()); + } catch (ServiceException e) { + log.error("A required service is missing.", e); + // if one of the services above fail to start, the editor can't run + // => stop + throw new PartInitException("could not initialize services", e); + } + + + // Listen on input changed from the ISaveAndDirtyService + editorInputChangedListener = new EditorInputChangedListener(this); + saveAndDirtyService.addInputChangedListener(editorInputChangedListener); + getLifecycleManager().firePostInit(this); + } + + private ILanguageChangeListener createLanguageChangeListener() { + return new ILanguageChangeListener() { + + @Override + public void languagesChanged(LanguageChangeEvent event) { + // Re-load the editor if languages changed, because new ModelSet configurations may be required + if (event.getType() == LanguageChangeEvent.ADDED) { + new UIJob(getSite().getShell().getDisplay(), NLS.bind("Reload editor {0}", getTitle())) { + + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + IStatus result = Status.OK_STATUS; + monitor = SubMonitor.convert(monitor, IProgressMonitor.UNKNOWN); + + try { + ISashWindowsContainer container = getISashWindowsContainer(); + if ((container != null) && !container.isDisposed()) { + IReloadableEditor.ReloadReason reason = IReloadableEditor.ReloadReason.RESOURCES_CHANGED; + + DirtyPolicy dirtyPolicy = DirtyPolicy.getDefault(); + try { + IReloadableEditor.Adapter.getAdapter(CoreMultiDiagramEditor.this).reloadEditor(resourceSet.getResources(), reason, dirtyPolicy); + } catch (CoreException e) { + result = e.getStatus(); + } + } + } finally { + monitor.done(); + } + + return result; + } + }.schedule(); + } + } + }; + } + + private InternalEditorLifecycleManager getLifecycleManager() { + // I've been disposed + if (servicesRegistry == null) { + return null; + } + try { + return (InternalEditorLifecycleManager) servicesRegistry.getService(EditorLifecycleManager.class); + } catch (ServiceException ex) { + Activator.log.error(ex); + } + return null; + } + + protected void loadNestedEditors() throws PartInitException { + ISashWindowsContentProvider contentProvider = null; + try { + contentProvider = servicesRegistry.getService(ISashWindowsContentProvider.class); + } catch (ServiceException ex) { + log.error("A required service is missing.", ex); + // if one of the services above fail to start, the editor can't run + // => stop + throw new PartInitException("could not initialize services", ex); + } + + // Set the content provider providing editors. + setContentProvider(contentProvider); + + // Listen on contentProvider changes + if (contentChangedListener == null) { + contentChangedListener = new ContentChangedListener(); + } + sashModelMngr.getSashModelContentChangedProvider().addListener(contentChangedListener); + + IEditorInput input = getEditorInput(); + + if (input instanceof IPapyrusPageInput) { + IPapyrusPageInput papyrusPageInput = (IPapyrusPageInput) input; + final IPageManager pageManager = getIPageManager(); + + if (papyrusPageInput.closeOtherPages()) { + pageManager.closeAllOpenedPages(); + } + + for (URI pageIdentifierURI : papyrusPageInput.getPages()) { + final EObject pageIdentifier = resourceSet.getEObject(pageIdentifierURI, true); + if (!pageManager.allPages().contains(pageIdentifier)) { + Activator.log.warn("The object " + pageIdentifier + " does not reference an existing page"); + continue; + } + + if (pageManager.isOpen(pageIdentifier)) { + pageManager.selectPage(pageIdentifier); + } else { + pageManager.openPage(pageIdentifier); + } + } + } + } + + protected void warnUser(ModelMultiException e) { + Activator.log.error(e); + MessageDialog.openError(getSite().getShell(), "Error", String.format("Your model is corrupted, invalid links have been found :\n" + "%s" + "It is recommended to fix it before editing it", e.getMessage())); + } + + /** + * Activate this editor. Called after the SWT.control is created. + */ + @Override + protected void activate() { + super.activate(); + + initFolderTabMenus(); + + try { + // Register ISashWindowsContainer as service + // Should be done only once the container is ready. + getServicesRegistry().add(ISashWindowsContainer.class, 1, getISashWindowsContainer()); + getServicesRegistry().startServicesByClassKeys(ISashWindowsContainer.class); + // Let the IPageMngr use the ISashWindowsContainer to discover current folder + // This should be done after SashWindowContainer initialization. + // DiSashModelManager sashModelManager = getServicesRegistry().getService(DiSashModelManager.class); + sashModelMngr.setCurrentFolderAndPageMngr(getISashWindowsContainer()); + + } catch (ServiceException e) { + log.error(e); + } + + } + + /** + * Init the contextual menu shown in the folder tabs. This popup menu is + * contributed by the help of Eclipse extensions, using the Commands + * framework. I.e, to add a menu item, create a menu, a command and an + * handler in the extension. + */ + protected void initFolderTabMenus() { + ISashWindowsContainer container = getISashWindowsContainer(); + + // TODO : use a constant + MenuManager menuManager = new MenuManager("tabmenu"); + menuManager.add(new Separator("tabcommands")); + menuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + container.setFolderTabMenuManager(menuManager); + + // TODO : use a constant + getSite().registerContextMenu("org.eclipse.papyrus.infra.core.editor.ui.tabmenu", menuManager, getSite().getSelectionProvider()); + + } + + /** + * Overrides getPropertySheetPage. + * + * {@inheritDoc} + * + * @see org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor#getPropertySheetPage() + */ + public IPropertySheetPage getPropertySheetPage() { + IPropertySheetPage propertiesPage = new MultiDiagramPropertySheetPage(this); + propertiesPages.add(propertiesPage); + return propertiesPage; + } + + @Override + public void dispose() { + for (IPropertySheetPage propertiesPage : this.propertiesPages) { + propertiesPage.dispose(); + } + propertiesPages.clear(); + + // Forget the outline page(s) + contentOutlineRegistry = null; + + super.dispose(); + } + + private IReloadableEditor createReloadAdapter() { + + return new IReloadableEditor() { + + @Override + public void reloadEditor(Collection<? extends Resource> triggeringResources, ReloadReason reason, DirtyPolicy dirtyPolicy) throws CoreException { + // Attempt to re-load, later + pendingReload.set(new DeferredReload(triggeringResources, reason, dirtyPolicy)); + + // If I am already active, then do it now. Or, if we're not going to ask the user about it, also do it now + IWorkbenchPage page = getSite().getPage(); + if ((page.getActiveEditor() == CoreMultiDiagramEditor.this) || (dirtyPolicy != DirtyPolicy.PROMPT_TO_SAVE)) { + pendingReload.get().reload(); + } + } + + @Override + public void addEditorReloadListener(IEditorReloadListener listener) { + reloadListeners.addIfAbsent(listener); + } + + @Override + public void removeEditorReloadListener(IEditorReloadListener listener) { + reloadListeners.remove(listener); + } + }; + } + + private void addSelfReloadListener() { + createReloadAdapter().addEditorReloadListener(new IEditorReloadListener() { + + @Override + public void editorAboutToReload(EditorReloadEvent event) { + event.putContext(new MultiDiagramEditorSelectionContext(event.getEditor())); + } + + @Override + public void editorReloaded(EditorReloadEvent event) { + ((MultiDiagramEditorSelectionContext) event.getContext()).restore(event.getEditor()); + } + }); + } + + /** + * Register an action to be run when I am closed. Any number of such actions may + * be added. note that close actions also run on re-load, which behaves to all + * outward appearances like a close and re-open. + * + * @param closeAction + * an action to run when I am closed + */ + public void onClose(Runnable closeAction) { + closeActions.add(closeAction); + } + + @Override + protected void deactivate() { + getLifecycleManager().fireBeforeClose(this); + if (sashModelMngr != null) { + sashModelMngr.getSashModelContentChangedProvider().removeListener(contentChangedListener); + } + + super.deactivate(); + + // dispose available service + if (servicesRegistry != null) { + try { + servicesRegistry.disposeRegistry(); + servicesRegistry = null; + } catch (ServiceMultiException e) { + log.error(e); + } + } + + if (contentChangedListener != null) { + this.contentChangedListener = null; + } + + if (editorInputChangedListener != null) { + this.editorInputChangedListener.dispose(); + this.editorInputChangedListener = null; + } + + for (Runnable next : closeActions) { + try { + next.run(); + } catch (Exception e) { + Activator.log.error("Uncaught exception in close action", e); //$NON-NLS-1$ + } + } + closeActions.clear(); + + transactionalEditingDomain = null; + resourceSet = null; + undoContext = null; + saveAndDirtyService = null; + sashModelMngr = null; + } + + void initContents() throws PartInitException { + loadModelAndServices(); + loadNestedEditors(); + } + + @Override + public void setFocus() { + super.setFocus(); + + DeferredReload reload = pendingReload.get(); + if (reload != null) { + reload.reload(); + } + } + + private void doReload() throws CoreException { + final IWorkbenchPage page = getSite().getPage(); + final IWorkbenchPart activePart = page.getActivePart(); + final IEditorPart activeEditor = page.getActiveEditor(); + + final Iterable<? extends IEditorReloadListener> listeners = ImmutableList.copyOf(reloadListeners); + final EditorReloadEvent event = new EditorReloadEvent(CoreMultiDiagramEditor.this); + + try { + event.dispatchEditorAboutToReload(listeners); + + deactivate(); + + initContents(); + + activate(); + + // My self-listener will be first, to ensure that the pages are all restored before dependents run + event.dispatchEditorReloaded(listeners); + } finally { + event.dispose(); + + // Ensure that the editor previously active is active again (if it still exists) + if ((activeEditor != null) && page.isPartVisible(activeEditor)) { + page.activate(activeEditor); + } + + // Ensure that the part previously active is active again (if it still exists and is not the active editor) + if ((activePart != null) && (activePart != activeEditor) && page.isPartVisible(activePart)) { + page.activate(activePart); + } + } + + } + + /** + * Overrides doSave. + * + * {@inheritDoc} + * + * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public void doSave(IProgressMonitor monitor) { + + saveAndDirtyService.doSave(monitor); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isDirty() { + // May happen if the editor has not yet been initialized. In this case, the editor cannot be dirty, so we simply return false. + // Bug 410286: The isDirty() method can also be called /after/ the editor has been disposed. Most likely an Eclipse bug? + if (saveAndDirtyService == null) { + return false; + } + return saveAndDirtyService.isDirty(); + } + + /** + * Overrides doSaveAs. + * + * {@inheritDoc} + * + * @see org.eclipse.ui.part.EditorPart#doSaveAs() + */ + @Override + public void doSaveAs() { + + saveAndDirtyService.doSaveAs(); + } + + /** + * Overrides isSaveAsAllowed. + * + * {@inheritDoc} + * + * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed() + */ + @Override + public boolean isSaveAsAllowed() { + return true; + } + + /** + * Overrides getContributorId. + * + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor#getContributorId() + */ + @Override + public String getContributorId() { + // return Activator.PLUGIN_ID; + return "TreeOutlinePage"; + + } + + // implements IDiagramWorkbenchPart to restore GMF standard behavior + // and delegate to the activeEditor + + /** + * Overrides getDiagram. + * + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart#getDiagram() + */ + // public org.eclipse.gmf.runtime.notation.Diagram getDiagram() { + // IEditorPart activeEditor = getActiveEditor(); + // if(activeEditor instanceof DiagramEditor) { + // return ((DiagramEditor)activeEditor).getDiagram(); + // } else { + // return null; + // } + // } + + /** + * This method is called from a GMF diagram. It should only be called from GMF diagram code. Normally, the Diagram under the Mouse is a GMF + * Diagram. The active Diagram can be another Diagram, not + * under the mouse. This is a GMF issue. + */ + // public DiagramEditPart getDiagramEditPart() { + // + // // Get the editor under the mouse + // // IEditorPart activeEditor = rootContainer.getEditorUnderMouse(); + // IEditorPart activeEditor = getActiveEditor(); + // if(activeEditor == null) { + // return null; + // } + // // IEditorPart activeEditor = getActiveEditor(); + // if(activeEditor instanceof DiagramEditor) { + // return ((DiagramEditor)activeEditor).getDiagramEditPart(); + // } else { + // // This case should never happen. + // // Return null, as the GMF runtime now support it (since 093009) + // return null; + // } + // } + + /** + * Overrides getDiagramGraphicalViewer. + * + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart#getDiagramGraphicalViewer() + */ + // public IDiagramGraphicalViewer getDiagramGraphicalViewer() { + // IEditorPart activeEditor = getActiveEditor(); + // if(activeEditor instanceof DiagramEditor) { + // return ((DiagramEditor)activeEditor).getDiagramGraphicalViewer(); + // } else { + // return null; + // } + // } + + /** + * Overrides getEditingDomain. + * + * {@inheritDoc} + * + * @see org.eclipse.emf.edit.domain.IEditingDomainProvider#getEditingDomain() + */ + @Override + public EditingDomain getEditingDomain() { + return transactionalEditingDomain; + } + + /** + * Throws an UnsupportedOperationException. + * + * @see org.eclipse.papyrus.infra.core.editor.IMultiDiagramEditor#getDiagramEditDomain() + */ + // public DiagramEditDomain getDiagramEditDomain() { + // throw new UnsupportedOperationException("Not implemented. Should not be called."); + // } + + + /** + * Change the editor input.<BR> + * <U>Note</U>: that method should be called within the UI-Thread. + * + * @see org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor#setEditorInput(org.eclipse.ui.IEditorInput) + * + * @param newInput + * The new input + * @deprecated Not used anymore + */ + + @Override + @Deprecated + public void setEditorInput(IEditorInput newInput) { + setInputWithNotify(newInput); + setPartName(newInput.getName()); + } + + @Override + @Deprecated + public void gotoMarker(IMarker marker) { + IWorkbench wb = PlatformUI.getWorkbench(); + IWorkbenchPage page = wb.getActiveWorkbenchWindow().getActivePage(); + boolean first = true; + for (IViewReference view : page.getViewReferences()) { + // no longer restrict to model explorer (see bug 387578) + IWorkbenchPart part = view.getPart(false); + if (part instanceof IGotoMarker) { + // activate first view implementing the IGotoMarker interface + if (first) { + page.activate(view.getPart(false)); + first = false; + } + ((IGotoMarker) part).gotoMarker(marker); + } + } + } + + private boolean needsRefresh; + + protected void scheduleRefresh() { + needsRefresh = true; + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + refreshTabs(); + } + }); + } + + @Override + protected void refreshTabs() { + if (!needsRefresh) { + return; + } + needsRefresh = false; + super.refreshTabs(); + } + + @Override + public synchronized IEditorPart getActiveEditor() { + refreshTabs(); + return super.getActiveEditor(); + } + + private final class DeferredReload extends IReloadableEditor.Adapter { + + private final Collection<? extends Resource> triggeringResources; + + private final ReloadReason reason; + + private final DirtyPolicy dirtyPolicy; + + DeferredReload(Collection<? extends Resource> triggeringResources, ReloadReason reason, DirtyPolicy dirtyPolicy) { + super(CoreMultiDiagramEditor.this); + + this.triggeringResources = ImmutableSet.copyOf(triggeringResources); + this.reason = reason; + this.dirtyPolicy = dirtyPolicy; + } + + void reload() { + try { + reloadEditor(triggeringResources, reason, dirtyPolicy); + } catch (CoreException e) { + // Failed to properly unload/load in place, so just close + getSite().getPage().closeEditor(CoreMultiDiagramEditor.this, false); + + StatusManager.getManager().handle(e.getStatus(), StatusManager.LOG | StatusManager.SHOW); + } + } + + @Override + public void reloadEditor(Collection<? extends Resource> triggeringResources, ReloadReason reason, DirtyPolicy dirtyPolicy) throws CoreException { + if (!pendingReload.compareAndSet(this, null)) { + return; + } + + final DirtyPolicy action = dirtyPolicy.resolve(CoreMultiDiagramEditor.this, triggeringResources, reason); + + if ((action == DirtyPolicy.SAVE) && isDirty()) { + doSave(new NullProgressMonitor()); + } + + switch (action) { + case SAVE: + case DO_NOT_SAVE: + if (reason.shouldReload(triggeringResources)) { + // Attempt to re-load + doReload(); + } else { + // Just close 'er down + getSite().getPage().closeEditor(CoreMultiDiagramEditor.this, false); + } + break; + case IGNORE: + // Pass + break; + default: + throw new IllegalArgumentException("Invalid resolution of editor re-load dirty policy: " + action); //$NON-NLS-1$ + } + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/DiSashModelManagerServiceFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/DiSashModelManagerServiceFactory.java new file mode 100644 index 00000000000..1f13fedb84b --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/DiSashModelManagerServiceFactory.java @@ -0,0 +1,114 @@ +/***************************************************************************** + * Copyright (c) 2013 Cedric Dumoulin. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModelUtils; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.DiSashModelManager; +import org.eclipse.papyrus.infra.core.services.IServiceFactory; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editorsfactory.PageModelFactoryRegistry; +import org.eclipse.papyrus.infra.ui.extension.diagrameditor.PluggableEditorFactoryReader; + +/** + * Service Factory to create the {@link DiSashModelManager} service. + * + * @author cedric dumoulin + * + */ +public class DiSashModelManagerServiceFactory implements IServiceFactory { + + private TransactionalEditingDomain transactionalEditingDomain; + + private SashModel sashModel; + + private DiSashModelManager sashModelMngr; + + private ServicesRegistry servicesRegistry; + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + * @throws ServiceException + */ + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + + this.servicesRegistry = servicesRegistry; + // Get required service + transactionalEditingDomain = servicesRegistry.getService(TransactionalEditingDomain.class); + + // Get the model holding the contentProvider + sashModel = SashModelUtils.getSashModelChecked(servicesRegistry); + + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + * + * @throws ServiceException + */ + @Override + public void startService() throws ServiceException { + + // Read declared editors + PageModelFactoryRegistry pageModelRegistry = new PageModelFactoryRegistry(); + PluggableEditorFactoryReader editorReader = new PluggableEditorFactoryReader(Activator.PLUGIN_ID); + editorReader.populate(pageModelRegistry, servicesRegistry); + + if (sashModel.getResource() == null) { + throw new ServiceException("Can't start " + this.getClass().getSimpleName() + "'. Required model (SashModel) should be loaded prior starting the service."); //$NON-NLS-1$ //$NON-NLS-2$ + } + + // create the service + sashModelMngr = new DiSashModelManager(pageModelRegistry, sashModel.getResource(), transactionalEditingDomain); + + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#disposeService() + * + * @throws ServiceException + */ + @Override + public void disposeService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IServiceFactory#createServiceInstance() + * + * @return + * @throws ServiceException + */ + @Override + public Object createServiceInstance() throws ServiceException { + + // Start locally the service if needed. + // Question: Can createServiceInstance() method be called before + // startService() is called ? + if (sashModelMngr == null) { + startService(); + } + + return sashModelMngr; + } + + + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/DiSashModelMngrServiceFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/DiSashModelMngrServiceFactory.java new file mode 100644 index 00000000000..ad0806cff77 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/DiSashModelMngrServiceFactory.java @@ -0,0 +1,101 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModelUtils; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.DiSashModelMngr; +import org.eclipse.papyrus.infra.core.services.IServiceFactory; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editorsfactory.PageModelFactoryRegistry; +import org.eclipse.papyrus.infra.ui.extension.diagrameditor.PluggableEditorFactoryReader; + +/** + * Service Factory to create the {@link DiSashModelMngr} service. + * + * @author cedric dumoulin + * + */ +public class DiSashModelMngrServiceFactory implements IServiceFactory { + + private TransactionalEditingDomain transactionalEditingDomain; + + private SashModel sashModel; + + private DiSashModelMngr sashModelMngr; + + private ServicesRegistry servicesRegistry; + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + * @throws ServiceException + */ + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + + this.servicesRegistry = servicesRegistry; + // Get required service + transactionalEditingDomain = servicesRegistry.getService(TransactionalEditingDomain.class); + + // Get the model holding the contentProvider + sashModel = SashModelUtils.getSashModelChecked(servicesRegistry); + + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + * + * @throws ServiceException + */ + @Override + public void startService() throws ServiceException { + + // Read declared editors + PageModelFactoryRegistry pageModelRegistry = new PageModelFactoryRegistry(); + PluggableEditorFactoryReader editorReader = new PluggableEditorFactoryReader(Activator.PLUGIN_ID); + editorReader.populate(pageModelRegistry, servicesRegistry); + + if (sashModel.getResource() == null) { + throw new ServiceException("Can't start " + this.getClass().getSimpleName() + "'. Required model (SashModel) should be loaded prior starting the service."); //$NON-NLS-1$ //$NON-NLS-2$ + } + + // create the service + sashModelMngr = new DiSashModelMngr(pageModelRegistry, sashModel.getResource()); + + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#disposeService() + * + * @throws ServiceException + */ + @Override + public void disposeService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IServiceFactory#createServiceInstance() + * + * @return + * @throws ServiceException + */ + @Override + public Object createServiceInstance() throws ServiceException { + + // Start locally the service if needed. + // Question: Can createServiceInstance() method be called before + // startService() is called ? + if (sashModelMngr == null) { + startService(); + } + + return sashModelMngr; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IMultiDiagramEditor.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IMultiDiagramEditor.java new file mode 100644 index 00000000000..4193c25cf3e --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IMultiDiagramEditor.java @@ -0,0 +1,93 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorSite; + +/** + * Interface implemented by the main multipage editor. This interface list the + * methods available to diagram editors. Diagram editors can relies on this + * interface to retrieve services from the main multi diagram editor. <br> + * This interface should stay minimalist, as the editor is not designed to + * handle the services itself. A service should be retrieved by using {@link #getServicesRegistry()}. + * + * + * @author cedric dumoulin + * + * TODO remove extends IEditingDomainProvider. This interface should be + * independant of any technology (EMF, GMF, ...). If the EditingDomain + * is required, it can be retrieved by the registry. + * + */ +public interface IMultiDiagramEditor extends IEditorPart { + + /** + * Returns the service registry associated to the editor. + * + * @return the servicesRegistry The registry. + */ + public ServicesRegistry getServicesRegistry(); + + /** + * Return the editor site. + * + * @return + */ + @Override + public IEditorSite getEditorSite(); + + /** + * Get the editor input. + * + * @return + */ + @Override + public IEditorInput getEditorInput(); + + /** + * Change the editor input. + * + * @param newInput + * The new input. + * @deprecated No replacement. Input can't be changed on multi editors. + */ + @Deprecated + public void setEditorInput(IEditorInput newInput); + + /** + * Returns the edit domain shared among editors + * + * @return the edit domain shared among editors + * @deprecated Use {@link #getServicesRegistry()} or {@link #getAdapter(Class)} + */ + // FIXME Remove it (GMF dependency) + // public DiagramEditDomain getDiagramEditDomain(); + + /** + * Get the currently active nested Editor. + */ + public IEditorPart getActiveEditor(); + + /** + * Get the property sheet page associated to the Editor. + * + * @return the property sheet page associated to the Editor. + * @deprecated Use {@link #getServicesRegistry()} or {@link #getAdapter(Class)} + */ + // @Deprecated + // public IPropertySheetPage getPropertySheetPage(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IPapyrusPageInput.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IPapyrusPageInput.java new file mode 100644 index 00000000000..50594efbec8 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IPapyrusPageInput.java @@ -0,0 +1,35 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.ui.IEditorInput; + +/** + * An IEditorInput used to reference the page(s) to open + * + * @author Camille Letavernier + * + */ +public interface IPapyrusPageInput extends IEditorInput { + + /** + * @return the list of pages to open + */ + public URI[] getPages(); + + /** + * + * @return true if the editor should close all other pages + */ + public boolean closeOtherPages(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IReloadableEditor.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IReloadableEditor.java new file mode 100644 index 00000000000..92bff539cf7 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/IReloadableEditor.java @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2014, 2016 CEA, Christian W. Damus, 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: + * Christian W. Damus (CEA) - Initial API and implementation + * Christian W. Damus - bug 485220 + * + */ +package org.eclipse.papyrus.infra.ui.editor; + +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.ui.URIEditorInput; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.tools.util.CoreExecutors; +import org.eclipse.papyrus.infra.tools.util.PlatformHelper; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editor.reload.IEditorReloadListener; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.ide.IDE; + + +/** + * An {@linkplain IAdaptable adapter protocol} for editors that know how to internally + * reload themselves without disturbing the workbench window's perspective layout. + */ +public interface IReloadableEditor { + + /** + * Reloads me in-place in the perspective layout. + * + * @param triggeringResources + * the resources that have changed in some way, triggering re-load + * @param reason + * the reason why the re-load is being requested + * @param dirtyPolicy + * how the client would like to handle the case of a dirty editor + * + * @throws CoreException + * on any failure to unload, reload, or whatever + */ + void reloadEditor(Collection<? extends Resource> triggeringResources, ReloadReason reason, DirtyPolicy dirtyPolicy) throws CoreException; + + void addEditorReloadListener(IEditorReloadListener listener); + + void removeEditorReloadListener(IEditorReloadListener listener); + + /** + * An enumeration of the reason why some resources that an editor has loaded are triggering a re-load (or close). + */ + enum ReloadReason { + /** Resources have changed in persistent storage. */ + RESOURCES_CHANGED, + /** Resources have been deleted from persistent storage. */ + RESOURCES_DELETED; + + /** + * Queries whether, under ordinary circumstances, the editor should attempt to re-load to pick up changes in its dependent resources. + * + * @param triggeringResources + * the resources triggering re-load (or close) + * + * @return whether the editor should re-load + */ + public boolean shouldReload(Collection<? extends Resource> triggeringResources) { + return this != RESOURCES_DELETED; + } + } + + /** + * An enumeration of policies that clients may request to govern what to do with the editor before re-load (or close) if it should happen to be + * dirty. Note that editors are free to honour the requested policy or not, according to their needs. + */ + enum DirtyPolicy { + /** + * Save the editor without prompting. + */ + SAVE, + /** + * Do not save the editor; just discard pending changes and re-load (or close). + */ + DO_NOT_SAVE, + /** + * Do not re-load (or close) the editor; just keep pending changes and deal with conflicts later. + */ + IGNORE, + /** + * Prompt the user to inquire whether to save, discard pending changes, or not re-load (or close) at all. + * Note that the user prompt must always result in one of the other policies being actually applied. + */ + PROMPT_TO_SAVE { + + @Override + public DirtyPolicy resolve(IEditorPart editor, final Collection<? extends Resource> triggeringResources, final ReloadReason reason) throws CoreException { + final boolean dirty = editor.isDirty(); + + if (!dirty) { + if (reason.shouldReload(triggeringResources)) { + // Just re-load it. Simple + return DO_NOT_SAVE; + } else if (isPrincipalResourceAffected(editor, triggeringResources)) { + // Just close it. Also simple + return DO_NOT_SAVE; + } + } + + final String editorName = getEditorName(editor); + + final boolean allReadOnly = allReadOnly(triggeringResources); + final String promptTitle; + final String promptIntro; + final String saveOption; + final String dontSaveOption; + final String ignoreOption = "Ignore"; + + switch (reason) { + case RESOURCES_DELETED: + promptTitle = "Resources Deleted"; + promptIntro = NLS.bind("Some resources used by \"{0}\" have been deleted.", editorName); + saveOption = "Save and Close"; + dontSaveOption = "Close Editor"; + break; + default: + promptTitle = "Resources Changed"; + promptIntro = NLS.bind("Some resources used by \"{0}\" have changed.", editorName); + saveOption = "Save and Re-open"; + dontSaveOption = "Re-open Editor"; + break; + } + + Callable<DirtyPolicy> result; + + if (allReadOnly) { + // Only read-only models have changed. We (most likely) won't save them within this current editor. As they are already loaded, we can just continue. + result = new Callable<DirtyPolicy>() { + + @Override + public DirtyPolicy call() { + Shell parentShell = Display.getCurrent().getActiveShell(); + + final String message; + final String[] options; + if (dirty) { + message = promptIntro + " Note: all these resources are loaded in read-only mode and won't be overridden if you choose to save. Unsaved changes will be lost."; + options = new String[] { saveOption, dontSaveOption, ignoreOption }; + } else { + message = promptIntro; + options = new String[] { dontSaveOption, ignoreOption }; + } + + final MessageDialog dialog = new MessageDialog(parentShell, promptTitle, null, message, MessageDialog.WARNING, options, 0) { + + @Override + protected void setShellStyle(int newShellStyle) { + super.setShellStyle(newShellStyle | SWT.SHEET); + } + }; + final int answer = dialog.open(); + + DirtyPolicy result; + + if (answer == SWT.DEFAULT) { + // User hit Esc or dismissed the dialog with the window manager button. Ignore + result = IGNORE; + } else if (dirty) { + result = values()[answer]; + } else { + result = values()[answer + 1]; // Account for the missing "Save and Xxx" option + } + + return result; + } + }; + } else { + // At least one read-write resource has changed. Potential conflicts. + result = new Callable<DirtyPolicy>() { + + @Override + public DirtyPolicy call() { + DirtyPolicy result = IGNORE; + + final Shell parentShell = Display.getCurrent().getActiveShell(); + final String action = reason.shouldReload(triggeringResources) ? "re-open" : "close"; + final String message; + + if (dirty) { + message = promptIntro + NLS.bind(" Do you wish to {0} the current editor? Unsaved changes will be lost.", action); + } else { + message = promptIntro + NLS.bind(" Do you wish to {0} the current editor?", action); + } + + final String[] options = { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }; + final MessageDialog dialog = new MessageDialog(parentShell, promptTitle, null, message, MessageDialog.WARNING, options, 0) { + + @Override + protected void setShellStyle(int newShellStyle) { + super.setShellStyle(newShellStyle | SWT.SHEET); + } + }; + if (dialog.open() == 0) { + result = DO_NOT_SAVE; + } + + return result; + } + }; + } + + try { + return CoreExecutors.getUIExecutorService().syncCall(result); + } catch (ExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to determine dirty policy for editor re-load.", e)); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Interrupted in determining dirty policy for editor re-load.", e)); + } + } + }; + + /** + * Queries the default dirty policy currently in effect. The default-default is {@link #PROMPT_TO_SAVE}. + * + * @return the default policy + */ + public static DirtyPolicy getDefault() { + return PROMPT_TO_SAVE; + } + + /** + * Resolves me to a specific actionable policy, based on the resources that are triggering re-load (or close) and the reason. + * + * @param editor + * the editor to be re-loaded + * @param triggeringResources + * the resources (possibly an empty collection) that have changed + * @param reloadReason + * the reason why re-load (or close) is triggered + * + * @return the specific policy to implement in re-loading the editor + * + * @throws CoreException + * on failure to resolve the specific policy + */ + public DirtyPolicy resolve(IEditorPart editor, Collection<? extends Resource> triggeringResources, ReloadReason reason) throws CoreException { + return this; + } + + String getEditorName(IEditorPart editor) { + ModelSet modelSet = getModelSet(editor); + return (modelSet == null) ? editor.getTitle() : modelSet.getURIWithoutExtension().lastSegment(); + } + + private ModelSet getModelSet(IEditorPart editor) { + ModelSet result = null; + + if (editor instanceof IMultiDiagramEditor) { + try { + result = ((IMultiDiagramEditor) editor).getServicesRegistry().getService(ModelSet.class); + } catch (ServiceException e) { + // No problem. We have a fall-back + Activator.log.error(e); + } + } + + return result; + } + + boolean isPrincipalResourceAffected(IEditorPart editor, Collection<? extends Resource> triggeringResources) { + boolean result = false; + + ModelSet modelSet = getModelSet(editor); + if (modelSet != null) { + URI principalURI = modelSet.getURIWithoutExtension(); + for (Resource next : triggeringResources) { + if (next.getURI().trimFileExtension().equals(principalURI)) { + result = true; + break; + } + } + } else { + URI principalURI = getURI(editor.getEditorInput()); + if (principalURI != null) { + for (Resource next : triggeringResources) { + if (next.getURI().equals(principalURI)) { + result = true; + break; + } + } + } + } + + return result; + } + + private URI getURI(IEditorInput input) { + URI result = null; + + if (input instanceof URIEditorInput) { + result = ((URIEditorInput) input).getURI(); + } else if (input instanceof IURIEditorInput) { + result = URI.createURI(((IURIEditorInput) input).getURI().toString()); + } + + return result; + } + + protected boolean allReadOnly(Collection<? extends Resource> resources) { + for (Resource resource : resources) { + EditingDomain domain = TransactionUtil.getEditingDomain(resource); + if ((domain == null) || !domain.isReadOnly(resource)) { + return false; + } + } + + return true; + } + } + + /** + * A convenience adapter for editors that don't actually know how to reload themselves in place. + * It simply closes the editor and then opens it again on the original input. + */ + class Adapter implements IReloadableEditor { + + private final IEditorPart editor; + + public Adapter(IEditorPart editor) { + super(); + + this.editor = editor; + } + + public static IReloadableEditor getAdapter(IMultiDiagramEditor editor) { + return PlatformHelper.getAdapter(editor, IReloadableEditor.class, () -> new Adapter(editor)); + } + + @Override + public void reloadEditor(Collection<? extends Resource> triggeringResources, ReloadReason reason, DirtyPolicy dirtyPolicy) throws CoreException { + final IWorkbenchPage page = editor.getSite().getPage(); + final IEditorInput currentInput = editor.getEditorInput(); + + final Display display = editor.getSite().getShell().getDisplay(); + + final String editorId = editor.getSite().getId(); + + final DirtyPolicy action = dirtyPolicy.resolve(editor, triggeringResources, reason); + final boolean save = action == DirtyPolicy.SAVE; + + if (save && editor.isDirty()) { + editor.doSave(new NullProgressMonitor()); + } + + if (action != DirtyPolicy.IGNORE) { + page.closeEditor(editor, save); + + // If resources were deleted, we close and don't re-open + if (reason.shouldReload(triggeringResources)) { + display.asyncExec(new Runnable() { + + @Override + public void run() { + try { + IDE.openEditor(page, currentInput, editorId); + } catch (PartInitException ex) { + Activator.log.error(ex); + } + } + }); + } + } + } + + @Override + public void addEditorReloadListener(IEditorReloadListener listener) { + // Don't need to track these listeners because I never properly reload an editor + } + + @Override + public void removeEditorReloadListener(IEditorReloadListener listener) { + // Don't need to track these listeners because I never properly reload an editor + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/MultiDiagramEditorSelectionContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/MultiDiagramEditorSelectionContext.java new file mode 100644 index 00000000000..fc0302004dd --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/MultiDiagramEditorSelectionContext.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor; + +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IComponentPage; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IEditorPage; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IPage; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageVisitor; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.core.utils.AdapterUtils; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editor.reload.CompositeReloadContext; +import org.eclipse.papyrus.infra.ui.editor.reload.DelegatingReloadContext; +import org.eclipse.papyrus.infra.ui.editor.reload.EMFSelectionContext; +import org.eclipse.papyrus.infra.ui.editor.reload.EditorReloadEvent; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + + +/** + * A {@linkplain EditorReloadEvent reload event} context that restores the selection of which editor page + * is active in an {@link IMultiDiagramEditor} that is reloaded and delegates to its pages to capture + * re-load context for each to restore whatever they need to restore (internal selections etc.). + */ +class MultiDiagramEditorSelectionContext extends CompositeReloadContext { + + private ISashWindowsContainer sashContainer; + + private final List<URI> resourcesToLoad; + + MultiDiagramEditorSelectionContext(IMultiDiagramEditor editor) { + super(); + + init(editor); + + resourcesToLoad = computeResourcesToLoad(editor); + + IPage active = sashContainer.getActiveSashWindowsPage(); + DiagramPageContext activePage = null; + + Set<IPage> visiblePages = Sets.newIdentityHashSet(); + visiblePages.addAll(sashContainer.getVisiblePages()); + + List<IPage> allPages = getAllPages(sashContainer); + + for (IPage page : allPages) { + final DelegatingReloadContext delegator = (page instanceof IEditorPage) ? add(new DelegatingReloadContext(((IEditorPage) page).getIEditorPart())) : null; + DiagramPageContext context; + + if (page == active) { + // This one will have the selection of the active page + context = new DiagramPageContext(new VisiblePageSelectionProvider(page), page, delegator); + activePage = context; + } else { + if (visiblePages.contains(page)) { + // This one must be selected in its folder in order to make it visible again + context = new DiagramPageContext(new VisiblePageSelectionProvider(page), page, delegator); + } else { + context = new DiagramPageContext(EmptySelectionProvider.INSTANCE, page, delegator); + } + + // We make sure always to restore the active page last + // so that it will not only be visible but also the + // over-all active page + add(context); + } + } + + if (activePage != null) { + // Restore this one last + add(activePage); + } + } + + @Override + public void dispose() { + sashContainer = null; + super.dispose(); + } + + private List<IPage> getAllPages(ISashWindowsContainer container) { + final List<IPage> result = Lists.newArrayList(); + + container.visit(new IPageVisitor() { + + @Override + public void accept(IEditorPage page) { + result.add(page); + } + + @Override + public void accept(IComponentPage page) { + result.add(page); + } + }); + + return result; + } + + private void init(IMultiDiagramEditor editor) { + sashContainer = AdapterUtils.adapt(editor, ISashWindowsContainer.class, null); + } + + void restore(IMultiDiagramEditor editor) { + init(editor); + + // Forcibly re-load all previously loaded resources to that + // (a) we don't lose imports that weren't yet used, and + // (b) we can restore selections in resources that wouldn't be loaded until proxies resolve later + reloadResources(editor); + + ISelectionProvider selectionProvider = new VisiblePageSelectionProvider(); + for (DiagramPageContext next : getReloadContexts(DiagramPageContext.class)) { + next.restore(selectionProvider); + } + } + + protected List<URI> computeResourcesToLoad(IMultiDiagramEditor editor) { + List<URI> result = null; + + ResourceSet rset = getResourceSet(editor); + if (rset != null) { + result = Lists.newArrayListWithCapacity(rset.getResources().size()); + + for (Resource next : rset.getResources()) { + if (next.isLoaded()) { + result.add(next.getURI()); + } + } + } + + return result; + } + + protected void reloadResources(IMultiDiagramEditor editor) { + if (resourcesToLoad != null) { + ResourceSet rset = getResourceSet(editor); + if (rset != null) { + for (URI next : resourcesToLoad) { + try { + rset.getResource(next, true); + } catch (Exception e) { + Activator.log.error("Failed to restore loaded resource: " + next, e); //$NON-NLS-1$ + } + } + } + } + } + + protected final ResourceSet getResourceSet(IMultiDiagramEditor editor) { + ResourceSet result = null; + + EditingDomain editingDomain = editor.getAdapter(EditingDomain.class); + if (editingDomain != null) { + result = editingDomain.getResourceSet(); + } + + return result; + } + + // + // Nested types + // + + private class DiagramPageContext extends EMFSelectionContext { + + private URI pageRef; + + private DelegatingReloadContext pageContext; + + DiagramPageContext(ISelectionProvider structuredSelectionProvider, IPage page, DelegatingReloadContext pageContext) { + super(structuredSelectionProvider); + + this.pageContext = pageContext; + this.pageRef = getToken(page.getRawModel()); + } + + @Override + public void restore(ISelectionProvider structuredSelectionProvider) { + IPage page = sashContainer.lookupModelPage(resolveToken(pageRef)); + + if ((pageContext != null) && (page instanceof IEditorPage)) { + pageContext.restore(((IEditorPage) page).getIEditorPart()); + } + + super.restore(structuredSelectionProvider); + } + + @Override + protected Object deresolveSelectableElement(Object selectableElement) { + return (selectableElement instanceof IPage) ? ((IPage) selectableElement).getRawModel() : super.deresolveSelectableElement(selectableElement); + } + + @Override + protected Object resolveSelectableElement(Object deresolved) { + return sashContainer.lookupModelPage(deresolved); + } + } + + private static class EmptySelectionProvider implements ISelectionProvider { + + static final EmptySelectionProvider INSTANCE = new EmptySelectionProvider(); + + EmptySelectionProvider() { + super(); + } + + @Override + public ISelection getSelection() { + return StructuredSelection.EMPTY; + } + + @Override + public void setSelection(ISelection selection) { + // Pass + } + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + // Not needed because the selection is always empty + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + // Not needed because the selection is always empty + } + + } + + private class VisiblePageSelectionProvider implements ISelectionProvider { + + private final IStructuredSelection selection; + + VisiblePageSelectionProvider() { + this(null); + } + + VisiblePageSelectionProvider(IPage visible) { + super(); + + this.selection = (visible == null) ? StructuredSelection.EMPTY : new StructuredSelection(visible); + } + + @Override + public ISelection getSelection() { + return selection; + } + + @Override + public void setSelection(ISelection selection) { + if (!selection.isEmpty()) { + IPage page = (IPage) ((IStructuredSelection) selection).getFirstElement(); + sashContainer.selectPage(page); + } + } + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + // Not needed + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + // Not needed + } + + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/MultiDiagramPropertySheetPage.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/MultiDiagramPropertySheetPage.java new file mode 100644 index 00000000000..9ef43cb4cd3 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/MultiDiagramPropertySheetPage.java @@ -0,0 +1,178 @@ +/***************************************************************************** + * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, 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: + * CEA LIST - Initial API and implementation + * Christian W. Damus - bug 469188 + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.core.commands.operations.IUndoContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.emf.common.ui.URIEditorInput; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.papyrus.infra.core.operation.DelegatingUndoContext; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.tools.util.PlatformHelper; +import org.eclipse.papyrus.infra.ui.editor.reload.EditorReloadEvent; +import org.eclipse.papyrus.infra.ui.editor.reload.IEditorReloadListener; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IStorageEditorInput; +import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.operations.RedoActionHandler; +import org.eclipse.ui.operations.UndoActionHandler; +import org.eclipse.ui.part.IShowInSource; +import org.eclipse.ui.part.ShowInContext; +import org.eclipse.ui.views.properties.PropertyShowInContext; +import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; + + +/** + * A specialized property-sheet page that knows how to restore the Property Sheet view's input from the workbench part + * that most recently gave it its input, after a {@link CoreMultiDiagramEditor} has been re-loaded. + */ +class MultiDiagramPropertySheetPage extends TabbedPropertySheetPage implements IEditorReloadListener { + + private final CoreMultiDiagramEditor multiDiagramEditor; + + private UndoActionHandler undo; + private RedoActionHandler redo; + private DelegatingUndoContext undoContext; + + public MultiDiagramPropertySheetPage(CoreMultiDiagramEditor editor) { + super(editor); + + this.multiDiagramEditor = editor; + IReloadableEditor.Adapter.getAdapter(editor).addEditorReloadListener(this); + } + + @Override + public void dispose() { + IReloadableEditor.Adapter.getAdapter(multiDiagramEditor).removeEditorReloadListener(this); + + if (undo != null) { + undo.dispose(); + } + if (redo != null) { + redo.dispose(); + } + + super.dispose(); + } + + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + if (selection.isEmpty() && (part instanceof IMultiDiagramEditor)) { + // Perhaps the user selected a page such as the Welcome Page that + // isn't an editor and so doesn't have a selection + IMultiDiagramEditor editor = (IMultiDiagramEditor) part; + ISashWindowsContainer sash = editor.getAdapter(ISashWindowsContainer.class); + if (sash != null) { + if ((sash.getActiveEditor() == null) && (sash.getActiveSashWindowsPage() != null)) { + // Yep, that's the case, here. So show the properties of the input + // resource (usually a DI file) + IEditorInput input = editor.getEditorInput(); + Object newSelection; + if (input instanceof IFileEditorInput) { + newSelection = ((IFileEditorInput) input).getFile(); + } else if (input instanceof IStorageEditorInput) { + IStorageEditorInput storageInput = (IStorageEditorInput) input; + try { + newSelection = storageInput.getStorage(); + } catch (CoreException e) { + newSelection = storageInput; + } + } else if (input instanceof IURIEditorInput) { + newSelection = ((IURIEditorInput) input).getURI(); + } else if (input instanceof URIEditorInput) { + newSelection = ((URIEditorInput) input).getURI(); + } else { + newSelection = null; + } + + if (newSelection != null) { + selection = new StructuredSelection(newSelection); + } + } + } + } + + super.selectionChanged(part, selection); + } + + @Override + public void editorAboutToReload(EditorReloadEvent event) { + Object propertySheet = getSite().getService(IViewPart.class); + if (propertySheet instanceof IShowInSource) { + ShowInContext context = ((IShowInSource) propertySheet).getShowInContext(); + + if (context instanceof PropertyShowInContext) { + IWorkbenchPart inputPart = ((PropertyShowInContext) context).getPart(); + if (inputPart != null) { + event.putContext(inputPart); + } + } + } + } + + @Override + public void editorReloaded(EditorReloadEvent event) { + final IWorkbenchPart inputPart = (IWorkbenchPart) event.getContext(); + if (inputPart != null) { + final Object propertySheet = getSite().getService(IViewPart.class); + if (propertySheet instanceof IPartListener) { + // Kick it with this part + ((IPartListener) propertySheet).partActivated(inputPart); + + // And again later to get its new selection (we don't know when its selection may be restored relative to us) + getSite().getShell().getDisplay().asyncExec(new Runnable() { + + @Override + public void run() { + ISelectionProvider selectionProvider = inputPart.getSite().getSelectionProvider(); + if (selectionProvider != null) { + ((ISelectionListener) propertySheet).selectionChanged(inputPart, selectionProvider.getSelection()); + } + } + }); + } + } + + // The editor will have a new undo context (because it will have a new editing domain) + if (undoContext != null) { + undoContext.setDelegate(PlatformHelper.getAdapter(multiDiagramEditor, IUndoContext.class)); + } + } + + @Override + public void setActionBars(IActionBars actionBars) { + super.setActionBars(actionBars); + + undoContext = new DelegatingUndoContext(); + undoContext.setDelegate(PlatformHelper.getAdapter(multiDiagramEditor, IUndoContext.class)); + + undo = new UndoActionHandler(multiDiagramEditor.getSite(), undoContext); + redo = new RedoActionHandler(multiDiagramEditor.getSite(), undoContext); + + actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(), undo); + actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(), redo); + } + + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PageIconRegistryServiceFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PageIconRegistryServiceFactory.java new file mode 100644 index 00000000000..c35a776b83a --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PageIconRegistryServiceFactory.java @@ -0,0 +1,71 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.papyrus.infra.core.services.IServiceFactory; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editorsfactory.IPageIconsRegistry; +import org.eclipse.papyrus.infra.ui.editorsfactory.PageIconsRegistry; +import org.eclipse.papyrus.infra.ui.extension.diagrameditor.PluggableEditorFactoryReader; + +/** + * Service Factory to register {@link IPageIconsRegistry}. + * + * @author cedric dumoulin + * + */ +public class PageIconRegistryServiceFactory implements IServiceFactory { + + private PageIconsRegistry pageIconsRegistry; + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + * @throws ServiceException + */ + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + * + * @throws ServiceException + */ + @Override + public void startService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#disposeService() + * + * @throws ServiceException + */ + @Override + public void disposeService() throws ServiceException { + if (pageIconsRegistry != null) { + pageIconsRegistry.dispose(); + } + } + + /** + * Create and populate a {@link PageIconsRegistry}. Return it as the service + * instance. + * + * @return + */ + @Override + public Object createServiceInstance() { + if (pageIconsRegistry == null) { + pageIconsRegistry = new PageIconsRegistry(); + PluggableEditorFactoryReader editorReader = new PluggableEditorFactoryReader(Activator.PLUGIN_ID); + editorReader.populate(pageIconsRegistry); + } + return pageIconsRegistry; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PageMngrServiceFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PageMngrServiceFactory.java new file mode 100644 index 00000000000..b7657e6ce1a --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PageMngrServiceFactory.java @@ -0,0 +1,83 @@ +/***************************************************************************** + * Copyright (c) 2011, 2016 LIFL, Christian W. Damus, 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: + * LIFL - Initial API and implementation + * Christian W. Damus - bugs 415638, 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.DiSashModelManager; +import org.eclipse.papyrus.infra.core.services.IServiceFactory; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServiceNotFoundException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; + +/** + * A service factory to create the {@link IPageMngr} service. This + * serviceFactory depends on {@link ISashWindowsContentProvider} service. + * + * @author cedric dumoulin + * + */ +public class PageMngrServiceFactory implements IServiceFactory { + + /** + * The sashModelMangr. + */ + private DiSashModelManager sashModelMngr; + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + * @throws ServiceException + */ + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + + // Get required services + sashModelMngr = servicesRegistry.getService(DiSashModelManager.class); + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + * + * @throws ServiceException + */ + @Override + public void startService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#disposeService() + * + * @throws ServiceException + */ + @Override + public void disposeService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IServiceFactory#createServiceInstance() + * + * @return + * @throws ServiceException + * if the required sash model manager service is unavailable + */ + @Override + public Object createServiceInstance() throws ServiceException { + if (sashModelMngr == null) { + throw new ServiceNotFoundException(DiSashModelManager.class.getName()); + } + return sashModelMngr.getIPageManager(); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PapyrusPageInput.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PapyrusPageInput.java new file mode 100644 index 00000000000..84d317fc793 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/PapyrusPageInput.java @@ -0,0 +1,62 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.editor; + +import org.eclipse.core.resources.IFile; +import org.eclipse.emf.common.util.URI; +import org.eclipse.ui.part.FileEditorInput; + +/** + * Basic implementation of {@link IPapyrusPageInput} + * + * @author Camille Letavernier + */ +public class PapyrusPageInput extends FileEditorInput implements IPapyrusPageInput { + + private final URI[] pages; + + private final boolean closeOtherPages; + + /** + * Creates a new PapyrusPageInput + * + * @param diFile + * The file resource + * @param pages + * The pageIdentifiers of the pages to open + * @param closeOtherPages + * True if only the selected pages should be opened. All other pages will be closed. + */ + public PapyrusPageInput(IFile diFile, URI[] pages, boolean closeOtherPages) { + super(diFile); + this.pages = pages; + this.closeOtherPages = closeOtherPages; + } + + /** + * {@inheritDoc} + */ + @Override + public URI[] getPages() { + return pages; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean closeOtherPages() { + return closeOtherPages; + } + + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/CompositeReloadContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/CompositeReloadContext.java new file mode 100644 index 00000000000..3eabad5cbf3 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/CompositeReloadContext.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.papyrus.infra.core.utils.AdapterUtils; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + + +/** + * An {@linkplain EditorReloadEvent#putContext(Object) editor reload context} that composes other reload contexts. + * This should be used whenever a {@linkplain IReloadContextProvider reload context provider} supplies multiple + * reload contexts, to ensure that they are properly initialized by the reload system. + */ +public class CompositeReloadContext implements IDisposableReloadContext, IAdaptable { + + private final Collection<Object> reloadContexts; + + public CompositeReloadContext() { + this(Collections.EMPTY_LIST); + } + + public CompositeReloadContext(Iterable<?> reloadContexts) { + super(); + + this.reloadContexts = Lists.newArrayList(reloadContexts); + } + + public <T> T add(T reloadContext) { + reloadContexts.add(reloadContext); + return reloadContext; + } + + public Iterable<?> getReloadContexts() { + return Collections.unmodifiableCollection(reloadContexts); + } + + public <T> Iterable<T> getReloadContexts(Class<T> type) { + return Iterables.filter(getReloadContexts(), type); + } + + @Override + public void dispose() { + for (Object next : reloadContexts) { + if (next instanceof IDisposableReloadContext) { + ((IDisposableReloadContext) next).dispose(); + } + } + + reloadContexts.clear(); + } + + @Override + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null; + } + + private IInternalEMFSelectionContext getEMFContext() { + IInternalEMFSelectionContext result = null; + + for (Object next : reloadContexts) { + if (AdapterUtils.adapt(next, IInternalEMFSelectionContext.class, null) != null) { + // We need the adapter + result = new IInternalEMFSelectionContext.Composite(this); + break; + } + } + + return result; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/DelegatingReloadContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/DelegatingReloadContext.java new file mode 100644 index 00000000000..718b376c2ba --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/DelegatingReloadContext.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.papyrus.infra.core.utils.AdapterUtils; + + +/** + * An {@linkplain EditorReloadEvent#putContext(Object) editor reload context} that delegates to another reload context. + * This should be used whenever a {@linkplain IReloadContextProvider reload context provider} is needed to get a reload + * context to delegate to. + */ +public class DelegatingReloadContext implements IDisposableReloadContext, IAdaptable { + + private Object delegate; + + public DelegatingReloadContext(Object reloadContextProvider) { + super(); + + IReloadContextProvider provider = AdapterUtils.adapt(reloadContextProvider, IReloadContextProvider.class, null); + if (provider != null) { + delegate = provider.createReloadContext(); + } + } + + @Override + public void dispose() { + if (delegate instanceof IDisposableReloadContext) { + ((IDisposableReloadContext) delegate).dispose(); + } + + delegate = null; + } + + public Object getDelegate() { + return delegate; + } + + public void restore(Object reloadContextProvider) { + if (delegate != null) { + IReloadContextProvider provider = AdapterUtils.adapt(reloadContextProvider, IReloadContextProvider.class, null); + if (provider != null) { + provider.restore(delegate); + } + } + } + + @Override + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null; + } + + private IInternalEMFSelectionContext getEMFContext() { + IInternalEMFSelectionContext result = null; + + if ((delegate != null) && (AdapterUtils.adapt(delegate, IInternalEMFSelectionContext.class, null) != null)) { + // We need the adapter + result = new IInternalEMFSelectionContext.Delegating(this); + } + + return result; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EMFSelectionContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EMFSelectionContext.java new file mode 100644 index 00000000000..e8eea1033cc --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EMFSelectionContext.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.common.util.URI; +import org.eclipse.jface.viewers.ISelectionProvider; + + +/** + * A convenient selection re-load context for UIs that present EMF-based content. + */ +public class EMFSelectionContext extends SelectionContext<ISelectionProvider, URI> implements IAdaptable { + + private IInternalEMFSelectionContext emfContext; + + public EMFSelectionContext(ISelectionProvider structuredSelectionProvider) { + super(structuredSelectionProvider); + } + + @Override + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null; + } + + final IInternalEMFSelectionContext getEMFContext() { + if (emfContext == null) { + emfContext = new IInternalEMFSelectionContext.Default(); + } + return emfContext; + } + + @Override + protected URI getToken(Object object) { + return getEMFContext().getToken(object); + } + + @Override + protected Object resolveToken(URI token) { + return getEMFContext().resolveToken(token); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EMFTreeViewerContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EMFTreeViewerContext.java new file mode 100644 index 00000000000..a6f99008f06 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EMFTreeViewerContext.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.common.util.URI; +import org.eclipse.jface.viewers.AbstractTreeViewer; + +/** + * A convenient context object for {@link IEditorReloadListener}s to store in an {@link EditorReloadEvent} to capture and restore + * the expansion and selection state of nodes in an EMF-based tree viewer. + */ +public class EMFTreeViewerContext extends TreeViewerContext<URI> implements IAdaptable { + + private IInternalEMFSelectionContext emfContext; + + public EMFTreeViewerContext(AbstractTreeViewer viewer) { + super(viewer); + } + + @Override + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + return (adapter == IInternalEMFSelectionContext.class) ? getEMFContext() : null; + } + + final IInternalEMFSelectionContext getEMFContext() { + if (emfContext == null) { + emfContext = new IInternalEMFSelectionContext.Default(); + } + return emfContext; + } + + @Override + protected URI getToken(Object object) { + return getEMFContext().getToken(object); + } + + @Override + protected Object resolveToken(URI token) { + return getEMFContext().resolveToken(token); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EditorReloadAdapter.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EditorReloadAdapter.java new file mode 100644 index 00000000000..5015f26011e --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EditorReloadAdapter.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + + + +/** + * Convenience superclass for selective implementation of editor reload call-backs. + */ +public class EditorReloadAdapter implements IEditorReloadListener { + + public EditorReloadAdapter() { + super(); + } + + @Override + public void editorAboutToReload(EditorReloadEvent event) { + // Pass + } + + @Override + public void editorReloaded(EditorReloadEvent event) { + // Pass + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EditorReloadEvent.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EditorReloadEvent.java new file mode 100644 index 00000000000..c4d13f721b4 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/EditorReloadEvent.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import java.util.EventObject; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.utils.AdapterUtils; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.editor.IReloadableEditor; + +import com.google.common.base.Supplier; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; + + +/** + * The event object for notifications of each phase in the reloading of a {@linkplain IReloadableEditor reloadable editor}. + */ +public class EditorReloadEvent extends EventObject { + + private static final long serialVersionUID = 1L; + + public static final int ABOUT_TO_RELOAD = 1; + + public static final int RELOADED = 2; + + private int type; + + private transient Map<IEditorReloadListener, Object> context; + + private transient IEditorReloadListener currentListener; + + public EditorReloadEvent(IMultiDiagramEditor editor) { + super(editor); + } + + public final IMultiDiagramEditor getEditor() { + return (IMultiDiagramEditor) getSource(); + } + + public final int getEventType() { + return type; + } + + /** + * Puts some opaque representation of contextual state for the caller to retrieve later when an editor is {@linkplain IEditorReloadListener#editorReloaded(EditorReloadEvent) reloaded}. + * The canonical example of this usage is storing state such as selection, expanded tree nodes, etc. to restore after re-building a UI that + * depends on the reloaded editor. After the editor re-load completes and all listeners are notified of that, then all context objects are + * released. Any that implement the {@link IDisposableReloadContext} interface are disposed according to that protocol. This is done even if it + * happens that the editor re-load procedure cannot complete normally and the editor is forced to close without notifying the post-reload + * listeners. + * + * @param object + * some state to stash for later retrieval following the re-loading of the editor + * + * @return the previous context object, if any (there normally wouldn't be as each listener that is invoked has its own context storage + * + * @throws IllegalStateException + * on any attempt to invoke this method except during an {@link IEditorReloadListener#editorAboutToReload(EditorReloadEvent)} call-back + */ + public Object putContext(Object object) { + checkContext(ABOUT_TO_RELOAD); + + IInternalEMFSelectionContext emfContext = AdapterUtils.adapt(object, IInternalEMFSelectionContext.class, null); + if (emfContext != null) { + initContext(emfContext); + } + + return context.put(currentListener, object); + } + + /** + * Retrieves an opaque representation of contextual state that was previously {@linkplain #putContext(Object) put} by the caller. + * + * @return the previously stashed object, or {@code null} if none + * + * @throws IllegalStateException + * on any attempt to invoke this method except during an {@link IEditorReloadListener#editorReloaded(EditorReloadEvent)} call-back + */ + public Object getContext() { + checkContext(RELOADED); + return context.get(currentListener); + } + + private void initContext(IInternalEMFSelectionContext context) { + Supplier<ResourceSet> resourceSetSupplier = new Supplier<ResourceSet>() { + + @Override + public ResourceSet get() { + try { + return getEditor().getServicesRegistry().getService(ModelSet.class); + } catch (ServiceException e) { + Activator.log.error(e); + throw new IllegalStateException("Invalid service registry in editor"); //$NON-NLS-1$ + } + } + }; + + context.setResourceSetSupplier(resourceSetSupplier); + } + + protected final void checkContext(int phase) { + if (currentListener == null) { + throw new IllegalStateException("Not in an IEditorReloadListener call-back"); //$NON-NLS-1$ + } + + if (phase != this.type) { + throw new IllegalStateException(String.format("Not in '%s' listener call-back", (phase == ABOUT_TO_RELOAD) ? "editorAboutToReload" : "editorReloaded")); + } + } + + public final void dispatchEditorAboutToReload(Iterable<? extends IEditorReloadListener> listeners) { + context = Maps.newHashMap(); + type = ABOUT_TO_RELOAD; + + for (Iterator<? extends IEditorReloadListener> iter = listeners.iterator(); iter.hasNext();) { + currentListener = iter.next(); + + try { + currentListener.editorAboutToReload(this); + } catch (Exception e) { + Activator.log.error("Uncaught exception in editor reload listener.", e); //$NON-NLS-1$ + } finally { + currentListener = null; + } + } + } + + public final void dispatchEditorReloaded(Iterable<? extends IEditorReloadListener> listeners) { + type = RELOADED; + + for (Iterator<? extends IEditorReloadListener> iter = listeners.iterator(); iter.hasNext();) { + currentListener = iter.next(); + + try { + currentListener.editorReloaded(this); + } catch (Exception e) { + Activator.log.error("Uncaught exception in editor reload listener.", e); //$NON-NLS-1$ + } finally { + currentListener = null; + } + } + } + + public void dispose() { + if (context != null) { + Error error = null; + + try { + for (IDisposableReloadContext next : Iterables.filter(context.values(), IDisposableReloadContext.class)) { + try { + next.dispose(); + } catch (Exception e) { + Activator.log.error("Uncaught exception in editor reload context disposal.", e); //$NON-NLS-1$ + } catch (Error e) { + if (error == null) { + error = e; + } + Activator.log.error("Uncaught exception in editor reload context disposal.", e); //$NON-NLS-1$ + } + } + } finally { + context = null; + } + + if (error != null) { + throw error; + } + } + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IDisposableReloadContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IDisposableReloadContext.java new file mode 100644 index 00000000000..8aff9ccf83b --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IDisposableReloadContext.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +/** + * Protocol implemented by {@link EditorReloadEvent} context objects that must be disposed when they are no longer needed. + * + * @see EditorReloadEvent#dispose() + */ +public interface IDisposableReloadContext { + + void dispose(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IEditorReloadListener.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IEditorReloadListener.java new file mode 100644 index 00000000000..2114d555152 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IEditorReloadListener.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import java.util.EventListener; + +import org.eclipse.papyrus.infra.ui.editor.IReloadableEditor; + + +/** + * A protocol for notification of the phases of re-loading of an {@link IReloadableEditor}. + */ +public interface IEditorReloadListener extends EventListener { + + /** + * Notifies that an editor is about to reload. Implementors may put stuff into the {@code event}'s {@link EditorReloadEvent#putContext(Object) + * context} to retrieve in an eventual {@linkplain #editorReloaded(EditorReloadEvent) }re-load} notification. The canonical example of this + * usage is storing state such as selection, expanded tree nodes, etc. to restore after re-building a UI that depends on the reloaded + * editor. + * + * @param event + * notification that an editor is going to re-load itself + */ + void editorAboutToReload(EditorReloadEvent event); + + /** + * Notifies that an editor has reloaded. Implementors may retrieve from the {@code event} any {@link EditorReloadEvent#getContext() + * context} that they put in {@linkplain #editorAboutToReload(EditorReloadEvent) before} the re-load. + * + * @param event + * notification that an editor has reloaded + */ + void editorReloaded(EditorReloadEvent event); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IInternalEMFSelectionContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IInternalEMFSelectionContext.java new file mode 100644 index 00000000000..f8bda865a97 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IInternalEMFSelectionContext.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.papyrus.infra.core.utils.AdapterUtils; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + + +/** + * An internal interface provided (usually as an {@linkplain IAdaptable#getAdapter(Class) adapter}) by EMF-based {@linkplain SelectionContext + * selection contexts}. + */ +interface IInternalEMFSelectionContext { + + void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier); + + URI getToken(Object object); + + Object resolveToken(URI token); + + class Default implements IInternalEMFSelectionContext { + + Supplier<? extends ResourceSet> resourceSetSupplier; + + Default() { + super(); + } + + @Override + public void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier) { + this.resourceSetSupplier = Suppliers.memoize(resourceSetSupplier); + } + + @Override + public URI getToken(Object object) { + return (object instanceof EObject) ? EcoreUtil.getURI((EObject) object) : null; + } + + @Override + public Object resolveToken(URI token) { + ResourceSet rset = (resourceSetSupplier == null) ? null : resourceSetSupplier.get(); + return (rset == null) ? null : rset.getEObject(token, true); + } + + } + + class Composite extends Default { + + private final CompositeReloadContext composite; + + Composite(CompositeReloadContext composite) { + super(); + + this.composite = composite; + } + + @Override + public void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier) { + super.setResourceSetSupplier(resourceSetSupplier); + + for (Object next : composite.getReloadContexts()) { + IInternalEMFSelectionContext emfContext = AdapterUtils.adapt(next, IInternalEMFSelectionContext.class, null); + if (emfContext != null) { + // Pass along the memoizer so that we can all share it + emfContext.setResourceSetSupplier(this.resourceSetSupplier); + } + } + } + } + + class Delegating extends Default { + + private final DelegatingReloadContext delegating; + + Delegating(DelegatingReloadContext delegating) { + super(); + + this.delegating = delegating; + } + + @Override + public void setResourceSetSupplier(Supplier<? extends ResourceSet> resourceSetSupplier) { + super.setResourceSetSupplier(resourceSetSupplier); + + IInternalEMFSelectionContext emfContext = AdapterUtils.adapt(delegating.getDelegate(), IInternalEMFSelectionContext.class, null); + if (emfContext != null) { + // Pass along the memoizer so that we can all share it + emfContext.setResourceSetSupplier(this.resourceSetSupplier); + } + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IReloadContextProvider.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IReloadContextProvider.java new file mode 100644 index 00000000000..f3449f59be9 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/IReloadContextProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + + +/** + * An adapter protocol for objects that can provide {@code context}s to be included in the + * re-load state of dependent parts in an {@link EditorReloadEvent}, for the purpose of + * restoring the state of those objects after re-load has completed. + */ +public interface IReloadContextProvider { + + /** + * Creates an opaque token from which the receiver can be {@linkplain #restore(Object) restored} after the editor has reloaded. + * + * @return an opaque editor re-load context, or {@code null} if none is needed on this occasion (for example because the receiver + * is in its default state) + */ + Object createReloadContext(); + + /** + * Reloads the receiver's state from a token previously {@linkplain #createReloadContext() provided}. + * + * @param reloadContext + * the opaque re-load context token + */ + void restore(Object reloadContext); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/SelectionContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/SelectionContext.java new file mode 100644 index 00000000000..15d58b5c4a1 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/SelectionContext.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import java.util.List; + +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; + +import com.google.common.collect.Lists; + +/** + * A convenient context object for {@link IEditorReloadListener}s to store in an {@link EditorReloadEvent} to capture and restore + * the selection state a selection provider; + * + * @param <V> + * the type of selection provider + * @param <T> + * the type of token used to restore the selection state + */ +public abstract class SelectionContext<V extends ISelectionProvider, T> { + + private List<T> selection = Lists.newArrayList(); + + public SelectionContext(V structuredSelectionProvider) { + for (Object next : ((IStructuredSelection) structuredSelectionProvider.getSelection()).toList()) { + T token = token(next); + if (token != null) { + selection.add(token); + } + } + } + + public void restore(V structuredSelectionProvider) { + List<Object> select = Lists.newArrayListWithCapacity(selection.size()); + for (T next : selection) { + Object resolved = resolve(next); + if (resolved != null) { + select.add(resolved); + } + } + setSelection(structuredSelectionProvider, select); + } + + T token(Object selectableElement) { + Object deresolved = deresolveSelectableElement(selectableElement); + return (deresolved == null) ? null : getToken(deresolved); + } + + protected Object deresolveSelectableElement(Object selectableElement) { + return selectableElement; + } + + protected abstract T getToken(Object object); + + Object resolve(T token) { + Object deresolved = resolveToken(token); + return (deresolved == null) ? null : resolveSelectableElement(deresolved); + } + + protected Object resolveSelectableElement(Object deresolved) { + return deresolved; + } + + protected abstract Object resolveToken(T token); + + protected void setSelection(V structuredSelectionProvider, List<?> selection) { + structuredSelectionProvider.setSelection(new StructuredSelection(selection)); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/TreeViewerContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/TreeViewerContext.java new file mode 100644 index 00000000000..6d70788326c --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editor/reload/TreeViewerContext.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editor.reload; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.StructuredSelection; + +import com.google.common.collect.Lists; + +/** + * A convenient context object for {@link IEditorReloadListener}s to store in an {@link EditorReloadEvent} to capture and restore + * the expansion and selection state of nodes in a tree viewer. + */ +public abstract class TreeViewerContext<T> extends SelectionContext<AbstractTreeViewer, T> { + + private List<T> expandedNodes = Lists.newArrayList(); + + public TreeViewerContext(AbstractTreeViewer viewer) { + super(viewer); + + for (Object next : viewer.getExpandedElements()) { + T token = token(next); + if (token != null) { + expandedNodes.add(token); + } + } + } + + @Override + public void restore(AbstractTreeViewer viewer) { + List<Object> expand = Lists.newArrayListWithCapacity(expandedNodes.size()); + for (T next : expandedNodes) { + Object resolved = resolve(next); + if (resolved != null) { + expand.add(resolved); + } + } + setExpandedElements(viewer, expand); + + super.restore(viewer); + } + + @Override + protected void setSelection(AbstractTreeViewer viewer, List<?> selection) { + viewer.setSelection(new StructuredSelection(selection), true); + } + + protected void setExpandedElements(AbstractTreeViewer viewer, Collection<?> toExpand) { + viewer.setExpandedElements(toExpand.toArray()); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/AbstractGetEditorIconQuery.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/AbstractGetEditorIconQuery.java new file mode 100644 index 00000000000..9c02a4bf35d --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/AbstractGetEditorIconQuery.java @@ -0,0 +1,74 @@ +/***************************************************************************** + * Copyright (c) 2011 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.editorsfactory; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForEObject; +import org.eclipse.papyrus.infra.ui.editorsfactory.IPageIconsRegistry; +import org.eclipse.papyrus.infra.ui.editorsfactory.IPageIconsRegistryExtended; +import org.eclipse.papyrus.infra.ui.editorsfactory.PageIconsRegistry; + +/** + * + * An abstract class to get the iconRegistery + * + */ +public abstract class AbstractGetEditorIconQuery {// we don't need to implements IJavaModelQuery here + + /** + * the icon registry + */ + private static IPageIconsRegistry editorRegistry; + + /** + * Get the EditorRegistry used to create editor instances. This default + * implementation return the singleton eINSTANCE. This method can be + * subclassed to return another registry. + * + * @return the singleton eINSTANCE of editor registry + */ + protected IPageIconsRegistryExtended getEditorRegistry(EObject context) { + try { + return (IPageIconsRegistryExtended) ServiceUtilsForEObject.getInstance().getService(IPageIconsRegistry.class, context); + } catch (Exception ex) { + // Skip + } + if (editorRegistry == null) { + editorRegistry = createEditorRegistry(context); + } + if (!(editorRegistry instanceof IPageIconsRegistryExtended)) { + throw new RuntimeException("The editor registry do not implement IPageIconsRegistryExtended");////$NON-NLS-1$ + } + return (IPageIconsRegistryExtended) editorRegistry; + } + + /** + * Return the EditorRegistry for nested editor descriptors. Subclass should + * implements this method in order to return the registry associated to the + * extension point namespace. + * + * @return the EditorRegistry for nested editor descriptors + */ + protected IPageIconsRegistry createEditorRegistry(EObject context) { + try { + return ServiceUtilsForEObject.getInstance().getService(IPageIconsRegistry.class, context); + } catch (ServiceException e) { + // Not found, return an empty one which return null for each + // request. + return new PageIconsRegistry(); + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorFactory.java new file mode 100644 index 00000000000..a107a8e53ff --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorFactory.java @@ -0,0 +1,57 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.editorsfactory; + +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel; + +/** + * Factory used to get the Icon associated to the editor used to render the + * specified pageIdentifier. + * + * + * @author cedric dumoulin + * + */ +public interface IEditorFactory { + + /** + * Create the {@link IPageModel} for the specified identifier. TODO throw an + * exception encapsulating problems encountered while creating the model. + * + * @param pageIdentifier + * Object identifying an Editor. + * @return PageModel allowing to create the editor. + */ + public IPageModel createIPageModel(Object pageIdentifier); + + /** + * Return true if the factory can create an IPageModel for the specified + * pageIdentifier. Return false otherwise TODO throw an exception + * encapsulating problems encountered while creating the model. + * + * @param pageIdentifier + * The object representing the page to test + * @return + */ + public boolean isPageModelFactoryFor(Object pageIdentifier); + + /** + * The ID of this factory + * + * @return + */ + default String getFactoryID() { + return getClass().getName(); + } + + /** + * The display label of this factory + * + * @return + */ + default String getLabel() { + return getClass().getSimpleName(); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorIconFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorIconFactory.java new file mode 100644 index 00000000000..16ea36ce42b --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorIconFactory.java @@ -0,0 +1,64 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.editorsfactory; + +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel; +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.DiSashModelMngr; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.swt.graphics.Image; + +/** + * Factory used to create an {@link IPageModel} used by the {@link ISashWindowsContainer} to create an instance of the editor represented + * by the provided Object. Such factory is required by the {@link DiSashModelMngr}. It is called whenever the ISashWindowsContainer need + * to create an editor from an EObject representing this editor in the Di + * implementation of the {@link ISashWindowsContentProvider} + * + * + * @author cedric dumoulin + * + */ +public interface IEditorIconFactory { + + /** + * Get the icon associated to the editor used to render the model. Model + * represent the top level object of a model editor. Can return a cached + * Image. + * + * @param pageIdentifier + * the pageIdentifier representing the Editor. This is usually + * the EObject used to reconstruct the editor. + * @return the icon representing the editor + */ + public Image getEditorIcon(Object pageIdentifier); + + /** + * Create the icon associated to the editor used to render the model. Model + * represent the top level object of a model editor. Always return a newly + * created Image. + * + * @param pageIdentifier + * the pageIdentifier representing the Editor. This is usually + * the EObject used to reconstruct the editor. + * @return the icon representing the editor + */ + public Image createEditorIcon(Object pageIdentifier); + + /** + * Return true if the factory can create an IPageModel for the specified + * pageIdentifier. Return false otherwise TODO throw an exception + * encapsulating problems encountered while creating the model. + * + * @param pageIdentifier + * The object representing the page to test + * @return + */ + public boolean isPageModelFactoryFor(Object pageIdentifier); + + /** + * Dispose this factory + */ + public void dispose(); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorIconFactoryExtended.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorIconFactoryExtended.java new file mode 100644 index 00000000000..52faed51ce4 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IEditorIconFactoryExtended.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2011 Atos. + * + * 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: + * Atos - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editorsfactory; + +/** + * + * @author "Arthur Daussy <a href="mailto:arthur.daussy@atos.net">arthur.daussy@atos.net</a>" + * + */ +public interface IEditorIconFactoryExtended extends IEditorIconFactory { + + /** + * Return the icon URL associated to the editor used to render the model. Model represent the top level + * object of a model editor. + * + * @param pageIdentifier + * @return + */ + public String getURLMainIcon(Object pageIdentifier); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IPageIconsRegistry.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IPageIconsRegistry.java new file mode 100644 index 00000000000..4063f28288c --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IPageIconsRegistry.java @@ -0,0 +1,40 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.editorsfactory; + +import org.eclipse.swt.graphics.Image; + +/** + * Registry used to get Icons associated to an editor. + * + * @author cedric dumoulin + */ +public interface IPageIconsRegistry { + + /** + * Get the icon associated to the editor used to render the model. Model + * represent the top level object of a model editor. + * + * @param model + * the model representing the Editor. This is usually the EObject + * used to reconstruct the editor. + * @return the icon representing the editor + */ + public Image getEditorIcon(Object model); + + /** + * Dispose this registry + */ + public void dispose(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IPageIconsRegistryExtended.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IPageIconsRegistryExtended.java new file mode 100644 index 00000000000..a11b5f5e7ff --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/IPageIconsRegistryExtended.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2011 Atos Origin. + * + * 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: + * Atos Origin - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.editorsfactory; + +/** + * Extends IPageIconsRegistry in order to offer a second methods which will give back the URL of the requested Icon + * + * @author "Arthur Daussy <a href="mailto:arthur.daussy@atos.net">arthur.daussy@atos.net</a>" + * + */ +public interface IPageIconsRegistryExtended extends IPageIconsRegistry { + + /** + * Get the URL icon associated to the editor used to render the model. Model represent the top level + * object of a model editor. + * + * @param model + * @return {@link String} which represent the URL of the resource + */ + public String getEditorURLIcon(Object model); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/PageIconsRegistry.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/PageIconsRegistry.java new file mode 100644 index 00000000000..d60e8f71f96 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/PageIconsRegistry.java @@ -0,0 +1,118 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.editorsfactory; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.graphics.Image; + +/** + * Concrete implementation of the {@link IPageIconsRegistry}. This + * implementation allows to add and remove {@link IPageIconsRegistry}. + * + * + * @author cedric dumoulin + */ +public class PageIconsRegistry implements IPageIconsRegistryExtended { + + /** list of registered icon factories */ + protected List<IEditorIconFactory> pageIcons = new ArrayList<IEditorIconFactory>(); + + /** + * Constructor. + * + * @param editorFactoryRegistry + * @param servicesRegistry + */ + public PageIconsRegistry() { + + } + + /** + * Walk each registered {@link IEditorFactory} to find the one handling the + * specified pageIdentifier. Call the corresponding method in the found + * pageIdentifier. + * + * TODO Throw an exception to report errors. + * + * @see org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.IPageModelFactory#createIPageModel(java.lang.Object) + */ + @Override + public Image getEditorIcon(Object pageIdentifier) { + + for (IEditorIconFactory factory : getPageIcons()) { + if (factory.isPageModelFactoryFor(pageIdentifier)) { + { + // return factory.getEditorIcon(pageIdentifier); + return factory.getEditorIcon(pageIdentifier); + } + } + } + // no editor found ! + // TODO Throw an exception. + // throw new EditorNotFoundException("No editor registered for '" + + // pageIdentifier + "'."); + return null; + } + + /** + * @return the editorFactories + */ + protected List<IEditorIconFactory> getPageIcons() { + return pageIcons; + } + + /** + * Add the specified {@link IEditorFactory} + * + * @param editorIconFactory + */ + public void add(IEditorIconFactory editorIconFactory) { + // This should never happen + if (editorIconFactory == null) { + throw new RuntimeException("Parameter should not be null."); //$NON-NLS-1$ + } + + pageIcons.add(editorIconFactory); + } + + /** + * Remove the specified {@link IEditorFactory} + * + * @param editorIconFactory + */ + public void remove(IEditorIconFactory editorIconFactory) { + pageIcons.remove(editorIconFactory); + } + + /** + * Return the path to the icon ressource. + * + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IPageIconsRegistryExtended#getEditorURLIcon(java.lang.Object) + * + * @param model + * @return + */ + @Override + public String getEditorURLIcon(Object model) { + for (IEditorIconFactory factory : getPageIcons()) { + if (factory.isPageModelFactoryFor(model)) { + { + if (factory instanceof IEditorIconFactoryExtended) { + return ((IEditorIconFactoryExtended) factory).getURLMainIcon(model); + } + } + } + } + return ""; + } + + @Override + public void dispose() { + for (IEditorIconFactory factory : pageIcons) { + factory.dispose(); + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/PageModelFactoryRegistry.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/PageModelFactoryRegistry.java new file mode 100644 index 00000000000..32774bb7a25 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/PageModelFactoryRegistry.java @@ -0,0 +1,165 @@ +/***************************************************************************** + * Copyright (c) 2009 - 2015 CEA LIST & LIFL + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * Camille Letavernier (CEA LIST) - camille.letavernier@cea.fr - Bug 476625 + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.editorsfactory; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel; +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.IPageModelFactory; +import org.eclipse.papyrus.infra.ui.Activator; + +/** + * Concrete implementation of the {@link IPageModelFactory} required by the di + * implementation of {@link ISashWindowsContentProvider}. This implementation + * allows to add and remove {@link IEditorFactory}. + * + * + * @author cedric dumoulin + */ +public class PageModelFactoryRegistry implements IPageModelFactory { + + /** ordered list of editor factories */ + protected List<IEditorFactory> editorFactories = new ArrayList<IEditorFactory>(); + + /** + * Constructor. + * + * @param editorFactoryRegistry + * @param servicesRegistry + */ + public PageModelFactoryRegistry() { + + } + + /** + * Walk each registered {@link IEditorFactory} to find the one handling the + * specified pageIdentifier. Call the corresponding method in the found + * pageIdentifier. + * + * TODO Throw an exception to report errors. + * + * @see org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.IPageModelFactory#createIPageModel(java.lang.Object) + */ + @Override + public IPageModel createIPageModel(Object pageIdentifier) { + return createIPageModel(pageIdentifier, null); + } + + /** + * Walk each registered {@link IEditorFactory} to find the one handling the + * specified pageIdentifier. Call the corresponding method in the found + * pageIdentifier. + * + * If several factories match the selected page, use the favorite editor. + * If the favorite editor is not available, use the priority mechanism + * + * @see org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.IPageModelFactory#createIPageModel(java.lang.Object) + */ + @Override + public IPageModel createIPageModel(Object pageIdentifier, String favoriteEditorID) { + + IEditorFactory factory = getFactoryFor(pageIdentifier, favoriteEditorID); + if (factory == null) { + return null; + } + return factory.createIPageModel(pageIdentifier); + } + + /** + * Returns the IEditorFactory for the given pageIdentifier. + * + * If several factories match the page identifier, use the favorite one + * + * @param pageIdentifier + * @return + */ + private IEditorFactory getFactoryFor(Object pageIdentifier, String favoriteEditorID) { + List<IEditorFactory> matchingFactories = new LinkedList<>(); + + for (IEditorFactory factory : getEditorFactories()) { + if (factory.isPageModelFactoryFor(pageIdentifier)) { + matchingFactories.add(factory); + } + } + + if (matchingFactories.isEmpty()) { + return null; + } else if (matchingFactories.size() == 1) { + return matchingFactories.get(0); + } else if (favoriteEditorID != null) { + for (IEditorFactory matchingFactory : matchingFactories) { + if (favoriteEditorID.equals(matchingFactory.getFactoryID())) { + return matchingFactory; + } + } + } + + return matchingFactories.get(0); + } + + /** + * @see org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.IPageModelFactory#getEditorIDsFor(java.lang.Object) + * + * @param pageIdentifier + * @return + */ + @Override + public Map<String, String> getEditorIDsFor(Object pageIdentifier) { + return getEditorFactories().stream() + .filter(f -> f.isPageModelFactoryFor(pageIdentifier)) + .collect(Collectors + .toMap( + f -> f.getFactoryID(), f -> f.getLabel(), // key, value + (v1, v2) -> { // Conflict merger + Activator.log.warn(String.format("Several editors are declared with the same ID: '%s', '%s'", v1, v2)); + return v1; // Any value + } , + LinkedHashMap::new)); // HashMap Supplier + } + + /** + * @return the editorFactories + */ + protected List<IEditorFactory> getEditorFactories() { + return editorFactories; + } + + /** + * Add the specified {@link IEditorFactory} + * + * @param editorFactory + */ + public void add(IEditorFactory editorFactory) { + // This should never happen + if (editorFactory == null) { + throw new IllegalArgumentException("Parameter should not be null."); //$NON-NLS-1$ + } + + editorFactories.add(editorFactory); + } + + /** + * Remove the specified {@link IEditorFactory} + * + * @param editorFactory + */ + public void remove(IEditorFactory editorFactory) { + editorFactories.remove(editorFactory); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/anytype/AnyTypeEditorFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/anytype/AnyTypeEditorFactory.java new file mode 100644 index 00000000000..137c3c533ef --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/editorsfactory/anytype/AnyTypeEditorFactory.java @@ -0,0 +1,135 @@ +/***************************************************************************** + * Copyright (c) 2013, 2014 CEA LIST 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: + * Camille Letavernier (camille.letavernier@cea.fr) - Initial API and implementation + * Christian W. Damus (CEA) - bug 392301 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.editorsfactory.anytype; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.xml.type.AnyType; +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IComponentModel; +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel; +import org.eclipse.papyrus.infra.ui.extension.diagrameditor.AbstractEditorFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; + +/** + * An EditorFactory for "AnyType", i.e. EObject deserialized from unknown Packages + * + * Allows recovery and manipulation of models containing optional EMF components + * + * @author Camille Letavernier + */ +public class AnyTypeEditorFactory extends AbstractEditorFactory { + + public AnyTypeEditorFactory() { + super(null, "AnyTypeHandler"); + } + + @Override + public IPageModel createIPageModel(Object pageIdentifier) { + + final AnyType anyTypeModel = (AnyType) pageIdentifier; + + return new IComponentModel() { + + private AnyType anyType = anyTypeModel; + + @Override + public void dispose() { + // Pass. The tab icon is a workbench-shared image + } + + @Override + public String getTabTitle() { + EClass eClass = anyType.eClass(); + String label; + if (eClass == null) { + label = "component"; + } else { + label = eClass.getName(); + } + return "Missing " + label; + } + + private String getTypeLabel() { + EClass eClass = anyType.eClass(); + String className = eClass == null ? "None" : eClass.getName(); + return className; + } + + private String getNsURI() { + EClass eClass = anyType.eClass(); + EPackage ePackage = eClass == null ? null : eClass.getEPackage(); + String ePackageName = ePackage == null ? "None" : ePackage.getNsURI(); + + return ePackageName; + } + + public Image getComponentIcon() { + return Display.getDefault().getSystemImage(SWT.ICON_WARNING); + } + + @Override + public Image getTabIcon() { + return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_WARN_TSK); + } + + @Override + public Object getRawModel() { + return anyType; + } + + public String getErrorText() { + String typeLabel = getTypeLabel(); + String packageURI = getNsURI(); + String message = "A component is missing. The following Model cannot be loaded: " + typeLabel + " (from " + packageURI + ")\n"; + message += "Changes to the model won't be reflected in this editor. This editor will be saved in the current state, i.e. without any data loss. "; + message += "However, this may result in an inconsistent state of this editor when the missing component will be restored\n"; + return message; + } + + @Override + public Composite createPartControl(Composite parent) { + Composite tabComposite = new Composite(parent, SWT.NONE); + tabComposite.setLayout(new GridLayout(2, false)); + + Image componentIcon = getComponentIcon(); + if (componentIcon != null) { + Label errorImageLabel = new Label(tabComposite, SWT.NONE); + errorImageLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); + errorImageLabel.setImage(componentIcon); + } + + Label label = new Label(tabComposite, SWT.WRAP); + label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + label.setText(getErrorText()); + + return tabComposite; + } + }; + } + + @Override + public boolean isPageModelFactoryFor(Object pageIdentifier) { + return pageIdentifier instanceof AnyType; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/ICreationCondition.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/ICreationCondition.java new file mode 100644 index 00000000000..035cfbfda70 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/ICreationCondition.java @@ -0,0 +1,36 @@ +/***************************************************************************** + * Copyright (c) 2009 ATOS ORIGIN. + * + * + * 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 + * + * Tristan Faure (ATOS ORIGIN) tristan.faure@atosorigin.com - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.commands; + +import org.eclipse.emf.ecore.EObject; + +public interface ICreationCondition { + + /** + * This method returns true if the diagram creation is allowed + * + * @param selectedElement + * the element where the diagram is provided + * @return true if the diagram can be created + */ + boolean create(EObject selectedElement); + + /** + * set the command ID in order to take account the environment in order to + * create a diagram + * + * @param commandID + */ + public void setCommand(String commandID); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/IModelCreationCommand.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/IModelCreationCommand.java new file mode 100644 index 00000000000..8b7f371b28e --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/IModelCreationCommand.java @@ -0,0 +1,31 @@ +/***************************************************************************** + * Copyright (c) 2010 CEA LIST. + * + * + * 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: + * Tatiana Fesenko (CEA LIST) - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.commands; + +import org.eclipse.papyrus.infra.core.resource.ModelSet; + +/** + * The Interface IModelCreationCommand. + */ +public interface IModelCreationCommand { + + /** + * Creates the model. + * + * @param modelSet + * the modelSet set + */ + void createModel(final ModelSet modelSet); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/PerspectiveContextDependence.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/PerspectiveContextDependence.java new file mode 100644 index 00000000000..0c8b0a9d9fe --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/commands/PerspectiveContextDependence.java @@ -0,0 +1,51 @@ +/***************************************************************************** + * Copyright (c) 2010 CEA LIST. + * + * + * 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: + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.commands; + +import org.eclipse.emf.ecore.EObject; + +//FIXME Refactoring Juno : I don't know how to migrate this code +public class PerspectiveContextDependence implements ICreationCondition { + + protected String commandID = null; + + public PerspectiveContextDependence() { + // TODO Auto-generated constructor stub + } + + /** + * {@inheritDoc} + */ + @Override + public boolean create(EObject selectedElement) { + // FIXME Refactoring Juno : I don't know how to migrate this code + // // Get the perspective + // Perspective perspective = ((WorkbenchPage)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()).getActivePerspective(); + // // look for the perspective + // // verify if the command has to be displayed + // if(perspective.getHiddenMenuItems().contains(commandID) && perspective.getHiddenToolbarItems().contains(commandID)) { + // return false; + // } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void setCommand(String commandID) { + this.commandID = commandID; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/AbstractEditorFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/AbstractEditorFactory.java new file mode 100644 index 00000000000..89ace2da204 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/AbstractEditorFactory.java @@ -0,0 +1,109 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory; + +/** + * Abstract base class for Factory of editors. See {@link IEditorFactory}. + * + * + * @author Remi Schnekenburger + * @author Patrick Tessier + * @author cedric dumoulin + */ +public abstract class AbstractEditorFactory implements IPluggableEditorFactory { + + /** + * Expected Class of the diagram to create. + */ + private Class<?> diagramClass; + + /** Expected diagram type (@see {@link Diagram#getType()}) */ + private String expectedType; + + /** + * EditorDescriptor associated to the factory. TODO : Maybe use individual + * setters to set the requested data (ContributorId and Icon). + */ + protected EditorDescriptor editorDescriptor; + + /** + * ServiceRegistry that can be provided to created editors. + */ + private ServicesRegistry serviceRegistry; + + /** + * Creates a new AbstractEditorFactory. + * + * @param diagramClass + * expected Class of the diagram to create. + * @param expectedType + * expected diagram type (@see {@link Diagram#getType()}) + */ + public AbstractEditorFactory(Class<?> diagramClass, String expectedType) { + assert (expectedType != null); + this.diagramClass = diagramClass; + this.expectedType = expectedType; + } + + /** + * Initialize the factory with useful Classes. + * + * @param serviceRegistry + * Service registry that will be provided to created editor. + * @param editorDescriptor + * Descriptor containing data from the Eclipse Extension. + */ + @Override + public void init(ServicesRegistry serviceRegistry, EditorDescriptor editorDescriptor) { + this.editorDescriptor = editorDescriptor; + this.serviceRegistry = serviceRegistry; + + } + + /** + * @return the serviceRegistry + */ + public ServicesRegistry getServiceRegistry() { + return serviceRegistry; + } + + /** + * Returns the expected class for the diagram implementation + * + * @return the expected class for the diagram implementation + */ + public Class<?> getDiagramClass() { + return diagramClass; + } + + /** + * Returns the expected type of the diagram + * + * @return the expected diagram type (@see {@link Diagram#getType()}) + */ + public String getExpectedType() { + return expectedType; + } + + /** + * @return the editorDescriptor + */ + public EditorDescriptor getEditorDescriptor() { + return editorDescriptor; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorDescriptor.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorDescriptor.java new file mode 100644 index 00000000000..d7aa6d558eb --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorDescriptor.java @@ -0,0 +1,168 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * This descriptor describes a nested diagram. It is used by MultiDiagramEditor + * to know about the nested diagram. It is fill by an extension. + * + * @author Cedric Dumoulin + * + */ +public class EditorDescriptor { + + /** + * Editor factory implementation class. + */ + private Class<IPluggableEditorFactory> editorFactoryClass; + + /** + * EditorActionBarContributor Id used to search the + * EditorActionBarContributor requested by the editor. + */ + private String actionBarContributorId; + + /** + * The icon representing the diagram + */ + private ImageDescriptor icon; + + /** + * Resource path to the icon + */ + private String iconPath; + + /** + * The order of this factory. Used when several factories match the same element (Diagram...) + * The lower the order, the higher the priority of this factory + */ + private int order; + + /** + * Constructor. + */ + public EditorDescriptor() { + + } + + /** + * + * @param attribute + */ + public void setActionBarContributorId(String actionBarContributorId) { + this.actionBarContributorId = actionBarContributorId; + + } + + /** + * @see org.eclipse.papyrus.infra.core.extension.diagrameditor.IEditorDescriptor#getActionBarContributorId() + * @return + * + */ + public String getActionBarContributorId() { + return actionBarContributorId; + } + + /** + * get the editor icon path + * + * @return the editor icon path + */ + public ImageDescriptor getIcon() { + return icon; + } + + /** + * set the editor icon + * + * @param icon + * the icon path + */ + public void setIcon(ImageDescriptor icon) { + this.icon = icon; + } + + /** + * get the class of the editor factory + * + * @return the class of the editor + */ + public Class<IPluggableEditorFactory> getEditorFactoryClass() { + return editorFactoryClass; + } + + /** + * set the editor facoty to this descriptor + * + * @param editorFactoryClass + * the class that represents the editor factory + */ + public void setEditorFactoryClass(Class<IPluggableEditorFactory> editorFactoryClass) { + this.editorFactoryClass = editorFactoryClass; + } + + /** + * + * {@inheritDoc} + */ + @Override + public String toString() { + if (editorFactoryClass == null || editorFactoryClass.getName() == null) { + return "[nestedEditor editorFactory:" + editorFactoryClass + "(null)]"; + } + return "[nestedEditor editorFactory:" + editorFactoryClass.getName() + "]"; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + /** + * Set the URL of the Icon + * + * @param iconPath + * path of the Icon + */ + public void setIconURL(String iconPath) { + this.iconPath = iconPath; + } + + /** + * Get the URL of the based images + * + * @return the path of the mai image. can return null if this property is not set + */ + public String getIconURL() { + return iconPath; + } + + /** + * set the Icon thanks to a {@link IConfigurationElement} and {@link String}which represent the path of the Icon + * + * @param element + * @param iconPath + */ + public void setIcon(IConfigurationElement element, String iconPath, String pluginID) { + setIcon(AbstractUIPlugin.imageDescriptorFromPlugin(element.getNamespaceIdentifier(), iconPath)); + setIconURL(element.getNamespaceIdentifier() + '/' + iconPath); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorDescriptorExtensionFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorDescriptorExtensionFactory.java new file mode 100644 index 00000000000..8d57b2fe3ab --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorDescriptorExtensionFactory.java @@ -0,0 +1,99 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +import static org.eclipse.papyrus.infra.core.Activator.log; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.papyrus.infra.core.extension.BadNameExtensionException; +import org.eclipse.papyrus.infra.core.extension.ExtensionException; +import org.eclipse.papyrus.infra.core.extension.ExtensionUtils; +import org.eclipse.papyrus.infra.ui.Activator; + +/** + * A factory used to create editor descriptor object from Eclipse extensions points elements. + * + * @author Cedric Dumoulin + * @author Patrick Tessier + */ +public class EditorDescriptorExtensionFactory extends ExtensionUtils { + + /** singleton eINSTANCE of this class */ + public final static EditorDescriptorExtensionFactory eINSTANCE = new EditorDescriptorExtensionFactory(); + + /** constant for the editor diagram **/ + public final static String EDITOR_DIAGRAM_EXTENSIONPOINT = "editorDiagram"; + + /** constant for the attribute factoryClass **/ + public final static String FACTORYCLASS_ATTRIBUTE = "factoryClass"; + + /** constant for the attribute contextId **/ + public final static String ACTIONBARCONTRIBUTORID_ATTRIBUTE = "actionBarContributorId"; + + /** constant for the attribute icon **/ + public final static String ICON_ATTRIBUTE = "icon"; + + /** constant for the order attribute */ + public final static String ORDER_ATTRIBUTE = "order"; + + /** + * @return the eINSTANCE + */ + public static EditorDescriptorExtensionFactory getInstance() { + return eINSTANCE; + } + + /** + * Create a descriptor instance corresponding to the ConfigurationElement. + * + * @param element + * an {@link IConfigurationElement} see eclipse extension point + * @return a nestedEditorDescriptor structure that contains information to create diagrams + * @throws BadNameExtensionException + */ + @SuppressWarnings("unchecked") + public EditorDescriptor createNestedEditorDescriptor(IConfigurationElement element) throws ExtensionException { + EditorDescriptor res; + + checkTagName(element, EDITOR_DIAGRAM_EXTENSIONPOINT); + + res = new EditorDescriptor(); + res.setEditorFactoryClass((Class<IPluggableEditorFactory>) parseClass(element, FACTORYCLASS_ATTRIBUTE, EDITOR_DIAGRAM_EXTENSIONPOINT)); + res.setActionBarContributorId(element.getAttribute(ACTIONBARCONTRIBUTORID_ATTRIBUTE)); + + int order = 0; // Default + try { + String orderAttribute = element.getAttribute(ORDER_ATTRIBUTE); + if (orderAttribute != null) { + order = Integer.parseInt(orderAttribute); + } + } catch (NumberFormatException ex) { + Activator.log.warn("Invalid order provided by " + element.getContributor() + ". Order should be an integer value"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + res.setOrder(order); + + String iconPath = element.getAttribute(ICON_ATTRIBUTE); + if (iconPath != null) { + /** Implementation which set the icon and register the complete URL of the icon : Bug eclipse 358732 */ + res.setIcon(element, iconPath, Activator.PLUGIN_ID); + + } + + if (log.isDebugEnabled()) { + log.debug("Read editor descriptor " + res); //$NON-NLS-1$ + } + return res; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorFactoryProxy.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorFactoryProxy.java new file mode 100644 index 00000000000..8a80509bda2 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorFactoryProxy.java @@ -0,0 +1,137 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory; + +/** + * A proxy implementation of {@link IEditorFactory} used to do lazy + * instantiation of concrete {@link IPluggableEditorFactory}. This class is used + * by the {@link PluggableEditorFactoryReader} + * + * @author cedric dumoulin + * + */ +public class EditorFactoryProxy implements IEditorFactory { + + /** + * The concrete implementation. + */ + private IPluggableEditorFactory editorFactory; + + /** + * EditorDescriptor associated to the factory. + */ + protected EditorDescriptor editorDescriptor; + + /** + * ServiceRegistry that can be provided to created editors. + */ + private ServicesRegistry serviceRegistry; + + /** + * Constructor. + * + * @param serviceRegistry + * @param editorDescriptor + */ + public EditorFactoryProxy(ServicesRegistry serviceRegistry, EditorDescriptor editorDescriptor) { + this.serviceRegistry = serviceRegistry; + this.editorDescriptor = editorDescriptor; + } + + /** + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory#createIPageModel(java.lang.Object) + * + * @param pageIdentifier + * @return + */ + @Override + public IPageModel createIPageModel(Object pageIdentifier) { + try { + return getEditorFactory().createIPageModel(pageIdentifier); + } catch (Exception ex) { + // An error occurred in a contribution. Do not use this factory + return null; + } + } + + /** + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory#isPageModelFactoryFor(java.lang.Object) + * + * @param pageIdentifier + * @return + */ + @Override + public boolean isPageModelFactoryFor(Object pageIdentifier) { + try { + return getEditorFactory().isPageModelFactoryFor(pageIdentifier); + } catch (Exception ex) { + // An error occurred in a contribution. Do not use this factory + return false; + } + } + + /** + * @return the editorFactory + */ + protected IPluggableEditorFactory getEditorFactory() { + + if (editorFactory == null) { + editorFactory = createEditorFactory(); + } + + return editorFactory; + + } + + /** + * Create an instance of IPluggableEditorFactory as described in the + * editorDescriptor. TODO let propagate the exceptions. + * + * @return + */ + private IPluggableEditorFactory createEditorFactory() { + // Create the requested class. + try { + editorFactory = editorDescriptor.getEditorFactoryClass().newInstance(); + // Set the descriptor. USed by the factory to get the ActionBarId + // and Icon + editorFactory.init(serviceRegistry, editorDescriptor); + return editorFactory; + } catch (InstantiationException e) { + // Lets propagate. This is an implementation problem that should be + // solved by programmer. + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + // Lets propagate. This is an implementation problem that should be + // solved by programmer. + throw new RuntimeException(e); + } + + } + + /** + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory#getFactoryID() + * + * @return + */ + @Override + public String getFactoryID() { + return getEditorFactory().getFactoryID(); + } + + /** + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory#getLabel() + * + * @return + */ + @Override + public String getLabel() { + return getEditorFactory().getLabel(); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorIconFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorIconFactory.java new file mode 100644 index 00000000000..94038633af4 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorIconFactory.java @@ -0,0 +1,152 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.papyrus.infra.ui.editorsfactory.IEditorIconFactoryExtended; +import org.eclipse.swt.graphics.Image; + +/** + * A factory used to create the Icon associated to an editor TODO Lets have a + * common ancestor for {@link EditorIconFactory} and {@link EditorFactoryProxy} + * + * @author cedric dumoulin + * + */ +public class EditorIconFactory implements IEditorIconFactoryExtended { + + /** + * The concrete implementation. + */ + private IPluggableEditorFactory editorFactory; + + /** + * EditorDescriptor associated to the factory. + */ + protected EditorDescriptor editorDescriptor; + + /** + * Cached image for reuse. + */ + protected Image cachedImage; + + /** + * Constructor. + * + * @param serviceRegistry + * @param editorDescriptor + */ + public EditorIconFactory(EditorDescriptor editorDescriptor) { + this.editorDescriptor = editorDescriptor; + } + + /** + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IEditorIconFactory#getEditorIcon(java.lang.Object) + * + * @param pageIdentifier + * @return + */ + @Override + public Image getEditorIcon(Object pageIdentifier) { + + if (cachedImage == null) { + cachedImage = createEditorIcon(pageIdentifier); + } + + return cachedImage; + } + + /** + * Create an Image associated to the editor used to render the specified + * pageIdentifier + * + * @return + */ + @Override + public Image createEditorIcon(Object pageIdentifier) { + ImageDescriptor imageDescriptor = editorDescriptor.getIcon(); + if (imageDescriptor == null) { + return null; + } + Image image = imageDescriptor.createImage(); + return image; + } + + /** + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory#isPageModelFactoryFor(java.lang.Object) + * + * @param pageIdentifier + * @return + */ + @Override + public boolean isPageModelFactoryFor(Object pageIdentifier) { + return getEditorFactory().isPageModelFactoryFor(pageIdentifier); + } + + /** + * @return the editorFactory + */ + protected IPluggableEditorFactory getEditorFactory() { + + if (editorFactory == null) { + editorFactory = createEditorFactory(); + } + + return editorFactory; + + } + + /** + * Create an instance of IPluggableEditorFactory as described in the + * editorDescriptor. TODO let propagate the exceptions. + * + * @return + */ + private IPluggableEditorFactory createEditorFactory() { + // Create the requested class. + try { + editorFactory = editorDescriptor.getEditorFactoryClass().newInstance(); + // Set the descriptor. USed by the factory to get the ActionBarId + // and Icon + // editorFactory.init(serviceRegistry, editorDescriptor); + return editorFactory; + } catch (InstantiationException e) { + // Lets propagate. This is an implementation problem that should be + // solved by programmer. + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + // Lets propagate. This is an implementation problem that should be + // solved by programmer. + throw new RuntimeException(e); + } + + } + + /** + * Return the URL of the main icon used to create this icon + * + * @see org.eclipse.papyrus.infra.ui.editorsfactory.IEditorIconFactory#getURLMainIcon(java.lang.Object) + * + * @param pageIdentifier + * @return + */ + @Override + public String getURLMainIcon(Object pageIdentifier) { + return editorDescriptor.getIconURL(); + } + + /** + * {@inheritDoc} + * + * Dispose the cached image + */ + @Override + public void dispose() { + if (cachedImage != null) { + cachedImage.dispose(); + cachedImage = null; + } + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorNotFoundException.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorNotFoundException.java new file mode 100644 index 00000000000..f0e189ae4e1 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/EditorNotFoundException.java @@ -0,0 +1,53 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +/** + * Editor was not found. + * + * @author dumoulin + * + */ +@SuppressWarnings("serial") +public class EditorNotFoundException extends MultiDiagramException { + + /** + * + */ + public EditorNotFoundException() { + } + + /** + * @param arg0 + */ + public EditorNotFoundException(String arg0) { + super(arg0); + } + + /** + * @param arg0 + */ + public EditorNotFoundException(Throwable arg0) { + super(arg0); + } + + /** + * @param arg0 + * @param arg1 + */ + public EditorNotFoundException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/IPluggableEditorFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/IPluggableEditorFactory.java new file mode 100644 index 00000000000..87f1cde5d2b --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/IPluggableEditorFactory.java @@ -0,0 +1,39 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.editorsfactory.IEditorFactory; + +/** + * This interface should be implemented by Editor Factories that can be declared + * as Eclipse extension. It extends the {@link IEditorFactory} by adding methods + * to initialize the factory with multieditor ServiceRegistry and associated + * editor data. + * + * @author C�dric Dumoulin + * + */ +public interface IPluggableEditorFactory extends IEditorFactory { + + /** + * Initialize the factory with useful Classes. + * + * @param serviceRegistry + * Service registry that will be provided to created editor. + * @param editorDescriptor + * Descriptor containing data from the Eclipse Extension. + */ + public void init(ServicesRegistry serviceRegistry, EditorDescriptor editorDescriptor); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/MultiDiagramException.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/MultiDiagramException.java new file mode 100644 index 00000000000..fd6a89ff037 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/MultiDiagramException.java @@ -0,0 +1,53 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +/** + * Root Exception of MultiDiagram exception + * + * @author dumoulin + * + */ +@SuppressWarnings("serial") +public class MultiDiagramException extends Exception { + + /** + * + */ + public MultiDiagramException() { + } + + /** + * @param arg0 + */ + public MultiDiagramException(String arg0) { + super(arg0); + } + + /** + * @param arg0 + */ + public MultiDiagramException(Throwable arg0) { + super(arg0); + } + + /** + * @param arg0 + * @param arg1 + */ + public MultiDiagramException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/PluggableEditorFactoryReader.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/PluggableEditorFactoryReader.java new file mode 100644 index 00000000000..75dc1d7cc96 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/extension/diagrameditor/PluggableEditorFactoryReader.java @@ -0,0 +1,143 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.extension.diagrameditor; + +import static org.eclipse.papyrus.infra.core.Activator.log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.Platform; +import org.eclipse.papyrus.infra.core.extension.ExtensionException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.editorsfactory.PageIconsRegistry; +import org.eclipse.papyrus.infra.ui.editorsfactory.PageModelFactoryRegistry; + +/** + * This reader is used to read PluggableEditorFactory from the Eclipse extension + * declarations. It can be used to populate an {@link PageModelFactoryRegistry}. + */ +public class PluggableEditorFactoryReader { + + /** ordered list of editor descriptors */ + protected List<EditorDescriptor> editorDescriptors; + + /** ID of the editor extension (schema filename) */ + public static final String EDITOR_EXTENSION_ID = "papyrusDiagram"; + + /** Namespace where to look for the extension points. */ + protected String extensionPointNamespace; + + /** indicates if extension is loaded or not */ + private boolean isExtensionLoaded = false; + + /** + * Create a new Registry reading extension from the specified namespace. The + * namespace is usually the name of the plugin owning the registry. + * + * @param extensionPointNamespace + */ + public PluggableEditorFactoryReader(String extensionPointNamespace) { + super(); + this.extensionPointNamespace = extensionPointNamespace; + editorDescriptors = new ArrayList<EditorDescriptor>(); + } + + /** + * Populate the provided {@link PageModelFactoryRegistry} with {@link IPluggableEditorFactory} read from Eclipse extension declarations. + * For each declared editor, create a proxy encapsulating the real + * EditorFactory. Then the proxy is added to the PageModelFactoryRegistry. + * + * @param pageModelFactoryRegistry + * The object to populate + * @param serviceRegistry + * ServiceRegistry provided to newly instantiated {@link IPluggableEditorFactory}. + */ + public void populate(PageModelFactoryRegistry pageModelFactoryRegistry, ServicesRegistry serviceRegistry) { + + for (EditorDescriptor desc : getEditorDescriptors()) { + + // Create and add a proxy encapsulating the EditorFactory. + pageModelFactoryRegistry.add(new EditorFactoryProxy(serviceRegistry, desc)); + } + } + + /** + * Populate the provided {@link PageIconsRegistry} with icons read from + * Eclipse extension declarations. For each declared editor, create a {@link EditorIconFactory}. + * + * @param pageModelFactoryRegistry + * The object to populate + * @param serviceRegistry + * ServiceRegistry provided to newly instantiated {@link IPluggableEditorFactory}. + */ + public void populate(PageIconsRegistry registry) { + + for (EditorDescriptor desc : getEditorDescriptors()) { + + // Create and add a proxy encapsulating the EditorFactory. + registry.add(new EditorIconFactory(desc)); + } + } + + /** + * Get the list of editor descriptor. + * + * @return the list of editor descriptor. + */ + public List<EditorDescriptor> getEditorDescriptors() { + if (!isExtensionLoaded) { + isExtensionLoaded = true; + initializeEditorDescriptors(); + } + return editorDescriptors; + } + + /** + * Read editor descriptors from extension points. + */ + private void initializeEditorDescriptors() { + // Reading data from plugins + IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor(extensionPointNamespace, EDITOR_EXTENSION_ID); + + for (IConfigurationElement ele : configElements) { + EditorDescriptor desc; + try { + if (EditorDescriptorExtensionFactory.EDITOR_DIAGRAM_EXTENSIONPOINT.equals(ele.getName())) { + desc = EditorDescriptorExtensionFactory.eINSTANCE.createNestedEditorDescriptor(ele); + editorDescriptors.add(desc); + } + } catch (ExtensionException e) { + log.error("Initialization editor problem ", e); //$NON-NLS-1$ + } + } + + Collections.sort(editorDescriptors, (ed1, ed2) -> Integer.compare(ed1.getOrder(), ed2.getOrder())); + + if (log.isDebugEnabled()) { + log.debug("Read " + editorDescriptors.size() + " editor descriptors from Eclipse extensions"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "EditorFactoryRegistry: " + editorDescriptors.toString(); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/PageLayoutStorageState.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/PageLayoutStorageState.java new file mode 100644 index 00000000000..6e604a90914 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/PageLayoutStorageState.java @@ -0,0 +1,164 @@ +/***************************************************************************** + * Copyright (c) 2015 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.internal.commands; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; + +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.jface.commands.ToggleState; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModelUtils; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IPartService; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * The boolean toggle state of the private page layout storage menu item. + */ +public class PageLayoutStorageState extends ToggleState implements IPartListener, PropertyChangeListener { + + private IPartService partService = null; + + private Reference<IMultiDiagramEditor> activeEditor; + + public PageLayoutStorageState() { + super(); + + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + + if (window != null) { + partService = window.getPartService(); + if (partService != null) { + partService.addPartListener(this); + update(partService.getActivePart()); + } + } + } + + @Override + public void dispose() { + if (partService != null) { + partService.removePartListener(this); + } + + super.dispose(); + } + + @Override + public void partDeactivated(IWorkbenchPart part) { + if ((activeEditor != null) && (activeEditor.get() == part)) { + update(null); + } + } + + @Override + public void partActivated(IWorkbenchPart part) { + update(part); + } + + private void update(IWorkbenchPart part) { + // Default state is private storage + boolean state = true; + + unhookSashModelListener(); + + activeEditor = null; + + if (part instanceof IMultiDiagramEditor) { + IMultiDiagramEditor editor = (IMultiDiagramEditor) part; + activeEditor = new WeakReference<>(editor); + state = isPrivateLayout(editor); + } + + hookSashModelListener(); + + // Fires notification if changed from previous state + setValue(state); + } + + // I am a computed value, actually + @Override + public Object getValue() { + IMultiDiagramEditor editor = (activeEditor == null) ? null : activeEditor.get(); + return (editor != null) ? isPrivateLayout(editor) : super.getValue(); + } + + boolean isPrivateLayout(IMultiDiagramEditor editor) { + ModelSet modelSet = (ModelSet) editor.getAdapter(EditingDomain.class).getResourceSet(); + SashModel sashModel = SashModelUtils.getSashModel(modelSet); + + // The default is private layout + return (sashModel == null) || !sashModel.isLegacyMode(); + } + + @Override + public void partBroughtToTop(IWorkbenchPart part) { + // Pass + } + + @Override + public void partClosed(IWorkbenchPart part) { + // Pass + } + + @Override + public void partOpened(IWorkbenchPart part) { + // Pass + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getSource() instanceof SashModel) { + switch (evt.getPropertyName()) { + case SashModel.PROPERTY_LEGACY_MODE: + setValue(!(Boolean) evt.getNewValue()); + break; + } + } + } + + private SashModel getSashModel() { + SashModel result = null; + + if (activeEditor != null) { + IMultiDiagramEditor editor = activeEditor.get(); + if (editor != null) { + result = SashModelUtils.getSashModel(editor.getServicesRegistry()); + } + } + + return result; + } + + private void unhookSashModelListener() { + SashModel sash = getSashModel(); + if (sash != null) { + sash.removePropertyChangeListener(SashModel.PROPERTY_LEGACY_MODE, this); + } + } + + private void hookSashModelListener() { + SashModel sash = getSashModel(); + if (sash != null) { + sash.addPropertyChangeListener(SashModel.PROPERTY_LEGACY_MODE, this); + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/SashLayoutCommandFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/SashLayoutCommandFactory.java new file mode 100644 index 00000000000..61c931b1ec4 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/SashLayoutCommandFactory.java @@ -0,0 +1,226 @@ +/***************************************************************************** + * Copyright (c) 2015 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.internal.commands; + +import java.util.ArrayList; + +import org.eclipse.emf.common.command.AbstractCommand; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.UnexecutableCommand; +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.util.EcoreUtil; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModelUtils; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.core.sashwindows.di.PageRef; +import org.eclipse.papyrus.infra.core.sashwindows.di.SashModel; +import org.eclipse.papyrus.infra.core.sashwindows.di.SashWindowsMngr; +import org.eclipse.papyrus.infra.core.sashwindows.di.TabFolder; +import org.eclipse.papyrus.infra.core.sashwindows.di.util.DiUtils; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; + +/** + * A factory for commands that manipulate the configuration of the sash editor layout. + */ +public class SashLayoutCommandFactory { + private final IMultiDiagramEditor editor; + + public SashLayoutCommandFactory(IMultiDiagramEditor editor) { + super(); + + this.editor = editor; + } + + /** + * Creates a command that toggles whether the sash model is stored in the private + * workspace metadata area or in the shared {@code *.di} file. + * + * @return a toggle command for the private layout storage + */ + public Command createTogglePrivateLayoutCommand() { + Command result = UnexecutableCommand.INSTANCE; + + ModelSet modelSet = (ModelSet) editor.getAdapter(EditingDomain.class).getResourceSet(); + org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel sashModel = SashModelUtils.getSashModel(modelSet); + if (sashModel != null) { + result = new AbstractToggleCommand("Toggle Private Editor Layout") { + private Command toggleRestoreActivePage; + + { + // If we are toggling off private mode, make sure that we stop + // tracking the active page selection if we were tracking it. + // And remember that for undo + if (!sashModel.isLegacyMode()) { + SashWindowsMngr sash = DiUtils.lookupSashWindowsMngr(sashModel.getResource()); + if ((sash != null) && (sash.getSashModel() != null) && sash.getSashModel().isRestoreActivePage()) { + toggleRestoreActivePage = createToggleRestoreActivePageCommand(); + } + } + } + + @Override + public void execute() { + // First, if we need to toggle restoring the active page, do that + if ((toggleRestoreActivePage != null) && toggleRestoreActivePage.canExecute()) { + toggleRestoreActivePage.execute(); + } + + SashWindowsMngr toMove = DiUtils.lookupSashWindowsMngr(sashModel.getResource()); + + // We don't record changes in the sash model for undo/redo, + // so we cannot assume that any changes to the current page selections + // are undoable in the usual way + if (!sashModel.isLegacyMode()) { + Resource sashResource = toMove.eResource(); + URI sharedURI = sashModel.getSharedResourceURI(); + + // Move the contents into the DI model. If the DI resource isn't loaded, + // give up because something is seriously wrong in that case + Resource diResource = modelSet.getResource(sharedURI, false); + if ((diResource != null) && diResource.isLoaded()) { + moveContents(sashResource, diResource); + + if (sashResource.getContents().isEmpty()) { + // Schedule deletion on save + modelSet.getResourcesToDeleteOnSave().add(sashResource.getURI()); + } + } + } else { + Resource sashResource; + URI privateURI = sashModel.getPrivateResourceURI(); + + // Move the contents into the sash model. If the sash resource isn't loaded + // or doesn't exist, it will have to be handled + if (modelSet.getURIConverter().exists(privateURI, null)) { + sashResource = modelSet.getResource(privateURI, true); + } else { + sashResource = modelSet.createResource(privateURI); + } + + // In case we had marked it for deletion, earlier + modelSet.getResourcesToDeleteOnSave().remove(privateURI); + + Resource diResource = toMove.eResource(); + moveContents(diResource, sashResource); + } + + // Re-load from the new resource. Snippets might find this odd, but + // it would be even more odd for there to be any snippets on this model + sashModel.loadModel(modelSet.getURIWithoutExtension()); + } + }; + } + + return result; + } + + void moveContents(Resource fromResource, Resource toResource) { + // Safe copy to allow concurrent modifications + for (EObject root : new ArrayList<>(fromResource.getContents())) { + EObject toReplace = (EObject) EcoreUtil.getObjectByType(toResource.getContents(), root.eClass()); + if (toReplace != null) { + EcoreUtil.replace(toReplace, root); + } else { + if (root instanceof SashWindowsMngr) { + // This one is expected always to be first + toResource.getContents().add(0, root); + } else { + toResource.getContents().add(root); + } + } + } + + } + + /** + * Creates a command that toggles whether the sash model records the currently + * active page to restore it on next opening. + * + * @return a toggle command for the restore-active-page behaviour + */ + public Command createToggleRestoreActivePageCommand() { + Command result = UnexecutableCommand.INSTANCE; + + ModelSet modelSet = (ModelSet) editor.getAdapter(EditingDomain.class).getResourceSet(); + SashWindowsMngr sashWindows = SashModelUtils.getSashWindowsMngr(modelSet); + ISashWindowsContainer container = editor.getAdapter(ISashWindowsContainer.class); + + SashModel sashModel = sashWindows.getSashModel(); + if (sashModel != null) { + // We don't record the tracking of the active page for undo/redo, + // so we cannot assume that any changes to the current page selections + // are undoable in the usual way + result = new AbstractToggleCommand("Toggle Restore Active Page") { + + @Override + public void execute() { + boolean oldValue = sashModel.isRestoreActivePage(); + + if (oldValue) { + // Clear each tab folder's selection + container.getIFolderList().stream() + .map(f -> f.getRawModel()) + .filter(TabFolder.class::isInstance).map(TabFolder.class::cast) + .filter(f -> f.getCurrentSelection() != null) + .forEach(f -> f.setCurrentSelection(null)); + } else { + // Set each tab folder's selection. + // The 'visible pages' are the current selection in each folder + container.getVisiblePages().stream() + .map(p -> p.getRawModel()) + .filter(PageRef.class::isInstance).map(PageRef.class::cast) + .filter(p -> p.getParent().getCurrentSelection() != p) + .forEach(p -> p.getParent().setCurrentSelection(p)); + } + + // The basic toggle + sashModel.setRestoreActivePage(!oldValue); + } + }; + } + + return result; + } + + // + // Nested types + // + + private static abstract class AbstractToggleCommand extends AbstractCommand { + + AbstractToggleCommand(String label) { + super(label); + } + + @Override + protected boolean prepare() { + // Nothing to prepare + return true; + } + + @Override + public void undo() { + // It's a toggle, so yeah, just execute again + execute(); + } + + @Override + public void redo() { + execute(); + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/TogglePageLayoutStorageHandler.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/TogglePageLayoutStorageHandler.java new file mode 100644 index 00000000000..12fa47811e9 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/commands/TogglePageLayoutStorageHandler.java @@ -0,0 +1,78 @@ +/***************************************************************************** + * Copyright (c) 2015 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.internal.commands; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.emf.transaction.RollbackException; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.utils.TransactionHelper; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.services.SaveLayoutBeforeClose; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.statushandlers.StatusManager; + +/** + * Command handler for the private page-layout storage toggle menu. + */ +public class TogglePageLayoutStorageHandler extends AbstractHandler { + + public TogglePageLayoutStorageHandler() { + super(); + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + IEditorPart active = HandlerUtil.getActiveEditor(event); + if (active instanceof IMultiDiagramEditor) { + IMultiDiagramEditor editor = (IMultiDiagramEditor) active; + + // Toggle the storage of the layout + togglePrivatePageLayout(editor); + + // And then save the layout immediately if the editor is not dirty + // (if it is dirty, then the layout will be saved when the editor + // is saved; saving it now would possibly result in inconsistencies) + try { + SaveLayoutBeforeClose save = editor.getServicesRegistry().getService(SaveLayoutBeforeClose.class); + save.saveBeforeClose(editor); + } catch (ServiceException e) { + // Doesn't matter; we'll just have to rely on the normal editor save + } + } + + return null; + } + + public void togglePrivatePageLayout(IMultiDiagramEditor editor) { + Command command = new SashLayoutCommandFactory(editor).createTogglePrivateLayoutCommand(); + EditingDomain domain = editor.getAdapter(EditingDomain.class); + + // Don't execute on the undo history because the changes in the sash model + // are never tracked for undo/redo + try { + TransactionHelper.run(domain, () -> command.execute()); + } catch (RollbackException e) { + StatusManager.getManager().handle(e.getStatus()); + } catch (InterruptedException e) { + Activator.log.error("Failed to execute page layout toggle command", e); //$NON-NLS-1$ + } + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/EditorPreferencePage.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/EditorPreferencePage.java new file mode 100644 index 00000000000..bff4950d394 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/EditorPreferencePage.java @@ -0,0 +1,52 @@ +/***************************************************************************** + * Copyright (c) 2015, 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.internal.preferences; + +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.RadioGroupFieldEditor; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.swt.SWT; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +/** + * The preference page for Papyrus Editor general preferences. + */ +public class EditorPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public EditorPreferencePage() { + super(Messages.EditorPreferencePage_0, SWT.FLAT); + + setDescription(Messages.EditorPreferencePage_5); + } + + @Override + public void init(IWorkbench workbench) { + setPreferenceStore(Activator.getDefault().getPreferenceStore()); + } + + @Override + protected void createFieldEditors() { + addField(new RadioGroupFieldEditor(EditorPreferences.PREF_CONVERT_SHARED_LAYOUT, + Messages.EditorPreferencePage_1, + 1, + new String[][] { + { Messages.EditorPreferencePage_2, YesNo.PROMPT.name() }, + { Messages.EditorPreferencePage_3, YesNo.NO.name() }, + { Messages.EditorPreferencePage_4, YesNo.YES.name() }, + }, + getFieldEditorParent())); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/EditorPreferences.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/EditorPreferences.java new file mode 100644 index 00000000000..0b8748f6032 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/EditorPreferences.java @@ -0,0 +1,88 @@ +/***************************************************************************** + * Copyright (c) 2015, 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.internal.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.papyrus.infra.ui.Activator; + +/** + * Accessor for the Papyrus Editor preferences. + */ +public class EditorPreferences { + public static final String PREF_CONVERT_SHARED_LAYOUT = "convertSharedLayout"; //$NON-NLS-1$ + + private static final EditorPreferences INSTANCE = new EditorPreferences(); + private final IPreferenceStore store; + + private EditorPreferences() { + super(); + + store = Activator.getDefault().getPreferenceStore(); + } + + public static EditorPreferences getInstance() { + return INSTANCE; + } + + /** + * Queries whether the user prefers to migrate shared editor layout to private storage + * always, never, or interactively pop up a dialog to ask (the default). + * + * @return the shared layout conversion on first open preference, which is never + * {@code null} and defaults to {@link YesNo#PROMPT} + */ + public YesNo getConvertSharedPageLayoutToPrivate() { + return YesNo.valueOf(store.getString(PREF_CONVERT_SHARED_LAYOUT)); + } + + /** + * Sets whether the editor will always, never, or ask the user to migrate shared + * (in the {@code *.di} resource) page layout into the private storage ({@code *.sash} resource} + * on the first opening of a Papyrus model in the workspace that uses the shared + * storage (usually from pre-1.0 release). + * + * @param convert + * the preference setting to assign, or {@code null} for the default, which + * is {@link YesNo#PROMPT} + */ + public void setConvertSharedPageLayoutToPrivate(YesNo convert) { + if (convert == null) { + convert = YesNo.PROMPT; + } + + store.setValue(PREF_CONVERT_SHARED_LAYOUT, convert.name()); + } + + // + // Nested types + // + + /** + * Initializer of defaults for the editor preferences. + */ + public static class Initializer extends AbstractPreferenceInitializer { + + public Initializer() { + super(); + } + + @Override + public void initializeDefaultPreferences() { + IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + + store.setDefault(PREF_CONVERT_SHARED_LAYOUT, YesNo.PROMPT.name()); + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/Messages.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/Messages.java new file mode 100644 index 00000000000..4fbebcf89bc --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/Messages.java @@ -0,0 +1,37 @@ +/***************************************************************************** + * Copyright (c) 2015 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.internal.preferences; + +import org.eclipse.osgi.util.NLS; + +/** + * Translatable strings. + */ +class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.papyrus.infra.core.internal.preferences.messages"; //$NON-NLS-1$ + public static String EditorPreferencePage_0; + public static String EditorPreferencePage_1; + public static String EditorPreferencePage_2; + public static String EditorPreferencePage_3; + public static String EditorPreferencePage_4; + public static String EditorPreferencePage_5; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/YesNo.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/YesNo.java new file mode 100644 index 00000000000..a9ee87d9e4f --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/YesNo.java @@ -0,0 +1,21 @@ +/***************************************************************************** + * Copyright (c) 2015 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.internal.preferences; + +/** + * A tri-state boolean-ish preference data type with a "prompt the user" value. + */ +public enum YesNo { + PROMPT, NO, YES; +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/messages.properties b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/messages.properties new file mode 100644 index 00000000000..a34a57ddfbe --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/internal/preferences/messages.properties @@ -0,0 +1,18 @@ +# +# Copyright (c) 2015 Christian W. Damus 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: +# Christian W. Damus - Initial API and implementation +# + +EditorPreferencePage_0=General Editor Settings +EditorPreferencePage_1=Convert shared storage of editor layout to private: +EditorPreferencePage_2=Ask each time +EditorPreferencePage_3=Never +EditorPreferencePage_4=Always +EditorPreferencePage_5=General settings for the Papyrus multi-diagram editor. diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/DoSaveEvent.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/DoSaveEvent.java new file mode 100644 index 00000000000..51e34e85d83 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/DoSaveEvent.java @@ -0,0 +1,66 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; + +/** + * Event sent whith a Save or SaveAs. + * + * @author cedric dumoulin + * + */ +public class DoSaveEvent { + + final protected ServicesRegistry serviceRegistry; + + final protected IMultiDiagramEditor multiDiagramEditor; + + final protected boolean isAutoSave; + + /** + * Create an Event that is sent with a Save or SaveAs. The same event can be + * reused. Constructor. + * + * @param serviceRegistry + * @param multiDiagramEditor + */ + public DoSaveEvent(ServicesRegistry serviceRegistry, IMultiDiagramEditor multiDiagramEditor) { + this(serviceRegistry, multiDiagramEditor, false); + } + + /** + * Create an Event that is sent with a Save or SaveAs. The same event can be + * reused. Constructor. + * + * @param serviceRegistry + * @param multiDiagramEditor + * @param isAutoSave + */ + public DoSaveEvent(ServicesRegistry serviceRegistry, IMultiDiagramEditor multiDiagramEditor, boolean isAutoSave) { + this.serviceRegistry = serviceRegistry; + this.multiDiagramEditor = multiDiagramEditor; + this.isAutoSave = isAutoSave; + } + + /** + * @return the serviceRegistry + */ + public ServicesRegistry getServiceRegistry() { + return serviceRegistry; + } + + /** + * @return the multiDiagramEditor + */ + public IMultiDiagramEditor getMultiDiagramEditor() { + return multiDiagramEditor; + } + + public boolean isAutoSave() { + return isAutoSave; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/IEditorInputChangedListener.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/IEditorInputChangedListener.java new file mode 100644 index 00000000000..64835143e55 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/IEditorInputChangedListener.java @@ -0,0 +1,40 @@ +/***************************************************************************** + * Copyright (c) 2010 LIFL & CEA LIST. + * + * + * 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: + * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +import org.eclipse.ui.part.FileEditorInput; + +/** + * Interface implemented by classes wishing to be notified of the inputChanged + * event after a call to {@link ISaveAndDirtyService#doSaveAs()}. + * + * @author cedric dumoulin + * + */ +public interface IEditorInputChangedListener { + + /** + * + * @param fileEditorInput + * The new value of EditorInput + */ + public void editorInputChanged(FileEditorInput fileEditorInput); + + /** + * Called when the value of the isDirty() flag has changed. + */ + public void isDirtyChanged(); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ILifeCycleEventsProvider.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ILifeCycleEventsProvider.java new file mode 100644 index 00000000000..31f2afae670 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ILifeCycleEventsProvider.java @@ -0,0 +1,57 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +/** + * Concrete implementation of this interface allows to listen on various + * lifecycle events. This interface is the "public" part of the {@link LifeCycleEventsProvider}. + * + * @author cedric dumoulin + * + */ +public interface ILifeCycleEventsProvider { + + /** + * Add specified listener. + * + * @param listener + */ + public void addDoSaveListener(ISaveEventListener listener); + + /** + * Remove specified listener. + * + * @param listener + */ + public void removeDoSaveListener(ISaveEventListener listener); + + /** + * Add specified listener. + * + * @param listener + */ + public void addAboutToDoSaveListener(ISaveEventListener listener); + + /** + * Remove specified listener. + * + * @param listener + */ + public void removeAboutToDoSaveListener(ISaveEventListener listener); + + /** + * Add specified listener. + * + * @param listener + */ + public void addPostDoSaveListener(ISaveEventListener listener); + + /** + * Remove specified listener. + * + * @param listener + */ + public void removePostDoSaveListener(ISaveEventListener listener); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ISaveAndDirtyService.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ISaveAndDirtyService.java new file mode 100644 index 00000000000..a93b164ea0d --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ISaveAndDirtyService.java @@ -0,0 +1,56 @@ +/***************************************************************************** + * Copyright (c) 2010 LIFL & CEA LIST. + * + * + * 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: + * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.ui.ISaveablePart; + +/** + * @author dumoulin + * + */ +public interface ISaveAndDirtyService extends ISaveablePart { + + /** + * Register a nested {@link ISaveablePart} as a listener that will be + * notified each time a {@link #doSave(IProgressMonitor)} or {@link #doSaveAs()} is performed. Also, it will be asked for the + * dirtyState. + * + * @param saveablePart + */ + public abstract void registerIsaveablePart(ISaveablePart saveablePart); + + /** + * Remove the specified {@link ISaveablePart} from the list of listeners. + * + * @param saveablePart + */ + public abstract void removeIsaveablePart(ISaveablePart saveablePart); + + /** + * Add a listeners on input changed event. + * + * @param inputChangedListener + */ + public void addInputChangedListener(IEditorInputChangedListener inputChangedListener); + + /** + * Remove a listeners on input changed event. + * + * @param inputChangedListener + */ + public void removeInputChangedListener(IEditorInputChangedListener inputChangedListener); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ISaveEventListener.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ISaveEventListener.java new file mode 100644 index 00000000000..03d6aaa6a02 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/ISaveEventListener.java @@ -0,0 +1,27 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +/** + * Interface used to listen on open, save and saveAs events. + * + * @author cedric dumoulin + * + * @param <T> + * Type of event passed to methods. + */ +public interface ISaveEventListener { + + /** + * + * @param editor + */ + public void doSave(DoSaveEvent event); + + /** + * + * @param editor + */ + public void doSaveAs(DoSaveEvent event); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/LifeCycleEventsProvider.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/LifeCycleEventsProvider.java new file mode 100644 index 00000000000..3fb4e594271 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/LifeCycleEventsProvider.java @@ -0,0 +1,291 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides events about the life cycle of a MultiEditor. Not all + * life cycle events are available. Available events: + * <ul> + * <li>aboutToDoSave, aboutToDoSaveAs - SaveEventListener</li> + * <li>doSave, doSaveAs - SaveEventListener</li> + * <li>afterDoSave, afterDoSaveAs - SaveEventListener</li> + * <li></li> + * <li></li> + * </ul> + * + * + * @author cedric dumoulin + * + */ +public class LifeCycleEventsProvider implements ILifeCycleEventsProvider { + + /** + * + */ + protected SaveEventListenerLazyList preSaveListeners = new SaveEventListenerLazyList(); + + /** + * + */ + protected SaveEventListenerLazyList saveListeners = new SaveEventListenerLazyList(); + + /** + * + */ + protected SaveEventListenerLazyList postSaveListeners = new SaveEventListenerLazyList(); + + /** + * Add specified listener. + * + * @param listener + */ + @Override + public void addDoSaveListener(ISaveEventListener listener) { + + saveListeners.addListener(listener); + } + + /** + * Remove specified listener. + * + * @param listener + */ + @Override + public void removeDoSaveListener(ISaveEventListener listener) { + saveListeners.removeListener(listener); + } + + /** + * Add specified listener. + * + * @param listener + */ + @Override + public void addAboutToDoSaveListener(ISaveEventListener listener) { + + preSaveListeners.addListener(listener); + } + + /** + * Remove specified listener. + * + * @param listener + */ + @Override + public void removeAboutToDoSaveListener(ISaveEventListener listener) { + preSaveListeners.removeListener(listener); + } + + /** + * Add specified listener. + * + * @param listener + */ + @Override + public void addPostDoSaveListener(ISaveEventListener listener) { + + postSaveListeners.addListener(listener); + } + + /** + * Remove specified listener. + * + * @param listener + */ + @Override + public void removePostDoSaveListener(ISaveEventListener listener) { + postSaveListeners.removeListener(listener); + } + + // ****************************************************** // + // Fire events methods // + // ****************************************************** // + + /** + * Fire AboutToSaveEvent to registered Listeners. + * + * @param editorPart + */ + public void fireAboutToDoSaveEvent(DoSaveEvent event) { + preSaveListeners.fireSaveEvent(event); + } + + /** + * Fire AboutToSaveAs to registered Listeners. + * + * @param editorPart + */ + public void fireAboutToDoSaveAsEvent(DoSaveEvent event) { + preSaveListeners.fireSaveAsEvent(event); + } + + /** + * Fire AboutToSaveEvent to registered Listeners. + * + * @param editorPart + */ + public void fireDoSaveEvent(DoSaveEvent event) { + saveListeners.fireSaveEvent(event); + } + + /** + * Fire AboutToSaveAs to registered Listeners. + * + * @param editorPart + */ + public void fireDoSaveAsEvent(DoSaveEvent event) { + saveListeners.fireSaveAsEvent(event); + } + + /** + * Fire AboutToSaveEvent to registered Listeners. + * + * @param editorPart + */ + public void firePostDoSaveEvent(DoSaveEvent event) { + postSaveListeners.fireSaveEvent(event); + } + + /** + * Fire AboutToSaveAs to registered Listeners. + * + * @param editorPart + */ + public void firePostDoSaveAsEvent(DoSaveEvent event) { + postSaveListeners.fireSaveAsEvent(event); + } + + /** + * Fire all Save events (about, events, post) to registered Listeners. + * Exceptions from listeners are propagated and stop the event chain. + * + * @param editorPart + */ + public void fireAllDoSaveEvent(DoSaveEvent event) { + fireAboutToDoSaveEvent(event); + fireDoSaveEvent(event); + firePostDoSaveEvent(event); + } + + /** + * Fire all SaveAs events (about, events, post) to registered Listeners. If + * one of the saveAs event fail, post events are not sent. + * + * @param editorPart + */ + public void fireAllDoSaveAsEvent(DoSaveEvent event) { + fireAboutToDoSaveAsEvent(event); + fireDoSaveAsEvent(event); + firePostDoSaveAsEvent(event); + } + + /** + * Base class encapsulating a lazy creation list. + * + * @author cedric dumoulin + * + * @param <T> + */ + abstract protected class AbstractEventListenersLazyList<T> { + + List<T> listeners; + + /** + * Add specified listener. + * + * @param listener + */ + public void addListener(T listener) { + // Lazy creation + if (listeners == null) { + listeners = new ArrayList<T>(); + } + + // do not add if already present. + if (listeners.contains(listener)) { + return; + } + + listeners.add(listener); + } + + /** + * Remove specified listener. + * + * @param listener + */ + public void removeListener(T listener) { + // Lazy creation + if (listeners == null) { + return; + } + + listeners.remove(listener); + } + + /** + * @return the listeners + */ + protected List<T> getListeners() { + return listeners; + } + + /** + * Remove all listeners. + */ + protected void clear() { + if (listeners != null) { + listeners.clear(); + } + } + } + + /** + * List of {@link ISaveEventListener}. + * + * @author cedric dumoulin + * + */ + protected class SaveEventListenerLazyList extends AbstractEventListenersLazyList<ISaveEventListener> { + + /** + * Fire OpenEvent to registered Listeners. If a listener throw an + * exception, remaining listeners are called, and then the exception is + * resent. + * + * @param editorPart + */ + public void fireSaveEvent(DoSaveEvent event) { + // Lazy creation + if (listeners == null) { + return; + } + + for (ISaveEventListener listener : listeners) { + listener.doSave(event); + } + } + + /** + * Fire OpenEvent to registered Listeners. + * + * @param editorPart + */ + public void fireSaveAsEvent(DoSaveEvent event) { + // Lazy creation + if (listeners == null) { + return; + } + + for (ISaveEventListener listener : listeners) { + listener.doSaveAs(event); + } + + } + + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/LifeCycleEventsProviderServiceFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/LifeCycleEventsProviderServiceFactory.java new file mode 100644 index 00000000000..b9126079262 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/LifeCycleEventsProviderServiceFactory.java @@ -0,0 +1,79 @@ +/***************************************************************************** + * Copyright (c) 2010 LIFL & CEA LIST. + * + * + * 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: + * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +import org.eclipse.papyrus.infra.core.services.IServiceFactory; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; + +/** + * A service factory to create the {@link ILifeCycleEventsProvider} service. + * This provide a nickname for {@link SaveAndDirtyService} service. This + * serviceFactory depends on {@link SaveAndDirtyService} service. + * + * @author cedric dumoulin + * + */ +public class LifeCycleEventsProviderServiceFactory implements IServiceFactory { + + /** + * The sashModelMangr. + */ + private SaveAndDirtyService saveAndDirtyService; + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + * @throws ServiceException + */ + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + // Get required services + // This rely on the real implementation. + saveAndDirtyService = (SaveAndDirtyService) servicesRegistry.getService(ISaveAndDirtyService.class); + + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + * + * @throws ServiceException + */ + @Override + public void startService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#disposeService() + * + * @throws ServiceException + */ + @Override + public void disposeService() throws ServiceException { + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IServiceFactory#createServiceInstance() + * + * @return + * @throws ServiceException + */ + @Override + public Object createServiceInstance() throws ServiceException { + return saveAndDirtyService; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/SaveAndDirtyService.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/SaveAndDirtyService.java new file mode 100644 index 00000000000..7e858153695 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/lifecycleevents/SaveAndDirtyService.java @@ -0,0 +1,550 @@ +/***************************************************************************** + * Copyright (c) 2010, 2013 LIFL & CEA LIST. + * + * + * 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: + * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation + * Christian W. Damus (CEA) - Don't make editor dirty on empty ResourceSetChangeEvent + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.lifecycleevents; + +import static org.eclipse.papyrus.infra.core.Activator.log; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.EventObject; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.common.command.BasicCommandStack; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CommandStack; +import org.eclipse.emf.common.command.CommandStackListener; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.transaction.NotificationFilter; +import org.eclipse.emf.transaction.ResourceSetChangeEvent; +import org.eclipse.emf.transaction.ResourceSetListener; +import org.eclipse.emf.transaction.RollbackException; +import org.eclipse.emf.transaction.Transaction; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.services.IService; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.ServiceUtils; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.ISaveablePart; +import org.eclipse.ui.actions.WorkspaceModifyOperation; +import org.eclipse.ui.dialogs.SaveAsDialog; +import org.eclipse.ui.part.FileEditorInput; + +/** + * A Papyrus Service allowing to perform save and saveAs on Papyrus Models. The + * service also allows to listen on the dirty state of the Models. <br> + * The service implements the {@link ISaveablePart} interface, and can be used + * directly in part requiring such interface of adapter. + * + * <br> + * This class allows nested editors to register themselves as nested {@link ISaveablePart}. In this case, the registered part will be notified + * each time a save or saveAs is performed. Also, the nested part will be asked + * for its dirtyState. + * + * TODO : Improve the implementation by registering the isDirty flag value, and + * firing events only if the value really change. Actually, the event is fired + * every time the model is modified, even if the virtual value of the flag + * hasn't changed. + * + * @author cedric dumoulin + * + */ +public class SaveAndDirtyService extends LifeCycleEventsProvider implements ISaveablePart, IService, ISaveAndDirtyService { + + /** + * Class used to propagate life cycle events. This class can be retrieved as + * a service using {@link ILifeCycleEventsProvider}.class. This class + * extends LifeCycleEventsProvider, so the local variable is set with + * ourself (historical reasons). TODO : remove this local variable. + */ + protected LifeCycleEventsProvider lifeCycleEventsProvider = this; + + /** + * Cached event that can be reused. + */ + protected DoSaveEvent lifeCycleEvent; + + /** + * Model set managing models. + */ + private ModelSet resourceSet; + + /** + * + */ + private TransactionalEditingDomain transactionalEditingDomain; + + /** + * The serviceRegistry. + */ + // private ServicesRegistry servicesRegistry; + + /** + * Associated editor. Needed by saveAs to synchronize editor input. + */ + private IMultiDiagramEditor multiDiagramEditor; + + /** + * List of registered {@link ISaveablePart}. This are usually nested + * editors. + */ + private ISaveablePartList registeredIsaveablePart; + + /** + * List of listeners on input changed event after a call to saveAs. + */ + private List<IEditorInputChangedListener> inputChangedListeners; + + /** + * Listener on commandStack changes. + */ + private final CommandStackListener commandStackListener = new CommandStackListener() { + + @Override + public void commandStackChanged(EventObject event) { + + fireIsDirtyChanged(); + }; + }; + + /* + * Listener on ResourceSet + */ + private final ResourceSetListener resourceSetListener = new ResourceSetListener() { + + @Override + public NotificationFilter getFilter() { + return null; + } + + @Override + public boolean isAggregatePrecommitListener() { + return false; + } + + @Override + public boolean isPostcommitOnly() { + return true; + } + + @Override + public boolean isPrecommitOnly() { + return false; + } + + @Override + public void resourceSetChanged(ResourceSetChangeEvent event) { + if (event.getTransaction() != null && event.getTransaction().getStatus().isOK() && madePersistableChanges(event)) { + fireIsDirtyChanged(); + } + } + + private boolean madePersistableChanges(ResourceSetChangeEvent event) { + return !event.getNotifications().isEmpty() && !isUnprotected(event.getTransaction()); + } + + private boolean isUnprotected(Transaction transaction) { + return !Boolean.TRUE.equals(transaction.getOptions().get(Transaction.OPTION_UNPROTECTED)); + } + + @Override + public Command transactionAboutToCommit(ResourceSetChangeEvent event) throws RollbackException { + return null; + } + + }; + + /** + * Constructor. + * + */ + public SaveAndDirtyService() { + registeredIsaveablePart = new ISaveablePartList(); + inputChangedListeners = new ArrayList<IEditorInputChangedListener>(); + } + + /** + * Initialize the service. Retrieve other required services (ModelSet, + * CoreEditor). + * + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + * @throws ServiceException + */ + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + + // this.servicesRegistry = servicesRegistry; + + // Retrieve required services. + resourceSet = servicesRegistry.getService(ModelSet.class); + multiDiagramEditor = servicesRegistry.getService(IMultiDiagramEditor.class); + transactionalEditingDomain = ServiceUtils.getInstance().getTransactionalEditingDomain(servicesRegistry); + + // Initialize and register the ILifeCycleEventsProvider service (which + // is ourself). + // This mean that the ILifeCycleEventsProvider is not available until we + // are started. + lifeCycleEvent = new DoSaveEvent(servicesRegistry, multiDiagramEditor); + // servicesRegistry.add(ILifeCycleEventsProvider.class, 1, + // lifeCycleEventsProvider); + + } + + /** + * Do nothing. + * + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + * + * @throws ServiceException + */ + @Override + public void startService() throws ServiceException { + + // Listen to the modifications of the EMF model + transactionalEditingDomain.getCommandStack().addCommandStackListener(commandStackListener); + + // Let's listen to the resource set change + transactionalEditingDomain.addResourceSetListener(resourceSetListener); + } + + /** + * @see org.eclipse.papyrus.infra.core.services.IService#disposeService() + * + * @throws ServiceException + */ + @Override + public void disposeService() throws ServiceException { + if (transactionalEditingDomain != null) { + // Check if commandStack is null (meaning that transactionalEditingDomain + // is disposed + CommandStack commandStack = transactionalEditingDomain.getCommandStack(); + if (commandStack != null) { + transactionalEditingDomain.getCommandStack().removeCommandStackListener(commandStackListener); + } + transactionalEditingDomain.removeResourceSetListener(resourceSetListener); + // resourceSetListener = null; + } + + // clean properties in order to help GC + inputChangedListeners.clear(); + inputChangedListeners = null; + multiDiagramEditor = null; + // servicesRegistry = null; + transactionalEditingDomain = null; + resourceSet = null; + lifeCycleEvent = null; + + postSaveListeners.clear(); + saveListeners.clear(); + preSaveListeners.clear(); + + + } + + /** + * Save the Models + * + * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) + * + * @param monitor + */ + @Override + public void doSave(IProgressMonitor monitor) { + // Sent pre doSave event + lifeCycleEventsProvider.fireAboutToDoSaveEvent(lifeCycleEvent); + + // sent doSaveEvent + lifeCycleEventsProvider.fireDoSaveEvent(lifeCycleEvent); + // Perform local doSave + // TODO : put it in a listener ? + try { + // Save each associated resource + resourceSet.save(monitor); + // notify registered IsaveablePart + registeredIsaveablePart.doSave(monitor); + markSaveLocation(); + } catch (IOException e) { + log.error("Error during save", e); //$NON-NLS-1$ + } + + // Sent post Events + lifeCycleEventsProvider.firePostDoSaveEvent(lifeCycleEvent); + + } + + /** + * @see org.eclipse.ui.ISaveablePart#doSaveAs() + * + */ + @Override + public void doSaveAs() { + // Sent pre doSave event + lifeCycleEventsProvider.fireAboutToDoSaveAsEvent(lifeCycleEvent); + + // sent doSaveEvent + lifeCycleEventsProvider.fireDoSaveAsEvent(lifeCycleEvent); + // Perform local doSaveAs + + // Show a SaveAs dialog + Shell shell = multiDiagramEditor.getEditorSite().getWorkbenchWindow().getShell(); + SaveAsDialog dialog = new SaveAsDialog(shell); + dialog.setOriginalFile(((IFileEditorInput) multiDiagramEditor.getEditorInput()).getFile()); + dialog.open(); + final IPath path = dialog.getResult(); + if (path != null) { + // try to save the editor's contents under a different file name + final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); + try { + new ProgressMonitorDialog(shell).run(false, // don't fork + false, // can't cancel + new WorkspaceModifyOperation() { // run this operation + + @Override + public void execute(final IProgressMonitor monitor) { + try { + // to event bad redirection after the saveAs + // see bug 319023 + EcoreUtil.resolveAll(resourceSet); + resourceSet.saveAs(path); + // notify registered IsaveablePart + registeredIsaveablePart.doSave(monitor); + } catch (IOException e) { + log.error("Unable to saveAs the resource set", e); //$NON-NLS-1$ + } + } + }); + // set input to the new file + fireEditorInputChanged(new FileEditorInput(file)); + markSaveLocation(); + } catch (InterruptedException e) { + // should not happen, since the monitor dialog is not cancelable + log.error(e); + } catch (InvocationTargetException e) { + log.error(e); + } + } + + // sent doSaveEvent + lifeCycleEventsProvider.firePostDoSaveAsEvent(lifeCycleEvent); + } + + /** + * Change the input of the underlying editor. + * + * @param fileEditorInput + */ + private void fireEditorInputChanged(FileEditorInput fileEditorInput) { + + for (IEditorInputChangedListener listener : inputChangedListeners) { + try { + listener.editorInputChanged(fileEditorInput); + } catch (Exception e) { + log.error("Can't set input for '" + listener + "'", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + } + + /** + * Fire a PropertyChanged event to registered {@link IEditorInputChangedListener}. + * + * @param propertyId + */ + private void fireIsDirtyChanged() { + + for (IEditorInputChangedListener listener : inputChangedListeners) { + try { + listener.isDirtyChanged(); + } catch (Exception e) { + log.error("Can't call listener '" + listener + "'", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + } + + /** + * Return true if the multiEditor is dirty, false otherwise. The dirty state + * is compute as follow: + * <ul> + * <li>The {@link TransactionalEditingDomain} commandStack is checked</li> + * <li>and each registered nested Isaveable.isDirty() state is checked</li> + * <li></li> + * <li></li> + * <li></li> + * <li></li> + * </ul> + * If one of these states is false, the returned value is false. <br> + * If all of these states are true, the returned value is true. + * + * @see org.eclipse.ui.ISaveablePart#isDirty() + * + * @return + */ + @Override + public boolean isDirty() { + // First, look if the model part (EMF) is dirty, else look at the + // Graphical part (GEF/GMF) + if (transactionalEditingDomain == null) { + return false; + } + return ((BasicCommandStack) transactionalEditingDomain.getCommandStack()).isSaveNeeded() || registeredIsaveablePart.isDirty(); + } + + /** + * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() + * + * @return + */ + @Override + public boolean isSaveAsAllowed() { + return true; + } + + /** + * @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded() + * + * @return + */ + @Override + public boolean isSaveOnCloseNeeded() { + return isDirty(); + } + + /** + * Mark the command stack of all sub-editors. Default implementation do + * nothing. + */ + protected void markSaveLocation() { + ((BasicCommandStack) transactionalEditingDomain.getCommandStack()).saveIsDone(); + fireIsDirtyChanged(); + } + + /** + * Register a nested {@link ISaveablePart} as a listener that will be + * notified each time a {@link #doSave(IProgressMonitor)} or {@link #doSaveAs()} is performed. Also, it will be asked for the + * dirtyState. + * + * @param saveablePart + */ + @Override + public void registerIsaveablePart(ISaveablePart saveablePart) { + registeredIsaveablePart.add(saveablePart); + } + + /** + * Remove the specified {@link ISaveablePart} from the list of listeners. + * + * @param saveablePart + */ + @Override + public void removeIsaveablePart(ISaveablePart saveablePart) { + registeredIsaveablePart.remove(saveablePart); + } + + /** + * Add a listeners on input changed event. + * + * @param inputChangedListener + */ + @Override + public void addInputChangedListener(IEditorInputChangedListener inputChangedListener) { + inputChangedListeners.add(inputChangedListener); + } + + /** + * Remove a listeners on input changed event. + * + * @param inputChangedListener + */ + @Override + public void removeInputChangedListener(IEditorInputChangedListener inputChangedListener) { + inputChangedListeners.remove(inputChangedListener); + } + + /** + * A list of {@link ISaveablePart}. + * + * @author dumoulin + * + */ + public class ISaveablePartList extends ArrayList<ISaveablePart> { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Return true if one of the part is dirty, false if all part are not + * dirty. + * + * @return + */ + public boolean isDirty() { + for (ISaveablePart part : this) { + if (part.isDirty()) { + return true; + } + } + + return false; + } + + /** + * Call doSave on each registered {@link ISaveablePart}. + * + * @param monitor + */ + public void doSave(IProgressMonitor monitor) { + for (ISaveablePart part : this) { + + try { + part.doSave(monitor); + } catch (Exception e) { + log.error("Can't save ISaveablePart '" + part + "'", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + } + + /** + * Call doSaveAs on each registered {@link ISaveablePart}. + * + * @param monitor + */ + public void doSaveAs() { + for (ISaveablePart part : this) { + try { + part.doSaveAs(); + } catch (Exception e) { + log.error("Can't save ISaveablePart '" + part + "'", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractCommonCommandHandler.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractCommonCommandHandler.java new file mode 100644 index 00000000000..deb6799a923 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractCommonCommandHandler.java @@ -0,0 +1,98 @@ +/***************************************************************************** + * Copyright (c) 2015 CEA LIST. + * + * + * 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: + * Francois Le Fevre (CEA LIST) francois.le-fevre@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.menu; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.ui.util.ServiceUtilsForHandlers; +import org.eclipse.ui.ISources; + +/** + * This abstract command handler: - calculates the current selection - + * calculates the visibility and enablement based on command executability - + * executes the command in Papyrus command stack + * + */ +public abstract class AbstractCommonCommandHandler extends AbstractHandler { + + /** + * Iterate over current selection and build a list of the {@link EObject} contained in the selection. + * + * @return the currently selected {@link EObject} + */ + protected abstract List<EObject> getSelectedElements(); + + /** + * + * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent) + * + * @param event + * @return null + * @throws ExecutionException + */ + @Override + public abstract Object execute(ExecutionEvent event) throws ExecutionException; + + protected TransactionalEditingDomain getEditingDomain(ExecutionEvent event) { + try { + return ServiceUtilsForHandlers.getInstance().getTransactionalEditingDomain(event); + } catch (ServiceException ex) { + return null; + } + } + + @Override + public void setEnabled(Object evaluationContext) { + if (evaluationContext instanceof IEvaluationContext) { + Object selection = ((IEvaluationContext) evaluationContext).getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME); + if (selection instanceof Collection<?>) { + this.selection = (selection instanceof List<?>) ? (List<?>) selection : new java.util.ArrayList<Object>((Collection<?>) selection); + } else if (selection instanceof IStructuredSelection) { + this.selection = ((IStructuredSelection) selection).toList(); + } + setBaseEnabled(computeEnabled()); + this.selection = Collections.EMPTY_LIST; + } + super.setEnabled(evaluationContext); + } + + protected boolean computeEnabled() { + List<EObject> elts = getSelectedElements(); + return !(elts.size() == 0); + } + + protected List<?> getSelection() { + return selection; + } + + protected List<?> selection = Collections.EMPTY_LIST; + + /** + * + * @return true if the command can be executed + */ + public boolean isVisible() { + return isEnabled(); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractEMFParametricOnSelectedElementsAction.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractEMFParametricOnSelectedElementsAction.java new file mode 100644 index 00000000000..001b9b50bc4 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractEMFParametricOnSelectedElementsAction.java @@ -0,0 +1,61 @@ +/***************************************************************************** + * Copyright (c) 2015 CEA LIST. + * + * + * 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: + * Francois Le Fevre (CEA LIST) francois.le-fevre@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.menu; + +import java.util.List; + +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.UnexecutableCommand; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.papyrus.infra.ui.menu.AbstractParametricOnSelectedElementsAction; + + +public abstract class AbstractEMFParametricOnSelectedElementsAction extends AbstractParametricOnSelectedElementsAction { + + /** + * + * Constructor. + * + * @param parameter + * parameter for the action + * @param selectedEditPart + * the selectedEditPart for the action + */ + public AbstractEMFParametricOnSelectedElementsAction(String parameter, List<EObject> selectedEditPart) { + super(parameter, selectedEditPart); + } + + /** + * Returns the command for this action + * + * @return + * the command for this action + */ + public Command getCommand() { + if (isEnabled()) { + Command cmd = getBuildedCommand(); + if (cmd != null && cmd.canExecute()) { + return cmd; + } + } + return UnexecutableCommand.INSTANCE; + } + + /** + * + * @return + * the command for this action + */ + protected abstract Command getBuildedCommand(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractParametricOnSelectedElementsAction.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractParametricOnSelectedElementsAction.java new file mode 100644 index 00000000000..5aa80a0b054 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/AbstractParametricOnSelectedElementsAction.java @@ -0,0 +1,115 @@ +/***************************************************************************** + * Copyright (c) 2015 CEA LIST. + * + * + * 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: + * Francois Le Fevre (CEA LIST) francois.le-fevre@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.menu; + +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers; + + +public abstract class AbstractParametricOnSelectedElementsAction { + + /** + * parameter for the action + */ + protected String parameter; + + /** + * selected EditPart + */ + private List<EObject> selection; + + /** + * + * Constructor. + * + * @param parameter + * parameter for the action + * @param selectedEditPart + * the selectedEditPart for the action + */ + public AbstractParametricOnSelectedElementsAction(String parameter, List<EObject> selectedEditPart) { + this.parameter = parameter; + this.selection = selectedEditPart; + } + + /** + * Returns the selected Editparts for this action + * + * @return + * {@link #selection} + */ + protected List<EObject> getSelection() { + return selection; + } + + /** + * Test if the command can be build + * + * @return + * <code>true</code> if the command can be build + */ + public boolean isEnabled() { + return true; + //return !selection.isEmpty(); + } + + /** + * Gets the parameter. + * + * @return the parameter + */ + public String getParameter() { + return parameter; + } + + + /** + * Sets the parameter. + * + * @param parameter + * the new parameter + */ + public void setParameter(String parameter) { + this.parameter = parameter; + } + + /** + * executes the action + */ + public void doRun(IProgressMonitor progressMonitor) { + // may be implemented by inherited class + }; + + + /** + * Returns the {@link TransactionalEditingDomain} + * + * @return the {@link TransactionalEditingDomain} or <code>null</code> if it can not be found + */ + protected TransactionalEditingDomain getEditingDomain() { + TransactionalEditingDomain editingDomain = null; + try { + editingDomain = ServiceUtilsForActionHandlers.getInstance().getTransactionalEditingDomain(); + } catch (ServiceException e) { + Activator.log.error(e); + } + return editingDomain; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NameNormalization.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NameNormalization.java new file mode 100644 index 00000000000..44d3711682d --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NameNormalization.java @@ -0,0 +1,24 @@ +/***************************************************************************** + * Copyright (c) 2015 CEA LIST 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: + * CEA LIST - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.menu; + +/** + * @author flefevre + * + */ +public interface NameNormalization { + + public String normalizeName(String name, String parameter); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NameNormalizationCommand.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NameNormalizationCommand.java new file mode 100644 index 00000000000..563cb346a83 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NameNormalizationCommand.java @@ -0,0 +1,124 @@ +/***************************************************************************** + * Copyright (c) 2015 CEA LIST. + * + * + * 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: + * Francois Le Fevre (CEA LIST) francois.le-fevre@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.menu; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +/** + * this command is used to create a link "satisfyBy" between requirement and namedElement + * + */ +public abstract class NameNormalizationCommand extends RecordingCommand implements NameNormalization{ + protected EObject source; + protected String parameter; + + private final String BLANK= new String("");//$NON-NLS-1$ + private final String SPACE= new String(" ");//$NON-NLS-1$ + private final String MULTISPACE= new String(" +");//$NON-NLS-1$ + private final String UNDERSCORE= new String("_");//$NON-NLS-1$ + private final String MULTIUNDERSCORE= new String("_+");//$NON-NLS-1$ + + public static final String NAME_ACTION="name quick formatting action";//$NON-NLS-1$ + + public static final String DEFAULT_ACTION="default";//$NON-NLS-1$ + public static final String UPPERCASE_ACTION="uppercase";//$NON-NLS-1$ + public static final String LOWERCASE_ACTION="lowercase";//$NON-NLS-1$ + public static final String SWITCHSPACE2UNDERSCORE_ACTION="switchSpace2Underscore";//$NON-NLS-1$ + public static final String CAPITALIZEFIRSTLETTER_ACTION="capitalizeFirstLetter";//$NON-NLS-1$ + public static final String REMOVESPACE_ACTION="removeSpace";//$NON-NLS-1$ + + public NameNormalizationCommand(TransactionalEditingDomain domain, EObject source, String normalization){ + super(domain,NAME_ACTION+": "+normalization); + this.source=source; + this.parameter=normalization; + } + + public String normalizeName(String name, String parameter){ + String newName = new String(name); + switch (parameter) { + case UPPERCASE_ACTION: + newName = getUpperCaseName(name); + break; + case LOWERCASE_ACTION: + newName = getLowerCaseName(name); + break; + case SWITCHSPACE2UNDERSCORE_ACTION: + newName = switchSpace2UnderscoreName(name); + break; + case REMOVESPACE_ACTION: + newName = removeSpaceName(name); + break; + case CAPITALIZEFIRSTLETTER_ACTION: + newName = capitalizeName(name); + break; + + default: + newName = new String(name); + break; + } + return newName; + } + + private String capitalizeName(String name) { + String[] ns = name.split(" "); + StringBuffer finalName=new StringBuffer(); + for(String n : ns){ + finalName.append(n.substring(0, 1).toUpperCase()+n.substring(1,n.length())+" "); + } + return finalName.toString().trim(); + } + + private String removeSpaceName(String name) { + StringBuffer finalName=new StringBuffer(); + int spacePos = name.indexOf(SPACE); + if(spacePos>0){ + finalName.append(name.replace(SPACE, BLANK)); + } + else{ + finalName.append(name.replaceAll("(.)([A-Z])", "$1 $2"));//$NON-NLS-1$ + } + + return finalName.toString().trim(); + } + + private String getUpperCaseName(String name){ + return name.toUpperCase().trim(); + } + + private String getLowerCaseName(String name){ + return name.toLowerCase().trim(); + } + + private String switchSpace2UnderscoreName(String name){ + StringBuffer finalName=new StringBuffer(); + int underscorePos = name.indexOf(UNDERSCORE); + int spacePos = name.indexOf(SPACE); + if(underscorePos>0 && spacePos>0 && underscorePos<spacePos){ + finalName.append(name.replace(UNDERSCORE, BLANK)); + } + else if(underscorePos>0 && spacePos>0 && underscorePos>spacePos){ + finalName.append(name.replace(SPACE, UNDERSCORE)); + } + else if(underscorePos<0 && spacePos>0 ){ + finalName.append(name.replace(SPACE, UNDERSCORE)); + } + else if(underscorePos>0 && spacePos<0){ + finalName.append(name.replace(UNDERSCORE, SPACE)); + } + + return finalName.toString().trim().replaceAll(MULTISPACE, SPACE).replaceAll(MULTIUNDERSCORE, UNDERSCORE); + } +}
\ No newline at end of file diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NamePropertyTester.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NamePropertyTester.java new file mode 100644 index 00000000000..70287ddb3a3 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/menu/NamePropertyTester.java @@ -0,0 +1,41 @@ +/***************************************************************************** + * Copyright (c) 2011 CEA LIST. + * + * 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: + * Francois Le Fevre (CEA LIST) francois.le-fevre@cea.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.menu; + +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.jface.viewers.IStructuredSelection; + +public abstract class NamePropertyTester extends PropertyTester { + /** + * property associated with the parameter linked to the command in the plugin.xml + */ + public static final String PARAMETER_ID = new String("org.eclipse.papyrus.infra.ui.menu.quickformatcommandParameter"); //$NON-NLS-1$ + /** + * property to test if a diagram has the required edit policy + */ + public static final String IS_NAME_CHANGEABLE = "isNameChangeable"; //$NON-NLS-1$ + + + /** + * + * {@inheritDoc} + */ + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if (IS_NAME_CHANGEABLE.equals(property) && receiver instanceof IStructuredSelection) { + boolean answer = isNameChangeable((IStructuredSelection) receiver); + return new Boolean(answer).equals(expectedValue); + } + return false; + } + + protected abstract boolean isNameChangeable(IStructuredSelection selection); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/messages/Messages.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/messages/Messages.java new file mode 100644 index 00000000000..4ece10f8d24 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/messages/Messages.java @@ -0,0 +1,48 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.messages; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.papyrus.infra.tools.messages.messages"; //$NON-NLS-1$ + + public static String AbstractPreferenceKeyDialog_Level; + + public static String AbstractPreferenceKeyDialog_Localization; + + public static String AbstractPreferenceKeyDialog_Pref_Kind; + + public static String AbstractPreferenceKeyDialog_WouldYouLikeOverloadPreferences; + + public static String AbstractStringValueConverter_NoXReprensentedByYHaveBeenFound; + + public static String AbstractStringValueConverter_SomeStringsAreNotValidToCreateY; + + public static String AbstractStringValueConverter_SomeStringsCantBeResolvedToFindY; + + public static String AbstractStringValueConverter_TheFeatureXCantBeResolved; + + public static String AbstractStringValueConverter_TheStringValueXCantBeResolved; + + public static String AbstractStringValueConverter_TheStringXIsNotValidToCreateY; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/messages/messages.properties b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/messages/messages.properties new file mode 100644 index 00000000000..845a45e10ab --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/messages/messages.properties @@ -0,0 +1,10 @@ +AbstractPreferenceKeyDialog_Level=Level +AbstractPreferenceKeyDialog_Localization=Localization +AbstractPreferenceKeyDialog_Pref_Kind=Pref. kind +AbstractPreferenceKeyDialog_WouldYouLikeOverloadPreferences=Would you like to overload those preferences? +AbstractStringValueConverter_NoXReprensentedByYHaveBeenFound=No {0} represented by {1} have been found +AbstractStringValueConverter_SomeStringsAreNotValidToCreateY=Some Strings are not valid to create {0} +AbstractStringValueConverter_SomeStringsCantBeResolvedToFindY=Some Strings can't be resolved to find {0} +AbstractStringValueConverter_TheFeatureXCantBeResolved=The feature {0} can't be resolved +AbstractStringValueConverter_TheStringValueXCantBeResolved=The string value {0} can't be resolved +AbstractStringValueConverter_TheStringXIsNotValidToCreateY=The String {0} is not valid to create {1} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorDescriptor.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorDescriptor.java new file mode 100644 index 00000000000..3af4448ed36 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorDescriptor.java @@ -0,0 +1,72 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.multidiagram.actionbarcontributor; + +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.ui.part.EditorActionBarContributor; + +/** + * Descriptor of an ActionBarContributor. This descriptor is usually loaded from + * the Eclipse extension mechanism. + * + * @author Cedric Dumoulin + * @author Patrick Tessier + * + */ +public class ActionBarContributorDescriptor { + + protected Class<? extends EditorActionBarContributor> contextClass; + + protected String contextId; + + /** + * Instance is created when requested. + */ + protected EditorActionBarContributor instance = null; + + /** + * constructor. + * + * @return the context descriptor + * @throws BackboneException + */ + protected EditorActionBarContributor getActionBarContributor() throws BackboneException { + if (instance == null) { + instance = createActionBarContributor(); + } + + return instance; + } + + private EditorActionBarContributor createActionBarContributor() throws BackboneException { + try { + EditorActionBarContributor context = contextClass.newInstance(); + return context; + + } catch (SecurityException e) { + // Lets propagate. This is an implementation problem that should be + // solved by programmer. + throw new RuntimeException(e); + } catch (InstantiationException e) { + // Lets propagate. This is an implementation problem that should be + // solved by programmer. + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + // Lets propagate. This is an implementation problem that should be + // solved by programmer. + throw new RuntimeException(e); + } + } + +} // end class diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorExtensionFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorExtensionFactory.java new file mode 100644 index 00000000000..da949ab92ae --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorExtensionFactory.java @@ -0,0 +1,72 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.multidiagram.actionbarcontributor; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.papyrus.infra.core.extension.BadNameExtensionException; +import org.eclipse.papyrus.infra.core.extension.ExtensionException; +import org.eclipse.papyrus.infra.core.extension.ExtensionUtils; +import org.eclipse.ui.part.EditorActionBarContributor; + +/** + * A factory used to create ActionBarContributor object from Eclipse extensions + * points elements. + * + * @author Cedric Dumoulin + * @auhtor Patrick Tessier + */ +public class ActionBarContributorExtensionFactory extends ExtensionUtils { + + /** singleton eINSTANCE of this class */ + public final static ActionBarContributorExtensionFactory eINSTANCE = new ActionBarContributorExtensionFactory(); + + /** constant for the editor diagram **/ + public final static String EDITOR_ACTIONBARCONTRIBUTOR_EXTENSIONPOINT = "" + "actionBarContributor"; + + /** constant for the attribute factoryClass **/ + public final static String CONTEXTCLASS_ATTRIBUTE = "implementingClass"; + + /** constant for the attribute contextId **/ + public final static String ID_ATTRIBUTE = "id"; + + /** + * @return the eINSTANCE + */ + public static ActionBarContributorExtensionFactory getInstance() { + return eINSTANCE; + } + + /** + * Create a ContextDescriptor instance corresponding to the + * ConfigurationElement. + * + * @param element + * an {@link IConfigurationElement} see eclipse extension point + * @return a ContextDescriptor structure that contains information to the + * diagram context + * @throws BadNameExtensionException + **/ + public ActionBarContributorDescriptor createActionBarContributorDescriptor(IConfigurationElement element) throws ExtensionException { + ActionBarContributorDescriptor res; + + checkTagName(element, EDITOR_ACTIONBARCONTRIBUTOR_EXTENSIONPOINT); + + res = new ActionBarContributorDescriptor(); + res.contextClass = (Class<EditorActionBarContributor>) parseClass(element, CONTEXTCLASS_ATTRIBUTE, EDITOR_ACTIONBARCONTRIBUTOR_EXTENSIONPOINT); + res.contextId = element.getAttribute(ID_ATTRIBUTE); + + return res; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorRegistry.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorRegistry.java new file mode 100644 index 00000000000..347e1e0d5c7 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/ActionBarContributorRegistry.java @@ -0,0 +1,176 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.multidiagram.actionbarcontributor; + +import static org.eclipse.papyrus.infra.core.Activator.log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.Platform; +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.papyrus.infra.core.extension.ExtensionException; +import org.eclipse.papyrus.infra.core.extension.NotFoundException; +import org.eclipse.papyrus.infra.core.services.IService; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.ui.part.EditorActionBarContributor; + +/** + * A factory managing ActionBarContributor creation. The factory is loaded from + * ActionBarContributor declared in Eclipse extension mechanism. + * + * @author dumoulin + * + */ +public class ActionBarContributorRegistry implements IActionBarContributorFactory, IService { + + /** ID of the editor extension (schema filename) */ + public static final String EDITOR_EXTENSION_ID = "papyrusDiagram"; + + /** Namespace where to look for the extension points. */ + protected String extensionPointNamespace; + + /** + * Registered context descriptors. + */ + private Map<Object, ActionBarContributorDescriptor> editorContextDescriptors; + + /** + * Constructor. defaultContext, input and site are explicitly required in + * order be sure that they are initialized. The multiEditor should be + * initialized. In particular, getEditorSite(), getEditorInput() and + * getDefaultContext() should return initialized values. + * + * @param multiEditor + * the multieditor + * @param extensionPointNamespace + */ + public ActionBarContributorRegistry(String extensionPointNamespace) { + + this.extensionPointNamespace = extensionPointNamespace; + initializeEditorContextDescriptors(); + } + + /** + * + * {@inheritDoc} + */ + @Override + public EditorActionBarContributor getActionBarContributor(Object key) throws BackboneException { + try { + ActionBarContributorDescriptor desc = editorContextDescriptors.get(key); + return desc.getActionBarContributor(); + } catch (NullPointerException e) { + // no context found. + throw new NotFoundException("No ActionBarContributor registered under id '" + key + "'."); + } + } + + /** + * Get the list of descriptors. + * + * @return + * @throws BackboneException + * If a contributor fail to be loaded. + */ + public List<EditorActionBarContributor> getActionBarContributors() throws BackboneException { + List<EditorActionBarContributor> res = new ArrayList<EditorActionBarContributor>(); + for (ActionBarContributorDescriptor desc : editorContextDescriptors.values()) { + res.add(desc.getActionBarContributor()); + } + return res; + } + + /** + * + * {@inheritDoc} + */ + public void registerActionBarContributor(String contextKey, EditorActionBarContributor contributor) { + ActionBarContributorDescriptor desc = new ActionBarContributorDescriptor(); + desc.contextId = contextKey; + desc.instance = contributor; + desc.contextClass = contributor.getClass(); + + editorContextDescriptors.put(contextKey, desc); + } + + /** + * Read context descriptors from extension points. + */ + private void initializeEditorContextDescriptors() { + + editorContextDescriptors = new HashMap<Object, ActionBarContributorDescriptor>(); + // Reading data from plugins + IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor(extensionPointNamespace, EDITOR_EXTENSION_ID); + + ActionBarContributorExtensionFactory extensionReader = new ActionBarContributorExtensionFactory(); + + for (IConfigurationElement ele : configElements) { + ActionBarContributorDescriptor desc; + try { + if (ActionBarContributorExtensionFactory.EDITOR_ACTIONBARCONTRIBUTOR_EXTENSIONPOINT.equals(ele.getName())) { + desc = extensionReader.createActionBarContributorDescriptor(ele); + // Check double + if (editorContextDescriptors.get(desc.contextId) != null) { + // Already exists. Check if it is the same + ActionBarContributorDescriptor existingDesc = editorContextDescriptors.get(desc.contextId); + if (desc.equals(existingDesc)) { + log.warn("More than one ActionBarContributor is registered under the name '" + desc.contextId + "', with different parameters. Extra declaration are discarded."); + } + } else { + editorContextDescriptors.put(desc.contextId, desc); + } + } + } catch (ExtensionException e) { + log.error(e.getMessage(), e); + } + } + + if (log.isDebugEnabled()) { + log.debug(this.getClass().getSimpleName() + " : contributors desc loaded [" + editorContextDescriptors.size() + "]"); + } + } + + /** + * Initialize the service. Do nothing here. + * + * @see org.eclipse.papyrus.infra.core.services.IService#init(org.eclipse.papyrus.infra.core.services.ServicesRegistry) + * + * @param servicesRegistry + */ + @Override + public void init(ServicesRegistry servicesRegistry) { + + } + + /** + * Do nothing in this implementation. {@inheritDoc} + * + * @see org.eclipse.papyrus.infra.core.services.IService#startService() + */ + @Override + public void startService() { + } + + /** + * Do nothing in this implementation. + */ + @Override + public void disposeService() { + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/CoreComposedActionBarContributor.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/CoreComposedActionBarContributor.java new file mode 100644 index 00000000000..2e8459fe5cf --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/CoreComposedActionBarContributor.java @@ -0,0 +1,122 @@ +/***************************************************************************** + * Copyright (c) 2008 CEA LIST. + * + * + * 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.multidiagram.actionbarcontributor; + +import java.util.List; + +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.papyrus.infra.core.sasheditor.editor.actionbarcontributor.ComposedActionBarContributor; +import org.eclipse.papyrus.infra.core.sasheditor.editor.actionbarcontributor.IMultiPageEditorActionBarContributor; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.part.EditorActionBarContributor; + +/** + * + * An ActionBarContributor composed of ActionBarContributor from multi editor. + * This ActionBarContributor switch to the contributor dedicated to the active + * editor in a MultiPageEditor environement. + * + * @author dumoulin + * + */ +public class CoreComposedActionBarContributor extends ComposedActionBarContributor implements IMultiPageEditorActionBarContributor { + + /** + * The registry. Used to initialize the registered actionBars. + */ + protected ActionBarContributorRegistry actionBarContributorRegistry; + + protected List<EditorActionBarContributor> contributors; + + /** + * Constructor. + * + * @throws BackboneException + */ + public CoreComposedActionBarContributor() throws BackboneException { + // Init the contributors + loadContributors(); + } + + /** + * + * @throws BackboneException + */ + private void loadContributors() throws BackboneException { + actionBarContributorRegistry = new ActionBarContributorRegistry(Activator.PLUGIN_ID); + + contributors = actionBarContributorRegistry.getActionBarContributors(); + } + + /** + * @return the actionBarContributorRegistry + */ + public ActionBarContributorRegistry getActionBarContributorRegistry() { + return actionBarContributorRegistry; + } + + /** + * Dispose all nested ActionBarContributors. + */ + @Override + public void dispose() { + // Dispose nested contributors. + for (EditorActionBarContributor contributor : contributors) { + contributor.dispose(); + } + super.dispose(); + } + + /** + * Call the same method on each registered nested ActionBarContributors. + */ + @Override + public void init(IActionBars bars, IWorkbenchPage page) { + super.init(bars, page); + buildActions(); + + // init nested contributors. + for (EditorActionBarContributor contributor : contributors) { + contributor.init(bars, page); + // remove GMF GlobalSaveAction from bar, fix bug 407854 - [Editor] The save action is disabled in Papyrus + bars.setGlobalActionHandler("save", null); // GMF is not using IWorkbenchCommandConstants.FILE_SAVE as ID //$NON-NLS-1$ + } + + } + + /** + * Load default actions (undo/redo/delete) + * + * @see org.eclipse.gef.ui.actions.ActionBarContributor#buildActions() + */ + protected void buildActions() { + // getActionBars().getToolBarManager().add(new UndoRetargetAction()); + // getActionBars().getToolBarManager().add(new RedoRetargetAction()); + } + + @Override + public void setActiveEditor(IEditorPart part) { + super.setActiveEditor(part); + for (EditorActionBarContributor contributor : contributors) { + if (part != null) { + contributor.setActiveEditor(part); + } + } + + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/IActionBarContributorFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/IActionBarContributorFactory.java new file mode 100644 index 00000000000..e296d88deab --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/multidiagram/actionbarcontributor/IActionBarContributorFactory.java @@ -0,0 +1,25 @@ +/** + * + */ +package org.eclipse.papyrus.infra.ui.multidiagram.actionbarcontributor; + +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.ui.part.EditorActionBarContributor; + +/** + * Interface used to get an ActionBarContributor from its ID. + * + * @author dumoulin + * + */ +public interface IActionBarContributorFactory { + + /** + * Get an ActionBarContributor by its key. If an ActionBarContributor + * already exists for this key, return it. + * + * @param key + * @return + */ + public EditorActionBarContributor getActionBarContributor(Object key) throws BackboneException; +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPapyrusPreferencePage.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPapyrusPreferencePage.java new file mode 100644 index 00000000000..13426ddc474 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPapyrusPreferencePage.java @@ -0,0 +1,237 @@ +/**************************************************************************** + * Copyright (c) 2008, 2016 Atos Origin, Christian W. Damus, 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: + * Thibault Landre (Atos Origin) - Initial API and implementation + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.preferences; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ProjectScope; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.IWorkbenchPropertyPage; +import org.eclipse.ui.preferences.ScopedPreferenceStore; + +/** + * An abstract implementation of a Preference page. + * + * This preference page allows clients to define preference page in the preference of Eclipse, and + * in the properties of a project in the workspace. + * <p> + * Clients must implement : + * <ul> + * <li>{@link #getBundleId()} method in order to define the preference scope (Project or Instance) of the preference page.</li> + * <li>{@link #createPageContents(Composite)} method to populate the preference page with the different {@link AbstractPreferenceGroup}s. </br> + * Each group added has to be declared through the {@link #addPreferenceGroup(AbstractPreferenceGroup)}</code> method</li> + * </ul> + * </p> + */ +public abstract class AbstractPapyrusPreferencePage extends PreferencePage implements IWorkbenchPreferencePage, IWorkbenchPropertyPage, IPapyrusPreferencePage { + + private IProject project; + + private Set<AbstractPreferenceGroup> groupSet; + + private String key; + + /** + * @see org.eclipse.ui.IWorkbenchPropertyPage#getElement() + */ + @Override + public IAdaptable getElement() { + return project; + } + + protected void setPreferenceKey(String aKey) { + this.key = aKey; + } + + protected String getPreferenceKey() { + return this.key; + } + + /** + * @see org.eclipse.ui.IWorkbenchPropertyPage#setElement(org.eclipse.core.runtime.IAdaptable) + */ + @Override + public void setElement(IAdaptable element) { + project = (IProject) element.getAdapter(IResource.class); + } + + /** + * @see org.eclipse.jface.preference.PreferencePage#doGetPreferenceStore() + */ + @Override + protected IPreferenceStore doGetPreferenceStore() { + IPreferenceStore store; + if (project != null) { + store = new ScopedPreferenceStore(new ProjectScope(project), getBundleId()); + } else { + store = new ScopedPreferenceStore(InstanceScope.INSTANCE, getBundleId()); + } + return store; + } + + /** + * Initializes this preference page for the given workbench. + * + * @param workbench + * the workbench + * + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + * + */ + @Override + public void init(IWorkbench workbench) { + // Do nothing + } + + /** + * Create the Papyrus preference page and inits the different fields editor contained in the + * page. + * <p> + * This method shouldn't be overriden by sub-classes + * </p> + * {@inheritDoc} + */ + @Override + protected Control createContents(Composite parent) { + // Create the container composite + Composite container = new Composite(parent, SWT.NONE); + GridLayout containerLayout = new GridLayout(); + container.setLayout(containerLayout); + + createPageContents(container); + + initGroup(); + + return container; + } + + /** + * Populate the preference page with the different field editor. + * <p> + * Each field added has to be declared through the <code>addEditorFields(FieldEditor fe)</code> method + * </p> + * + * @param parent + * the parent composite + */ + protected abstract void createPageContents(Composite parent); + + /** + * Add the given field editor to the page. + */ + protected void addPreferenceGroup(AbstractPreferenceGroup fe) { + if (groupSet == null) { + groupSet = new HashSet<>(); + } + groupSet.add(fe); + } + + @Override + public boolean performOk() { + VisiblePageSingleton.getInstance().store(); + return super.performOk(); + } + + /** + * Stores the values of the fields contained in this page into the preference store. + */ + protected void storePreferences() { + if (groupSet != null) { + for (AbstractPreferenceGroup gs : groupSet) { + gs.storePreferences(); + } + } + } + + /** + * Store all preferences + */ + @Override + public void storeAllPreferences() { + storePreferences(); + + } + + @Override + protected void performDefaults() { + loadDefaultPreferences(); + super.performDefaults(); + } + + /** + * Load the default preferences of the fields contained in this page + */ + private void loadDefaultPreferences() { + if (groupSet != null) { + for (AbstractPreferenceGroup gs : groupSet) { + gs.loadDefault(); + } + } + + } + + /** + * Init groups contained in this page. + */ + private void initGroup() { + if (groupSet != null) { + for (AbstractPreferenceGroup gs : groupSet) { + gs.setPreferenceStore(getPreferenceStore()); + gs.load(); + } + } + } + + @Override + public void dispose() { + super.dispose(); + if (groupSet != null) { + for (AbstractPreferenceGroup gs : groupSet) { + gs.dispose(); + } + } + + + } + + @Override + public void setVisible(boolean visible) { + if (visible == true) { + VisiblePageSingleton.getInstance().setVisiblePage(this); + initGroup(); + } + super.setVisible(visible); + + } + + /** + * The bundle ID used to defined the preference store + * + * @return String + */ + protected abstract String getBundleId(); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPapyrusPreferenceStore.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPapyrusPreferenceStore.java new file mode 100644 index 00000000000..922fd74b9c3 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPapyrusPreferenceStore.java @@ -0,0 +1,300 @@ +/***************************************************************************** + * Copyright (c) 2014 CEA LIST. + * + * 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: + * + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.preferences; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.preferences.dialog.AbstractApplyValueOnPreferenceKeyDialog; + +public abstract class AbstractPapyrusPreferenceStore extends PapyrusScopedPreferenceStore { + + /** + * key for element level + */ + private final String elementLevelPrefix; + + /** + * key for editor level + */ + private final String instanceEditorLevelPrefix; + + /** + * key for all editor of the same kind + */ + private final String editorLevelPrefix; + + /** + * constructor + * + * @param context + * the scope to store to + * @param qualifier + * the qualifier used to look up the preference node + * @param key + * for all editor of the same kind (all diagrams, all tables, ...) + * @param key + * for an instance of this editor + * @param key + * for an element + */ + public AbstractPapyrusPreferenceStore(IScopeContext context, String qualifier, String editorLevelPrefix, String instanceEditorLevelPrefix, String elementLevelPrefix) { + super(context, qualifier); + this.editorLevelPrefix = editorLevelPrefix; + this.instanceEditorLevelPrefix = instanceEditorLevelPrefix; + this.elementLevelPrefix = elementLevelPrefix; + } + + /** + * constructor + * + * @param context + * the scope to store to + * @param qualifier + * the qualifier used to look up the preference node + * @param defaultQualifierPath + * the qualifier used when looking up the defaults + * @param key + * for all editor of the same kind (all diagrams, all tables, ...) + * @param key + * for an instance of this editor + * @param key + * for an element + */ + public AbstractPapyrusPreferenceStore(IScopeContext context, String qualifier, String defaultQualifierPath, String editorLevelPrefix, String instanceEditorLevelPrefix, String elementLevelPrefix) { + super(context, qualifier, defaultQualifierPath); + this.editorLevelPrefix = editorLevelPrefix; + this.instanceEditorLevelPrefix = instanceEditorLevelPrefix; + this.elementLevelPrefix = elementLevelPrefix; + } + + + + + /** + * this method is used to overload all value under a level of preferences. + * In order to overload a pop-up is opened, and the user can choose value to overload + * + * @param level + * of preference: Editor or diagram + */ + + public void deleteAllSubPreference(String level) { + // remove all sub value diagram+ element + + // key to collect + List<String> elementKey = new ArrayList<String>(); + try { + for (int i = 0; i < getStorePreferences().keys().length; i++) { + // level diagram collect only element + if (level.startsWith(instanceEditorLevelPrefix)) { + if (getStorePreferences().keys()[i].startsWith(elementLevelPrefix)) { + elementKey.add(getStorePreferences().keys()[i]); + } + } + // editor level, collect all element+diagram + else if (level.startsWith(editorLevelPrefix)) { + if ((getStorePreferences().keys()[i].startsWith(elementLevelPrefix)) || (getStorePreferences().keys()[i].startsWith(instanceEditorLevelPrefix))) { + elementKey.add(getStorePreferences().keys()[i]); + } + } + + } + + } catch (Exception e) { + Activator.log.error(e); + } + if (elementKey.size() > 0) { + List<String> keytoRemove = new ArrayList<String>(); + String[] keyRoconsult = new String[elementKey.size()]; + AbstractApplyValueOnPreferenceKeyDialog dialog = createPreferenceKeyDialog(elementKey.toArray(keyRoconsult)); + dialog.open(); + keytoRemove = dialog.getKeyToRemove(); + + // remove key + Iterator<String> iterator = keytoRemove.iterator(); + while (iterator.hasNext()) { + String key = iterator.next(); + getStorePreferences().remove(key); + } + } + } + + /** + * + * @param keys + * @return + * the dialog to apply values + */ + protected abstract AbstractApplyValueOnPreferenceKeyDialog createPreferenceKeyDialog(String[] keys); + + + + // each get value will be overloaded + // if not value is found for an element, a value is look for in DIAGRAM + // if a the value is not find for Diagram a value is find for Papyrus editor + + + /** + * this method is used to find a key that a got a value: + * if the key is an element. The method look for if this key exist. If no value exists, it look for the key for diagram + * if the key for diagram do not exist it look for key for papyrus Editor + * the structure of Key is: + * element : ELEMENT_DiagramKind_ElementKind.preferenceKind + * Diagram : DIAGRAM_DiagramKind.preferenceKind + * Editor: PAPYRUS_EDITOR.preferenceKind + * + */ + protected String findKeyWithAValue(String initialKey) { + String foundedKey = null; + // first look for in value stack + foundedKey = findKeyAStoreValue(initialKey); + // then look for in default stack + if (foundedKey == null) { + foundedKey = findKeyWithADefaultValue(initialKey); + } + if (foundedKey == null) { + foundedKey = initialKey; + } + return foundedKey; + + } + + /** + * look for a key with a value in the store stack + * + * @param initialKey + * element : ELEMENT_DiagramKind_ElementKind.preferenceKind + * Diagram : DIAGRAM_DiagramKind.preferenceKind + * Editor: PAPYRUS_EDITOR.preferenceKind + * @return the key that returns a value or null if there is no value + */ + protected String findKeyAStoreValue(String initialKey) { + String foundedKey = null; + if (getStorePreferences().get(initialKey, null) != null) { + foundedKey = initialKey; + } + + if (foundedKey == null && hasPrefix(initialKey)) { + foundedKey = findKeyAStoreValue(getUpperKey(initialKey)); + } + return foundedKey; + } + + /** + * this method is used to find a key that a got a value: + * if the key is an element. The method look for if this key exist. If no value exists, it look for the key for diagram + * if the key for diagram do not exist it look for key for papyrus Editor + * the structure of Key is: + * element : ELEMENT_DiagramKind_ElementKind.preferenceKind + * Diagram : DIAGRAM_DiagramKind.preferenceKind + * Editor: PAPYRUS_EDITOR.preferenceKind + * + */ + protected String findKeyWithADefaultValue(String initialKey) { + String foundedKey = null; + + if (getDefaultPreferences().get(initialKey, null) != null) { + foundedKey = initialKey; + } + + if (foundedKey == null && hasPrefix(initialKey)) { + return findKeyWithADefaultValue(getUpperKey(initialKey)); + } else { + foundedKey = initialKey; + } + return foundedKey; + + } + + /** + * get the upper Key from the initial Key + * * the structure of Key is: + * element : ELEMENT_DiagramKind_ElementKind.preferenceKind + * Diagram : DIAGRAM_DiagramKind.preferenceKind + * Editor: PAPYRUS_EDITOR.preferenceKind + * + * @param initialKey + * @return the upperKey + * + */ + protected String getUpperKey(String initialKey) { + + String out = initialKey.toString(); + if (initialKey.startsWith(elementLevelPrefix)) { + out = initialKey.toString().replaceAll(elementLevelPrefix, instanceEditorLevelPrefix); + out = out.substring(0, out.lastIndexOf("_")) + out.substring(out.indexOf("."), out.length()); + } + if (initialKey.startsWith(instanceEditorLevelPrefix)) { + // out=initialKey.toString().replaceAll(instanceEditorLevelPrefix, editorLevelPrefix); + out = editorLevelPrefix + out.substring(out.indexOf("."), out.length()); + } + return out; + } + + protected boolean hasPrefix(String key) { + if (key.startsWith(elementLevelPrefix) || key.startsWith(instanceEditorLevelPrefix)) { + return true; + } + return false; + } + + /** + * get the value from a key + * + * @param key + * @return the value + */ + @Override + protected String internalGet(String key) { + String newKey = findKeyWithAValue(key); + // System.err.println("-->Initial Key "+key+"--> "+ newKey); + return Platform.getPreferencesService().get(newKey, null, getPreferenceNodes(true)); + } + + @Override + public boolean getDefaultBoolean(String name) { + + return super.getDefaultBoolean(findKeyWithADefaultValue(name)); + } + + @Override + public double getDefaultDouble(String name) { + return super.getDefaultDouble(findKeyWithADefaultValue(name)); + } + + @Override + public float getDefaultFloat(String name) { + return super.getDefaultFloat(findKeyWithADefaultValue(name)); + }; + + @Override + public int getDefaultInt(String name) { + return super.getDefaultInt(findKeyWithADefaultValue(name)); + } + + @Override + public long getDefaultLong(String name) { + return super.getDefaultLong(findKeyWithADefaultValue(name)); + } + + @Override + public String getDefaultString(String name) { + return super.getDefaultString(findKeyWithADefaultValue(name)); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPreferenceGroup.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPreferenceGroup.java new file mode 100644 index 00000000000..b90d098e54b --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/AbstractPreferenceGroup.java @@ -0,0 +1,161 @@ +/***************************************************************************** + * Copyright (c) 2009, 2016 CEA LIST, Christian W. Damus, 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: + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr + * Thibault Landre (Atos Origin) + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.preferences; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jface.dialogs.DialogPage; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; + +/** + * The Class AbstractPreferenceGroup. + */ +public abstract class AbstractPreferenceGroup extends Composite { + + /** The key to find preference */ + private String key; + + /** + * The fieldsEditor : a set that will contain all editor in the composite. It is in charge of + * loading / storing / setting the preference store / loading default of all its contained field + * editor + */ + private Set<FieldEditor> fieldsEditor; + + /** + * Gets the dialog page. + * + * @return the dialogPage + */ + protected DialogPage getDialogPage() { + return dialogPage; + } + + /** The dialog page. */ + protected DialogPage dialogPage; + + /** + * Gets the title. + * + * @return the title + */ + public String getKey() { + return key; + } + + /** + * Sets the title. + * + * @param title + * the title to set + */ + protected void setKey(String title) { + this.key = title; + } + + /** + * Instantiates a new abstract group. + * + * @param parent + * the parent of the composite + * @param String + * the title of the page + * @param dialogPage + * to set the page in field editor + */ + public AbstractPreferenceGroup(Composite parent, String key, DialogPage dialogPage) { + super(parent, SWT.None); + this.key = key; + this.dialogPage = dialogPage; + this.setLayout(new GridLayout()); + fieldsEditor = new HashSet<FieldEditor>(); + } + + /** + * Gets an encapsulated composite. This composite is used to contain a FieldEditor and to allow + * developers to work with a FieldEditor like Composite element. + * + * @param parent + * the parent + * + * @return the encapsulated compo + */ + protected final Composite getEncapsulatedComposite(Composite parent) { + Composite compo = new Composite(parent, SWT.NONE); + compo.setLayout(new GridLayout()); + return compo; + } + + /** + * Register field editor. It will add the fieldEditor to a map that will be used to + * store/load/loadDefault/set the PreferenceStore of contained fieldEditor + * + * @param fieldEditor + * the fieldEditor to add. + */ + protected void addFieldEditor(FieldEditor fieldEditor) { + fieldsEditor.add(fieldEditor); + } + + /** + * Load preferences of all registered fieldEditors. + * + * @see org.eclipse.papyrus.infra.AbstractPreferenceGroup.preferences.ui.AbstractGroup#addFieldEditor(FieldEditor) + */ + public void load() { + for (FieldEditor fe : fieldsEditor) { + fe.load(); + } + } + + /** + * Set the preference store of all registered fieldEditors. + * + * @see org.eclipse.papyrus.infra.AbstractPreferenceGroup.preferences.ui.AbstractGroup#addFieldEditor(FieldEditor) + */ + public final void setPreferenceStore(IPreferenceStore store) { + for (FieldEditor fe : fieldsEditor) { + fe.setPreferenceStore(store); + } + } + + /** + * Load default preferences of all registered fieldEditors. + * + * @see org.eclipse.papyrus.infra.AbstractPreferenceGroup.preferences.ui.AbstractGroup#addFieldEditor(FieldEditor) + */ + public final void loadDefault() { + for (FieldEditor fe : fieldsEditor) { + fe.loadDefault(); + } + } + + /** + * Store preferences of the registered fieldEditors. + * + * @see org.eclipse.papyrus.infra.AbstractPreferenceGroup.preferences.ui.AbstractGroup#addFieldEditor(FieldEditor) + */ + public final void storePreferences() { + for (FieldEditor fe : fieldsEditor) { + fe.store(); + } + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/IPapyrusPreferencePage.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/IPapyrusPreferencePage.java new file mode 100644 index 00000000000..4a82d671fef --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/IPapyrusPreferencePage.java @@ -0,0 +1,28 @@ +/***************************************************************************** + * Copyright (c) 2010, 2016 CEA LIST, Christian W. Damus, 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: + * CEA LIST - Initial API and implementation + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.preferences; + +import org.eclipse.jface.preference.IPreferencePage; + +/** + * Specialized protocol for preference pages participating in the {@link VisiblePageSingleton} + * mechanism. + */ +public interface IPapyrusPreferencePage extends IPreferencePage { + /** + * Requests the page to store all of its preferences in the preference store. + */ + void storeAllPreferences(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/PapyrusScopedPreferenceStore.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/PapyrusScopedPreferenceStore.java new file mode 100644 index 00000000000..f3ec34c88c1 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/PapyrusScopedPreferenceStore.java @@ -0,0 +1,858 @@ +/***************************************************************************** + * Copyright (c) 2010 CEA LIST. + * + * + * 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: + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.preferences; + +/*************************************************************************** + Copyright (c) 2010 CEA LIST. + * + * + * 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: + * the code is copy from ScopedPreferenceStore but I have open some methods in order to be + * available for the overload + * + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation + * + * + *******************************************************************************/ + +import java.io.IOException; + +import org.eclipse.core.commands.common.EventManager; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.NodeChangeEvent; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.jface.preference.IPersistentPreferenceStore; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.util.SafeRunnable; +import org.eclipse.ui.internal.WorkbenchMessages; +import org.osgi.service.prefs.BackingStoreException; + +/** + * The ScopedPreferenceStore is an IPreferenceStore that uses the scopes + * provided in org.eclipse.core.runtime.preferences. + * <p> + * A ScopedPreferenceStore does the lookup of a preference based on it's search scopes and sets the value of the preference based on its store scope. + * </p> + * <p> + * The default scope is always included in the search scopes when searching for preference values. + * </p> + * + * @see org.eclipse.core.runtime.preferences + * @since 3.1 + */ +public class PapyrusScopedPreferenceStore extends EventManager implements IPreferenceStore, IPersistentPreferenceStore { + + /** + * The storeContext is the context where values will stored with the + * setValue methods. If there are no searchContexts this will be the search + * context. (along with the "default" context) + */ + protected IScopeContext storeContext; + + /** + * The searchContext is the array of contexts that will be used by the get + * methods for searching for values. + */ + protected IScopeContext[] searchContexts; + + /** + * A boolean to indicate the property changes should not be propagated. + */ + protected boolean silentRunning = false; + + /** + * The listener on the IEclipsePreferences. This is used to forward updates + * to the property change listeners on the preference store. + */ + protected IEclipsePreferences.IPreferenceChangeListener preferencesListener; + + /** + * The default context is the context where getDefault and setDefault + * methods will search. This context is also used in the search. + */ + protected IScopeContext defaultContext = DefaultScope.INSTANCE; + + /** + * The nodeQualifer is the string used to look up the node in the contexts. + */ + protected String nodeQualifier; + + /** + * The defaultQualifier is the string used to look up the default node. + */ + protected String defaultQualifier; + + /** + * Boolean value indicating whether or not this store has changes to be + * saved. + */ + private boolean dirty; + + /** + * Create a new instance of the receiver. Store the values in context in the + * node looked up by qualifier. <strong>NOTE:</strong> Any instance of + * ScopedPreferenceStore should call + * + * @param context + * the scope to store to + * @param qualifier + * the qualifier used to look up the preference node + * @param defaultQualifierPath + * the qualifier used when looking up the defaults + */ + public PapyrusScopedPreferenceStore(IScopeContext context, String qualifier, String defaultQualifierPath) { + this(context, qualifier); + this.defaultQualifier = defaultQualifierPath; + } + + /** + * Create a new instance of the receiver. Store the values in context in the + * node looked up by qualifier. + * + * @param context + * the scope to store to + * @param qualifier + * the qualifer used to look up the preference node + */ + public PapyrusScopedPreferenceStore(IScopeContext context, String qualifier) { + storeContext = context; + this.nodeQualifier = qualifier; + this.defaultQualifier = qualifier; + + ((IEclipsePreferences) getStorePreferences().parent()).addNodeChangeListener(getNodeChangeListener()); + } + + /** + * Return a node change listener that adds a removes the receiver when nodes + * change. + * + * @return INodeChangeListener + */ + private INodeChangeListener getNodeChangeListener() { + return new IEclipsePreferences.INodeChangeListener() { + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener#added(org.eclipse.core.runtime.preferences.IEclipsePreferences + * .NodeChangeEvent) + */ + public void added(NodeChangeEvent event) { + if (nodeQualifier.equals(event.getChild().name()) && isListenerAttached()) { + getStorePreferences().addPreferenceChangeListener(preferencesListener); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener#removed(org.eclipse.core.runtime.preferences. + * IEclipsePreferences.NodeChangeEvent) + */ + public void removed(NodeChangeEvent event) { + // Do nothing as there are no events from removed node + } + }; + } + + /** + * Initialize the preferences listener. + */ + private void initializePreferencesListener() { + if (preferencesListener == null) { + preferencesListener = new IEclipsePreferences.IPreferenceChangeListener() { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime. + * preferences.IEclipsePreferences.PreferenceChangeEvent) + */ + public void preferenceChange(PreferenceChangeEvent event) { + + if (silentRunning) { + return; + } + + Object oldValue = event.getOldValue(); + Object newValue = event.getNewValue(); + String key = event.getKey(); + if (newValue == null) { + newValue = getDefault(key, oldValue); + } else if (oldValue == null) { + oldValue = getDefault(key, newValue); + } + firePropertyChangeEvent(event.getKey(), oldValue, newValue); + } + }; + getStorePreferences().addPreferenceChangeListener(preferencesListener); + } + + } + + /** + * Does its best at determining the default value for the given key. Checks + * the given object's type and then looks in the list of defaults to see if + * a value exists. If not or if there is a problem converting the value, the + * default default value for that type is returned. + * + * @param key + * the key to search + * @param obj + * the object who default we are looking for + * @return Object or <code>null</code> + */ + protected Object getDefault(String key, Object obj) { + IEclipsePreferences defaults = getDefaultPreferences(); + if (obj instanceof String) { + return defaults.get(key, STRING_DEFAULT_DEFAULT); + } else if (obj instanceof Integer) { + return Integer.valueOf(defaults.getInt(key, INT_DEFAULT_DEFAULT)); + } else if (obj instanceof Double) { + return new Double(defaults.getDouble(key, DOUBLE_DEFAULT_DEFAULT)); + } else if (obj instanceof Float) { + return new Float(defaults.getFloat(key, FLOAT_DEFAULT_DEFAULT)); + } else if (obj instanceof Long) { + return Long.valueOf(defaults.getLong(key, LONG_DEFAULT_DEFAULT)); + } else if (obj instanceof Boolean) { + return defaults.getBoolean(key, BOOLEAN_DEFAULT_DEFAULT) ? Boolean.TRUE : Boolean.FALSE; + } else { + return null; + } + } + + /** + * Return the IEclipsePreferences node associated with this store. + * + * @return the preference node for this store + */ + protected IEclipsePreferences getStorePreferences() { + return storeContext.getNode(nodeQualifier); + } + + /** + * Return the default IEclipsePreferences for this store. + * + * @return this store's default preference node + */ + protected IEclipsePreferences getDefaultPreferences() { + return defaultContext.getNode(defaultQualifier); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#addPropertyChangeListener(org.eclipse.jface.util.IPropertyChangeListener) + */ + public void addPropertyChangeListener(IPropertyChangeListener listener) { + initializePreferencesListener();// Create the preferences listener if it + // does not exist + addListenerObject(listener); + } + + /** + * Return the preference path to search preferences on. This is the list of + * preference nodes based on the scope contexts for this store. If there are + * no search contexts set, then return this store's context. + * <p> + * Whether or not the default context should be included in the resulting list is specified by the <code>includeDefault</code> parameter. + * </p> + * + * @param includeDefault + * <code>true</code> if the default context should be included + * and <code>false</code> otherwise + * @return IEclipsePreferences[] + * @since 3.4 public, was added in 3.1 as private method + */ + public IEclipsePreferences[] getPreferenceNodes(boolean includeDefault) { + // if the user didn't specify a search order, then return the scope that + // this store was created on. (and optionally the default) + if (searchContexts == null) { + if (includeDefault) { + return new IEclipsePreferences[] { getStorePreferences(), getDefaultPreferences() }; + } + return new IEclipsePreferences[] { getStorePreferences() }; + } + // otherwise the user specified a search order so return the appropriate + // nodes based on it + int length = searchContexts.length; + if (includeDefault) { + length++; + } + IEclipsePreferences[] preferences = new IEclipsePreferences[length]; + for (int i = 0; i < searchContexts.length; i++) { + preferences[i] = searchContexts[i].getNode(nodeQualifier); + } + if (includeDefault) { + preferences[length - 1] = getDefaultPreferences(); + } + return preferences; + } + + /** + * Set the search contexts to scopes. When searching for a value the seach + * will be done in the order of scope contexts and will not search the + * storeContext unless it is in this list. + * <p> + * If the given list is <code>null</code>, then clear this store's search contexts. This means that only this store's scope context and default scope will be used during preference value searching. + * </p> + * <p> + * The defaultContext will be added to the end of this list automatically and <em>MUST NOT</em> be included by the user. + * </p> + * + * @param scopes + * a list of scope contexts to use when searching, or <code>null</code> + */ + public void setSearchContexts(IScopeContext[] scopes) { + this.searchContexts = scopes; + if (scopes == null) { + return; + } + + // Assert that the default was not included (we automatically add it to + // the end) + for (int i = 0; i < scopes.length; i++) { + if (scopes[i].equals(defaultContext)) { + Assert.isTrue(false, WorkbenchMessages.ScopedPreferenceStore_DefaultAddedError); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#contains(java.lang.String) + */ + public boolean contains(String name) { + if (name == null) { + return false; + } + return (Platform.getPreferencesService().get(name, null, getPreferenceNodes(true))) != null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#firePropertyChangeEvent(java.lang.String, + * java.lang.Object, java.lang.Object) + */ + public void firePropertyChangeEvent(String name, Object oldValue, Object newValue) { + // important: create intermediate array to protect against listeners + // being added/removed during the notification + final Object[] list = getListeners(); + if (list.length == 0) { + return; + } + final PropertyChangeEvent event = new PropertyChangeEvent(this, name, oldValue, newValue); + for (int i = 0; i < list.length; i++) { + final IPropertyChangeListener listener = (IPropertyChangeListener) list[i]; + SafeRunner.run(new SafeRunnable(JFaceResources.getString("PreferenceStore.changeError")) { //$NON-NLS-1$ + + public void run() { + listener.propertyChange(event); + } + }); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getBoolean(java.lang.String) + */ + public boolean getBoolean(String name) { + String value = internalGet(name); + return value == null ? BOOLEAN_DEFAULT_DEFAULT : Boolean.valueOf(value).booleanValue(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getDefaultBoolean(java.lang.String) + */ + public boolean getDefaultBoolean(String name) { + return getDefaultPreferences().getBoolean(name, BOOLEAN_DEFAULT_DEFAULT); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getDefaultDouble(java.lang.String) + */ + public double getDefaultDouble(String name) { + return getDefaultPreferences().getDouble(name, DOUBLE_DEFAULT_DEFAULT); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getDefaultFloat(java.lang.String) + */ + public float getDefaultFloat(String name) { + return getDefaultPreferences().getFloat(name, FLOAT_DEFAULT_DEFAULT); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getDefaultInt(java.lang.String) + */ + public int getDefaultInt(String name) { + return getDefaultPreferences().getInt(name, INT_DEFAULT_DEFAULT); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getDefaultLong(java.lang.String) + */ + public long getDefaultLong(String name) { + return getDefaultPreferences().getLong(name, LONG_DEFAULT_DEFAULT); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getDefaultString(java.lang.String) + */ + public String getDefaultString(String name) { + return getDefaultPreferences().get(name, STRING_DEFAULT_DEFAULT); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getDouble(java.lang.String) + */ + public double getDouble(String name) { + String value = internalGet(name); + if (value == null) { + return DOUBLE_DEFAULT_DEFAULT; + } + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return DOUBLE_DEFAULT_DEFAULT; + } + } + + /** + * Return the string value for the specified key. Look in the nodes which + * are specified by this object's list of search scopes. If the value does + * not exist then return <code>null</code>. + * + * @param key + * the key to search with + * @return String or <code>null</code> if the value does not exist. + */ + protected String internalGet(String key) { + return Platform.getPreferencesService().get(key, null, getPreferenceNodes(true)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getFloat(java.lang.String) + */ + public float getFloat(String name) { + String value = internalGet(name); + if (value == null) { + return FLOAT_DEFAULT_DEFAULT; + } + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return FLOAT_DEFAULT_DEFAULT; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getInt(java.lang.String) + */ + public int getInt(String name) { + String value = internalGet(name); + if (value == null) { + return INT_DEFAULT_DEFAULT; + } + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return INT_DEFAULT_DEFAULT; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getLong(java.lang.String) + */ + public long getLong(String name) { + String value = internalGet(name); + if (value == null) { + return LONG_DEFAULT_DEFAULT; + } + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return LONG_DEFAULT_DEFAULT; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#getString(java.lang.String) + */ + public String getString(String name) { + String value = internalGet(name); + return value == null ? STRING_DEFAULT_DEFAULT : value; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#isDefault(java.lang.String) + */ + public boolean isDefault(String name) { + if (name == null) { + return false; + } + return (Platform.getPreferencesService().get(name, null, getPreferenceNodes(false))) == null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#needsSaving() + */ + public boolean needsSaving() { + return dirty; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#putValue(java.lang.String, + * java.lang.String) + */ + public void putValue(String name, String value) { + try { + // Do not notify listeners + silentRunning = true; + getStorePreferences().put(name, value); + } finally { + // Be sure that an exception does not stop property updates + silentRunning = false; + dirty = true; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#removePropertyChangeListener(org.eclipse.jface.util.IPropertyChangeListener) + */ + public void removePropertyChangeListener(IPropertyChangeListener listener) { + removeListenerObject(listener); + if (!isListenerAttached()) { + disposePreferenceStoreListener(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setDefault(java.lang.String, + * double) + */ + public void setDefault(String name, double value) { + getDefaultPreferences().putDouble(name, value); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setDefault(java.lang.String, + * float) + */ + public void setDefault(String name, float value) { + getDefaultPreferences().putFloat(name, value); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setDefault(java.lang.String, + * int) + */ + public void setDefault(String name, int value) { + getDefaultPreferences().putInt(name, value); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setDefault(java.lang.String, + * long) + */ + public void setDefault(String name, long value) { + getDefaultPreferences().putLong(name, value); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setDefault(java.lang.String, + * java.lang.String) + */ + public void setDefault(String name, String defaultObject) { + getDefaultPreferences().put(name, defaultObject); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setDefault(java.lang.String, + * boolean) + */ + public void setDefault(String name, boolean value) { + getDefaultPreferences().putBoolean(name, value); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setToDefault(java.lang.String) + */ + public void setToDefault(String name) { + + String oldValue = getString(name); + String defaultValue = getDefaultString(name); + try { + silentRunning = true;// Turn off updates from the store + // removing a non-existing preference is a no-op so call the Core + // API directly + getStorePreferences().remove(name); + if (!oldValue.equals(defaultValue)) { + dirty = true; + firePropertyChangeEvent(name, oldValue, defaultValue); + } + + } finally { + silentRunning = false;// Restart listening to preferences + } + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setValue(java.lang.String, + * double) + */ + public void setValue(String name, double value) { + double oldValue = getDouble(name); + if (oldValue == value) { + return; + } + try { + silentRunning = true;// Turn off updates from the store + if (getDefaultDouble(name) == value) { + getStorePreferences().remove(name); + } else { + getStorePreferences().putDouble(name, value); + } + dirty = true; + firePropertyChangeEvent(name, new Double(oldValue), new Double(value)); + } finally { + silentRunning = false;// Restart listening to preferences + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setValue(java.lang.String, + * float) + */ + public void setValue(String name, float value) { + float oldValue = getFloat(name); + if (oldValue == value) { + return; + } + try { + silentRunning = true;// Turn off updates from the store + if (getDefaultFloat(name) == value) { + getStorePreferences().remove(name); + } else { + getStorePreferences().putFloat(name, value); + } + dirty = true; + firePropertyChangeEvent(name, new Float(oldValue), new Float(value)); + } finally { + silentRunning = false;// Restart listening to preferences + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setValue(java.lang.String, + * int) + */ + public void setValue(String name, int value) { + int oldValue = getInt(name); + if (oldValue == value) { + return; + } + try { + silentRunning = true;// Turn off updates from the store + if (getDefaultInt(name) == value) { + getStorePreferences().remove(name); + } else { + getStorePreferences().putInt(name, value); + } + dirty = true; + firePropertyChangeEvent(name, Integer.valueOf(oldValue), Integer.valueOf(value)); + } finally { + silentRunning = false;// Restart listening to preferences + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setValue(java.lang.String, + * long) + */ + public void setValue(String name, long value) { + long oldValue = getLong(name); + if (oldValue == value) { + return; + } + try { + silentRunning = true;// Turn off updates from the store + if (getDefaultLong(name) == value) { + getStorePreferences().remove(name); + } else { + getStorePreferences().putLong(name, value); + } + dirty = true; + firePropertyChangeEvent(name, Long.valueOf(oldValue), Long.valueOf(value)); + } finally { + silentRunning = false;// Restart listening to preferences + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setValue(java.lang.String, + * java.lang.String) + */ + public void setValue(String name, String value) { + // Do not turn on silent running here as Strings are propagated + if (getDefaultString(name).equals(value)) { + getStorePreferences().remove(name); + } else { + getStorePreferences().put(name, value); + } + dirty = true; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPreferenceStore#setValue(java.lang.String, + * boolean) + */ + public void setValue(String name, boolean value) { + boolean oldValue = getBoolean(name); + if (oldValue == value) { + return; + } + try { + silentRunning = true;// Turn off updates from the store + if (getDefaultBoolean(name) == value) { + getStorePreferences().remove(name); + } else { + getStorePreferences().putBoolean(name, value); + } + dirty = true; + firePropertyChangeEvent(name, oldValue ? Boolean.TRUE : Boolean.FALSE, value ? Boolean.TRUE : Boolean.FALSE); + } finally { + silentRunning = false;// Restart listening to preferences + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.preference.IPersistentPreferenceStore#save() + */ + public void save() throws IOException { + try { + getStorePreferences().flush(); + dirty = false; + } catch (BackingStoreException e) { + throw new IOException(e.getMessage()); + } + + } + + /** + * Dispose the receiver. + */ + private void disposePreferenceStoreListener() { + + IEclipsePreferences root = (IEclipsePreferences) Platform.getPreferencesService().getRootNode().node(Plugin.PLUGIN_PREFERENCE_SCOPE); + try { + if (!(root.nodeExists(nodeQualifier))) { + return; + } + } catch (BackingStoreException e) { + return;// No need to report here as the node won't have the + // listener + } + + IEclipsePreferences preferences = getStorePreferences(); + if (preferences == null) { + return; + } + if (preferencesListener != null) { + preferences.removePreferenceChangeListener(preferencesListener); + preferencesListener = null; + } + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/VisiblePageSingleton.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/VisiblePageSingleton.java new file mode 100644 index 00000000000..7fafd1364aa --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/VisiblePageSingleton.java @@ -0,0 +1,67 @@ +/***************************************************************************** + * Copyright (c) 2010, 2016 CEA LIST, Christian W. Damus, 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: + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.preferences; + +import org.eclipse.jface.preference.IPreferencePage; + +/** + * This singleton has bee created to manage the button ok and apply of preference page. + * In the case of button ok pressed, the behavior of eclipse try of apply in the first preference page found. + * Here each page has a specific behavior. So to store the preference, the active page is called + * + */ +public class VisiblePageSingleton { + + private static VisiblePageSingleton instance; + + private IPreferencePage page; + + /** + * + * @return the instance of the {@link VisiblePageSingleton} + */ + public static VisiblePageSingleton getInstance() { + if (instance == null) { + instance = new VisiblePageSingleton(); + } + return instance; + } + + /** + * set the visible page + * + * @param page + * a {@link IPreferencePage} --> {@link DiagramPreferencePage} or {@link AbstractPapyrusPreferencePage} + */ + public void setVisiblePage(IPreferencePage page) { + this.page = page; + } + + /** + * + * @return the Visible Page + */ + public IPreferencePage getVisiblePage() { + return this.page; + } + + /** + * call the visisble page in order to store preferences + */ + public void store() { + if (this.page instanceof IPapyrusPreferencePage) { + ((IPapyrusPreferencePage) (this.page)).storeAllPreferences(); + } + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/dialog/AbstractApplyValueOnPreferenceKeyDialog.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/dialog/AbstractApplyValueOnPreferenceKeyDialog.java new file mode 100644 index 00000000000..3b4a3791000 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/dialog/AbstractApplyValueOnPreferenceKeyDialog.java @@ -0,0 +1,71 @@ +/***************************************************************************** + * Copyright (c) 2010 CEA LIST. + * + * + * 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: + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.preferences.dialog; + +import java.util.ArrayList; + +/** + * The Class ApplyValueOnPreferenceKeyDialog display all the preference key and give all selected keys + */ +public abstract class AbstractApplyValueOnPreferenceKeyDialog extends AbstractPreferenceKeyDialog { + + /** The checked key. */ + protected ArrayList<String> checkedKey; + + /** + * Instantiates a new apply value on preference key dialog. + * + * @param keys + * the keys + */ + public AbstractApplyValueOnPreferenceKeyDialog(String[] keys) { + super(keys); + checkedKey = new ArrayList<String>(); + } + + /** + * Gets the key to remove. + * + * @return the key to remove + */ + public ArrayList<String> getKeyToRemove() { + return checkedKey; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.dialogs.Dialog#okPressed() + */ + @Override + protected void okPressed() { + for (int i = 0; i < keyTable.getItems().length; i++) { + if (keyTable.getItems()[i].getChecked()) { + checkedKey.add((String) keyTable.getItems()[i].getData()); + } + } + super.okPressed(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.dialogs.Dialog#cancelPressed() + */ + @Override + protected void cancelPressed() { + super.cancelPressed(); + checkedKey = new ArrayList<String>(); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/dialog/AbstractPreferenceKeyDialog.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/dialog/AbstractPreferenceKeyDialog.java new file mode 100644 index 00000000000..14b07d5d618 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/preferences/dialog/AbstractPreferenceKeyDialog.java @@ -0,0 +1,105 @@ +/***************************************************************************** + * Copyright (c) 2010 CEA LIST. + * + * + * 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: + * Patrick Tessier (CEA LIST) Patrick.tessier@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.preferences.dialog; + +import java.util.Arrays; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.messages.Messages; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +/** + * The Class AbstractPreferenceKeyDialog display all preference key that are given in parameters + */ +public abstract class AbstractPreferenceKeyDialog extends org.eclipse.jface.dialogs.StatusDialog { + + /** The key table. */ + protected Table keyTable; + + /** The table viewer. */ + protected TableViewer tableViewer; + + /** The keys. */ + protected String[] keys; + + /** + * Instantiates a new abstract preference key dialog. + * + * @param keys + * the array of preference jy to display + */ + public AbstractPreferenceKeyDialog(String[] keys) { + super(new Shell()); + this.keys = Arrays.copyOf(keys, keys.length); + setStatusLineAboveButtons(true); + updateStatus(new Status(IStatus.INFO, Activator.PLUGIN_ID, Messages.AbstractPreferenceKeyDialog_WouldYouLikeOverloadPreferences)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite) super.createDialogArea(parent); + keyTable = new Table(composite, SWT.CHECK | SWT.BORDER); + tableViewer = new TableViewer(keyTable); + tableViewer.setLabelProvider(createLabelProvider()); + tableViewer.setContentProvider(createContentProvider()); + + TableColumn column = new TableColumn(keyTable, SWT.NONE); + column.setWidth(150); + column.setText(Messages.AbstractPreferenceKeyDialog_Pref_Kind); + + column = new TableColumn(keyTable, SWT.NONE); + column.setWidth(90); + column.setText(Messages.AbstractPreferenceKeyDialog_Level); + + column = new TableColumn(keyTable, SWT.NONE); + column.setWidth(200); + column.setText(Messages.AbstractPreferenceKeyDialog_Localization); + tableViewer.setInput(keys); + keyTable.setHeaderVisible(true); + + + return composite; + + } + + /** + * + * @return + * the label provider for the table viewer + */ + protected abstract IBaseLabelProvider createLabelProvider(); + + /** + * + * @return + * the content provider for the table viewer + */ + protected abstract IContentProvider createContentProvider(); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/CompositePapyrusContentProvider.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/CompositePapyrusContentProvider.java new file mode 100644 index 00000000000..b667228dde5 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/CompositePapyrusContentProvider.java @@ -0,0 +1,109 @@ +/***************************************************************************** + * Copyright (c) 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.providers; + +import java.util.Objects; +import java.util.stream.Stream; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.papyrus.infra.widgets.providers.IAdaptableContentProvider; +import org.eclipse.papyrus.infra.widgets.providers.IHierarchicContentProvider; +import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider; + +import com.google.common.collect.Iterables; + +/** + * A content provider that synthesizes content from multiple other providers. + */ +public class CompositePapyrusContentProvider implements IAdaptableContentProvider, IHierarchicContentProvider, IStaticContentProvider { + + private final ITreeContentProvider[] delegates; + + public CompositePapyrusContentProvider(ITreeContentProvider... delegates) { + super(); + + this.delegates = new ITreeContentProvider[delegates.length]; + for (int i = 0; i < delegates.length; i++) { + // Wrap it or not, as needed + this.delegates[i] = DelegatingPapyrusContentProvider.wrap(delegates[i]); + } + } + + public CompositePapyrusContentProvider(Iterable<? extends ITreeContentProvider> delegates) { + this(Iterables.toArray(delegates, ITreeContentProvider.class)); + } + + @Override + public Object[] getElements(Object inputElement) { + return Stream.of(delegates) + .flatMap(d -> Stream.of(d.getElements(inputElement))) + .toArray(); + } + + @Override + public void dispose() { + Stream.of(delegates).forEach(ITreeContentProvider::dispose); + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + Stream.of(delegates).forEach(d -> d.inputChanged(viewer, oldInput, newInput)); + } + + @Override + public Object[] getChildren(Object parentElement) { + return Stream.of(delegates) + .flatMap(d -> Stream.of(d.getChildren(parentElement))) + .toArray(); + } + + @Override + public Object getParent(Object element) { + return Stream.of(delegates) + .map(d -> d.getParent(element)) + .filter(Objects::nonNull) + .findAny().orElse(null); + } + + @Override + public boolean hasChildren(Object element) { + return Stream.of(delegates) + .anyMatch(d -> d.hasChildren(element)); + } + + @Override + public Object getAdaptedValue(Object containerElement) { + return Stream.of(delegates) + .map(IAdaptableContentProvider.class::cast) + .map(d -> d.getAdaptedValue(containerElement)) + .filter(Objects::nonNull) + .findAny().orElse(containerElement); + } + + @Override + public boolean isValidValue(Object element) { + return Stream.of(delegates) + .map(IHierarchicContentProvider.class::cast) + .anyMatch(d -> d.isValidValue(element)); + } + + @Override + public Object[] getElements() { + return Stream.of(delegates) + .map(IStaticContentProvider.class::cast) + .flatMap(d -> Stream.of(d.getElements())) + .toArray(); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/CompositeSemanticContentProviderFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/CompositeSemanticContentProviderFactory.java new file mode 100644 index 00000000000..71616365e59 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/CompositeSemanticContentProviderFactory.java @@ -0,0 +1,63 @@ +/***************************************************************************** + * Copyright (c) 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.providers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.jface.viewers.ITreeContentProvider; + +/** + * Default implementation of a composite content-provider factory. + */ +class CompositeSemanticContentProviderFactory implements ISemanticContentProviderFactory { + private final List<ISemanticContentProviderFactory> factories; + + CompositeSemanticContentProviderFactory(ISemanticContentProviderFactory first, ISemanticContentProviderFactory second) { + super(); + + factories = Arrays.asList(first, second); + } + + private CompositeSemanticContentProviderFactory(CompositeSemanticContentProviderFactory composite, ISemanticContentProviderFactory other) { + super(); + + if (other instanceof CompositeSemanticContentProviderFactory) { + List<ISemanticContentProviderFactory> otherFactories = ((CompositeSemanticContentProviderFactory) other).factories; + factories = new ArrayList<>(composite.factories.size() + otherFactories.size()); + factories.addAll(composite.factories); + factories.addAll(otherFactories); + } else { + factories = new ArrayList<>(composite.factories.size() + 1); + factories.addAll(composite.factories); + factories.add(other); + } + } + + @Override + public ITreeContentProvider createSemanticContentProvider(ResourceSet resourceSet) { + return DelegatingPapyrusContentProvider.compose(factories.stream() + .map(f -> f.createSemanticContentProvider(resourceSet)) + .collect(Collectors.toList())); + } + + @Override + public ISemanticContentProviderFactory compose(ISemanticContentProviderFactory other) { + return new CompositeSemanticContentProviderFactory(this, other); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/DelegatingPapyrusContentProvider.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/DelegatingPapyrusContentProvider.java new file mode 100644 index 00000000000..de1ddcce51f --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/DelegatingPapyrusContentProvider.java @@ -0,0 +1,170 @@ +/***************************************************************************** + * Copyright (c) 2012, 2016 CEA LIST, Christian W. Damus, 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus (CEA) - bug 410346 + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.providers; + +import java.util.Collection; +import java.util.Iterator; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.papyrus.infra.tools.util.TypeUtils; +import org.eclipse.papyrus.infra.widgets.providers.IAdaptableContentProvider; +import org.eclipse.papyrus.infra.widgets.providers.IHierarchicContentProvider; +import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/** + * A content-provider implementing the complete set of Papyrus-specific content-provider APIs that + * delegates those APIs to another provider according to its actual capabilities. + */ +public class DelegatingPapyrusContentProvider implements IAdaptableContentProvider, IHierarchicContentProvider, IStaticContentProvider { + + private static final Object[] NONE = {}; + + private final ITreeContentProvider treeDelegate; + private final IAdaptableContentProvider adaptableDelegate; + private final IHierarchicContentProvider hierarchicDelegate; + private final IStaticContentProvider staticDelegate; + + public DelegatingPapyrusContentProvider(ITreeContentProvider delegate) { + super(); + + treeDelegate = delegate; + adaptableDelegate = TypeUtils.as(delegate, IAdaptableContentProvider.class); + hierarchicDelegate = TypeUtils.as(delegate, IHierarchicContentProvider.class); + staticDelegate = TypeUtils.as(delegate, IStaticContentProvider.class); + } + + /** + * Obtains a content-provider based on the given {@code provider} that implements all of the + * the Papyrus-specific extension protocols. + * + * @param provider + * a tree-content provider + * @return a complete provider, which may be a delegating provider or may be the original + * {@code provider} if it is already complete + */ + public static ITreeContentProvider wrap(ITreeContentProvider provider) { + return ((provider instanceof IAdaptableContentProvider) && (provider instanceof IHierarchicContentProvider) && (provider instanceof IStaticContentProvider)) + ? provider + : new DelegatingPapyrusContentProvider(provider); + } + + /** + * Obtains a content-provider based on the given providers that implements all of the + * the Papyrus-specific extension protocols. + * + * @param first, second, rest + * two or more tree-content providers + * @return a complete provider based on the given providers + */ + public static ITreeContentProvider compose(ITreeContentProvider first, ITreeContentProvider second, ITreeContentProvider... rest) { + return compose(Lists.asList(first, second, rest)); + } + + /** + * Obtains a content-provider based on the given {@code providers} that implements all of the + * the Papyrus-specific extension protocols. + * + * @param providers + * zero or more tree-content providers + * @return a complete provider based on the given {@code providers} + */ + public static ITreeContentProvider compose(Iterable<? extends ITreeContentProvider> providers) { + ITreeContentProvider result; + + // Obtain optimal result in case of a single provider + if (providers instanceof Collection<?>) { + Collection<? extends ITreeContentProvider> collection = (Collection<? extends ITreeContentProvider>) providers; + switch (collection.size()) { + case 0: + result = new CompositePapyrusContentProvider(); + break; + case 1: + result = wrap(Iterables.getOnlyElement(collection)); + break; + default: + result = new CompositePapyrusContentProvider(providers); + break; + } + } else { + Iterator<? extends ITreeContentProvider> iter = providers.iterator(); + if (!iter.hasNext()) { + result = new CompositePapyrusContentProvider(); + } else { + ITreeContentProvider provider = iter.next(); + if (iter.hasNext()) { + result = new CompositePapyrusContentProvider(providers); + } else { + result = wrap(provider); + } + } + } + + return result; + } + + @Override + public Object[] getElements(Object inputElement) { + return treeDelegate.getElements(inputElement); + } + + @Override + public void dispose() { + treeDelegate.dispose(); + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + treeDelegate.inputChanged(viewer, oldInput, newInput); + } + + @Override + public Object[] getChildren(Object parentElement) { + return treeDelegate.getChildren(parentElement); + } + + @Override + public Object getParent(Object element) { + return treeDelegate.getParent(element); + } + + @Override + public boolean hasChildren(Object element) { + return treeDelegate.hasChildren(element); + } + + @Override + public Object getAdaptedValue(Object containerElement) { + return (adaptableDelegate == null) + ? containerElement + : adaptableDelegate.getAdaptedValue(containerElement); + } + + @Override + public boolean isValidValue(Object element) { + return (hierarchicDelegate == null) || hierarchicDelegate.isValidValue(element); + } + + @Override + public Object[] getElements() { + return (staticDelegate == null) + ? NONE + : staticDelegate.getElements(); + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/ISemanticContentProviderFactory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/ISemanticContentProviderFactory.java new file mode 100644 index 00000000000..05eecf9f087 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/providers/ISemanticContentProviderFactory.java @@ -0,0 +1,59 @@ +/***************************************************************************** + * Copyright (c) 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.providers; + +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.papyrus.infra.core.resource.IModel; +import org.eclipse.papyrus.infra.core.resource.ModelSet; + +/** + * <p> + * A protocol for creation of semantic model content providers on EMF resource sets. + * </p> + * <p> + * It is expected that {@link IModel}s representing semantic model content in the + * {@link ModelSet} provide adapters of this interface type for the purpose of obtaining + * suitable content-providers for presentation of the model content to the user. + * Because there there are potentially multiple such {@code IModel}s that have + * semantic content, it is possible that multiple content-providers will have to be + * combined via the {@link #compose(ISemanticContentProviderFactory)} API. + * </p> + * + * @see IModel + * @see #compose(ISemanticContentProviderFactory) + */ +@FunctionalInterface +public interface ISemanticContentProviderFactory { + /** + * Creates a semantic model content provider on the given {@code ResourceSet}. + * + * @param resourceSet + * a resource set + * + * @return the semantic model content provider + */ + ITreeContentProvider createSemanticContentProvider(ResourceSet resourceSet); + + /** + * Obtains a factory that composes my provider with an{@code other} factory's provider. + * + * @param other + * another semantic content-provider factory + * @return the composed factory, which generally creates composed content providers + */ + default ISemanticContentProviderFactory compose(ISemanticContentProviderFactory other) { + return new CompositeSemanticContentProviderFactory(this, other); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/EditorLifecycleEventListener.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/EditorLifecycleEventListener.java new file mode 100644 index 00000000000..fb55dabd681 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/EditorLifecycleEventListener.java @@ -0,0 +1,55 @@ +/***************************************************************************** + * Copyright (c) 2013, 2015 CEA LIST, Christian W. Damus, 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus - bug 469188 + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.services; + +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; + +/** + * Listens to the Lifecycle of an {@link IMultiDiagramEditor} + * + * @author Camille Letavernier + * + */ +public interface EditorLifecycleEventListener { + + /** + * The ServicesRegistry is successfully started + * + * @param editor + */ + public void postInit(IMultiDiagramEditor editor); + + /** + * All the editors are constructed, but not yet displayed + * + * @param editor + */ + public default void preDisplay(IMultiDiagramEditor editor) { + // Pass + } + + /** + * All the editors are displayed + * + * @param editor + */ + public void postDisplay(IMultiDiagramEditor editor); + + /** + * The editor is about to be closed + * + * @param editor + */ + public void beforeClose(IMultiDiagramEditor editor); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/EditorLifecycleManager.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/EditorLifecycleManager.java new file mode 100644 index 00000000000..d018ab385d1 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/EditorLifecycleManager.java @@ -0,0 +1,30 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.services; + +import org.eclipse.papyrus.infra.core.services.IService; + +/** + * The LifecycleManager for IMultiDiagramEditor + * + * It notifies its listeners when the state of the editor changes + * + * @author Camille Letavernier + * + */ +public interface EditorLifecycleManager extends IService { + + public void addEditorLifecycleEventsListener(EditorLifecycleEventListener listener); + + public void removeEditorLifecycleEventsListener(EditorLifecycleEventListener listener); + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/Messages.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/Messages.java new file mode 100644 index 00000000000..0b8d66d2056 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/Messages.java @@ -0,0 +1,34 @@ +/***************************************************************************** + * Copyright (c) 2015, 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.services; + +import org.eclipse.osgi.util.NLS; + +/** + * Translatable strings. + */ +class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.papyrus.infra.ui.services.messages"; //$NON-NLS-1$ + public static String SaveLayoutBeforeClose_0; + public static String SaveLayoutBeforeClose_1; + public static String SaveLayoutBeforeClose_2; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/ResourceUpdateService.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/ResourceUpdateService.java new file mode 100644 index 00000000000..c83e251e17c --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/ResourceUpdateService.java @@ -0,0 +1,292 @@ +/***************************************************************************** + * Copyright (c) 2013, 2014 CEA LIST 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: + * Camille Letavernier (camille.letavernier@cea.fr) - Initial API and implementation + * Christian W. Damus (CEA) - bug 437217 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.services; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.ConcurrentMap; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.osgi.util.NLS; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.services.IService; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.editor.IReloadableEditor; +import org.eclipse.papyrus.infra.ui.editor.IReloadableEditor.DirtyPolicy; +import org.eclipse.papyrus.infra.ui.lifecycleevents.DoSaveEvent; +import org.eclipse.papyrus.infra.ui.lifecycleevents.ILifeCycleEventsProvider; +import org.eclipse.papyrus.infra.ui.lifecycleevents.ISaveEventListener; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.progress.IWorkbenchSiteProgressService; +import org.eclipse.ui.progress.UIJob; + +import com.google.common.collect.Maps; + +/** + * A Service to check workspace modifications on current resources + * + * @author Camille Letavernier + * + */ +public class ResourceUpdateService implements IService, IPartListener { + + protected ServicesRegistry registry; + + protected ModelSet modelSet; + + static int[] handledTypes = new int[] { IResourceChangeEvent.POST_CHANGE, IResourceChangeEvent.PRE_DELETE, IResourceChangeEvent.PRE_CLOSE }; + + protected boolean isSaving; + + protected ConcurrentMap<IMultiDiagramEditor, Job> pendingEditorCloseJobs = Maps.newConcurrentMap(); + + private final ISaveEventListener preSaveListener = new ISaveEventListener() { + + @Override + public void doSaveAs(DoSaveEvent event) { + isSaving = true; + } + + @Override + public void doSave(DoSaveEvent event) { + isSaving = true; + } + }; + + private final ISaveEventListener postSaveListener = new ISaveEventListener() { + + @Override + public void doSaveAs(DoSaveEvent event) { + isSaving = false; + } + + @Override + public void doSave(DoSaveEvent event) { + isSaving = false; + } + }; + + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + this.registry = servicesRegistry; + } + + @Override + public void startService() throws ServiceException { + ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceChangeListener, IResourceChangeEvent.POST_CHANGE); + modelSet = registry.getService(ModelSet.class); + registry.getService(ILifeCycleEventsProvider.class).addAboutToDoSaveListener(preSaveListener); + registry.getService(ILifeCycleEventsProvider.class).addPostDoSaveListener(postSaveListener); + } + + @Override + public void disposeService() throws ServiceException { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceChangeListener); + modelSet = null; + } + + protected void closeEditor() { + closeEditor(Collections.<Resource> emptyList(), false); + } + + protected void closeEditor(final Collection<? extends Resource> triggeringResources, final boolean reopen) { + try { + if (!reopen) { + registry.remove(SaveLayoutBeforeClose.class.getName()); + } + + final IMultiDiagramEditor editor = registry.getService(IMultiDiagramEditor.class); + if (editor != null) { + final IWorkbenchPartSite site = editor.getSite(); + UIJob closeEditorJob = new UIJob(site.getShell().getDisplay(), NLS.bind("Reload editor {0}", editor.getTitle())) { + + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + // Remove the pending job + pendingEditorCloseJobs.remove(editor); + + IStatus result = Status.OK_STATUS; + monitor = SubMonitor.convert(monitor, IProgressMonitor.UNKNOWN); + + try { + IReloadableEditor.ReloadReason reason = reopen ? IReloadableEditor.ReloadReason.RESOURCES_CHANGED : IReloadableEditor.ReloadReason.RESOURCES_DELETED; + + DirtyPolicy dirtyPolicy = DirtyPolicy.getDefault(); + if (!reopen && !editor.isDirty()) { + // Check whether we're deleting one of our own resources. If so, just close + URI principalURI = modelSet.getURIWithoutExtension(); + for (Resource next : triggeringResources) { + if (next.getURI().trimFileExtension().equals(principalURI)) { + dirtyPolicy = DirtyPolicy.DO_NOT_SAVE; + break; + } + } + } + + try { + IReloadableEditor.Adapter.getAdapter(editor).reloadEditor(triggeringResources, reason, dirtyPolicy); + } catch (CoreException e) { + result = e.getStatus(); + } + } finally { + monitor.done(); + } + + return result; + } + }; + + // We are notified usually of at least three resources (*.di, *.notation, *.uml) that are unloaded, but + // there's no need to close and re-open the same editor three times + if (pendingEditorCloseJobs.putIfAbsent(editor, closeEditorJob) == null) { + // Async execution to avoid lock conflicts on the Workspace (Probably owned by this thread, and not the UI thread) + IWorkbenchSiteProgressService progressService = site.getService(IWorkbenchSiteProgressService.class); + progressService.schedule(closeEditorJob); + } + } + } catch (ServiceException ex) { + // Nothing + } + } + + protected void handleResourcesRemoved(Collection<Resource> emfResources) { + closeEditor(emfResources, false); + } + + protected void handleResourceChanged(Collection<Resource> emfResources) { + closeEditor(emfResources, true); + } + + // Copied from org.eclipse.emf.ecore.presentation.EcoreEditor + protected IResourceChangeListener resourceChangeListener = new IResourceChangeListener() { + + @Override + public void resourceChanged(IResourceChangeEvent event) { + IResourceDelta delta = event.getDelta(); + try { + class ResourceDeltaVisitor implements IResourceDeltaVisitor { + + protected Collection<Resource> changedResources = new ArrayList<Resource>(); + + protected Collection<Resource> removedResources = new ArrayList<Resource>(); + + @Override + public boolean visit(final IResourceDelta delta) { + if (delta.getResource().getType() == IResource.FILE) { + if (delta.getKind() == IResourceDelta.REMOVED || delta.getKind() == IResourceDelta.CHANGED) { + URI resourceURI = URI.createPlatformResourceURI(delta.getFullPath().toString(), true); + Resource resource = modelSet.getResource(resourceURI, false); + if (resource == null) { + // try again, with a pluginURI, see bug 418428 + URI pluginURI = URI.createPlatformPluginURI(delta.getFullPath().toString(), true); + resource = modelSet.getResource(pluginURI, false); + } + if (resource != null) { + + if (delta.getKind() == IResourceDelta.REMOVED) { + removedResources.add(resource); + } else { + if ((delta.getFlags() & IResourceDelta.MARKERS) != 0) { + // Skip markers + // DiagnosticDecorator.DiagnosticAdapter.update(resource, markerHelper.getMarkerDiagnostics(resource, (IFile)delta.getResource())); + } + if ((delta.getFlags() & IResourceDelta.CONTENT) != 0) { + // if(!savedResources.remove(resource)) { + // changedResources.add(resource); + // } + if (!isSaving) { + changedResources.add(resource); + } + } + } + } + } + return false; + } + + return true; + } + + public Collection<Resource> getChangedResources() { + return changedResources; + } + + public Collection<Resource> getRemovedResources() { + return removedResources; + } + } + + final ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(); + + delta.accept(visitor); + + if (!visitor.getRemovedResources().isEmpty()) { + handleResourcesRemoved(visitor.getRemovedResources()); + } + + if (!visitor.getChangedResources().isEmpty()) { + handleResourceChanged(visitor.getChangedResources()); + } + } catch (CoreException exception) { + Activator.log.error(exception); + } + } + }; + + @Override + public void partActivated(IWorkbenchPart part) { + // Nothing + } + + @Override + public void partBroughtToTop(IWorkbenchPart part) { + // Nothing + } + + @Override + public void partClosed(IWorkbenchPart part) { + // Nothing + } + + @Override + public void partDeactivated(IWorkbenchPart part) { + // Nothing + } + + @Override + public void partOpened(IWorkbenchPart part) { + // Nothing + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/SaveLayoutBeforeClose.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/SaveLayoutBeforeClose.java new file mode 100644 index 00000000000..5b84eadb2cb --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/SaveLayoutBeforeClose.java @@ -0,0 +1,218 @@ +/***************************************************************************** + * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus - bug 434983 + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.services; + +import java.io.IOException; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialogWithToggle; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel; +import org.eclipse.papyrus.infra.core.services.IService; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.internal.commands.TogglePageLayoutStorageHandler; +import org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences; +import org.eclipse.papyrus.infra.ui.internal.preferences.YesNo; +import org.eclipse.papyrus.infra.ui.lifecycleevents.DoSaveEvent; +import org.eclipse.papyrus.infra.ui.lifecycleevents.ILifeCycleEventsProvider; +import org.eclipse.papyrus.infra.ui.lifecycleevents.LifeCycleEventsProvider; + +/** + * This service automatically saves the current SashModel before closing the Papyrus editor + * + * This is useful, as modifications to the SashModel do not dirty the editor + * + * The save action is not executed if the editor is dirty when it is closed (To ensure model consistency) + * + * Bug 430976: [SashEditor] Editor layout is not exactly the same when reopening the model + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=430976 + * + * @author Camille Letavernier + */ +public class SaveLayoutBeforeClose implements IService { + + private ServicesRegistry registry; + + private EditorLifecycleManager lifecycleManager; + + private EditorLifecycleEventListener lifecycleListener; + + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + this.registry = servicesRegistry; + } + + @Override + public void startService() throws ServiceException { + installSaveOnClose(); + } + + protected void installSaveOnClose() { + try { + lifecycleManager = registry.getService(EditorLifecycleManager.class); + if (lifecycleManager == null) { + return; + } + } catch (ServiceException ex) { + return; + } + + lifecycleListener = new EditorLifecycleEventListener() { + + @Override + public void postInit(IMultiDiagramEditor editor) { + // Nothing + } + + @Override + public void postDisplay(IMultiDiagramEditor editor) { + checkSharedLayout(editor); + } + + @Override + public void beforeClose(IMultiDiagramEditor editor) { + saveBeforeClose(editor); + } + }; + + lifecycleManager.addEditorLifecycleEventsListener(lifecycleListener); + } + + public void saveBeforeClose(IMultiDiagramEditor editor) { + if (editor.isDirty()) { + return; // User explicitly quit without saving. Do nothing (And if user wants to save during exit, the sashmodel will be saved anyway) + } + + ModelSet modelSet; // Required + LifeCycleEventsProvider internalLifecycleEventsProvider = null; // Optional + + try { + modelSet = registry.getService(ModelSet.class); + } catch (ServiceException ex) { + return; + } + + try { + ILifeCycleEventsProvider eventsProvider = registry.getService(ILifeCycleEventsProvider.class); + if (eventsProvider instanceof LifeCycleEventsProvider) { + internalLifecycleEventsProvider = (LifeCycleEventsProvider) eventsProvider; + } + } catch (ServiceException ex) { + // Ignore: the service is optional + } + + SashModel sashModel = (SashModel) modelSet.getModel(SashModel.MODEL_ID); + + try { + // We need to send pre- and post-save events, but we can only do that with the internal LifecycleEventsProvider + // The ISaveAndDirtyService can only save the whole model, but we just want to save the sash + DoSaveEvent event = new DoSaveEvent(registry, editor, true); + if (internalLifecycleEventsProvider != null) { + internalLifecycleEventsProvider.fireAboutToDoSaveEvent(event); + internalLifecycleEventsProvider.fireDoSaveEvent(event); + } + sashModel.saveModel(); + if (internalLifecycleEventsProvider != null) { + internalLifecycleEventsProvider.firePostDoSaveEvent(event); + } + } catch (IOException ex) { + Activator.log.error(ex); + } + } + + private void checkSharedLayout(IMultiDiagramEditor editor) { + try { + ModelSet modelSet = registry.getService(ModelSet.class); + SashModel sashModel = (SashModel) modelSet.getModel(SashModel.MODEL_ID); + + if (sashModel.isLegacyMode()) { + // Have we ever created the private sash model file? + URI privateURI = sashModel.getPrivateResourceURI(); + if (!modelSet.getURIConverter().exists(privateURI, null)) { + // Prompt the user + promptToEnablePrivateStorage(editor); + } + } + } catch (ServiceException ex) { + // Shared layout doesn't matter if there's no model-set + } + } + + private void promptToEnablePrivateStorage(IMultiDiagramEditor editor) { + YesNo preference = EditorPreferences.getInstance().getConvertSharedPageLayoutToPrivate(); + + if (preference == YesNo.PROMPT) { + MessageDialogWithToggle dlg = MessageDialogWithToggle.openYesNoCancelQuestion(editor.getSite().getShell(), + Messages.SaveLayoutBeforeClose_0, + Messages.SaveLayoutBeforeClose_1, + Messages.SaveLayoutBeforeClose_2, false, null, null); + + switch (dlg.getReturnCode()) { + case IDialogConstants.YES_ID: + preference = YesNo.YES; + break; + case IDialogConstants.NO_ID: + preference = YesNo.NO; + break; + default: + // User cancelled + preference = YesNo.PROMPT; + break; + } + + if (dlg.getToggleState()) { + EditorPreferences.getInstance().setConvertSharedPageLayoutToPrivate(preference); + } + } + + switch (preference) { + case YES: + // Change the storage to private + new TogglePageLayoutStorageHandler().togglePrivatePageLayout(editor); + + // And save the new layout scheme + saveBeforeClose(editor); + break; + case NO: + // Just create the empty resource and save it + try { + ModelSet modelSet = editor.getServicesRegistry().getService(ModelSet.class); + SashModel sashModel = (SashModel) modelSet.getModel(SashModel.MODEL_ID); + modelSet.createResource(sashModel.getPrivateResourceURI()); + saveBeforeClose(editor); + } catch (ServiceException e) { + // Without a model-set, much else is going wrong, so there's no need to deal + // with this here + } + break; + default: + // User cancelled + break; + } + } + + @Override + public void disposeService() throws ServiceException { + registry = null; + if (lifecycleManager != null) { + lifecycleManager.removeEditorLifecycleEventsListener(lifecycleListener); + lifecycleListener = null; + lifecycleManager = null; + } + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/internal/EditorLifecycleManagerImpl.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/internal/EditorLifecycleManagerImpl.java new file mode 100644 index 00000000000..99a102093f5 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/internal/EditorLifecycleManagerImpl.java @@ -0,0 +1,128 @@ +/***************************************************************************** + * Copyright (c) 2013, 2015 CEA LIST, Christian W. Damus, 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus - bug 469188 + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.services.internal; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.services.EditorLifecycleEventListener; +import org.eclipse.papyrus.infra.ui.services.EditorLifecycleManager; + + +public class EditorLifecycleManagerImpl implements EditorLifecycleManager, InternalEditorLifecycleManager { + + private final Set<EditorLifecycleEventListener> listeners = new HashSet<EditorLifecycleEventListener>(); + + @Override + public void init(ServicesRegistry servicesRegistry) throws ServiceException { + // Nothing + } + + @Override + public void startService() throws ServiceException { + // Nothing + } + + @Override + public void disposeService() throws ServiceException { + listeners.clear(); + } + + @Override + public void addEditorLifecycleEventsListener(EditorLifecycleEventListener listener) { + listeners.add(listener); + } + + @Override + public void removeEditorLifecycleEventsListener(EditorLifecycleEventListener listener) { + listeners.remove(listener); + } + + @Override + public void firePostInit(final IMultiDiagramEditor editor) { + for (final EditorLifecycleEventListener listener : listeners) { + SafeRunner.run(new ISafeRunnable() { + + @Override + public void run() throws Exception { + listener.postInit(editor); + } + + @Override + public void handleException(Throwable exception) { + // Already logged by the SafeRunner + } + }); + } + } + + @Override + public void firePreDisplay(final IMultiDiagramEditor editor) { + for (final EditorLifecycleEventListener listener : listeners) { + SafeRunner.run(new ISafeRunnable() { + + @Override + public void run() throws Exception { + listener.preDisplay(editor); + } + + @Override + public void handleException(Throwable exception) { + // Already logged by the SafeRunner + } + }); + } + } + + @Override + public void firePostDisplay(final IMultiDiagramEditor editor) { + for (final EditorLifecycleEventListener listener : listeners) { + SafeRunner.run(new ISafeRunnable() { + + @Override + public void run() throws Exception { + listener.postDisplay(editor); + } + + @Override + public void handleException(Throwable exception) { + // Already logged by the SafeRunner + } + }); + } + } + + @Override + public void fireBeforeClose(final IMultiDiagramEditor editor) { + for (final EditorLifecycleEventListener listener : listeners) { + SafeRunner.run(new ISafeRunnable() { + + @Override + public void run() throws Exception { + listener.beforeClose(editor); + } + + @Override + public void handleException(Throwable exception) { + // Already logged by the SafeRunner + } + }); + } + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/internal/InternalEditorLifecycleManager.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/internal/InternalEditorLifecycleManager.java new file mode 100644 index 00000000000..97420afd721 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/internal/InternalEditorLifecycleManager.java @@ -0,0 +1,48 @@ +/***************************************************************************** + * Copyright (c) 2013, 2015 CEA LIST, Christian W. Damus, 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus - bug 469188 + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.services.internal; + +import org.eclipse.papyrus.infra.core.services.IService; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; + + +public interface InternalEditorLifecycleManager extends IService { + + /** + * Sends the postInit notification for this editor + * + * @param editor + */ + void firePostInit(IMultiDiagramEditor editor); + + /** + * Sets the preDisplay notification for this editor + * + * @param editor + */ + void firePreDisplay(IMultiDiagramEditor editor); + + /** + * Sends the postDisplay notification for this editor + * + * @param editor + */ + void firePostDisplay(IMultiDiagramEditor editor); + + /** + * Sends the beforeClose notification for this Editor + * + * @param editor + */ + void fireBeforeClose(IMultiDiagramEditor editor); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/messages.properties b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/messages.properties new file mode 100644 index 00000000000..a6f77150932 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/services/messages.properties @@ -0,0 +1,15 @@ +# +# Copyright (c) 2015 Christian W. Damus 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: +# Christian W. Damus - Initial API and implementation +# + +SaveLayoutBeforeClose_0=Editor Layout Storage +SaveLayoutBeforeClose_1=This model stores the editor page layout in the DI resource, which if managed in a source control system will share the layout with others. Convert to local (private) storage of the page layout? +SaveLayoutBeforeClose_2=Remember my decision diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/AbstractCreateMenuFromCommandCategory.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/AbstractCreateMenuFromCommandCategory.java new file mode 100644 index 00000000000..c392791147e --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/AbstractCreateMenuFromCommandCategory.java @@ -0,0 +1,135 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.Category; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.commands.common.NotDefinedException; +import org.eclipse.core.expressions.EvaluationResult; +import org.eclipse.core.expressions.Expression; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.action.IContributionManager; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.swt.SWT; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.menus.CommandContributionItem; +import org.eclipse.ui.menus.CommandContributionItemParameter; +import org.eclipse.ui.menus.ExtensionContributionFactory; +import org.eclipse.ui.menus.IContributionRoot; +import org.eclipse.ui.services.IServiceLocator; + +/** + * Abstract Class to create menu from an Eclipse Command category + * + * @author VL222926 + * + */ +public abstract class AbstractCreateMenuFromCommandCategory extends ExtensionContributionFactory { + + /** the category of the command contributing to this menu */ + protected final String commandCateogyId; + + /** + * + * Constructor. + * + * @param commandCategoryId + * the category of the command contributing to this menu + */ + public AbstractCreateMenuFromCommandCategory(final String commandCategoryId) { + this.commandCateogyId = commandCategoryId; + } + + /** + * + * @see org.eclipse.ui.menus.AbstractContributionFactory#createContributionItems(org.eclipse.ui.services.IServiceLocator, org.eclipse.ui.menus.IContributionRoot) + * + * @param serviceLocator + * @param additions + */ + @Override + public void createContributionItems(IServiceLocator serviceLocator, IContributionRoot additions) { + // test to know if we can create elements if it is possible... + Expression visibleWhen = new Expression() { + + @Override + public EvaluationResult evaluate(IEvaluationContext context) throws CoreException { + return EvaluationResult.TRUE; + } + }; + for (final CommandContributionItem item : addCreationItems(serviceLocator, additions, null)) { + additions.addContributionItem(item, visibleWhen); + } + } + + /** + * + * @param serviceLocator + * @param additions + * @param parent + * @return + */ + protected List<CommandContributionItem> addCreationItems(final IServiceLocator serviceLocator, final IContributionRoot additions, IContributionManager parent) { + final ICommandService commandService = PlatformUI.getWorkbench().getService(ICommandService.class); + final List<CommandContributionItem> items = new ArrayList<CommandContributionItem>(); + final Category category = commandService.getCategory(this.commandCateogyId); + final Set<Command> commands = new TreeSet<Command>(); + commands.addAll(Arrays.asList(commandService.getDefinedCommands())); + for (Command command : commands) { + Category currentCategory = null; + try { + currentCategory = command.getCategory(); + } catch (NotDefinedException e) { + Activator.log.debug(e.getLocalizedMessage()); + continue; + } + if (command.isDefined() && category.equals(currentCategory)) { + final IHandler handler = command.getHandler(); + if (handler instanceof AbstractHandler) { + + // required!?!?! in some case can avoid the message for handler conflicting (ex : Allocate in SysML NatTable Allocation + ((AbstractHandler) handler).setEnabled(null); + boolean isEnabled = handler.isEnabled(); + command.setEnabled(null); + ((AbstractHandler) handler).setEnabled(null); + + isEnabled = handler.isEnabled(); + try { + if (isEnabled) { + CommandContributionItemParameter p = new CommandContributionItemParameter(serviceLocator, "", command.getId(), SWT.PUSH); //$NON-NLS-1$ + p.label = command.getDescription(); + p.icon = EclipseCommandUtils.getCommandIcon(command); + CommandContributionItem item = new CommandContributionItem(p); + items.add(item); + } + } catch (NotDefinedException e) { + Activator.log.debug(e.getLocalizedMessage()); + } + } + } + } + return items; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/DisplayUtils.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/DisplayUtils.java new file mode 100644 index 00000000000..27bc70bb5bc --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/DisplayUtils.java @@ -0,0 +1,47 @@ +/***************************************************************************** + * Copyright (c) 2010 CEA LIST. + * + * 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: + * Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.ui.Activator; + + +/** + * Util class for display in Papyrus (label providers, etc...) + * + * @deprecated Use the LabelProviderService instead + */ +@Deprecated +public class DisplayUtils { + + /** + * Gets the shared label provider. + * + * @return Get the current {@link ILabelProvider} or <code>null</code> if + * not found + */ + public static ILabelProvider getLabelProvider() { + try { + ServicesRegistry registry = EditorUtils.getServiceRegistry(); + return registry == null ? null : registry.getService(ILabelProvider.class); + } catch (IllegalStateException e) { + // Registry can't be found, do nothing. + Activator.log.error(e); + } catch (ServiceException e) { + Activator.log.error(e); + } + return null; + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EclipseCommandUtils.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EclipseCommandUtils.java new file mode 100644 index 00000000000..ace809b60a5 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EclipseCommandUtils.java @@ -0,0 +1,137 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import java.util.Collection; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.core.commands.Category; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.State; +import org.eclipse.core.commands.common.NotDefinedException; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandImageService; +import org.eclipse.ui.commands.ICommandService; + +/** + * This class provides useful methods to manipulate Eclipse Command + * + * @author vl222926 + * + */ +public class EclipseCommandUtils { + + private EclipseCommandUtils() { + // to prevent instanciation + } + + public static final String TOGGLE_STATE = "org.eclipse.ui.commands.toggleState"; //$NON-NLS-1$ + + public static final String RADIO_STATE = "org.eclipse.ui.commands.radioState"; //$NON-NLS-1$ + + public static final String DELETE_COMMAND = "org.eclipse.ui.edit.delete"; //$NON-NLS-1$ + + /** + * + * @param categoryId + * a category id + * @return + * all commands defined for this category + */ + public static final Collection<Command> getAllExistingCommandsInCategory(final String categoryId) { + final Set<Command> commands = new TreeSet<Command>(); + final ICommandService commandService = PlatformUI.getWorkbench().getService(ICommandService.class); + final Category category = commandService.getCategory(categoryId); + for (final Command command : commandService.getDefinedCommands()) { + Category currentCategory = null; + try { + currentCategory = command.getCategory(); + } catch (NotDefinedException e) { + Activator.log.debug(e.getLocalizedMessage()); + continue; + } + if (/* command.isDefined() && */category.equals(currentCategory)) { + commands.add(command); + } + } + return commands; + } + + /** + * + * @param command + * an Eclipse command + * @return + * the image descriptor associated to this command + */ + public static final ImageDescriptor getCommandIcon(final Command command) { + final IWorkbench workbench = PlatformUI.getWorkbench(); + final ICommandImageService service = workbench.getService(ICommandImageService.class); + final ImageDescriptor imageDescriptor = service.getImageDescriptor(command.getId()); + return imageDescriptor; + } + + /** + * + * @param command + * an eclipse command + * @param newValue + * the new boolean value to set to the state of this command + */ + public static final void updateToggleCommandState(final org.eclipse.core.commands.Command command, final boolean newValue) { + if (command != null) { + final State state = command.getState(TOGGLE_STATE); + if (state != null) { + state.setValue(newValue); + } + } + } + + /** + * + * @param command + * an eclipse command + * @param newValue + * the new value to set to the state of this command + */ + public static final void updateRadioCommandState(final org.eclipse.core.commands.Command command, final Object newValue) { + if (command != null) { + final State state = command.getState(RADIO_STATE); + if (state != null) { + state.setValue(newValue); + } + } + } + + /** + * + * @return + * the eclipse command service + */ + public static final ICommandService getCommandService() { + IWorkbench wb = PlatformUI.getWorkbench(); + if (wb != null) { + IWorkbenchWindow ww = wb.getActiveWorkbenchWindow(); + if (ww != null) { + return ww.getService(ICommandService.class); + } + } + return null; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EditorHelper.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EditorHelper.java new file mode 100644 index 00000000000..01802f176fa --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EditorHelper.java @@ -0,0 +1,71 @@ +/***************************************************************************** + * Copyright (c) 2012 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * + * a helper for the Editor + * + */ +public class EditorHelper { + + private EditorHelper() { + // nothing to do + } + + /** + * + * @return + * the current editor or <code>null</code> if not found + */ + public static final IEditorPart getCurrentEditor() { + final IWorkbench workbench = PlatformUI.getWorkbench(); + if (workbench != null) { + final IWorkbenchWindow activeWorkbench = workbench.getActiveWorkbenchWindow(); + if (activeWorkbench != null) { + final IWorkbenchPage activePage = activeWorkbench.getActivePage(); + if (activePage != null) { + return activePage.getActiveEditor(); + } + } + } + return null; + } + + /** + * + * @return + * the current active part or <code>null</code> if not found + */ + public static final IWorkbenchPart getActivePart() { + final IWorkbench workbench = PlatformUI.getWorkbench(); + if (workbench != null) { + final IWorkbenchWindow activeWorkbench = workbench.getActiveWorkbenchWindow(); + if (activeWorkbench != null) { + final IWorkbenchPage activePage = activeWorkbench.getActivePage(); + if (activePage != null) { + return activePage.getActivePart(); + } + } + } + return null; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EditorUtils.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EditorUtils.java new file mode 100644 index 00000000000..6918f42b92d --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/EditorUtils.java @@ -0,0 +1,721 @@ +/***************************************************************************** + * Copyright (c) 2008, 2013 CEA LIST 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: + * Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation + * <a href="mailto:thomas.szadel@atosorigin.com">Thomas Szadel</a>: Code simplification and NPE + * management. + * Christian W. Damus (CEA LIST) - API for determining URI of a resource in an editor + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.emf.common.ui.URIEditorInput; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.papyrus.infra.core.editor.BackboneException; +import org.eclipse.papyrus.infra.core.resource.sasheditor.DiModelUtils; +import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider; +import org.eclipse.papyrus.infra.core.sasheditor.di.contentprovider.DiSashModelMngr; +import org.eclipse.papyrus.infra.core.sasheditor.editor.IPage; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServiceNotFoundException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.DiResourceSet; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.papyrus.infra.ui.editor.CoreMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; + +/** + * Set of utility methods for the CoreEditor. <br> + * WARNING : Some of these methods rely on + * PlatformUI.getWorkbench().getActiveWorkbenchWindow()getActivePage() to lookup + * for shared objects owned by the main editor. This doesn't work during the + * initialization of the main editor because the main editor is not yet + * registered in the Eclipse workbench. This can lead to a null or an exception, + * and sometime this can lead to getting the shared object of another main + * editor ! + * + * @author cedric dumoulin + * @author <a href="mailto:thomas.szadel@atosorigin.com">Thomas Szadel</a> + */ +// FIXME throws Exception (eg: NotFoundException) instead of null +public class EditorUtils { + + /** + * Gets the opened multi-diagram editors. + * + * @return The opened {@link IMultiDiagramEditor} or null if an error + * occured. + */ + public static IMultiDiagramEditor[] getMultiDiagramEditors() { + // Lookup ServiceRegistry + IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (workbenchWindow == null) { + return null; + } + IWorkbenchPage page = workbenchWindow.getActivePage(); + if (page == null) { + return null; + } + List<IMultiDiagramEditor> list = new ArrayList<IMultiDiagramEditor>(); + for (IEditorReference editorRef : page.getEditorReferences()) { + IEditorPart editorPart = editorRef.getEditor(false); + if (editorPart instanceof IMultiDiagramEditor) { + list.add((IMultiDiagramEditor) editorPart); + } + } + return list.toArray(new IMultiDiagramEditor[list.size()]); + } + + /** + * Returns the editors that are related to to given file.<BR> + * + * @param file + * The file (model, di or notation). + * @return The associated editors. + */ + public static IMultiDiagramEditor[] getRelatedEditors(IFile file) { + // Get the DI file + IFile diFile = DiModelUtils.getRelatedDiFile(file); + if (diFile == null || !diFile.exists()) { + return new IMultiDiagramEditor[0]; + } + + IMultiDiagramEditor[] openedEditors = EditorUtils.getMultiDiagramEditors(); + if (openedEditors == null) { + return new IMultiDiagramEditor[0]; + } + List<IMultiDiagramEditor> list = new ArrayList<IMultiDiagramEditor>(openedEditors.length); + + for (IMultiDiagramEditor editorPart : openedEditors) { + if (editorPart.getEditorInput() instanceof IFileEditorInput && diFile.equals(((IFileEditorInput) editorPart.getEditorInput()).getFile())) { + list.add(editorPart); + } + } + return list.toArray(new IMultiDiagramEditor[list.size()]); + } + + /** + * Create an instance of IPageMngr acting on the provided resource. This + * instance is suitable to add, remove, close or open diagrams. + * + * @param diResource + * @return The non transactional implementation of IPageMngr + */ + public static IPageManager getIPageMngr(Resource diResource) { + return DiSashModelMngr.createIPageMngr(diResource); + } + + + // ////////////////////////////////////////// + // The following methods are deprecated. They have been replaced by specific + // implementations of ServiceUtils (e.g. ServiceUtilsForHandlers, ServiceUtilsForEObject), + // which depend on a specific context (ExecutionEvent, EObject, ...) instead of + // the active editor + // ////////////////////////////////////////// + + /** + * Gets the {@link IMultiDiagramEditor} interface of the a Eclipse active + * editor, if possible, or null if not possible. <br> + * WARNING - This method doesn't work during the initialization of the main + * editor. See note in class doc. <br> + * This method return null if there is no active editor, or if the editor is + * not instance of IMultiDiagramEditor. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * In any case, a check should be done on the returned value that can be + * null. Usage of this method is discouraged. Use {@link #getMultiDiagramEditorChecked()} instead. + * + * + * @return Get the current {@link IMultiDiagramEditor} or null if not found. + */ + public static IMultiDiagramEditor getMultiDiagramEditor() { + // Lookup ServiceRegistry + IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (workbenchWindow == null) { + return null; + } + IWorkbenchPage page = workbenchWindow.getActivePage(); + if (page == null) { + return null; + } + IEditorPart editor = page.getActiveEditor(); + if (editor instanceof IMultiDiagramEditor) { + return (IMultiDiagramEditor) editor; + } else { + return null; + } + } + + /** + * Lookup the currently active Diagram from the Papyrus editor. Return the + * current Diagram or null if none is active. <br> + * WARNING - This method doesn't work during the initialization of the main + * editor. See note in class doc. <br> + * This method return null if the ServicesRegistry can not be found. <br> + * TODO This method introduce dependency on GMF. It can be moved to a GMF + * plugin. + * + * @return The active diagram or null if not found. + * + * @deprecated The core do make suppositions about the type of nested + * Editors, GMF stuff should be moved in GMF projects. In many + * case, {@link #lookupActiveNestedIEditor()} can be used. + */ + // @Deprecated + // public static Diagram lookupEditorActiveDiagram() { + // DiagramEditor diagEditor = lookupActiveDiagramEditor(); + // return diagEditor == null ? null : diagEditor.getDiagram(); + // } + + /** + * Lookup the currently active Diagram from the Papyrus editor. Return the + * current Diagram or null if none is active. <br> + * WARNING - This method doesn't work during the initialization of the main + * editor. See note in class doc. <br> + * This method return null if the ServicesRegistry can not be found. <br> + * TODO This method introduce dependency on GMF. It can be moved to a GMF + * plugin. + * + * @return the active diagram editor or null if not found. + * + * @deprecated The core do make suppositions about the type of nested + * Editors, GMF stuff should be moved in GMF projects. In many + * case, {@link #lookupActiveNestedIEditor()} can be used. + */ + // @Deprecated + // public static DiagramEditor lookupActiveDiagramEditor() { + // // Get the active page within the sashcontainer + // IEditorPart activeEditor = lookupActiveNestedIEditor(); + // // Check if it is a GMF DiagramEditor + // if(activeEditor instanceof DiagramEditor) { + // return ((DiagramEditor)activeEditor); + // } else { + // // Not found + // return null; + // } + // + // } + + /** + * Lookup the currently active {@link IEditorPart} from the Papyrus editor. + * Return the current nested editor part, or null if it can not be found. <br> + * WARNING - This method doesn't work during the initialization of the main + * editor. See note in class doc. <br> + * This method return null if the ServicesRegistry can not be found. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * In any case, a check should be done on the returned value that can be + * null. An alternative is to use + * serviceRegistry.getService(ISashWindowsContainer + * .class).getActiveEditor(); <br> + * It is preferable to retrieve the ServiceRegistry from elsewhere whenever + * it is possible. <br> + * + * + * @return + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + public static IEditorPart lookupActiveNestedIEditor() { + // Get the sashwindow container + ISashWindowsContainer container = getSashWindowContainer(); + // Get the active page within the sashcontainer + return container == null ? null : container.getActiveEditor(); + } + + /** + * Lookup the currently active IEditor in the SashSystem. If the currently + * eclipse active editor doesn't contains a {@link ISashWindowsContainer}, + * return null. If the current SashSystem page is not a IEditor, return + * null. <br> + * WARNING - This method doesn't work during the initialization of the main + * editor. See note in class doc. <br> + * This method return null if the ServicesRegistry can not be found. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * In any case, a check should be done on the returned value that can be + * null. An alternative is to use + * serviceRegistry.getService(ISashWindowsContainer + * .class).getActiveSashWindowsPage(); <br> + * It is preferable to retrieve the ServiceRegistry from elsewhere whenever + * it is possible. + * + * @return + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + public static IPage lookupActiveNestedPage() { + + // Get the sashwindow container + ISashWindowsContainer container = getSashWindowContainer(); + // Get the active page within the sashcontainer + return container == null ? null : container.getActiveSashWindowsPage(); + } + + /** + * + * @return + */ + private static ISashWindowsContainer getSashWindowContainer() { + + try { + return getServiceRegistryChecked().getService(ISashWindowsContainer.class); + } catch (ServiceException e) { + // The contract says that we return null if not found + return null; + } + } + + /** + * Gets the di resource set. + * + * @return Get the current {@link DiResourceSet} or null if not found. + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + public static DiResourceSet getDiResourceSet() { + try { + ServicesRegistry registry = getServiceRegistry(); + return registry == null ? null : registry.getService(DiResourceSet.class); + } catch (ServiceException e) { + Activator.log.error(e); + } + return null; + } + + /** + * Gets the {@link TransactionalEditingDomain} of the current active Eclipse + * Editor. This method should be used only when it is sure that the active + * editor exist, and that you want the EditingDomain of this editor. <br> + * This method return null if the TransactionalEditingDomain can not be + * found. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * In any case, a check should be done on the returned value that can be + * null. An alternative is to use {@link #getTransactionalEditingDomainChecked()} and to catch the + * exception. <br> + * It is preferable to use {@link #getTransactionalEditingDomain(ServicesRegistry)} whenever it is + * possible. <br> + * In GMF EditParts or EditPolicies, the ServiceRegistry can be retrieved + * with methods from + * org.eclipse.papyrus.uml.diagram.common.util.DiagramCoreServiceUtils <br> + * WARNING: This method can return null if there is no Active Editor. This + * happen during the editor initialization, especially when there is no + * other editor opened. + * + * @return Get the current {@link TransactionalEditingDomain} or null if not + * found + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + public static TransactionalEditingDomain getTransactionalEditingDomain() { + try { + ServicesRegistry registry = getServiceRegistry(); + return registry == null ? null : registry.getService(TransactionalEditingDomain.class); + } catch (IllegalStateException e) { + // Registry can't be found, do nothing. + } catch (ServiceException e) { + Activator.log.error(e); + } + return null; + } + + /** + * Gets the {@link TransactionalEditingDomain} of the current active Eclipse + * Editor. This method should be used only when it is sure that the active + * editor exist, and that you want the EditingDomain of this editor. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * It is preferable to use {@link #getTransactionalEditingDomain(ServicesRegistry)} whenever it is + * possible. <br> + * This method throw a {@link ServiceException} if the + * TransactionalEditingDomain can not be found. <br> + * In GMF EditParts or EditPolicies, the ServiceRegistry can be retrieved + * with methods from + * org.eclipse.papyrus.uml.diagram.common.util.DiagramCoreServiceUtils + * + * + * WARNING: This method throws an exception when no Active Editor is found. + * This happen during the editor initialization, especially when there is no + * other editor opened. + * + * @return Get the current {@link TransactionalEditingDomain} + * @throws ServiceException + * @throws ServiceNotFoundException + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + public static TransactionalEditingDomain getTransactionalEditingDomainChecked() throws ServiceException { + try { + ServicesRegistry registry = getServiceRegistryChecked(); + return registry.getService(TransactionalEditingDomain.class); + } catch (IllegalStateException e) { + throw new ServiceException(e); + } catch (Exception e) { + throw new ServiceException(e); + } + } + + /** + * Gets the {@link TransactionalEditingDomain} registered in the {@link ServicesRegistry}. + * + * @param servicesRegistry + * @return + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * </ul> + */ + @Deprecated + public static TransactionalEditingDomain getTransactionalEditingDomain(ServicesRegistry registry) { + try { + return registry.getService(TransactionalEditingDomain.class); + } catch (IllegalStateException e) { + // Registry can't be found, do nothing. + } catch (ServiceException e) { + Activator.log.error(e); + } + return null; + } + + /** + * Gets the {@link TransactionalEditingDomain} registered in the {@link ServicesRegistry}. + * + * @param servicesRegistry + * @return + * @throws ServiceException + * If the TransactionalEditingDomain can not be found. + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * </ul> + */ + @Deprecated + public static TransactionalEditingDomain getTransactionalEditingDomainChecked(ServicesRegistry registry) throws ServiceException { + return registry.getService(TransactionalEditingDomain.class); + } + + /** + * Get the {@link ServicesRegistry}of the currently active eclipse editor. <br> + * WARNING - This method doesn't work during the initialization of the main + * editor. See note in class doc. <br> + * This method return null if the ServicesRegistry can not be found. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * In any case, a check should be done on the returned value that can be + * null. An alternative is to use {@link #getServiceRegistryChecked()} and + * to catch the exception. <br> + * It is preferable to retrieve the ServiceRegistry from elsewhere whenever + * it is possible. <br> + * In GMF EditParts or EditPolicies, the ServiceRegistry can be retrieved + * with methods from + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF + * + * <br> + * WARNING: This method can return null if there is no Active Editor. This + * happen during the editor initialization, especially when there is no + * other editor opened. + * + * @return The {@link ServicesRegistry} or null if not found. + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + static public ServicesRegistry getServiceRegistry() { + // Lookup ServiceRegistry + IMultiDiagramEditor editor = getMultiDiagramEditor(); + return editor == null ? null : (ServicesRegistry) editor.getAdapter(ServicesRegistry.class); + } + + /** + * Get the service registry of the currently active main editor. <br> + * WARNING - This method doesn't work during the initialization of the main + * editor. See note in class doc. + * + * @return The {@link ServicesRegistry} or null if not found. + * @throws ServiceException + * If an error occurs. + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + static public ServicesRegistry getServiceRegistryChecked() throws ServiceException { + // Lookup ServiceRegistry + IMultiDiagramEditor editor = getMultiDiagramEditor(); + if (editor == null) { + throw new ServiceException("Can't get ServiceRegistry"); //$NON-NLS-1$ + } + + return editor.getAdapter(ServicesRegistry.class); + } + + /** + * Get the ISashWindowsContentProvider of the active Eclipse Editor, if + * possible. <br> + * This method return null if the ServiceRegistry can not be found or if an + * error occur. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * In any case, a check should be done on the returned value that can be + * null. <br> + * + * @return the ISashWindowsContentProvider from the main editor or null if + * not found. + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + static public ISashWindowsContentProvider getISashWindowsContentProvider() { + + try { + return getServiceRegistryChecked().getService(ISashWindowsContentProvider.class); + } catch (ServiceException e) { + // The contract says that we return null if not found + return null; + } + } + + /** + * Get the ISashWindowsContentProvider of the active Eclipse Editor, if + * possible. <br> + * This method return null if the ServiceRegistry can not be found or if an + * error occur. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * This method should not be used during the editor initialization phase. <br> + * In any case, a check should be done on the returned value that can be + * null. + * + * @return the ISashWindowsContentProvider from the main editor or null if + * not found. + * @deprecated Check + * modeling/org.eclipse.mdt.papyrus/trunk/doc/DevelopperDocuments + * /cookbook/PapyrusCookBook.odt and use one of the replacement: + * <ul> + * <li>org.eclipse.papyrus.infra.core.utils.ServiceUtils</li> + * <li> + * org.eclipse.papyrus.uml.diagram.common.util.ServiceUtilsForGMF</li> + * <li> + * org.eclipse.papyrus.infra.ui.util.ServiceUtilsForActionHandlers (to be used with care !)</li> + * </ul> + */ + @Deprecated + public static IPageManager getIPageMngr() { + + try { + return getServiceRegistryChecked().getService(IPageManager.class); + } catch (ServiceException e) { + // The contract says that we return null if not found + return null; + } + } + + /** + * Get the Eclipse ActiveEditor. + * + * @return The active {@link CoreMultiDiagramEditor} or null if not found. + * @deprecated Use {@link EditorUtils#getMultiDiagramEditor()} + */ + @Deprecated + protected static IEditorPart getWorkbenchActiveEditor() { + IMultiDiagramEditor editorPart = getMultiDiagramEditor(); + if (editorPart instanceof CoreMultiDiagramEditor) { + return editorPart; + } else { + return null; + } + } + + /** + * Gets the {@link IMultiDiagramEditor} interface of the a Eclipse active + * editor, if possible, or throw an exception if not possible. <br> + * WARNING - This method throw an exception during the initialization of the + * main editor. This method throws an exception if there is no active + * editor, or if the editor is not instance of IMultiDiagramEditor. <br> + * This method is designed to be used by ui actions that interact with the + * active editor. <br> + * + * + * @return Get the current {@link IMultiDiagramEditor} or null if not found. + * @throws BackboneException + * If it is not possible to get an instanceof {@link IMultiDiagramEditor} + */ + public static IMultiDiagramEditor getMultiDiagramEditorChecked() throws BackboneException { + IEditorPart editor; + try { + editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(); + } catch (NullPointerException e) { + // Can't get the active editor + throw new BackboneException("Can't get the current Eclipse Active Editor: There is no active editor at this time."); //$NON-NLS-1$ + } + + if (editor instanceof IMultiDiagramEditor) { + return (IMultiDiagramEditor) editor; + } else { + throw new BackboneException("Can't get an Active Editor instance of IMultiDiagramEditor. (actual type:" + editor.getClass().getName() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * Obtains the URI of the EMF resource identified by the given editor reference. + * + * @param editorRef + * an editor reference + * + * @return the best-effort URI of the resource that it edits, or {@code null} if it could not be determined, + * including the case when the editor input could not be obtained from the reference + */ + public static URI getResourceURI(IEditorReference editorRef) { + try { + return getResourceURI(editorRef.getEditorInput()); + } catch (PartInitException e) { + Activator.log.error("Could not obtain editor input from editor reference.", e); //$NON-NLS-1$ + return null; + } + } + + /** + * Obtains the URI of the EMF resource edited by the given {@code editor}. + * + * @param editor + * an open editor + * + * @return the best-effort URI of the resource that it edits, or {@code null} if it could not be determined, + * such as if the editor input could not be obtained from the editor + */ + public static URI getResourceURI(IEditorPart editor) { + return getResourceURI(editor.getEditorInput()); + } + + /** + * Obtains the URI of the EMF resource identified by the given editor input. + * + * @param editorInput + * an editor input + * + * @return the best-effort URI of the resource that it edits, or {@code null} if it could not be determined + */ + public static URI getResourceURI(IEditorInput editorInput) { + URI result = null; + + if (editorInput instanceof IFileEditorInput) { + result = URI.createPlatformResourceURI(((IFileEditorInput) editorInput).getFile().getFullPath().toString(), true); + } else if (editorInput instanceof URIEditorInput) { + result = ((URIEditorInput) editorInput).getURI(); + } else if (editorInput instanceof IURIEditorInput) { + result = URI.createURI(((IURIEditorInput) editorInput).getURI().toASCIIString(), true); + } else if (editorInput != null) { + // desperation + Object adapter = editorInput.getAdapter(URI.class); + if (adapter instanceof URI) { + result = (URI) adapter; + } + } + + return result; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ICallableWithProgress.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ICallableWithProgress.java new file mode 100644 index 00000000000..08d3f1d857b --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ICallableWithProgress.java @@ -0,0 +1,50 @@ +/***************************************************************************** + * Copyright (c) 2014, 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.util; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Callable; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.operation.IRunnableContext; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.papyrus.infra.tools.util.IProgressCallable; + +/** + * The {@link Callable} analogue of an {@link IRunnableWithProgress}. + * + * @deprecated Use the {@link IProgressCallable} API, instead. + */ +@Deprecated +public interface ICallableWithProgress<V> extends IProgressCallable<V> { + /** + * Invokes me in a runnable context with a progress monitor. + * + * @param monitor + * the progress monitor to use to display progress and receive + * requests for cancellation + * @exception InvocationTargetException + * if the run method must propagate a checked exception, + * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically + * wrapped in an <code>InvocationTargetException</code> by the calling context + * @exception InterruptedException + * if the operation detects a request to cancel, + * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing <code>InterruptedException</code> + * + * @see UIUtil#call(IRunnableContext, ICallableWithProgress) + * @see IRunnableContext#run + */ + @Override + V call(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException; +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/LocalMemento.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/LocalMemento.java new file mode 100644 index 00000000000..309542ee425 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/LocalMemento.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2014 CEA 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: + * Christian W. Damus (CEA) - Initial API and implementation + * + */ +package org.eclipse.papyrus.infra.ui.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.ui.IMemento; + + +/** + * Invocation handler for the dynamic local memento implementation. + */ +class LocalMemento implements InvocationHandler { + + private static final Class<?>[] INTERFACES = { IMemento.class }; + + private static final Map<Method, Method> delegates = createDelegates(); + + private final String type; + + private final String id; + + private final List<IMemento> children = new ArrayList<IMemento>(); + + private final Map<String, Object> attributes = new HashMap<String, Object>(); + + private String textData; + + LocalMemento(String type, String id) { + super(); + + this.type = type; + this.id = id; + } + + static IMemento createMemento(String type, String id) { + LocalMemento handler = new LocalMemento(type, id); + return (IMemento) Proxy.newProxyInstance(LocalMemento.class.getClassLoader(), INTERFACES, handler); + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object result = null; + + Method implementation = delegates.get(method); + if (implementation == null) { + throw new UnsupportedOperationException("dynamic proxy handler does not understand " + method.getName()); //$NON-NLS-1$ + } else { + result = implementation.invoke(this, args); + } + + return result; + } + + @API + String getType() { + return type; + } + + @API + String getID() { + return id; + } + + @API + IMemento createChild(String type) { + return createChild(type, null); + } + + @API + IMemento createChild(String type, String id) { + IMemento result = createMemento(type, id); + children.add(result); + return result; + } + + @API + IMemento getChild(String type) { + IMemento result = null; + for (IMemento next : children) { + if (type.equals(next.getType())) { + result = next; + break; + } + } + return result; + } + + @API + IMemento[] getChildren() { + return children.toArray(new IMemento[children.size()]); + } + + @API + IMemento[] getChildren(String type) { + List<IMemento> result = new ArrayList<IMemento>(children.size()); + for (IMemento next : children) { + if (type.equals(next.getType())) { + result.add(next); + } + } + return result.toArray(new IMemento[result.size()]); + } + + @API + Float getFloat(String key) { + return coerce(attributes.get(key), Float.class); + } + + private <T> T coerce(Object value, Class<T> type) { + Object result; + + if (value == null) { + result = value; + } else if (type.isInstance(value)) { + result = value; + } else if (Number.class.isAssignableFrom(type) && (value instanceof Number)) { + Number number = (Number) value; + if (type == Integer.class) { + result = number.intValue(); + } else if (type == Float.class) { + result = number.floatValue(); + } else { + throw new IllegalArgumentException("unsupported numeric type: " + type.getSimpleName()); //$NON-NLS-1$ + } + } else if (Number.class.isAssignableFrom(type) && (value instanceof String)) { + String string = (String) value; + if (type == Integer.class) { + result = Integer.valueOf(string); + } else if (type == Float.class) { + result = Float.valueOf(string); + } else { + throw new IllegalArgumentException("unsupported numeric type: " + type.getSimpleName()); //$NON-NLS-1$ + } + } else if (type == Boolean.class) { + // We know the value isn't a Boolean, otherwise we would have handled it already + if (value instanceof String) { + result = Boolean.valueOf((String) value); + } else { + throw new IllegalArgumentException("unsupported boolean conversion from type: " + ((value == null) ? "null" : value.getClass().getSimpleName())); //$NON-NLS-1$ + } + } else if (type == String.class) { + result = String.valueOf(value); + } else { + throw new IllegalArgumentException("unsupported attribute type: " + type.getSimpleName()); //$NON-NLS-1$ + } + + return type.cast(result); + } + + @API + Integer getInteger(String key) { + return coerce(attributes.get(key), Integer.class); + } + + @API + String getString(String key) { + return coerce(attributes.get(key), String.class); + } + + @API + Boolean getBoolean(String key) { + return coerce(attributes.get(key), Boolean.class); + } + + @API + String getTextData() { + return textData; + } + + @API + String[] getAttributeKeys() { + return attributes.keySet().toArray(new String[attributes.size()]); + } + + @API + void putFloat(String key, float value) { + attributes.put(key, value); + } + + @API + void putInteger(String key, int value) { + attributes.put(key, value); + } + + @API + void putString(String key, String value) { + attributes.put(key, value); + } + + @API + void putBoolean(String key, boolean value) { + attributes.put(key, value); + } + + @API + void putTextData(String data) { + textData = data; + } + + private boolean isLocalMemento(IMemento memento) { + return (memento != null) && Proxy.isProxyClass(memento.getClass()) && ((Proxy.getInvocationHandler(memento) instanceof LocalMemento)); + } + + @API + void putMemento(IMemento memento) { + if (!isLocalMemento(memento)) { + throw new IllegalArgumentException("memento is not a local memento"); //$NON-NLS-1$ + } + children.add(memento); + } + + @Override + @API(owner = Object.class) + public String toString() { + StringBuilder result = new StringBuilder(); + + append(result, 0); + + return result.toString(); + } + + private void append(StringBuilder buf, int depth) { + // Indent + for (int i = 0; i < depth; i++) { + buf.append(" "); //$NON-NLS-1$ + } + + buf.append("LocalMemento(");//$NON-NLS-1$ + buf.append(type); + if (id != null) { + buf.append('[').append(id).append(']'); + } + buf.append(") ").append(attributes); //$NON-NLS-1$ + buf.append('\n'); + + final int nextDepth = depth + 1; + for (IMemento next : children) { + ((LocalMemento) Proxy.getInvocationHandler(next)).append(buf, nextDepth); + } + } + + private static Map<Method, Method> createDelegates() { + Map<Method, Method> result = new HashMap<Method, Method>(); + + for (Method implementation : LocalMemento.class.getDeclaredMethods()) { + if (implementation.isAnnotationPresent(API.class)) { + try { + Method api = implementation.getAnnotation(API.class).owner().getMethod(implementation.getName(), implementation.getParameterTypes()); + result.put(api, implementation); + } catch (NoSuchMethodException e) { + throw new LinkageError("Incompatible IMemento API change: " + implementation.getName()); + } + } + } + + return result; + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + private @interface API { + + Class<?> owner() default IMemento.class; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/PapyrusImageUtils.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/PapyrusImageUtils.java new file mode 100644 index 00000000000..ee0d9fb314f --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/PapyrusImageUtils.java @@ -0,0 +1,67 @@ +/***************************************************************************** + * Copyright (c) 2010 CEA LIST. + * + * 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: + * Tristan Faure (ATOS ORIGIN INTEGRATION) tristan.faure@atosorigin.com - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import java.io.IOException; +import java.net.URL; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.papyrus.infra.ui.Activator; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +/** + * Services to access to Papyrus images + * + * @author tristan faure + * + */ +public class PapyrusImageUtils { + + private static final String default_icon_32 = "/icons/papyrus/32x32/Papyrus_32x32_t.gif"; //$NON-NLS-1$ + + private static final String default_icon = "/icons/papyrus/Papyrus.gif"; //$NON-NLS-1$ + + /** + * get the default icon for Papyrus the image does not have to be disposed + * as it is registered in an ImageRegistry + * + * @return the Image + */ + public static Image getDefaultIcon() { + return getIcon(default_icon); + } + + /** + * get the default icon 32x32 for Papyrus the image does not have to be + * disposed as it is registered in an ImageRegistry + * + * @return the Image + */ + public static Image getDefaultIcon32() { + return getIcon(default_icon_32); + } + + private static Image getIcon(String path) { + String key = Activator.PLUGIN_ID + path; + Image result = JFaceResources.getImageRegistry().get(key); + if (result == null) { + URL url = Activator.getDefault().getBundle().getEntry(path); + try { + result = new Image(Display.getDefault(), url.openStream()); + JFaceResources.getImageRegistry().put(key, result); + } catch (IOException e) { + } + } + return result; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/SelectionHelper.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/SelectionHelper.java new file mode 100644 index 00000000000..5132a841f4c --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/SelectionHelper.java @@ -0,0 +1,119 @@ +/***************************************************************************** + * Copyright (c) 2014 CEA LIST 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: + * CEA LIST - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.ui.ISelectionService; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * @author VL222926 + * + */ +public class SelectionHelper { + + /** + * Constructor. + * + */ + private SelectionHelper() { + // to avoid instanciation + } + + /** + * + * @return + * the selection service or <code>null</code> if not found + * + */ + public static final ISelectionService getSelectionService() { + IWorkbench wb = PlatformUI.getWorkbench(); + if (wb != null) { + // don't work + // ISelectionService s1 = (ISelectionService) wb.getService(ISelectionService.class); + IWorkbenchWindow ww = wb.getActiveWorkbenchWindow(); + if (ww != null) { + return (ISelectionService) ww.getService(ISelectionService.class); + } + } + return null; + } + + /** + * + * @return + * the current selection or an empty selection. can't be <code>null</code> + */ + public static final ISelection getCurrentSelection() { + ISelectionService selectionService = getSelectionService(); + if (selectionService != null) { + ISelection currentSelection = selectionService.getSelection(); + if (currentSelection != null) { + return currentSelection; + } + } + return StructuredSelection.EMPTY; + } + + /** + * + * @param viewId + * the id of the view for which we want the selection + * @return + * the current selection for the view, the returned value can't be <code>null</code> + */ + public static final ISelection getCurrentSelection(String viewId) { + ISelectionService selectionService = getSelectionService(); + if (selectionService != null) { + ISelection currentSelection = selectionService.getSelection(viewId); + if (currentSelection != null) { + return currentSelection; + } + } + return StructuredSelection.EMPTY; + } + + /** + * + * @return + * a structured selection. + * the returned value can't be <code>null</code> + */ + public static final IStructuredSelection getCurrentStructuredSelection() { + ISelection selection = getCurrentSelection(); + if (selection instanceof IStructuredSelection) { + return (IStructuredSelection) selection; + } + return StructuredSelection.EMPTY; + } + + /** + * + * @param viewId + * the id of the view for which we want the selection + * @return + * the current selection for the view, the returned value can't be <code>null</code> + */ + public static final IStructuredSelection getCurrentStructuredSelection(String viewId) { + ISelection selection = getCurrentSelection(viewId); + if (selection instanceof IStructuredSelection) { + return (IStructuredSelection) selection; + } + return StructuredSelection.EMPTY; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForActionHandlers.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForActionHandlers.java new file mode 100644 index 00000000000..917a41e8688 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForActionHandlers.java @@ -0,0 +1,149 @@ +/***************************************************************************** + * Copyright (c) 2010, 2016 LIFL, CEA LIST, Christian W. Damus, 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: + * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServiceNotFoundException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.papyrus.infra.ui.lifecycleevents.ILifeCycleEventsProvider; +import org.eclipse.ui.IEditorPart; + +/** + * Set of utility methods for accessing core Services. This class provide + * methods to access the Papyrus well known services. This class is designed to + * be used from ui Action Handlers or from any code relying on the Eclipse + * Active Editor. <br> + * All methods from this class rely on the Eclipse Active Editor, which should + * be an instance of {@link IMultiDiagramEditor}. If this is not the case, + * methods throw an exception {@link ServiceException}. + * + * @author cedric dumoulin + * + * @deprecated 0.10: Use org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForHandlers instead + */ +@Deprecated +public class ServiceUtilsForActionHandlers extends AbstractServiceUtils<Void> { + + private ServiceUtilsForActionHandlers() { + // Singleton + } + + private final static ServiceUtilsForActionHandlers instance = new ServiceUtilsForActionHandlers(); + + /** + * Get the singleton instance of the class. + * + * @return + */ + public static final ServiceUtilsForActionHandlers getInstance() { + return instance; + } + + @Override + public ServicesRegistry getServiceRegistry(Void from) throws ServiceException { + return getServiceRegistry(); + } + + /** + * Get the service registry from the specified parameter. + * + * @param from + * @return + */ + public ServicesRegistry getServiceRegistry() throws ServiceException { + ServicesRegistry serviceRegistry = getContextualServiceRegistry(); + if (serviceRegistry != null) { + return serviceRegistry; + } + + // Not found + throw new ServiceNotFoundException("Can't get the ServiceRegistry from current Eclipse Active Editor"); //$NON-NLS-1$ + } + + /** + * Gets the {@link TransactionalEditingDomain} registered in the {@link ServicesRegistry}. + * + * @return + * @throws ServiceException + * If an error occurs while getting the requested service. + */ + public TransactionalEditingDomain getTransactionalEditingDomain() throws ServiceException { + return getServiceRegistry().getService(TransactionalEditingDomain.class); + } + + /** + * Gets the {@link IPageManager} registered in the {@link ServicesRegistry}. + * + * @return + * @throws ServiceException + * If an error occurs while getting the requested service. + */ + public IPageManager getIPageManager() throws ServiceException { + return getServiceRegistry().getService(IPageManager.class); + } + + /** + * Gets the {@link IPageMngr} registered in the {@link ServicesRegistry}. + * + * @return + * @throws ServiceException + * If an error occurs while getting the requested service. + */ + public ModelSet getModelSet() throws ServiceException { + return getServiceRegistry().getService(ModelSet.class); + } + + /** + * Gets the {@link ILifeCycleEventsProvider} registered in the {@link ServicesRegistry}. + * + * @param from + * @return + * @throws ServiceException + * If an error occurs while getting the requested service. + */ + public ILifeCycleEventsProvider getILifeCycleEventsProvider() throws ServiceException { + return getServiceRegistry().getService(ILifeCycleEventsProvider.class); + } + + /** + * Gets the {@link ISashWindowsContainer} registered in the {@link ServicesRegistry}. + * + * @param from + * @return + * @throws ServiceException + * If an error occurs while getting the requested service. + */ + public ISashWindowsContainer getISashWindowsContainer() throws ServiceException { + return getServiceRegistry().getService(ISashWindowsContainer.class); + } + + /** + * Gets the {@link IEditorPart} of the currently nested active editor. + * + * @param from + * @return + * @throws ServiceException + * If an error occurs while getting the requested service. + */ + public IEditorPart getNestedActiveIEditorPart() throws ServiceException { + return getISashWindowsContainer().getActiveEditor(); + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForHandlers.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForHandlers.java new file mode 100644 index 00000000000..aec185cc30e --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForHandlers.java @@ -0,0 +1,122 @@ +/***************************************************************************** + * Copyright (c) 2012, 2016 CEA LIST, Christian W. Damus, 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServiceNotFoundException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.ISources; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartSite; + +/** + * ServicesUtils based on the Handler's ExecutionEvent + * + * It first tests the current selection, then the IWorkbenchPart on which the handler is executed. + * The IWorkbenchPart is expected to be adaptable to a ServiceRegistry. + * + * @author Camille Letavernier + * + * @see ServiceUtilsForSelection + */ +public class ServiceUtilsForHandlers extends AbstractServiceUtils<ExecutionEvent> { + + private ServiceUtilsForHandlers() { + // Singleton + } + + /** + * Gets the {@link IEditorPart} of the currently nested active editor. + * + * @param from + * @return + * @throws ServiceException + * If an error occurs while getting the requested service. + */ + public IEditorPart getNestedActiveIEditorPart(ExecutionEvent from) throws ServiceException { + return getService(ISashWindowsContainer.class, from).getActiveEditor(); + } + + @Override + public ServicesRegistry getServiceRegistry(ExecutionEvent from) throws ServiceException { + + if (from == null) { + return getContextualServiceRegistry(); + } + + Object context = from.getApplicationContext(); + + if (context instanceof IEvaluationContext) { + IEvaluationContext evaluationContext = (IEvaluationContext) context; + + // Search for the IWorkbenchPartSite from which the ExecutionEvent is sent (May be different that the Active one) + Object workbenchPartSite = evaluationContext.getVariable("org.eclipse.ui.IWorkbenchPartSite"); + if (workbenchPartSite instanceof IWorkbenchPartSite) { + IWorkbenchPartSite site = (IWorkbenchPartSite) workbenchPartSite; + Object registry = site.getAdapter(ServicesRegistry.class); + if (registry != null && registry instanceof ServicesRegistry) { + return (ServicesRegistry) registry; + } + + // Search for the IWorkbenchPart from which the ExecutionEvent is sent (May be different that the Active one) + IWorkbenchPart workbenchPart = site.getPart(); + registry = workbenchPart.getAdapter(ServicesRegistry.class); + if (registry != null && registry instanceof ServicesRegistry) { + return (ServicesRegistry) registry; + } + } + + Object selection = evaluationContext.getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME); + + ServicesRegistry registry; + + // Try to resolve the ServicesRegistry from the current selection + if (selection instanceof ISelection && !((ISelection) selection).isEmpty()) { + try { + registry = ServiceUtilsForSelection.getInstance().getServiceRegistry((ISelection) selection); + if (registry != null) { + return registry; + } + } catch (ServiceException ex) { + // Ignore and try another ServiceUtils + } + } + + // We couldn't retrieve the ServiceRegistry from the current selection. + + // Try to adapt the active part to the ServicesRegistry + IWorkbenchPart part = (IWorkbenchPart) evaluationContext.getVariable(ISources.ACTIVE_PART_NAME); + registry = (part).getAdapter(ServicesRegistry.class); + if (registry != null) { + return registry; + } + } + + throw new ServiceNotFoundException("The ServiceRegistry cannot be resolved"); //$NON-NLS-1$ + } + + + + public static ServiceUtilsForHandlers getInstance() { + return instance; + } + + private static final ServiceUtilsForHandlers instance = new ServiceUtilsForHandlers(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForIEvaluationContext.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForIEvaluationContext.java new file mode 100644 index 00000000000..015a5e5076e --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForIEvaluationContext.java @@ -0,0 +1,113 @@ +/***************************************************************************** + * Copyright (c) 2012, 2016 Cedric Dumoulin, Christian W. Damus, 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: + * Cedric Dumoulin - Initial API and implementation + * Christian W. Damus - bug 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServiceNotFoundException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils; +import org.eclipse.ui.ISources; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartSite; + +/** + * ServicesUtils based on the Handler's IEvaluationContext. + * This class can be used for both the {@link AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)} and the {@link AbstractHandler#setEnabled(Object)} methods. + * + * + * @author Cedric Dumoulin + * + */ +public class ServiceUtilsForIEvaluationContext extends AbstractServiceUtils<IEvaluationContext> { + + private ServiceUtilsForIEvaluationContext() { + // Singleton + } + + /** + * + * @see org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils#getServiceRegistry(java.lang.Object) + * + * @param from + * @return + * @throws ServiceException + */ + @Override + public ServicesRegistry getServiceRegistry(IEvaluationContext from) throws ServiceException { + + if (from == null) { + return getContextualServiceRegistry(); + } + + IEvaluationContext evaluationContext = from; + + // Search for the IWorkbenchPartSite from which the ExecutionEvent is sent (May be different that the Active one) + Object workbenchPartSite = evaluationContext.getVariable("org.eclipse.ui.IWorkbenchPartSite"); + if (workbenchPartSite instanceof IWorkbenchPartSite) { + IWorkbenchPartSite site = (IWorkbenchPartSite) workbenchPartSite; + Object registry = site.getAdapter(ServicesRegistry.class); + if (registry != null && registry instanceof ServicesRegistry) { + return (ServicesRegistry) registry; + } + + // Search for the IWorkbenchPart from which the ExecutionEvent is sent (May be different that the Active one) + IWorkbenchPart workbenchPart = site.getPart(); + registry = workbenchPart.getAdapter(ServicesRegistry.class); + if (registry != null && registry instanceof ServicesRegistry) { + return (ServicesRegistry) registry; + } + } + + Object selection = evaluationContext.getVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME); + + ServicesRegistry registry; + + // Try to resolve the ServicesRegistry from the current selection + if (selection instanceof ISelection && !((ISelection) selection).isEmpty()) { + try { + registry = ServiceUtilsForSelection.getInstance().getServiceRegistry((ISelection) selection); + if (registry != null) { + return registry; + } + } catch (ServiceException ex) { + // Ignore and try another ServiceUtils + } + } + + // We couldn't retrieve the ServiceRegistry from the current selection. + + // Try to adapt the active part to the ServicesRegistry + Object _part = evaluationContext.getVariable(ISources.ACTIVE_PART_NAME); + if (_part instanceof IWorkbenchPart) { + IWorkbenchPart part = (IWorkbenchPart) _part; + registry = (part).getAdapter(ServicesRegistry.class); + if (registry != null) { + return registry; + } + } + + + // nothing found + throw new ServiceNotFoundException("The ServiceRegistry cannot be resolved"); //$NON-NLS-1$ + } + + public static ServiceUtilsForIEvaluationContext getInstance() { + return instance; + } + + private static final ServiceUtilsForIEvaluationContext instance = new ServiceUtilsForIEvaluationContext(); +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForSelection.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForSelection.java new file mode 100644 index 00000000000..f3a799d303a --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForSelection.java @@ -0,0 +1,61 @@ +/***************************************************************************** + * Copyright (c) 2012 CEA LIST. + * + * 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import java.util.Iterator; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils; +import org.eclipse.papyrus.infra.emf.utils.EMFHelper; +import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForEObject; + +/** + * ServiceUtils based on an ISelection. + * + * Expects an IStructuredSelection containing at least one EObject (It then relies on ServiceUtilsForEObject to retrieve the ServicesRegistry) + * + * @author Camille Letavernier + */ +public class ServiceUtilsForSelection extends AbstractServiceUtils<ISelection> { + + private ServiceUtilsForSelection() { + // Singleton + } + + private static ServiceUtilsForSelection instance = new ServiceUtilsForSelection(); + + public static ServiceUtilsForSelection getInstance() { + return instance; + } + + @Override + public ServicesRegistry getServiceRegistry(ISelection from) throws ServiceException { + if (from instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection) from; + Iterator<?> selectionIterator = selection.iterator(); + while (selectionIterator.hasNext()) { + Object selectedElement = selectionIterator.next(); + EObject selectedEObject = EMFHelper.getEObject(selectedElement); + if (selectedEObject != null) { + return ServiceUtilsForEObject.getInstance().getServiceRegistry(selectedEObject); + } + } + } + + throw new ServiceException("Cannot retrieve the ServiceRegistry"); //$NON-NLS-1$ + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForWorkbenchPage.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForWorkbenchPage.java new file mode 100644 index 00000000000..624257964cc --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/ServiceUtilsForWorkbenchPage.java @@ -0,0 +1,63 @@ +/***************************************************************************** + * Copyright (c) 2012 CEA LIST. + * + * 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils; +import org.eclipse.ui.IWorkbenchPage; + +/** + * A ServiceUtils implementation for manipulating the Papyrus services from an IWorkbenchPage + * + * @author Camille Letavernier + * + */ +public class ServiceUtilsForWorkbenchPage extends AbstractServiceUtils<IWorkbenchPage> { + + @Override + public ServicesRegistry getServiceRegistry(IWorkbenchPage from) throws ServiceException { + IAdaptable adaptable = null; + if (from instanceof IAdaptable) { + adaptable = (IAdaptable) from; + } else if (from != null) { + // 421392: [Model Explorer] Link with Editor does not work properly + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=421392 + + // Since Eclipse 4.4, the concrete WorkbenchPage is not IAdaptable anymore. + // Try the ActivePart + adaptable = from.getActivePart(); + } + + if (adaptable != null) { + ServicesRegistry registry = (ServicesRegistry) adaptable.getAdapter(ServicesRegistry.class); + if (registry != null) { + return registry; + } + } + + + throw new ServiceException("Cannot resolve the ServiceRegistry from the IWorkbenchPage. Page: " + from); //$NON-NLS-1$ + } + + public static ServiceUtilsForWorkbenchPage getInstance() { + return instance; + } + + private static ServiceUtilsForWorkbenchPage instance = new ServiceUtilsForWorkbenchPage(); + + private ServiceUtilsForWorkbenchPage() { + // Singleton + } + +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/TransactionUIHelper.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/TransactionUIHelper.java new file mode 100644 index 00000000000..825a99386a3 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/TransactionUIHelper.java @@ -0,0 +1,81 @@ +/***************************************************************************** + * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, 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: + * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation + * Christian W. Damus - bug 465416 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.emf.common.util.WrappedException; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.jface.operation.IRunnableWithProgress; + + +/** + * Helper utilities for working with transactions on the UI thread. + */ +public class TransactionUIHelper { + + /** + * Create a privileged runnable with progress, which is like a regular {@linkplain TransactionalEditingDomain#createPrivilegedRunnable(Runnable) + * privileged runnable} except that it is given a progress monitor for progress reporting. + * This enables execution of monitored runnables on the modal-context thread using the transaction borrowed from the UI thread. + * + * @param domain + * an editing domain + * @param runnable + * a runnable with progress that is to borrow the {@code domain}'s active transaction on the modal context thread + * @return the privileged runnable, ready to pass into the progress service or other such API + */ + public static IRunnableWithProgress createPrivilegedRunnableWithProgress(TransactionalEditingDomain domain, final IRunnableWithProgress runnable) { + final IProgressMonitor monitorHolder[] = { null }; + + final Runnable privileged = domain.createPrivilegedRunnable(new Runnable() { + + @Override + public void run() { + try { + runnable.run(monitorHolder[0]); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new WrappedException(e); + } + } + }); + + return new IRunnableWithProgress() { + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + monitorHolder[0] = monitor; + + try { + privileged.run(); + } catch (OperationCanceledException e) { + throw new InterruptedException(e.getLocalizedMessage()); + } catch (WrappedException e) { + Exception unwrapped = e.exception(); + if (unwrapped instanceof InvocationTargetException) { + throw (InvocationTargetException) unwrapped; + } else if (unwrapped instanceof InterruptedException) { + throw (InterruptedException) unwrapped; + } else { + throw e; + } + } + } + }; + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/UIUtil.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/UIUtil.java new file mode 100644 index 00000000000..343bcdeba56 --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/UIUtil.java @@ -0,0 +1,706 @@ +/* + * Copyright (c) 2014, 2016 CEA, Christian W. Damus, 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: + * Christian W. Damus (CEA) - Initial API and implementation + * Christian W. Damus - bug 399859 + * Christian W. Damus - bug 451557 + * Christian W. Damus - bug 485220 + * + */ +package org.eclipse.papyrus.infra.ui.util; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.emf.common.util.AbstractTreeIterator; +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.jface.operation.IRunnableContext; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.services.ServicesRegistry; +import org.eclipse.papyrus.infra.core.utils.IServiceRegistryProvider; +import org.eclipse.papyrus.infra.core.utils.ServiceUtils; +import org.eclipse.papyrus.infra.tools.util.IExecutorService; +import org.eclipse.papyrus.infra.tools.util.IProgressCallable; +import org.eclipse.papyrus.infra.tools.util.IProgressRunnable; +import org.eclipse.papyrus.infra.tools.util.Iterators2; +import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.IProgressService; +import org.eclipse.ui.progress.IWorkbenchSiteProgressService; + +import com.google.common.collect.Iterators; + + +/** + * Miscellaneous general-purpose UI utilities. + */ +public class UIUtil { + + /** + * Not instantiable by clients. + */ + private UIUtil() { + super(); + } + + /** + * Create an executor that runs tasks asynchronously on the UI thread. If you need synchronous execution, schedule {@link Future}s and {@linkplain Future#get() wait} for them. + * + * @param display + * the display on which thread to execute tasks + * + * @return the executor + */ + public static IExecutorService createUIExecutor(Display display) { + return new DisplayExecutorService(display); + } + + /** + * Create an executor that runs tasks asynchronously on an observable {@link Realm}. If you need synchronous execution, schedule {@link Future}s and {@linkplain Future#get() wait} for them. + * + * @param realm + * the observable realm on which thread to execute tasks + * + * @return the executor + */ + public static ExecutorService createObservableExecutor(Realm realm) { + return new RealmExecutorService(realm); + } + + /** + * Creates a local memento that is not persistable and is not based on an XML document. This is useful for capturing the + * state of UI elements locally in cases where persistence of the memento is not required. + * + * @return the memento + */ + public static IMemento createLocalMemento() { + return LocalMemento.createMemento("__anonymous__", null); //$NON-NLS-1$ + } + + /** + * Synchronously invokes a {@code callable} on the given {@code display}'s thread. + * + * @param display + * a display + * @param callable + * a callable to invoke + * @return the callable's result (which, because this method is synchronous, will be ready) + * + * @see #asyncCall(Display, Callable) + * @see #createUIExecutor(Display) + */ + public static <V> Future<V> syncCall(Display display, Callable<V> callable) { + final FutureTask<V> result = new FutureTask<V>(callable); + display.syncExec(result); + return result; + } + + /** + * Synchronously invokes a {@code callable} on the default display thread. + * + * @param callable + * a callable to invoke + * @return the callable's result (which, because this method is synchronous, will be ready) + * + * @see #syncCall(Display, Callable) + * @see #asyncCall(Callable) + * @see #createUIExecutor(Display) + */ + public static <V> Future<V> syncCall(Callable<V> callable) { + return syncCall(Display.getDefault(), callable); + } + + /** + * Asynchronously invokes a {@code callable} on the given {@code display}'s thread. + * + * @param display + * a display + * @param callable + * a callable to invoke + * @return the callable's result + * + * @see #syncCall(Display, Callable) + * @see #createUIExecutor(Display) + */ + public static <V> Future<V> asyncCall(Display display, Callable<V> callable) { + final FutureTask<V> result = new FutureTask<V>(callable); + display.asyncExec(result); + return result; + } + + /** + * Asynchronously invokes a {@code callable} on the default display thread. + * + * @param callable + * a callable to invoke + * @return the callable's result + * + * @see #asyncCall(Display, Callable) + * @see #syncCall(Callable) + * @see #createUIExecutor(Display) + */ + public static <V> Future<V> asyncCall(Callable<V> callable) { + return asyncCall(Display.getDefault(), callable); + } + + /** + * Calls a {@code callable} in the given {@code context}. + * + * @param fork + * {@code true} if the runnable should be run in a separate thread, + * and {@code false} to run in the same thread + * @param cancelable + * {@code true} to enable the cancellation, and {@code false} to make the operation uncancellable + * @param runnable + * the runnable to run + * + * @exception InvocationTargetException + * wraps any exception or error which occurs + * while running the runnable + * @exception InterruptedException + * propagated by the context if the runnable + * acknowledges cancellation by throwing this exception. This should not be thrown + * if {@code cancelable} is {@code false}. + * + * @deprecated Use the {@link #call(IRunnableContext, boolean, boolean, IProgressCallable)} or + * {@link IExecutorService#submit(IProgressCallable)} API, instead. + */ + @Deprecated + public static <V> V call(IRunnableContext context, boolean fork, boolean cancelable, ICallableWithProgress<V> callable) throws InvocationTargetException, InterruptedException { + return call(context, fork, cancelable, (IProgressCallable<V>) callable); + } + + /** + * Calls a {@code callable} in the given {@code context}. + * + * @param fork + * {@code true} if the runnable should be run in a separate thread, + * and {@code false} to run in the same thread + * @param cancelable + * {@code true} to enable the cancellation, and {@code false} to make the operation uncancellable + * @param runnable + * the runnable to run + * + * @exception InvocationTargetException + * wraps any exception or error which occurs + * while running the runnable + * @exception InterruptedException + * propagated by the context if the runnable + * acknowledges cancellation by throwing this exception. This should not be thrown + * if {@code cancelable} is {@code false}. + */ + public static <V> V call(IRunnableContext context, boolean fork, boolean cancelable, IProgressCallable<V> callable) throws InvocationTargetException, InterruptedException { + class RunnableWrapper implements IRunnableWithProgress { + final IProgressCallable<V> delegate; + + V result; + + RunnableWrapper(IProgressCallable<V> delegate) { + this.delegate = delegate; + } + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + try { + result = delegate.call(monitor); + } catch (OperationCanceledException e) { + throw new InterruptedException(e.getMessage()); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new InvocationTargetException(e); + } + } + } + + RunnableWrapper wrapper = new RunnableWrapper(callable); + context.run(fork, cancelable, wrapper); + return wrapper.result; + } + + /** + * Obtains a simple executor that asynchronously executes at most one task on the default + * display thread. While any task is still pending execution on this executor, + * all others are silently discarded. This is useful for cases where, for example, UI + * refreshes are posted repeatedly from independent events that aren't aware of each other + * but where each refresh task would repeat the same work. + * + * @param display + * a display on which thread to execute tasks + * + * @return the executor + * + * @see #createAsyncOnceExecutor(Display) + */ + public static Executor createAsyncOnceExecutor() { + return createAsyncOnceExecutor(Display.getDefault()); + } + + /** + * Obtains a simple executor that asynchronously executes at most one task on the given {@code display}'s thread. While any task is still pending execution on this executor, + * all others are silently discarded. This is useful for cases where, for example, UI + * refreshes are posted repeatedly from independent events that aren't aware of each other + * but where each refresh task would repeat the same work. + * + * @param display + * a display on which thread to execute tasks + * + * @return the executor + */ + public static Executor createAsyncOnceExecutor(final Display display) { + return new Executor() { + private final AtomicBoolean pending = new AtomicBoolean(); + + @Override + public void execute(final Runnable task) { + if (pending.compareAndSet(false, true)) { + display.asyncExec(new Runnable() { + + @Override + public void run() { + pending.set(false); + task.run(); + } + }); + } + } + }; + } + + /** + * Obtains a tree iterator over all of the controls contained within a given {@code root} control, not including that {@code root}. + * + * @param root + * a control to iterate + * @return an unmodifiable iterator over all of its nested controls, which naturally will be empty if the {@code root} is not a {@link Composite} + */ + public static TreeIterator<Control> allChildren(Control root) { + return new AbstractTreeIterator<Control>(root, false) { + private static final long serialVersionUID = 1L; + + @Override + protected Iterator<? extends Control> getChildren(Object object) { + return (object instanceof Composite) ? Iterators.forArray(((Composite) object).getChildren()) : Iterators.<Control> emptyIterator(); + } + }; + } + + /** + * Obtains a tree iterator over all of the controls of a particular type contained within a given {@code root} control, not including that {@code root}. + * + * @param root + * a control to iterate + * @param type + * the type of children to include in the iteration + * + * @return an unmodifiable iterator over all of its nested controls, which naturally will be empty if the {@code root} is not a {@link Composite} + */ + public static <C extends Control> TreeIterator<C> allChildren(Control root, final Class<C> type) { + return Iterators2.filter(allChildren(root), type); + } + + // + // Nested types + // + + private static abstract class UIExecutorService extends AbstractExecutorService implements IExecutorService { + + private final Lock lock = new ReentrantLock(); + + private final Condition emptyCond = lock.newCondition(); + + private final Queue<RunnableWrapper> pending = new LinkedList<RunnableWrapper>(); + + private volatile boolean shutdown; + + UIExecutorService() { + super(); + } + + @Override + public void execute(Runnable command) { + if (isShutdown()) { + throw new RejectedExecutionException("Executor service is shut down"); //$NON-NLS-1$ + } + + asyncExec(enqueue(command)); + } + + @Override + public <V> V syncCall(Callable<V> callable) throws InterruptedException, ExecutionException { + class SyncResult implements Runnable { + V result; + ExecutionException fail; + + @Override + public void run() { + try { + result = callable.call(); + } catch (Exception e) { + fail = new ExecutionException(e); + fail.fillInStackTrace(); + } + } + } + + SyncResult result = new SyncResult(); + + syncExec(result); + + if (result.fail != null) { + throw result.fail; + } + + return result.result; + } + + abstract void asyncExec(Runnable runnable); + + @Override + public List<Runnable> shutdownNow() { + List<Runnable> result = new ArrayList<Runnable>(); + + shutdown(); + + for (Runnable dequeued = dequeue(); dequeued != null; dequeued = dequeue()) { + result.add(dequeued); + } + + return result; + } + + private RunnableWrapper enqueue(Runnable task) { + RunnableWrapper result = new RunnableWrapper(task); + + lock.lock(); + try { + boolean wasEmpty = pending.isEmpty(); + pending.offer(result); + if (wasEmpty) { + // Now not empty + emptyCond.signalAll(); + } + } finally { + lock.unlock(); + } + + return result; + } + + private RunnableWrapper dequeue() { + RunnableWrapper result = null; + + lock.lock(); + try { + result = pending.poll(); + if (result == null) { + // Now empty + emptyCond.signalAll(); + } + } finally { + lock.unlock(); + } + + return result; + } + + boolean dequeue(RunnableWrapper task) { + boolean result = false; + + lock.lock(); + try { + result = pending.remove(task); + if (result && pending.isEmpty()) { + // Now empty + emptyCond.signalAll(); + } + } finally { + lock.unlock(); + } + + return result; + } + + @Override + public void shutdown() { + shutdown = true; + } + + @Override + public boolean isTerminated() { + lock.lock(); + try { + return isShutdown() && pending.isEmpty(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean isShutdown() { + return shutdown; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + if (timeout < 0L) { + throw new IllegalArgumentException("negative timeout"); //$NON-NLS-1$ + } + + final Date deadline = (timeout == 0L) ? null : new Date(System.currentTimeMillis() + unit.toMillis(timeout)); + boolean result = false; + + lock.lock(); + try { + boolean stillWaiting = true; + for (result = isTerminated(); !result && stillWaiting; result = isTerminated()) { + if (deadline == null) { + emptyCond.await(); + } else { + stillWaiting = emptyCond.awaitUntil(deadline); + } + } + } finally { + lock.unlock(); + } + + return result; + } + + @Override + public Future<?> submit(IProgressRunnable task) { + return submit(new IProgressCallable<Void>() { + @Override + public Void call(IProgressMonitor monitor) { + task.run(monitor); + return null; + } + }); + } + + @Override + public void syncExec(IProgressRunnable task) throws InterruptedException, ExecutionException { + syncCall(new IProgressCallable<Void>() { + @Override + public Void call(IProgressMonitor monitor) { + task.run(monitor); + return null; + } + }); + } + + IProgressService getProgressService(IProgressCallable<?> callable) { + IProgressService result; + + try { + ServicesRegistry registry = (callable instanceof IServiceRegistryProvider) + ? ((IServiceRegistryProvider) callable).getServiceRegistry() + : null; + IMultiDiagramEditor editor = ServiceUtils.getInstance().getService( + IMultiDiagramEditor.class, registry); + result = editor.getEditorSite().getService(IWorkbenchSiteProgressService.class); + } catch (ServiceException e) { + // Fine, there's no editor + result = PlatformUI.getWorkbench().getProgressService(); + } + + return result; + } + + // + // Nested types + // + + private class RunnableWrapper implements Runnable { + + private final Runnable delegate; + + RunnableWrapper(Runnable delegate) { + this.delegate = delegate; + } + + @Override + public void run() { + // Don't run if I was cancelled by shutdown + if (dequeue(this)) { + delegate.run(); + } + } + } + }; + + private static class DisplayExecutorService extends UIExecutorService { + private final Display display; + + DisplayExecutorService(Display display) { + super(); + + this.display = display; + } + + @Override + void asyncExec(Runnable runnable) { + display.asyncExec(runnable); + } + + @Override + public void syncExec(Runnable task) { + display.syncExec(task); + } + + @Override + public <V> Future<V> submit(IProgressCallable<V> callable) { + IProgressService service = getProgressService(callable); + IWorkbenchSiteProgressService wbService = (service instanceof IWorkbenchSiteProgressService) ? (IWorkbenchSiteProgressService) service : null; + + FutureProgress<V> result = new FutureProgress<>(callable, wbService); + + try { + service.run(true, true, result); + } catch (Exception e) { + // This shouldn't happen when running asynchronously + result.completeExceptionally(e); + } + + return result; + } + + @Override + public <V> V syncCall(IProgressCallable<V> callable) throws InterruptedException, ExecutionException { + IProgressService service = getProgressService(callable); + IWorkbenchSiteProgressService wbService = (service instanceof IWorkbenchSiteProgressService) ? (IWorkbenchSiteProgressService) service : null; + + FutureProgress<V> result = new FutureProgress<>(callable, wbService); + + try { + service.busyCursorWhile(result); + } catch (Exception e) { + result.completeExceptionally(e); + } + + return result.get(); // It really should be completed, by now + } + } + + private static class RealmExecutorService extends UIExecutorService { + private final Realm realm; + + RealmExecutorService(Realm realm) { + super(); + + this.realm = realm; + } + + @Override + void asyncExec(Runnable runnable) { + realm.asyncExec(runnable); + } + + @Override + public void syncExec(Runnable task) { + realm.exec(task); + } + + @Override + public <V> Future<V> submit(IProgressCallable<V> callable) { + // No place to report progress in this case + FutureTask<V> result = new FutureTask<V>(() -> callable.call(new NullProgressMonitor())); + asyncExec(result); + return result; + } + + @Override + public <V> V syncCall(IProgressCallable<V> callable) throws InterruptedException, ExecutionException { + // No place to report progress in this case + FutureTask<V> result = new FutureTask<V>(() -> callable.call(new NullProgressMonitor())); + syncExec(result); + return result.get(); // It really should be completed, by now + } + } + + private static class FutureProgress<V> extends CompletableFuture<V> implements IRunnableWithProgress { + private final IProgressCallable<V> delegate; + private final IWorkbenchSiteProgressService service; + + private volatile IProgressMonitor monitor; + + FutureProgress(IProgressCallable<V> delegate, IWorkbenchSiteProgressService service) { + super(); + + this.delegate = delegate; + this.service = service; + } + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + try { + this.monitor = monitor; + + if (service != null) { + service.incrementBusy(); + } + + try { + complete(delegate.call(monitor)); + } finally { + this.monitor = null; + + if (service != null) { + service.decrementBusy(); + } + } + } catch (OperationCanceledException e) { + throw new InterruptedException(e.getMessage()); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new InvocationTargetException(e); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + IProgressMonitor monitor = this.monitor; + if (monitor != null) { + monitor.setCanceled(true); + } + + return super.cancel(mayInterruptIfRunning); + } + + } +} diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/WorkbenchPartHelper.java b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/WorkbenchPartHelper.java new file mode 100644 index 00000000000..4ecdc94d48f --- /dev/null +++ b/plugins/infra/ui/org.eclipse.papyrus.infra.ui/src/org/eclipse/papyrus/infra/ui/util/WorkbenchPartHelper.java @@ -0,0 +1,71 @@ +/***************************************************************************** + * Copyright (c) 2012 CEA LIST. + * + * + * 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: + * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.ui.util; + +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +/** + * + * a helper for the Eclipse workbench part + * + */ +public class WorkbenchPartHelper { + + private WorkbenchPartHelper() { + // nothing to do + } + + /** + * + * @return + * the current IWorkbenchPart or <code>null</code> if not found + */ + public static final IWorkbenchPart getCurrentActiveWorkbenchPart() { + final IWorkbench workbench = PlatformUI.getWorkbench(); + if (workbench != null) { + final IWorkbenchWindow activeWorkbench = workbench.getActiveWorkbenchWindow(); + if (activeWorkbench != null) { + final IWorkbenchPage activePage = activeWorkbench.getActivePage(); + if (activePage != null) { + return activePage.getActivePart(); + } + } + } + return null; + } + + /** + * + * @return + * the current IEditorPart or <code>null</code> if not found + */ + public static final IEditorPart getCurrentActiveEditorPart() { + final IWorkbench workbench = PlatformUI.getWorkbench(); + if (workbench != null) { + final IWorkbenchWindow activeWorkbench = workbench.getActiveWorkbenchWindow(); + if (activeWorkbench != null) { + final IWorkbenchPage activePage = activeWorkbench.getActivePage(); + if (activePage != null) { + return activePage.getActiveEditor(); + } + } + } + return null; + } +} |