Added basic data for run records, and table view with pagination.
This commit is contained in:
parent
8269804f9b
commit
c94bac792c
|
@ -10,14 +10,10 @@ import java.sql.SQLException;
|
||||||
* The main application entrypoint.
|
* The main application entrypoint.
|
||||||
*/
|
*/
|
||||||
public class RecorderApp {
|
public class RecorderApp {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) throws SQLException {
|
||||||
try (var dataSource = new DataSource("jdbc:sqlite:runs.db")) {
|
DataSource dataSource = new DataSource("jdbc:sqlite:runs.db");
|
||||||
FlatLightLaf.setup();
|
FlatLightLaf.setup();
|
||||||
var window = new RecorderAppWindow(dataSource);
|
var window = new RecorderAppWindow(dataSource);
|
||||||
window.setVisible(true);
|
window.setVisible(true);
|
||||||
} catch (SQLException e) {
|
|
||||||
System.err.println("An SQL error occurred: " + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,11 @@ package com.github.andrewlalis.running_every_day.data;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public class DataSource implements AutoCloseable {
|
public class DataSource {
|
||||||
private final Connection conn;
|
private final Connection conn;
|
||||||
|
|
||||||
public DataSource(String url) throws SQLException {
|
public DataSource(String url) throws SQLException {
|
||||||
|
@ -16,17 +15,32 @@ public class DataSource implements AutoCloseable {
|
||||||
this.initSchemaIfNeeded();
|
this.initSchemaIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RunRecordRepository runRecords() {
|
||||||
|
return new RunRecordRepository(this.conn);
|
||||||
|
}
|
||||||
|
|
||||||
public void close() throws SQLException {
|
public void close() throws SQLException {
|
||||||
this.conn.close();
|
this.conn.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for any missing tables and initializes the database schema with
|
||||||
|
* them if so.
|
||||||
|
* @throws SQLException If an SQL error occurs.
|
||||||
|
*/
|
||||||
private void initSchemaIfNeeded() throws SQLException {
|
private void initSchemaIfNeeded() throws SQLException {
|
||||||
boolean shouldInitSchema;
|
boolean shouldInitSchema = false;
|
||||||
try (
|
final String[] tableNames = {"run"};
|
||||||
var stmt = this.conn.prepareStatement("SELECT name FROM sqlite_master WHERE type='table' AND name='run'");
|
try (var stmt = this.conn.prepareStatement("SELECT name FROM sqlite_master WHERE type='table' AND name=?")) {
|
||||||
var rs = stmt.executeQuery()
|
for (var name : tableNames) {
|
||||||
) {
|
stmt.setString(1, name);
|
||||||
shouldInitSchema = !rs.next();
|
try (var rs = stmt.executeQuery()) {
|
||||||
|
if (!rs.next()) {
|
||||||
|
shouldInitSchema = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (shouldInitSchema) {
|
if (shouldInitSchema) {
|
||||||
try (var stmt = this.conn.createStatement()) {
|
try (var stmt = this.conn.createStatement()) {
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.github.andrewlalis.running_every_day.data;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record Page<T>(
|
||||||
|
List<T> items,
|
||||||
|
Pagination pagination
|
||||||
|
) {
|
||||||
|
public static <T> Page<T> fromResultSet(ResultSet rs, ResultSetMapper<T> mapper, Pagination pagination) throws SQLException {
|
||||||
|
List<T> items = new ArrayList<>(pagination.size());
|
||||||
|
while (rs.next() && items.size() < pagination.size()) {
|
||||||
|
items.add(mapper.map(rs));
|
||||||
|
}
|
||||||
|
return new Page<>(items, pagination);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.github.andrewlalis.running_every_day.data;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record Pagination(int page, int size, List<Sort> sorts) {
|
||||||
|
public record Sort(String property, SortDir direction, NullsDir nullsDirection) {
|
||||||
|
public Sort(String property, SortDir direction) {
|
||||||
|
this(property, direction, NullsDir.FIRST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sort(String property) {
|
||||||
|
this(property, SortDir.ASC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public enum SortDir {ASC, DESC}
|
||||||
|
public enum NullsDir {FIRST, LAST}
|
||||||
|
|
||||||
|
public Pagination(int page, int size) {
|
||||||
|
this(page, size, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pagination nextPage() {
|
||||||
|
return new Pagination(this.page + 1, this.size, this.sorts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pagination previousPage() {
|
||||||
|
return new Pagination(this.page - 1, this.size, this.sorts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toQuerySyntax() {
|
||||||
|
StringBuilder sb = new StringBuilder(256);
|
||||||
|
List<String> orderingTerms = this.sorts.stream()
|
||||||
|
.map(s -> s.property + ' ' + s.direction.name())
|
||||||
|
.toList();
|
||||||
|
if (!orderingTerms.isEmpty()) {
|
||||||
|
sb.append(String.join(",", orderingTerms)).append(' ');
|
||||||
|
}
|
||||||
|
sb.append("LIMIT ").append(this.size).append(" OFFSET ").append(this.page * this.size);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Page<T> execute(Connection conn, String baseQuery, ResultSetMapper<T> mapper) throws SQLException {
|
||||||
|
String query = baseQuery + ' ' + this.toQuerySyntax();
|
||||||
|
try (var stmt = conn.prepareStatement(query)) {
|
||||||
|
return Page.fromResultSet(stmt.executeQuery(), mapper, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.github.andrewlalis.running_every_day.data;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ResultSetMapper<T> {
|
||||||
|
T map(ResultSet rs) throws SQLException;
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package com.github.andrewlalis.running_every_day.data;
|
package com.github.andrewlalis.running_every_day.data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
@ -23,4 +26,38 @@ public record RunRecord(
|
||||||
Duration duration,
|
Duration duration,
|
||||||
BigDecimal weightKg,
|
BigDecimal weightKg,
|
||||||
String comment
|
String comment
|
||||||
) {}
|
) {
|
||||||
|
public BigDecimal distanceMeters() {
|
||||||
|
return distanceKm.multiply(BigDecimal.valueOf(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int durationSeconds () {
|
||||||
|
return (int) duration.getSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal weightGrams() {
|
||||||
|
return weightKg.multiply(BigDecimal.valueOf(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Mapper implements ResultSetMapper<RunRecord> {
|
||||||
|
@Override
|
||||||
|
public RunRecord map(ResultSet rs) throws SQLException {
|
||||||
|
long id = rs.getLong("id");
|
||||||
|
String dateStr = rs.getString("date");
|
||||||
|
String startTimeStr = rs.getString("start_time");
|
||||||
|
int distanceMeters = rs.getInt("distance");
|
||||||
|
int durationSeconds = rs.getInt("duration");
|
||||||
|
int weightGrams = rs.getInt("weight");
|
||||||
|
String comment = rs.getString("comment");
|
||||||
|
return new RunRecord(
|
||||||
|
id,
|
||||||
|
LocalDate.parse(dateStr),
|
||||||
|
LocalTime.parse(startTimeStr),
|
||||||
|
BigDecimal.valueOf(distanceMeters, 3).divide(BigDecimal.valueOf(1000, 3), RoundingMode.UNNECESSARY),
|
||||||
|
Duration.ofSeconds(durationSeconds),
|
||||||
|
BigDecimal.valueOf(weightGrams, 3).divide(BigDecimal.valueOf(1000, 3), RoundingMode.UNNECESSARY),
|
||||||
|
comment
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.github.andrewlalis.running_every_day.data;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record RunRecordRepository(Connection conn) {
|
||||||
|
public Page<RunRecord> findAll(Pagination pagination) throws SQLException {
|
||||||
|
String query = "SELECT id, date, start_time, distance, duration, weight, comment FROM run";
|
||||||
|
return pagination.execute(conn, query, new RunRecord.Mapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RunRecord> findAllByQuery(String query) throws SQLException {
|
||||||
|
List<RunRecord> items = new ArrayList<>();
|
||||||
|
var mapper = new RunRecord.Mapper();
|
||||||
|
try (
|
||||||
|
var stmt = conn.prepareStatement(query);
|
||||||
|
var rs = stmt.executeQuery()
|
||||||
|
) {
|
||||||
|
while (rs.next()) {
|
||||||
|
items.add(mapper.map(rs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(long id) throws SQLException {
|
||||||
|
try (var stmt = conn.prepareStatement("DELETE FROM run WHERE id = ?")) {
|
||||||
|
stmt.setLong(1, id);
|
||||||
|
stmt.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RunRecord save(RunRecord runRecord) throws SQLException {
|
||||||
|
String query = "INSERT INTO run (date, start_time, distance, duration, weight, comment) VALUES (?, ?, ?, ?, ?, ?)";
|
||||||
|
try (var stmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
|
||||||
|
stmt.setString(1, runRecord.date().toString());
|
||||||
|
stmt.setString(2, runRecord.startTime().toString());
|
||||||
|
stmt.setInt(3, runRecord.distanceMeters().intValue());
|
||||||
|
stmt.setInt(4, runRecord.durationSeconds());
|
||||||
|
stmt.setInt(5, runRecord.weightGrams().intValue());
|
||||||
|
stmt.setString(6, runRecord.comment());
|
||||||
|
stmt.executeUpdate();
|
||||||
|
try (var rs = stmt.getGeneratedKeys()) {
|
||||||
|
if (rs.next()) {
|
||||||
|
long id = rs.getLong(1);
|
||||||
|
return new RunRecord(
|
||||||
|
id,
|
||||||
|
runRecord.date(),
|
||||||
|
runRecord.startTime(),
|
||||||
|
runRecord.distanceKm(),
|
||||||
|
runRecord.duration(),
|
||||||
|
runRecord.weightKg(),
|
||||||
|
runRecord.comment()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new SQLException("Missing generated primary key for record.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long countAll() throws SQLException {
|
||||||
|
String query = "SELECT COUNT(id) FROM run";
|
||||||
|
try (
|
||||||
|
var stmt = conn.prepareStatement(query);
|
||||||
|
var rs = stmt.executeQuery()
|
||||||
|
) {
|
||||||
|
if (rs.next()) {
|
||||||
|
return rs.getLong(1);
|
||||||
|
} else {
|
||||||
|
throw new SQLException("Missing count.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long pageCount(int size) throws SQLException {
|
||||||
|
long recordCount = countAll();
|
||||||
|
return (recordCount / size) + (recordCount % size != 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,9 +9,25 @@ public class RecorderAppWindow extends JFrame {
|
||||||
public RecorderAppWindow(DataSource dataSource) {
|
public RecorderAppWindow(DataSource dataSource) {
|
||||||
super("Run-Recorder");
|
super("Run-Recorder");
|
||||||
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
// TODO: Build UI
|
this.setContentPane(buildGui(dataSource));
|
||||||
this.setPreferredSize(new Dimension(800, 600));
|
this.setPreferredSize(new Dimension(1000, 600));
|
||||||
this.pack();
|
this.pack();
|
||||||
this.setLocationRelativeTo(null);
|
this.setLocationRelativeTo(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Container buildGui(DataSource dataSource) {
|
||||||
|
JTabbedPane tabbedPane = new JTabbedPane();
|
||||||
|
tabbedPane.addTab("Run Records", new RunRecordsPanel(dataSource));
|
||||||
|
tabbedPane.addTab("Aggregate Statistics", buildAggregateStatisticsPanel(dataSource));
|
||||||
|
tabbedPane.addTab("Charts", buildChartsPanel(dataSource));
|
||||||
|
return tabbedPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Container buildAggregateStatisticsPanel(DataSource dataSource) {
|
||||||
|
return new JPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Container buildChartsPanel(DataSource dataSource) {
|
||||||
|
return new JPanel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.github.andrewlalis.running_every_day.view;
|
||||||
|
|
||||||
|
import com.github.andrewlalis.running_every_day.data.Page;
|
||||||
|
import com.github.andrewlalis.running_every_day.data.RunRecord;
|
||||||
|
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RunRecordTableModel extends AbstractTableModel {
|
||||||
|
private List<RunRecord> records;
|
||||||
|
|
||||||
|
public RunRecordTableModel() {
|
||||||
|
this.records = new ArrayList<>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPage(Page<RunRecord> page) {
|
||||||
|
this.records = page.items();
|
||||||
|
this.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return records.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
|
if (rowIndex < 0 || rowIndex >= records.size()) return null;
|
||||||
|
RunRecord r = records.get(rowIndex);
|
||||||
|
DecimalFormat singleDigitFormat = new DecimalFormat("#,##0.0");
|
||||||
|
return switch (columnIndex) {
|
||||||
|
case 0 -> Long.toString(r.id());
|
||||||
|
case 1 -> r.date().toString();
|
||||||
|
case 2 -> r.startTime().toString();
|
||||||
|
case 3 -> r.distanceKm().toPlainString();
|
||||||
|
case 4 -> r.duration().toString();
|
||||||
|
case 5 -> r.weightKg().toPlainString();
|
||||||
|
case 6 -> r.comment();
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int column) {
|
||||||
|
return switch (column) {
|
||||||
|
case 0 -> "Id";
|
||||||
|
case 1 -> "Date";
|
||||||
|
case 2 -> "Start Time";
|
||||||
|
case 3 -> "Distance (Km)";
|
||||||
|
case 4 -> "Duration";
|
||||||
|
case 5 -> "Weight (Kg)";
|
||||||
|
case 6 -> "Comment";
|
||||||
|
default -> "Unknown Value";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.github.andrewlalis.running_every_day.view;
|
||||||
|
|
||||||
|
import com.github.andrewlalis.running_every_day.data.DataSource;
|
||||||
|
import com.github.andrewlalis.running_every_day.data.Pagination;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A panel for displaying a table view of run records, and various controls for
|
||||||
|
* navigating the data.
|
||||||
|
*/
|
||||||
|
public class RunRecordsPanel extends JPanel {
|
||||||
|
private final DataSource dataSource;
|
||||||
|
private final RunRecordTableModel tableModel = new RunRecordTableModel();
|
||||||
|
private final JTextField currentPageField;
|
||||||
|
private final JComboBox<Integer> pageSizeSelector;
|
||||||
|
private final JButton firstPageButton;
|
||||||
|
private final JButton previousPageButton;
|
||||||
|
private final JButton nextPageButton;
|
||||||
|
private final JButton lastPageButton;
|
||||||
|
|
||||||
|
private Pagination currentPage;
|
||||||
|
public RunRecordsPanel(DataSource dataSource) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
this.currentPage = new Pagination(0, 20);
|
||||||
|
|
||||||
|
var table = new JTable(tableModel);
|
||||||
|
var scrollPane = new JScrollPane(table, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||||
|
this.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
JPanel paginationPanel = new JPanel(new GridLayout(1, 6));
|
||||||
|
firstPageButton = new JButton("First Page");
|
||||||
|
firstPageButton.addActionListener(e -> {
|
||||||
|
currentPage = new Pagination(0, currentPage.size(), currentPage.sorts());
|
||||||
|
loadPage();
|
||||||
|
});
|
||||||
|
previousPageButton = new JButton("Previous Page");
|
||||||
|
previousPageButton.addActionListener(e -> {
|
||||||
|
currentPage = currentPage.previousPage();
|
||||||
|
loadPage();
|
||||||
|
});
|
||||||
|
nextPageButton = new JButton("Next Page");
|
||||||
|
nextPageButton.addActionListener(e -> {
|
||||||
|
currentPage = currentPage.nextPage();
|
||||||
|
loadPage();
|
||||||
|
});
|
||||||
|
lastPageButton = new JButton("Last Page");
|
||||||
|
lastPageButton.addActionListener(e -> {
|
||||||
|
try {
|
||||||
|
long pageCount = dataSource.runRecords().pageCount(currentPage.size());
|
||||||
|
if (pageCount <= 0) pageCount = 1;
|
||||||
|
currentPage = new Pagination((int) (pageCount - 1), currentPage.size(), currentPage.sorts());
|
||||||
|
loadPage();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JPanel currentPagePanel = new JPanel();
|
||||||
|
currentPagePanel.add(new JLabel("Current Page: "));
|
||||||
|
this.currentPageField = new JTextField("1", 3);
|
||||||
|
// TODO: Add document change listener.
|
||||||
|
currentPagePanel.add(this.currentPageField);
|
||||||
|
|
||||||
|
JPanel pageSizePanel = new JPanel();
|
||||||
|
pageSizePanel.add(new JLabel("Page Size: "));
|
||||||
|
pageSizeSelector = new JComboBox<>(new Integer[]{5, 10, 20, 50, 100, 500});
|
||||||
|
pageSizeSelector.setSelectedItem(this.currentPage.size());
|
||||||
|
pageSizeSelector.addItemListener(e -> {
|
||||||
|
currentPage = new Pagination(0, (Integer) e.getItem(), currentPage.sorts());
|
||||||
|
loadPage();
|
||||||
|
});
|
||||||
|
pageSizePanel.add(pageSizeSelector);
|
||||||
|
|
||||||
|
|
||||||
|
paginationPanel.add(firstPageButton);
|
||||||
|
paginationPanel.add(previousPageButton);
|
||||||
|
paginationPanel.add(currentPagePanel);
|
||||||
|
paginationPanel.add(pageSizePanel);
|
||||||
|
paginationPanel.add(nextPageButton);
|
||||||
|
paginationPanel.add(lastPageButton);
|
||||||
|
|
||||||
|
this.add(paginationPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(this::loadPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPage() {
|
||||||
|
try {
|
||||||
|
this.tableModel.setPage(dataSource.runRecords().findAll(this.currentPage));
|
||||||
|
long pageCount = dataSource.runRecords().pageCount(this.currentPage.size());
|
||||||
|
this.firstPageButton.setEnabled(this.currentPage.page() != 0);
|
||||||
|
this.previousPageButton.setEnabled(this.currentPage.page() > 0);
|
||||||
|
this.nextPageButton.setEnabled(this.currentPage.page() < pageCount - 1);
|
||||||
|
this.lastPageButton.setEnabled(this.currentPage.page() != pageCount - 1);
|
||||||
|
this.currentPageField.setText(String.valueOf(this.currentPage.page() + 1));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
CREATE TABLE run (
|
CREATE TABLE IF NOT EXISTS run (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
date TEXT NOT NULL,
|
date TEXT NOT NULL,
|
||||||
start_time TEXT,
|
start_time TEXT,
|
||||||
|
|
Loading…
Reference in New Issue