#include <QGridLayout>
#include <QVideoWidget>
#include <QShortcut>
+#include <QInputDialog>
#include <algorithm>
#include <string>
#include <map>
#include "ui_mainwindow.h"
#include "events.h"
#include "players.h"
+#include "json.h"
using namespace std;
QShortcut *pgup = new QShortcut(QKeySequence(Qt::Key_PageUp), this);
connect(pgup, &QShortcut::activated, [this]() { seek(120000); });
+ // Ugh. Used when Qt messes up and hangs the video.
+ QShortcut *f5 = new QShortcut(QKeySequence(Qt::Key_F5), this);
+ connect(f5, &QShortcut::activated, [this]() {
+ QVideoWidget *nvw = new QVideoWidget(ui->video->parentWidget());
+ nvw->setObjectName("video");
+ nvw->setMinimumSize(QSize(320, 240));
+ video->setVideoOutput(nvw);
+ ui->main_grid->replaceWidget(ui->video, nvw);
+ ui->video = nvw;
+ });
+
connect(ui->minus10s, &QPushButton::clicked, [this]() { seek(-10000); });
connect(ui->plus10s, &QPushButton::clicked, [this]() { seek(10000); });
});
// Defensive events (TODO add more)
+ connect(ui->interception, &QPushButton::clicked, [this]() { set_current_event_type("interception"); });
connect(ui->defense_label, &ClickableLabel::clicked, [this]() { insert_noplayer_event("set_defense"); });
connect(ui->their_throwaway, &QPushButton::clicked, [this]() { insert_noplayer_event("their_throwaway"); });
connect(ui->their_goal, &QPushButton::clicked, [this]() { insert_noplayer_event("their_goal"); });
)", 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.
sqlite3_exec(db, "PRAGMA synchronous=NORMAL", nullptr, nullptr, nullptr); // Ignore errors.
+ sqlite3_exec(db, "PRAGMA foreign_keys=ON", nullptr, nullptr, nullptr); // Ignore errors.
return db;
}
+int get_match_id(sqlite3 *db, QWidget *parent, int requested_match)
+{
+ QStringList items;
+ vector<int> ids;
+ bool requested_match_ok = false;
+
+ // 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));
+ if (ids.back() == requested_match) {
+ requested_match_ok = true;
+ }
+ 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…");
+
+ if (requested_match_ok) {
+ return requested_match;
+ }
+
+ 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));
+ // TODO: do this on-demand instead, from a menu
+ export_to_json(db, "ultimate.json");
+
+ int requested_match = -1;
+ if (argc >= 2) {
+ requested_match = atoi(argv[1]);
+ }
+
+ int match_id = get_match_id(db, nullptr, requested_match);
+ if (match_id <= 0) { // Cancel.
+ return 0;
+ }
+
+ MainWindow mainWindow(new EventsModel(db, match_id), new PlayersModel(db));
mainWindow.resize(QSize(1280, 720));
mainWindow.show();