Commit 5a556945 authored by Nico Mack's avatar Nico Mack

Cleanup of code. Implemented cloning of widgets, required by feature

allowing mutliple widgets with identical IDs.
parent 5e2bc616
...@@ -171,6 +171,7 @@ STYLESHEET_NODE=styleSheet ...@@ -171,6 +171,7 @@ STYLESHEET_NODE=styleSheet
STYLERULE_NODE=rule STYLERULE_NODE=rule
PATH_NODE=path PATH_NODE=path
PAGE_NODE=page
MEDIA_NODE=media MEDIA_NODE=media
LOCATION_NODE=location LOCATION_NODE=location
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2016.
*
* This file is part of TULIP.
*
* TULIP is licensed under a dual-licensing scheme. For non-commercial purposes, the LGPL version 3,
* as stated below, is applicable. For all commercial purposes TULIP is licensed under a LIST
* proprietary license. Please contact LIST at tto@list.lu to obtain a commercial license.
*
* For all non-commercial purposes, TULIP is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, version 3 of the License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.content;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.widget.ZoomWidget;
/**
* Interface imposing functionality in order to be used with the {@link ZoomWidget}.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 1.0
* @version 2.3.0
*/
@NonNullByDefault
public interface Zoomable {
/**
* Method used to specify the initial position of the zoom.
*
* @param position
*/
public void centre(Point position);
/**
* Method used to request all zooms be translate to the specified position.
*
* @param position
* The position to centre on.
*/
public void translate(Point position);
/**
* Method used to zoom by a factor given as parameter The direction of the zoom depends whether
* the parameter is positive or negative.
*
* @param position
* Zoom factor contained in a {@link Point}.
*/
public void zoom(@Nullable Point position);
/**
* Method called by the manager handling the {@link Zoomable} instance when the state changed to
* where it is no longer considered zooming. This method provides the opportunity to run code
* only executable when the zooming stopped.
*/
public void stoppedZooming();
/**
* Method called by the manager handling the {@link Zoomable} instance when the state changed to
* where it is no longer considered moving. This method provides the opportunity to run code
* only executable when the moving stopped.
*/
public void stoppedMoving();
}
\ No newline at end of file
...@@ -215,6 +215,7 @@ public class Externalization extends NLS { ...@@ -215,6 +215,7 @@ public class Externalization extends NLS {
public static String POINTING_OFFSET_NODE; public static String POINTING_OFFSET_NODE;
public static String PATH_NODE; public static String PATH_NODE;
public static String PAGE_NODE;
public static String MEDIA_NODE; public static String MEDIA_NODE;
public static String LOCATION_NODE; public static String LOCATION_NODE;
......
...@@ -441,22 +441,6 @@ public class Point extends Float implements KdComparator<Point> { ...@@ -441,22 +441,6 @@ public class Point extends Float implements KdComparator<Point> {
return clone(); return clone();
} }
// switch (state.getClass().getSimpleName()) {
// case "TableCoordinates": //$NON-NLS-1$
// point.toTableCoordinates();
// break;
// case "ScreenCoordinates": //$NON-NLS-1$
// point.toScreenCoordinates();
// break;
// case "CameraCoordinates": //$NON-NLS-1$
// point.toCameraCoordinates();
// break;
// default:
// logger.error("This should never happen! State: {}!", state.toString()); //$NON-NLS-1$
// break;
// }
point.getState().toCoordinates(point, this.state.getClass()); point.getState().toCoordinates(point, this.state.getClass());
Point clone = clone(); Point clone = clone();
......
...@@ -30,6 +30,7 @@ import lu.list.itis.dkd.tui.widget.corona.Corona; ...@@ -30,6 +30,7 @@ import lu.list.itis.dkd.tui.widget.corona.Corona;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.collect.TreeMultimap;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Shape; import java.awt.Shape;
...@@ -47,10 +48,15 @@ import java.util.Set; ...@@ -47,10 +48,15 @@ import java.util.Set;
* Concretely, the {@link BaseWidget} caters to the base, including angle, of all handles and holds * Concretely, the {@link BaseWidget} caters to the base, including angle, of all handles and holds
* a map of all {@link Corona} instances defined for the widget and its handles. * a map of all {@link Corona} instances defined for the widget and its handles.
* *
* @author Nico Mack [nico.mack@list.lu]
* @author Eric Tobias [eric.tobias@list.lu] * @author Eric Tobias [eric.tobias@list.lu]
* @since 1.0 * @since 1.0
* @version 2.3.1 * @version 2.5.0
*/ */
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
@NonNullByDefault @NonNullByDefault
public class BaseWidget { public class BaseWidget {
/** Specifies the ID of the stage the widget is to be presented on. */ /** Specifies the ID of the stage the widget is to be presented on. */
...@@ -58,10 +64,10 @@ public class BaseWidget { ...@@ -58,10 +64,10 @@ public class BaseWidget {
/** The visual components to display as part of the widget. It's background. */ /** The visual components to display as part of the widget. It's background. */
protected Multimap<Integer, Corona> coronas; protected Multimap<Integer, Corona> coronas;
/** The positions where the widget's handles are at. */ /** The positions where the widget's handles are at. */
private HashMap<Integer, Point> positions; protected HashMap<Integer, Point> positions;
/** The name given to this widget. */ /** The name given to this widget. */
protected String name; protected String name;
/** Specifies whether this widget is an avatar for a tangible object or not. */ /** Specifies whether this widget is controlled by a tangible object or not. */
protected boolean tangible; protected boolean tangible;
/** Specifies whether multiple instances of the same widget may coexist or not. */ /** Specifies whether multiple instances of the same widget may coexist or not. */
protected boolean multipleInstances; protected boolean multipleInstances;
...@@ -71,54 +77,86 @@ public class BaseWidget { ...@@ -71,54 +77,86 @@ public class BaseWidget {
protected Shape definingShape; protected Shape definingShape;
@Nullable @Nullable
protected boolean onStage; protected boolean onStage;
/** /**
* Field keeping track of the current Shape of the widget for the purpose of determining which * Field keeping track of the current Shape of the widget for the purpose of determining which
* screen areas need to be redrawn. * screen areas need to be redrawn.
*/ */
@Nullable
protected Area clippingRegion; protected Area clippingRegion;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/** /**
* Constructor setting all fields as by the values specified by the builder. * Constructor setting all fields as by the values specified by the builder.
* *
* @param builder * @param builder
* The builder instance defining all parameters. * The builder instance defining all parameters.
*/ */
// ---------------------------------------------------------------------------
public BaseWidget(BaseBuilder<?> builder) { public BaseWidget(BaseBuilder<?> builder) {
coronas = builder.coronas;
positions = builder.positions;
name = builder.name; name = builder.name;
stageId = builder.stageId; stageId = builder.stageId;
tangible = builder.tangible;
multipleInstances = builder.multipleInstances;
networkAdapter = builder.networkAdapter; networkAdapter = builder.networkAdapter;
definingShape = builder.definingShape; definingShape = builder.definingShape;
coronas = builder.coronas;
positions = builder.positions;
clippingRegion = new Area(); clippingRegion = new Area();
onStage = false; onStage = false;
tangible = builder.tangible;
multipleInstances = builder.multipleInstances;
} }
// ---------------------------------------------------------------------------
/** /**
* Method invoked when a handle associated with the widget was moved. This default * Copy constructor to clone widget.
* implementation will set the base and angle fields to the corresponding values and update the
* coronas of the handle that the {@link TangibleObject} corresponds to.
* *
* @param tangibleObject * @param original
* The {@link TangibleObject} that was triggering the move. * specifies the original widget to create an exact copy from.
*/ */
public void actionMove(TangibleObject tangibleObject) { // ---------------------------------------------------------------------------
if (!tangible)
return; public BaseWidget(BaseWidget original) {
positions.put(tangibleObject.getObjectId(), new Point(tangibleObject.getX(), tangibleObject.getY(), tangibleObject.getAngle(), Math.signum(tangibleObject.getRotationSpeed()))); name = original.name;
updateCoronas(tangibleObject.getObjectId()); stageId = original.stageId;
tangible = original.tangible;
multipleInstances = original.multipleInstances;
networkAdapter = original.networkAdapter;
definingShape = original.definingShape;
coronas = original.cloneCoronas();
positions = original.clonePositions();
clippingRegion = new Area();
onStage = false;
} }
// ***************************************************************************
// * Constants *
// ***************************************************************************
protected static final double PI_HALF = Math.PI / 2;
protected static final double THREE_PI_HALF = 1.5 * Math.PI;
protected static final double TWO_PI = 2 * Math.PI;
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitive(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/** /**
* Method used to keep the conona's positions aligned with the base of the widget. * Method used to keep the conona's positions aligned with the base of the widget.
* *
* @pre <code>positions.contains(handleID);</code> * @pre <code>positions.contains(handleID);</code>
* @post <code>forAll coronas.get(handleID) as corona : corona.getCentre().equals(positions.get(handleID);</code> * @post <code>forAll coronas.get(handleID) as corona : corona.getCentre().equals(positions.get(handleID);</code>
*/ */
// ---------------------------------------------------------------------------
private void updateCoronas(int handleID) { private void updateCoronas(int handleID) {
assert positions.get(handleID) != null; assert positions.get(handleID) != null;
Point centre = getPosition(handleID); Point centre = getPosition(handleID);
...@@ -128,37 +166,42 @@ public class BaseWidget { ...@@ -128,37 +166,42 @@ public class BaseWidget {
} }
} }
// ---------------------------------------------------------------------------
/** /**
* @return * Method used to activate all appropriate visualisations due to the handle having been dropped
* onto the table.
*
* @param handleID
* The ID of the handle that was dropped.
*/ */
public boolean isTangible() { // ---------------------------------------------------------------------------
return this.tangible;
private void startDrawingIfAppropriate(int handleID) {
for (Corona corona : coronas.get(handleID)) {
corona.onTable();
}
} }
// ---------------------------------------------------------------------------
/** /**
* @param showIt * Method used to cancel all visualisations for this widget's handle.
*
* @param handleID
* The handle that was lifted.
*/ */
public void setVisible(boolean showIt) { // ---------------------------------------------------------------------------
if (tangible) {
return;
}
if (showIt) { private void stopDrawing(int handleID) {
for (Entry<Integer, Point> handle : positions.entrySet()) { for (Corona corona : coronas.get(handleID)) {
if (handle.getValue() == null) { corona.setActive(false);
handle.setValue(new Point());
}
updateCoronas(handle.getKey());
startDrawingIfAppropriate(handle.getKey());
}
} else {
for (Entry<Integer, Point> handle : positions.entrySet()) {
stopDrawing(handle.getKey());
}
} }
} }
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/** /**
* Method invoked when the tangible is detected on the table surface for the first time. The * Method invoked when the tangible is detected on the table surface for the first time. The
* {@link BaseWidget} instance is set to be active. * {@link BaseWidget} instance is set to be active.
...@@ -166,6 +209,8 @@ public class BaseWidget { ...@@ -166,6 +209,8 @@ public class BaseWidget {
* @param tangibleObject * @param tangibleObject
* The {@link TangibleObject} that was triggering the drop action. * The {@link TangibleObject} that was triggering the drop action.
*/ */
// ---------------------------------------------------------------------------
public void actionDrop(TangibleObject tangibleObject) { public void actionDrop(TangibleObject tangibleObject) {
if (!tangible) if (!tangible)
return; return;
...@@ -174,19 +219,25 @@ public class BaseWidget { ...@@ -174,19 +219,25 @@ public class BaseWidget {
startDrawingIfAppropriate(tangibleObject.getObjectId()); startDrawingIfAppropriate(tangibleObject.getObjectId());
} }
// ---------------------------------------------------------------------------
/** /**
* Method used to activate all appropriate visualisations due to the handle having been dropped * Method invoked when a handle associated with the widget was moved. This default
* onto the table. * implementation will set the base and angle fields to the corresponding values and update the
* coronas of the handle that the {@link TangibleObject} corresponds to.
* *
* @param handleID * @param tangibleObject
* The ID of the handle that was dropped. * The {@link TangibleObject} that was triggering the move.
*/ */
private void startDrawingIfAppropriate(int handleID) { // ---------------------------------------------------------------------------
for (Corona corona : coronas.get(handleID)) {
corona.onTable(); public void actionMove(TangibleObject tangibleObject) {
} if (!tangible)
return;
positions.put(tangibleObject.getObjectId(), new Point(tangibleObject.getX(), tangibleObject.getY(), tangibleObject.getAngle(), Math.signum(tangibleObject.getRotationSpeed())));
updateCoronas(tangibleObject.getObjectId());
} }
// ---------------------------------------------------------------------------
/** /**
* Method invoked when the tangible is removed from the table surface. The {@link BaseWidget} * Method invoked when the tangible is removed from the table surface. The {@link BaseWidget}
* instance will be set to no longer be active. * instance will be set to no longer be active.
...@@ -194,6 +245,8 @@ public class BaseWidget { ...@@ -194,6 +245,8 @@ public class BaseWidget {
* @param tangibleObject * @param tangibleObject
* The TangibleObject that was triggering the drop action. * The TangibleObject that was triggering the drop action.
*/ */
// ---------------------------------------------------------------------------
public void actionLift(TangibleObject tangibleObject) { public void actionLift(TangibleObject tangibleObject) {
if (!tangible) if (!tangible)
return; return;
...@@ -202,18 +255,49 @@ public class BaseWidget { ...@@ -202,18 +255,49 @@ public class BaseWidget {
onStage = false; onStage = false;
} }
// ---------------------------------------------------------------------------
/** /**
* Method used to cancel all visualisations for this widget's handle. * allows checking whether this widget is controlled by a tangible object or whether it can
* exist autonomously.
* *
* @param handleID * @return <code>true</code> if widget is controlled by a tangible object (default), <code>
* The handle that was lifted. * false</code> otherwise.
*/ */
private void stopDrawing(int handleID) { // ---------------------------------------------------------------------------
for (Corona corona : coronas.get(handleID)) {
corona.setActive(false); public boolean isTangible() {
return this.tangible;
}
// ---------------------------------------------------------------------------
/**
* @param showIt
*/
// ---------------------------------------------------------------------------
public void setVisible(boolean showIt) {
if (tangible) {
return;
}
if (showIt) {
for (Entry<Integer, Point> handle : positions.entrySet()) {
if (handle.getValue() == null) {
handle.setValue(new Point());
}
updateCoronas(handle.getKey());
startDrawingIfAppropriate(handle.getKey());
}
} else {
for (Entry<Integer, Point> handle : positions.entrySet()) {
stopDrawing(handle.getKey());
} }
} }
}
// ---------------------------------------------------------------------------
/** /**
* Draws a the visual feedback for any widget. The method will iterate through all coronas and * Draws a the visual feedback for any widget. The method will iterate through all coronas and
* call their <code>paint()</code> method. The consistency of their position is ensured due to * call their <code>paint()</code> method. The consistency of their position is ensured due to
...@@ -223,6 +307,8 @@ public class BaseWidget { ...@@ -223,6 +307,8 @@ public class BaseWidget {
* @param canvas * @param canvas
* The Graphics2D instance to draw on. * The Graphics2D instance to draw on.
*/ */
// ---------------------------------------------------------------------------
public void paint(Graphics2D canvas) { public void paint(Graphics2D canvas) {
clippingRegion.reset(); clippingRegion.reset();
for (Corona corona : coronas.values()) { for (Corona corona : coronas.values()) {
...@@ -234,38 +320,63 @@ public class BaseWidget { ...@@ -234,38 +320,63 @@ public class BaseWidget {
} }
} }
// ---------------------------------------------------------------------------
/** /**
* Method for retrieving all coronas of a particular class. * Method for retrieving all coronas of a particular class.
* *
* @param <T> * @param <T>
* The type of the class of corona to retrieve. * The type of the class of corona to retrieve.
* @param _class * @param clazz
* The class of corona's to retrieve. * The class of corona's to retrieve.
* @return A list of coronas matching the criterion. * @return A list of coronas matching the criterion.
*/ */
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> List<T> getCoronas(Class<T> _class) { public <T> List<T> getCoronas(Class<T> clazz) {
List<T> results = new ArrayList<>(); List<T> results = new ArrayList<>();
for (Corona corona : coronas.values()) { for (Corona corona : coronas.values()) {
if (_class.isAssignableFrom(corona.getClass())) { if (clazz.isAssignableFrom(corona.getClass())) {
results.add((T) corona); results.add((T) corona);
} }
} }
return results; return results;
} }
// ---------------------------------------------------------------------------
/** /**
* Method for retrieving all coronas held by any of the handles associated to this widget. * Method for retrieving all coronas held by any of the handles associated to this widget.
* *
* @return A {@link List} holding all coronas that are attached to any of the handles associated * @return A {@link List} holding all coronas that are attached to any of the handles associated
* to this {@link BaseWidget}'s concrete instance. * to this {@link BaseWidget}'s concrete instance.
*/ */
// ---------------------------------------------------------------------------
public List<Corona> getCoronas() { public List<Corona> getCoronas() {
return new ArrayList<>(coronas.values());