+ return { queue_status, "text/plain" };
+}
+
+void MainWindow::display_frame(unsigned stream_idx, const FrameOnDisk &frame)
+{
+ if (stream_idx >= MAX_STREAMS) {
+ fprintf(stderr, "WARNING: Ignoring too-high stream index %u.\n", stream_idx);
+ return;
+ }
+ if (stream_idx >= num_cameras) {
+ post_to_main_thread_and_wait([this, stream_idx] {
+ num_cameras = stream_idx + 1;
+ change_num_cameras();
+ });
+ }
+ displays[stream_idx].display->setFrame(stream_idx, frame);
+}
+
+void MainWindow::preview()
+{
+ post_to_main_thread([this] {
+ preview_clicked();
+ });
+}
+
+void MainWindow::queue()
+{
+ post_to_main_thread([this] {
+ queue_clicked();
+ });
+}
+
+void MainWindow::play()
+{
+ post_to_main_thread([this] {
+ play_clicked();
+ });
+}
+
+void MainWindow::toggle_lock()
+{
+ post_to_main_thread([this] {
+ ui->speed_lock_btn->setChecked(!ui->speed_lock_btn->isChecked());
+ speed_lock_clicked();
+ });
+}
+
+void MainWindow::jog(int delta)
+{
+ post_to_main_thread([this, delta] {
+ int64_t pts_delta = delta * (TIMEBASE / 60); // One click = frame at 60 fps.
+ if (ui->playlist->hasFocus()) {
+ QModelIndex selected = ui->playlist->selectionModel()->currentIndex();
+ if (selected.column() != -1 && selected.row() != -1) {
+ jog_internal(JOG_PLAYLIST, selected.row(), selected.column(), /*stream_idx=*/-1, pts_delta);
+ }
+ } else if (ui->clip_list->hasFocus()) {
+ QModelIndex selected = ui->clip_list->selectionModel()->currentIndex();
+ if (cliplist_clips->is_camera_column(selected.column()) &&
+ hidden_jog_column != -1) {
+ // See the definition on hidden_jog_column.
+ selected = selected.sibling(selected.row(), hidden_jog_column);
+ ui->clip_list->selectionModel()->setCurrentIndex(selected, QItemSelectionModel::ClearAndSelect);
+ hidden_jog_column = -1;
+ }
+ if (selected.column() != -1 && selected.row() != -1) {
+ jog_internal(JOG_CLIP_LIST, selected.row(), selected.column(), ui->preview_display->get_stream_idx(), pts_delta);
+ }
+ }
+ });
+}
+
+void MainWindow::switch_camera(unsigned camera_idx)
+{
+ post_to_main_thread([this, camera_idx] {
+ if (camera_idx < num_cameras) { // TODO: Also make this change a highlighted clip?
+ preview_angle_clicked(camera_idx);
+ }
+ });
+}
+
+void MainWindow::set_master_speed(float speed)
+{
+ speed = min(max(speed, 0.1f), 2.0f);
+
+ post_to_main_thread([this, speed] {
+ if (ui->speed_lock_btn->isChecked()) {
+ midi_mapper.set_locked(MIDIMapper::Blinking);
+ lock_blink_timeout->start(1000);
+ return;
+ }
+
+ int percent = lrintf(speed * 100.0f);
+ ui->speed_slider->blockSignals(true);
+ ui->speed_slider->setValue(percent);
+ ui->speed_slider->blockSignals(false);
+ ui->speed_lock_btn->setText(QString::fromStdString(" " + to_string(percent) + "%"));
+
+ live_player->set_master_speed(speed);
+ midi_mapper.set_speed_light(speed);
+ });
+}
+
+void MainWindow::cue_in()
+{
+ post_to_main_thread([this] { cue_in_clicked(); });
+}
+
+void MainWindow::cue_out()
+{
+ post_to_main_thread([this] { cue_out_clicked(); });
+}
+
+template<class Model>
+void MainWindow::replace_model(QTableView *view, Model **model, Model *new_model)
+{
+ QItemSelectionModel *old_selection_model = view->selectionModel();
+ view->setModel(new_model);
+ delete *model;
+ delete old_selection_model;
+ *model = new_model;
+ connect(new_model, &Model::any_content_changed, this, &MainWindow::content_changed);
+}
+
+void MainWindow::start_tally()
+{
+ http_reply = http.get(QNetworkRequest(QString::fromStdString(global_flags.tally_url)));
+ connect(http_reply, &QNetworkReply::finished, this, &MainWindow::tally_received);
+}
+
+void MainWindow::tally_received()
+{
+ unsigned time_to_next_tally_ms;
+ if (http_reply->error()) {
+ fprintf(stderr, "HTTP get of '%s' failed: %s\n", global_flags.tally_url.c_str(),
+ http_reply->errorString().toStdString().c_str());
+ ui->live_frame->setStyleSheet("");
+ time_to_next_tally_ms = 1000;
+ } else {
+ string contents = http_reply->readAll().toStdString();
+ ui->live_frame->setStyleSheet(QString::fromStdString("background: " + contents));
+ time_to_next_tally_ms = 100;
+ }
+ http_reply->deleteLater();
+ http_reply = nullptr;
+
+ QTimer::singleShot(time_to_next_tally_ms, this, &MainWindow::start_tally);