From bc56d92a0711e307873093b369a172f89c68a816 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 4 May 2023 00:04:06 +0200 Subject: [PATCH] Support having multiple matches (only set on startup for now). --- events.cpp | 16 +++++---- events.h | 3 +- main.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/events.cpp b/events.cpp index cb552c5..e7db8f4 100644 --- a/events.cpp +++ b/events.cpp @@ -10,7 +10,7 @@ using namespace std; string format_timestamp(uint64_t pos); -EventsModel::EventsModel(sqlite3 *db) : db(db) +EventsModel::EventsModel(sqlite3 *db, int match_id) : db(db), match_id(match_id) { load_data(); } @@ -91,11 +91,12 @@ void EventsModel::load_data() } // Read the events. - ret = sqlite3_prepare_v2(db, "SELECT event, t, player, type FROM event ORDER BY t", -1, &stmt, 0); + ret = sqlite3_prepare_v2(db, "SELECT event, t, player, type FROM event WHERE match=? ORDER BY t", -1, &stmt, 0); if (ret != SQLITE_OK) { fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db)); abort(); } + sqlite3_bind_int64(stmt, 1, match_id); for ( ;; ) { ret = sqlite3_step(stmt); if (ret == SQLITE_ROW) { @@ -138,19 +139,20 @@ unsigned EventsModel::insert_event(uint64_t t, optional player_id, const st // Insert the new row into the database. sqlite3_stmt *stmt; - int ret = sqlite3_prepare_v2(db, "INSERT INTO event (t, player, type) VALUES (?, ?, ?)", -1, &stmt, 0); + int ret = sqlite3_prepare_v2(db, "INSERT INTO event (match, t, player, type) VALUES (?, ?, ?, ?)", -1, &stmt, 0); if (ret != SQLITE_OK) { fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db)); abort(); } - sqlite3_bind_int64(stmt, 1, t); + sqlite3_bind_int64(stmt, 1, match_id); + sqlite3_bind_int64(stmt, 2, t); if (player_id) { - sqlite3_bind_int64(stmt, 2, *player_id); + sqlite3_bind_int64(stmt, 3, *player_id); } else { - sqlite3_bind_null(stmt, 2); + sqlite3_bind_null(stmt, 3); } - sqlite3_bind_text(stmt, 3, type.data(), type.size(), SQLITE_STATIC); + sqlite3_bind_text(stmt, 4, type.data(), type.size(), SQLITE_STATIC); ret = sqlite3_step(stmt); if (ret == SQLITE_ROW) { diff --git a/events.h b/events.h index 3dd6959..caf832a 100644 --- a/events.h +++ b/events.h @@ -12,7 +12,7 @@ class EventsModel : public QAbstractTableModel { public: - EventsModel(sqlite3 *db); + EventsModel(sqlite3 *db, int match_id); int rowCount(const QModelIndex &parent) const override { @@ -67,6 +67,7 @@ private: std::vector events; sqlite3 *db; + int match_id; void load_data(); }; diff --git a/main.cpp b/main.cpp index 858cb16..d9008dd 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -442,7 +443,11 @@ sqlite3 *open_db(const char *filename) )", nullptr, nullptr, nullptr); // Ignore errors. sqlite3_exec(db, R"( - CREATE TABLE IF NOT EXISTS event (event INTEGER PRIMARY KEY, t INTEGER, player INTEGER, type VARCHAR, FOREIGN KEY (player) REFERENCES player(player)); + CREATE TABLE IF NOT EXISTS match (match INTEGER PRIMARY KEY, description VARCHAR); + )", nullptr, nullptr, nullptr); // Ignore errors. + + sqlite3_exec(db, R"( + CREATE TABLE IF NOT EXISTS event (event INTEGER PRIMARY KEY, match INTEGER, t INTEGER, player INTEGER, type VARCHAR, FOREIGN KEY (player) REFERENCES player(player), FOREIGN KEY (match) REFERENCES match (match)); )", nullptr, nullptr, nullptr); // Ignore errors. sqlite3_exec(db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr); // Ignore errors. @@ -450,12 +455,102 @@ sqlite3 *open_db(const char *filename) return db; } +int get_match_id(sqlite3 *db, QWidget *parent) +{ + QStringList items; + vector ids; + + // Read the list of matches already in the database. + sqlite3_stmt *stmt; + int ret = sqlite3_prepare_v2(db, "SELECT match, description FROM match ORDER BY match", -1, &stmt, 0); + if (ret != SQLITE_OK) { + fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db)); + abort(); + } + for ( ;; ) { + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW) { + char buf[256]; + snprintf(buf, sizeof(buf), "%s (%d)", sqlite3_column_text(stmt, 1), sqlite3_column_int(stmt, 0)); + ids.push_back(sqlite3_column_int(stmt, 0)); + items.push_back(buf); + } else if (ret == SQLITE_DONE) { + break; + } else { + fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db)); + abort(); + } + } + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) { + fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db)); + abort(); + } + items.push_back("Add new…"); + + QString chosen_str; + { + QInputDialog dialog(parent, Qt::WindowFlags()); + dialog.setWindowTitle("Open game"); + dialog.setLabelText("Choose game to analyze:"); + dialog.setComboBoxItems(items); + dialog.setTextValue(items[items.size() - 2]); + dialog.setOption(QInputDialog::UseListViewForComboBoxItems, true); + if (!dialog.exec()) { + return -1; + } + chosen_str = dialog.textValue(); + } + + for (unsigned i = 0; i < ids.size(); ++i) { + if (chosen_str == items[i]) { + return ids[i]; + } + } + + // Must be a new game. Get its name and insert it into the database. + bool ok; + QString new_game_str = QInputDialog::getText(parent, "New game", "Choose name for new game:", QLineEdit::Normal, "", &ok); + if (!ok || new_game_str.isEmpty()) { + return -1; + } + + // Insert the new row into the database. + ret = sqlite3_prepare_v2(db, "INSERT INTO match (description) VALUES (?)", -1, &stmt, 0); + if (ret != SQLITE_OK) { + fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db)); + abort(); + } + + QByteArray new_game_utf8 = new_game_str.toUtf8(); + sqlite3_bind_text(stmt, 1, (const char *)new_game_utf8.data(), new_game_utf8.size(), SQLITE_STATIC); + + ret = sqlite3_step(stmt); + if (ret == SQLITE_ROW) { + fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db)); + abort(); + } + + ret = sqlite3_finalize(stmt); + if (ret != SQLITE_OK) { + fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db)); + abort(); + } + + return sqlite3_last_insert_rowid(db); +} + int main(int argc, char *argv[]) { QApplication app(argc, argv); sqlite3 *db = open_db("ultimate.db"); - MainWindow mainWindow(new EventsModel(db), new PlayersModel(db)); + int match_id = get_match_id(db, nullptr); + if (match_id <= 0) { // Cancel. + return 0; + } + + MainWindow mainWindow(new EventsModel(db, match_id), new PlayersModel(db)); mainWindow.resize(QSize(1280, 720)); mainWindow.show(); -- 2.39.2