Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue #343 Restrict message end movement #331

Merged
merged 13 commits into from
Sep 12, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,22 @@
<!-- when integrated with Papyrus, we should avoid applying this context by default -->
<context appliedByDefault="true" contextModel="context/lightweight-seqd.contexts" isCustomizable="true"/>
</extension>

<extension
point="org.eclipse.gmf.runtime.diagram.ui.paletteProviders">
<paletteProvider
class="org.eclipse.papyrus.uml.diagram.sequence.runtime.internal.providers.SequencePaletteProvider">
<Priority
name="Highest">
</Priority>
<!-- For some reason, Papyrus ignores the <content> object descriptor. -->
<editor>
<!-- the <method> is understood by the extension point. It's an oversight in the exsd. -->
<method
name="getContributorId()"
value="org.eclipse.papyrus.uml.diagram.sequence.runtime">
</method>
</editor>
</paletteProvider>
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Bundle-Activator: org.eclipse.papyrus.uml.diagram.sequence.figure.internal.Activ
Export-Package: org.eclipse.papyrus.uml.diagram.sequence.figure,
org.eclipse.papyrus.uml.diagram.sequence.figure.anchors,
org.eclipse.papyrus.uml.diagram.sequence.figure.border,
org.eclipse.papyrus.uml.diagram.sequence.figure.internal;x-internal:=true
org.eclipse.papyrus.uml.diagram.sequence.figure.internal;x-internal:=true,
org.eclipse.papyrus.uml.diagram.sequence.figure.magnets
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.13.0,4.0.0)",
org.eclipse.draw2d;bundle-version="[3.10.0,4.0.0)",
org.eclipse.gmf.runtime.draw2d.ui;bundle-version="[1.9.0,2.0.0)",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*****************************************************************************
* Copyright (c) 2018 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.uml.diagram.sequence.figure.magnets;

import org.eclipse.draw2d.geometry.Point;

