#include "mainwindow.h"
+#include "shared/aboutdialog.h"
#include "clip_list.h"
-#include "disk_space_estimator.h"
+#include "shared/disk_space_estimator.h"
#include "flags.h"
#include "frame_on_disk.h"
#include "player.h"
#include "shared/post_to_main_thread.h"
-#include "timebase.h"
+#include "shared/timebase.h"
#include "ui_mainwindow.h"
+#include <QDesktopServices>
+#include <QMessageBox>
#include <QMouseEvent>
#include <QShortcut>
#include <QTimer>
// The menus.
connect(ui->exit_action, &QAction::triggered, this, &MainWindow::exit_triggered);
+ connect(ui->manual_action, &QAction::triggered, this, &MainWindow::manual_triggered);
+ connect(ui->about_action, &QAction::triggered, this, &MainWindow::about_triggered);
global_disk_space_estimator = new DiskSpaceEstimator(bind(&MainWindow::report_disk_space, this, _1, _2));
disk_free_label = new QLabel(this);
this, &MainWindow::playlist_selection_changed);
playlist_selection_changed(); // First time set-up.
- preview_player = new Player(ui->preview_display, /*also_output_to_stream=*/false);
- live_player = new Player(ui->live_display, /*also_output_to_stream=*/true);
+ preview_player.reset(new Player(ui->preview_display, /*also_output_to_stream=*/false));
+ live_player.reset(new Player(ui->live_display, /*also_output_to_stream=*/true));
live_player->set_done_callback([this]{
post_to_main_thread([this]{
live_player_clip_done();
this, &MainWindow::clip_list_selection_changed);
}
+MainWindow::~MainWindow()
+{
+ // Empty so that we can forward-declare Player in the .h file.
+}
+
void MainWindow::cue_in_clicked()
{
if (!cliplist_clips->empty() && cliplist_clips->back()->pts_out < 0) {
clip.pts_in = current_pts;
cliplist_clips->add_clip(clip);
playlist_selection_changed();
+ ui->clip_list->scrollToBottom();
}
void MainWindow::cue_out_clicked()
if (clip.pts_out != -1) {
playlist_clips->add_clip(clip);
playlist_selection_changed();
+ ui->playlist->scrollToBottom();
}
return;
}
if (clip.pts_out != -1) {
playlist_clips->add_clip(clip);
playlist_selection_changed();
+ ui->playlist->scrollToBottom();
+ if (!ui->playlist->selectionModel()->hasSelection()) {
+ // TODO: Figure out why this doesn't always seem to actually select the row.
+ QModelIndex bottom = playlist_clips->index(playlist_clips->size() - 1, 0);
+ ui->playlist->setCurrentIndex(bottom);
+ }
}
}
{
// playlist_clips can only be accessed on the main thread.
// Hopefully, we won't have to wait too long for this to come back.
+ //
+ // TODO: If MainWindow is in the process of being destroyed and waiting
+ // for Player to shut down, we could have a deadlock here.
promise<pair<Clip, size_t>> clip_promise;
future<pair<Clip, size_t>> clip = clip_promise.get_future();
post_to_main_thread([this, &clip_promise] {
lock_guard<mutex> lock(frame_mu);
if (frames[stream_idx].empty())
return;
- auto it = lower_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts,
- [](const FrameOnDisk &frame, int64_t pts) { return frame.pts < pts; });
+ auto it = find_last_frame_before(frames[stream_idx], pts);
if (it != frames[stream_idx].end()) {
pts = it->pts;
}
lock_guard<mutex> lock(frame_mu);
if (frames[stream_idx].empty())
return;
- auto it = upper_bound(frames[stream_idx].begin(), frames[stream_idx].end(), pts - 1,
- [](int64_t pts, const FrameOnDisk &frame) { return pts < frame.pts; });
+ auto it = find_first_frame_at_or_after(frames[stream_idx], pts);
if (it != frames[stream_idx].end()) {
pts = it->pts;
}
close();
}
+void MainWindow::manual_triggered()
+{
+ if (!QDesktopServices::openUrl(QUrl("https://nageru.sesse.net/doc/"))) {
+ QMessageBox msgbox;
+ msgbox.setText("Could not launch manual in web browser.\nPlease see https://nageru.sesse.net/doc/ manually.");
+ msgbox.exec();
+ }
+}
+
+void MainWindow::about_triggered()
+{
+ AboutDialog("Futatabi", "Multicamera slow motion video server").exec();
+}
+
void MainWindow::highlight_camera_input(int stream_idx)
{
if (stream_idx == 0) {