/*
* JClic accessibility
* FressaFunctionsClass.java
* Created on august / 2009
*
* @author Jordi Lagares Roset "jlagares@xtec.cat - www.lagares.org"
* amb el suport del Departament d'Educacio de la Generalitat de Catalunya
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details (see the LICENSE file).
*/
package edu.xtec.jclic.accessibility;
import edu.xtec.jclic.Activity;
import edu.xtec.jclic.skins.Skin;
import edu.xtec.util.Messages;
import edu.xtec.util.Options;
import java.awt.AWTEvent;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import javax.swing.Timer;
/**
*
* @author jlagares
*/
public class FressaFunctions {
// Constants
public static final String BUNDLE = "messages.AccessibilityMessages";
public static final String FRESSA_VERSION = "v: alfa 99 - 23/07/2010";
public static final int DEFAULT_TIMER_SPAN = 12;
public static final int MAX_SCANNING_ZONES = 3;
public static final int BUTTONS_ZONE = 0;
public static final int PUZZLE_ZONE = 1;
public static final int ASSOCIATION_ZONE = 2;
public static final int MAX_POINTS_PER_ZONE = 1000;
public static final Rectangle INITIAL_FORM_EXCHANGE_POS = new Rectangle(100, 100, 45, 30);
public static final int SCAN_ACTIVATION_DELAY = 5000;
public static final int SCAN_ACTIVATION_CHECK_SPAN = 1000;
public static final int SWAYING_ACTIVATION_DELAY = 400;
public static final int SWAYING_SPAN = 100;
public static final int SWAYING_AMOUNT = 2;
// Static variables
public static boolean noHandCursor = false;
public static boolean forceRectangularShapes = false;
// Global settings
public boolean scanIncludingArrowButtons = true;
public boolean withChangeZoneButton = false;
public boolean scanArrowsAtEndOfActivity = false;
public boolean scanFlagButton = false;
public boolean jumpWellPlaced = true;
public boolean showKeyboard = true;
public boolean changeZoneIfNoClick = false;
public boolean autoScanOnStart = false;
public boolean directedScanOnStart = false;
public boolean autoAutoScan = false;
public boolean weAreAtTheStart = true;
public int scanTimerSpan = DEFAULT_TIMER_SPAN;
// TFormKeyboard settings
public boolean mustDisableScanTimer = false;
public boolean isScanning = false;
public boolean isAutoScanning = false;
public boolean isDirectedScanning = false;
// Transient properties
// Needed to know where the buttons are placed
Skin skin;
Activity.Panel actPanel;
String activityType;
// Scanning points
boolean isFirstScreen = false;
int place;
int numberOfZones;
int currentZone;
int px[][] = new int[MAX_SCANNING_ZONES][MAX_POINTS_PER_ZONE];
int py[][] = new int[MAX_SCANNING_ZONES][MAX_POINTS_PER_ZONE];
int zonePoints[] = new int[MAX_SCANNING_ZONES];
//In case we need to read the contents of the cells
public String sll[][] = new String[MAX_SCANNING_ZONES][MAX_POINTS_PER_ZONE];
int clickNumber = 1;
boolean activityFinished = false;
TFormExchange formExchange;
TFormKeyboard formKeyboard;
boolean activityWithKeyboard = false;
//JClic
Rectangle actPanelRectangle;
Rectangle oldActPanelRectangle;
Point actPanelRectangleMiddleBottom;
// Voice synthesis
boolean withVoice = true;
boolean readLabels = false;
// *** >> public FressaVoice fressaVoice;
// Timers
Timer scanTimer;
Timer autoScanActivationTimer;
long tickCountTime;
int cursorPosX;
int cursorPosY;
// Swaying
boolean withSwaying = false;
int swayingCounter = 0;
int swayingCursorX;
int swayingCursorY;
Timer swayingTimer;
Timer directedScanStartSwayingTimer;
// Robot
Robot robot;
// Messages
Messages msg;
// Keys for storing options
public static final String SCAN_TIMER_SPAN = "scanTimerSpan", SCAN_INCLUDING_ARROWS = "scanIncludingArrows",
CHANGE_ZONE_BUTTON = "changeZoneButton", SCAN_ARROWS_AT_END = "scanArrowsAtEnd",
SCAN_FLAG = "scanFlag", JUMP_WELL_PLACED = "jumpWellPlaced", SHOW_KEYBOARD = "showKeyboard",
CHANGE_ZONE_IF_NO_CLICK = "changeZoneIfNoClick", AUTO_SCAN_ON_START = "autoScanOnStart",
DIRECTED_SCAN_ON_START = "directedScanOnStart", AUTO_AUTO_SCAN = "autoAutoScan",
SWAYING = "swaying", NO_HAND_CURSOR = "noHandCursor", FORCE_RECTANGLES = "forceRectangles",
READ_LABELS = "readLabels", WITH_VOICE = "withVoice";
public FressaFunctions(Options options) {
msg = options.getMessages(BUNDLE);
try {
robot = new Robot();
} catch (java.awt.AWTException ex) {
System.err.println("Unable to create java.awt.Robot: " + ex.getMessage());
}
formExchange = new TFormExchange(INITIAL_FORM_EXCHANGE_POS, msg.get("acc_exchangeBtnChar"));
formKeyboard = new TFormKeyboard(options, this);
noHandCursor = options.getBoolean(NO_HAND_CURSOR, false);
forceRectangularShapes = options.getBoolean(FORCE_RECTANGLES, false);
scanTimerSpan = options.getInt(SCAN_TIMER_SPAN, DEFAULT_TIMER_SPAN);
scanIncludingArrowButtons = options.getBoolean(SCAN_INCLUDING_ARROWS, true);
withChangeZoneButton = options.getBoolean(CHANGE_ZONE_BUTTON, false);
scanArrowsAtEndOfActivity = options.getBoolean(SCAN_ARROWS_AT_END, false);
scanFlagButton = options.getBoolean(SCAN_FLAG, false);
jumpWellPlaced = options.getBoolean(JUMP_WELL_PLACED, true);
showKeyboard = options.getBoolean(SHOW_KEYBOARD, true);
changeZoneIfNoClick = options.getBoolean(CHANGE_ZONE_IF_NO_CLICK, false);
autoScanOnStart = options.getBoolean(AUTO_SCAN_ON_START, false);
directedScanOnStart = options.getBoolean(DIRECTED_SCAN_ON_START, false);
autoAutoScan = options.getBoolean(AUTO_AUTO_SCAN, false);
withSwaying = options.getBoolean(SWAYING, false);
readLabels = options.getBoolean(READ_LABELS, false);
withVoice = options.getBoolean(WITH_VOICE, false);
if (withVoice) {
/*
fressaVoice=new FressaVoice();
fressaVoice.start();
readText(msg.get("acc_voiceGreeting"));
withVoice=fressaVoice.xxxxrunning;
*/
}
}
/**************************************************************/
/*READ*******************************************************/
/**************************************************************/
/*******************************************************************************************************/
/*CONTROL FUNCTIONS**Player.java**********************************************************************/
/*******************************************************************************************************/
public void startScanning() {
if (actPanel != null) {
zonePoints[0] = -1;
zonePoints[1] = -1;
zonePoints[2] = -1;
if (withChangeZoneButton && formExchange != null) {
formExchange.setVisible(true);
}
place = 0;
isFirstScreen = true;
isScanning = true;
isAutoScanning = false;
isDirectedScanning = false;
actPanel.calcScanPoints();
// WARNING: isScanning was just initialized, so it always will be "true":
if (formKeyboard != null && activityWithKeyboard && showKeyboard && isScanning) {
formKeyboard.isHorizontalScanning = false;
formKeyboard.xPos = 0;
formKeyboard.yPos = -1;
formKeyboard.setVisible(true);
formKeyboard.calcFrameSize();
numberOfZones = 1;
clickNumber = 1;
currentZone = 1;
}
}
}
public void stopScanning() {
isScanning = false;
isAutoScanning = false;
isDirectedScanning = false;
if (formExchange != null) {
formExchange.setVisible(false);
}
if (formKeyboard != null) {
formKeyboard.setVisible(false);
}
}
public void initActivity(Activity.Panel actPanel) {
this.actPanel = actPanel;
if (formKeyboard.isVisible()) {
formKeyboard.setVisible(false);
}
activityWithKeyboard = false;
activityType = actPanel.getActivity().getShortClassName();
// Clear data
// WARNING: Avoid the use of fixed-length arrays!
for (int i = 0; i < MAX_SCANNING_ZONES; i++) {
zonePoints[i] = -1;
for (int n = 0; n < MAX_POINTS_PER_ZONE; n++) {
px[i][n] = 0;
py[i][n] = 0;
sll[i][n] = "";
}
}
currentZone = 0; //Start scanning only buttons
place = 0;
oldActPanelRectangle.setBounds(0, 0, 0, 0);
activityFinished = false;
activityWithKeyboard = actPanel.getActivity().needsKeyboard();
if (formKeyboard != null && activityWithKeyboard && showKeyboard && isScanning) {
formKeyboard.isHorizontalScanning = false;
formKeyboard.xPos = 0;
formKeyboard.yPos = -1;
formKeyboard.setVisible(true);
formKeyboard.calcFrameSize();
numberOfZones = 1;
clickNumber = 1;
currentZone = 1;
}
actPanel.calcScanPoints();
if (autoAutoScan) {
if (autoScanActivationTimer == null) {
enableAutoScanTimer();
}
} else if (weAreAtTheStart) {
if (autoScanOnStart) {
enableAutoScan();
} else if (directedScanOnStart) {
enableDirectedScan();
}
}
weAreAtTheStart = false;
if (isAutoScanning) {
startScanTimer();
}
}
/*******************************************************************************************************/
/*EN ESCANEIG QUE ES MOGUI EL CURSOR EN EL LLOC PERQUE ES VEG MILLOR*************************************/
/*******************************************************************************************************/
public void startSwayingTimer() {
if (swayingTimer == null) {
swayingTimer = new Timer(SWAYING_SPAN, new ActionListener() {
public void actionPerformed(ActionEvent e) {
changeSwayingPos();
}
});
swayingCounter = 0;
Point p = MouseInfo.getPointerInfo().getLocation();
swayingCursorX = p.x;
swayingCursorY = p.y;
swayingTimer.start();
}
if (!swayingTimer.isRunning()) {
swayingTimer.start();
}
}
public void stopSwayingTimer() {
if (swayingTimer != null) {
swayingCounter = 0;
changeSwayingPos();
swayingTimer.stop();
swayingTimer = null;
}
}
public void changeSwayingPos() {
int dx = 0;
int dy = 0;
switch (swayingCounter) {
case 0:
dx = -1;
dy = -1;
break;
case 1:
dx = 1;
dy = -1;
break;
case 2:
dx = 1;
dy = 1;
break;
case 3:
dx = -1;
dy = 1;
break;
}
swayingCounter = ++swayingCounter % 4;
if (robot != null) {
robot.mouseMove(swayingCursorX + dx * SWAYING_AMOUNT, swayingCursorY + dy * SWAYING_AMOUNT);
}
}
/*******************************************************************************************************/
/**96-Per engegar el Fiambreig en escaneig dirgit que la primera vegada després d'un clic no funciona***/
/*******************************************************************************************************/
public void startDirectedScanSwayingTimer() {
if (swayingTimer == null && directedScanStartSwayingTimer == null) {
directedScanStartSwayingTimer = new Timer(SWAYING_ACTIVATION_DELAY,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
startSwayingTimer();
directedScanStartSwayingTimer = null;
}
});
directedScanStartSwayingTimer.setRepeats(false);
directedScanStartSwayingTimer.start();
}
}
/*******************************************************************************************************/
/*DEACTIVATE ALL KINDS OF SCANNING**********************************************************************/
/*******************************************************************************************************/
public void disableScanning() {
if (withSwaying) {
stopSwayingTimer();
}
stopScanning();
stopScanTimer();
Toolkit.getDefaultToolkit().removeAWTEventListener(autoScanMouseListener);
Toolkit.getDefaultToolkit().removeAWTEventListener(mouseDirectedScanListener);
Toolkit.getDefaultToolkit().removeAWTEventListener(directedScanKeyboardListener);
if (autoAutoScan) {
Point p = MouseInfo.getPointerInfo().getLocation();
cursorPosX = p.x;
cursorPosY = p.y;
tickCountTime = System.currentTimeMillis();
}
}
/*******************************************************************************************************/
/*AUTOMATIC SCANNING************************************************************************************/
/*******************************************************************************************************/
public void enableAutoScan() {
disableScanning();
startScanning();
isAutoScanning = true;
startScanTimer();
Toolkit.getDefaultToolkit().addAWTEventListener(autoScanMouseListener, AWTEvent.MOUSE_EVENT_MASK);
}
public void startScanTimer() {
if (scanTimer == null) {
scanTimer = new Timer(scanTimerSpan * 100,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (withSwaying) {
stopSwayingTimer();
}
actPanel.nextScanPosition();
if (withSwaying) {
startSwayingTimer();
}
}
});
scanTimer.start();
}
}
public void stopScanTimer() {
if (scanTimer != null) {
scanTimer.stop();
scanTimer = null;
}
}
private AWTEventListener autoScanMouseListener = new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
if (isAutoScanning && event instanceof MouseEvent) {
MouseEvent me = (MouseEvent) event;
if (me.getButton() == MouseEvent.BUTTON1) {
if (withSwaying) {
stopSwayingTimer();
}
// WARNING: Is this really needed?
//if(event.getID()==MouseEvent.MOUSE_RELEASED) {
// actPanel.ClickLeftButton();
//}
} else if (me.getButton() == MouseEvent.BUTTON3) {
if (withSwaying) {
stopSwayingTimer();
}
stopScanning();
stopScanTimer();
Toolkit.getDefaultToolkit().removeAWTEventListener(autoScanMouseListener);
}
}
}
};
/*******************************************************************************************************/
/*ACTIVATION/DEACTIVATION AUTOMATIC AUTOMATIC SCANNING**************************************************/
/*******************************************************************************************************/
public void enableAutoScanTimer() {
if (autoAutoScan && (autoScanActivationTimer == null || !autoScanActivationTimer.isRunning())) {
Point p = MouseInfo.getPointerInfo().getLocation();
cursorPosX = p.x;
cursorPosY = p.y;
tickCountTime = System.currentTimeMillis();
autoScanActivationTimer = new Timer(SCAN_ACTIVATION_CHECK_SPAN,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!isScanning) {
Point p = MouseInfo.getPointerInfo().getLocation();
int x = p.x;
int y = p.y;
if ((x != cursorPosX) || (y != cursorPosY)) {
cursorPosX = x;
cursorPosY = y;
tickCountTime = System.currentTimeMillis();
} else if (System.currentTimeMillis() - tickCountTime > SCAN_ACTIVATION_DELAY) {
enableAutoScan();
}
}
}
});
autoScanActivationTimer.start();
}
}
//WARNING: Why not recicle timers instead of destroying and creating it every time?
public void disableAutoScanTimer() {
if (autoScanActivationTimer != null) {
autoScanActivationTimer.stop();
autoScanActivationTimer = null;
}
}
/*******************************************************************************************************/
/*DIRECTED SCANNING*************************************************************************************/
/*******************************************************************************************************/
public void enableDirectedScan() {
disableScanning();
startScanning();
isDirectedScanning = true;
if (withSwaying) {
stopSwayingTimer();
}
actPanel.nextScanPosition();
if (withSwaying) {
stopSwayingTimer();
}
Toolkit.getDefaultToolkit().addAWTEventListener(mouseDirectedScanListener, AWTEvent.MOUSE_EVENT_MASK);
Toolkit.getDefaultToolkit().addAWTEventListener(directedScanKeyboardListener, AWTEvent.KEY_EVENT_MASK);
//if (HiHaFiambreig) TimerEscaneigDirigitEngegarFiambreig();
}
private AWTEventListener mouseDirectedScanListener = new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
if (isDirectedScanning) {
MouseEvent me = (MouseEvent) event;
if (me.getButton() == MouseEvent.BUTTON1) {
//96
//if (HiHaFiambreig) PararTimerFiambreig();
if (me.getID() == MouseEvent.MOUSE_RELEASED) {
if (withSwaying) {
stopSwayingTimer();
}
actPanel.clickLeftButton();
if (withSwaying) {
startDirectedScanSwayingTimer();
}
}
} else if (me.getButton() == MouseEvent.BUTTON3) {
if (event.getID() == MouseEvent.MOUSE_RELEASED) {
if (withSwaying) {
stopSwayingTimer();
}
actPanel.nextScanPosition();
if (withSwaying) {
startSwayingTimer();
}
}
}
}
}
};
//He posat aquest listener de Teclat perque amb Escaneig Dirigit i amb Fiambreig tinguem una forma
//fàcil de parar l'escaneig. Prement la Tecla ESCAPE. No sé si JClic la fa servir
private AWTEventListener directedScanKeyboardListener = new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
KeyEvent ke = (KeyEvent) event;
if (ke.getID() == KeyEvent.KEY_TYPED) {
if (ke.getKeyChar() == KeyEvent.VK_ESCAPE) {
disableScanning();
} else {
//JOptionPane.showMessageDialog(null,ke.getKeyText(ke.getKeyChar()),"JAVA PLAPHOONS",JOptionPane.INFORMATION_MESSAGE);
}
}
}
};
/*******************************************************************************************************/
/**INTERFACE*ClickLeftButton***************************************************************************/
/*******************************************************************************************************/
public void clickLeftButton() {
//System.out.println("Clic Botó esquerra. Zona: "+String.valueOf(Zona));
if (withChangeZoneButton) {
//System.out.println("Clic Botó esquerra. Zona: "+String.valueOf(Zona)+" - "+String.valueOf(Lloc)+" - "+String.valueOf(NumeroPuntsZona[Zona]));
if (activityWithKeyboard && showKeyboard && isScanning) {
if (currentZone == 0) {
formKeyboard.isHorizontalScanning = false;
formKeyboard.xPos = 0;
formKeyboard.yPos = -1;
formKeyboard.setVisible(true);
formKeyboard.calcFrameSize();
numberOfZones = 1;
clickNumber = 1;
currentZone = 1;
}
formExchange.changeBgColor(currentZone);
return;
}
if (changeZoneIfNoClick
&& (activityType.equals("@associations.SimpleAssociation")
|| activityType.equals("@associations.ComplexAssociation"))) {
if (currentZone == 0 || currentZone == 2) {
if (place > zonePoints[currentZone]) {
if (currentZone == 0 && numberOfZones > 0) {
currentZone = 1;
place = 0;
} else if (currentZone == 2) {
currentZone = 0;
place = 0;
}
//System.out.println("Clic Botó esquerra. Zona: "+String.valueOf(Zona)+" - "+String.valueOf(Lloc));
formExchange.changeBgColor(currentZone);
return;
}
}
} else {
if (currentZone == 0 || currentZone == 1) {
if (place > zonePoints[currentZone]) {
if (currentZone == 0 && numberOfZones > 0) {
currentZone = 1;
place = 0;
} else if (currentZone == 1) {
currentZone = 0;
place = 0;
}
//System.out.println("Clic Botó esquerra. Zona: "+String.valueOf(Zona)+" - "+String.valueOf(Lloc));
formExchange.changeBgColor(currentZone);
return;
}
}
}
}
if (numberOfZones == 1) {
if (currentZone == 1) {
if (!activityType.equals("@panels.InformationScreen")
&& !activityType.equals("@panels.Explore")
&& !activityType.equals("@panels.Identify")
&& !activityType.equals("@textGrid.WordSearch")) {
place = 0;
}
/*
NumeroClic++;
if (NumeroClic==3) NumeroClic=1;
*/
}
} else if (numberOfZones == 2) {
if (currentZone == 1) {
currentZone = 2;
place = 0;
} else if (currentZone == 2) {
currentZone = 1;
place = 0;
}
}
/*
if (currentZone>0) {
ActivitatFinalitzada=actPanel.solved;
if(ActivitatFinalitzada) System.out.println("Activitat Finalitzada");
}
*/
//if (OpcionsSaltarBenColocat) CalculateInactivePoints();
}
// provisional
public void readText(String txt) {
}
public void calcScanPoints() {
/*
if (CalculateScanningPointsBeginning()) {
CalculateScanningPointsEnd();
}
*
*/
}
public void activityFinished() {
}
public void nextScanPosition() {
}
}