/* This file is part of Wattzap Community Edition.
*
* Wattzap Community Edtion 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 3 of the License, or
* (at your option) any later version.
*
* Wattzap Community Edition 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 Wattzap. If not, see <http://www.gnu.org/licenses/>.
*/
package com.wattzap.view;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.TreeMap;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jfree.data.xy.XYSeries;
import com.wattzap.controller.DistributionAccessor;
import com.wattzap.controller.MessageBus;
import com.wattzap.controller.MessageCallback;
import com.wattzap.controller.Messages;
import com.wattzap.model.UserPreferences;
import com.wattzap.model.dto.Telemetry;
import com.wattzap.model.dto.TrainingItem;
import com.wattzap.model.dto.WorkoutData;
import com.wattzap.utils.ActivityReader;
import com.wattzap.utils.TcxWriter;
import com.wattzap.view.graphs.DistributionGraph;
import com.wattzap.view.graphs.GPanel;
import com.wattzap.view.graphs.GenericScatterGraph;
import com.wattzap.view.graphs.MMPGraph;
import com.wattzap.view.graphs.SCHRGraph;
import com.wattzap.view.training.TrainingAnalysis;
/**
* List of workouts stored in the system
*
* @author David George (c) 2014-2016
* @date 17 April 2014
*/
public class Workouts extends JPanel implements ActionListener, MessageCallback {
private static final long serialVersionUID = 1L;
private List<WorkoutData> workoutList;
private List<Integer> selectedRows;
WorkoutData workoutData = null;
public final static String IMPORTDIR = "/Imports/";
private boolean listChanged = true;
private final JTable table;
private final JFrame frame;
ArrayList<Telemetry> telemetry[] = null;
private final UserPreferences userPrefs = UserPreferences.INSTANCE;
private static final Logger logger = LogManager.getLogger("Workouts");
private final static String scGraph = "SCG";
public final static String hrWattsGraph = "PWG"; // heart rate vs power
public final static String qaGraph = "QAG"; // quadrant analysis
public final static double pi = 3.14159;
// summary graphs
private final static String mmpGraph = "MMP";
private final static String schrGraph = "SCHR";
// distribution graphs
private final static String pdGraph = "PDG";
private final static String cdGraph = "CDG";
private final static String hrdGraph = "HRDG";
private final static String tlGraph = "TLDG";
private final static String tlhrGraph = "TLHRDG";
public final static String importer = "IMP";
// menus
private final JMenu summaryMenu;
private final JMenuItem mmpMenuItem;
private final JMenuItem schrMenuItem;
private final JMenuItem importMenuItem;
private final JMenu fatMenu;
private final JMenu scatMenu;
private final JMenuItem cpgMenuItem;
private final JMenuItem powerWattsMenuItem;
private final JMenuItem quadAnalysisMenuItem;
private final JMenu distMenu;
private final JMenuItem pdgMenuItem;
private final JMenuItem cadMenuItem;
private final JMenuItem hrMenuItem;
private final JMenuItem tlMenuItem;
private final JMenuItem tlhrMenuItem;
private final static String[] columnNames = { "Date", "Time", "Source",
"QPower", "Max HR", "Ave HR", "Max Cadence", "Ave Cadence",
"5Sec W/kg", "1Min W/kg", "5Min W/kg", "20Min W/kg", "Load",
"Stress" };
public Workouts() {
super(new GridLayout(1, 0));
this.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
selectedRows = null;
DefaultTableModel model = new DefaultTableModel(0, columnNames.length);
loadData(model);
table = new JTable(model);
model.fireTableDataChanged();
table.setPreferredScrollableViewportSize(new Dimension(1000, 400));
table.setFillsViewportHeight(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
table.getColumnModel().getColumn(0).setPreferredWidth(130);
table.getColumnModel().getColumn(1).setPreferredWidth(100);
// table.getColumnModel().getColumn(0).setCellRenderer(
// new MyCellRenderer());
table.setRowSelectionAllowed(true);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
/*
* Called each time there is a change to the Workouts
* selection (deselect, select)
*
* @see
* javax.swing.event.ListSelectionListener#valueChanged(
* javax.swing.event.ListSelectionEvent)
*/
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
ListSelectionModel lsm = (ListSelectionModel) e
.getSource();
if (lsm.isSelectionEmpty()) {
selectedRows = null;
} else {
selectedRows = new ArrayList<Integer>();
// Find out which indexes are selected.
int minIndex = lsm.getMinSelectionIndex();
int maxIndex = lsm.getMaxSelectionIndex();
for (int i = minIndex; i <= maxIndex; i++) {
if (lsm.isSelectedIndex(i)) {
selectedRows.add(i);
}
}
listChanged = true;
}
}
}
});
// Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
this.setLayout(new BorderLayout());
// Add the scroll pane to this panel.
add(scrollPane, BorderLayout.CENTER);
WorkoutButtonPanel wBP = new WorkoutButtonPanel(this);
add(wBP, BorderLayout.PAGE_END);
// need button for Load, Delete
// Create and set up the window.
frame = new JFrame();
ImageIcon img = new ImageIcon("icons/turbo.jpg");
frame.setIconImage(img.getImage());
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
this.setOpaque(true); // content panes must be opaque
frame.setContentPane(this);
// Menu
JMenuBar menuBar = new JMenuBar();
frame.setJMenuBar(menuBar);
summaryMenu = new JMenu();
summaryMenu.setMnemonic(KeyEvent.VK_S);
summaryMenu.setMargin(new Insets(0, 0, 0, 10));
menuBar.add(summaryMenu);
// MMP, hr/cad/pwr graph
// pwr/time, hr/time, cad, time
mmpMenuItem = new JMenuItem();
mmpMenuItem.setActionCommand(mmpGraph);
summaryMenu.add(mmpMenuItem);
mmpMenuItem.addActionListener(this);
schrMenuItem = new JMenuItem();
schrMenuItem.setActionCommand(schrGraph);
summaryMenu.add(schrMenuItem);
schrMenuItem.addActionListener(this);
importMenuItem = new JMenuItem();
importMenuItem.setActionCommand(importer);
summaryMenu.add(importMenuItem);
importMenuItem.addActionListener(this);
// 1sec...?
fatMenu = new JMenu();
fatMenu.setMnemonic(KeyEvent.VK_F);
fatMenu.setMargin(new Insets(0, 0, 0, 10));
menuBar.add(fatMenu);
// pwr/cad, pwr/dist, speed/cadence
scatMenu = new JMenu();
scatMenu.setMnemonic(KeyEvent.VK_A);
menuBar.add(scatMenu);
scatMenu.setMargin(new Insets(0, 0, 0, 10));
// menuBar.add(new JSeparator());
cpgMenuItem = new JMenuItem();
cpgMenuItem.setActionCommand(scGraph);
cpgMenuItem.addActionListener(this);
scatMenu.add(cpgMenuItem);
powerWattsMenuItem = new JMenuItem();
powerWattsMenuItem.setActionCommand(hrWattsGraph);
powerWattsMenuItem.addActionListener(this);
scatMenu.add(powerWattsMenuItem);
quadAnalysisMenuItem = new JMenuItem(userPrefs.getString("quadAnal"));
quadAnalysisMenuItem.setActionCommand(qaGraph);
quadAnalysisMenuItem.addActionListener(this);
scatMenu.add(quadAnalysisMenuItem);
// distribution
distMenu = new JMenu();
distMenu.setMnemonic(KeyEvent.VK_D);
menuBar.add(distMenu);
pdgMenuItem = new JMenuItem();
pdgMenuItem.setActionCommand(pdGraph);
distMenu.add(pdgMenuItem);
pdgMenuItem.addActionListener(this);
cadMenuItem = new JMenuItem();
cadMenuItem.setActionCommand(cdGraph);
distMenu.add(cadMenuItem);
cadMenuItem.addActionListener(this);
hrMenuItem = new JMenuItem();
hrMenuItem.setActionCommand(hrdGraph);
distMenu.add(hrMenuItem);
hrMenuItem.addActionListener(this);
tlMenuItem = new JMenuItem();
tlMenuItem.setActionCommand(tlGraph);
distMenu.add(tlMenuItem);
tlMenuItem.addActionListener(this);
tlhrMenuItem = new JMenuItem();
tlhrMenuItem.setActionCommand(tlhrGraph);
distMenu.add(tlhrMenuItem);
tlhrMenuItem.addActionListener(this);
doText();
// Display the window.
frame.pack();
frame.setVisible(true);
MessageBus.INSTANCE.register(Messages.LOCALE, this);
}
public void updateModel() {
DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
loadData(tableModel);
tableModel.fireTableDataChanged();
}
public void setVisible(boolean flag) {
if (!frame.isVisible()) {
frame.setVisible(flag);
super.setVisible(flag);
}
DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
loadData(tableModel);
// table = new JTable(model);
tableModel.fireTableDataChanged();
frame.toFront();
}
private void loadData(DefaultTableModel tableModel) {
DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
timeFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
workoutList = UserPreferences.INSTANCE.listWorkouts();
tableModel.setRowCount(0);
// String[] data = new String[columnNames.length];
Object[] data = new Object[columnNames.length];
for (WorkoutData workout : workoutList) {
data[0] = workout.getDateAsString();
// data[0] = new Boolean(true); - checkbox
data[1] = timeFormat.format(new Date(workout.getTime()));
data[2] = workout.getSourceAsString();
data[3] = "" + workout.getQuadraticPower();
data[4] = "" + workout.getMaxHR();
data[5] = "" + workout.getAveHR();
data[6] = "" + workout.getMaxCadence();
data[7] = "" + workout.getAveCadence();
data[8] = String.format("%.2f", workout.getFiveSecondPwr()
/ workout.getWeight());
data[9] = String.format("%.2f",
workout.getOneMinutePwr() / workout.getWeight());
data[10] = String.format("%.2f", workout.getFiveMinutePwr()
/ workout.getWeight());
data[11] = String.format("%.2f", workout.getTwentyMinutePwr()
/ workout.getWeight());
// round up
data[12] = String.format("%.2f", workout.getIntensity() * 100);
data[13] = "" + workout.getStress();
tableModel.addRow(data);
}
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (schrGraph.equals(command)) {
// Speed,Cadence, HR graph - this can only take a single line, don't
// reload data
SCHRGraph();
return;
}
if (importer.equals(command)) {
// Import new workouts
if (!UserPreferences.INSTANCE.isRegistered()
&& (UserPreferences.INSTANCE.getEvalTime()) <= 0) {
logger.info("Out of time "
+ UserPreferences.INSTANCE.getEvalTime());
JOptionPane.showMessageDialog(this,
UserPreferences.INSTANCE.getString("trial_expired"),
UserPreferences.INSTANCE.getString("warning"),
JOptionPane.WARNING_MESSAGE);
return;
}
// All workouts to be imported are dumped into an "Imports"
// directory
String workoutDir = UserPreferences.INSTANCE.getUserDataDirectory()
+ IMPORTDIR;
ActivityReader ar = new ActivityReader();
File dir = new File(workoutDir);
for (File entry : dir.listFiles()) {
if (entry.isFile()) {
try {
ar.readActivity(entry.getCanonicalPath());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
StringBuilder importedFiles = new StringBuilder();
if (ar.getImportedFileList().isEmpty()) {
importedFiles.append(userPrefs.getString("noFiles"));
} else {
importedFiles.append(userPrefs.getString("imported") + ":\n\n");
for (String file : ar.getImportedFileList()) {
importedFiles.append(file);
importedFiles.append("\n");
}// for
updateModel();
}
JOptionPane.showMessageDialog(this, importedFiles.toString(),
userPrefs.getString("import"),
JOptionPane.INFORMATION_MESSAGE);
return;
}
/*
* Load selected data for other graphs
*/
if (!load()) {
return;
}
if (mmpGraph.equals(command)) {
mmpGraph();
return;
}
if (scGraph.equals(command)) {
CSScatterPlot();
return;
}
if (qaGraph.equals(command)) {
QuadrantAnalysis();
return;
}
if (hrWattsGraph.equals(command)) {
// Heart Rate vs Watts Scatter Plot
HRWattsScatterPlot();
return;
}
if (pdGraph.equals(command)) {
// Power distribution graph
DistributionGraph(new DistributionAccessor() {
public int getKey(Telemetry t) {
return getKey(t.getPower());
}
}, 15, userPrefs.getString("pdGr"), userPrefs.getString("poWtt"));
return;
}
if (cdGraph.equals(command)) {
// Cadence distribution graph
DistributionGraph(new DistributionAccessor() {
public int getKey(Telemetry t) {
return getKey(t.getCadence());
}
}, 5, userPrefs.getString("cDgr"), userPrefs.getString("cDrpm"));
return;
}
if (hrdGraph.equals(command)) {
DistributionGraph(new DistributionAccessor() {
public int getKey(Telemetry t) {
if (t.getHeartRate() < 30) {
return -1; // ignore these values
}
return getKey(t.getHeartRate());
}
}, 10, userPrefs.getString("hrDgr"), userPrefs.getString("hrBpm"));
return;
}
if (tlGraph.equals(command)) {
// Training Zone Graph
DistributionGraph(new DistributionAccessor() {
public int getKey(Telemetry t) {
if (!keepZeroes && t.getPower() < 5) {
return -1;
}
return TrainingItem.getTrainingLevel(t.getPower());
}
public String getValueLabel(int v) {
return TrainingItem.getTrainingName(v) + " " + v;
}
}, 0, userPrefs.getString("trainDist"),
userPrefs.getString("trainlevel"));
return;
}
if (tlhrGraph.equals(command)) {
// Training Zone Graph
DistributionGraph(new DistributionAccessor() {
public int getKey(Telemetry t) {
if (!keepZeroes && t.getPower() < 5) {
return -1;
}
return TrainingItem.getHRTrainingLevel(t.getHeartRate());
}
public String getValueLabel(int v) {
return TrainingItem.getTrainingName(v) + " " + v;
}
}, 0, userPrefs.getString("trainlevelhr"),
userPrefs.getString("trainlevel"));
return;
}
}
/**
* Load one or more workouts selected in view. We concatenate all data into
* a telemetry array.
*
* Side effect: Global workoutData points to the last workout data loaded.
* FIX: we should change this
*
* @return false - no data could be loaded due to a selection error. True :
* data is ready to use.
*/
private boolean load() {
if (selectedRows == null || selectedRows.isEmpty()) {
JOptionPane.showMessageDialog(this,
userPrefs.getString("noDataDisp"),
userPrefs.getString("noData"), JOptionPane.ERROR_MESSAGE);
return false;
} else if (listChanged == false && telemetry != null) {
// data already loaded and nothing changed
return true;
}
String workoutDir = UserPreferences.INSTANCE.getUserDataDirectory()
+ TcxWriter.WORKOUTDIR;
telemetry = new ArrayList[selectedRows.size()];
int count = 0;
for (int i : selectedRows) {
workoutData = workoutList.get(i);
String fileName = workoutData.getTcxFile();
try {
telemetry[count] = ActivityReader.readTelemetry(workoutDir
+ fileName);
} catch (Exception e1) {
// bah, do nothing
logger.error(e1.getLocalizedMessage());
}
count++;
}// for
listChanged = false;
return true;
}
/**
* Will reanalyze workouts on the selected rows, saving the data to the
* database.
*/
void reanalyze() {
if (selectedRows == null || selectedRows.isEmpty()) {
JOptionPane.showMessageDialog(this,
userPrefs.getString("noDataDisp"),
userPrefs.getString("noData"), JOptionPane.ERROR_MESSAGE);
return;
}
String workoutDir = UserPreferences.INSTANCE.getUserDataDirectory()
+ TcxWriter.WORKOUTDIR;
telemetry = new ArrayList[selectedRows.size()];
int count = 0;
for (int i : selectedRows) {
workoutData = workoutList.get(i);
String fileName = workoutData.getTcxFile();
int ftp = workoutData.getFtp();
try {
telemetry[count] = ActivityReader.readTelemetry(workoutDir
+ fileName);
workoutData = TrainingAnalysis.analyze(telemetry[count]);
workoutData.setTcxFile(fileName);
workoutData.setFtp(ftp);
userPrefs.updateWorkout(workoutData); // saves data to RDBMS
workoutList.set(i, workoutData); // FIXME updates local cache of
// workouts but won't fire
// list changed event?
} catch (Exception e1) {
logger.error(e1.getLocalizedMessage());
}
count++;
}// for
listChanged = false;
return;
}
/*
* We don't destroy workouts view, we just hide it.
*/
void quit() {
frame.setVisible(false);
}
/**
* Create Mean Maximal Power Graph
*
* This is a graph of power plotted by time.
*/
public void mmpGraph() {
TreeMap<Integer, Long> powerValues = new TreeMap<Integer, Long>();
for (int i = 0; i < telemetry.length; i++) {
Telemetry first = null;
for (Telemetry t : telemetry[i]) {
if (first == null) {
// first time through
first = t;
} else {
int power = t.getPower();
if (power > 50) {
if (powerValues.containsKey(power)) {
long time = powerValues.get(power);
powerValues.put(power,
time + (t.getTime() - first.getTime()));
} else {
powerValues.put(power,
t.getTime() - first.getTime());
}
first = t;
}
}
}// for
}// for
int ftp20 = 0;
long ftp20T = 0;
int ftp = 0;
long ftpT = 0;
long total = 0;
XYSeries series = new XYSeries(userPrefs.getString("mmp"));
for (Entry<Integer, Long> entry : powerValues.descendingMap()
.entrySet()) {
Integer pwr = entry.getKey(); // power, Y axis
if (total == 0) {
// first time thru
total = entry.getValue();
if (total > 1) {
// add extra point at 1 second
series.addOrUpdate(1, (double) pwr);
}
} else {
total += entry.getValue(); // time in milliseconds - X axis
}
if (total > (20 * 60 * 1000) && ftp20 == 0) {
// 20 minutes
ftp20 = pwr;
ftp20T = total / 1000;
}
if (total > (60 * 60 * 1000) && ftp == 0) {
// 20 minutes
ftp = pwr;
ftpT = total / 1000;
}
series.addOrUpdate(total / 1000, (double) pwr);
}// for
MMPGraph mmp = new MMPGraph(series, ftp20, ftp20T, ftp, ftpT);
String title = userPrefs.getString("mmp");
JFrame frame = new JFrame(title);
ImageIcon img = new ImageIcon("icons/turbo.jpg");
frame.setIconImage(img.getImage());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Create and set up the content pane.
mmp.setOpaque(true); // content panes must be opaque
frame.setContentPane(new GPanel(mmp, removeNonASCII(title)));
// Display the window.
frame.pack();
frame.setVisible(true);
}
/**
* Quadrant Analysis
*/
private void QuadrantAnalysis() {
XYSeries series = new XYSeries(userPrefs.getString("forceVeloc"));
for (int i = 0; i < telemetry.length; i++) {
for (Telemetry t : telemetry[i]) {
if (t.getCadence() > 50 && t.getPower() > 50) {
// CPV - (Cadence * crankLength (meters) * 2 * Pi) / 60
double cpv = (t.getCadence() * 0.1725 * 2 * pi) / 60;
// AEPF = (power * 60) / (Cadence * 2 * Pi * Crank Length)
double aepf = (t.getPower() * 60)
/ (t.getCadence() * 2 * pi * 0.1725);
series.addOrUpdate(cpv, aepf);
}
}// for
}// for
GenericScatterGraph mmp = new GenericScatterGraph(series, "CPV (m/s)",
"AEPF (newtons)");
XYSeries series1 = new XYSeries("FTP");
for (int cadence = 16; cadence < 160; cadence++) {
// CPV - (Cadence * crankLength (meters) * 2 * Pi) / 60
double cpv = (cadence * 0.1725 * 2 * 3.142) / 60;
// AEPF = (power * 60) / (Cadence * 2 * Pi * Crank Length)
double aepf = (UserPreferences.INSTANCE.getMaxPower() * 60)
/ (cadence * 2 * 3.142 * 0.1725);
series1.add(cpv, aepf);
}
mmp.addLine(series1);
XYSeries series2 = new XYSeries("CPV (80 rpm)");
double cpv = (80 * 0.1725 * 2 * 3.142) / 60;
series2.add(cpv, 0);
series2.add(cpv, 700);
mmp.addLine(series2);
XYSeries series3 = new XYSeries("CPV (80 rpm)");
double aepf = (UserPreferences.INSTANCE.getMaxPower() * 60)
/ (80 * 2 * 3.142 * 0.1725);
series3.add(0, aepf);
series3.add(3, aepf);
mmp.addLine(series3);
String title = userPrefs.getString("quadAnal");
JFrame frame = new JFrame(title);
ImageIcon img = new ImageIcon("icons/turbo.jpg");
frame.setIconImage(img.getImage());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Create and set up the content pane.
mmp.setOpaque(true); // content panes must be opaque
frame.setContentPane(new GPanel(mmp, removeNonASCII(title)));
// Display the window.
frame.pack();
frame.setVisible(true);
}
/**
* Cadence / Speed Scatter plot
*/
private void CSScatterPlot() {
XYSeries series = new XYSeries(userPrefs.getString("cadPow"));
for (int i = 0; i < telemetry.length; i++) {
for (Telemetry t : telemetry[i]) {
if (t.getCadence() > 0 && t.getPower() > 0) {
series.addOrUpdate(t.getPower(), t.getCadence());
}
}// for
}// for
GenericScatterGraph mmp = new GenericScatterGraph(series,
userPrefs.getString("poWtt"), userPrefs.getString("cDrpm"));
String title = userPrefs.getString("cadPow");
JFrame frame = new JFrame(title);
ImageIcon img = new ImageIcon("icons/turbo.jpg");
frame.setIconImage(img.getImage());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Create and set up the content pane.
mmp.setOpaque(true); // content panes must be opaque
frame.setContentPane(new GPanel(mmp, removeNonASCII(title)));
// Display the window.
frame.pack();
frame.setVisible(true);
}
public void HRWattsScatterPlot() {
XYSeries series = new XYSeries(userPrefs.getString("hrWatts"));
for (int i = 0; i < telemetry.length; i++) {
for (Telemetry t : telemetry[i]) {
if (t.getHeartRate() > 0 && t.getPower() > 0) {
series.addOrUpdate(t.getPower(), t.getHeartRate());
}
}// for
}// for
GenericScatterGraph mmp = new GenericScatterGraph(series,
userPrefs.getString("poWtt"), userPrefs.getString("hrBpm"));
String title = userPrefs.getString("hrPow");
JFrame frame = new JFrame(title);
ImageIcon img = new ImageIcon("icons/turbo.jpg");
frame.setIconImage(img.getImage());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Create and set up the content pane.
mmp.setOpaque(true); // content panes must be opaque
frame.setContentPane(new GPanel(mmp, removeNonASCII(title)));
// Display the window.
frame.pack();
frame.setVisible(true);
}
public void DistributionGraph(DistributionAccessor da, int scale,
String title, String label) {
DistributionGraph dgGraph = new DistributionGraph(telemetry, da, label,
scale);
dgGraph.updateValues(scale, true);
JFrame frame = new JFrame(title);
ImageIcon img = new ImageIcon("icons/turbo.jpg");
frame.setIconImage(img.getImage());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Create and set up the content pane.
dgGraph.setOpaque(true); // content panes must be opaque
frame.setContentPane(new GPanel(dgGraph, removeNonASCII(title)));
// Display the window.
frame.pack();
frame.setVisible(true);
}
/**
* Draw a graph of Power, Cadence and Heart Rate (and maybe speed?).
*/
public void SCHRGraph() {
if (selectedRows != null && selectedRows.size() > 1) {
JOptionPane.showMessageDialog(this, userPrefs.getString("sglWk"),
userPrefs.getString("selErr"), JOptionPane.ERROR_MESSAGE);
return;
}
if (!load()) {
return;
}
SCHRGraph pchrGraph = new SCHRGraph(telemetry);
// show data with smoothing of 1 second
pchrGraph.updateValues(1);
pchrGraph.updateWorkoutData(workoutData);
String title = userPrefs.getString("rideSum");
JFrame frame = new JFrame(title);
ImageIcon img = new ImageIcon("icons/turbo.jpg");
frame.setIconImage(img.getImage());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
pchrGraph.setOpaque(true); // content panes must be opaque
frame.setContentPane(new GPanel(pchrGraph, removeNonASCII(title)));
// Display the window.
frame.pack();
frame.setVisible(true);
}
void delete() {
if (selectedRows == null && selectedRows.isEmpty()) {
JOptionPane.showMessageDialog(this, "No Rows",
"No rows selected for delete", JOptionPane.WARNING_MESSAGE);
return;
}
StringBuilder output = new StringBuilder();
output.append(UserPreferences.INSTANCE.getString("delMsg") + "\n\n");
int[] rows = new int[selectedRows.size()];
Iterator<Integer> row = selectedRows.iterator();
for (int i = 0; i < selectedRows.size(); i++) {
rows[i] = row.next().intValue();
WorkoutData data = workoutList.get(rows[i]);
output.append(data.getTcxFile() + "\n");
}// for
// 0 is Yes
int deleteData = JOptionPane.showConfirmDialog(this, output,
"Workout Delete", JOptionPane.YES_NO_OPTION);
if (deleteData == 0) {
deleteSelectedRows(rows);
}
}
/*
* Delete one or more rows of data from the database.
*/
private void deleteSelectedRows(int[] rows) {
DefaultTableModel dm = (DefaultTableModel) table.getModel();
for (int i = rows.length - 1; i >= 0; i--) {
dm.removeRow(rows[i]);
String fileName = workoutList.get(rows[i]).getTcxFile();
userPrefs.deleteWorkout(fileName);
String path = UserPreferences.INSTANCE.getUserDataDirectory()
+ TcxWriter.WORKOUTDIR + fileName;
File file = new File(path);
if (file.delete()) {
// log an error
logger.error("deleted " + path);
}
workoutList.remove(rows[i]);
}// for
selectedRows = null;
}
/*
* Setup button text, makes it easy to update if locale is changed
*/
private void doText() {
frame.setTitle("Wattzap Analyzer - "
+ userPrefs.getString("training_analysis"));
summaryMenu.setText(userPrefs.getString("summary"));
mmpMenuItem.setText(userPrefs.getString("mmp"));
schrMenuItem.setText(userPrefs.getString("schr"));
importMenuItem.setText(userPrefs.getString("import"));
fatMenu.setText(userPrefs.getString("fatigue"));
scatMenu.setText(userPrefs.getString("scatter"));
cpgMenuItem.setText(userPrefs.getString("cpg"));
powerWattsMenuItem.setText(userPrefs.getString("poWt"));
quadAnalysisMenuItem.setText(userPrefs.getString("quadAnal"));
distMenu.setText(userPrefs.getString("distribution"));
pdgMenuItem.setText(userPrefs.getString("power"));
cadMenuItem.setText(userPrefs.getString("cadence"));
hrMenuItem.setText(userPrefs.getString("heartrate"));
tlMenuItem.setText(userPrefs.getString("trainlevel"));
tlhrMenuItem.setText(userPrefs.getString("trainlevelhr"));
for (int i = 0; i < table.getColumnCount(); i++) {
TableColumn column1 = table.getTableHeader().getColumnModel()
.getColumn(i);
column1.setHeaderValue(columnNames[i]);
}
}
/**
* Change text language if we get a LOCALE message
*/
@Override
public void callback(Messages message, Object o) {
switch (message) {
case LOCALE:
doText();
break;
}
}
private String removeNonASCII(String s) {
return s.replaceAll("[^\\x00-\\x7F]", "");
}
public class MyCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
if (value instanceof JComboBox) {
return (JComboBox) value;
}
if (value instanceof Boolean) {
JCheckBox cb = new JCheckBox();
cb.setSelected(((Boolean) value).booleanValue());
return cb;
}
if (value instanceof JCheckBox) {
return (JCheckBox) value;
}
return new JTextField(value.toString());
}
}
}