diff --git a/recorder/build.sh b/recorder/build.sh new file mode 100755 index 0000000..0027ede --- /dev/null +++ b/recorder/build.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +mvn clean compile assembly:single +cp target/recorder-*-jar-with-dependencies.jar recorder.jar +chmod +x recorder.jar \ No newline at end of file diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordTableModel.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordTableModel.java index fb3aaa0..9a1f6a2 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordTableModel.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordTableModel.java @@ -81,6 +81,11 @@ public class RunRecordTableModel extends AbstractTableModel { } } + public long getRecordIdAtRow(int row) { + if (row < 0 || row >= records.size()) return -1; + return records.get(row).id(); + } + @Override public int getRowCount() { return records.size(); diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordsPanel.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordsPanel.java index 93eb24e..f5356c6 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordsPanel.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/RunRecordsPanel.java @@ -5,8 +5,6 @@ import com.github.andrewlalis.running_every_day.data.db.DataSource; import com.github.andrewlalis.running_every_day.data.db.Queries; import javax.swing.*; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; import java.awt.*; import java.math.BigDecimal; import java.math.RoundingMode; @@ -24,6 +22,10 @@ public class RunRecordsPanel extends JPanel { private final DataSource dataSource; private final RunRecordTableModel tableModel; + private final JTable table; + + private final JButton deleteRecordButton = new JButton("Delete Record"); + private final JTextField currentPageField = new JTextField("1", 3); private final JButton firstPageButton = new JButton("First Page"); private final JButton previousPageButton = new JButton("Previous Page"); @@ -34,6 +36,7 @@ public class RunRecordsPanel extends JPanel { super(new BorderLayout()); this.dataSource = dataSource; this.tableModel = new RunRecordTableModel(dataSource); + this.table = new JTable(tableModel); tableModel.addTableModelListener(e -> updateButtonStates()); this.add(buildTablePanel(), BorderLayout.CENTER); @@ -46,7 +49,6 @@ public class RunRecordsPanel extends JPanel { } private Container buildTablePanel() { - var table = new JTable(tableModel); table.getTableHeader().setReorderingAllowed(false); table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); table.getColumnModel().getColumn(0).setMaxWidth(50); @@ -61,6 +63,9 @@ public class RunRecordsPanel extends JPanel { for (int i = 0; i < 7; i++) { table.getColumnModel().getColumn(i).setResizable(false); } + table.getSelectionModel().addListSelectionListener(e -> { + deleteRecordButton.setEnabled(!e.getValueIsAdjusting() && tableModel.getRecordIdAtRow(table.getSelectedRow()) != -1); + }); return new JScrollPane(table, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); } @@ -78,6 +83,32 @@ public class RunRecordsPanel extends JPanel { JButton generateRandomDataButton = new JButton("Generate Random Data"); generateRandomDataButton.addActionListener(e -> generateRandomData()); actionsPanel.add(generateRandomDataButton); + deleteRecordButton.addActionListener(e -> { + long id = tableModel.getRecordIdAtRow(table.getSelectedRow()); + if (id != -1) { + int result = JOptionPane.showConfirmDialog( + this, + "Are you sure you want to delete this record? This is permanent.", + "Confirm Deletion", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE + ); + if (result == JOptionPane.OK_OPTION) { + try { + dataSource.runRecords().delete(id); + } catch (SQLException ex) { + ex.printStackTrace(); + JOptionPane.showMessageDialog( + this, + "An SQL Exception occurred: " + ex.getMessage(), + "Error", + JOptionPane.ERROR_MESSAGE + ); + } + } + } + }); + actionsPanel.add(deleteRecordButton); return actionsPanel; } diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderer.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderer.java index 8643634..f324060 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderer.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderer.java @@ -4,5 +4,5 @@ import java.awt.*; import java.awt.geom.Rectangle2D; public interface ChartRenderer { - void render(Graphics2D graphics, Rectangle2D area); + void render(Graphics2D graphics, Rectangle2D area, float textScale); } diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderingPanel.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderingPanel.java index fb97070..a669532 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderingPanel.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/ChartRenderingPanel.java @@ -23,7 +23,7 @@ public class ChartRenderingPanel extends JPanel implements Consumer browseForFilePath()); formPanel.add(selectFileButton, c); @@ -57,8 +60,12 @@ public class ExportChartImageDialog extends JDialog { JSpinner heightSpinner = new JSpinner(heightSpinnerModel); heightSpinner.setLocale(Locale.US); formPanel.add(heightSpinner, c); - c.gridy = 2; + JSpinner textScaleSpinner = new JSpinner(textScaleSpinnerModel); + textScaleSpinner.setLocale(Locale.US); + formPanel.add(textScaleSpinner, c); + + c.gridy = 3; filePathField.setEditable(false); filePathField.setText(currentFilePath.toAbsolutePath().toString()); filePathField.addMouseListener(new MouseAdapter() { @@ -110,8 +117,9 @@ public class ExportChartImageDialog extends JDialog { } int width = (int) widthSpinnerModel.getValue(); int height = (int) heightSpinnerModel.getValue(); + double textScale = (Double) textScaleSpinnerModel.getValue(); BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR); - chartRenderer.render(img.createGraphics(), new Rectangle2D.Float(0, 0, width, height)); + chartRenderer.render(img.createGraphics(), new Rectangle2D.Float(0, 0, width, height), (float) textScale); try { ImageIO.write(img, "png", currentFilePath.toFile()); JOptionPane.showMessageDialog( diff --git a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java index d5c9a9f..c9ebd8f 100644 --- a/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java +++ b/recorder/src/main/java/com/github/andrewlalis/running_every_day/view/chart/JFreeChartRenderer.java @@ -4,6 +4,7 @@ import com.github.andrewlalis.running_every_day.Resources; import org.jfree.chart.ChartTheme; import org.jfree.chart.JFreeChart; import org.jfree.chart.StandardChartTheme; +import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import java.awt.*; @@ -12,23 +13,27 @@ import java.io.IOException; public abstract class JFreeChartRenderer implements ChartRenderer { private JFreeChart lastChart = null; - public static final ChartTheme standardChartTheme = getChartTheme(); + + private final StandardChartTheme standardChartTheme = getChartTheme(); + private static final float SMALL_FONT_BASE_SIZE = 10; + private static final float REGULAR_FONT_BASE_SIZE = 12; + private static final float LARGE_FONT_BASE_SIZE = 18; + private static final float EXTRA_LARGE_FONT_BASE_SIZE = 30; protected abstract JFreeChart getChart() throws Exception; - protected void applyCustomStyles(JFreeChart chart) {} - - public void refresh() throws Exception { - lastChart = getChart(); - standardChartTheme.apply(lastChart); - applyCustomStyles(lastChart); - } + protected void applyCustomStyles(JFreeChart chart, float textScale) {} @Override - public void render(Graphics2D graphics, Rectangle2D area) { + public void render(Graphics2D graphics, Rectangle2D area, float textScale) { + if (textScale < 0) textScale = 1; try { if (lastChart == null) { - refresh(); + lastChart = getChart(); } + setThemeTextScale(textScale); + standardChartTheme.apply(lastChart); + applyCustomStyles(lastChart, textScale); + lastChart.draw(graphics, area); } catch (Exception e) { graphics.setColor(Color.RED); @@ -39,7 +44,7 @@ public abstract class JFreeChartRenderer implements ChartRenderer { } } - private static ChartTheme getChartTheme() { + private static StandardChartTheme getChartTheme() { StandardChartTheme theme = new StandardChartTheme("Standard Theme"); Font baseFont = Font.getFont("sans-serif"); Font lightFont = Font.getFont("sans-serif"); @@ -49,10 +54,10 @@ public abstract class JFreeChartRenderer implements ChartRenderer { } catch (IOException e) { e.printStackTrace(); } - theme.setSmallFont(baseFont.deriveFont(10f)); - theme.setRegularFont(baseFont.deriveFont(12f)); - theme.setLargeFont(lightFont.deriveFont(18f)); - theme.setExtraLargeFont(lightFont.deriveFont(30f)); + theme.setSmallFont(baseFont); + theme.setRegularFont(baseFont); + theme.setLargeFont(lightFont); + theme.setExtraLargeFont(lightFont); theme.setChartBackgroundPaint(Color.WHITE); theme.setPlotBackgroundPaint(Color.WHITE); @@ -62,10 +67,29 @@ public abstract class JFreeChartRenderer implements ChartRenderer { return theme; } - protected static void applyStandardXYLineColor(JFreeChart chart, Paint paint) { + private void setThemeTextScale(float scale) { + standardChartTheme.setSmallFont(standardChartTheme.getSmallFont().deriveFont(scale * SMALL_FONT_BASE_SIZE)); + standardChartTheme.setRegularFont(standardChartTheme.getRegularFont().deriveFont(scale * REGULAR_FONT_BASE_SIZE)); + standardChartTheme.setLargeFont(standardChartTheme.getLargeFont().deriveFont(scale * LARGE_FONT_BASE_SIZE)); + standardChartTheme.setExtraLargeFont(standardChartTheme.getExtraLargeFont().deriveFont(scale * EXTRA_LARGE_FONT_BASE_SIZE)); + } + + protected static void applyStandardXYLineColor(JFreeChart chart, Paint paint, float textScale) { XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer(); renderer.setSeriesPaint(0, paint); renderer.setSeriesShapesVisible(0, false); - renderer.setSeriesStroke(0, new BasicStroke(3)); + renderer.setSeriesStroke(0, new BasicStroke(3 * textScale)); + + XYPlot plot = chart.getXYPlot(); + BasicStroke stroke = new BasicStroke( + textScale, + BasicStroke.CAP_SQUARE, + BasicStroke.JOIN_MITER, + 1, + new float[]{2 * textScale, 2 * textScale}, + 0 + ); + plot.setDomainGridlineStroke(stroke); + plot.setRangeGridlineStroke(stroke); } }