| /***************************************************************************** |
| * Copyright (c) 2019 CEA LIST. |
| * |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Xavier Le Pallec (for CEA LIST) xlepallec@lilo.org - Bug 558456 |
| * |
| *****************************************************************************/ |
| |
| package org.eclipse.papyrus.uml.diagram.clazz.lf.autosizeableclasses.edit.policies; |
| |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.transaction.RecordingCommand; |
| import org.eclipse.emf.transaction.TransactionalEditingDomain; |
| import org.eclipse.gef.EditPart; |
| import org.eclipse.gef.Request; |
| import org.eclipse.gef.commands.Command; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest; |
| import org.eclipse.gmf.runtime.notation.BasicCompartment; |
| import org.eclipse.gmf.runtime.notation.LayoutConstraint; |
| import org.eclipse.gmf.runtime.notation.Node; |
| import org.eclipse.gmf.runtime.notation.NotationPackage; |
| import org.eclipse.papyrus.infra.core.services.ServiceException; |
| import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForEObject; |
| import org.eclipse.papyrus.uml.diagram.clazz.edit.parts.ClassEditPart; |
| import org.eclipse.papyrus.uml.diagram.clazz.edit.parts.ClassEditPartCN; |
| import org.eclipse.papyrus.uml.diagram.clazz.lf.autosizeableclasses.Activator; |
| import org.eclipse.papyrus.uml.diagram.clazz.lf.autosizeableclasses.edit.policies.utils.UmlClassResizer; |
| import org.eclipse.papyrus.uml.diagram.clazz.lf.autosizeableclasses.preference.Settings; |
| |
| /** |
| * This is an edit policy that inherits from |
| * {@link org.eclipse.papyrus.uml.diagram.clazz.lf.autosizeableclasses.edit.policies.HoveringElementEditPolicy} |
| * and dedicated to process request / hovering events for the class diagram |
| * itself or packages. |
| * |
| */ |
| |
| public class ContainerEditPolicy extends HoveringElementEditPolicy { |
| |
| private static final int HEIGHT_OF_A_NON_COMPARTMENTED_CLASS = 30; |
| |
| /** |
| * see |
| * {@link org.eclipse.papyrus.uml.diagram.clazz.lf.autosizeableclasses.edit.policies.HoveringElementEditPolicy#PolicyToInheritFrom(EditPart)} |
| * |
| * @param editPart edit part which the edit policy is associated to |
| */ |
| |
| public ContainerEditPolicy(EditPart editPart) { |
| super(editPart); |
| } |
| |
| /** |
| * see |
| * {@link org.eclipse.papyrus.uml.diagram.clazz.lf.autosizeableclasses.edit.policies.HoveringElementEditPolicy} |
| * for global explanation of edit policy and this method. Short summary: |
| * intercept a move (during a creation/drop interaction) over the associated |
| * graphical element |
| * |
| * @param request the request corresponding to the interaction event |
| */ |
| |
| @Override |
| public Command getCommand(Request request) { |
| if (Settings.getInstance().isActivated() && isCreationOrDropRequest(request)) { |
| setCurrentRequest(request); |
| getStateMachine().getLowEventManager().hoveringSomethingButAClass(editPart); |
| } |
| return super.getCommand(request); |
| } |
| |
| /** |
| * This method is called for request about creating something. As this edit |
| * policy is associated to UML Class, it is for creation of uml classes. The |
| * implementation of this inherited method is to add some behavior in the |
| * creation of class: |
| * <UL> |
| * <LI>hide each compartment of the class</LI> |
| * <LI>Keep the height of the class at a certain value (except if the keeping |
| * creation size option is on).</LI> |
| * </UL> |
| * To do this, 2 commands are inserted in the global creation command: |
| * <UL> |
| * <LI>one, inserted at the beginning, for adding a adapter that listens the |
| * next creation in the related class diagram, in order to get a reference on |
| * the newly create class (notation level) and to listen to every update |
| * operations on it</LI> |
| * <LI>one, inserted at the end, for adding a adapter that detects one of the |
| * last operations: changing the height of the class. The goal is to keep at a |
| * certain value (or not).</LI> |
| * </UL> |
| * |
| */ |
| @Override |
| protected Command getCreateCommand(CreateViewRequest request) { |
| if (Settings.getInstance().isActivated() |
| && request.getViewDescriptors().get(0).getSemanticHint().startsWith("Class_") |
| && editPart.getModel() instanceof Notifier) { |
| |
| final Notifier container = Notifier.class.cast(editPart.getModel()); |
| final AdapterToHideCompartmentOfAUmlClass compartmentListener = new AdapterToHideCompartmentOfAUmlClass(); |
| final AdapterForMemorizeTheNewClass listener = new AdapterForMemorizeTheNewClass(compartmentListener); |
| final AdapterToSetMyHeight heightListener = new AdapterToSetMyHeight(request.getSize() != null); |
| |
| // listenClassDiagramToDetectTheCreatedClass |
| Command firstCommand = commandToDetectACreationOfAClass(container, listener); |
| |
| |
| // listenTheClassBoundsToDetectTheChangeOfHeight |
| Command lastCommand = commandToDetectChangeOfHeight(container, listener, compartmentListener, |
| heightListener); |
| |
| // command that create the class and its components, and bounds it. |
| Command command = super.getCreateCommand(request); |
| |
| return firstCommand.chain(command).chain(lastCommand); |
| } else { |
| return super.getCreateCommand(request); |
| } |
| |
| } |
| |
| // This method creates a command, located at the beginning, in which an adapter |
| // will be added to the class |
| // diagram to detect the next creation of a class |
| |
| private Command commandToDetectACreationOfAClass(final Notifier container, |
| final AdapterForMemorizeTheNewClass listener) { |
| return new Command() { |
| @Override |
| public boolean canExecute() { |
| return true; |
| } |
| |
| @Override |
| public void execute() { |
| container.eAdapters().add(listener); |
| } |
| }; |
| } |
| |
| // This method creates a command, located at the end, in which an adapter will |
| // be added to size part to the class |
| // in order to detect the resize of the class (one of the last operation of |
| // creation process). |
| |
| private Command commandToDetectChangeOfHeight(final Notifier container, |
| final AdapterForMemorizeTheNewClass listener, final AdapterToHideCompartmentOfAUmlClass compartmentListener, |
| AdapterToSetMyHeight heightListener) { |
| |
| return new Command() { |
| @Override |
| public boolean canExecute() { |
| return true; |
| } |
| |
| @Override |
| public void execute() { |
| |
| container.eAdapters().remove(listener); |
| if (compartmentListener.theClass != null) { |
| compartmentListener.theClass.eAdapters().remove(compartmentListener); |
| } |
| |
| if (listener.theClass != null) { |
| listener.theClass.getLayoutConstraint().eAdapters().add(heightListener); |
| } |
| } |
| |
| }; |
| } |
| |
| /** |
| * This class defines an adapter that will listen the class diagram in order to |
| * detect the creation of the new class (notation level). When it detects such |
| * event, it associates an adapter (listener) to the new class in order to hide |
| * each new compartment that will be added to it. |
| * |
| */ |
| protected class AdapterForMemorizeTheNewClass extends AdapterImpl { |
| private Node theClass; |
| |
| /** |
| * The future adapter that will listen each newly created class in order to hide |
| * each new compartment that will be added to the class (notation level). |
| */ |
| private AdapterToHideCompartmentOfAUmlClass classListener; |
| |
| public AdapterForMemorizeTheNewClass(AdapterToHideCompartmentOfAUmlClass classListener) { |
| this.classListener = classListener; |
| } |
| |
| /** |
| * This implementation of the notifyChanged method aims at adding the "hide |
| * compartment adapter" to each newly created class. |
| */ |
| @Override |
| public void notifyChanged(Notification notification) { |
| Object value = notification.getNewValue(); |
| if (value instanceof Node) { |
| theClass = Node.class.cast(value); |
| if (theClass.getType().equals(ClassEditPart.VISUAL_ID) || |
| theClass.getType().equals(ClassEditPartCN.VISUAL_ID)) { |
| classListener.theClass = theClass; |
| theClass.eAdapters().add(classListener); |
| } |
| } |
| } |
| } |
| |
| /** |
| * This class aims at listening classes (notation level) from the very beginning |
| * of their life cycle (before their components are created) in order to hide |
| * each compartment that will be created and added to the class. |
| * |
| */ |
| protected class AdapterToHideCompartmentOfAUmlClass extends AdapterImpl { |
| protected Node theClass; |
| |
| /** |
| * This implementation of the notifyChanged method aims at hiding compartment |
| * when they are added to the class |
| * |
| */ |
| @Override |
| public void notifyChanged(Notification notification) { |
| if (notification.getNewValue() instanceof BasicCompartment |
| && notification.getEventType() == Notification.ADD) { |
| hideCompartment(BasicCompartment.class.cast(notification.getNewValue())); |
| } |
| } |
| } |
| |
| /** |
| * This class aims at listening classes (notation level) from the very beginning |
| * of their life cycle (before their components are created) in order to that |
| * height of the class remains at the value set by the constant |
| * HEIGHT_OF_A_NON_COMPARTMENTED_CLASS. The height is changed at the end of the |
| * creation of a class (notation level): so this adapter will act only once. |
| * |
| */ |
| protected class AdapterToSetMyHeight extends AdapterImpl { |
| private LayoutConstraint layoutConstraint; |
| boolean aSpecificSizeHasBeenProposed; |
| |
| public AdapterToSetMyHeight(boolean aSpecificSizeHasBeenProposed) { |
| this.aSpecificSizeHasBeenProposed = aSpecificSizeHasBeenProposed; |
| } |
| |
| /** |
| * This implementation of the notifyChanged method aims at bringing the height |
| * at HEIGHT_OF_A_NON_COMPARTMENTED_CLASS when the height is being changed (at |
| * the end of the creation of the class). As the modification of the height |
| * happens once, the adapter is removed after it has acted. |
| */ |
| @Override |
| public void notifyChanged(Notification notification) { |
| if (!(Settings.getInstance().keepsCreationSize() && aSpecificSizeHasBeenProposed |
| && notification.getNotifier() instanceof LayoutConstraint)) { |
| layoutConstraint = LayoutConstraint.class.cast(notification.getNotifier()); |
| |
| if (notification.getFeature() == NotationPackage.eINSTANCE.getSize_Height()) { |
| layoutConstraint.eAdapters().remove(this); |
| try { |
| UmlClassResizer.getInstance().resizeHeight(layoutConstraint, |
| HEIGHT_OF_A_NON_COMPARTMENTED_CLASS); |
| } catch (ServiceException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * This method is used by the adapter that hiding compartment in order to... |
| * hide a compartment. |
| * |
| * @param compartment the compartment to hide. |
| */ |
| protected void hideCompartment(BasicCompartment compartment) { |
| try { |
| TransactionalEditingDomain editingDomain = ServiceUtilsForEObject.getInstance() |
| .getTransactionalEditingDomain(compartment); |
| RecordingCommand command = new RecordingCommand(editingDomain, "hide compartment") { |
| |
| @Override |
| protected void doExecute() { |
| compartment.setVisible(false); |
| } |
| |
| }; |
| editingDomain.getCommandStack().execute(command); |
| } catch (ServiceException exception) { |
| Activator.getDefault().logError(exception.getMessage(), exception); |
| } |
| } |
| } |