/**
* Squidy Interaction Library 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, either version 3 of the License,
* or (at your option) any later version.
*
* Squidy Interaction Library 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 Squidy Interaction Library. If not, see
* <http://www.gnu.org/licenses/>.
*
* 2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website
* <http://www.squidy-lib.de> for further information.
*/
package org.squidy.nodes;
import java.awt.event.KeyEvent;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
import org.squidy.manager.controls.ComboBox;
import org.squidy.manager.controls.Slider;
import org.squidy.manager.controls.ComboBoxControl.ComboBoxItemWrapper;
import org.squidy.manager.data.IData;
import org.squidy.manager.data.Processor;
import org.squidy.manager.data.Property;
import org.squidy.manager.data.domainprovider.DomainProvider;
import org.squidy.manager.data.impl.DataButton;
import org.squidy.manager.data.impl.DataDigital;
import org.squidy.manager.data.impl.DataKey;
import org.squidy.manager.data.impl.DataPosition2D;
import org.squidy.manager.model.AbstractNode;
/**
* The <code>SquidyPresenter</code> node maps iPhone-gestures to mouse- or key-events
* such that you can use your iPhone to control PowerPoint presentations.
* Simply use the iPhone touch screen to move the mouse cursor or touch to click.
* Swiping from left to right and right to left maps to PAGE UP and PAGE DOWN keys,
* therefore accessing the next / previous slide.
* Swiping from top to bottom or vice versa maps to a right click (context menu).
*
* <pre>
* Date: Dec 16, 2009
* Time: 20:10:33 PM
* </pre>
*
* @author Markus Nitsche, markus.nitsche@uni-konstanz.de, University of Konstanz
* @version $Id: SquidyPresenter.java 772 2011-09-16 15:39:44Z raedle $
*/
@XmlType(name = "SquidyPresenter")
@Processor(
name = "SquidyPresenter",
icon = "/org/squidy/nodes/image/48x48/presenter.png",
types = { Processor.Type.FILTER },
description = "/org/squidy/nodes/html/SquidyPresenter.html",
tags = { "presenter", "iPhone", "Android", "PowerPoint" }
)
public class SquidyPresenter extends AbstractNode {
// Private global variables
private DataPosition2D last = new DataPosition2D(); //saves the last sent position
private DataPosition2D origin; //saves the position of any "TOUCHES_BEGAN" event
private double mThreshold = 0.3;
// ################################################################################
// BEGIN OF ADJUSTABLES
// ################################################################################
@XmlAttribute(name = "movement-threshold")
@Property(
name = "Movement threshold",
suffix = "\u0025",
description = "This value describes how large the 'distance' of a swiping gesture must be such that a gesture is recognized. If the value is for example is 30, the gesture needs to cover 30% of the screen width/height to be recognized"
)
@Slider(
type = Integer.class,
minimumValue = 0,
maximumValue = 100,
showLabels = true,
showTicks = true,
majorTicks = 25,
minorTicks = 5,
snapToTicks = true
)
private int movementThreshold = 30;
public int getMovementThreshold() {
return movementThreshold;
}
public void setMovementThreshold(int movementThreshold) {
this.movementThreshold = movementThreshold;
mThreshold = (double) movementThreshold / 100.0;
}
// ################################################################################
@XmlAttribute(name = "time-threshold")
@Property(
name = "Time threshold",
suffix = "ms",
description = "After the given time (in ms), touches are ignored (Timeout)"
)
@Slider(
type = Integer.class,
minimumValue = 250,
maximumValue = 1000,
showLabels = true,
showTicks = true,
majorTicks = 250,
minorTicks = 50,
snapToTicks = true
)
private int timeThreshold = 500;
public int getTimeThreshold() {
return timeThreshold;
}
public void setTimeThreshold(int timeThreshold) {
this.timeThreshold = timeThreshold;
}
// ################################################################################
@XmlAttribute(name = "swipe-right")
@Property(
name = "Swipe right",
description = "The key event that will be released on swipe right."
)
@ComboBox(domainProvider = KeyEventDomainProvider.class)
private int swipeRight = KeyEvent.VK_PAGE_DOWN;
public int getSwipeRight() {
return swipeRight;
}
public void setSwipeRight(int swipeRight) {
this.swipeRight = swipeRight;
}
@XmlAttribute(name = "swipe-left")
@Property(
name = "Swipe left",
description = "The key event that will be released on swipe left."
)
@ComboBox(domainProvider = KeyEventDomainProvider.class)
private int swipeLeft = KeyEvent.VK_PAGE_UP;
public int getSwipeLeft() {
return swipeLeft;
}
public void setSwipeLeft(int swipeLeft) {
this.swipeLeft = swipeLeft;
}
public static class KeyEventDomainProvider implements DomainProvider {
public Object[] getValues() {
ComboBoxItemWrapper[] keyEvents = new ComboBoxItemWrapper[4];
keyEvents[0] = new ComboBoxItemWrapper(KeyEvent.VK_RIGHT, "Arrow Right");
keyEvents[1] = new ComboBoxItemWrapper(KeyEvent.VK_LEFT, "Arrow Left");
keyEvents[2] = new ComboBoxItemWrapper(KeyEvent.VK_PAGE_DOWN, "Page Down");
keyEvents[3] = new ComboBoxItemWrapper(KeyEvent.VK_PAGE_UP, "Page Up");
return keyEvents;
}
}
// ################################################################################
// ################################################################################
// END OF ADJUSTABLES
// ################################################################################
//Here's what happens when Data comes in
public IData process(DataPosition2D data) {
if(data.getAttribute(iPhone.TOUCHES_BEGAN) != null) {
//save origin of touch
origin = data;
}
if(data.getAttribute(iPhone.TOUCHES_ENDED) != null) {
DataDigital key = null;
if(data.getTimestamp()-origin.getTimestamp() > timeThreshold) {
//ignore touches that have been active too lang
return data;
}
if(origin.getX() - data.getX() > mThreshold) {
//this is a swiping gesture from right to left --> previous slide / PAGE UP
// key = new DataKey(this.getClass(), swipeLeft, true);
key = new DataDigital(Powerpointer.class, true);
key.setAttribute(Keyboard.KEY_EVENT, KeyEvent.VK_LEFT);
// publish(key);
}
else if (data.getX() - origin.getX() > mThreshold) {
//swiping gesture from left to right --> next slide / PAGE DOWN
// key = new DataKey(this.getClass(), swipeRight, true);
key = new DataDigital(Powerpointer.class, true);
key.setAttribute(Keyboard.KEY_EVENT, KeyEvent.VK_RIGHT);
// publish(key);
}
else if (origin.getY() - data.getY() > mThreshold || data.getY() - origin.getY() > mThreshold) {
//top-down movement --> right-click
DataButton but = new DataButton(this.getClass(), DataButton.BUTTON_3, true);
publish(but);
but.setFlag(false);
publish(but);
}
if(key != null) {
//if we set a key, publish it to the pipeline
publish(key);
DataDigital key2 = key.getClone();
key2.setFlag(false);
publish(key2);
}
}
//always return data, otherwise it will get "lost"
return data;
}
}