diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-02-20 23:10:50 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-02-23 01:47:38 +0100 |
| commit | 81d8c747c7a04be10821223dea69faf058b05b1d (patch) | |
| tree | 98f58e45012c3133e719024fe8c84d7b9c691811 /src | |
| parent | 6628b2573f601b7448da169453d5f113534e525e (diff) | |
Allow verification of multiple DCPs in one go (#2843).
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/job_manager.cc | 10 | ||||
| -rw-r--r-- | src/lib/job_manager.h | 6 | ||||
| -rw-r--r-- | src/tools/dcpomatic_verifier.cc | 111 | ||||
| -rw-r--r-- | src/wx/verify_dcp_progress_dialog.cc | 4 | ||||
| -rw-r--r-- | src/wx/verify_dcp_progress_dialog.h | 4 | ||||
| -rw-r--r-- | src/wx/verify_dcp_progress_panel.cc | 23 | ||||
| -rw-r--r-- | src/wx/verify_dcp_progress_panel.h | 6 | ||||
| -rw-r--r-- | src/wx/verify_dcp_result_dialog.cc | 2 | ||||
| -rw-r--r-- | src/wx/verify_dcp_result_panel.cc | 180 | ||||
| -rw-r--r-- | src/wx/verify_dcp_result_panel.h | 11 |
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; }; |
