2 Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 #include "wx/dir_picker_ctrl.h"
23 #include "wx/editable_list.h"
24 #include "wx/wx_signal_manager.h"
25 #include "lib/combine_dcp_job.h"
26 #include "lib/config.h"
27 #include "lib/cross.h"
28 #include "lib/job_manager.h"
30 #include <dcp/combine.h>
31 DCPOMATIC_DISABLE_WARNINGS
32 #include <wx/filepicker.h>
33 DCPOMATIC_ENABLE_WARNINGS
35 #include <boost/bind.hpp>
36 #include <boost/filesystem.hpp>
43 using boost::dynamic_pointer_cast;
44 using boost::optional;
45 using boost::shared_ptr;
49 display_string (boost::filesystem::path p, int)
51 return p.filename().string();
55 class DirDialogWrapper : public wxDirDialog
58 DirDialogWrapper (wxWindow* parent)
59 : wxDirDialog (parent, _("Choose a DCP folder"), wxT(""), wxDD_DIR_MUST_EXIST)
64 boost::filesystem::path get () const
66 return boost::filesystem::path(wx_to_std(GetPath()));
69 void set (boost::filesystem::path)
76 class DOMFrame : public wxFrame
79 explicit DOMFrame (wxString const & title)
80 : wxFrame (0, -1, title)
82 /* Use a panel as the only child of the Frame so that we avoid
83 the dark-grey background on Windows.
85 wxPanel* overall_panel = new wxPanel (this);
86 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
87 s->Add (overall_panel, 1, wxEXPAND);
90 vector<EditableListColumn> columns;
91 columns.push_back(EditableListColumn(_("Input DCP"), 600, true));
93 _input = new EditableList<boost::filesystem::path, DirDialogWrapper>(
96 boost::bind(&DOMFrame::inputs, this),
97 boost::bind(&DOMFrame::set_inputs, this, _1),
103 wxBoxSizer* output = new wxBoxSizer (wxHORIZONTAL);
104 add_label_to_sizer (output, overall_panel, _("Output DCP folder"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
105 _output = new DirPickerCtrl (overall_panel);
106 output->Add (_output, 1, wxEXPAND);
108 _combine = new Button (overall_panel, _("Combine"));
110 wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
111 sizer->Add (_input, 1, wxALL | wxEXPAND, DCPOMATIC_DIALOG_BORDER);
112 sizer->Add (output, 0, wxALL | wxEXPAND, DCPOMATIC_DIALOG_BORDER);
113 sizer->Add (_combine, 0, wxALL | wxALIGN_RIGHT, DCPOMATIC_DIALOG_BORDER);
114 overall_panel->SetSizer (sizer);
116 SetSize (768, GetSize().GetHeight() + 32);
118 _combine->Bind (wxEVT_BUTTON, boost::bind(&DOMFrame::combine, this));
119 _output->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind(&DOMFrame::setup_sensitivity, this));
121 setup_sensitivity ();
125 void set_inputs (vector<boost::filesystem::path> inputs)
130 vector<boost::filesystem::path> inputs () const
137 boost::filesystem::path const output = wx_to_std(_output->GetPath());
139 if (boost::filesystem::is_directory(output) && !boost::filesystem::is_empty(output)) {
140 if (!confirm_dialog (
143 String::compose(wx_to_std(_("The directory %1 already exists and is not empty. "
144 "Are you sure you want to use it?")),
150 } else if (boost::filesystem::is_regular_file(output)) {
153 String::compose (wx_to_std(_("%1 already exists as a file, so you cannot use it for a DCP.")), output.string())
158 JobManager* jm = JobManager::instance ();
159 jm->add (shared_ptr<Job>(new CombineDCPJob(_inputs, output)));
160 bool const ok = display_progress (_("DCP-o-matic Combine"), _("Combining DCPs"));
165 DCPOMATIC_ASSERT (!jm->get().empty());
166 shared_ptr<CombineDCPJob> last = dynamic_pointer_cast<CombineDCPJob> (jm->get().back());
167 DCPOMATIC_ASSERT (last);
169 if (last->finished_ok()) {
170 message_dialog (this, _("DCPs combined successfully."));
172 wxString m = std_to_wx(last->error_summary());
173 if (!last->error_details().empty()) {
174 m += wxString::Format(" (%s)", std_to_wx(last->error_details()));
176 error_dialog (this, m);
180 void setup_sensitivity ()
182 _combine->Enable (!_output->GetPath().IsEmpty());
185 EditableList<boost::filesystem::path, DirDialogWrapper>* _input;
186 DirPickerCtrl* _output;
187 vector<boost::filesystem::path> _inputs;
192 class App : public wxApp
202 Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this));
203 Config::Warning.connect (boost::bind (&App::config_warning, this, _1));
205 SetAppName (_("DCP-o-matic Combiner"));
207 if (!wxApp::OnInit()) {
211 #ifdef DCPOMATIC_LINUX
212 unsetenv ("UBUNTU_MENUPROXY");
216 make_foreground_application ();
219 dcpomatic_setup_path_encoding ();
221 /* Enable i18n; this will create a Config object
222 to look for a force-configured language. This Config
223 object will be wrong, however, because dcpomatic_setup
224 hasn't yet been called and there aren't any filters etc.
227 dcpomatic_setup_i18n ();
229 /* Set things up, including filters etc.
230 which will now be internationalised correctly.
234 /* Force the configuration to be re-loaded correctly next
239 _frame = new DOMFrame (_("DCP-o-matic DCP Combiner"));
240 SetTopWindow (_frame);
244 signal_manager = new wxSignalManager (this);
245 Bind (wxEVT_IDLE, boost::bind(&App::idle, this, _1));
249 error_dialog (0, wxString::Format ("DCP-o-matic DCP Combiner could not start."), std_to_wx(e.what()));
256 void config_failed_to_load ()
258 message_dialog (_frame, _("The existing configuration failed to load. Default values will be used instead. These may take a short time to create."));
261 void config_warning (string m)
263 message_dialog (_frame, std_to_wx(m));
266 void idle (wxIdleEvent& ev)
268 signal_manager->ui_idle ();
272 void report_exception ()
276 } catch (FileError& e) {
280 _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM,
281 std_to_wx (e.what()),
282 std_to_wx (e.file().string().c_str ())
285 } catch (exception& e) {
289 _("An exception occurred: %s.\n\n") + REPORT_PROBLEM,
290 std_to_wx (e.what ())
294 error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM);
298 bool OnExceptionInMainLoop ()
301 /* This will terminate the program */
305 void OnUnhandledException ()