]> git.sesse.net Git - pkanalytics/blobdiff - mainwindow.cpp
Start working on some team-wide stats.
[pkanalytics] / mainwindow.cpp
index b8322cbb856c20f0c5902251d45e8135afd242e2..254e62ddf6ccd66b844ccc186f024d953fd33453 100644 (file)
@@ -67,6 +67,37 @@ string get_video_filename(sqlite3 *db, int match_id)
        return filename;
 }
 
+bool get_match_property(sqlite3 *db, int match_id, const string &prop_name)
+{
+       sqlite3_stmt *stmt;
+
+       int ret = sqlite3_prepare_v2(db, ("SELECT " + prop_name + " FROM match WHERE match=?").c_str(), -1, &stmt, 0);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+
+       sqlite3_bind_int64(stmt, 1, match_id);
+
+       ret = sqlite3_step(stmt);
+       if (ret != SQLITE_ROW) {
+               fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+
+       if (sqlite3_column_type(stmt, 0) != SQLITE_INTEGER) {
+               return "";
+       }
+       bool value = sqlite3_column_int(stmt, 0);
+
+       ret = sqlite3_finalize(stmt);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+       return value;
+}
+
 void save_video_filename(sqlite3 *db, int match_id, const string &filename)
 {
        sqlite3_stmt *stmt;
@@ -82,7 +113,33 @@ void save_video_filename(sqlite3 *db, int match_id, const string &filename)
 
        ret = sqlite3_step(stmt);
        if (ret == SQLITE_ROW) {
-               fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
+               fprintf(stderr, "UPDATE 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();
+       }
+}
+
+void save_match_property(sqlite3 *db, int match_id, const string &prop_name, bool value)
+{
+       sqlite3_stmt *stmt;
+
+       int ret = sqlite3_prepare_v2(db, ("UPDATE match SET " + prop_name + "=? WHERE match=?").c_str(), -1, &stmt, 0);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+
+       sqlite3_bind_int64(stmt, 1, value);
+       sqlite3_bind_int64(stmt, 2, match_id);
+
+       ret = sqlite3_step(stmt);
+       if (ret == SQLITE_ROW) {
+               fprintf(stderr, "UPDATE step: %s\n", sqlite3_errmsg(db));
                abort();
        }
 
@@ -162,22 +219,22 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players,
        // It's not really clear whether PgUp should be forwards or backwards,
        // but mpv does at least up = forwards, so that's probably standard.
        QShortcut *pgdown = new QShortcut(QKeySequence(Qt::Key_PageDown), this);
-       connect(pgdown, &QShortcut::activated, [this]() { ui->video->seek(-120000); });
+       connect(pgdown, &QShortcut::activated, [this] { ui->video->seek(-120000); });
        QShortcut *pgup = new QShortcut(QKeySequence(Qt::Key_PageUp), this);
-       connect(pgup, &QShortcut::activated, [this]() { ui->video->seek(120000); });
+       connect(pgup, &QShortcut::activated, [this] { ui->video->seek(120000); });
 
-       connect(ui->minus10s, &QPushButton::clicked, [this]() { ui->video->seek(-10000); });
-       connect(ui->plus10s, &QPushButton::clicked, [this]() { ui->video->seek(10000); });
+       connect(ui->minus10s, &QPushButton::clicked, [this] { ui->video->seek(-10000); });
+       connect(ui->plus10s, &QPushButton::clicked, [this] { ui->video->seek(10000); });
 
-       connect(ui->minus2s, &QPushButton::clicked, [this]() { ui->video->seek(-2000); });
-       connect(ui->plus2s, &QPushButton::clicked, [this]() { ui->video->seek(2000); });
-       connect(ui->video, &VideoWidget::mouse_back_clicked, [this]() { ui->video->seek(-2000); });
-       connect(ui->video, &VideoWidget::mouse_forward_clicked, [this]() { ui->video->seek(2000); });
+       connect(ui->minus2s, &QPushButton::clicked, [this] { ui->video->seek(-2000); });
+       connect(ui->plus2s, &QPushButton::clicked, [this] { ui->video->seek(2000); });
+       connect(ui->video, &VideoWidget::mouse_back_clicked, [this] { ui->video->seek(-2000); });
+       connect(ui->video, &VideoWidget::mouse_forward_clicked, [this] { ui->video->seek(2000); });
 
-       connect(ui->minus1f, &QPushButton::clicked, [this]() { ui->video->seek_frames(-1); });
-       connect(ui->plus1f, &QPushButton::clicked, [this]() { ui->video->seek_frames(1); });
+       connect(ui->minus1f, &QPushButton::clicked, [this] { ui->video->seek_frames(-1); });
+       connect(ui->plus1f, &QPushButton::clicked, [this] { ui->video->seek_frames(1); });
 
-       connect(ui->play_pause, &QPushButton::clicked, [this]() {
+       connect(ui->play_pause, &QPushButton::clicked, [this] {
                if (playing) {
                        ui->video->pause();
                        ui->play_pause->setText("Play (space)");
@@ -191,18 +248,18 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players,
                ui->play_pause->setShortcut(QCoreApplication::translate("MainWindow", "Space", nullptr));
        });
 
-       connect(ui->player_1, &QPushButton::clicked, [this]() { insert_player_event(0); });
-       connect(ui->player_2, &QPushButton::clicked, [this]() { insert_player_event(1); });
-       connect(ui->player_3, &QPushButton::clicked, [this]() { insert_player_event(2); });
-       connect(ui->player_4, &QPushButton::clicked, [this]() { insert_player_event(3); });
-       connect(ui->player_5, &QPushButton::clicked, [this]() { insert_player_event(4); });
-       connect(ui->player_6, &QPushButton::clicked, [this]() { insert_player_event(5); });
-       connect(ui->player_7, &QPushButton::clicked, [this]() { insert_player_event(6); });
+       connect(ui->player_1, &QPushButton::clicked, [this] { insert_player_event(0); });
+       connect(ui->player_2, &QPushButton::clicked, [this] { insert_player_event(1); });
+       connect(ui->player_3, &QPushButton::clicked, [this] { insert_player_event(2); });
+       connect(ui->player_4, &QPushButton::clicked, [this] { insert_player_event(3); });
+       connect(ui->player_5, &QPushButton::clicked, [this] { insert_player_event(4); });
+       connect(ui->player_6, &QPushButton::clicked, [this] { insert_player_event(5); });
+       connect(ui->player_7, &QPushButton::clicked, [this] { insert_player_event(6); });
 
        // Offensive events
-       connect(ui->offense_label, &ClickableLabel::clicked, [this]() { insert_noplayer_event("set_offense"); });
-       connect(ui->catch_, &QPushButton::clicked, [this]() { set_current_event_type("catch"); });
-       connect(ui->throwaway, &QPushButton::clicked, [this, events]() {
+       connect(ui->offense_label, &ClickableLabel::clicked, [this] { insert_noplayer_event("set_offense"); });
+       connect(ui->catch_, &QPushButton::clicked, [this] { set_current_event_type("catch"); });
+       connect(ui->throwaway, &QPushButton::clicked, [this, events] {
                EventsModel::Status s = events->get_status_at(ui->video->get_position());
                if (s.attack_state == EventsModel::Status::DEFENSE && s.pull_state == EventsModel::Status::PULL_IN_AIR) {
                        insert_noplayer_event("pull_oob");
@@ -210,10 +267,10 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players,
                        set_current_event_type("throwaway");
                }
        });
-       connect(ui->drop, &QPushButton::clicked, [this]() { set_current_event_type("drop"); });
-       connect(ui->goal, &QPushButton::clicked, [this]() { set_current_event_type("goal"); });
-       connect(ui->stallout, &QPushButton::clicked, [this]() { set_current_event_type("stallout"); });
-       connect(ui->soft_plus, &QPushButton::clicked, [this, events]() {
+       connect(ui->drop, &QPushButton::clicked, [this] { set_current_event_type("drop"); });
+       connect(ui->goal, &QPushButton::clicked, [this] { set_current_event_type("goal"); });
+       connect(ui->stallout, &QPushButton::clicked, [this] { set_current_event_type("stallout"); });
+       connect(ui->soft_plus, &QPushButton::clicked, [this, events] {
                EventsModel::Status s = events->get_status_at(ui->video->get_position());
                if (s.attack_state == EventsModel::Status::OFFENSE) {
                        set_current_event_type("offensive_soft_plus");
@@ -221,7 +278,7 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players,
                        set_current_event_type("defensive_soft_plus");
                }
        });
-       connect(ui->soft_minus, &QPushButton::clicked, [this, events]() {
+       connect(ui->soft_minus, &QPushButton::clicked, [this, events] {
                EventsModel::Status s = events->get_status_at(ui->video->get_position());
                if (s.attack_state == EventsModel::Status::OFFENSE) {
                        set_current_event_type("offensive_soft_minus");
@@ -229,9 +286,10 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players,
                        set_current_event_type("defensive_soft_minus");
                }
        });
-       connect(ui->pull_or_was_d, &QPushButton::clicked, [this, events]() {
+       connect(ui->pull_or_was_d, &QPushButton::clicked, [this, events] {
                EventsModel::Status s = events->get_status_at(ui->video->get_position());
-               if (s.pull_state == EventsModel::Status::SHOULD_PULL) {
+               if (s.pull_state == EventsModel::Status::SHOULD_PULL ||
+                   events->get_status_at(ui->video->get_position() - 1).pull_state == EventsModel::Status::SHOULD_PULL) {
                        set_current_event_type("pull");
                } else if (s.pull_state == EventsModel::Status::PULL_IN_AIR) {
                        insert_noplayer_event("pull_landed");
@@ -240,25 +298,25 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players,
                }
        });
 
-       // 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"); });
-       connect(ui->their_pull, &QPushButton::clicked, [this, events]() {
+       // Defensive events.
+       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"); });
+       connect(ui->their_pull, &QPushButton::clicked, [this, events] {
                EventsModel::Status s = events->get_status_at(ui->video->get_position());
                if (s.pull_state == EventsModel::Status::SHOULD_PULL) {
                        insert_noplayer_event("their_pull");
                }
        });
-       connect(ui->our_defense, &QPushButton::clicked, [this]() { set_current_event_type("defense"); });
+       connect(ui->our_defense, &QPushButton::clicked, [this] { set_current_event_type("defense"); });
 
-       connect(ui->offensive_formation, &QPushButton::clicked, [this]() { insert_or_change_formation(/*offense=*/true); });
-       connect(ui->defensive_formation, &QPushButton::clicked, [this]() { insert_or_change_formation(/*offense=*/false); });
+       connect(ui->offensive_formation, &QPushButton::clicked, [this] { insert_or_change_formation(/*offense=*/true); });
+       connect(ui->defensive_formation, &QPushButton::clicked, [this] { insert_or_change_formation(/*offense=*/false); });
 
        // Misc. events
-       connect(ui->substitution, &QPushButton::clicked, [this]() { make_substitution(); });
-       connect(ui->stoppage, &QPushButton::clicked, [this, events]() {
+       connect(ui->substitution, &QPushButton::clicked, [this] { make_substitution(); });
+       connect(ui->stoppage, &QPushButton::clicked, [this, events] {
                EventsModel::Status s = events->get_status_at(ui->video->get_position());
                if (s.stoppage) {
                        insert_noplayer_event("restart");
@@ -266,11 +324,24 @@ MainWindow::MainWindow(EventsModel *events, PlayersModel *players,
                        insert_noplayer_event("stoppage");
                }
        });
-       connect(ui->unknown, &QPushButton::clicked, [this]() { insert_noplayer_event("unknown"); });
+       connect(ui->unknown, &QPushButton::clicked, [this] { insert_noplayer_event("unknown"); });
 
        QShortcut *key_delete = new QShortcut(QKeySequence(Qt::Key_Delete), this);
-       connect(key_delete, &QShortcut::activated, [this]() { ui->delete_->animateClick(); });
-       connect(ui->delete_, &QPushButton::clicked, [this]() { delete_current_event(); });
+       connect(key_delete, &QShortcut::activated, [this] { ui->delete_->animateClick(); });
+       connect(ui->delete_, &QPushButton::clicked, [this] { delete_current_event(); });
+
+       // Menus.
+       connect(ui->action_exit, &QAction::triggered, [this] { close(); });
+       connect(ui->action_export_json, &QAction::triggered, [db] { export_to_json(db, "ultimate.json"); });
+
+       ui->action_gender_rule_a->setChecked(get_match_property(db, match_id, "gender_rule_a"));
+       ui->action_gender_pull_rule->setChecked(get_match_property(db, match_id, "gender_pull_rule"));
+       connect(ui->action_gender_rule_a, &QAction::toggled, [this, db, match_id] {
+               save_match_property(db, match_id, "gender_rule_a", ui->action_gender_rule_a->isChecked());
+       });
+       connect(ui->action_gender_pull_rule, &QAction::toggled, [this, db, match_id] {
+               save_match_property(db, match_id, "gender_pull_rule", ui->action_gender_pull_rule->isChecked());
+       });
 }
 
 void MainWindow::position_changed(uint64_t pos)
@@ -351,7 +422,7 @@ void MainWindow::insert_or_change_formation(bool offense)
        QItemSelectionModel *select = ui->event_view->selectionModel();
        if (select->hasSelection()) {
                int row = select->selectedRows().front().row();  // Should only be one, due to our selection behavior.
-               string expected_type = offense ? "formation_offense" : "formation_defense";
+               EventType expected_type = offense ? EventType::FORMATION_OFFENSE : EventType::FORMATION_DEFENSE;
                if (events->get_event_type(row) == expected_type) {
                        events->set_event_formation(row, formation_id);
                        update_ui_from_time(ui->video->get_position());
@@ -417,7 +488,7 @@ void MainWindow::update_status(uint64_t t)
        snprintf(buf, sizeof(buf), "%d–%d | %s | %d passes, %d sec possession",
                s.our_score, s.their_score, formation.c_str(), s.num_passes, s.possession_sec);
        if (s.stoppage_sec > 0) {
-               char buf2[256];
+               char buf2[512];
                snprintf(buf2, sizeof(buf2), "%s (plus %d sec stoppage)", buf, s.stoppage_sec);
                ui->status->setText(buf2);
        } else {
@@ -521,7 +592,8 @@ void MainWindow::update_action_buttons(uint64_t t)
        ui->throwaway->setText("Throwaway (&t)");
        ui->throwaway->setShortcut(QCoreApplication::translate("MainWindow", "T", nullptr));
 
-       if (s.pull_state == EventsModel::Status::SHOULD_PULL) {
+       if (s.pull_state == EventsModel::Status::SHOULD_PULL ||
+           (has_selection_with_player && events->get_status_at(ui->video->get_position() - 1).pull_state == EventsModel::Status::SHOULD_PULL)) {  // Can change this event to pull.
                ui->pull_or_was_d->setEnabled(s.attack_state == EventsModel::Status::DEFENSE && has_selection_with_player);
                ui->their_pull->setEnabled(s.attack_state == EventsModel::Status::OFFENSE);