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

Removal of obsolete coronas. Implementation of multiple screens.

Preparations for multiple widgets with identical IDs.
parent bcedad68
......@@ -20,8 +20,11 @@
<circleSize>60</circleSize>
<triangleSize>80</triangleSize>
<squareSize>50</squareSize>
<cornerRadius>10</cornerRadius>
<rectangleWidth>40</rectangleWidth>
<rectangleHeight>20</rectangleHeight>
<movementThreshold>0.01</movementThreshold>
<rotationThreshold>0.2</rotationThreshold>
</table>
<window>
<!-- <x>1550</x>
......
......@@ -171,3 +171,19 @@ STYLESHEET_NODE=styleSheet
STYLERULE_NODE=rule
PATH_NODE=path
MEDIA_NODE=media
LOCATION_NODE=location
VOLUME_NODE=volume
PREFETCH_NODE=prefetch
STAGESET_NODE=stageSet
STAGE_NODE=stage
STAGE_ID_NODE=stageId
SCREEN_ID_NODE=screenId
BOUNDS_NODE=bounds
FULLSCREEN_NODE=fullScreen
STAGE_POSITION_NODE=stagePosition
TANGIBLE_NODE=tangible
MULTIPLE_INSTANCES_NODE=multipleInstances
......@@ -127,6 +127,12 @@
<artifactId>batik-parser</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jmf</artifactId>
<version>2.1.1e</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<repositories>
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved.
*
* This file is part of TULIP.
*
* 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;
import lu.list.itis.dkd.tui.bootstrapping.StageSetBootstrapper;
import lu.list.itis.dkd.tui.content.Content;
import lu.list.itis.dkd.tui.content.Stage;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.widget.BaseWidget;
import org.jdom2.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class StageManager {
protected static volatile ConcurrentHashMap<Integer, Stage> stageMap = new ConcurrentHashMap<>();
private static final int TANGIBLE_INTERFACE_STAGE_ID = 0;
protected static final Logger LOGGER = LoggerFactory.getLogger(StageManager.class.getSimpleName());
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param properties
* @param bootstrap
* @throws BuildException
*/
// ---------------------------------------------------------------------------
public StageManager(Properties properties, Document bootstrap) throws BuildException {
TangibleInterfaceManager interfaceManager = new TangibleInterfaceManager(properties);
stageMap.put(TANGIBLE_INTERFACE_STAGE_ID, interfaceManager);
StageSetBootstrapper stageSet = new StageSetBootstrapper(bootstrap);
Map<Integer, Stage> stages = stageSet.setup();
for (Entry<Integer, Stage> entry : stages.entrySet()) {
Stage stage = entry.getValue();
if (entry.getKey() == TANGIBLE_INTERFACE_STAGE_ID) {
LOGGER.error("Stage ID {} is reserved for the Tangible Interface! Assign a different ID to stage {}!", entry.getKey(), stage.getName()); //$NON-NLS-1$
} else if (stageMap.containsKey(entry.getKey())) {
LOGGER.error("Stage {} with ID {} conflicts with already existing stage!", stage.getName(), stage.getStageId()); //$NON-NLS-1$
} else {
stageMap.put(stage.getStageId(), stage);
}
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
*
*/
public void doneLoading() {
for (Entry<Integer, Stage> entry : stageMap.entrySet()) {
Stage stage = entry.getValue();
stage.doneLoading();
}
}
// ---------------------------------------------------------------------------
/**
*
*/
// ---------------------------------------------------------------------------
public void repaint() {
for (Entry<Integer, Stage> entry : stageMap.entrySet()) {
Stage stage = entry.getValue();
stage.repaint();
}
}
// ---------------------------------------------------------------------------
/**
*
*/
// ---------------------------------------------------------------------------
public void show() {
for (Entry<Integer, Stage> entry : stageMap.entrySet()) {
Stage stage = entry.getValue();
stage.setupWindow();
stage.showWindow();
}
}
// ---------------------------------------------------------------------------
/**
* @param contents
*/
// ---------------------------------------------------------------------------
public void setContent(Collection<Entry<String, Content>> contents) {
if (contents != null) {
for (Entry<String, Content> entry : contents) {
Content content = entry.getValue();
if (stageMap.containsKey(content.getStageId())) {
stageMap.get(content.getStageId()).addToStage(entry.getKey(), content);
}
}
}
}
// ---------------------------------------------------------------------------
/**
* @param objects
*/
// ---------------------------------------------------------------------------
public void setObjects(Collection<Entry<Integer, BaseWidget>> objects) {
if (objects != null) {
for (Entry<Integer, BaseWidget> entry : objects) {
BaseWidget widget = entry.getValue();
if (stageMap.containsKey(widget.getStageId())) {
stageMap.get(widget.getStageId()).addToStage(entry.getKey(), widget);
if (!widget.isTangible()) {
widget.setVisible(true);
}
}
}
}
}
// ---------------------------------------------------------------------------
/**
* @return
*/
// ---------------------------------------------------------------------------
public TangibleInterfaceManager getInterfaceManager() {
return (TangibleInterfaceManager) stageMap.get(TANGIBLE_INTERFACE_STAGE_ID);
}
}
......@@ -24,23 +24,27 @@ import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.adapter.TuiAdapter;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.utility.GlobalContext;
import lu.list.itis.dkd.tui.utility.IdMapper;
import lu.list.itis.dkd.tui.utility.PropertiesFetcher;
import org.pushingpixels.trident.Timeline.RepeatBehavior;
import com.google.common.base.Strings;
import org.jdom2.Document;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.pushingpixels.trident.TridentConfig;
import org.pushingpixels.trident.swing.SwingRepaintTimeline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Properties;
import java.util.Vector;
import javax.swing.JComponent;
/**
* Abstract class holding the basic necessities to have in a tangible application. The class
* initialises properties and holds references to all managers. The class is used to configure the
......@@ -55,6 +59,8 @@ import javax.swing.JComponent;
public abstract class TangibleApplication {
protected static Properties properties = new Properties();
protected Document bootstrap;
static {
System.setProperty("log4j.configurationFile", "log4j2.xml"); //$NON-NLS-1$//$NON-NLS-2$
}
......@@ -62,12 +68,6 @@ public abstract class TangibleApplication {
/** A {@link Logger} to log all messages during execution. */
protected static final Logger logger = LoggerFactory.getLogger(TangibleApplication.class.getSimpleName());
/**
* An implementation of the {@link JComponent} handling all interactions with the visual
* interface as well as its construction.
*/
private static TangibleInterfaceManager interfaceManager;
/**
* An instance of the {@link TangibleObjectManager} to handle all interactions with tangible
* objects piloting the interface.
......@@ -87,25 +87,39 @@ public abstract class TangibleApplication {
@Nullable
protected TangibleContentManager contentManager;
protected static StageManager stageManager;
/**
* Constructor initialising several basic properties.
*
* @param newPropertiesFileUri
* The URI of the properties file to load.
* @throws BuildException
*/
protected TangibleApplication(@Nullable String newPropertiesFileUri) {
/** Frame rate for animation expressed in FPS (Frames per Second) */
private static final int ANIMATION_FRAME_RATE = 25;
protected TangibleApplication(@Nullable String newPropertiesFileUri) throws BuildException {
if (newPropertiesFileUri != null && !newPropertiesFileUri.isEmpty()) {
properties = PropertiesFetcher.fetchProperties(newPropertiesFileUri);
} else {
properties = PropertiesFetcher.fetchProperties();
}
properties = PropertiesFetcher.fetchProperties();
String bootstrapURI = properties.getProperty("bootstrapping.root", "bootstrapping.xml"); //$NON-NLS-1$//$NON-NLS-2$
bootstrap = parseBootstrapDocument(bootstrapURI);
// setupAnimation();
// configureLogger(logger);
// interfaceManager = new TangibleInterfaceManager(this, logger, properties);
interfaceManager = new TangibleInterfaceManager(this, properties);
stageManager = new StageManager(properties, bootstrap);
stageManager.show();
try {
adapter = (TuiAdapter) Class.forName(properties.getProperty("adapter.class")).getConstructor(TangibleInterfaceManager.class).newInstance(interfaceManager); //$NON-NLS-1$
adapter = (TuiAdapter) Class.forName(properties.getProperty("adapter.class")).getConstructor(StageManager.class).newInstance(stageManager); //$NON-NLS-1$
} catch (InstantiationException e) {
logger.error("Cannot instantiate adapter. The adapter must be a concrete class, not an interface or abstract class and it must directly inherit from TuiAdapter! Check your properties file and set adapter.class accordingly."); //$NON-NLS-1$
System.exit(42);
......@@ -129,38 +143,33 @@ public abstract class TangibleApplication {
System.exit(42);
}
TridentConfig.getInstance().setPulseSource(new TridentConfig.FixedRatePulseSource(40));
new SwingRepaintTimeline(TangibleApplication.getInterfaceManager()).playLoop(RepeatBehavior.LOOP);
GlobalContext.setGlobalContext(adapter);
IdMapper.initialise(properties);
// EventLogger.getInstance().initialise(properties);
}
/**
* Method for configuring a {@link Logger} with several properties as given by the loader
* properties file.
*
* @param unconfiguredLogger
* The {@link Logger} instance to configure.
* @pre logger != null
*/
// public static void configureLogger(final Logger unconfiguredLogger) {
// Preconditions.checkArgument(unconfiguredLogger != null, "The provider logger must not be
// null!"); //$NON-NLS-1$
//
// if (Boolean.parseBoolean(properties.getProperty("logger.event.output.enabled"))) {
// //$NON-NLS-1$
// try {
// unconfiguredLogger.addHandler(new
// FileHandler(properties.getProperty("logger.event.output.location"), true)); //$NON-NLS-1$
// } catch (IOException e) {
// unconfiguredLogger.log(Level.SEVERE, "The logger could not be configured!", e); //$NON-NLS-1$
// }
// }
// unconfiguredLogger.setLevel(Level.parse(properties.getProperty("logger.level", "ALL")));
// //$NON-NLS-1$ //$NON-NLS-2$
// }
protected Document parseBootstrapDocument(String bootstrapURI) throws BuildException {
Document document = null;
if (Strings.isNullOrEmpty(bootstrapURI)) {
throw new BuildException("The provided bootstrap URI may not be null or empty!"); //$NON-NLS-1$
}
SAXBuilder builder = new SAXBuilder();
File xmlFile = new File(bootstrapURI);
try {
document = builder.build(xmlFile);
} catch (IOException | JDOMException e) {
logger.error("Error occured while trying to process the object bootstrapping file!", e); //$NON-NLS-1$
throw new BuildException("The document containing the bootstrapping information could not be processed!", e); //$NON-NLS-1$
}
return document;
}
public void setupAnimation() {
TridentConfig.getInstance().setPulseSource(new TridentConfig.FixedRatePulseSource(1000 / ANIMATION_FRAME_RATE));
}
/**
* Method used to connect the application to the NUI device sending out events for the listener
......@@ -191,7 +200,7 @@ public abstract class TangibleApplication {
* @return The value of interfaceManager.
*/
public static TangibleInterfaceManager getInterfaceManager() {
return interfaceManager;
return stageManager.getInterfaceManager();
}
/**
......@@ -210,9 +219,9 @@ public abstract class TangibleApplication {
* @param interfaceManager
* The value to set interfaceManager to.
*/
public void setInterfaceManager(TangibleInterfaceManager interfaceManager) {
TangibleApplication.interfaceManager = interfaceManager;
}
// public void setInterfaceManager(TangibleInterfaceManager interfaceManager) {
// TangibleApplication.interfaceManager = interfaceManager;
// }
/**
* Simple setter method for objectManager.
......@@ -243,8 +252,19 @@ public abstract class TangibleApplication {
*/
public void setContentManager(TangibleContentManager contentManager) {
this.contentManager = contentManager;
stageManager.setContent(contentManager.getContents());
}
/**
*
*/
public void doneLoading() {
stageManager.setObjects(TangibleObjectManager.getWidgets());
stageManager.doneLoading();
stageManager.repaint();
}
/**
* Method for retrieving all active {@link TangibleObject} cursors from the {@link TuiAdapter}
* client managing the global state of the application.
......
......@@ -23,8 +23,6 @@ package lu.list.itis.dkd.tui;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.content.Content;
import lu.list.itis.dkd.tui.content.InformationProvider;
import lu.list.itis.dkd.tui.content.Zoomable;
import lu.list.itis.dkd.tui.event.ContentEventListener;
import com.google.common.collect.HashMultimap;
......@@ -33,6 +31,8 @@ import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
......@@ -46,26 +46,27 @@ import java.util.concurrent.ConcurrentHashMap;
@NonNullByDefault
public abstract class TangibleContentManager implements ContentEventListener {
/** Mapping of handle IDs to the content they manipulate. */
protected volatile Multimap<Integer, Content> contentMapping = HashMultimap.create();
protected volatile Multimap<String, Content> contentMapping = HashMultimap.create();
/**
* Getter method returning the content managed by this instance.
*
* @return The content managed by this instance, might not be sorted.
*/
public synchronized @Nullable Collection<Content> getContents() {
return contentMapping.values();
public synchronized @Nullable Collection<Entry<String, Content>> getContents() {
return contentMapping.entries();
}
/**
* Getter method for returning content mapped to a specific key.
*
* @param key
* The key to retrieve the content for.
* @return The content accessible by the key and managed by this instance, might not be sorted.
* @param identifier
* The identifier to retrieve the content for.
* @return The content accessible by the identifier and managed by this instance, might not be
* sorted.
*/
public synchronized @Nullable Collection<Content> getContents(int key) {
return contentMapping.get(key);
public synchronized @Nullable Collection<Content> getContents(String identifier) {
return contentMapping.get(identifier);
}
/**
......@@ -80,25 +81,32 @@ public abstract class TangibleContentManager implements ContentEventListener {
* extends the desired class.
*/
@SuppressWarnings("unchecked")
public synchronized <I> ConcurrentHashMap<String, I> getContent(Class<I> desiredParent) {
public synchronized <I> Map<String, I> getContent(Class<I> desiredParent) {
ConcurrentHashMap<String, I> implementingContent = new ConcurrentHashMap<>();
if (desiredParent.equals(Zoomable.class)) {
for (Content content : contentMapping.values()) {
if (content instanceof Zoomable) {
implementingContent.put(content.getIdentifier(), (I) content);
}
}
}
// if (desiredParent.equals(Zoomable.class)) {
// for (Content content : contentMapping.values()) {
// if (content instanceof Zoomable) {
// implementingContent.put(content.getIdentifier(), (I) content);
// }
// }
// }
//
// if (desiredParent.equals(InformationProvider.class)) {
// for (Content content : contentMapping.values()) {
// if (content instanceof InformationProvider) {
// implementingContent.put(content.getIdentifier(), (I) content);
// }
// }
// }
if (desiredParent.equals(InformationProvider.class)) {
for (Content content : contentMapping.values()) {
if (content instanceof InformationProvider) {
implementingContent.put(content.getIdentifier(), (I) content);
}
for (Content content : contentMapping.values()) {
if (desiredParent.isAssignableFrom(content.getClass())) {
implementingContent.put(content.getIdentifier(), (I) content);
}
}
return implementingContent;
}
......@@ -121,6 +129,7 @@ public abstract class TangibleContentManager implements ContentEventListener {
*/
public abstract Collection<Content> getDrawableContents();
/**
* Method used to map a handle ID to a {@link Content} instance.
*
......@@ -129,8 +138,8 @@ public abstract class TangibleContentManager implements ContentEventListener {
* @param value
* The {@link Content} instance to map to.
*/
protected synchronized void addMapping(Integer key, Content value) {
contentMapping.put(key, value);
protected synchronized void addMapping(String identifier, Content value) {
contentMapping.put(identifier, value);
}
/** Method used to initialise the content. */
......
......@@ -29,7 +29,7 @@ import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.widget.BaseWidget;
import lu.list.itis.dkd.tui.widget.builder.BaseWidgetBuilder;
import lu.list.itis.dkd.tui.widget.corona.ShapeFactory;
import lu.list.itis.dkd.tui.widget.corona.builder.ShadowBuilder;
import lu.list.itis.dkd.tui.widget.corona.builder.ShapeCoronaBuilder;
import lu.list.itis.dkd.tui.widget.tether.Tether;
import com.google.common.base.Preconditions;
......@@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
......@@ -182,11 +183,11 @@ public abstract class TangibleObjectManager {
*
* @return The collection of managed {@link BaseWidget} instances.
*/
public static synchronized Collection<BaseWidget> getWidgets() {
Collection<BaseWidget> widgets = new ArrayList<>();
widgets.addAll(objectMap.values());
widgets.addAll(cursorMap.values());
widgets.addAll(blobMap.values());
public static synchronized Collection<Entry<Integer, BaseWidget>> getWidgets() {
Collection<Entry<Integer, BaseWidget>> widgets = new ArrayList<>();
widgets.addAll(objectMap.entrySet());
widgets.addAll(cursorMap.entrySet());
widgets.addAll(blobMap.entrySet());
return widgets;
}
......@@ -240,7 +241,7 @@ public abstract class TangibleObjectManager {
* The widget for which to retrieve all mapped handle IDs.
* @return An {@link ArrayList} containing all associated handle IDs.
*/
public static synchronized ArrayList<Integer> getIdentifiers(BaseWidget widget) {
public static synchronized List<Integer> getIdentifiers(BaseWidget widget) {
return new ArrayList<>(widget.getWidgetIds());
}
......@@ -260,7 +261,7 @@ public abstract class TangibleObjectManager {
if (!cursorsEnabled)
break;
cursorMap.putIfAbsent(tangibleObject.getObjectId(), new BaseWidgetBuilder().withCorona(tangibleObject.getObjectId(), new ShadowBuilder(new Point()).withShape(ShapeFactory.buildCircle(5)).build()).build());
cursorMap.putIfAbsent(tangibleObject.getObjectId(), new BaseWidgetBuilder().withCorona(tangibleObject.getObjectId(), new ShapeCoronaBuilder(new Point()).withShape(ShapeFactory.buildCircle(5)).build()).build());
cursorMap.get(tangibleObject.getObjectId()).actionDrop(tangibleObject);
ensureConsistency();
......@@ -280,7 +281,7 @@ public abstract class TangibleObjectManager {
// TODO This line add a small shadow with each blob. This behaviour might not be
// desired but needs to be investigated. Opening issue #27.
blobMap.putIfAbsent(tangibleObject.getObjectId(), new BaseWidgetBuilder().withCorona(tangibleObject.getObjectId(), new ShadowBuilder(new Point()).withShape(ShapeFactory.buildCircle(5)).build()).build());
blobMap.putIfAbsent(tangibleObject.getObjectId(), new BaseWidgetBuilder().withCorona(tangibleObject.getObjectId(), new ShapeCoronaBuilder(new Point()).withShape(ShapeFactory.buildCircle(5)).build()).build());
blobMap.get(tangibleObject.getObjectId()).actionDrop(tangibleObject);
break;
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. All rights reserved.
*
* This file is part of TULIP.
*
* 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.adapter;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import java.util.Objects;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class ObjectHandle {
private int objectId;
private long sessionId;
// ---------------------------------------------------------------------------
public ObjectHandle() {