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
STYLERULE_NODE=rule
PATH_NODE=path
PAGE_NODE=page
MEDIA_NODE=media
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 {
public static String POINTING_OFFSET_NODE;
public static String PATH_NODE;
public static String PAGE_NODE;
public static String MEDIA_NODE;
public static String LOCATION_NODE;
......
......@@ -441,22 +441,6 @@ public class Point extends Float implements KdComparator<Point> {
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 clone = clone();
......
......@@ -36,36 +36,53 @@ import java.util.ArrayList;
* @since 1.0
* @version 2.3.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
@NonNullByDefault
public class ContentWidget extends BaseWidget {
/** List of {@link ContentEventListener} instances listening to updates to content. */
protected ArrayList<ContentEventListener> listeners = new ArrayList<>();
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Constructor making use of the super constructor to initialise all fields.
*
* @param builder
* The builder instance defining all parameters.
*/
// ---------------------------------------------------------------------------
public ContentWidget(BaseContentBuilder<?> builder) {
super(builder);
listeners = builder.listeners;
}
// ---------------------------------------------------------------------------
/**
* Method invoked when the tangible is detected on the table surface for the first time. The
* {@link BaseWidget} instance is set to be active.
* Copy constructor to clone widget.
*
* @param tangibleObject
* The {@link TangibleObject} that triggered the drop action.
* @param original
* specifies the original widget to create an exact copy from.
*/
@Override
public void actionDrop(TangibleObject tangibleObject) {
super.actionDrop(tangibleObject);
notify(this, tangibleObject.getObjectId(), ContentEventType.DROP);
}
// ---------------------------------------------------------------------------
public ContentWidget(ContentWidget original) {
super(original);
listeners = new ArrayList<>(original.listeners);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitive(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Method used to notify all listeners to a specific event.
*
......@@ -76,26 +93,35 @@ public class ContentWidget extends BaseWidget {
* @param type
* The type of the triggered event.
*/
// ---------------------------------------------------------------------------
private void notify(ContentWidget widget, int handleID, ContentEventType type) {
for (ContentEventListener listener : listeners) {
listener.contentUpdated(new ContentEvent(widget, handleID, type));
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Method invoked when the tangible is removed from the table surface. The {@link BaseWidget}
* instance will be set to no longer be active.
* Method invoked when the tangible is detected on the table surface for the first time. The
* {@link BaseWidget} instance is set to be active.
*
* @param tangibleObject
* The {@link TangibleObject} that was triggering the lift action.
* The {@link TangibleObject} that triggered the drop action.
*/
// ---------------------------------------------------------------------------
@Override
public void actionLift(TangibleObject tangibleObject) {
super.actionLift(tangibleObject);
notify(this, tangibleObject.getObjectId(), ContentEventType.LIFT);
public void actionDrop(TangibleObject tangibleObject) {
super.actionDrop(tangibleObject);
notify(this, tangibleObject.getObjectId(), ContentEventType.DROP);
}
// ---------------------------------------------------------------------------
/**
* Method invoked when a handle associated with the widget was moved. This default
* implementation will set the base and angle fields to the corresponding values and update the
......@@ -104,13 +130,31 @@ public class ContentWidget extends BaseWidget {
* @param tangibleObject
* The {@link TangibleObject} that was triggering the move.
*/
// ---------------------------------------------------------------------------
@Override
public void actionMove(TangibleObject tangibleObject) {
super.actionMove(tangibleObject);
notify(this, tangibleObject.getObjectId(), ContentEventType.MANIPULATION);
}
// ---------------------------------------------------------------------------
/**
* Method invoked when the tangible is removed from the table surface. The {@link BaseWidget}
* instance will be set to no longer be active.
*
* @param tangibleObject
* The {@link TangibleObject} that was triggering the lift action.
*/
// ---------------------------------------------------------------------------
@Override
public void actionLift(TangibleObject tangibleObject) {
super.actionLift(tangibleObject);
notify(this, tangibleObject.getObjectId(), ContentEventType.LIFT);
}
// ---------------------------------------------------------------------------
/**
* Method used to determine which segment the rotation of a handle is in given the number of
* segments to cover by one rotation.
......@@ -120,13 +164,25 @@ public class ContentWidget extends BaseWidget {
* @param handleID
* The ID of the handle to check the rotation for.
* @return The segment the rotation is currently in given:<br>
* <code>rotation / (PI / totalSegments)</code>.
* <code>rotation / (2*PI / totalSegments)</code>.
*/
public int getSegment(int totalSegments, int handleID) {
// float rotation = Math.abs(positions.get(handleID).getAngle());
float rotation = Math.abs(getPosition(handleID).getAngle());
float segmentSize = (float) (2 * Math.PI / totalSegments);
// ---------------------------------------------------------------------------
// public int getSegment(int totalSegments, int handleID) {
// Preconditions.checkArgument(totalSegments > 0, "Number of segments MUST be greater than 0!");
// //$NON-NLS-1$
//
// double rotation = Math.abs(getPosition(handleID).getAngle() % TWO_PI);
// double segmentSize = (float) (TWO_PI / totalSegments);
//
// return (int) Math.floor(rotation / segmentSize);
// }
// ---------------------------------------------------------------------------
return (int) Math.floor(rotation / segmentSize);
@Override
public ContentWidget clone() {
return new ContentWidget(this);
}
}
\ No newline at end of file
......@@ -29,15 +29,15 @@ import lu.list.itis.dkd.tui.exception.UnsupportedInformationException;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.widget.builder.BaseContextAwareBuilder;
import lu.list.itis.dkd.tui.widget.corona.ContextAwareCorona;
import lu.list.itis.dkd.tui.widget.corona.Corona;
import com.google.common.collect.HashMultimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
import java.util.List;
import TUIO.TuioCursor;
......@@ -47,11 +47,16 @@ import TUIO.TuioCursor;
* set of listeners of any changes in its state in regard to placement. This is done to give
* listeners as much context information as possible.<br>
*
* @author Nico Mack [nico.mack@list.lu]
* @author Valérie Maquil [valerie.maquil@list.lu]
* @author Eric Tobias [eric.tobias@list.lu]
* @since 1.0
* @version 2.3.0
* @version 2.5.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
@NonNullByDefault
public abstract class ContextAwareWidget extends BaseWidget {
/**
......@@ -60,10 +65,19 @@ public abstract class ContextAwareWidget extends BaseWidget {
*/
protected volatile HashMultimap<ContextAwareCorona, ContextEventListener> contextEventListeners = HashMultimap.create();
/** Field holding all listeners to be notified on events changing the global state. */
protected volatile Vector<ContextEventListener> globalStateListeners = new Vector<>();
protected volatile List<ContextEventListener> globalStateListeners = new ArrayList<>();
// ***************************************************************************
// * Constants *
// ***************************************************************************
private static final Logger logger = LoggerFactory.getLogger(ContextAwareWidget.class.getSimpleName());
private static final Logger LOGGER = LoggerFactory.getLogger(ContextAwareWidget.class.getSimpleName());
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Constructor making use of the super constructor to initialise all fields.
*
......@@ -76,67 +90,122 @@ public abstract class ContextAwareWidget extends BaseWidget {
globalStateListeners = builder.globalStateListeners;
}
// ---------------------------------------------------------------------------
/**
* Copy constructor to clone widget.
*
* @param original
* specifies the original widget to create an exact copy from.
*/
// ---------------------------------------------------------------------------
public ContextAwareWidget(ContextAwareWidget original) {
super(original);
contextEventListeners = HashMultimap.create(original.contextEventListeners);
globalStateListeners = new ArrayList<>(original.globalStateListeners);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitive(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Method used to notify all listeners of a context event.
*
* @param event
* The {@link ContextEvent} instance to notify the listeners of.
*/
// ---------------------------------------------------------------------------
private void notifyStateListeners(ContextEvent event) {
for (ContextEventListener listener : globalStateListeners) {
try {
listener.contextUpdated(event);
} catch (UnsupportedInformationException e) {
LOGGER.warn("The listener could not process the provided information!", e); //$NON-NLS-1$
}
}
}
// ---------------------------------------------------------------------------
/**
* Method invoked when a cursor is placed.
*
* @param tangibleCursor
* The cursor that was placed.
*/
// ---------------------------------------------------------------------------
public abstract void cursorPlaced(TuioCursor tangibleCursor);
// ---------------------------------------------------------------------------
/**
* Method invoked when a cursor is moved.
*
* @param tangibleCursor
* The cursor that has moved.
*/
// ---------------------------------------------------------------------------
public abstract void cursorMoved(TuioCursor tangibleCursor);
// ---------------------------------------------------------------------------
/**
* Method invoked when a cursor is removed.
*
* @param tangibleCursor
* The cursor that was removed.
*/
// ---------------------------------------------------------------------------
public abstract void cursorRemoved(TuioCursor tangibleCursor);
// ---------------------------------------------------------------------------
/**
* {@inheritDoc}<br>
* <br>
*
* The method notifies all global state listeners of the lifting.
*/
// ---------------------------------------------------------------------------
@Override
public void actionLift(TangibleObject tangibleObject) {
notifyStateListeners(new ContextEvent(this, ContextType.OBJECT_LIFT, null));
super.actionLift(tangibleObject);
}
// ---------------------------------------------------------------------------
/**
* {@inheritDoc}<br>
* <br>
*
* The method notifies all global state listeners of the dropping.
*/
// ---------------------------------------------------------------------------
@Override
public void actionDrop(TangibleObject tangibleObject) {
notifyStateListeners(new ContextEvent(this, ContextType.OBJECT_DROP, null));
super.actionDrop(tangibleObject);
}
// ---------------------------------------------------------------------------
/**
* {@inheritDoc}<br>
* <br>
*
* The method notifies all global state listeners of the moving.
*/
// ---------------------------------------------------------------------------
@Override
public void actionMove(TangibleObject tangibleObject) {
notifyStateListeners(new ContextEvent(this, ContextType.OBJECT_MOVE, null));
super.actionMove(tangibleObject);
}
// ---------------------------------------------------------------------------
/**
* Method used to return all {@link ContextAwareCorona} instances where the point would be
* included in.
......@@ -147,27 +216,33 @@ public abstract class ContextAwareWidget extends BaseWidget {
* contained in.
* @see ContextAwareCorona#includesInCorona(Point)
*/
protected Collection<ContextAwareCorona> retrieveCoronas(Point point) {
Collection<ContextAwareCorona> results = new Vector<>();
// ---------------------------------------------------------------------------
protected Collection<ContextAwareCorona> getContextAwareCoronasAt(Point point) {
Collection<ContextAwareCorona> results = new ArrayList<>();
for (Corona corona : getCoronas(ContextAwareCorona.class)) {
if (((ContextAwareCorona) corona).includesInCorona(corona.getPointRelativeToCentre(point))) {
results.add((ContextAwareCorona) corona);
for (ContextAwareCorona corona : getCoronas(ContextAwareCorona.class)) {
if (corona.includesInCorona(corona.getPointRelativeToCentre(point))) {
results.add(corona);
}
}
return results;
}
// ---------------------------------------------------------------------------
/**
* Method for retrieving all {@link ContextAwareCorona} instances linked to this widget.
*
* @return A {@link Collection} of {@link ContextAwareCorona} instances held by this
* {@link ContextAwareWidget} instance.
*/
protected Collection<ContextAwareCorona> retrieveCoronas() {
// ---------------------------------------------------------------------------
protected Collection<ContextAwareCorona> retrieveAllContextAwareCoronas() {
return getCoronas(ContextAwareCorona.class);
}
// ---------------------------------------------------------------------------
/**
* Method used to add more listeners to the map.
*
......@@ -176,33 +251,29 @@ public abstract class ContextAwareWidget extends BaseWidget {
* @param listener
* The {@link ContextEventListener} that is doing the listening.
*/
// ---------------------------------------------------------------------------
public void addContextEventListener(ContextAwareCorona listenee, ContextEventListener listener) {
contextEventListeners.put(listenee, listener);
}
// ---------------------------------------------------------------------------
/**
* Method used to add a listener.
*
* @param listener
* The {@link ContextEventListener} that is doing the listening.
*/
// ---------------------------------------------------------------------------
public void addGlobalStateListener(ContextEventListener listener) {
globalStateListeners.add(listener);
}
/**
* Method used to notify all listeners of a context event.
*
* @param event
* The {@link ContextEvent} instance to notify the listeners of.
*/
private void notifyStateListeners(ContextEvent event) {
for (ContextEventListener listener : globalStateListeners) {
try {
listener.contextUpdated(event);
} catch (UnsupportedInformationException e) {
logger.warn("The listener could not process the provided information!", e); //$NON-NLS-1$
}
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
// ***************************************************************************
// ---------------------------------------------------------------------------
}
\ No newline at end of file
......@@ -35,27 +35,60 @@ import org.slf4j.LoggerFactory;
/**
* Widget used to display information in a box next to the summit of a, usually, triangular shape.
*
* @author Nico Mack [nico.mack@list.lu]
* @author Eric Tobias [eric.tobias@list.lu]
* @since 1.0
* @version 2.3.0
* @version 2.5.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
@NonNullByDefault
public class InfoWidget extends TetherableWidget implements TetherListener {
private static Logger LOGGER = LoggerFactory.getLogger(InfoWidget.class.getSimpleName());
private static final Logger LOGGER = LoggerFactory.getLogger(InfoWidget.class.getSimpleName());
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* Constructor setting all fields by calling the super constructor.
*
* @param builder
* The builder instance defining all parameters.
*/
// ---------------------------------------------------------------------------
public InfoWidget(BaseInfoBuilder<InfoWidgetBuilder> builder) {
super(builder);
this.addTetherListener(this);
}
// ---------------------------------------------------------------------------
/**
* Copy constructor to clone widget.
*
* @param original
* specifies the original widget to create an exact copy from.
*/
// ---------------------------------------------------------------------------