Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Iniitial commit

  • Loading branch information...
commit 0cc91f553e8a0e2a97c8f5fab8164d91e255afd2 0 parents
@georgepalmer authored
Showing with 9,887 additions and 0 deletions.
  1. +32 −0 com/rowtheboat/AllTests.java
  2. +48 −0 com/rowtheboat/GetVersion.java
  3. +131 −0 com/rowtheboat/SerialTest.java
  4. +562 −0 com/rowtheboat/controller/Core.java
  5. +57 −0 com/rowtheboat/controller/IInputOutputDevice.java
  6. +424 −0 com/rowtheboat/controller/IODeviceHandler.java
  7. +79 −0 com/rowtheboat/controller/Start.java
  8. +134 −0 com/rowtheboat/controller/StrokeCollection.java
  9. +185 −0 com/rowtheboat/gui/AboutWindow.java
  10. +491 −0 com/rowtheboat/gui/AddRowerWindow.java
  11. +153 −0 com/rowtheboat/gui/CountdownWindow.java
  12. +287 −0 com/rowtheboat/gui/GUIUtil.java
  13. +426 −0 com/rowtheboat/gui/MainWindow.java
  14. +720 −0 com/rowtheboat/gui/NewWorkoutWindow.java
  15. +439 −0 com/rowtheboat/gui/OptionsSingleton.java
  16. +199 −0 com/rowtheboat/gui/OptionsWindow.java
  17. +394 −0 com/rowtheboat/gui/ResultsWindow.java
  18. +269 −0 com/rowtheboat/gui/Sleak.java
  19. +74 −0 com/rowtheboat/gui/output/IGUIOutputDevice.java
  20. +285 −0 com/rowtheboat/gui/output/PMDisplay.java
  21. +946 −0 com/rowtheboat/gui/output/River.java
  22. +164 −0 com/rowtheboat/input/FixedSplitInput.java
  23. +68 −0 com/rowtheboat/input/IInputDevice.java
  24. +210 −0 com/rowtheboat/input/PM2PlusReadEventListener.java
  25. +119 −0 com/rowtheboat/input/PM2PlusStrokeManager.java
  26. +333 −0 com/rowtheboat/input/PM2PlusUnit.java
  27. +373 −0 com/rowtheboat/input/StrokeData.java
  28. +236 −0 com/rowtheboat/input/VariableSplitInput.java
  29. +98 −0 com/rowtheboat/input/test/StrokeDataTest.java
  30. +56 −0 com/rowtheboat/output/IFileOutputDevice.java
  31. +49 −0 com/rowtheboat/output/IOutputDevice.java
  32. +235 −0 com/rowtheboat/output/XMLWorkoutWriter.java
  33. +179 −0 com/rowtheboat/workout/ComputerRower.java
  34. +119 −0 com/rowtheboat/workout/HumanRower.java
  35. +91 −0 com/rowtheboat/workout/Rower.java
  36. +388 −0 com/rowtheboat/workout/Workout.java
  37. +66 −0 data/geo.xml
  38. +66 −0 data/geo2.xml
  39. +219 −0 data/performanceTest.xml
  40. +340 −0 gpl.txt
  41. BIN  images/CorrectSigns.png
  42. BIN  images/boatGreen.gif
  43. BIN  images/boatGrey.gif
  44. BIN  images/boatOrange.gif
  45. BIN  images/boatOriginal.png
  46. BIN  images/boatPurple.gif
  47. BIN  images/boatRed.gif
  48. BIN  images/boatSpacer.gif
  49. BIN  images/boatYellow.gif
  50. BIN  images/icon.gif
  51. BIN  images/laneSplit.gif
  52. BIN  images/signBottom.gif
  53. BIN  images/signTop.gif
  54. BIN  images/startLine.gif
  55. +1 −0  java batch files/linux/gtk/PC-Rower
  56. +1 −0  java batch files/linux/motif/PC-Rower
  57. +1 −0  java batch files/mac/PC-Rower
  58. +1 −0  java batch files/win32/PC-Rower.bat
  59. +17 −0 javax.comm.properties
  60. +14 −0 options.xml
  61. +33 −0 readme files/linux/Readme_JVM.txt
  62. +27 −0 readme files/mac/Readme_JVM.txt
  63. +26 −0 readme files/win32/Readme_JVM.txt
  64. +22 −0 readme files/win32/Readme_Native.txt
  65. BIN  source_104.zip
