summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-02-20 23:10:50 +0100
committerCarl Hetherington <cth@carlh.net>2025-02-23 01:47:38 +0100
commit81d8c747c7a04be10821223dea69faf058b05b1d (patch)
tree98f58e45012c3133e719024fe8c84d7b9c691811 /src
parent6628b2573f601b7448da169453d5f113534e525e (diff)
Allow verification of multiple DCPs in one go (#2843).
Diffstat (limited to 'src')
-rw-r--r--src/lib/job_manager.cc10
-rw-r--r--src/lib/job_manager.h6
-rw-r--r--src/tools/dcpomatic_verifier.cc111
-rw-r--r--src/wx/verify_dcp_progress_dialog.cc4
-rw-r--r--src/wx/verify_dcp_progress_dialog.h4
-rw-r--r--src/wx/verify_dcp_progress_panel.cc23
-rw-r--r--src/wx/verify_dcp_progress_panel.h6
-rw-r--r--src/wx/verify_dcp_result_dialog.cc2
-rw-r--r--src/wx/verify_dcp_result_panel.cc180
-rw-r--r--src/wx/verify_dcp_result_panel.h11
10 files changed, 247 insertions, 110 deletions
diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc
index 86230db2b..3fac8e6fa 100644
--- a/src/lib/job_manager.cc
+++ b/src/lib/job_manager.cc
@@ -177,8 +177,9 @@ JobManager::scheduler ()
} else {
i->resume ();
}
- emit (boost::bind (boost::ref (ActiveJobsChanged), _last_active_job, i->json_name()));
- _last_active_job = i->json_name ();
+ auto last = _last_active_job.lock();
+ emit(boost::bind(boost::ref(ActiveJobsChanged), last ? last->json_name() : std::string{}, i->json_name()));
+ _last_active_job = i;
have_running = true;
} else if (!have_running && i->running()) {
have_running = true;
@@ -195,8 +196,9 @@ JobManager::job_finished ()
{
{
boost::mutex::scoped_lock lm (_mutex);
- emit (boost::bind(boost::ref (ActiveJobsChanged), _last_active_job, optional<string>()));
- _last_active_job = optional<string>();
+ auto job = _last_active_job.lock();
+ emit(boost::bind(boost::ref(ActiveJobsChanged), job ? job->json_name() : string{}, optional<string>()));
+ _last_active_job = {};
}
_schedule_condition.notify_all();
diff --git a/src/lib/job_manager.h b/src/lib/job_manager.h
index c8450bfda..b4cb49060 100644
--- a/src/lib/job_manager.h
+++ b/src/lib/job_manager.h
@@ -65,6 +65,10 @@ public:
return _paused;
}
+ std::weak_ptr<Job> last_active_job() const {
+ return _last_active_job;
+ }
+
void analyse_audio (
std::shared_ptr<const Film> film,
std::shared_ptr<const Playlist> playlist,
@@ -105,7 +109,7 @@ private:
std::list<boost::signals2::connection> _connections;
bool _terminate = false;
- boost::optional<std::string> _last_active_job;
+ std::weak_ptr<Job> _last_active_job;
boost::thread _scheduler;
/** true if all jobs should be paused */
diff --git a/src/tools/dcpomatic_verifier.cc b/src/tools/dcpomatic_verifier.cc
index 44285ca49..925168106 100644
--- a/src/tools/dcpomatic_verifier.cc
+++ b/src/tools/dcpomatic_verifier.cc
@@ -27,6 +27,7 @@
#include "wx/check_box.h"
#include "wx/dcpomatic_button.h"
#include "wx/dir_picker_ctrl.h"
+#include "wx/editable_list.h"
#include "wx/verify_dcp_progress_panel.h"
#include "wx/verify_dcp_result_panel.h"
#include "wx/wx_util.h"
@@ -46,8 +47,51 @@ LIBDCP_ENABLE_WARNINGS
#endif
+using std::dynamic_pointer_cast;
using std::exception;
using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+#if BOOST_VERSION >= 106100
+using namespace boost::placeholders;
+#endif
+
+
+class DirDialogWrapper : public wxDirDialog
+{
+public:
+ DirDialogWrapper(wxWindow* parent)
+ : wxDirDialog(parent, _("Choose a folder"), {}, wxDD_DIR_MUST_EXIST)
+ {
+
+ }
+
+ vector<boost::filesystem::path> get() const
+ {
+ vector<boost::filesystem::path> dcp;
+ search(dcp, boost::filesystem::path(wx_to_std(GetPath())));
+ return dcp;
+ }
+
+ void set(boost::filesystem::path)
+ {
+ /* Not used */
+ }
+
+private:
+ void search(vector<boost::filesystem::path>& dcp, boost::filesystem::path path) const
+ {
+ if (dcp::filesystem::exists(path / "ASSETMAP") || dcp::filesystem::exists(path / "ASSETMAP.xml")) {
+ dcp.push_back(path);
+ } else if (boost::filesystem::is_directory(path)) {
+ for (auto i: boost::filesystem::directory_iterator(path)) {
+ search(dcp, i.path());
+ }
+ }
+ };
+};
+
+
class DOMFrame : public wxFrame
@@ -62,13 +106,23 @@ public:
auto overall_sizer = new wxBoxSizer(wxVERTICAL);
auto dcp_sizer = new wxBoxSizer(wxHORIZONTAL);
- add_label_to_sizer(dcp_sizer, this, _("DCP"), true, 0, wxALIGN_CENTER_VERTICAL);
- _dcp = new DirPickerCtrl(this, true);
- dcp_sizer->Add(_dcp, 1, wxEXPAND);
+ add_label_to_sizer(dcp_sizer, this, _("DCPs"), true, 0, wxALIGN_CENTER_VERTICAL);
+
+ auto dcps = new EditableList<boost::filesystem::path, DirDialogWrapper>(
+ this,
+ { EditableListColumn(_("DCP"), 300, true) },
+ boost::bind(&DOMFrame::dcp_paths, this),
+ boost::bind(&DOMFrame::set_dcp_paths, this, _1),
+ [](boost::filesystem::path p, int) { return p.filename().string(); },
+ EditableListTitle::INVISIBLE,
+ EditableListButton::NEW | EditableListButton::REMOVE
+ );
+
+ dcp_sizer->Add(dcps, 1, wxLEFT | wxEXPAND, DCPOMATIC_SIZER_GAP);
overall_sizer->Add(dcp_sizer, 0, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
auto options_sizer = new wxBoxSizer(wxVERTICAL);
- _write_log = new CheckBox(this, _("Write log to DCP folder"));
+ _write_log = new CheckBox(this, _("Write logs to DCP folders"));
options_sizer->Add(_write_log, 0, wxBOTTOM, DCPOMATIC_SIZER_GAP);
overall_sizer->Add(options_sizer, 0, wxLEFT, DCPOMATIC_DIALOG_BORDER);
@@ -83,7 +137,6 @@ public:
SetSizerAndFit(overall_sizer);
- _dcp->Changed.connect(boost::bind(&DOMFrame::setup_sensitivity, this));
_verify->bind(&DOMFrame::verify_clicked, this);
setup_sensitivity();
@@ -92,35 +145,55 @@ public:
private:
void setup_sensitivity()
{
- _verify->Enable(!_dcp->GetPath().IsEmpty());
+ _verify->Enable(!_dcp_paths.empty());
}
void verify_clicked()
{
- auto dcp = boost::filesystem::path(wx_to_std(_dcp->GetPath()));
- if (dcp.empty()) {
- return;
- }
-
auto job_manager = JobManager::instance();
- auto job = make_shared<VerifyDCPJob>(std::vector<boost::filesystem::path>{dcp}, std::vector<boost::filesystem::path>());
- job_manager->add(job);
+ vector<shared_ptr<const VerifyDCPJob>> jobs;
+ for (auto const& dcp: _dcp_paths) {
+ auto job = make_shared<VerifyDCPJob>(std::vector<boost::filesystem::path>{dcp}, std::vector<boost::filesystem::path>());
+ job_manager->add(job);
+ jobs.push_back(job);
+ }
while (job_manager->work_to_do()) {
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI | wxEVT_CATEGORY_USER_INPUT);
dcpomatic_sleep_seconds(1);
-
- _progress_panel->update(job);
+ auto last = job_manager->last_active_job();
+ if (auto locked = last.lock()) {
+ if (auto dcp = dynamic_pointer_cast<VerifyDCPJob>(locked)) {
+ _progress_panel->update(dcp);
+ }
+ }
}
- _result_panel->fill(job);
+ DCPOMATIC_ASSERT(_dcp_paths.size() == jobs.size());
+ _result_panel->add(jobs);
if (_write_log->get()) {
- dcp::TextFormatter formatter(dcp / "REPORT.txt");
- dcp::verify_report(job->result(), formatter);
+ for (size_t i = 0; i < _dcp_paths.size(); ++i) {
+ dcp::TextFormatter formatter(_dcp_paths[i] / "REPORT.txt");
+ dcp::verify_report({ jobs[i]->result() }, formatter);
+ }
}
+
+ _progress_panel->clear();
+ }
+
+private:
+ void set_dcp_paths (vector<boost::filesystem::path> dcps)
+ {
+ _dcp_paths = dcps;
+ setup_sensitivity();
+ }
+
+ vector<boost::filesystem::path> dcp_paths() const
+ {
+ return _dcp_paths;
}
- DirPickerCtrl* _dcp;
+ std::vector<boost::filesystem::path> _dcp_paths;
CheckBox* _write_log;
Button* _verify;
VerifyDCPProgressPanel* _progress_panel;
diff --git a/src/wx/verify_dcp_progress_dialog.cc b/src/wx/verify_dcp_progress_dialog.cc
index 5e3a7be62..cdca99603 100644
--- a/src/wx/verify_dcp_progress_dialog.cc
+++ b/src/wx/verify_dcp_progress_dialog.cc
@@ -23,8 +23,8 @@
#include "verify_dcp_progress_panel.h"
#include "wx_util.h"
#include "lib/cross.h"
-#include "lib/job.h"
#include "lib/job_manager.h"
+#include "lib/verify_dcp_job.h"
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
#include <wx/evtloop.h>
@@ -66,7 +66,7 @@ VerifyDCPProgressDialog::cancel ()
bool
-VerifyDCPProgressDialog::run (shared_ptr<Job> job)
+VerifyDCPProgressDialog::run(shared_ptr<VerifyDCPJob> job)
{
Show ();
diff --git a/src/wx/verify_dcp_progress_dialog.h b/src/wx/verify_dcp_progress_dialog.h
index 1ae3336e8..5120b309b 100644
--- a/src/wx/verify_dcp_progress_dialog.h
+++ b/src/wx/verify_dcp_progress_dialog.h
@@ -26,7 +26,7 @@ LIBDCP_ENABLE_WARNINGS
#include <memory>
-class Job;
+class VerifyDCPJob;
class VerifyDCPProgressPanel;
@@ -35,7 +35,7 @@ class VerifyDCPProgressDialog : public wxDialog
public:
VerifyDCPProgressDialog (wxWindow* parent, wxString title);
- bool run (std::shared_ptr<Job> job);
+ bool run(std::shared_ptr<VerifyDCPJob> job);
private:
void cancel ();
diff --git a/src/wx/verify_dcp_progress_panel.cc b/src/wx/verify_dcp_progress_panel.cc
index a99791911..2b0455ca1 100644
--- a/src/wx/verify_dcp_progress_panel.cc
+++ b/src/wx/verify_dcp_progress_panel.cc
@@ -19,7 +19,7 @@
*/
-#include "lib/job.h"
+#include "lib/verify_dcp_job.h"
#include "verify_dcp_progress_panel.h"
#include "wx_util.h"
@@ -37,6 +37,12 @@ VerifyDCPProgressPanel::VerifyDCPProgressPanel(wxWindow* parent)
{
auto overall_sizer = new wxBoxSizer(wxVERTICAL);
+ _directory_name = new wxStaticText(this, wxID_ANY, {});
+ wxFont directory_name_font(*wxNORMAL_FONT);
+ directory_name_font.SetFamily(wxFONTFAMILY_MODERN);
+ _directory_name->SetFont(directory_name_font);
+ overall_sizer->Add(_directory_name, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
+
_job_name = new wxStaticText(this, wxID_ANY, {});
overall_sizer->Add(_job_name, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
@@ -61,8 +67,11 @@ VerifyDCPProgressPanel::VerifyDCPProgressPanel(wxWindow* parent)
void
-VerifyDCPProgressPanel::update(shared_ptr<Job> job)
+VerifyDCPProgressPanel::update(shared_ptr<const VerifyDCPJob> job)
{
+ DCPOMATIC_ASSERT(!job->directories().empty());
+ checked_set(_directory_name, std_to_wx(job->directories()[0].filename().string()));
+
auto const progress = job->progress();
if (progress) {
_progress->SetValue(*progress * 100);
@@ -85,3 +94,13 @@ VerifyDCPProgressPanel::update(shared_ptr<Job> job)
}
}
+
+void
+VerifyDCPProgressPanel::clear()
+{
+ _directory_name->SetLabel(wxT(""));
+ _job_name->SetLabel(wxT(""));
+ _file_name->SetLabel(wxT(""));
+ _progress->SetValue(0);
+}
+
diff --git a/src/wx/verify_dcp_progress_panel.h b/src/wx/verify_dcp_progress_panel.h
index 3fde50827..52ef221cb 100644
--- a/src/wx/verify_dcp_progress_panel.h
+++ b/src/wx/verify_dcp_progress_panel.h
@@ -26,7 +26,7 @@ LIBDCP_ENABLE_WARNINGS
#include <memory>
-class Job;
+class VerifyDCPJob;
class VerifyDCPProgressPanel : public wxPanel
@@ -34,9 +34,11 @@ class VerifyDCPProgressPanel : public wxPanel
public:
VerifyDCPProgressPanel(wxWindow* parent);
- void update(std::shared_ptr<Job> job);
+ void update(std::shared_ptr<const VerifyDCPJob> job);
+ void clear();
private:
+ wxStaticText* _directory_name;
wxStaticText* _job_name;
wxStaticText* _file_name;
wxGauge* _progress;
diff --git a/src/wx/verify_dcp_result_dialog.cc b/src/wx/verify_dcp_result_dialog.cc
index 806eac85a..c61ed19ea 100644
--- a/src/wx/verify_dcp_result_dialog.cc
+++ b/src/wx/verify_dcp_result_dialog.cc
@@ -33,7 +33,7 @@ VerifyDCPResultDialog::VerifyDCPResultDialog(wxWindow* parent, shared_ptr<Verify
auto sizer = new wxBoxSizer (wxVERTICAL);
auto panel = new VerifyDCPResultPanel(this);
- panel->fill(job);
+ panel->add({ job });
sizer->Add(panel, 1, wxEXPAND);
auto buttons = CreateStdDialogButtonSizer(0);
diff --git a/src/wx/verify_dcp_result_panel.cc b/src/wx/verify_dcp_result_panel.cc
index 4cf44431c..6f8b1f53a 100644
--- a/src/wx/verify_dcp_result_panel.cc
+++ b/src/wx/verify_dcp_result_panel.cc
@@ -28,8 +28,8 @@
#include <dcp/verify_report.h>
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
-#include <wx/richtext/richtextctrl.h>
#include <wx/notebook.h>
+#include <wx/treectrl.h>
LIBDCP_ENABLE_WARNINGS
#include <fmt/format.h>
#include <boost/algorithm/string.hpp>
@@ -44,17 +44,30 @@ using std::vector;
VerifyDCPResultPanel::VerifyDCPResultPanel(wxWindow* parent)
: wxPanel(parent, wxID_ANY)
+ , _types{
+ dcp::VerificationNote::Type::ERROR,
+ dcp::VerificationNote::Type::BV21_ERROR,
+ dcp::VerificationNote::Type::WARNING
+ }
{
auto sizer = new wxBoxSizer(wxVERTICAL);
- auto notebook = new wxNotebook(this, wxID_ANY);
+ auto notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 400));
sizer->Add(notebook, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
- _pages[dcp::VerificationNote::Type::ERROR] = new wxRichTextCtrl(notebook, wxID_ANY, wxEmptyString, wxDefaultPosition, {400, 300}, wxRE_READONLY);
- notebook->AddPage(_pages[dcp::VerificationNote::Type::ERROR], _("Errors"));
- _pages[dcp::VerificationNote::Type::BV21_ERROR] = new wxRichTextCtrl(notebook, wxID_ANY, wxEmptyString, wxDefaultPosition, {400, 300}, wxRE_READONLY);
- notebook->AddPage(_pages[dcp::VerificationNote::Type::BV21_ERROR], _("SMPTE Bv2.1 errors"));
- _pages[dcp::VerificationNote::Type::WARNING] = new wxRichTextCtrl(notebook, wxID_ANY, wxEmptyString, wxDefaultPosition, {400, 300}, wxRE_READONLY);
- notebook->AddPage(_pages[dcp::VerificationNote::Type::WARNING], _("Warnings"));
+ auto names = map<dcp::VerificationNote::Type, wxString>{
+ { dcp::VerificationNote::Type::ERROR, _("Errors") },
+ { dcp::VerificationNote::Type::BV21_ERROR, _("SMPTE Bv2.1 errors") },
+ { dcp::VerificationNote::Type::WARNING, _("Warnings") }
+ };
+
+ for (auto const type: _types) {
+ auto panel = new wxPanel(notebook, wxID_ANY);
+ _pages[type] = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS | wxTR_NO_LINES);
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(_pages[type], 1, wxEXPAND);
+ panel->SetSizer(sizer);
+ notebook->AddPage(panel, names[type]);
+ }
_summary = new wxStaticText(this, wxID_ANY, {});
sizer->Add(_summary, 0, wxALL, DCPOMATIC_DIALOG_BORDER);
@@ -70,10 +83,6 @@ VerifyDCPResultPanel::VerifyDCPResultPanel(wxWindow* parent)
sizer->Layout();
sizer->SetSizeHints(this);
- for (auto const& i: _pages) {
- i.second->GetCaret()->Hide();
- }
-
_save_text_report->bind(&VerifyDCPResultPanel::save_text_report, this);
_save_html_report->bind(&VerifyDCPResultPanel::save_html_report, this);
@@ -83,24 +92,82 @@ VerifyDCPResultPanel::VerifyDCPResultPanel(wxWindow* parent)
void
-VerifyDCPResultPanel::fill(shared_ptr<VerifyDCPJob> job)
+VerifyDCPResultPanel::add(vector<shared_ptr<const VerifyDCPJob>> jobs)
{
- if (job->finished_ok() && job->result().notes.empty()) {
- _summary->SetLabel(_("DCP validates OK."));
- return;
+ _jobs = jobs;
+
+ for (auto const type: _types) {
+ _pages[type]->DeleteAllItems();
+ _pages[type]->AddRoot(wxT(""));
}
- vector<dcp::VerificationNote::Type> const types = {
- dcp::VerificationNote::Type::WARNING,
- dcp::VerificationNote::Type::BV21_ERROR,
- dcp::VerificationNote::Type::ERROR
- };
+ map<dcp::VerificationNote::Type, int> counts;
+ for (auto type: _types) {
+ counts[type] = 0;
+ }
+
+ for (auto job: jobs) {
+ auto job_counts = add(job, jobs.size() > 1);
+ for (auto const type: _types) {
+ counts[type] += job_counts[type];
+ }
+ }
+
+ wxString summary_text;
+
+ if (counts[dcp::VerificationNote::Type::ERROR] == 1) {
+ /// TRANSLATORS: this will be used at the start of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
+ summary_text = _("1 error, ");
+ } else {
+ /// TRANSLATORS: this will be used at the start of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
+ summary_text = wxString::Format(_("%d errors, "), counts[dcp::VerificationNote::Type::ERROR]);
+ }
+
+ if (counts[dcp::VerificationNote::Type::BV21_ERROR] == 1) {
+ /// TRANSLATORS: this will be used in the middle of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
+ summary_text += _("1 Bv2.1 error, ");
+ } else {
+ /// TRANSLATORS: this will be used in the middle of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
+ summary_text += wxString::Format(_("%d Bv2.1 errors, "), counts[dcp::VerificationNote::Type::BV21_ERROR]);
+ }
+
+ if (counts[dcp::VerificationNote::Type::WARNING] == 1) {
+ /// TRANSLATORS: this will be used at the end of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
+ summary_text += _("and 1 warning.");
+ } else {
+ /// TRANSLATORS: this will be used at the end of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
+ summary_text += wxString::Format(_("and %d warnings."), counts[dcp::VerificationNote::Type::WARNING]);
+ }
+
+ _summary->SetLabel(summary_text);
+ _save_text_report->Enable(true);
+ _save_html_report->Enable(true);
+
+ for (auto type: _types) {
+ _pages[type]->ExpandAll();
+ }
+}
+
+
+map<dcp::VerificationNote::Type, int>
+VerifyDCPResultPanel::add(shared_ptr<const VerifyDCPJob> job, bool many)
+{
map<dcp::VerificationNote::Type, int> counts;
- for (auto type: types) {
+ for (auto type: _types) {
counts[type] = 0;
}
+ map<dcp::VerificationNote::Type, wxTreeItemId> root;
+
+ for (auto type: _types) {
+ root[type] = _pages[type]->GetRootItem();
+ if (many) {
+ DCPOMATIC_ASSERT(!job->directories().empty());
+ root[type] = _pages[type]->AppendItem(root[type], std_to_wx(job->directories()[0].filename().string()));
+ }
+ }
+
int constexpr limit_per_type = 20;
auto substitute = [](wxString message, dcp::VerificationNote const& note) {
@@ -146,25 +213,22 @@ VerifyDCPResultPanel::fill(shared_ptr<VerifyDCPJob> job)
return message;
};
- auto add_bullet = [this](dcp::VerificationNote::Type type, wxString message) {
- _pages[type]->BeginStandardBullet(char_to_wx("standard/diamond"), 1, 50);
- _pages[type]->WriteText(message);
- _pages[type]->Newline();
- _pages[type]->EndStandardBullet();
+ auto add_line = [this, &root](dcp::VerificationNote::Type type, wxString message) {
+ _pages[type]->AppendItem(root[type], message);
};
- auto add = [&add_bullet, &substitute](vector<dcp::VerificationNote> const& notes, wxString message, wxString more_message = {}) {
+ auto add = [&add_line, &substitute](vector<dcp::VerificationNote> const& notes, wxString message, wxString more_message = {}) {
for (auto const& note: notes) {
- add_bullet(note.type(), substitute(message, note));
+ add_line(note.type(), substitute(message, note));
}
if (notes.size() == limit_per_type && !more_message.IsEmpty()) {
- add_bullet(notes[0].type(), more_message);
+ add_line(notes[0].type(), more_message);
}
};
if (job->finished_in_error() && job->error_summary() != "") {
/* We have an error that did not come from dcp::verify */
- add_bullet(dcp::VerificationNote::Type::ERROR, std_to_wx(job->error_summary()));
+ add_line(dcp::VerificationNote::Type::ERROR, std_to_wx(job->error_summary()));
}
/* Gather notes by code, discarding more than limit_per_type so we don't get overwhelmed if
@@ -603,54 +667,24 @@ VerifyDCPResultPanel::fill(shared_ptr<VerifyDCPJob> job)
}
}
- wxString summary_text;
-
- if (counts[dcp::VerificationNote::Type::ERROR] == 1) {
- /// TRANSLATORS: this will be used at the start of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
- summary_text = _("1 error, ");
- } else {
- /// TRANSLATORS: this will be used at the start of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
- summary_text = wxString::Format(_("%d errors, "), counts[dcp::VerificationNote::Type::ERROR]);
- }
-
- if (counts[dcp::VerificationNote::Type::BV21_ERROR] == 1) {
- /// TRANSLATORS: this will be used in the middle of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
- summary_text += _("1 Bv2.1 error, ");
- } else {
- /// TRANSLATORS: this will be used in the middle of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
- summary_text += wxString::Format(_("%d Bv2.1 errors, "), counts[dcp::VerificationNote::Type::BV21_ERROR]);
- }
-
- if (counts[dcp::VerificationNote::Type::WARNING] == 1) {
- /// TRANSLATORS: this will be used at the end of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
- summary_text += _("and 1 warning.");
- } else {
- /// TRANSLATORS: this will be used at the end of a string like "1 error, 2 Bv2.1 errors and 3 warnings."
- summary_text += wxString::Format(_("and %d warnings."), counts[dcp::VerificationNote::Type::WARNING]);
- }
-
- _summary->SetLabel(summary_text);
-
if (counts[dcp::VerificationNote::Type::ERROR] == 0) {
- add_bullet(dcp::VerificationNote::Type::ERROR, _("No errors found."));
+ add_line(dcp::VerificationNote::Type::ERROR, _("No errors found."));
}
if (counts[dcp::VerificationNote::Type::BV21_ERROR] == 0) {
- add_bullet(dcp::VerificationNote::Type::BV21_ERROR, _("No SMPTE Bv2.1 errors found."));
+ add_line(dcp::VerificationNote::Type::BV21_ERROR, _("No SMPTE Bv2.1 errors found."));
}
if (counts[dcp::VerificationNote::Type::WARNING] == 0) {
- add_bullet(dcp::VerificationNote::Type::WARNING, _("No warnings found."));
+ add_line(dcp::VerificationNote::Type::WARNING, _("No warnings found."));
}
- _job = job;
- _save_text_report->Enable(true);
- _save_html_report->Enable(true);
+ return counts;
}
template <class T>
-void save(wxWindow* parent, wxString filter, dcp::VerificationResult const& result)
+void save(wxWindow* parent, wxString filter, vector<shared_ptr<const VerifyDCPJob>> jobs)
{
FileDialog dialog(parent, _("Verification report"), filter, wxFD_SAVE | wxFD_OVERWRITE_PROMPT, "SaveVerificationReport");
if (!dialog.show()) {
@@ -658,23 +692,23 @@ void save(wxWindow* parent, wxString filter, dcp::VerificationResult const& resu
}
T formatter(dialog.path());
- dcp::verify_report(result, formatter);
+ auto results = std::vector<dcp::VerificationResult>();
+ for (auto job: jobs) {
+ results.push_back(job->result());
+ }
+ dcp::verify_report(results, formatter);
}
void
VerifyDCPResultPanel::save_text_report()
{
- if (_job) {
- save<dcp::TextFormatter>(this, char_to_wx("Text files (*.txt)|*.txt"), _job->result());
- }
+ save<dcp::TextFormatter>(this, char_to_wx("Text files (*.txt)|*.txt"), _jobs);
}
void
VerifyDCPResultPanel::save_html_report()
{
- if (_job) {
- save<dcp::HTMLFormatter>(this, char_to_wx("HTML files (*.htm;*html)|*.htm;*.html"), _job->result());
- }
+ save<dcp::HTMLFormatter>(this, char_to_wx("HTML files (*.htm;*html)|*.htm;*.html"), _jobs);
}
diff --git a/src/wx/verify_dcp_result_panel.h b/src/wx/verify_dcp_result_panel.h
index 8cf92118b..fe75bfe35 100644
--- a/src/wx/verify_dcp_result_panel.h
+++ b/src/wx/verify_dcp_result_panel.h
@@ -27,7 +27,7 @@
class Button;
class VerifyDCPJob;
-class wxRichTextCtrl;
+class wxTreeCtrl;
class VerifyDCPResultPanel : public wxPanel
@@ -35,16 +35,19 @@ class VerifyDCPResultPanel : public wxPanel
public:
VerifyDCPResultPanel(wxWindow* parent);
- void fill(std::shared_ptr<VerifyDCPJob> job);
+ void add(std::vector<std::shared_ptr<const VerifyDCPJob>> job);
private:
+ std::map<dcp::VerificationNote::Type, int> add(std::shared_ptr<const VerifyDCPJob> job, bool many);
void save_text_report();
void save_html_report();
wxStaticText* _summary;
- std::map<dcp::VerificationNote::Type, wxRichTextCtrl*> _pages;
+ std::map<dcp::VerificationNote::Type, wxTreeCtrl*> _pages;
Button* _save_text_report;
Button* _save_html_report;
- std::shared_ptr<VerifyDCPJob> _job;
+ std::vector<std::shared_ptr<const VerifyDCPJob>> _jobs;
+
+ std::vector<dcp::VerificationNote::Type> _types;
};