]> git.sesse.net Git - nageru/blobdiff - futatabi/mainwindow.cpp
Be more lenient about the playlist focus; this was too much (we should at least be...
[nageru] / futatabi / mainwindow.cpp
index f465468b1dbafe68ee2217a786e8db35add06511..9b6b47845a46b62b2549eeb2dddd71c127f6ecd0 100644 (file)
@@ -65,8 +65,11 @@ MainWindow::MainWindow()
                        global_flags.interpolation_quality = settings.interpolation_quality() - 1;
                }
        }
-       if (!global_flags.cue_point_padding_set) {
-               global_flags.cue_point_padding_seconds = settings.cue_point_padding_seconds();  // Default 0 is fine.
+       if (!global_flags.cue_in_point_padding_set) {
+               global_flags.cue_in_point_padding_seconds = settings.cue_in_point_padding_seconds();  // Default 0 is fine.
+       }
+       if (!global_flags.cue_out_point_padding_set) {
+               global_flags.cue_out_point_padding_seconds = settings.cue_out_point_padding_seconds();  // Default 0 is fine.
        }
        if (global_flags.interpolation_quality == 0) {
                // Allocate something just for simplicity; we won't be using it
@@ -115,27 +118,49 @@ MainWindow::MainWindow()
        connect(ui->quality_3_action, &QAction::toggled, bind(&MainWindow::quality_toggled, this, 3, _1));
        connect(ui->quality_4_action, &QAction::toggled, bind(&MainWindow::quality_toggled, this, 4, _1));
 
-       // The cue point padding group.
-       QActionGroup *padding_group = new QActionGroup(ui->interpolation_menu);
-       padding_group->addAction(ui->padding_0_action);
-       padding_group->addAction(ui->padding_1_action);
-       padding_group->addAction(ui->padding_2_action);
-       padding_group->addAction(ui->padding_5_action);
-       if (global_flags.cue_point_padding_seconds <= 1e-3) {
-               ui->padding_0_action->setChecked(true);
-       } else if (fabs(global_flags.cue_point_padding_seconds - 1.0) < 1e-3) {
-               ui->padding_1_action->setChecked(true);
-       } else if (fabs(global_flags.cue_point_padding_seconds - 2.0) < 1e-3) {
-               ui->padding_2_action->setChecked(true);
-       } else if (fabs(global_flags.cue_point_padding_seconds - 5.0) < 1e-3) {
-               ui->padding_5_action->setChecked(true);
+       // The cue-in point padding group.
+       QActionGroup *in_padding_group = new QActionGroup(ui->in_padding_menu);
+       in_padding_group->addAction(ui->in_padding_0_action);
+       in_padding_group->addAction(ui->in_padding_1_action);
+       in_padding_group->addAction(ui->in_padding_2_action);
+       in_padding_group->addAction(ui->in_padding_5_action);
+       if (global_flags.cue_in_point_padding_seconds <= 1e-3) {
+               ui->in_padding_0_action->setChecked(true);
+       } else if (fabs(global_flags.cue_in_point_padding_seconds - 1.0) < 1e-3) {
+               ui->in_padding_1_action->setChecked(true);
+       } else if (fabs(global_flags.cue_in_point_padding_seconds - 2.0) < 1e-3) {
+               ui->in_padding_2_action->setChecked(true);
+       } else if (fabs(global_flags.cue_in_point_padding_seconds - 5.0) < 1e-3) {
+               ui->in_padding_5_action->setChecked(true);
        } else {
                // Nothing to check, which is fine.
        }
-       connect(ui->padding_0_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 0.0, _1));
-       connect(ui->padding_1_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 1.0, _1));
-       connect(ui->padding_2_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 2.0, _1));
-       connect(ui->padding_5_action, &QAction::toggled, bind(&MainWindow::padding_toggled, this, 5.0, _1));
+       connect(ui->in_padding_0_action, &QAction::toggled, bind(&MainWindow::in_padding_toggled, this, 0.0, _1));
+       connect(ui->in_padding_1_action, &QAction::toggled, bind(&MainWindow::in_padding_toggled, this, 1.0, _1));
+       connect(ui->in_padding_2_action, &QAction::toggled, bind(&MainWindow::in_padding_toggled, this, 2.0, _1));
+       connect(ui->in_padding_5_action, &QAction::toggled, bind(&MainWindow::in_padding_toggled, this, 5.0, _1));
+
+       // Same for the cue-out padding.
+       QActionGroup *out_padding_group = new QActionGroup(ui->out_padding_menu);
+       out_padding_group->addAction(ui->out_padding_0_action);
+       out_padding_group->addAction(ui->out_padding_1_action);
+       out_padding_group->addAction(ui->out_padding_2_action);
+       out_padding_group->addAction(ui->out_padding_5_action);
+       if (global_flags.cue_out_point_padding_seconds <= 1e-3) {
+               ui->out_padding_0_action->setChecked(true);
+       } else if (fabs(global_flags.cue_out_point_padding_seconds - 1.0) < 1e-3) {
+               ui->out_padding_1_action->setChecked(true);
+       } else if (fabs(global_flags.cue_out_point_padding_seconds - 2.0) < 1e-3) {
+               ui->out_padding_2_action->setChecked(true);
+       } else if (fabs(global_flags.cue_out_point_padding_seconds - 5.0) < 1e-3) {
+               ui->out_padding_5_action->setChecked(true);
+       } else {
+               // Nothing to check, which is fine.
+       }
+       connect(ui->out_padding_0_action, &QAction::toggled, bind(&MainWindow::out_padding_toggled, this, 0.0, _1));
+       connect(ui->out_padding_1_action, &QAction::toggled, bind(&MainWindow::out_padding_toggled, this, 1.0, _1));
+       connect(ui->out_padding_2_action, &QAction::toggled, bind(&MainWindow::out_padding_toggled, this, 2.0, _1));
+       connect(ui->out_padding_5_action, &QAction::toggled, bind(&MainWindow::out_padding_toggled, this, 5.0, _1));
 
        global_disk_space_estimator = new DiskSpaceEstimator(bind(&MainWindow::report_disk_space, this, _1, _2));
        disk_free_label = new QLabel(this);
