X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Ftools%2Fdcpomatic.cc;h=1b1ef06294373adaa68443e9909b43981d4cb7a6;hb=f610a8708f11d6052a995f31cd506bc93faafa1c;hp=0226eb7af1ac32e69217959bde0e491d27dd4ff5;hpb=cde563eea0cc9291f2c5eb5205a23723cce010f3;p=dcpomatic.git diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 0226eb7af..1b1ef0629 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -24,88 +24,94 @@ */ -#include "wx/standard_controls.h" -#include "wx/film_viewer.h" -#include "wx/film_editor.h" -#include "wx/job_manager_view.h" -#include "wx/full_config_dialog.h" -#include "wx/wx_util.h" -#include "wx/film_name_location_dialog.h" -#include "wx/wx_signal_manager.h" -#include "wx/recreate_chain_dialog.h" #include "wx/about_dialog.h" -#include "wx/kdm_dialog.h" -#include "wx/dkdm_dialog.h" -#include "wx/self_dkdm_dialog.h" -#include "wx/servers_list_dialog.h" -#include "wx/hints_dialog.h" -#include "wx/update_dialog.h" #include "wx/content_panel.h" -#include "wx/report_problem_dialog.h" -#include "wx/video_waveform_dialog.h" -#include "wx/system_information_dialog.h" -#include "wx/save_template_dialog.h" -#include "wx/templates_dialog.h" -#include "wx/nag_dialog.h" +#include "wx/dkdm_dialog.h" #include "wx/export_subtitles_dialog.h" #include "wx/export_video_file_dialog.h" -#include "wx/paste_dialog.h" +#include "wx/film_editor.h" +#include "wx/film_name_location_dialog.h" +#include "wx/film_viewer.h" #include "wx/focus_manager.h" +#include "wx/full_config_dialog.h" +#include "wx/hints_dialog.h" #include "wx/html_dialog.h" -#include "wx/send_i18n_dialog.h" +#include "wx/file_dialog.h" #include "wx/i18n_hook.h" -#include "lib/film.h" +#include "wx/id.h" +#include "wx/job_manager_view.h" +#include "wx/kdm_dialog.h" +#include "wx/nag_dialog.h" +#include "wx/paste_dialog.h" +#include "wx/recreate_chain_dialog.h" +#include "wx/report_problem_dialog.h" +#include "wx/save_template_dialog.h" +#include "wx/self_dkdm_dialog.h" +#include "wx/send_i18n_dialog.h" +#include "wx/servers_list_dialog.h" +#include "wx/standard_controls.h" +#include "wx/system_information_dialog.h" +#include "wx/templates_dialog.h" +#include "wx/update_dialog.h" +#include "wx/video_waveform_dialog.h" +#include "wx/wx_signal_manager.h" +#include "wx/wx_util.h" #include "lib/analytics.h" -#include "lib/emailer.h" +#include "lib/audio_content.h" +#include "lib/check_content_job.h" +#include "lib/cinema.h" +#include "lib/compose.hpp" #include "lib/config.h" -#include "lib/cross.h" -#include "lib/util.h" -#include "lib/video_content.h" +#include "lib/constants.h" #include "lib/content.h" -#include "lib/version.h" -#include "lib/signal_manager.h" -#include "lib/log.h" -#include "lib/screen.h" -#include "lib/job_manager.h" -#include "lib/exceptions.h" -#include "lib/cinema.h" -#include "lib/kdm_with_metadata.h" -#include "lib/send_kdm_email_job.h" -#include "lib/encode_server_finder.h" -#include "lib/update_checker.h" -#include "lib/cross.h" #include "lib/content_factory.h" -#include "lib/compose.hpp" -#include "lib/dcpomatic_socket.h" -#include "lib/hints.h" +#include "lib/cross.h" +#include "lib/cross.h" #include "lib/dcp_content.h" -#include "lib/ffmpeg_encoder.h" -#include "lib/transcode_job.h" -#include "lib/dkdm_wrapper.h" -#include "lib/audio_content.h" -#include "lib/check_content_change_job.h" -#include "lib/text_content.h" #include "lib/dcpomatic_log.h" +#include "lib/dcpomatic_socket.h" +#include "lib/dkdm_wrapper.h" +#include "lib/email.h" +#include "lib/encode_server_finder.h" +#include "lib/exceptions.h" +#include "lib/ffmpeg_encoder.h" +#include "lib/film.h" +#include "lib/font_config.h" +#include "lib/hints.h" +#include "lib/job_manager.h" +#include "lib/kdm_with_metadata.h" +#include "lib/log.h" +#include "lib/make_dcp.h" +#include "lib/release_notes.h" +#include "lib/screen.h" +#include "lib/send_kdm_email_job.h" +#include "lib/signal_manager.h" #include "lib/subtitle_encoder.h" -#include "lib/warnings.h" +#include "lib/text_content.h" +#include "lib/transcode_job.h" +#include "lib/update_checker.h" +#include "lib/version.h" +#include "lib/video_content.h" #include +#include #include -DCPOMATIC_DISABLE_WARNINGS -#include -#include +#include +LIBDCP_DISABLE_WARNINGS #include +#include #include #include +#include #include -DCPOMATIC_ENABLE_WARNINGS +LIBDCP_ENABLE_WARNINGS #ifdef __WXGTK__ #include #endif #ifdef __WXMSW__ #include #endif -#include #include +#include #include #include /* This is OK as it's only used with DCPOMATIC_WINDOWS */ @@ -141,36 +147,27 @@ class FilmChangedClosingDialog { public: explicit FilmChangedClosingDialog (string name) - { - _dialog = new wxMessageDialog ( + : _dialog( nullptr, wxString::Format(_("Save changes to film \"%s\" before closing?"), std_to_wx (name).data()), /// TRANSLATORS: this is the heading for a dialog box, which tells the user that the current /// project (Film) has been changed since it was last saved. _("Film changed"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION - ); - - _dialog->SetYesNoCancelLabels ( + ) + { + _dialog.SetYesNoCancelLabels( _("Save film and close"), _("Close without saving film"), _("Don't close") ); } - ~FilmChangedClosingDialog () - { - _dialog->Destroy (); - } - - FilmChangedClosingDialog (FilmChangedClosingDialog const&) = delete; - FilmChangedClosingDialog& operator= (FilmChangedClosingDialog const&) = delete; - int run () { - return _dialog->ShowModal (); + return _dialog.ShowModal(); } private: - wxMessageDialog* _dialog; + wxMessageDialog _dialog; }; @@ -178,36 +175,27 @@ class FilmChangedDuplicatingDialog { public: explicit FilmChangedDuplicatingDialog (string name) - { - _dialog = new wxMessageDialog ( + : _dialog( nullptr, wxString::Format(_("Save changes to film \"%s\" before duplicating?"), std_to_wx (name).data()), /// TRANSLATORS: this is the heading for a dialog box, which tells the user that the current /// project (Film) has been changed since it was last saved. _("Film changed"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION - ); - - _dialog->SetYesNoCancelLabels ( + ) + { + _dialog.SetYesNoCancelLabels( _("Save film and duplicate"), _("Duplicate without saving film"), _("Don't duplicate") ); } - ~FilmChangedDuplicatingDialog () - { - _dialog->Destroy (); - } - - FilmChangedDuplicatingDialog (FilmChangedDuplicatingDialog const&) = delete; - FilmChangedDuplicatingDialog& operator= (FilmChangedDuplicatingDialog const&) = delete; - int run () { - return _dialog->ShowModal (); + return _dialog.ShowModal(); } private: - wxMessageDialog* _dialog; + wxMessageDialog _dialog; }; @@ -225,7 +213,7 @@ private: map menu_items; enum { - ID_file_new = 1, + ID_file_new = DCPOMATIC_MAIN_MENU, ID_file_open, ID_file_save, ID_file_save_as_template, @@ -233,7 +221,7 @@ enum { ID_file_duplicate_and_open, ID_file_history, /* Allow spare IDs after _history for the recent files list */ - ID_file_close = 100, + ID_file_close = DCPOMATIC_MAIN_MENU + 100, ID_edit_copy, ID_edit_paste, ID_edit_select_all, @@ -256,6 +244,8 @@ enum { ID_tools_send_translations, ID_tools_system_information, ID_tools_restore_default_preferences, + ID_tools_export_preferences, + ID_tools_import_preferences, ID_help_report_a_problem, /* IDs for shortcuts (with no associated menu item) */ ID_add_file, @@ -267,31 +257,59 @@ enum { }; -class DOMFrame : public wxFrame +class LimitedFrameSplitter : public wxSplitterWindow { public: - explicit DOMFrame (wxString const& title) - : wxFrame (nullptr, -1, title) + LimitedFrameSplitter(wxWindow* parent) + : wxSplitterWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_3DSASH | wxSP_LIVE_UPDATE) { -#if defined(DCPOMATIC_WINDOWS) - if (Config::instance()->win32_console()) { - AllocConsole(); + /* This value doesn't really mean much but we just want to stop double-click on the + divider from shrinking the left panel. + */ + SetMinimumPaneSize(64); - HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE); - int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT); - FILE* hf_out = _fdopen(hCrt, "w"); - setvbuf(hf_out, NULL, _IONBF, 1); - *stdout = *hf_out; + Bind(wxEVT_SIZE, boost::bind(&LimitedFrameSplitter::sized, this, _1)); + } - HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE); - hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT); - FILE* hf_in = _fdopen(hCrt, "r"); - setvbuf(hf_in, NULL, _IONBF, 128); - *stdin = *hf_in; + bool OnSashPositionChange(int new_position) override + { + /* Try to stop the left bit of the splitter getting too small */ + auto const ok = new_position > _left_panel_minimum_size; + if (ok) { + Config::instance()->set_main_divider_sash_position(new_position); + } + return ok; + } - cout << "DCP-o-matic is starting." << "\n"; +private: + void sized(wxSizeEvent& ev) + { + if (GetSize().GetWidth() > _left_panel_minimum_size && GetSashPosition() < _left_panel_minimum_size) { + /* The window is now fairly big but the left panel is small; this happens when the DCP-o-matic window + * is shrunk and then made larger again. Try to set a sensible left panel size in this case. + */ + SetSashPosition(Config::instance()->main_divider_sash_position().get_value_or(_left_panel_minimum_size)); } -#endif + + ev.Skip(); + } + + int const _left_panel_minimum_size = 200; +}; + + +class DOMFrame : public wxFrame +{ +public: + explicit DOMFrame (wxString const& title) + : wxFrame (nullptr, -1, title) + /* Use a panel as the only child of the Frame so that we avoid + the dark-grey background on Windows. + */ + , _splitter(new LimitedFrameSplitter(this)) + , _right_panel(new wxPanel(_splitter, wxID_ANY)) + , _film_viewer(_right_panel) + { auto bar = new wxMenuBar; setup_menu (bar); @@ -338,40 +356,42 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_send_translations, this), ID_tools_send_translations); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_system_information, this),ID_tools_system_information); Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_restore_default_preferences, this), ID_tools_restore_default_preferences); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_export_preferences, this), ID_tools_export_preferences); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_import_preferences, this), ID_tools_import_preferences); Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_about, this), wxID_ABOUT); Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_report_a_problem, this), ID_help_report_a_problem); Bind (wxEVT_CLOSE_WINDOW, boost::bind (&DOMFrame::close, this, _1)); Bind (wxEVT_SHOW, boost::bind (&DOMFrame::show, this, _1)); - /* Use a panel as the only child of the Frame so that we avoid - the dark-grey background on Windows. - */ - auto overall_panel = new wxPanel (this, wxID_ANY); + auto left_panel = new wxPanel(_splitter, wxID_ANY); + + _film_editor = new FilmEditor(left_panel, _film_viewer); + + auto left_sizer = new wxBoxSizer(wxHORIZONTAL); + left_sizer->Add(_film_editor, 1, wxEXPAND); + + left_panel->SetSizerAndFit(left_sizer); - _film_viewer.reset (new FilmViewer (overall_panel)); - _controls = new StandardControls (overall_panel, _film_viewer, true); - _film_editor = new FilmEditor (overall_panel, _film_viewer); - auto job_manager_view = new JobManagerView (overall_panel, false); + _controls = new StandardControls(_right_panel, _film_viewer, true); + _controls->set_film(_film_viewer.film()); + auto job_manager_view = new JobManagerView(_right_panel, false); auto right_sizer = new wxBoxSizer (wxVERTICAL); - right_sizer->Add (_film_viewer->panel(), 2, wxEXPAND | wxALL, 6); + right_sizer->Add(_film_viewer.panel(), 2, wxEXPAND | wxALL, 6); right_sizer->Add (_controls, 0, wxEXPAND | wxALL, 6); right_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6); - wxBoxSizer* main_sizer = new wxBoxSizer (wxHORIZONTAL); - main_sizer->Add (_film_editor, 0, wxEXPAND | wxALL, 6); - main_sizer->Add (right_sizer, 1, wxEXPAND | wxALL, 6); + _right_panel->SetSizer(right_sizer); + + _splitter->SplitVertically(left_panel, _right_panel, Config::instance()->main_divider_sash_position().get_value_or(left_panel->GetSize().GetWidth() + 8)); set_menu_sensitivity (); - _film_editor->FileChanged.connect (bind (&DOMFrame::file_changed, this, _1)); _film_editor->content_panel()->SelectionChanged.connect (boost::bind (&DOMFrame::set_menu_sensitivity, this)); - file_changed (""); - - JobManager::instance()->ActiveJobsChanged.connect (boost::bind (&DOMFrame::set_menu_sensitivity, this)); + set_title (); - overall_panel->SetSizer (main_sizer); + JobManager::instance()->ActiveJobsChanged.connect(boost::bind(&DOMFrame::active_jobs_changed, this)); UpdateChecker::instance()->StateChanged.connect(boost::bind(&DOMFrame::update_checker_state_changed, this)); @@ -387,7 +407,7 @@ public: #else int accelerators = 6; #endif - auto accel = new wxAcceleratorEntry[accelerators]; + std::vector accel(accelerators); /* [Shortcut] Ctrl+A:Add file(s) to the film */ accel[0].Set (wxACCEL_CTRL, static_cast('A'), ID_add_file); /* [Shortcut] Delete:Remove selected content from film */ @@ -409,9 +429,8 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::timeline_pressed, this), ID_timeline); Bind (wxEVT_MENU, boost::bind (&DOMFrame::back_frame, this), ID_back_frame); Bind (wxEVT_MENU, boost::bind (&DOMFrame::forward_frame, this), ID_forward_frame); - wxAcceleratorTable accel_table (accelerators, accel); + wxAcceleratorTable accel_table (accelerators, accel.data()); SetAcceleratorTable (accel_table); - delete[] accel; } void remove_accelerators () @@ -457,11 +476,11 @@ public: set_film (film); - JobManager::instance()->add(shared_ptr(new CheckContentChangeJob(film))); + JobManager::instance()->add(make_shared(film)); } catch (FileNotFoundError& e) { auto const dir = e.file().parent_path(); - if (boost::filesystem::exists(dir / "ASSETMAP") || boost::filesystem::exists(dir / "ASSETMAP.xml")) { + if (dcp::filesystem::exists(dir / "ASSETMAP") || dcp::filesystem::exists(dir / "ASSETMAP.xml")) { error_dialog ( this, _("Could not open this folder as a DCP-o-matic project."), _("It looks like you are trying to open a DCP. File -> Open is for loading DCP-o-matic projects, not DCPs. To import a DCP, create a new project with File -> New and then click the \"Add DCP...\" button.") @@ -479,13 +498,10 @@ public: void set_film (shared_ptr film) { _film = film; - _film_viewer->set_film (_film); - _film_editor->set_film (_film); + _film_viewer.set_film(_film); + _film_editor->set_film(_film); _controls->set_film (_film); - if (_video_waveform_dialog) { - _video_waveform_dialog->Destroy (); - _video_waveform_dialog = nullptr; - } + _video_waveform_dialog.reset(); set_menu_sensitivity (); if (_film && _film->directory()) { Config::instance()->add_to_history (_film->directory().get()); @@ -493,8 +509,10 @@ public: if (_film) { _film->Change.connect (boost::bind (&DOMFrame::film_change, this, _1)); _film->Message.connect (boost::bind(&DOMFrame::film_message, this, _1)); + _film->DirtyChange.connect (boost::bind(&DOMFrame::set_title, this)); dcpomatic_log = _film->log (); } + set_title (); } shared_ptr film () const { @@ -523,54 +541,44 @@ private: } } - void file_changed (boost::filesystem::path f) - { - auto s = wx_to_std(_("DCP-o-matic")); - if (!f.empty ()) { - s += " - " + f.string (); - } - - SetTitle (std_to_wx (s)); - } - void file_new () { - auto d = new FilmNameLocationDialog (this, _("New Film"), true); - int const r = d->ShowModal (); + FilmNameLocationDialog dialog(this, _("New Film"), true); + int const r = dialog.ShowModal(); - if (r == wxID_OK && d->check_path() && maybe_save_then_delete_film()) { - try { - new_film (d->path(), d->template_name()); - } catch (boost::filesystem::filesystem_error& e) { + if (r != wxID_OK || !dialog.check_path() || !maybe_save_then_delete_film()) { + return; + } + + try { + new_film(dialog.path(), dialog.template_name()); + } catch (boost::filesystem::filesystem_error& e) { #ifdef DCPOMATIC_WINDOWS - string bad_chars = "<>:\"/|?*"; - string const filename = d->path().filename().string(); - string found_bad_chars; - for (size_t i = 0; i < bad_chars.length(); ++i) { - if (filename.find(bad_chars[i]) != string::npos && found_bad_chars.find(bad_chars[i]) == string::npos) { - found_bad_chars += bad_chars[i]; - } + string bad_chars = "<>:\"/|?*"; + string const filename = dialog.path().filename().string(); + string found_bad_chars; + for (size_t i = 0; i < bad_chars.length(); ++i) { + if (filename.find(bad_chars[i]) != string::npos && found_bad_chars.find(bad_chars[i]) == string::npos) { + found_bad_chars += bad_chars[i]; } - wxString message = _("Could not create folder to store film."); - message += " "; - if (!found_bad_chars.empty()) { - message += wxString::Format (_("Try removing the %s characters from your folder name."), std_to_wx(found_bad_chars).data()); - } else { - message += _("Please check that you do not have Windows controlled folder access enabled for DCP-o-matic."); - } - error_dialog (this, message, std_to_wx(e.what())); + } + wxString message = _("Could not create folder to store film."); + message += " "; + if (!found_bad_chars.empty()) { + message += wxString::Format (_("Try removing the %s characters from your folder name."), std_to_wx(found_bad_chars).data()); + } else { + message += _("Please check that you do not have Windows controlled folder access enabled for DCP-o-matic."); + } + error_dialog (this, message, std_to_wx(e.what())); #else - error_dialog (this, _("Could not create folder to store film."), std_to_wx(e.what())); + error_dialog (this, _("Could not create folder to store film."), std_to_wx(e.what())); #endif - } } - - d->Destroy (); } void file_open () { - auto c = new wxDirDialog ( + wxDirDialog dialog( this, _("Select film to open"), std_to_wx (Config::instance()->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ()), @@ -579,8 +587,8 @@ private: int r; while (true) { - r = c->ShowModal (); - if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) { + r = dialog.ShowModal(); + if (r == wxID_OK && dialog.GetPath() == wxStandardPaths::Get().GetDocumentsDir()) { error_dialog (this, _("You did not select a folder. Make sure that you select a folder before clicking Open.")); } else { break; @@ -588,67 +596,69 @@ private: } if (r == wxID_OK && maybe_save_then_delete_film()) { - load_film (wx_to_std (c->GetPath ())); + load_film(wx_to_std(dialog.GetPath())); } - - c->Destroy (); } void file_save () { - _film->write_metadata (); + try { + _film->write_metadata (); + } catch (exception& e) { + error_dialog(this, _("Could not save project."), std_to_wx(e.what())); + } } void file_save_as_template () { - auto d = new SaveTemplateDialog (this); - int const r = d->ShowModal (); - if (r == wxID_OK) { - Config::instance()->save_template (_film, d->name ()); + SaveTemplateDialog dialog(this); + if (dialog.ShowModal() == wxID_OK) { + try { + Config::instance()->save_template(_film, dialog.name()); + } catch (exception& e) { + error_dialog(this, _("Could not save template."), std_to_wx(e.what())); + } } - d->Destroy (); } void file_duplicate () { - auto d = new FilmNameLocationDialog (this, _("Duplicate Film"), false); - int const r = d->ShowModal (); + FilmNameLocationDialog dialog(this, _("Duplicate Film"), false); - if (r == wxID_OK && d->check_path() && maybe_save_film()) { - shared_ptr film (new Film (d->path())); + if (dialog.ShowModal() == wxID_OK && dialog.check_path() && maybe_save_film()) { + auto film = make_shared(dialog.path()); film->copy_from (_film); - film->set_name (d->path().filename().generic_string()); - film->write_metadata (); + film->set_name(dialog.path().filename().generic_string()); + try { + film->write_metadata(); + } catch (exception& e) { + error_dialog(this, _("Could not duplicate project."), std_to_wx(e.what())); + } } - - d->Destroy (); } void file_duplicate_and_open () { - auto d = new FilmNameLocationDialog (this, _("Duplicate Film"), false); - int const r = d->ShowModal (); + FilmNameLocationDialog dialog(this, _("Duplicate Film"), false); - if (r == wxID_OK && d->check_path() && maybe_save_film()) { - shared_ptr film (new Film (d->path())); + if (dialog.ShowModal() == wxID_OK && dialog.check_path() && maybe_save_film()) { + auto film = make_shared(dialog.path()); film->copy_from (_film); - film->set_name (d->path().filename().generic_string()); - film->write_metadata (); - set_film (film); + film->set_name(dialog.path().filename().generic_string()); + try { + film->write_metadata (); + set_film (film); + } catch (exception& e) { + error_dialog(this, _("Could not duplicate project."), std_to_wx(e.what())); + } } - - d->Destroy (); } void file_close () { if (_film && _film->dirty ()) { - - auto dialog = new FilmChangedClosingDialog (_film->name ()); - int const r = dialog->run (); - delete dialog; - - switch (r) { + FilmChangedClosingDialog dialog(_film->name()); + switch (dialog.run()) { case wxID_NO: /* Don't save and carry on to close */ break; @@ -683,38 +693,42 @@ private: void edit_copy () { auto const sel = _film_editor->content_panel()->selected(); - DCPOMATIC_ASSERT (sel.size() == 1); - _clipboard = sel.front()->clone(); + if (sel.size() == 1) { + _clipboard = sel.front()->clone(); + } } void edit_paste () { - DCPOMATIC_ASSERT (_clipboard); + if (!_clipboard) { + return; + } - auto d = new PasteDialog (this, static_cast(_clipboard->video), static_cast(_clipboard->audio), !_clipboard->text.empty()); - if (d->ShowModal() == wxID_OK) { - for (auto i: _film_editor->content_panel()->selected()) { - if (d->video() && i->video) { - DCPOMATIC_ASSERT (_clipboard->video); - i->video->take_settings_from (_clipboard->video); - } - if (d->audio() && i->audio) { - DCPOMATIC_ASSERT (_clipboard->audio); - i->audio->take_settings_from (_clipboard->audio); - } + PasteDialog dialog(this, static_cast(_clipboard->video), static_cast(_clipboard->audio), !_clipboard->text.empty()); + if (dialog.ShowModal() != wxID_OK) { + return; + } - if (d->text()) { - auto j = i->text.begin (); - auto k = _clipboard->text.begin (); - while (j != i->text.end() && k != _clipboard->text.end()) { - (*j)->take_settings_from (*k); - ++j; - ++k; - } + for (auto i: _film_editor->content_panel()->selected()) { + if (dialog.video() && i->video) { + DCPOMATIC_ASSERT (_clipboard->video); + i->video->take_settings_from (_clipboard->video); + } + if (dialog.audio() && i->audio) { + DCPOMATIC_ASSERT (_clipboard->audio); + i->audio->take_settings_from (_clipboard->audio); + } + + if (dialog.text()) { + auto j = i->text.begin (); + auto k = _clipboard->text.begin (); + while (j != i->text.end() && k != _clipboard->text.end()) { + (*j)->take_settings_from (*k); + ++j; + ++k; } } } - d->Destroy (); } void edit_select_all () @@ -732,21 +746,48 @@ private: void tools_restore_default_preferences () { - auto d = new wxMessageDialog ( - 0, + wxMessageDialog dialog( + nullptr, _("Are you sure you want to restore preferences to their defaults? This cannot be undone."), _("Restore default preferences"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION ); - int const r = d->ShowModal (); - d->Destroy (); - - if (r == wxID_YES) { + if (dialog.ShowModal() == wxID_YES) { Config::restore_defaults (); } } + void tools_export_preferences () + { + FileDialog dialog( + this, _("Specify ZIP file"), wxT("ZIP files (*.zip)|*.zip"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT, "Preferences", string("dcpomatic_config.zip") + ); + + if (dialog.ShowModal() == wxID_OK) { + auto const path = boost::filesystem::path(wx_to_std(dialog.GetPath())); + if (boost::filesystem::exists(path)) { + boost::system::error_code ec; + boost::filesystem::remove(path, ec); + if (ec) { + error_dialog(nullptr, _("Could not remove existing preferences file"), std_to_wx(path.string())); + return; + } + } + + save_all_config_as_zip(path); + } + } + + void tools_import_preferences() + { + FileDialog dialog(this, _("Specify ZIP file"), wxT("ZIP files (*.zip)|*.zip"), wxFD_OPEN, "Preferences"); + + if (dialog.show()) { + Config::instance()->load_from_zip(dialog.path()); + } + } + void jobs_make_dcp () { double required; @@ -766,10 +807,8 @@ private: } if (Config::instance()->show_hints_before_make_dcp()) { - auto hints = new HintsDialog (this, _film, false); - int const r = hints->ShowModal(); - hints->Destroy (); - if (r == wxID_CANCEL) { + HintsDialog hints(this, _film, false); + if (hints.ShowModal() == wxID_CANCEL) { return; } } @@ -787,11 +826,11 @@ private: /* Remove any existing DCP if the user agrees */ auto const dcp_dir = _film->dir (_film->dcp_name(), false); - if (boost::filesystem::exists(dcp_dir)) { + if (dcp::filesystem::exists(dcp_dir)) { if (!confirm_dialog (this, wxString::Format (_("Do you want to overwrite the existing DCP %s?"), std_to_wx(dcp_dir.string()).data()))) { return; } - boost::filesystem::remove_all (dcp_dir); + dcp::filesystem::remove_all(dcp_dir); } try { @@ -799,7 +838,7 @@ private: a long time, and crashes/power failures are moderately likely. */ _film->write_metadata (); - _film->make_dcp (TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); + make_dcp (_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); } catch (BadSettingError& e) { error_dialog (this, wxString::Format (_("Bad setting for %s."), std_to_wx(e.setting()).data()), std_to_wx(e.what())); } catch (std::exception& e) { @@ -813,12 +852,7 @@ private: return; } - if (_kdm_dialog) { - _kdm_dialog->Destroy (); - _kdm_dialog = 0; - } - - _kdm_dialog = new KDMDialog (this, _film); + _kdm_dialog.reset(this, _film); _kdm_dialog->Show (); } @@ -828,12 +862,7 @@ private: return; } - if (_dkdm_dialog) { - _dkdm_dialog->Destroy (); - _dkdm_dialog = nullptr; - } - - _dkdm_dialog = new DKDMDialog (this, _film); + _dkdm_dialog.reset(this, _film); _dkdm_dialog->Show (); } @@ -880,10 +909,8 @@ private: } if (Config::instance()->show_hints_before_make_dcp()) { - auto hints = new HintsDialog (this, _film, false); - int const r = hints->ShowModal(); - hints->Destroy (); - if (r == wxID_CANCEL) { + HintsDialog hints(this, _film, false); + if (hints.ShowModal() == wxID_CANCEL) { return; } } @@ -920,9 +947,8 @@ private: return; } - auto d = new SelfDKDMDialog (this, _film); - if (d->ShowModal () != wxID_OK) { - d->Destroy (); + SelfDKDMDialog dialog(this, _film); + if (dialog.ShowModal() != wxID_OK) { return; } @@ -943,17 +969,24 @@ private: dcp::LocalTime to (Config::instance()->signer_chain()->leaf().not_after()); to.add_days (-1); + auto signer = Config::instance()->signer_chain(); + if (!signer->valid()) { + error_dialog(this, _("The certificate chain for signing is invalid")); + return; + } + optional kdm; try { - kdm = _film->make_kdm ( - Config::instance()->decryption_chain()->leaf(), - vector(), - d->cpl (), - from, to, - dcp::Formulation::MODIFIED_TRANSITIONAL_1, - true, - 0 - ); + auto const decrypted_kdm = _film->make_kdm(dialog.cpl(), from, to); + auto const kdm = decrypted_kdm.encrypt(signer, Config::instance()->decryption_chain()->leaf(), {}, dcp::Formulation::MODIFIED_TRANSITIONAL_1, true, 0); + if (dialog.internal()) { + auto dkdms = Config::instance()->dkdms(); + dkdms->add(make_shared(kdm)); + Config::instance()->changed (); + } else { + auto path = dialog.directory() / (_film->dcp_name(false) + "_DKDM.xml"); + kdm.as_xml(path); + } } catch (dcp::NotEncryptedError& e) { error_dialog (this, _("CPL's content is not encrypted.")); } catch (exception& e) { @@ -961,60 +994,47 @@ private: } catch (...) { error_dialog (this, _("An unknown exception occurred.")); } - - if (kdm) { - if (d->internal ()) { - auto dkdms = Config::instance()->dkdms(); - dkdms->add (make_shared(kdm.get())); - Config::instance()->changed (); - } else { - auto path = d->directory() / (_film->dcp_name(false) + "_DKDM.xml"); - kdm->as_xml (path); - } - } - - d->Destroy (); } void jobs_export_video_file () { - auto d = new ExportVideoFileDialog (this, _film->isdcf_name(true)); - if (d->ShowModal() == wxID_OK) { - if (boost::filesystem::exists(d->path())) { - bool ok = confirm_dialog( - this, - wxString::Format (_("File %s already exists. Do you want to overwrite it?"), std_to_wx(d->path().string()).data()) - ); + ExportVideoFileDialog dialog(this, _film->isdcf_name(true)); + if (dialog.ShowModal() != wxID_OK) { + return; + } - if (!ok) { - d->Destroy (); - return; - } - } + if (dcp::filesystem::exists(dialog.path())) { + bool ok = confirm_dialog( + this, + wxString::Format(_("File %s already exists. Do you want to overwrite it?"), std_to_wx(dialog.path().string()).data()) + ); - auto job = make_shared(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); - job->set_encoder ( - make_shared ( - _film, job, d->path(), d->format(), d->mixdown_to_stereo(), d->split_reels(), d->split_streams(), d->x264_crf()) - ); - JobManager::instance()->add (job); + if (!ok) { + return; + } } - d->Destroy (); + + auto job = make_shared(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); + job->set_encoder ( + make_shared ( + _film, job, dialog.path(), dialog.format(), dialog.mixdown_to_stereo(), dialog.split_reels(), dialog.split_streams(), dialog.x264_crf()) + ); + JobManager::instance()->add (job); } void jobs_export_subtitles () { - auto d = new ExportSubtitlesDialog (this, _film->reels().size(), _film->interop()); - if (d->ShowModal() == wxID_OK) { - auto job = make_shared(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); - job->set_encoder ( - make_shared(_film, job, d->path(), _film->isdcf_name(true), d->split_reels(), d->include_font()) - ); - JobManager::instance()->add (job); + ExportSubtitlesDialog dialog(this, _film->reels().size(), _film->interop()); + if (dialog.ShowModal() != wxID_OK) { + return; } - d->Destroy (); + auto job = make_shared(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP); + job->set_encoder( + make_shared(_film, job, dialog.path(), _film->isdcf_name(true), dialog.split_reels(), dialog.include_font()) + ); + JobManager::instance()->add(job); } @@ -1033,13 +1053,13 @@ private: void view_closed_captions () { - _film_viewer->show_closed_captions (); + _film_viewer.show_closed_captions (); } void view_video_waveform () { if (!_video_waveform_dialog) { - _video_waveform_dialog = new VideoWaveformDialog (this, _film, _film_viewer); + _video_waveform_dialog.reset(this, _film, _film_viewer); } _video_waveform_dialog->Show (); @@ -1075,7 +1095,7 @@ private: void tools_manage_templates () { if (!_templates_dialog) { - _templates_dialog = new TemplatesDialog (this); + _templates_dialog.reset(this); } _templates_dialog->Show (); @@ -1089,48 +1109,45 @@ private: void tools_send_translations () { - auto d = new SendI18NDialog (this); - if (d->ShowModal() == wxID_OK) { - string body; - body += d->name() + "\n"; - body += d->language() + "\n"; - body += string(dcpomatic_version) + " " + string(dcpomatic_git_commit) + "\n"; - body += "--\n"; - auto translations = I18NHook::translations (); - for (auto i: translations) { - body += i.first + "\n" + i.second + "\n\n"; - } - list to = { "carl@dcpomatic.com" }; - if (d->email().find("@") == string::npos) { - error_dialog (this, _("You must enter a valid email address when sending translations, " - "otherwise the DCP-o-matic maintainers cannot credit you or contact you with questions.")); - } else { - Emailer emailer (d->email(), to, "DCP-o-matic translations", body); - try { - emailer.send ("main.carlh.net", 2525, EmailProtocol::STARTTLS); - } catch (NetworkError& e) { - error_dialog (this, _("Could not send translations"), std_to_wx(e.what())); - } - } + SendI18NDialog dialog(this); + if (dialog.ShowModal() != wxID_OK) { + return; } - d->Destroy (); + string body; + body += dialog.name() + "\n"; + body += dialog.language() + "\n"; + body += string(dcpomatic_version) + " " + string(dcpomatic_git_commit) + "\n"; + body += "--\n"; + auto translations = I18NHook::translations (); + for (auto i: translations) { + body += i.first + "\n" + i.second + "\n\n"; + } + if (dialog.email().find("@") == string::npos) { + error_dialog (this, _("You must enter a valid email address when sending translations, " + "otherwise the DCP-o-matic maintainers cannot credit you or contact you with questions.")); + } else { + Email email(dialog.email(), { "carl@dcpomatic.com" }, "DCP-o-matic translations", body); + try { + email.send("main.carlh.net", 2525, EmailProtocol::STARTTLS); + } catch (NetworkError& e) { + error_dialog (this, _("Could not send translations"), std_to_wx(e.what())); + } + } } void help_about () { - auto d = new AboutDialog (this); - d->ShowModal (); - d->Destroy (); + AboutDialog dialog(this); + dialog.ShowModal(); } void help_report_a_problem () { - auto d = new ReportProblemDialog (this, _film); - if (d->ShowModal () == wxID_OK) { - d->report (); + ReportProblemDialog dialog(this, _film); + if (dialog.ShowModal() == wxID_OK) { + dialog.report(); } - d->Destroy (); } bool should_close () @@ -1139,16 +1156,14 @@ private: return true; } - auto d = new wxMessageDialog ( - 0, + wxMessageDialog dialog( + nullptr, _("There are unfinished jobs; are you sure you want to quit?"), _("Unfinished jobs"), wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION ); - bool const r = d->ShowModal() == wxID_YES; - d->Destroy (); - return r; + return dialog.ShowModal() == wxID_YES; } void close (wxCloseEvent& ev) @@ -1159,12 +1174,8 @@ private: } if (_film && _film->dirty ()) { - - auto dialog = new FilmChangedClosingDialog (_film->name ()); - int const r = dialog->run (); - delete dialog; - - switch (r) { + FilmChangedClosingDialog dialog(_film->name()); + switch (dialog.run()) { case wxID_NO: /* Don't save and carry on to close */ break; @@ -1188,9 +1199,20 @@ private: /* Also stop hearing about analytics-related stuff */ _analytics_message_connection.disconnect (); + FontConfig::drop(); + ev.Skip (); } + void active_jobs_changed() + { + /* ActiveJobsChanged can be called while JobManager holds a lock on its mutex, making + * the call to JobManager::get() in set_menu_sensitivity() deadlock unless we work around + * it by using an idle callback. This feels quite unpleasant. + */ + signal_manager->when_idle(boost::bind(&DOMFrame::set_menu_sensitivity, this)); + } + void set_menu_sensitivity () { auto jobs = JobManager::instance()->get (); @@ -1254,14 +1276,20 @@ private: return true; } - if (_film->dirty ()) { - T d (_film->name ()); - switch (d.run ()) { + while (_film->dirty()) { + T dialog(_film->name()); + switch (dialog.run()) { case wxID_NO: return true; case wxID_YES: - _film->write_metadata (); - return true; + try { + _film->write_metadata(); + return true; + } catch (exception& e) { + error_dialog(this, _("Could not save project."), std_to_wx(e.what())); + /* Go round again for another try */ + } + break; case wxID_CANCEL: return false; } @@ -1327,7 +1355,7 @@ private: add_item (edit, _("Select all\tShift-Ctrl-A"), ID_edit_select_all, NEEDS_FILM); #ifdef __WXOSX__ - add_item (_file_menu, _("&Preferences...\tCtrl-P"), wxID_PREFERENCES, ALWAYS); + add_item(_file_menu, _("&Preferences...\tCtrl-,"), wxID_PREFERENCES, ALWAYS); #else edit->AppendSeparator (); /* [Shortcut] Ctrl+P:Open preferences window */ @@ -1375,6 +1403,9 @@ private: add_item (tools, _("System information..."), ID_tools_system_information, 0); tools->AppendSeparator (); add_item (tools, _("Restore default preferences"), ID_tools_restore_default_preferences, ALWAYS); + tools->AppendSeparator (); + add_item (tools, _("Export preferences..."), ID_tools_export_preferences, ALWAYS); + add_item (tools, _("Import preferences..."), ID_tools_import_preferences, ALWAYS); wxMenu* help = new wxMenu; #ifdef __WXOSX__ @@ -1395,7 +1426,8 @@ private: void config_changed (Config::Property what) { /* Instantly save any config changes when using the DCP-o-matic GUI */ - if (what == Config::CINEMAS) { + switch (what) { + case Config::CINEMAS: try { Config::instance()->write_cinemas(); } catch (exception& e) { @@ -1407,7 +1439,21 @@ private: ) ); } - } else { + break; + case Config::DKDM_RECIPIENTS: + try { + Config::instance()->write_dkdm_recipients(); + } catch (exception& e) { + error_dialog ( + this, + wxString::Format ( + _("Could not write to DKDM recipients file at %s. Your changes have not been saved."), + std_to_wx(Config::instance()->dkdm_recipients_file().string()).data() + ) + ); + } + break; + default: try { Config::instance()->write_config(); } catch (exception& e) { @@ -1433,7 +1479,7 @@ private: int pos = _history_position; - /* Clear out non-existant history items before we re-build the menu */ + /* Clear out non-existent history items before we re-build the menu */ Config::instance()->clean_history (); auto history = Config::instance()->history(); @@ -1472,9 +1518,8 @@ private: } if (uc->state() == UpdateChecker::State::YES) { - auto dialog = new UpdateDialog (this, uc->stable(), uc->test()); - dialog->ShowModal (); - dialog->Destroy (); + UpdateDialog dialog(this, uc->stable(), uc->test()); + dialog.ShowModal(); } else if (uc->state() == UpdateChecker::State::FAILED) { error_dialog (this, _("The DCP-o-matic download server could not be contacted.")); } else { @@ -1486,10 +1531,10 @@ private: void start_stop_pressed () { - if (_film_viewer->playing()) { - _film_viewer->stop(); + if (_film_viewer.playing()) { + _film_viewer.stop(); } else { - _film_viewer->start(); + _film_viewer.start(); } } @@ -1500,32 +1545,48 @@ private: void back_frame () { - _film_viewer->seek_by (-_film_viewer->one_video_frame(), true); + _film_viewer.seek_by(-_film_viewer.one_video_frame(), true); } void forward_frame () { - _film_viewer->seek_by (_film_viewer->one_video_frame(), true); + _film_viewer.seek_by(_film_viewer.one_video_frame(), true); } void analytics_message (string title, string html) { - auto d = new HTMLDialog(this, std_to_wx(title), std_to_wx(html)); - d->ShowModal(); - d->Destroy(); + HTMLDialog dialog(this, std_to_wx(title), std_to_wx(html)); + dialog.ShowModal(); + } + + void set_title () + { + auto s = wx_to_std(_("DCP-o-matic")); + if (_film) { + if (_film->directory()) { + s += " - " + _film->directory()->string(); + } + if (_film->dirty()) { + s += " *"; + } + } + + SetTitle (std_to_wx(s)); } FilmEditor* _film_editor; - std::shared_ptr _film_viewer; + LimitedFrameSplitter* _splitter; + wxPanel* _right_panel; + FilmViewer _film_viewer; StandardControls* _controls; - VideoWaveformDialog* _video_waveform_dialog = nullptr; + wx_ptr _video_waveform_dialog; SystemInformationDialog* _system_information_dialog = nullptr; HintsDialog* _hints_dialog = nullptr; ServersListDialog* _servers_list_dialog = nullptr; wxPreferencesEditor* _config_dialog = nullptr; - KDMDialog* _kdm_dialog = nullptr; - DKDMDialog* _dkdm_dialog = nullptr; - TemplatesDialog* _templates_dialog = nullptr; + wx_ptr _kdm_dialog; + wx_ptr _dkdm_dialog; + wx_ptr _templates_dialog; wxMenu* _file_menu = nullptr; shared_ptr _film; int _history_items = 0; @@ -1559,6 +1620,7 @@ public: App () : wxApp () { + dcpomatic_setup_path_encoding (); #ifdef DCPOMATIC_LINUX XInitThreads (); #endif @@ -1566,12 +1628,32 @@ public: private: - bool OnInit () + bool OnInit () override { try { + +#if defined(DCPOMATIC_WINDOWS) + if (Config::instance()->win32_console()) { + AllocConsole(); + + HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE); + int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT); + FILE* hf_out = _fdopen(hCrt, "w"); + setvbuf(hf_out, NULL, _IONBF, 1); + *stdout = *hf_out; + + HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE); + hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT); + FILE* hf_in = _fdopen(hCrt, "r"); + setvbuf(hf_in, NULL, _IONBF, 128); + *stdin = *hf_in; + + cout << "DCP-o-matic is starting." << "\n"; + } +#endif wxInitAllImageHandlers (); - Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this)); + Config::FailedToLoad.connect(boost::bind(&App::config_failed_to_load, this, _1)); Config::Warning.connect (boost::bind (&App::config_warning, this, _1)); _splash = maybe_show_splash (); @@ -1591,8 +1673,6 @@ private: make_foreground_application (); #endif - dcpomatic_setup_path_encoding (); - /* Enable i18n; this will create a Config object to look for a force-configured language. This Config object will be wrong, however, because dcpomatic_setup @@ -1634,7 +1714,7 @@ private: signal_manager = new wxSignalManager (this); Bind (wxEVT_IDLE, boost::bind (&App::idle, this, _1)); - if (!_film_to_load.empty() && boost::filesystem::is_directory(_film_to_load)) { + if (!_film_to_load.empty() && dcp::filesystem::is_directory(_film_to_load)) { try { _frame->load_film (_film_to_load); } catch (exception& e) { @@ -1661,26 +1741,30 @@ private: if (Config::instance()->check_for_updates ()) { UpdateChecker::instance()->run (); } + + auto release_notes = find_release_notes(gui_is_dark()); + if (release_notes) { + HTMLDialog notes(nullptr, _("Release notes"), std_to_wx(*release_notes), true); + notes.Centre(); + notes.ShowModal(); + } } catch (exception& e) { - if (_splash) { - _splash->Destroy (); - _splash = nullptr; - } + close_splash(); error_dialog (nullptr, wxString::Format ("DCP-o-matic could not start."), std_to_wx(e.what())); } return true; } - void OnInitCmdLine (wxCmdLineParser& parser) + void OnInitCmdLine (wxCmdLineParser& parser) override { parser.SetDesc (command_line_description); parser.SetSwitchChars (wxT ("-")); } - bool OnCmdLineParsed (wxCmdLineParser& parser) + bool OnCmdLineParsed (wxCmdLineParser& parser) override { if (parser.Found (wxT("version"))) { cout << "dcpomatic version " << dcpomatic_version << " " << dcpomatic_git_commit << "\n"; @@ -1750,14 +1834,14 @@ private: } /* An unhandled exception has occurred inside the main event loop */ - bool OnExceptionInMainLoop () + bool OnExceptionInMainLoop () override { report_exception (); /* This will terminate the program */ return false; } - void OnUnhandledException () + void OnUnhandledException () override { report_exception (); } @@ -1780,14 +1864,14 @@ private: void close_splash () { if (_splash) { - _splash->Destroy (); - _splash = 0; + _splash->Destroy(); + _splash = nullptr; } } - void config_failed_to_load () + void config_failed_to_load (Config::LoadFailure what) { - message_dialog (_frame, _("The existing configuration failed to load. Default values will be used instead. These may take a short time to create.")); + report_config_load_failure(_frame, what); } void config_warning (string m) @@ -1800,8 +1884,7 @@ private: /* Destroy the splash screen here, as otherwise bad things seem to happen (for reasons unknown) when we open our recreate dialog, close it, *then* try to Destroy the splash (the Destroy fails). */ - _splash->Destroy (); - _splash = nullptr; + close_splash(); auto config = Config::instance(); switch (reason) { @@ -1810,7 +1893,7 @@ private: if (config->nagged(Config::NAG_BAD_SIGNER_CHAIN_UTF8)) { return false; } - auto d = new RecreateChainDialog ( + RecreateChainDialog dialog( _frame, _("Recreate signing certificates"), _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs contains a small error\n" "which will prevent DCPs from being validated correctly on some systems. Do you want to re-create\n" @@ -1818,16 +1901,14 @@ private: _("Do nothing"), Config::NAG_BAD_SIGNER_CHAIN_UTF8 ); - int const r = d->ShowModal (); - d->Destroy (); - return r == wxID_OK; + return dialog.ShowModal() == wxID_OK; } case Config::BAD_SIGNER_VALIDITY_TOO_LONG: { if (config->nagged(Config::NAG_BAD_SIGNER_CHAIN_VALIDITY)) { return false; } - auto d = new RecreateChainDialog ( + RecreateChainDialog dialog( _frame, _("Recreate signing certificates"), _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs has a validity period\n" "that is too long. This will cause problems playing back DCPs on some systems.\n" @@ -1835,29 +1916,25 @@ private: _("Do nothing"), Config::NAG_BAD_SIGNER_CHAIN_VALIDITY ); - int const r = d->ShowModal (); - d->Destroy (); - return r == wxID_OK; + return dialog.ShowModal() == wxID_OK; } case Config::BAD_SIGNER_INCONSISTENT: { - auto d = new RecreateChainDialog ( + RecreateChainDialog dialog( _frame, _("Recreate signing certificates"), _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs is inconsistent and\n" "cannot be used. DCP-o-matic cannot start unless you re-create it. Do you want to re-create\n" "the certificate chain for signing DCPs and KDMs?"), _("Close DCP-o-matic") ); - int const r = d->ShowModal (); - d->Destroy (); - if (r != wxID_OK) { + if (dialog.ShowModal() != wxID_OK) { exit (EXIT_FAILURE); } return true; } case Config::BAD_DECRYPTION_INCONSISTENT: { - auto d = new RecreateChainDialog ( + RecreateChainDialog dialog( _frame, _("Recreate KDM decryption chain"), _("The certificate chain that DCP-o-matic uses for decrypting KDMs is inconsistent and\n" "cannot be used. DCP-o-matic cannot start unless you re-create it. Do you want to re-create\n" @@ -1865,13 +1942,24 @@ private: "configuration before continuing."), _("Close DCP-o-matic") ); - int const r = d->ShowModal (); - d->Destroy (); - if (r != wxID_OK) { + if (dialog.ShowModal() != wxID_OK) { exit (EXIT_FAILURE); } return true; } + case Config::BAD_SIGNER_DN_QUALIFIER: + { + RecreateChainDialog dialog( + _frame, _("Recreate signing certificates"), + _("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs contains a small error\n" + "which will prevent DCPs from being validated correctly on some systems. This error was caused\n" + "by a bug in DCP-o-matic which has now been fixed. Do you want to re-create the certificate chain\n" + "for signing DCPs and KDMs?"), + _("Do nothing"), + Config::NAG_BAD_SIGNER_DN_QUALIFIER + ); + return dialog.ShowModal() == wxID_OK; + } default: DCPOMATIC_ASSERT (false); }