/*
- Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2022 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "wx/about_dialog.h"
#include "wx/content_panel.h"
+#ifdef DCPOMATIC_CUCUMBER
+#include "wx/cucumber_bridge.h"
+#include "wx/cucumber_registry.h"
+#endif
#include "wx/dkdm_dialog.h"
#include "wx/export_subtitles_dialog.h"
#include "wx/export_video_file_dialog.h"
#include "lib/cinema.h"
#include "lib/compose.hpp"
#include "lib/config.h"
+#include "lib/constants.h"
#include "lib/content.h"
#include "lib/content_factory.h"
#include "lib/cross.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/text_content.h"
#include "lib/transcode_job.h"
#include "lib/update_checker.h"
-#include "lib/util.h"
#include "lib/version.h"
#include "lib/video_content.h"
#include <dcp/exceptions.h>
+#include <dcp/filesystem.h>
#include <dcp/raw_convert.h>
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
LIBDCP_ENABLE_WARNINGS
#ifdef __WXGTK__
#include <X11/Xlib.h>
+extern "C" {
+#include <xdo.h>
+}
#endif
#ifdef __WXMSW__
#include <shellapi.h>
{
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;
};
{
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;
};
};
+class LimitedFrameSplitter : public wxSplitterWindow
+{
+public:
+ LimitedFrameSplitter(wxWindow* parent)
+ : wxSplitterWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_3DSASH | wxSP_LIVE_UPDATE)
+ {
+ /* 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);
+
+ Bind(wxEVT_SIZE, boost::bind(&LimitedFrameSplitter::sized, this, _1));
+ }
+
+ 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;
+ }
+
+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));
+ }
+
+ ev.Skip();
+ }
+
+ int const _left_panel_minimum_size = 200;
+};
+
+
class DOMFrame : public wxFrame
{
public:
/* Use a panel as the only child of the Frame so that we avoid
the dark-grey background on Windows.
*/
- , _overall_panel(new wxPanel(this, wxID_ANY))
- , _film_viewer(_overall_panel)
+ , _splitter(new LimitedFrameSplitter(this))
+ , _right_panel(new wxPanel(_splitter, wxID_ANY))
+ , _film_viewer(_right_panel)
{
-#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
auto bar = new wxMenuBar;
setup_menu (bar);
Bind (wxEVT_CLOSE_WINDOW, boost::bind (&DOMFrame::close, this, _1));
Bind (wxEVT_SHOW, boost::bind (&DOMFrame::show, this, _1));
- _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);
+ 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);
+
+ _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 (_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 ();
JobManager::instance()->ActiveJobsChanged.connect(boost::bind(&DOMFrame::active_jobs_changed, this));
- _overall_panel->SetSizer(main_sizer);
-
UpdateChecker::instance()->StateChanged.connect(boost::bind(&DOMFrame::update_checker_state_changed, this));
FocusManager::instance()->SetFocus.connect (boost::bind (&DOMFrame::remove_accelerators, this));
}
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.")
_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());
return _film;
}
+#ifdef DCPOMATIC_CUCUMBER
+ void cucumber_add_content_file (string filename) {
+ std::cout << ".cucumber_add_content_file\n";
+ _film_editor->cucumber_add_content_file (filename);
+ }
+
+ /* Could be called from any thread */
+ std::string cucumber_get_content_list () {
+ std::cout << ".cucumber_get_content_list\n";
+ return _film_editor->cucumber_get_content_list();
+ }
+#endif
+
private:
void show (wxShowEvent& ev)
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<FilmChangedClosingDialog>()) {
- 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<FilmChangedClosingDialog>()) {
+ 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];
- }
- }
- 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.");
+ 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];
}
- 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 ()),
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;
}
if (r == wxID_OK && maybe_save_then_delete_film<FilmChangedClosingDialog>()) {
- 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<FilmChangedDuplicatingDialog>()) {
- shared_ptr<Film> film (new Film (d->path()));
+ if (dialog.ShowModal() == wxID_OK && dialog.check_path() && maybe_save_film<FilmChangedDuplicatingDialog>()) {
+ auto film = make_shared<Film>(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<FilmChangedDuplicatingDialog>()) {
- shared_ptr<Film> film (new Film (d->path()));
+ if (dialog.ShowModal() == wxID_OK && dialog.check_path() && maybe_save_film<FilmChangedDuplicatingDialog>()) {
+ auto film = make_shared<Film>(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;
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<bool>(_clipboard->video), static_cast<bool>(_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<bool>(_clipboard->video), static_cast<bool>(_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 ()
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 ()
{
- auto dialog = new wxFileDialog (
+ wxFileDialog dialog(
this, _("Specify ZIP file"), wxEmptyString, wxT("dcpomatic_config.zip"), wxT("ZIP files (*.zip)|*.zip"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
- int const r = dialog->ShowModal ();
- dialog->Destroy ();
-
- if (r == wxID_OK) {
- save_all_config_as_zip (wx_to_std(dialog->GetPath()));
+ if (dialog.ShowModal() == wxID_OK) {
+ save_all_config_as_zip(wx_to_std(dialog.GetPath()));
}
}
}
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;
}
}
/* 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 {
return;
}
- if (_kdm_dialog) {
- _kdm_dialog->Destroy ();
- _kdm_dialog = 0;
- }
-
- _kdm_dialog = new KDMDialog (this, _film);
+ _kdm_dialog.reset(this, _film);
_kdm_dialog->Show ();
}
return;
}
- if (_dkdm_dialog) {
- _dkdm_dialog->Destroy ();
- _dkdm_dialog = nullptr;
- }
-
- _dkdm_dialog = new DKDMDialog (this, _film);
+ _dkdm_dialog.reset(this, _film);
_dkdm_dialog->Show ();
}
}
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;
}
}
return;
}
- auto d = new SelfDKDMDialog (this, _film);
- if (d->ShowModal () != wxID_OK) {
- d->Destroy ();
+ SelfDKDMDialog dialog(this, _film);
+ if (dialog.ShowModal() != wxID_OK) {
return;
}
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<dcp::EncryptedKDM> kdm;
try {
- kdm = _film->make_kdm (
- Config::instance()->decryption_chain()->leaf(),
- vector<string>(),
- 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<DKDM>(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) {
} catch (...) {
error_dialog (this, _("An unknown exception occurred."));
}
-
- if (kdm) {
- if (d->internal ()) {
- auto dkdms = Config::instance()->dkdms();
- dkdms->add (make_shared<DKDM>(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())
- );
-
- if (!ok) {
- d->Destroy ();
- return;
- }
- }
+ ExportVideoFileDialog dialog(this, _film->isdcf_name(true));
+ if (dialog.ShowModal() != wxID_OK) {
+ return;
+ }
- auto job = make_shared<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP);
- job->set_encoder (
- make_shared<FFmpegEncoder> (
- _film, job, d->path(), d->format(), d->mixdown_to_stereo(), d->split_reels(), d->split_streams(), d->x264_crf())
- );
- JobManager::instance()->add (job);
+ 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())
+ );
+
+ if (!ok) {
+ return;
+ }
}
- d->Destroy ();
+
+ auto job = make_shared<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP);
+ job->set_encoder (
+ make_shared<FFmpegEncoder> (
+ _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<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP);
- job->set_encoder (
- make_shared<SubtitleEncoder>(_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<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP);
+ job->set_encoder(
+ make_shared<SubtitleEncoder>(_film, job, dialog.path(), _film->isdcf_name(true), dialog.split_reels(), dialog.include_font())
+ );
+ JobManager::instance()->add(job);
}
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 ();
void tools_manage_templates ()
{
if (!_templates_dialog) {
- _templates_dialog = new TemplatesDialog (this);
+ _templates_dialog.reset(this);
}
_templates_dialog->Show ();
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<string> 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 {
+ Emailer emailer(dialog.email(), { "carl@dcpomatic.com" }, "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()));
+ }
+ }
}
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 ()
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)
}
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;
/* Also stop hearing about analytics-related stuff */
_analytics_message_connection.disconnect ();
+ FontConfig::drop();
+
ev.Skip ();
}
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;
}
}
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 {
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 ()
}
FilmEditor* _film_editor;
- wxPanel* _overall_panel;
+ LimitedFrameSplitter* _splitter;
+ wxPanel* _right_panel;
FilmViewer _film_viewer;
StandardControls* _controls;
- VideoWaveformDialog* _video_waveform_dialog = nullptr;
+ wx_ptr<VideoWaveformDialog> _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<KDMDialog> _kdm_dialog;
+ wx_ptr<DKDMDialog> _dkdm_dialog;
+ wx_ptr<TemplatesDialog> _templates_dialog;
wxMenu* _file_menu = nullptr;
shared_ptr<Film> _film;
int _history_items = 0;
{ wxCMD_LINE_OPTION, "c", "content", "add content file / directory", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_OPTION, "d", "dcp", "add content DCP", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_SWITCH, "v", "version", "show DCP-o-matic version", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+#ifdef DCPOMATIC_CUCUMBER
+ { wxCMD_LINE_SWITCH, "t", "cucumber", "enable cucumber tests", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+#endif
{ wxCMD_LINE_OPTION, "", "config", "directory containing config.xml and cinemas.xml", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_PARAM, 0, 0, "film to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
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, _1));
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) {
UpdateChecker::instance()->run ();
}
- auto release_notes = find_release_notes();
+ auto release_notes = find_release_notes(gui_is_dark());
if (release_notes) {
- auto notes = new HTMLDialog(nullptr, _("Release notes"), std_to_wx(*release_notes), true);
- notes->Centre();
- notes->ShowModal();
- notes->Destroy();
+ HTMLDialog notes(nullptr, _("Release notes"), std_to_wx(*release_notes), true);
+ notes.Centre();
+ notes.ShowModal();
+ }
+
+#ifdef DCPOMATIC_CUCUMBER
+ if (_cucumber_bridge) {
+ _cucumber_bridge->start();
}
+#endif
}
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) override
+ int FilterEvent(wxEvent& event) override
+ {
+ auto key = dynamic_cast<wxKeyEvent*>(&event);
+ if (key && key->GetEventType() == wxEVT_CHAR) {
+ std::cout << "CHAR: " << key->m_keyCode << " " << key->m_uniChar << " " << key->m_rawCode << " " << key->m_rawFlags << "\n";
+ } else if (key && key->GetEventType() == wxEVT_KEY_UP) {
+ std::cout << "KEY_UP: " << key->m_keyCode << " " << key->m_uniChar << " " << key->m_rawCode << " " << key->m_rawFlags << "\n";
+ } else if (key && key->GetEventType() == wxEVT_KEY_DOWN) {
+ std::cout << "KEY_DOWN: " << key->m_keyCode << " " << key->m_uniChar << " " << key->m_rawCode << " " << key->m_rawFlags << "\n";
+ }
+ return -1;
+ }
+
+ void OnInitCmdLine(wxCmdLineParser& parser) override
{
parser.SetDesc (command_line_description);
parser.SetSwitchChars (wxT ("-"));
State::override_path = wx_to_std (config);
}
+#ifdef DCPOMATIC_CUCUMBER
+ if (parser.Found(wxT("cucumber"))) {
+ _cucumber_bridge = new CucumberBridge ();
+ _cucumber_bridge->ClickButton.connect (bind(&App::cucumber_click_button, this, _1));
+ _cucumber_bridge->SelectMenu.connect (bind(&App::cucumber_select_menu, this, _1));
+ _cucumber_bridge->Type.connect (bind(&App::cucumber_type, this, _1));
+ _cucumber_bridge->AddContentFile.connect (bind(&App::cucumber_add_content_file, this, _1));
+ _cucumber_bridge->GetContentList.connect (bind(&App::cucumber_get_content_list, this));
+ }
+#endif
+
return true;
}
+
+#ifdef DCPOMATIC_CUCUMBER
+ void cucumber_add_content_file (string filename)
+ {
+ _frame->cucumber_add_content_file (filename);
+ }
+
+
+ std::string cucumber_get_content_list ()
+ {
+ return _frame->cucumber_get_content_list ();
+ }
+
+
+ void cucumber_click_button (string id)
+ {
+ CucumberRegistry::instance()->click_button (id);
+ }
+
+
+ void cucumber_select_menu (string id)
+ {
+ if (id == CUCUMBER_MENU_FILE_NEW) {
+ wxPostEvent (_frame, wxMenuEvent(wxEVT_MENU, ID_file_new));
+ }
+ }
+
+
+ void cucumber_type (string text)
+ {
+#ifdef DCPOMATIC_LINUX
+ // xdo_t* xdo = xdo_new (NULL);
+ // xdo_enter_text_window (xdo, CURRENTWINDOW, text.c_str(), 200000);
+ // xdo_send_keysequence_window (xdo, CURRENTWINDOW, "Return", 200000);
+ // xdo_free (xdo);
+ auto send = [this](int code, int rawcode) {
+ auto event = wxKeyEvent(wxEVT_KEY_DOWN);
+ event.SetEventObject(this);
+ event.m_keyCode = event.m_uniChar = code;
+ event.m_rawCode = rawcode;
+ std::cout << "process " << code << " " << rawcode << "\n";
+ ProcessEvent (event);
+
+ event = wxKeyEvent(wxEVT_CHAR);
+ event.SetEventObject(this);
+ event.m_keyCode = event.m_uniChar = code;
+ event.m_rawCode = rawcode;
+ std::cout << "process " << code << " " << rawcode << "\n";
+ ProcessEvent (event);
+
+ event = wxKeyEvent(wxEVT_KEY_UP);
+ event.SetEventObject(this);
+ event.m_keyCode = event.m_uniChar = code;
+ event.m_rawCode = rawcode;
+ std::cout << "process " << code << " " << rawcode << "\n";
+ ProcessEvent (event);
+ };
+
+ for (auto i = 0U; i < text.length(); ++i) {
+ send (text[i], text[i]);
+ }
+ send (WXK_RETURN, 65293);
+#endif
+ }
+#endif
+
void report_exception ()
{
try {
void close_splash ()
{
if (_splash) {
- _splash->Destroy ();
- _splash = 0;
+ _splash->Destroy();
+ _splash = nullptr;
}
}
/* 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) {
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"
_("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"
_("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"
"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;
string _film_to_create;
string _content_to_add;
string _dcp_to_add;
+#ifdef DCPOMATIC_CUCUMBER
+ CucumberBridge* _cucumber_bridge = nullptr;
+#endif
};