@@ -180,6 +205,10 @@ MainWindow::MainWindow()
        connect(play, &QShortcut::activated, ui->play_btn, &QPushButton::click);
        connect(ui->play_btn, &QPushButton::clicked, this, &MainWindow::play_clicked);
 
+       QShortcut *next = new QShortcut(QKeySequence(Qt::Key_N), this);
+       connect(next, &QShortcut::activated, ui->next_btn, &QPushButton::click);
+       connect(ui->next_btn, &QPushButton::clicked, this, &MainWindow::next_clicked);
+
        connect(ui->stop_btn, &QPushButton::clicked, this, &MainWindow::stop_clicked);
        ui->stop_btn->setEnabled(false);
 
@@ -272,9 +301,17 @@ void MainWindow::change_num_cameras()
 {
        assert(num_cameras >= displays.size());  // We only add, never remove.
 
+       // Make new entries to hide the displays.
+       for (unsigned i = displays.size(); i < num_cameras; ++i) {
+               char title[256];
+               snprintf(title, sizeof(title), "Camera %u", i + 1);
+               QAction *hide_action = ui->hide_camera_menu->addAction(title);
+               hide_action->setCheckable(true);
+               hide_action->setChecked(false);
+               connect(hide_action, &QAction::toggled, bind(&MainWindow::hide_camera_toggled, this, i, _1));
+       }
+
        // Make new display rows.
-       unsigned display_rows = (num_cameras + 1) / 2;
-       ui->video_displays->setStretch(1, display_rows);
        for (unsigned i = displays.size(); i < num_cameras; ++i) {
                QFrame *frame = new QFrame(this);
                frame->setAutoFillBackground(true);
@@ -287,7 +324,6 @@ void MainWindow::change_num_cameras()
                display->setAutoFillBackground(true);
                layout->addWidget(display);
 
-               ui->input_displays->addWidget(frame, i / 2, i % 2);
                display->set_overlay(to_string(i + 1));
 
                QPushButton *preview_btn = new QPushButton(this);
@@ -295,7 +331,7 @@ void MainWindow::change_num_cameras()
                preview_btn->setText(QString::fromStdString(to_string(i + 1)));
                ui->preview_layout->addWidget(preview_btn);
 
-               displays.emplace_back(FrameAndDisplay{ frame, display, preview_btn });
+               displays.emplace_back(FrameAndDisplay{ frame, display, preview_btn, /*hidden=*/false });
 
                connect(display, &JPEGFrameView::clicked, preview_btn, &QPushButton::click);
                QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_1 + i), this);
@@ -303,6 +339,7 @@ void MainWindow::change_num_cameras()
 
                connect(preview_btn, &QPushButton::clicked, [this, i] { preview_angle_clicked(i); });
        }
+       relayout_displays();
 
        cliplist_clips->change_num_cameras(num_cameras);
        playlist_clips->change_num_cameras(num_cameras);
@@ -310,9 +347,34 @@ void MainWindow::change_num_cameras()
        QMetaObject::invokeMethod(this, "relayout", Qt::QueuedConnection);
 }
 
+void MainWindow::relayout_displays()
+{
+       while (ui->input_displays->count() > 0) {
+               QLayoutItem *item = ui->input_displays->takeAt(0);
+               ui->input_displays->removeWidget(item->widget());
+       }
+
+       unsigned cell_idx = 0;
+       for (unsigned i = 0; i < displays.size(); ++i) {
+               if (displays[i].hidden) {
+                       displays[i].frame->setVisible(false);
+               } else {
+                       displays[i].frame->setVisible(true);
+                       ui->input_displays->addWidget(displays[i].frame, cell_idx / 2, cell_idx % 2);
+                       ++cell_idx;
+               }
+       }
+       ui->video_displays->setStretch(1, (cell_idx + 1) / 2);
+
+       QMetaObject::invokeMethod(this, "relayout", Qt::QueuedConnection);
+}
+
 MainWindow::~MainWindow()
 {
-       // Empty so that we can forward-declare Player in the .h file.
+       // We don't have a context to release Player's OpenGL resources in here,
+       // so instead of crashing on exit, leak it.
+       live_player.release();
+       preview_player.release();
 }
 
 void MainWindow::cue_in_clicked()
