diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-06-04 01:11:33 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-07-09 00:35:10 +0200 |
| commit | 86aece3fc0368fdbe58b3501c0abfb4b1d4c564b (patch) | |
| tree | 7f52ae85b5ab0b36d05a0e75b8fd148ed07ba3b5 | |
| parent | 81fea8d53c5e92be9b43d97005e88cd92fe9e85d (diff) | |
Change TMS upload feature to generic upload.
| -rw-r--r-- | src/lib/config.cc | 8 | ||||
| -rw-r--r-- | src/lib/config.h | 9 | ||||
| -rw-r--r-- | src/tools/dcpomatic.cc | 17 | ||||
| -rw-r--r-- | src/wx/upload_destination_dialog.cc | 140 | ||||
| -rw-r--r-- | src/wx/upload_destination_dialog.h | 55 | ||||
| -rw-r--r-- | src/wx/upload_destination_panel.cc | 189 | ||||
| -rw-r--r-- | src/wx/upload_destination_panel.h | 61 | ||||
| -rw-r--r-- | src/wx/upload_dialog.cc | 70 | ||||
| -rw-r--r-- | src/wx/upload_dialog.h | 42 | ||||
| -rw-r--r-- | src/wx/wscript | 3 |
10 files changed, 589 insertions, 5 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc index 233c7fed7..42fd5d75a 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -362,6 +362,10 @@ try _tms = UploadDestination(node); } + for (auto i: f.node_children("UploadDestination")) { + _upload_destinations.push_back(UploadDestination(i)); + } + _language = f.optional_string_child("Language"); _default_dcp_content_type = DCPContentType::from_isdcf_name(f.optional_string_child("DefaultDCPContentType").get_value_or("FTR")); @@ -789,6 +793,10 @@ Config::write_config() const _tms->as_xml(cxml::add_child(root, "TMS")); } + for (auto i: _upload_destinations) { + i.as_xml(cxml::add_child(root, "UploadDestination")); + } + if (_language) { /* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */ cxml::add_text_child(root, "Language", _language.get()); diff --git a/src/lib/config.h b/src/lib/config.h index f65edda20..e33bfe3f3 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -155,6 +155,10 @@ public: return _only_servers_encode; } + std::vector<UploadDestination> upload_destinations() const { + return _upload_destinations; + } + boost::optional<UploadDestination> tms() const { return _tms; } @@ -697,6 +701,10 @@ public: maybe_set(_only_servers_encode, o); } + void set_upload_destinations(std::vector<UploadDestination> const& destinations) { + maybe_set(_upload_destinations, destinations); + } + void set_tms(UploadDestination tms) { _tms = tms; changed(); @@ -1319,6 +1327,7 @@ private: /** J2K encoding servers that should definitely be used */ std::vector<std::string> _servers; bool _only_servers_encode; + std::vector<UploadDestination> _upload_destinations; boost::optional<UploadDestination> _tms; /** The list of possible DCP frame rates that DCP-o-matic will use */ std::list<int> _allowed_dcp_frame_rates; diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index d8e79d667..fc7dd5e40 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -54,6 +54,7 @@ #include "wx/system_information_dialog.h" #include "wx/templates_dialog.h" #include "wx/update_dialog.h" +#include "wx/upload_dialog.h" #include "wx/video_waveform_dialog.h" #include "wx/wx_signal_manager.h" #include "wx/wx_util.h" @@ -97,6 +98,7 @@ #include "lib/transcode_job.h" #include "lib/unzipper.h" #include "lib/update_checker.h" +#include "lib/upload_job.h" #include "lib/variant.h" #include "lib/version.h" #include "lib/video_content.h" @@ -241,7 +243,7 @@ enum { ID_jobs_make_self_dkdm, ID_jobs_export_video_file, ID_jobs_export_subtitles, - ID_jobs_send_dcp_to_tms, + ID_jobs_upload_dcp, ID_jobs_show_dcp, ID_jobs_open_dcp_in_player, ID_view_closed_captions, @@ -352,7 +354,7 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_self_dkdm, this), ID_jobs_make_self_dkdm); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_export_video_file, this), ID_jobs_export_video_file); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_export_subtitles, this), ID_jobs_export_subtitles); - Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_send_dcp_to_tms, this), ID_jobs_send_dcp_to_tms); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_upload_dcp, this), ID_jobs_upload_dcp); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_show_dcp, this), ID_jobs_show_dcp); Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_open_dcp_in_player, this), ID_jobs_open_dcp_in_player); Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_closed_captions, this), ID_view_closed_captions); @@ -1068,9 +1070,14 @@ private: } - void jobs_send_dcp_to_tms () + void jobs_upload_dcp() { - _film->send_dcp_to_tms (); + UploadDialog dialog(this); + if (dialog.ShowModal() == wxID_OK) { + for (auto const& destination: dialog.destinations()) { + JobManager::instance()->add(make_shared<UploadJob>(_film, destination)); + } + } } void jobs_show_dcp () @@ -1403,7 +1410,7 @@ private: add_item (jobs_menu, _("Export video file...\tCtrl-E"), ID_jobs_export_video_file, NEEDS_FILM); add_item (jobs_menu, _("Export subtitles..."), ID_jobs_export_subtitles, NEEDS_FILM); jobs_menu->AppendSeparator (); - add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL); + add_item (jobs_menu, _("&Upload DCP..."), ID_jobs_upload_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL); #if defined(DCPOMATIC_OSX) add_item (jobs_menu, _("S&how DCP in Finder"), ID_jobs_show_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL); diff --git a/src/wx/upload_destination_dialog.cc b/src/wx/upload_destination_dialog.cc new file mode 100644 index 000000000..a32055c60 --- /dev/null +++ b/src/wx/upload_destination_dialog.cc @@ -0,0 +1,140 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "check_box.h" +#include "dcpomatic_choice.h" +#include "dcpomatic_spin_ctrl.h" +#include "password_entry.h" +#include "upload_destination_dialog.h" +#include "wx_util.h" +#include "lib/upload_destination.h" + + +using std::shared_ptr; +using std::string; +using std::vector; +using boost::optional; + + +UploadDestinationDialog::UploadDestinationDialog(wxWindow* parent) + : TableDialog(parent, _("Upload destination"), 2, 1, true) +{ + add(_("Name"), true); + _name = add(new wxTextCtrl(this, wxID_ANY, {}, wxDefaultPosition, wxSize(480, -1))); + add(_("Protocol"), true); + _protocol = add(new Choice(this)); + _passive = add(new CheckBox(this, _("Passive"))); + add_spacer(); + _limit_connections = add(new CheckBox(this, _("Limit connections"))); + _maximum_connections = add(new SpinCtrl(this, DCPOMATIC_SPIN_CTRL_WIDTH), 0, wxALIGN_CENTER_VERTICAL); + _maximum_connections->SetRange(1, 100); + add(_("Host"), true); + _host = add(new wxTextCtrl(this, wxID_ANY)); + add(_("Path"), true); + _path = add(new wxTextCtrl(this, wxID_ANY)); + add(_("User"), true); + _user = add(new wxTextCtrl(this, wxID_ANY)); + add(_("Password"), true); + _password = new PasswordEntry(this); + add(_password->get_panel()); + + _protocol->add_entry(_("SCP"), file_transfer_protocol_to_string(FileTransferProtocol::SCP)); + _protocol->add_entry(_("FTP"), file_transfer_protocol_to_string(FileTransferProtocol::FTP)); + + _protocol->bind(&UploadDestinationDialog::protocol_changed, this); + _limit_connections->bind(&UploadDestinationDialog::limit_connections_changed, this); + + layout(); + + _name->SetFocus(); + + _protocol->set(0); + protocol_changed(); + _limit_connections->set(false); + _maximum_connections->set(5); + limit_connections_changed(); +} + + +void +UploadDestinationDialog::protocol_changed() +{ + auto const ftp = _protocol->get_data() == string("ftp") || _protocol->get_data() == string("ftps"); + _passive->Enable(ftp); + _passive->SetValue(ftp); +} + + +void +UploadDestinationDialog::limit_connections_changed() +{ + _maximum_connections->Enable(_limit_connections->get()); +} + + +void +UploadDestinationDialog::set(UploadDestination const& destination) +{ + checked_set(_name, destination.name); + checked_set(_protocol, static_cast<int>(destination.protocol)); + checked_set(_passive, destination.passive_ftp); + checked_set(_limit_connections, static_cast<bool>(destination.maximum_connections)); + if (_limit_connections->get()) { + checked_set(_maximum_connections, *destination.maximum_connections); + } else { + checked_set(_maximum_connections, 5); + } + checked_set(_host, destination.host); + checked_set(_path, destination.path); + checked_set(_user, destination.user); + checked_set(_password, destination.password); + + protocol_changed(); + limit_connections_changed(); +} + + +vector<UploadDestination> +UploadDestinationDialog::get () const +{ + auto protocol = FileTransferProtocol::SCP; + if (auto protocol_name = _protocol->get_data()) { + protocol = file_transfer_protocol_from_string(*protocol_name); + } + + optional<int> max; + if (_limit_connections->get()) { + max = _maximum_connections->get(); + } + + return { + UploadDestination( + wx_to_std(_name->GetValue()), + protocol, + _passive->get(), + max, + wx_to_std(_host->GetValue()), + wx_to_std(_path->GetValue()), + wx_to_std(_user->GetValue()), + _password->get() + ) + }; +} diff --git a/src/wx/upload_destination_dialog.h b/src/wx/upload_destination_dialog.h new file mode 100644 index 000000000..1ebe13552 --- /dev/null +++ b/src/wx/upload_destination_dialog.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "table_dialog.h" +#include "lib/upload_destination.h" +#include <boost/optional.hpp> + + +class CheckBox; +class Choice; +class PasswordEntry; +class SpinCtrl; +class wxTextCtrl; + + +class UploadDestinationDialog : public TableDialog +{ +public: + explicit UploadDestinationDialog(wxWindow *); + + void set(UploadDestination const& destination); + std::vector<UploadDestination> get() const; + +private: + void protocol_changed(); + void limit_connections_changed(); + + wxTextCtrl* _name; + Choice* _protocol; + CheckBox* _passive; + CheckBox* _limit_connections; + SpinCtrl* _maximum_connections; + wxTextCtrl* _host; + wxTextCtrl* _path; + wxTextCtrl* _user; + PasswordEntry* _password; +}; diff --git a/src/wx/upload_destination_panel.cc b/src/wx/upload_destination_panel.cc new file mode 100644 index 000000000..6465400d9 --- /dev/null +++ b/src/wx/upload_destination_panel.cc @@ -0,0 +1,189 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "dcpomatic_button.h" +#include "upload_destination_dialog.h" +#include "upload_destination_panel.h" +#include "wx_util.h" +#include "lib/config.h" +#include <wx/treelist.h> + + +using std::string; +using std::vector; +using boost::optional; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif + + +UploadDestinationPanel::UploadDestinationPanel(wxWindow* parent) + : wxPanel(parent, wxID_ANY) +{ + _list = new wxTreeListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_NO_HEADER | wxTL_3STATE); + _list->AppendColumn(char_to_wx("foo"), 640); + + for (auto destination: Config::instance()->upload_destinations()) { + add_destination(destination); + } + + auto buttons = new wxBoxSizer(wxVERTICAL); + + _add = new Button(this, _("Add...")); + buttons->Add(_add, 0, wxEXPAND | wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + _edit = new Button(this, _("Edit...")); + buttons->Add(_edit, 0, wxEXPAND | wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + _remove = new Button(this, _("Remove")); + buttons->Add(_remove, 0, wxEXPAND | wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + + _add->bind(&UploadDestinationPanel::add, this); + _edit->bind(&UploadDestinationPanel::edit, this); + _remove->bind(&UploadDestinationPanel::remove, this); + + _list->Bind(wxEVT_TREELIST_SELECTION_CHANGED, boost::bind(&UploadDestinationPanel::selection_changed, this)); + _list->Bind(wxEVT_TREELIST_ITEM_CHECKED, boost::bind(&UploadDestinationPanel::checkbox_changed, this)); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(_list, 1, wxEXPAND); + sizer->Add(buttons, 0, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_X_GAP); + SetSizerAndFit(sizer); + + setup_sensitivity(); +} + + +void +UploadDestinationPanel::checkbox_changed() +{ + DestinationsChanged(); +} + + +void +UploadDestinationPanel::selection_changed() +{ + setup_sensitivity(); +} + + +void +UploadDestinationPanel::setup_sensitivity() +{ + auto const selection = static_cast<bool>(selected()); + _edit->Enable(selection); + _remove->Enable(selection); +} + + +void +UploadDestinationPanel::add_destination(UploadDestination destination) +{ + auto const id = _list->AppendItem(_list->GetRootItem(), std_to_wx(destination.name)); + _destinations.push_back(std::make_pair(id, destination)); +} + + +void +UploadDestinationPanel::add() +{ + UploadDestinationDialog dialog(this); + if (dialog.ShowModal() == wxID_OK) { + add_destination(dialog.get()[0]); + update_config(); + } +} + + +void +UploadDestinationPanel::update_config() +{ + vector<UploadDestination> dest; + for (auto const& i: _destinations) { + dest.push_back(i.second); + } + Config::instance()->set_upload_destinations(dest); +} + + +optional<wxTreeListItem> +UploadDestinationPanel::selected() const +{ + wxTreeListItems selection; + _list->GetSelections(selection); + DCPOMATIC_ASSERT(selection.size() <= 1); + if (selection.empty()) { + return {}; + } + + return selection[0]; +} + +void +UploadDestinationPanel::edit() +{ + if (auto sel = selected()) { + for (auto& i: _destinations) { + if (i.first == *sel) { + UploadDestinationDialog dialog(this); + dialog.set(i.second); + if (dialog.ShowModal() == wxID_OK) { + i.second = dialog.get()[0]; + update_config(); + _list->SetItemText(*sel, std_to_wx(i.second.name)); + } + } + } + } +} + + +void +UploadDestinationPanel::remove() +{ + if (auto sel = selected()) { + for (auto iter = _destinations.begin(); iter != _destinations.end(); ) { + if (iter->first == *sel) { + iter = _destinations.erase(iter); + _list->DeleteItem(iter->first); + } else { + ++iter; + } + } + update_config(); + } + +} + + +vector<UploadDestination> +UploadDestinationPanel::destinations() const +{ + vector<UploadDestination> checked; + + for (auto const& dest: _destinations) { + if (_list->GetCheckedState(dest.first) == wxCHK_CHECKED) { + checked.push_back(dest.second); + } + } + + return checked; +} + diff --git a/src/wx/upload_destination_panel.h b/src/wx/upload_destination_panel.h new file mode 100644 index 000000000..fb2fee99e --- /dev/null +++ b/src/wx/upload_destination_panel.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "lib/upload_destination.h" +#include <wx/treelist.h> +#include <wx/wx.h> +#include <boost/signals2.hpp> +#include <map> + + +class Button; + + +class UploadDestinationPanel : public wxPanel +{ +public: + UploadDestinationPanel(wxWindow* parent); + + /** @return ticked, i.e. "enabled" destinations */ + std::vector<UploadDestination> destinations() const; + + boost::signals2::signal<void ()> DestinationsChanged; + +private: + void add(); + void edit(); + void remove(); + + void add_destination(UploadDestination destination); + boost::optional<wxTreeListItem> selected() const; + void selection_changed(); + void setup_sensitivity(); + void update_config(); + void destinations_changed(); + void checkbox_changed(); + + wxTreeListCtrl* _list; + Button* _add; + Button* _edit; + Button* _remove; + std::vector<std::pair<wxTreeListItem, UploadDestination>> _destinations; +}; + diff --git a/src/wx/upload_dialog.cc b/src/wx/upload_dialog.cc new file mode 100644 index 000000000..606ba5ee3 --- /dev/null +++ b/src/wx/upload_dialog.cc @@ -0,0 +1,70 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "static_text.h" +#include "upload_destination_panel.h" +#include "upload_dialog.h" +#include "wx_util.h" +#include <wx/wx.h> + + +using std::vector; + + +UploadDialog::UploadDialog(wxWindow* parent) + : wxDialog(parent, wxID_ANY, _("Upload DCP")) + , _upload_destination_panel(new UploadDestinationPanel(this)) +{ + auto sizer = new wxBoxSizer(wxVERTICAL); + + wxFont subheading_font(*wxNORMAL_FONT); + subheading_font.SetWeight(wxFONTWEIGHT_BOLD); + + sizer->Add(_upload_destination_panel, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + auto buttons = CreateStdDialogButtonSizer(0); + sizer->Add(CreateSeparatedSizer(buttons), wxSizerFlags().Expand().DoubleBorder()); + _start_upload = new wxButton(this, wxID_OK, _("Start upload")); + buttons->SetAffirmativeButton(_start_upload); + buttons->SetNegativeButton(new wxButton(this, wxID_CANCEL, _("Cancel"))); + buttons->Realize(); + + _upload_destination_panel->DestinationsChanged.connect(boost::bind(&UploadDialog::setup_sensitivity, this)); + setup_sensitivity(); + + SetSizer(sizer); + SetSize({640, 480}); +} + + +vector<UploadDestination> +UploadDialog::destinations() const +{ + return _upload_destination_panel->destinations(); +} + + +void +UploadDialog::setup_sensitivity() +{ + _start_upload->Enable(!_upload_destination_panel->destinations().empty()); +} + diff --git a/src/wx/upload_dialog.h b/src/wx/upload_dialog.h new file mode 100644 index 000000000..20f395254 --- /dev/null +++ b/src/wx/upload_dialog.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "lib/upload_destination.h" +#include <wx/wx.h> + + +class UploadDestinationPanel; + + +class UploadDialog : public wxDialog +{ +public: + UploadDialog(wxWindow* parent); + + std::vector<UploadDestination> destinations() const; + +private: + void setup_sensitivity(); + + UploadDestinationPanel* _upload_destination_panel; + wxButton* _start_upload; +}; + diff --git a/src/wx/wscript b/src/wx/wscript index 3dfc157e2..ae754f0a9 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -185,7 +185,10 @@ sources = """ timeline_time_axis_view.cc timing_panel.cc try_unmount_dialog.cc + upload_destination_dialog.cc + upload_destination_panel.cc update_dialog.cc + upload_dialog.cc verify_dcp_dialog.cc verify_dcp_progress_panel.cc verify_dcp_result_panel.cc |
