blob: 1082a0d593659cad978ff4b3e81f4d3fd8b716e1 [file] [log] [blame]
/*****************************************************************************
* 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);
}
}
}