32 com/rowtheboat/AllTests.java
@@ -0,0 +1,32 @@
+/*
+ * File: AllTests.java
+ *
+ * Date Version User Description
+ * 11-Feb-2004 1.0 GeorgeP Initial version coded
+ *
+ */
+
+package com.rowtheboat;
+
+import com.rowtheboat.input.test.StrokeDataTest;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * This class performs all the JUnit tests for PC-Rower
+ *
+ * @author GeorgeP
+ */
+public class AllTests {
+
+ public static Test suite() {
+
+ TestSuite suite = new TestSuite("PC-Rower tests");
+
+ //$JUnit-BEGIN$
+ suite.addTest(new TestSuite(StrokeDataTest.class));
+ //$JUnit-END$
+
+ return suite;
+ }
+}
48 com/rowtheboat/GetVersion.java
@@ -0,0 +1,48 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+/*
+ * File: GetVersion.java
+ *
+ * Date Version User Description
+ * 01-Dec-2004 1.0 GeorgeP Initial version coded
+ *
+ */
+
+package com.rowtheboat;
+
+import com.rowtheboat.gui.OptionsSingleton;
+
+/**
+ * This class outputs the software version for use by Ant. It is not required for the software to
+ * function.s
+ *
+ * @author GeorgeP
+ */
+public class GetVersion {
+
+ public static void main(String[] args) {
+
+ System.out.print(OptionsSingleton.getVersion());
+ }
+}
131 com/rowtheboat/SerialTest.java
@@ -0,0 +1,131 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: SerialTest.java
+ *
+ * Date Version User Description
+ * 28-Nov-2004 1.0 GeorgeP Initial version coded
+ *
+ */
+
+package com.rowtheboat;
+
+import gnu.io.CommPortIdentifier;
+import gnu.io.PortInUseException;
+import gnu.io.SerialPort;
+import gnu.io.UnsupportedCommOperationException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Enumeration;
+
+/**
+ * Type class description here
+ *
+ * @author GeorgeP
+ */
+public class SerialTest {
+
+ public static void main(String[] args) {
+
+ CommPortIdentifier [] commPorts = new CommPortIdentifier[10];
+ Enumeration ports = CommPortIdentifier.getPortIdentifiers();
+ int i = 0;
+ while (ports.hasMoreElements()) {
+
+ commPorts[i] = (CommPortIdentifier) ports.nextElement();
+ System.out.println(i + ". " + commPorts[i].getName());
+ i++;
+ }
+
+ System.out.println("Please select a port");
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+ int choice = -1;
+ try {
+ choice = Integer.parseInt(br.readLine());
+ } catch (NumberFormatException e) {
+ System.out.println("Error formating number");
+ e.printStackTrace();
+ } catch (IOException e) {
+ System.out.println("IO Exception. Check running java not javaw");
+ e.printStackTrace();
+ }
+
+ if (choice != -1) {
+ Enumeration ports2 = CommPortIdentifier.getPortIdentifiers();
+ int j = 0;
+ while (ports2.hasMoreElements()) {
+
+ if (j == choice) {
+ initialisePort(commPorts[j]);
+ return;
+ }
+ j++;
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private static void initialisePort(CommPortIdentifier port) {
+
+ /* Open the serial port */
+ SerialPort serialPort;
+ int section = 0;
+ try {
+ serialPort = (SerialPort) port.open("PC-Rower", 2000);
+ section = 1;
+
+ /* Set the port up to the defaults required */
+ serialPort.notifyOnDataAvailable(true);
+ serialPort.setSerialPortParams
+ (9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
+ section = 2;
+
+ /* Get the input and output streams */
+ OutputStream pm2PlusOutputStream = serialPort.getOutputStream();
+ InputStream pm2PlusInputStream = serialPort.getInputStream();
+ section = 3;
+
+ System.out.println("Successfully opened!!");
+ /* Add the event listener */
+ //serialPort.addEventListener(
+ // new PM2PlusReadEventListener( pm2PlusInputStream, workout.getType() ) );
+ } catch (PortInUseException e) {
+ System.out.println("PortInUseException. Section: " + section);
+ e.printStackTrace();
+ } catch (UnsupportedCommOperationException e) {
+ System.out.println("UnsupportedCommOperationException. Section: " + section);
+ e.printStackTrace();
+ } catch (IOException e) {
+ System.out.println("IOException. Section: " + section);
+ e.printStackTrace();
+ }
+ }
+}
562 com/rowtheboat/controller/Core.java
@@ -0,0 +1,562 @@
+ /*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+/*
+ * File: Core.java
+ *
+ * Date Version User Description
+ * 11-Nov-2003 1.0 GeorgeP Initial version coded
+ * 30-Oct-2004 1.03 GeorgeP Updated to RXTX pure serial driver
+ * 10-Dec-2004 1.04 GeorgeP Garbage collection changes made. Sorted Just Row not
+ * displaying boats straight away issue. Fixed xml file saving
+ * issue if quit without selecting stop rowing.
+ *
+ */
+
+package com.rowtheboat.controller;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import gnu.io.PortInUseException;
+
+import com.rowtheboat.gui.AboutWindow;
+import com.rowtheboat.gui.CountdownWindow;
+import com.rowtheboat.gui.GUIUtil;
+import com.rowtheboat.gui.MainWindow;
+import com.rowtheboat.gui.NewWorkoutWindow;
+import com.rowtheboat.gui.OptionsSingleton;
+
+import org.dom4j.DocumentException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.xml.sax.SAXException;
+
+import com.rowtheboat.workout.Rower;
+import com.rowtheboat.workout.Workout;
+
+/**
+ * This class is the core of the controller, tying together the input, output, gui, user and
+ * workout packages.
+ * @author GeorgeP
+ */
+
+public class Core {
+
+ /* Class Variables */
+
+ private boolean aborted; /* This flag is set when the race is
+ aborted */
+ private boolean racing; /* This flag is set when the racing
+ is taking place */
+ private long lastTime; /* Used for performance ratings */
+ private BufferedWriter performanceWriter; /* The performance writer */
+ private MainWindow window; /* The main program window shell */
+ private NewWorkoutWindow newWorkoutWindow; /* The new workout window */
+ private IODeviceHandler ioDevices; /* The io device handler */
+ private Workout workout; /* The workout */
+
+
+ /* Constructor */
+
+ /**
+ * Constructs the Core class and associated data
+ */
+ public Core() {
+
+ /* Initialise the new window */
+ window = new MainWindow(this);
+ lastTime = System.currentTimeMillis();
+ }
+
+
+ /* Public Methods */
+
+ /**
+ * Display the about box
+ */
+ public void aboutBox() {
+
+ /* Construct the shell and open it */
+ AboutWindow win = new AboutWindow
+ ( window.getShell().getSize(), window.getShell().getLocation() );
+ Shell aboutShell = win.getShell();
+ aboutShell.open();
+
+ /* Wait until its closed */
+ while ( !aboutShell.isDisposed() ) {
+
+ if ( !aboutShell.getDisplay().readAndDispatch() ) {
+ aboutShell.getDisplay().sleep();
+ }
+ }
+
+ /* Dispose of the about shell resources */
+ aboutShell.dispose();
+
+ /* Update the shell so no remains of box showing */
+ window.getShell().update();
+ }
+
+
+ /**
+ * Begin the workout
+ */
+ public void beginWorkout() {
+
+ /* Clear the window of the results box if not happened already */
+ window.getShell().update();
+
+ /* If a new workout is successfully entered by the user then we must first remove the old
+ * workout as it will be holding resources for a user - eg a serial port. */
+ cleanUpWorkout();
+
+ /* Setup the workout */
+ setupIODevices();
+
+ /* Display a click OK to start workout dialog */
+ MessageBox clickToStart = new MessageBox(window.getShell(), SWT.ICON_INFORMATION | SWT.OK);
+ clickToStart.setText("Start Workout");
+ clickToStart.setMessage("Click OK to start the workout");
+ clickToStart.open();
+
+ /* Start the countdown */
+ startRowing();
+ }
+
+
+ /**
+ * This is called before another workout can take place or as the program exits to clear the
+ * resources associated with the old workout.
+ */
+ public void cleanUpWorkout() {
+
+ /* These should be set in case user aborts half way through race */
+ racing = false;
+ aborted = false;
+
+ /* Clean up the devices */
+ try {
+ if (ioDevices != null) {
+ ioDevices.cleanUp();
+ }
+ }
+ catch (Exception e) {
+ boolean flag = true;
+ if (e instanceof NumberFormatException) {
+ GUIUtil.showNumberFormatException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof IOException) {
+ GUIUtil.showIOException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof InterruptedException) {
+ GUIUtil.showInterruptedException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (flag) {
+ GUIUtil.showErrorDialog(window.getShell(), "Program Error\n( " +
+ e.getMessage() + " )");
+ }
+ }
+ }
+
+
+ /**
+ * This is called by the GUI when the new workout is created
+ */
+ public void createWorkout() {
+
+ /* Create the workout */
+ workout = new Workout( newWorkoutWindow.getWorkoutType() );
+ workout.setShowDistance( newWorkoutWindow.getShowDistance() );
+ workout.setStartType( newWorkoutWindow.getStartType() );
+
+ if ( workout.getType() == Workout.DISTANCE_WORKOUT ) {
+ workout.setDistance( newWorkoutWindow.getDistance() );
+ }
+ else {
+ workout.setTime( newWorkoutWindow.getTime() );
+ }
+
+ /* Get the rowers and shadows */
+ Object [] rowers = newWorkoutWindow.getRowers();
+ Object [] shadows = newWorkoutWindow.getShadows();
+
+ /* Add these to the workout */
+ for (int i = 0; i < rowers.length; i++) {
+ workout.addRower((Rower) rowers[i]);
+ if (shadows[i] != null) {
+ workout.addShadow((Rower) shadows[i]);
+ }
+ }
+ }
+
+
+ /**
+ * Exits the program.
+ */
+ public void exitProgram() {
+
+ /* Dispose of the window. The control will then go back to the Start class and the
+ * cleanUpWorkout() method will be called to ensure any resources are free again. */
+ Start.setCleanUp(true);
+ if (performanceWriter != null) {
+ try {
+ performanceWriter.close();
+ } catch (IOException e) {
+ GUIUtil.showIOException( window.getShell(), e.getMessage() );
+ }
+ }
+
+ /* Save the options if possible */
+ try {
+ OptionsSingleton.getInstance().saveOptions();
+ }
+ catch (Exception e) {
+ GUIUtil.showErrorDialog(newWorkoutWindow.getShell(), "Error saving options");
+ }
+
+ /* Call workout aborted to clear up any iodevices and window resources (assuming there's a
+ * reference in the first place) */
+ if (ioDevices != null && window != null) {
+ workoutAborted();
+ }
+
+ /* Dispose of the shell */
+ window.getShell().dispose();
+ }
+
+
+ /**
+ * Start a just row workout where the rower just rows down the river.
+ */
+ public void justRow() {
+
+ /* CleanUp any previous workouts */
+ if (ioDevices != null) {
+ cleanUpWorkout();
+ }
+
+ /* Construct the new workout */
+ workout = new Workout(Workout.JUST_ROW);
+ setupIODevices();
+ startRowing();
+ }
+
+
+ /**
+ * The main program loop. This opens the main window, retrieves the input data and delegates
+ * the data to output devices.
+ */
+ public void mainProgramLoop() {
+
+ /* Open the main window */
+ Shell mainWindowShell = window.getShell();
+ mainWindowShell.open();
+
+ try {
+ /* Main window loop */
+ while ( !mainWindowShell.isDisposed() ) {
+
+ /* If in racing mode then retrieve the latest data and update the outputs */
+ if (racing) {
+ updateStrokeData();
+
+ /* Clear up any garbage. This is done with each stroke to keep ongoing
+ * memory requirements to a minimum. */
+ OptionsSingleton.getInstance().garbageCollect();
+ }
+
+ if ( !mainWindowShell.getDisplay().readAndDispatch() ) {
+ mainWindowShell.getDisplay().sleep();
+ }
+ }
+ }
+ catch (Exception e) {
+ boolean flag = true;
+ if (e instanceof NumberFormatException) {
+ GUIUtil.showNumberFormatException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof IOException) {
+ GUIUtil.showIOException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof SAXException) {
+ GUIUtil.showSAXException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (flag) {
+ GUIUtil.showErrorDialog(window.getShell(), "Program Error\n( " +
+ e.getMessage() + " )");
+ }
+ }
+ }
+
+
+ /**
+ * This is called by the GUI when new workout is selected from the menu
+ */
+ public void newWorkout() {
+
+ newWorkoutWindow = new NewWorkoutWindow( window.getShell(), this );
+ Shell newWorkoutShell = newWorkoutWindow.getShell();
+ newWorkoutShell.open();
+
+ while ( !newWorkoutShell.isDisposed() ) {
+
+ if ( !newWorkoutShell.getDisplay().readAndDispatch() ) {
+ newWorkoutShell.getDisplay().sleep();
+ }
+ }
+
+ /* Dispose of the shell */
+ newWorkoutShell.dispose();
+ }
+
+
+ /**
+ * This is called to setup the devices used in the workout
+ */
+ public void setupIODevices() {
+
+ try {
+ /* Remove old ioDevices and set new one */
+ ioDevices = null;
+ ioDevices = new IODeviceHandler(workout, this);
+
+ /* Construct outputs and turn on racing mode */
+ window.setupWorkout(workout);
+ racing = true;
+ }
+ catch (Exception e) {
+ boolean flag = true;
+ if (e instanceof NumberFormatException) {
+ GUIUtil.showNumberFormatException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof IOException) {
+ GUIUtil.showIOException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof SAXException) {
+ GUIUtil.showSAXException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof InterruptedException) {
+ GUIUtil.showInterruptedException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof PortInUseException) {
+ GUIUtil.showErrorDialog(window.getShell(), "The selected serial port is in use." +
+ "Please select another from the options dialog");
+ flag = false;
+ }
+ if (e instanceof MalformedURLException || e instanceof DocumentException) {
+ GUIUtil.showXMLReadException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (flag) {
+ GUIUtil.showErrorDialog(window.getShell(), "Program Error\n( " +
+ e.getMessage() + " )");
+ }
+ }
+ }
+
+
+ /**
+ * Start the workout. This starts the countdown and once finished starts the logging of the
+ * data.
+ */
+ public void startRowing() {
+
+ /* Update the display now everything has been drawn */
+ window.getShell().getDisplay().update();
+
+ /* Clear up any garbage so that the first garbage collection after the first stroke isn't
+ * too big and thus too time consuming. */
+ OptionsSingleton.getInstance().garbageCollect();
+
+ /* Start the workout */
+ try {
+ if (workout.getStartType() == Workout.START_ON_STROKE) {
+ ioDevices.startRowing();
+ }
+ else {
+
+ /* Construct the countdown window */
+ CountdownWindow cw = new CountdownWindow
+ ( window.getShell().getSize(), window.getShell().getLocation() );
+ Shell countdownShell = cw.getShell();
+ countdownShell.open();
+
+
+ /* Whilst the shell is still there */
+ while (!countdownShell.isDisposed()) {
+ if (!countdownShell.getDisplay().readAndDispatch()) {
+ countdownShell.getDisplay().sleep();
+ }
+
+ /* Get the countdown delay from the options singleton */
+ byte count = OptionsSingleton.getInstance().getDelay();
+
+ /* Perform the countdown on both the window and input devices */
+ while (count >= 0) {
+ cw.setCount(count);
+ ioDevices.displayCount(count);
+ if (count != 0) {
+ Thread.sleep(1000);
+ }
+ count--;
+ }
+
+ /* Start the rowing */
+ ioDevices.startRowing();
+
+ /* Dispose of the countdown and refresh the display so we can see the
+ * gui outputs */
+ countdownShell.dispose();
+ window.getShell().getDisplay().update();
+ }
+ }
+ }
+ catch (Exception e) {
+ boolean flag = true;
+ if (e instanceof NumberFormatException) {
+ GUIUtil.showNumberFormatException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof IOException) {
+ GUIUtil.showIOException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (e instanceof InterruptedException) {
+ GUIUtil.showInterruptedException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (flag) {
+ GUIUtil.showErrorDialog(window.getShell(), "Program Error\n( " +
+ e.getMessage() + " )");
+
+ }
+ }
+ }
+
+
+ /**
+ * This is called when a workout is aborted. It ensures that all devices are aware of this
+ * regardless of the location in the program loop (so window.workoutFinished() in
+ * updateStrokeData() can't be entered due to the if statement on the aborted flag but the
+ * call to window.workoutFinished in this method will be).
+ */
+ public void workoutAborted() {
+
+ /* Set the aborted flag and racing flags */
+ aborted = true;
+ workoutFinished();
+
+ /* Call workout finished in the IODevice handler and main window */
+ try {
+ ioDevices.workoutAborted();
+ window.workoutAborted();
+ } catch (Exception e) {
+ boolean flag = true;
+ if (e instanceof IOException) {
+ GUIUtil.showIOException(window.getShell(), e.getMessage());
+ flag = false;
+ }
+ if (flag) {
+ GUIUtil.showErrorDialog(window.getShell(), "Program Error\n( " +
+ e.getMessage() + " )");
+ }
+ }
+ }
+
+
+ /**
+ * This is called when a workout is finished. It is distinct from cleanUpWorkout() which
+ * removes the associated input resources. This is not required at this point as the user
+ * may wish to cool down and see their split.
+ */
+ public void workoutFinished() {
+
+ racing = false;
+ }
+
+
+ /**
+ * If called performace figures are written to performance.txt
+ */
+ public void writePerformanceFile() {
+
+ try {
+ performanceWriter = new BufferedWriter( new FileWriter("performance.txt") );
+ } catch (IOException e) {
+ GUIUtil.showIOException( window.getShell(), e.getMessage() );
+ }
+ }
+
+
+ /* Private Methods */
+
+ /**
+ * Updates the stroke data for every rower and passes this to the window
+ */
+ private void updateStrokeData() throws Exception {
+
+ /* Performance note:
+ * The retrieval of stroke data takes approximately 50ms per PM2Unit - this is due to
+ * waiting for the PM2PlusUnit event to fire (v. annoying). However this can be
+ * dropped to 30ms by not retrieving power and hr (option in tools->options).
+ * The updating of the GUI outputs approximately 10ms */
+
+ /* Generate the latest stroke data and pass this to the GUI */
+ if (performanceWriter != null) {
+ long current = System.currentTimeMillis();
+ performanceWriter.write(current - lastTime + " ");
+ lastTime = current;
+ }
+ StrokeCollection strokes = ioDevices.getLatestStrokeCollection();
+ if (performanceWriter != null) {
+ long current = System.currentTimeMillis();
+ performanceWriter.write(current - lastTime + "\n");
+ lastTime = current;
+ }
+
+ /* If it's the last stroke retrieved then inform main window of workout end. Otherwise
+ * just update as normal. */
+ if (racing) {
+ window.updateStrokeData( strokes );
+ }
+ else {
+ if (!aborted) {
+ window.workoutFinished( strokes );
+ }
+ }
+ }
+}
57 com/rowtheboat/controller/IInputOutputDevice.java
@@ -0,0 +1,57 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: IInputOutputDevice.java
+ *
+ * Date Version User Description
+ * 22-Jan-2004 1.0 GeorgeP Initial version coded
+ *
+ */
+
+package com.rowtheboat.controller;
+
+/**
+ * IInputOutputDevice is the top level interface for all input and output devices for the program.
+ *
+ * @author GeorgeP
+ */
+public interface IInputOutputDevice {
+
+ /**
+ * Display a count, which is used to do a countdown
+ *
+ * @param time the number to display
+ * @throws Exception
+ */
+ public void displayCount(byte time) throws Exception;
+
+
+ /**
+ * Starts the rowing.
+ *
+ * @param startType the start type to use (get from Workout)
+ * @throws Exception
+ */
+ public void startRowing(int startType) throws Exception;
+}
424 com/rowtheboat/controller/IODeviceHandler.java
@@ -0,0 +1,424 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: IODeviceHandler.java
+ *
+ * Date Version User Description
+ * 28-Nov-2003 1.0 GeorgeP Initial version coded
+ * 30-Oct-2004 1.03 GeorgeP Updated to RXTX pure serial driver
+ * 10-Dec-2004 1.04 GeorgeP Garbage collection changes made
+ *
+ */
+
+package com.rowtheboat.controller;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.TooManyListenersException;
+
+import gnu.io.PortInUseException;
+import gnu.io.UnsupportedCommOperationException;
+
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.dom4j.DocumentException;
+import org.xml.sax.SAXException;
+
+import com.rowtheboat.output.IFileOutputDevice;
+import com.rowtheboat.output.XMLWorkoutWriter;
+import com.rowtheboat.workout.ComputerRower;
+import com.rowtheboat.workout.HumanRower;
+import com.rowtheboat.workout.Rower;
+import com.rowtheboat.workout.Workout;
+import com.rowtheboat.gui.OptionsSingleton;
+import com.rowtheboat.input.FixedSplitInput;
+import com.rowtheboat.input.IInputDevice;
+import com.rowtheboat.input.PM2PlusUnit;
+import com.rowtheboat.input.StrokeData;
+import com.rowtheboat.input.VariableSplitInput;
+
+/**
+ * IODeviceHandler encapsulates the input and output devices for the workout. It provides the
+ * latest StrokeCollection upon request.
+ *
+ * @author GeorgeP
+ */
+
+public class IODeviceHandler {
+
+ /* Class Variables */
+ private float lastStrokeTime = -1; /* The workout time of the last stroke */
+ private long lastStrokeMillis; /* The last stroke time in milliseconds */
+ private int numberOfRowers; /* The number of rowers */
+ private int startType; /* The workout start type */
+ private Core core; /* Reference to the core */
+
+
+ private int [] humanRowerErgoNumbers; /* Maintains human rower indexes */
+ private IInputDevice [] inputDevices; /* The input devices */
+ private IInputDevice [] shadowInputDevices; /* The shadow input devices */
+ private IFileOutputDevice [] fileOutputDevices; /* The file output devices */
+
+
+ /* Constructor */
+
+ /**
+ * Construct the collection of strokes
+ *
+ * @param workout the workout which the strokes will be part of
+ * @param core the Core to which this handler should be references
+ */
+ public IODeviceHandler(Workout workout, Core core) throws Exception {
+
+ /* Initialise the class fields variables */
+ this.core = core;
+ numberOfRowers = workout.numberOfRowers();
+ startType = workout.getStartType();
+
+ /* Initialise the array class variables */
+ humanRowerErgoNumbers = new int[numberOfRowers];
+ inputDevices = new IInputDevice [numberOfRowers];
+ shadowInputDevices = new IInputDevice [numberOfRowers];
+ fileOutputDevices = new IFileOutputDevice [numberOfRowers];
+
+ /* Initialise the input devices */
+ initialiseInputDevices(workout);
+ setupWorkout(workout);
+ }
+
+
+ /* Public Methods */
+
+ /**
+ * Clean up any resources associated with the input devices
+ */
+ public void cleanUp() throws Exception {
+
+ /* Used so PM methods only called once */
+ boolean flag = true;
+
+ /* Delegate the responsability to each input device */
+ for (int i = 0; i < inputDevices.length; i++) {
+ if (humanRowerErgoNumbers[i] == -1) {
+ inputDevices[i].cleanUp();
+ }
+ else {
+ if (flag) {
+ inputDevices[i].cleanUp();
+ flag = false;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Called to retrieve the latest stroke collection
+ *
+ * @return a stroke collection
+ */
+ public StrokeCollection getLatestStrokeCollection() throws Exception {
+
+ /* Initialise the stroke collection */
+ StrokeCollection strokeCollection = new StrokeCollection(numberOfRowers);
+
+ /* The boolean that tracks if the workout has finished for all human boats */
+ boolean workoutFinished = true;
+
+ /* The current lag since the last time update */
+ float lag = 0;
+
+ /* Loop over the input devices */
+ for (int i = 0; i < inputDevices.length; i++) {
+
+ if (humanRowerErgoNumbers[i] != -1) {
+
+ /* If the input device is a human rower, get the stroke data, add this to the
+ * stroke collection and set the last time */
+
+ StrokeData stroke = inputDevices[i].retrieveStrokeData( humanRowerErgoNumbers[i] );
+
+ /* Call a garbage collection as retrieving stroke data sets a stroke to null so
+ * it is desirable to recover this space. */
+ OptionsSingleton.getInstance().garbageCollect();
+
+ /* If the stroke time and last stroke time are not equal (i.e. a second has cligged
+ * on in the PM2+) then calculate the latest stroke distance and time */
+ if ( OptionsSingleton.getInstance().getBoatSmoothing() ) {
+
+ /* Calculate the lag and use to update the stroke time for more accuracy */
+ lag = calculateLag(stroke);
+ stroke.setTime( lastStrokeTime + lag );
+ }
+ else {
+ lastStrokeTime = stroke.getTime();
+ }
+
+ /* Set the stroke in the stroke collection */
+ strokeCollection.setStroke(i, stroke);
+
+ /* Generate shadow stroke data where appropriate */
+ if (shadowInputDevices[i] != null) {
+ strokeCollection.setShadowStroke( i,
+ shadowInputDevices[i].retrieveStrokeData( lastStrokeTime + lag ) );
+ }
+
+ /* If the stroke data is the end of the stroke then update the output file */
+ if ( stroke.isEndOfStroke() && fileOutputDevices[i] != null ) {
+ fileOutputDevices[i].updateStrokeData(stroke);
+ }
+
+ /* If the stroke is the the end of the workout then inform the output device
+ * of the last stroke */
+ if ( stroke.isEndOfWorkout() && fileOutputDevices[i] != null ) {
+ fileOutputDevices[i].workoutFinished(stroke);
+ }
+
+ /* Update if the workout has finished */
+ workoutFinished = workoutFinished && stroke.isEndOfWorkout();
+ }
+ else {
+ /* Else, the rower is a computer generated, so use the time to retrieve the
+ * stroke data. As there must be one human rower, the last time variable will
+ * always be set. */
+
+ strokeCollection.setStroke
+ (i, inputDevices[i].retrieveStrokeData(lastStrokeTime + lag));
+ }
+ }
+
+
+ /* Test to see whether the workout is over */
+ if ( workoutFinished ) {
+
+ /* Inform the core that the workout has finished */
+ core.workoutFinished();
+ }
+
+ /* Return the stroke collection */
+ return strokeCollection;
+ }
+
+
+ /**
+ * Display the count on each input device. This is used as the basis of a countdown
+ *
+ * @param time the time (count) to display on the input device
+ */
+ public void displayCount(byte time) throws Exception {
+
+ /* Used so PM methods only called once */
+ boolean flag = true;
+
+ /* Delegate the responsability to each input device */
+ for (int i = 0; i < inputDevices.length; i++) {
+ if (humanRowerErgoNumbers[i] == -1) {
+ inputDevices[i].displayCount(time);
+ }
+ else {
+ if (flag) {
+ inputDevices[i].displayCount(time);
+ flag = false;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Starts the rowing by informing the input and output devices to begin
+ */
+ public void startRowing() throws Exception {
+
+ /* Used so PM methods only called once */
+ boolean flag = true;
+
+ /* Delegate the responsability to each input device */
+ for (int i = 0; i < inputDevices.length; i++) {
+ if (humanRowerErgoNumbers[i] == -1) {
+ inputDevices[i].startRowing(startType);
+ }
+ else {
+ if (flag) {
+ inputDevices[i].startRowing(startType);
+ flag = false;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * This is called if a workout is aborted and essentially cleans up the file output devices,
+ * thus ensuring that any cached data is written out.
+ */
+ public void workoutAborted() throws Exception {
+
+ for (int i = 0; i < fileOutputDevices.length; i++) {
+ if (fileOutputDevices[i] != null) {
+ fileOutputDevices[i].workoutAborted();
+ }
+ }
+ }
+
+
+ /* Private Methods */
+
+ /**
+ * Calculate the lag since the last PM unit update
+ *
+ * @param stroke the stroke
+ */
+ private float calculateLag(StrokeData stroke) {
+
+ float lag;
+
+ /* Calculate the latest distance, time and millis */
+ if ( stroke.getTime() != lastStrokeTime ) {
+
+ lastStrokeTime = stroke.getTime();
+ lastStrokeMillis = System.currentTimeMillis();
+ }
+
+ /* Calculate the lag */
+ lag = (float) ((System.currentTimeMillis() - lastStrokeMillis) / 1000.0);
+
+ /* This stops boats moving backwards - although it doesn't solve the
+ * original problem */
+ if (lag > 1) {
+ lag = 1;
+ }
+
+ return lag;
+ }
+
+
+ /**
+ * This method initialises the input devices
+ *
+ * @param workout the workout to initialise the devices for
+ */
+ private void initialiseInputDevices(Workout workout)
+ throws ParserConfigurationException, FactoryConfigurationError, NumberFormatException,
+ PortInUseException, UnsupportedCommOperationException, TooManyListenersException,
+ IOException, InterruptedException, SAXException, MalformedURLException,
+ DocumentException {
+
+ /* Loop over the human rowers */
+ for (int i = 0; i < inputDevices.length; i++) {
+
+ /* Test the type of rower */
+ Rower rower = workout.getRower(i);
+ if (rower instanceof HumanRower) {
+
+ /* If the rower is a human rower then initialise the PM2+ unit and set their ergo
+ * number. */
+ inputDevices[i] = PM2PlusUnit.getInstance
+ ( OptionsSingleton.getInstance().getSerialPort(), workout );
+ humanRowerErgoNumbers[i] = ((HumanRower) rower).getErgoNumber();
+
+ /* If workout data should be logged then create the xml writer */
+ HumanRower humanRower = ((HumanRower) rower);
+ if ( humanRower.shouldLogData() ) {
+ fileOutputDevices[i] =
+ new XMLWorkoutWriter(humanRower, workout);
+ }
+
+ /* If the rower has a shadow then initialise this */
+ if ( workout.hasShadowRower(i) ) {
+ shadowInputDevices[i] =
+ initialiseComputerRower( ((ComputerRower) workout.getShadow(i)), workout );
+ }
+ }
+ else {
+ /* Initialise the correct type of computer rower */
+ inputDevices[i] = initialiseComputerRower( ((ComputerRower) rower), workout );
+
+ /* Set ergo number to -1, i.e. not a human rower */
+ humanRowerErgoNumbers[i] = -1;
+ }
+ }
+ }
+
+
+ /**
+ * Initilase the computer rower
+ *
+ * @param compRower the computer rower to initialise
+ * @param workout the workout that the computer rower is in
+ * @return a concrete implementation of an input device
+ */
+ private IInputDevice initialiseComputerRower(ComputerRower compRower, Workout workout)
+ throws MalformedURLException, DocumentException {
+
+ /* Test the computer rower type */
+ if ( compRower.getType() == ComputerRower.FIXED_SPLIT ) {
+
+ /* Initialise the correct fixed split computer rower depending on the workout type. */
+ FixedSplitInput input;
+ if (workout.getType() == Workout.DISTANCE_WORKOUT) {
+ input = new FixedSplitInput( Workout.DISTANCE_WORKOUT );
+ input.setTargetDistance(workout.getDistance());
+ }
+ else {
+ /* Must be fixed time workout */
+ input = new FixedSplitInput( Workout.TIME_WORKOUT );
+ input.setTargetSplit(compRower.getSplitToRowAt());
+ }
+ input.setTargetTime(compRower.getRowTime());
+
+ return input;
+ }
+ else {
+ return new VariableSplitInput( compRower.getVariableInputFile() );
+ }
+ }
+
+
+ /**
+ * Setups the input devices for the workout
+ *
+ * @param workout the workout to setup
+ * @throws Exception
+ */
+ private void setupWorkout(Workout workout) throws Exception {
+
+ /* Used so PM methods only called once */
+ boolean flag = true;
+
+ /* Delegate the responsability to each input device */
+ for (int i = 0; i < inputDevices.length; i++) {
+ if (humanRowerErgoNumbers[i] == -1) {
+ inputDevices[i].setupWorkout(workout);
+ }
+ else {
+ if (flag) {
+ inputDevices[i].setupWorkout(workout);
+ flag = false;
+ }
+ }
+ }
+ }
+}
79 com/rowtheboat/controller/Start.java
@@ -0,0 +1,79 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: Start.java
+ *
+ * Date Version User Description
+ * 11-Nov-2003 1.0 GeorgeP Initial version coded
+ *
+ */
+
+package com.rowtheboat.controller;
+
+/**
+ * This class is responsible for starting and exiting PC-Rower properly.
+ *
+ * @author GeorgeP
+ */
+public class Start {
+
+ /* Class Variable */
+
+ private static boolean cleanUp = false; /* Clean up the resources. If not set and the
+ program crashes then don't lose current
+ workout on the PM2+ */
+
+ /* Main Method */
+
+ /**
+ * This method starts the program
+ */
+ public static void main(String[] args) {
+
+ /* Initiate and run the core class, checking for performance criteria */
+ Core core = new Core();
+ for (int i = 0; i < args.length; i++) {
+ if ( args[i].equals("-performance") ) {
+ core.writePerformanceFile();
+ }
+ }
+ core.mainProgramLoop();
+
+ /* Clean up any resources associated with the workout before exiting */
+ if (cleanUp) {
+ core.cleanUpWorkout();
+ }
+ }
+
+
+ /**
+ * This method sets the clean up variable
+ *
+ * @param clean whether to clean up the resources
+ */
+ public static void setCleanUp(boolean clean) {
+
+ cleanUp = clean;
+ }
+}
134 com/rowtheboat/controller/StrokeCollection.java
@@ -0,0 +1,134 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: StrokeCollection.java
+ *
+ * Date Version User Description
+ * 01-Dec-2003 1.0 GeorgeP Initial version coded
+ *
+ */
+
+package com.rowtheboat.controller;
+
+import com.rowtheboat.input.StrokeData;
+
+/**
+ * StrokeCollection encapsulates the stroke information for all rowers in a workout
+ *
+ * @author GeorgeP
+ */
+
+public class StrokeCollection {
+
+ /* Class Variables */
+
+ private StrokeData [] strokes; /* The strokes */
+ private StrokeData [] shadowStrokes; /* The shadow strokes */
+
+
+ /* Constructor */
+
+ /**
+ * Constructs a StrokeCollection for the given number of rowers
+ *
+ * @param numberOfRowers the number of rowers
+ */
+ public StrokeCollection(int numberOfRowers) {
+
+ strokes = new StrokeData[numberOfRowers];
+ shadowStrokes = new StrokeData[numberOfRowers];
+ }
+
+
+ /* Public Methods */
+
+ /**
+ * Returns the number of rowers
+ *
+ * @return the number of rowers
+ */
+ public int getNumberOfRowers() {
+
+ return strokes.length;
+ }
+
+
+ /**
+ * Returns the number of shadow rowers
+ *
+ * @return the number of shadow rowers
+ */
+ public int getNumberOfShadowRowers() {
+
+ return shadowStrokes.length;
+ }
+
+
+ /**
+ * Returns the strokes for a given rower
+ *
+ * @param the index of the rower
+ * @return a StrokeData object
+ */
+ public StrokeData getStroke(int index) {
+
+ return strokes[index];
+ }
+
+
+ /**
+ * Returns the strokes for a given shadow rower
+ *
+ * @param the index of the shadow rower
+ * @return a StrokeData object
+ */
+ public StrokeData getShadowStroke(int index) {
+
+ return shadowStrokes[index];
+ }
+
+
+ /**
+ * Sets the index to be the passed stroke
+ *
+ * @param index the index
+ * @param stroke the stroke
+ */
+ public void setStroke(int index, StrokeData stroke) {
+
+ strokes[index] = stroke;
+ }
+
+
+ /**
+ * Sets the index to be the passed stroke (for a shadow boat)
+ *
+ * @param index the index
+ * @param stroke the stroke
+ */
+ public void setShadowStroke(int index, StrokeData shadowStroke) {
+
+ shadowStrokes[index] = shadowStroke;
+ }
+}
185 com/rowtheboat/gui/AboutWindow.java
@@ -0,0 +1,185 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: AboutWindow.java
+ *
+ * Date Version User Description
+ * 25-Jan-2004 1.0 GeorgeP Initial version coded
+ * 12-Oct-2004 1.01 GeorgeP Corrected file location for platform indepedence
+ * 15-Oct-2004 1.02 GeorgeP Made alterations for generic window sizing
+ *
+ */
+
+package com.rowtheboat.gui;
+
+import java.io.File;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+
+/**
+ * AboutWindow provides the about box
+ *
+ * @author GeorgeP
+ */
+
+public class AboutWindow {
+
+ /* Class Variables */
+
+ private Shell shell; /* The shell for this window */
+
+ private static final String BOAT_SPACER_IMAGE =
+ "images" + File.separatorChar + "boatSpacer.gif";
+ /* The boat spacer image location */
+ private final RGB BACKGROUND_COLOUR = new RGB(222, 231, 231);
+ /* The shell background colour */
+
+
+ /* Constructor */
+
+ /**
+ * Construct the about window
+ *
+ * @param parentSize the parent window size
+ * @param parentLocation the parent window location
+ */
+ public AboutWindow(Point parentSize, Point parentLocation) {
+
+ /* Initialise the window */
+ shell = new Shell( SWT.DIALOG_TRIM | SWT.RESIZE );
+ final GridLayout gridLayout = new GridLayout();
+ shell.setLayout(gridLayout);
+ shell.setText("About Box");
+
+ Color backgroundColour = new Color(shell.getDisplay(), BACKGROUND_COLOUR);
+ shell.setBackground( backgroundColour );
+ backgroundColour.dispose();
+
+ /* Setup the window */
+ Image image =
+ new Image( shell.getDisplay(), OptionsSingleton.getInstance().getIconLocation() );
+ shell.setImage( image );
+ image.dispose();
+
+ /* Setup the contents */
+ setupContents();
+
+ /* Set the shell size and centre */
+ shell.setSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ int x = parentLocation.x + ((parentSize.x - shell.getSize().x) / 2);
+ int y = parentLocation.y + ((parentSize.y - shell.getSize().y) / 4);
+ shell.setLocation(x, y);
+ }
+
+
+ /* Public Methods */
+
+ /**
+ * Returns the shell for this window
+ *
+ * @return the shell
+ */
+ public Shell getShell() {
+
+ return shell;
+ }
+
+
+ /* Private Methods */
+
+ /**
+ * Setup the contents of the window
+ */
+ private void setupContents() {
+
+ /* Set the background colour */
+ Color backgroundColour = new Color(shell.getDisplay(), BACKGROUND_COLOUR);
+
+ /* Application name */
+ Label appName = new Label(shell, SWT.NONE);
+
+ /* Set the font */
+ Font font = new Font(shell.getDisplay(), new FontData("Arial", 24, SWT.BOLD));
+ appName.setFont( font );
+
+ /* Set and dispose of the foreground colout */
+ Color foreground = new Color(shell.getDisplay(), 123, 148, 181);
+ appName.setForeground( foreground );
+ foreground.dispose();
+
+ /* Set the background colour and text */
+ appName.setBackground( backgroundColour );
+ appName.setText(" " + OptionsSingleton.APP_NAME + " ");
+
+
+ /* Version information */
+ Label version = new Label(shell, SWT.NONE);
+ version.setText("Version: " + OptionsSingleton.APP_VERSION);
+ version.setBackground( backgroundColour );
+ GridData versionLayout = new GridData();
+ versionLayout.horizontalAlignment = GridData.CENTER;
+ version.setLayoutData(versionLayout);
+
+ /* Boat spacer */
+ Label boat = new Label(shell, SWT.NONE);
+ Image image = new Image(shell.getDisplay(), BOAT_SPACER_IMAGE);
+ boat.setImage( image );
+ boat.setBackground( backgroundColour );
+ GridData boatSpacerLayout = new GridData();
+ boatSpacerLayout.horizontalAlignment = GridData.CENTER;
+ boat.setLayoutData(boatSpacerLayout);
+
+ /* Author */
+ Label author = new Label(shell, SWT.NONE);
+ author.setBackground( backgroundColour );
+ author.setText("Written by: " + OptionsSingleton.APP_AUTHOR);
+
+ /* Thanks to */
+ Label thanks = new Label(shell, SWT.NONE);
+ thanks.setBackground( backgroundColour );
+ thanks.setText("Thanks to: ");
+
+ Label liz = new Label(shell, SWT.NONE);
+ liz.setBackground( backgroundColour );
+ liz.setText("\tDr. Liz Burd");
+
+ Label wade = new Label(shell, SWT.NONE);
+ wade.setBackground( backgroundColour );
+ wade.setText("\tOwen Wade Hall-Craggs");
+
+
+ /* Dispose of the background colour */
+ backgroundColour.dispose();
+ }
+}
491 com/rowtheboat/gui/AddRowerWindow.java
@@ -0,0 +1,491 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: AddRower.java
+ *
+ * Date Version User Description
+ * 11-Nov-2003 1.0 GeorgeP Initial version coded
+ * 15-Oct-2004 1.02 GeorgeP Made alterations for generic window sizing and seperator.
+ * Allowed for different file seperators for XML file paths.
+ * 30-Oct-2004 1.03 GeorgeP Renamed shadow to isShadow and controller to parentWindow
+ * 10-Nov-2004 1.04 GeorgeP Changed the 0 for split to 00:00. Garbage collect changes
+ * Sorted out sizing on Linux Gtk
+ *
+ */
+
+package com.rowtheboat.gui;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.SWT;
+
+import com.rowtheboat.workout.Workout;
+
+/**
+ * This is the class that provides the add rower window
+ *
+ * @author George Palmer
+ */
+
+public class AddRowerWindow {
+
+ /* Class Variables */
+
+ private boolean isShadow; /* Whether the window is to add a shadow */
+ private Shell shell; /* This shell */
+ private NewWorkoutWindow parentWindow; /* The parent window/controller */
+
+ private Button cancelButton;
+ private Button okButton;
+ private Button xmlReadButton;
+ private Text xmlReadText;
+ private Label readXMLLabel;
+ private Button variableSplitButton;
+ private Text splitOrTimeText;
+ private Label splitOrTimeLabel;
+ private Button fixedSplitButton;
+ private Button writeXMLButton;
+ private Text writeXMLLogText;
+ private Button writeXMLLogButton;
+ private Text ergoText;
+ private Label ergoLabel;
+ private Button humanRowerButton;
+ private Label rowerTypeLabel;
+ private Text nameText;
+ private Label nameLabel;
+
+
+ /* Constructor */
+
+ public AddRowerWindow(NewWorkoutWindow parent, int workoutType, boolean shadow) {
+
+ /* Set the class variables */
+ this.parentWindow = parent;
+ this.isShadow = shadow;
+
+ /* Create the shell */
+ shell = new Shell(parent.getShell(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE );
+ if (shadow) {
+ shell.setText("Add shadow");
+ }
+ else {
+ shell.setText("Add rower");
+ }
+
+ GridLayout thisLayout = new GridLayout(6, false);
+ shell.setLayout(thisLayout);
+
+ /* Setup the icon */
+ Image image =
+ new Image( shell.getDisplay(), OptionsSingleton.getInstance().getIconLocation() );
+ shell.setImage( image );
+ image.dispose();
+
+ /* Setup the contents */
+ setupContents(workoutType);
+
+ /* Set the size of the window */
+ shell.setSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ }
+
+
+ /* Public Methods */
+
+ /**
+ * Return teh ergo number
+ *
+ * @return the ergo number
+ */
+ public byte getErgoNumber() {
+
+ /* Convert the ergo number to be zero indexed and return this */
+ int ergoZeroIndexed = (new Integer( ergoText.getText() )).intValue() - 1;
+
+ return new Integer(ergoZeroIndexed).byteValue();
+ }
+
+
+ /**
+ * Return the rower name
+ *
+ * @return the rower name
+ */
+ public String getRowerName() {
+
+ return nameText.getText();
+ }
+
+
+ /**
+ * Return the rower type as an int:
+ * <br>
+ * 1 = Human Rower<br>
+ * 2 = Fixed Split Rower<br>
+ * 3 = Variable Split Rower<br>
+ *
+ * @return the rower type as described above or -1 if an error
+ */
+ public int getRowerType() {
+
+ /* Determine the rower type and return an int value */
+ int type = -1;
+
+ if (humanRowerButton.getSelection()) {
+ type = 1;
+ }
+ if (fixedSplitButton.getSelection()) {
+ type = 2;
+ }
+ if (variableSplitButton.getSelection()) {
+ type = 3;
+ }
+
+ return type;
+ }
+
+
+ /**
+ * Return the current shell
+ *
+ * @return the shell
+ */
+ public Shell getShell() {
+
+ return shell;
+ }
+
+
+ /**
+ * Get the split or the time for the fixed pace boat. Both are in seconds and the caller will
+ * know which to expect as they will have constructed the class.
+ *
+ * @return the split or time
+ */
+ public int getSplitOrTime() {
+
+ String [] split = splitOrTimeText.getText().split(":");
+ if (split.length == 2) {
+ int mins = Integer.parseInt( split[0] );
+ int secs = Integer.parseInt( split[1] );
+
+ return mins * 60 + secs;
+ }
+ else {
+ if (split.length == 1) {
+ return Integer.parseInt( split[0] );
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+
+
+ /**
+ * Return the xml read file location
+ *
+ * @return the read file location
+ */
+ public File getXMLReadFile() {
+
+ /* Return the file replacing \ with \\ */
+ return new File( xmlReadText.getText().replaceAll("\\\\", "\\\\\\\\") );
+ }
+
+
+ /**
+ * Return the xml save file location
+ *
+ * @return the xml save file
+ */
+ public File getXMLSaveFile() throws IOException {
+
+ /* Return the file location if the box is ticked */
+ if ( writeXMLLogButton.getSelection() ) {
+
+ /* Replace \ with \\ */
+ File file = new File( writeXMLLogText.getText().replaceAll("\\\\", "\\\\\\\\") );
+
+ /* If the file doesn't exist then create it */
+ if (!file.canWrite()) {
+ file.createNewFile();
+ }
+
+ return file;
+ }
+ else {
+ return null;
+ }
+ }
+
+
+ /* Private Methods */
+
+ /**
+ * Setup the GUI components
+ */
+ private void setupContents(int workoutType) {
+
+ nameLabel = new Label(shell, SWT.NULL);
+ nameLabel.setText("Name:");
+
+ nameText = new Text(shell, SWT.BORDER);
+ nameText.setLayoutData( GUIUtil.getHorizontalSpanGridData(5, 80) );
+
+ rowerTypeLabel = new Label(shell, SWT.NULL);
+ rowerTypeLabel.setLayoutData( GUIUtil.getHorizontalSpanGridData(6) );
+ rowerTypeLabel.setText("Type:");
+
+ humanRowerButton = new Button(shell, SWT.RADIO | SWT.LEFT);
+ humanRowerButton.setText("Ergo");
+ if (isShadow) {
+ humanRowerButton.setEnabled(false);
+ }
+ else {
+ humanRowerButton.setSelection(true);
+ }
+
+ ergoLabel = new Label(shell, SWT.NULL);
+ ergoLabel.setText("Ergo Number:");
+
+ ergoText = new Text(shell, SWT.BORDER);
+ ergoText.setLayoutData( GUIUtil.getHorizontalSpanGridData(4, 20) );
+ ergoText.setText("1");
+ if (isShadow) {
+ ergoText.setEnabled(false);
+ }
+
+ writeXMLLogButton = new Button(shell, SWT.CHECK | SWT.LEFT);
+ GridData writeXMLLogButtonLData = GUIUtil.getHorizontalGridData(2, 87);
+ writeXMLLogButton.setLayoutData(writeXMLLogButtonLData);
+ writeXMLLogButton.setText("Write XML Log:");
+ if (isShadow) {
+ writeXMLLogButton.setEnabled(false);
+ }
+
+ writeXMLLogText = new Text(shell, SWT.BORDER);
+ if (isShadow) {
+ writeXMLLogText.setEnabled(false);
+ }
+ writeXMLLogText.setLayoutData( GUIUtil.getHorizontalSpanGridData(3, 80) );
+
+ writeXMLButton = new Button(shell, SWT.PUSH | SWT.CENTER);
+ writeXMLButton.setText("...");
+ writeXMLButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ /* Create an open a save dialog */
+ FileDialog saveDialog = new FileDialog(shell, SWT.SAVE);
+ saveDialog.setFilterExtensions(new String [] {"*.xml"});
+ saveDialog.open();
+
+ /* Ensure the path has the xml file extension */
+ String filename = saveDialog.getFileName();
+ if ( !filename.endsWith(".xml") ) {
+ filename += ".xml";
+ }
+ writeXMLLogText.setText( saveDialog.getFilterPath() + File.separatorChar + filename );
+
+ /* Do a garbage collection due to the amount of resources associated with a file
+ * dialog. */
+ OptionsSingleton.getInstance().garbageCollect();
+ }
+ });
+ if (isShadow) {
+ writeXMLButton.setEnabled(false);
+ }
+
+ fixedSplitButton = new Button(shell, SWT.RADIO | SWT.LEFT);
+ fixedSplitButton.setText("Fixed Split");
+ if (isShadow) {
+ fixedSplitButton.setSelection(true);
+ }
+
+ splitOrTimeLabel = new Label(shell, SWT.NULL);
+ String labelText = "Time:";
+ if (workoutType == Workout.TIME_WORKOUT) {
+ labelText = "Split:";
+ }
+ splitOrTimeLabel.setText(labelText);
+
+ splitOrTimeText = new Text(shell, SWT.BORDER);
+ splitOrTimeText.setLayoutData( GUIUtil.getHorizontalSpanGridData(4, 40) );
+ String splitText = "00:00";
+ if (workoutType == Workout.TIME_WORKOUT) {
+ splitText = "00:00";
+ }
+ splitOrTimeText.setText(splitText);
+
+ variableSplitButton = new Button(shell, SWT.RADIO | SWT.LEFT);
+ variableSplitButton.setText("Variable Split");
+
+ readXMLLabel = new Label(shell, SWT.NULL);
+ readXMLLabel.setText("XML file location:");
+
+ xmlReadText = new Text(shell, SWT.BORDER);
+ xmlReadText.setLayoutData( GUIUtil.getHorizontalSpanGridData(3, 80) );
+
+ xmlReadButton = new Button(shell, SWT.PUSH | SWT.CENTER);
+ xmlReadButton.setText("...");
+ xmlReadButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ /* Create an open a save dialog */
+ FileDialog openDialog = new FileDialog(shell, SWT.OPEN);
+ openDialog.setFilterExtensions(new String [] {"*.xml"});
+ openDialog.open();
+
+ /* Ensure the path has the xml file extension */
+ String filename = openDialog.getFileName();
+ if ( !filename.endsWith(".xml") ) {
+ filename += ".xml";
+ }
+ xmlReadText.setText( openDialog.getFilterPath() + File.separatorChar + filename );
+
+ /* Do a garbage collection due to the amount of resources associated with a file
+ * dialog. */
+ OptionsSingleton.getInstance().garbageCollect();
+ }
+ });
+
+ Label seperator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);
+ GridData seperatorGridData = GUIUtil.getHorizontalSpanGridData(6);
+ seperatorGridData.widthHint = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+ seperator.setLayoutData( seperatorGridData );
+
+ okButton = new Button(shell, SWT.PUSH | SWT.CENTER);
+ GridData okButtonData = GUIUtil.getHorizontalSpanGridData(5, 60);
+ okButtonData.horizontalAlignment = GridData.END;
+ okButton.setLayoutData(okButtonData);
+ okButton.setText("OK");
+ okButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ if ( validateRower() ) {
+ shell.dispose();
+ }
+ }
+ });
+
+ cancelButton = new Button(shell, SWT.PUSH | SWT.CENTER);
+ cancelButton.setText("Cancel");
+ GridData cancelButtonGridData = GUIUtil.getHorizontalSpanGridData(1, 60);
+ cancelButtonGridData.horizontalAlignment = GridData.END;
+ cancelButton.setLayoutData( cancelButtonGridData );
+ cancelButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ shell.dispose();
+ }
+ });
+ }
+
+
+ /**
+ * Validate the rower about to be created.
+ */
+ private boolean validateRower() {
+
+ /* Create an error string */
+ String errorString = "";
+
+ /* Check the selected rower type */
+ switch ( getRowerType() ) {
+ case 1 : {
+ /* If a human rower check the ergo number and file if applicable */
+ try {
+ getErgoNumber();
+ }
+ catch (NumberFormatException n) {
+ errorString += "Please enter a valid ergo number\n";
+ }
+
+ try {
+ File file = getXMLSaveFile();
+ if ( file != null && !file.canWrite() ) {
+ errorString += "Please enter a valid file path\n";
+ }
+ }
+ catch (IOException i) {
+ errorString += "Error writing file\n";
+ }
+
+ break;
+ }
+ case 2 : {
+ /* If a fixed split rower check the split or time and use the correct error text */
+ try {
+ if (getSplitOrTime() == -1) {
+ if ( splitOrTimeLabel.getText().equals("Time:") ) {
+ errorString += "Please enter a time in mm:ss form\n";
+ }
+ else {
+ errorString += "Please enter split in mm:ss form\n";
+ }
+ }
+ }
+ catch (NumberFormatException n) {
+ if ( splitOrTimeLabel.getText().equals("Time:") ) {
+ errorString += "Please enter a time in mm:ss form\n";
+ }
+ else {
+ errorString += "Please enter split in mm:ss form\n";
+ }
+ }
+
+ break;
+ }
+ case 3 : {
+ /* If a variable split rower check the input file can be read */
+ if (!getXMLReadFile().canRead()) {
+ errorString += "Please enter a valid file path\n";
+ }
+
+ break;
+ }
+ }
+
+ /* If there's no error add the rower, else show the dialog box */
+ if (errorString.equals("")) {
+ parentWindow.addRower(isShadow);
+ }
+ else {
+ MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR);
+ errorBox.setText("New Rower Error");
+ errorBox.setMessage(errorString);
+ errorBox.open();
+ }
+
+ /* Return whether there's an error */
+ return errorString.equals("");
+ }
+}
153 com/rowtheboat/gui/CountdownWindow.java
@@ -0,0 +1,153 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: CountdownWindow.java
+ *
+ * Date Version User Description
+ * 22-Jan-2004 1.0 GeorgeP Initial version coded
+ * 15-Oct-2004 1.02 GeorgeP Fixed countdown update, font and position for Linux
+ * 11-Dec-2004 1.04 GeorgeP Fixed double numbers bug in setCount. Added shell update
+ */
+
+package com.rowtheboat.gui;
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * CountdownWindow is the window that displays the countdown timer
+ *
+ * @author GeorgeP
+ */
+public class CountdownWindow {
+
+ /* Class Variables */
+
+ private Shell shell; /* The shell */
+ private Label time; /* The time label */
+
+ private static final int SHELL_WIDTH = 225; /* The width to use */
+ private static final int SHELL_HEIGHT = 165; /* The height to use */
+
+ /* Constructor */
+
+ /**
+ * Construct the countdown window
+ *
+ * @param parentSize the parent size
+ * @param parentLocation the parent location
+ */
+ public CountdownWindow(Point parentSize, Point parentLocation) {
+
+ /* Construct the shell */
+ shell = new Shell(SWT.NONE);
+ shell.setLayout(new GridLayout());
+ shell.setText("Countdown");
+
+ /* Set the window icon and dispose of it */
+ Image image =
+ new Image( shell.getDisplay(), OptionsSingleton.getInstance().getIconLocation() );
+ shell.setImage( image );
+
+ /* Setup the shell */
+ setupShell(parentSize, parentLocation);
+ }
+
+
+ /* Public Methods */
+
+ /**
+ * Return the shell
+ *
+ * @return the shell
+ */
+ public Shell getShell() {
+
+ return shell;
+ }
+
+
+ /**
+ * Set the count on the window
+ *
+ * @param count the count to set on the countdown
+ */
+ public void setCount(byte count) {
+
+ if (count == 0) {
+ time.setText("GO");
+ }
+ else {
+ /* Create the spacer. If sized window automatically this wouldn't be needed */
+ String spacer = " ";
+ if (OptionsSingleton.getInstance().getOS()==OptionsSingleton.LINUX) {
+ spacer = " ";
+ }
+
+ /* If the count is double figures then reduce the spacing */
+ if (count > 9) {
+ spacer = spacer.substring(1);
+ }
+
+ time.setText(spacer + count);
+ }
+
+ time.update();
+ shell.update();
+ }
+
+
+ /* Private Methods */
+
+ /**
+ * Sets the shell up. Includes size, location and contents.
+ */
+ private void setupShell(Point parentSize, Point parentLocation) {
+
+ /* Set the shell size and centre */
+ shell.setSize(SHELL_WIDTH,SHELL_HEIGHT);
+ int x = parentLocation.x + ((parentSize.x - SHELL_WIDTH) / 2);
+ int y = parentLocation.y + ((parentSize.y - SHELL_HEIGHT) / 2);
+ shell.setLocation(x, y);
+
+ /* Construct the contents */
+ time = new Label(shell, SWT.NONE);
+ GridData gd = new GridData();
+ gd.widthHint = 200;
+ gd.heightHint = 125;
+ time.setLayoutData(gd);
+
+ /* Set the font and dispose of it */
+ Font font = new Font(shell.getDisplay(),
+ new FontData(OptionsSingleton.getInstance().getOSDependentFont(), 100, SWT.BOLD));
+ time.setFont( font );
+ }
+}
287 com/rowtheboat/gui/GUIUtil.java
@@ -0,0 +1,287 @@
+/*
+ * PC-Rower PC-Rower is a piece of software that allows the connection of a Concept II rowing
+ * machine to a PC to provide real-time and post workout analysis of performance.
+ * Copyright (C) 2003-2005 George Palmer
+ *
+ *
+ * This file is part of PC-Rower. PC-Rower is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License(GPL) as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any later version.
+ * Under the GPL any derivations or alterations of this software must keep this header intact.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The author may be contacted at feedback@rowtheboat.com
+ */
+
+
+ /*
+ * File: GUIUtil.java
+ *
+ * Date Version User Description
+ * 05-Feb-2004 1.0 GeorgeP Initial version coded
+ *
+ */
+
+package com.rowtheboat.gui;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * GUIUtil provides a selection of utilities that can are used by the varous GUI classes.
+ *
+ * @author George Palmer
+ */
+
+public class GUIUtil {
+
+ /**
+ * Create a standard grid data with a horizontal span
+ *
+ * @param horizontalSpan the number of columns to span
+ * @param hIndent the horizontal indent
+ * @return the grid data object
+ */
+ public static GridData getHorizontalGridData(int horizontalSpan, int hIndent) {
+
+ GridData gridData = new GridData();
+ gridData.horizontalSpan = horizontalSpan;
+ gridData.horizontalIndent = hIndent;
+
+ return gridData;
+ }
+
+
+ /**
+ * Create a standard grid data with a horizontal span
+ *
+ * @param horizontalSpan the number of columns to span
+ * @return the grid data object
+ */
+ public static GridData getHorizontalSpanGridData(int horizontalSpan) {
+
+ GridData gridData = new GridData();
+ gridData.horizontalSpan = horizontalSpan;
+
+ return gridData;
+ }
+
+
+ /**
+ * Create a standard grid data with a horizontal span and width hint
+ *
+ * @param horizontalSpan the number of columns to span
+ * @param widthHint the width hint
+ * @return the grid data object
+ */
+ public static GridData getHorizontalSpanGridData(int horizontalSpan, int widthHint) {
+
+ GridData gridData = getHorizontalSpanGridData(horizontalSpan);
+ gridData.widthHint = widthHint;
+
+ return gridData;
+ }
+
+
+ /**
+ * Create a standard grid data with a horizontal and vertical span
+ *
+ * @param horizontalSpan the number of columns to span
+ * @param verticalSpan the number of rows to span
+ * @return the grid data object
+ */
+ public static GridData getHorizontalAndVerticalSpanGridData
+ (int horizontalSpan, int verticalSpan) {
+
+ GridData gridData = getHorizontalSpanGridData(horizontalSpan);
+ gridData.verticalSpan = verticalSpan;
+
+ return gridData;
+ }
+
+
+ /**
+ * Returns a standard GridData
+ *
+ * @param width the width hint to apply to the grid data
+ * @param height the height hint to apply to the grid data
+ *
+ * @return the GridData object
+ */
+ public static GridData getStandardGridData(int width, int height) {
+