*/
-#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/i18n_hook.h"
-#include "lib/film.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/util.h"
-#include "lib/video_content.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/emailer.h"
+#include "lib/encode_server_finder.h"
+#include "lib/exceptions.h"
+#include "lib/ffmpeg_encoder.h"
+#include "lib/film.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/util.h"
+#include "lib/version.h"
+#include "lib/video_content.h"
#include <dcp/exceptions.h>
#include <dcp/raw_convert.h>
-DCPOMATIC_DISABLE_WARNINGS
-#include <wx/generic/aboutdlgg.h>
-#include <wx/stdpaths.h>
+#include <dcp/warnings.h>
+LIBDCP_DISABLE_WARNINGS
#include <wx/cmdline.h>
+#include <wx/generic/aboutdlgg.h>
#include <wx/preferences.h>
#include <wx/splash.h>
+#include <wx/stdpaths.h>
#include <wx/wxhtml.h>
-DCPOMATIC_ENABLE_WARNINGS
+LIBDCP_ENABLE_WARNINGS
#ifdef __WXGTK__
#include <X11/Xlib.h>
#endif
#ifdef __WXMSW__
#include <shellapi.h>
#endif
-#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
#include <iostream>
#include <fstream>
/* This is OK as it's only used with DCPOMATIC_WINDOWS */
using std::string;
using std::vector;
using std::wcout;
-using std::wstring;
-using std::wstringstream;
using boost::optional;
using boost::is_any_of;
using boost::algorithm::find;
ID_tools_send_translations,
ID_tools_system_information,
ID_tools_restore_default_preferences,
+ ID_tools_export_preferences,
ID_help_report_a_problem,
/* IDs for shortcuts (with no associated menu item) */
ID_add_file,
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::help_about, this), wxID_ABOUT);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_report_a_problem, this), ID_help_report_a_problem);
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 ("");
+ set_title ();
- JobManager::instance()->ActiveJobsChanged.connect (boost::bind (&DOMFrame::set_menu_sensitivity, this));
+ 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));
+ UpdateChecker::instance()->StateChanged.connect(boost::bind(&DOMFrame::update_checker_state_changed, this));
FocusManager::instance()->SetFocus.connect (boost::bind (&DOMFrame::remove_accelerators, this));
FocusManager::instance()->KillFocus.connect (boost::bind (&DOMFrame::add_accelerators, this));
#else
int accelerators = 6;
#endif
- wxAcceleratorEntry* accel = new wxAcceleratorEntry[accelerators];
+ std::vector<wxAcceleratorEntry> accel(accelerators);
+ /* [Shortcut] Ctrl+A:Add file(s) to the film */
accel[0].Set (wxACCEL_CTRL, static_cast<int>('A'), ID_add_file);
+ /* [Shortcut] Delete:Remove selected content from film */
accel[1].Set (wxACCEL_NORMAL, WXK_DELETE, ID_remove);
+ /* [Shortcut] Space:Start/stop playback */
accel[2].Set (wxACCEL_NORMAL, WXK_SPACE, ID_start_stop);
+ /* [Shortcut] Ctrl+T:Open timeline window */
accel[3].Set (wxACCEL_CTRL, static_cast<int>('T'), ID_timeline);
+ /* [Shortcut] Left arrow:Move back one frame */
accel[4].Set (wxACCEL_NORMAL, WXK_LEFT, ID_back_frame);
+ /* [Shortcut] Right arrow:Move forward one frame */
accel[5].Set (wxACCEL_NORMAL, WXK_RIGHT, ID_forward_frame);
#ifdef __WXOSX__
accel[6].Set (wxACCEL_CTRL, static_cast<int>('W'), ID_file_close);
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 ()
set_film (film);
- JobManager::instance()->add(shared_ptr<Job>(new CheckContentChangeJob(film)));
+ JobManager::instance()->add(make_shared<CheckContentJob>(film));
}
catch (FileNotFoundError& e) {
auto const dir = e.file().parent_path();
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> film () const {
}
}
- 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);
}
}
+ void tools_export_preferences ()
+ {
+ auto dialog = new wxFileDialog (
+ 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()));
+ }
+ }
+
void jobs_make_dcp ()
{
double required;
a long time, and crashes/power failures are moderately likely.
*/
_film->write_metadata ();
- _film->make_dcp (true);
+ 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) {
_("You are making a DKDM which is encrypted by a private key held in"
"\n\n<tt>%s</tt>\n\nIt is <span weight=\"bold\" size=\"larger\">VITALLY IMPORTANT</span> "
"that you <span weight=\"bold\" size=\"larger\">BACK UP THIS FILE</span> since if it is lost "
- "your DKDMs (and the DCPs they protect) will become useless."), std_to_wx(Config::config_file().string()).data()
+ "your DKDMs (and the DCPs they protect) will become useless."), std_to_wx(Config::config_read_file().string()).data()
)
);
}
}
- auto job = make_shared<TranscodeJob>(_film);
+ 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())
{
auto d = new ExportSubtitlesDialog (this, _film->reels().size(), _film->interop());
if (d->ShowModal() == wxID_OK) {
- auto job = make_shared<TranscodeJob>(_film);
+ 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())
);
void jobs_show_dcp ()
{
DCPOMATIC_ASSERT (_film->directory ());
-#ifdef DCPOMATIC_WINDOWS
- wstringstream args;
- args << "/select," << _film->dir (_film->dcp_name(false));
- ShellExecute (0, L"open", L"explorer.exe", args.str().c_str(), 0, SW_SHOWDEFAULT);
-#endif
-
-#ifdef DCPOMATIC_LINUX
- int r = system ("which nautilus");
- if (WEXITSTATUS (r) == 0) {
- r = system (String::compose("nautilus \"%1\"", _film->directory()->string()).c_str());
- if (WEXITSTATUS (r)) {
- error_dialog (this, _("Could not show DCP."), _("Could not run nautilus"));
- }
- } else {
- int r = system ("which konqueror");
- if (WEXITSTATUS (r) == 0) {
- r = system (String::compose ("konqueror \"%1\"", _film->directory()->string()).c_str());
- if (WEXITSTATUS (r)) {
- error_dialog (this, _("Could not show DCP"), _("Could not run konqueror"));
- }
- }
- }
-#endif
-
-#ifdef DCPOMATIC_OSX
- int r = system (String::compose ("open -R \"%1\"", _film->dir (_film->dcp_name(false)).string()).c_str());
- if (WEXITSTATUS (r)) {
- error_dialog (this, _("Could not show DCP"));
+ if (show_in_file_manager(_film->directory().get(), _film->dir(_film->dcp_name(false)))) {
+ error_dialog (this, _("Could not show DCP."));
}
-#endif
}
void view_closed_captions ()
void tools_check_for_updates ()
{
- UpdateChecker::instance()->run ();
_update_news_requested = true;
+ UpdateChecker::instance()->run();
}
void tools_send_translations ()
body += i.first + "\n" + i.second + "\n\n";
}
list<string> to = { "carl@dcpomatic.com" };
- Emailer emailer (d->email(), to, "DCP-o-matic translations", body);
- emailer.send ("main.carlh.net", 2525, EmailProtocol::STARTTLS);
+ 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()));
+ }
+ }
}
d->Destroy ();
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 ();
void setup_menu (wxMenuBar* m)
{
_file_menu = new wxMenu;
+ /* [Shortcut] Ctrl+N:New film */
add_item (_file_menu, _("New...\tCtrl-N"), ID_file_new, ALWAYS);
+ /* [Shortcut] Ctrl+O:Open existing film */
add_item (_file_menu, _("&Open...\tCtrl-O"), ID_file_open, ALWAYS);
_file_menu->AppendSeparator ();
+ /* [Shortcut] Ctrl+S:Save current film */
add_item (_file_menu, _("&Save\tCtrl-S"), ID_file_save, NEEDS_FILM);
_file_menu->AppendSeparator ();
add_item (_file_menu, _("Save as &template..."), ID_file_save_as_template, NEEDS_FILM);
_history_position = _file_menu->GetMenuItems().GetCount();
_file_menu->AppendSeparator ();
+ /* [Shortcut] Ctrl+W:Close current film */
add_item (_file_menu, _("&Close\tCtrl-W"), ID_file_close, NEEDS_FILM);
#ifndef __WXOSX__
#endif
auto edit = new wxMenu;
+ /* [Shortcut] Ctrl+C:Copy settings from currently selected content */
add_item (edit, _("Copy settings\tCtrl-C"), ID_edit_copy, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_SINGLE_SELECTED_CONTENT);
+ /* [Shortcut] Ctrl+V:Paste settings into currently selected content */
add_item (edit, _("Paste settings...\tCtrl-V"), ID_edit_paste, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_SELECTED_CONTENT | NEEDS_CLIPBOARD);
edit->AppendSeparator ();
+ /* [Shortcut] Shift+Ctrl+A:Select all content */
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);
#else
edit->AppendSeparator ();
+ /* [Shortcut] Ctrl+P:Open preferences window */
add_item (edit, _("&Preferences...\tCtrl-P"), wxID_PREFERENCES, ALWAYS);
#endif
auto jobs_menu = new wxMenu;
+ /* [Shortcut] Ctrl+M:Make DCP */
add_item (jobs_menu, _("&Make DCP\tCtrl-M"), ID_jobs_make_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
+ /* [Shortcut] Ctrl+B:Make DCP in the batch converter*/
add_item (jobs_menu, _("Make DCP in &batch converter\tCtrl-B"), ID_jobs_make_dcp_batch, NEEDS_FILM | NOT_DURING_DCP_CREATION);
jobs_menu->AppendSeparator ();
+ /* [Shortcut] Ctrl+K:Make KDMs */
add_item (jobs_menu, _("Make &KDMs...\tCtrl-K"), ID_jobs_make_kdms, NEEDS_FILM);
+ /* [Shortcut] Ctrl+D:Make DKDMs */
add_item (jobs_menu, _("Make &DKDMs...\tCtrl-D"), ID_jobs_make_dkdms, NEEDS_FILM);
add_item (jobs_menu, _("Make DKDM for DCP-o-matic..."), ID_jobs_make_self_dkdm, NEEDS_FILM | NEEDS_ENCRYPTION);
jobs_menu->AppendSeparator ();
+ /* [Shortcut] Ctrl+E:Export video file */
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 (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);
wxMenu* help = new wxMenu;
#ifdef __WXOSX__
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();
d->Destroy();
}
+ 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<FilmViewer> _film_viewer;
StandardControls* _controls;
private:
- bool OnInit ()
+ bool OnInit () override
{
try {
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 ();
if (Config::instance()->check_for_updates ()) {
UpdateChecker::instance()->run ();
}
+
+ auto release_notes = find_release_notes();
+ if (release_notes) {
+ auto notes = new HTMLDialog(nullptr, _("Release notes"), std_to_wx(*release_notes), true);
+ notes->Centre();
+ notes->ShowModal();
+ notes->Destroy();
+ }
}
catch (exception& e)
{
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";
}
/* 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 ();
}
}
}
- 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)
switch (reason) {
case Config::BAD_SIGNER_UTF8_STRINGS:
{
- if (config->nagged(Config::NAG_BAD_SIGNER_CHAIN)) {
+ if (config->nagged(Config::NAG_BAD_SIGNER_CHAIN_UTF8)) {
return false;
}
auto d = new RecreateChainDialog (
"which will prevent DCPs from being validated correctly on some systems. Do you want to re-create\n"
"the certificate chain for signing DCPs and KDMs?"),
_("Do nothing"),
- Config::NAG_BAD_SIGNER_CHAIN
+ Config::NAG_BAD_SIGNER_CHAIN_UTF8
+ );
+ int const r = d->ShowModal ();
+ d->Destroy ();
+ return r == wxID_OK;
+ }
+ case Config::BAD_SIGNER_VALIDITY_TOO_LONG:
+ {
+ if (config->nagged(Config::NAG_BAD_SIGNER_CHAIN_VALIDITY)) {
+ return false;
+ }
+ auto d = new RecreateChainDialog (
+ _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 you want to re-create the certificate chain for signing DCPs and KDMs?"),
+ _("Do nothing"),
+ Config::NAG_BAD_SIGNER_CHAIN_VALIDITY
);
int const r = d->ShowModal ();
d->Destroy ();