@@ -321,7 +383,7 @@ void MainWindow::cue_in_clicked()
                cliplist_clips->mutable_back()->pts_in = current_pts;
        } else {
                Clip clip;
-               clip.pts_in = max<int64_t>(current_pts - lrint(global_flags.cue_point_padding_seconds * TIMEBASE), 0);
+               clip.pts_in = max<int64_t>(current_pts - lrint(global_flags.cue_in_point_padding_seconds * TIMEBASE), 0);
                cliplist_clips->add_clip(clip);
                playlist_selection_changed();
        }
@@ -340,7 +402,7 @@ void MainWindow::cue_out_clicked()
                return;
        }
 
-       cliplist_clips->mutable_back()->pts_out = current_pts + lrint(global_flags.cue_point_padding_seconds * TIMEBASE);
+       cliplist_clips->mutable_back()->pts_out = current_pts + lrint(global_flags.cue_out_point_padding_seconds * TIMEBASE);
 
        // Select the item so that we can jog it.
        ui->clip_list->setFocus();
@@ -606,7 +668,8 @@ void MainWindow::save_settings()
 {
        SettingsProto settings;
        settings.set_interpolation_quality(global_flags.interpolation_quality + 1);
-       settings.set_cue_point_padding_seconds(global_flags.cue_point_padding_seconds);
+       settings.set_cue_in_point_padding_seconds(global_flags.cue_in_point_padding_seconds);
+       settings.set_cue_out_point_padding_seconds(global_flags.cue_out_point_padding_seconds);
        db.store_settings(settings);
 }
 
@@ -634,6 +697,11 @@ void MainWindow::play_clicked()
        playlist_selection_changed();
 }
 
+void MainWindow::next_clicked()
+{
+       live_player->skip_to_next();
+}
+
 void MainWindow::stop_clicked()
 {
        Clip fake_clip;
@@ -704,6 +772,7 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event)
 
        if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) {
                enable_or_disable_preview_button();
+               playlist_selection_changed();
                hidden_jog_column = -1;
        }
 
@@ -912,6 +981,19 @@ void MainWindow::playlist_selection_changed()
                any_selected && selected->selectedRows().back().row() < int(playlist_clips->size()) - 1);
 
        ui->play_btn->setEnabled(any_selected);
+       ui->next_btn->setEnabled(ui->stop_btn->isEnabled());  // TODO: Perhaps not if we're on the last clip?
+       midi_mapper.set_next_ready(ui->next_btn->isEnabled() ? MIDIMapper::On : MIDIMapper::Off);
+
+       // NOTE: The hidden button is still reachable by keyboard or MIDI.
+       if (any_selected) {
+               ui->play_btn->setVisible(true);
+       } else if (ui->stop_btn->isEnabled()) {  // Playing.
+               ui->play_btn->setVisible(false);
+       } else {
+               ui->play_btn->setVisible(true);
+       }
+       ui->next_btn->setVisible(!ui->play_btn->isVisible());
+
        if (ui->stop_btn->isEnabled()) {  // Playing.
                midi_mapper.set_play_enabled(MIDIMapper::On);
        } else if (any_selected) {
@@ -1127,15 +1209,30 @@ void MainWindow::quality_toggled(int quality, bool checked)
        save_settings();
 }
 
-void MainWindow::padding_toggled(double seconds, bool checked)
+void MainWindow::in_padding_toggled(double seconds, bool checked)
+{
+       if (!checked) {
+               return;
+       }
+       global_flags.cue_in_point_padding_seconds = seconds;
+       save_settings();
+}
+
+void MainWindow::out_padding_toggled(double seconds, bool checked)
 {
        if (!checked) {
                return;
        }
-       global_flags.cue_point_padding_seconds = seconds;
+       global_flags.cue_out_point_padding_seconds = seconds;
        save_settings();
 }
 
+void MainWindow::hide_camera_toggled(unsigned camera_idx, bool checked)
+{
+       displays[camera_idx].hidden = checked;
+       relayout_displays();
+}
+
 void MainWindow::highlight_camera_input(int stream_idx)
 {
        for (unsigned i = 0; i < num_cameras; ++i) {
@@ -1250,6 +1347,13 @@ void MainWindow::play()
        });
 }
 
+void MainWindow::next()
+{
+       post_to_main_thread([this] {
+               next_clicked();
+       });
+}
+
 void MainWindow::toggle_lock()
 {
        post_to_main_thread([this] {