/**
* A simple implementation of the {@linkplain IMagnet magnet} protocol.
*/
public class DefaultMagnet implements IMagnet {

private final int strength;

private Point location;

/**
* Initializes me with my {@code strength}.
*
* @param strength
* my strength
*/
public DefaultMagnet(int strength) {
super();

this.strength = strength;
}

@Override
public final int getStrength() {
return strength;
}

@Override
public void setLocation(Point location) {
this.location = location;
}

@Override
public Point getLocation() {
return location;
}

@Override
public boolean influences(@SuppressWarnings("hiding") Point location) {
return (this.location != null) && (this.location.getDistance(location) <= 1.5 * strength);
}

@SuppressWarnings("boxing")
@Override
public String toString() {
return String.format("DefaultMagnet at (%s, %s), strength %s", //$NON-NLS-1$
location.preciseX(), location.preciseY(), strength);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*****************************************************************************
* Copyright (c) 2018 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.uml.diagram.sequence.figure.magnets;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;

/**
* Protocol of a <em>magnet</em> that draws objects towards it (connection bendpoints and endpoints, shapes)
* that are being dragged about by the mouse pointer.
*/
public interface IMagnet {
/**
* Obtain the magnet's strength, which is the square radius within which it captures the mouse pointer.
*
* @return a positive strength, in pixels
*/
int getStrength();

/**
* Queries whether a {@code location} is within my {@linkplain #getStrength() square radius}.
*
* @param location
* a location in absolute coördinates
* @return whether I capture the {@code location}
*/
default boolean captures(Point location) {
Point self = getLocation();
int strength = getStrength();
int x = self.x - strength;
int y = self.y - strength;
int size = strength << 1;

Rectangle.SINGLETON.setBounds(x, y, size, size);

return Rectangle.SINGLETON.contains(location);
}

/**
* Query the location of the magnet, in absolute coördinates.
*
* @return the location
*/
Point getLocation();

/**
* Update (move) the location of the magnet.
*
* @param location
* the new location of the magnet
*/
void setLocation(Point location);

/**
* Determine whether a {@code location} feels my magnetic influence. This does not necessarily mean that I
* {@link #captures(Point) capture} the mouse pointer from that {@code location}. My influence could
* extend beyond my {@link #getStrength() strength}.
*
* @param location
* a location
* @return whether the {@code location} feels my influence
* @see #getStrength()
*/
boolean influences(Point location);

// TODO: API for showing and hiding feedback (with a secondary radius within which it is activated?)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*****************************************************************************
* Copyright (c) 2018 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.uml.diagram.sequence.figure.magnets;

import java.util.Optional;
import java.util.stream.Stream;

import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.Platform;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.SnapToHelper;
import org.eclipse.gef.tools.AbstractTool;
import org.eclipse.swt.SWT;

/**
* Protocol for management of {@link IMagnet}s on a shape in the diagram editor to which connection ends snap.
* Note that we do not use this for snapping of shapes, as GEF already provides support for that via the
* {@link SnapToHelper} API which does not deal with connections but only shapes.
*/
public interface IMagnetManager {

/**
* Key modifier for ignoring snap while dragging. It's <tt>CTRL</tt> on Mac and <tt>ALT</tt> on all other
* platforms, as defined in the GEF {@link AbstractTool} class.
*/
int MODIFIER_NO_SNAPPING = Platform.OS_MACOSX.equals(Platform.getOS()) ? SWT.CTRL : SWT.ALT;

/**
* Add a {@code magnet} to the diagram editor.
*
* @param magnet
* the magnet to add
*/
void addMagnet(IMagnet magnet);

/**
* Remove a {@code magnet} from the diagram editor.
*
* @param magnet
* the magnet to remove
*/
void removeMagnet(IMagnet magnet);

/**
* Find the magnets within whose influence the given {@code location} lies, in increasing order of
* distance from that location (and so decreasing order of influence).
*
* @param location
* a location in the diagram, in absolute coördinates
* @return the magnets whose influence is felt at that {@code location}
*/
Stream<IMagnet> getMagnets(Point location);

/**
* Find the magnet that captures a {@code location}.
*
* @param location
* a location, in absolute coördinates
* @return the magnet that captures the {@code location}
*/
Optional<IMagnet> getCapturingMagnet(Point location);

/**
* Queries whether the magnet manager is currently enabled. When it is not enabled, then snapping
* connections to magnets does not happen.
*
* @return whether the magnet manager is enabled
*/
boolean isEnabled();

/**
* Get the magnet manager for an {@code object}, usually an edit-part or figure. There should be exactly
* one magnet manager for the diagram, maintained by a root-ish edit-part or figure, which always exists.
*
* @param object
* an object in the diagram
* @return its magnet manager. Never {@code null}
* @throws IllegalStateException
* if the {@code object} does not trace to some magnet manager
*/
static IMagnetManager get(Object object) {
IMagnetManager result = Adapters.adapt(object, IMagnetManager.class);

if (result == null) {
throw new IllegalStateException("object has no magnet manager: " + object); //$NON-NLS-1$
}

return result;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*****************************************************************************
* Copyright (c) 2018 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.uml.diagram.sequence.figure.magnets;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.stream.Stream;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.tools.AbstractTool.Input;

/**
* A manager of {@link IMagnet}s.
*/
public class MagnetManager implements IMagnetManager {

// If scale becomes a problem, we may need a quad-tree or some such
private final List<IMagnet> magnets = new ArrayList<>();

private Input currentInput;

/**
* Initializes me.
*/
public MagnetManager() {
super();
}

@Override
public void addMagnet(IMagnet magnet) {
magnets.add(magnet);
}

@Override
public void removeMagnet(IMagnet magnet) {
magnets.remove(magnet);
}

/**
* Find the magnets within whose influence the given {@code location} lies, in increasing order of
* distance from that location (and so decreasing order of influence).
*
* @param location
* a location in the diagram, in absolute coördinates
* @return the magnets whose influence is felt at that {@code location}
*/
@Override
public Stream<IMagnet> getMagnets(Point location) {
if (!isEnabled()) {
return Stream.empty();
}

return magnets.stream().filter(influences(location))
.sorted(Comparator.comparingDouble(distance(location)));
}

/**
* Find the magnet that captures a {@code location}.
*
* @param location
* a location, in absolute coördinates
* @return the magnet that captures the {@code location}
*/
@Override
public Optional<IMagnet> getCapturingMagnet(Point location) {
// The first magnet is the nearest, and if it isn't capturing, then none is
return getMagnets(location).findFirst().filter(captures(location));
}

@Override
public boolean isEnabled() {
Input input = getCurrentInput();
return input == null || !input.isModKeyDown(MODIFIER_NO_SNAPPING);
}

/**
* @return the currentInput
*/
public Input getCurrentInput() {
return currentInput;
}

/**
* @param currentInput
* the currentInput to set
*/
public void setCurrentInput(Input currentInput) {
this.currentInput = currentInput;
}

private Predicate<IMagnet> influences(Point location) {
return magnet -> magnet.influences(location);
}

private Predicate<IMagnet> captures(Point location) {
return magnet -> magnet.captures(location);
}

private ToDoubleFunction<IMagnet> distance(Point location) {
return magnet -> magnet.getLocation().getDistance(location);
}

public static Optional<MagnetManager> get(Object object) {
return Optional.ofNullable(IMagnetManager.get(object)).filter(MagnetManager.class::isInstance)
.map(MagnetManager.class::cast);
}
}
Loading