From 0f42e807a707249cd1a60fa6e476cb47a4147c5a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 9 May 2017 13:44:43 +0100 Subject: [PATCH] Basica save-as (duplicate) (#746). --- ChangeLog | 2 + src/lib/film.cc | 14 +- src/lib/film.h | 4 + src/tools/dcpomatic.cc | 134 +++++++++++++----- ...dialog.cc => film_name_location_dialog.cc} | 72 +++++++--- ...m_dialog.h => film_name_location_dialog.h} | 7 +- src/wx/wscript | 2 +- 7 files changed, 171 insertions(+), 64 deletions(-) rename src/wx/{new_film_dialog.cc => film_name_location_dialog.cc} (52%) rename src/wx/{new_film_dialog.h => film_name_location_dialog.h} (87%) diff --git a/ChangeLog b/ChangeLog index bc8e6bb81..975ead7bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2017-05-09 Carl Hetherington + * Basic save-as (duplicate) feature (#746). + * Write a simple cover sheet when making a DCP (#1039). 2017-05-08 Carl Hetherington diff --git a/src/lib/film.cc b/src/lib/film.cc index 04ff00250..36221fb9a 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -98,6 +98,8 @@ using dcp::raw_convert; #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL); #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL); +string const Film::metadata_file = "metadata.xml"; + /* 5 -> 6 * AudioMapping XML changed. * 6 -> 7 @@ -399,7 +401,7 @@ Film::write_metadata () const DCPOMATIC_ASSERT (directory()); boost::filesystem::create_directories (directory().get()); shared_ptr doc = metadata (); - doc->write_to_file_formatted (file("metadata.xml").string ()); + doc->write_to_file_formatted (file(metadata_file).string ()); _dirty = false; } @@ -419,11 +421,11 @@ list Film::read_metadata (optional path) { if (!path) { - if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) { + if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file (metadata_file))) { throw runtime_error (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version. You will need to create a new Film, re-add your content and set it up again. Sorry!")); } - path = file ("metadata.xml"); + path = file (metadata_file); } cxml::Document f ("Metadata"); @@ -1575,3 +1577,9 @@ Film::speed_up_range (int dcp_frame_rate) const { return _playlist->speed_up_range (dcp_frame_rate); } + +void +Film::copy_from (shared_ptr film) +{ + read_metadata (film->file (metadata_file)); +} diff --git a/src/lib/film.h b/src/lib/film.h index fdd4674d5..ec64c3913 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -96,6 +96,8 @@ public: void write_template (boost::filesystem::path path) const; boost::shared_ptr metadata (bool with_content_paths = true) const; + void copy_from (boost::shared_ptr film); + std::string isdcf_name (bool if_created_now) const; std::string dcp_name (bool if_created_now = false) const; @@ -337,6 +339,8 @@ private: void maybe_add_content (boost::weak_ptr, boost::weak_ptr); void audio_analysis_finished (); + static std::string const metadata_file; + /** Log to write to */ boost::shared_ptr _log; boost::shared_ptr _playlist; diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index eca9d2689..6543db551 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -27,7 +27,7 @@ #include "wx/job_manager_view.h" #include "wx/config_dialog.h" #include "wx/wx_util.h" -#include "wx/new_film_dialog.h" +#include "wx/film_name_location_dialog.h" #include "wx/wx_signal_manager.h" #include "wx/about_dialog.h" #include "wx/kdm_dialog.h" @@ -103,10 +103,10 @@ using boost::dynamic_pointer_cast; using boost::optional; using dcp::raw_convert; -class FilmChangedDialog : public boost::noncopyable +class FilmChangedClosingDialog : public boost::noncopyable { public: - FilmChangedDialog (string name) + FilmChangedClosingDialog (string name) { _dialog = new wxMessageDialog ( 0, @@ -122,7 +122,40 @@ public: ); } - ~FilmChangedDialog () + ~FilmChangedClosingDialog () + { + _dialog->Destroy (); + } + + int run () + { + return _dialog->ShowModal (); + } + +private: + wxMessageDialog* _dialog; +}; + +class FilmChangedDuplicatingDialog : public boost::noncopyable +{ +public: + FilmChangedDuplicatingDialog (string name) + { + _dialog = new wxMessageDialog ( + 0, + 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 ( + _("Save film and duplicate"), _("Duplicate without saving film"), _("Don't duplicate") + ); + } + + ~FilmChangedDuplicatingDialog () { _dialog->Destroy (); } @@ -149,6 +182,8 @@ enum { ID_file_open, ID_file_save, ID_file_save_as_template, + ID_file_duplicate, + ID_file_duplicate_and_open, ID_file_history, /* Allow spare IDs after _history for the recent files list */ ID_content_scale_to_fit_width = 100, @@ -223,6 +258,8 @@ public: Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_open, this), ID_file_open); Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_save, this), ID_file_save); Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_save_as_template, this), ID_file_save_as_template); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_duplicate, this), ID_file_duplicate); + Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_duplicate_and_open, this), ID_file_duplicate_and_open); Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_history, this, _1), ID_file_history, ID_file_history + HISTORY_SIZE); Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT); Bind (wxEVT_MENU, boost::bind (&DOMFrame::edit_preferences, this), wxID_PREFERENCES); @@ -295,8 +332,8 @@ public: if (template_name) { film->use_template (template_name.get()); } - film->write_metadata (); film->set_name (path.filename().generic_string()); + film->write_metadata (); set_film (film); } @@ -357,33 +394,11 @@ private: void file_new () { - NewFilmDialog* d = new NewFilmDialog (this); + FilmNameLocationDialog* d = new FilmNameLocationDialog (this, _("New Film"), true); int const r = d->ShowModal (); - if (r == wxID_OK) { - - if (boost::filesystem::is_directory (d->path()) && !boost::filesystem::is_empty(d->path())) { - if (!confirm_dialog ( - this, - std_to_wx ( - String::compose (wx_to_std (_("The directory %1 already exists and is not empty. " - "Are you sure you want to use it?")), - d->path().string().c_str()) - ) - )) { - return; - } - } else if (boost::filesystem::is_regular_file (d->path())) { - error_dialog ( - this, - String::compose (wx_to_std (_("%1 already exists as a file, so you cannot use it for a new film.")), d->path().c_str()) - ); - return; - } - - if (maybe_save_then_delete_film ()) { - new_film (d->path(), d->template_name()); - } + if (r == wxID_OK && d->check_path() && maybe_save_then_delete_film()) { + new_film (d->path(), d->template_name()); } d->Destroy (); @@ -408,7 +423,7 @@ private: } } - if (r == wxID_OK && maybe_save_then_delete_film()) { + if (r == wxID_OK && maybe_save_then_delete_film()) { load_film (wx_to_std (c->GetPath ())); } @@ -430,11 +445,42 @@ private: d->Destroy (); } + void file_duplicate () + { + FilmNameLocationDialog* d = new FilmNameLocationDialog (this, _("Duplicate Film"), false); + int const r = d->ShowModal (); + + if (r == wxID_OK && d->check_path() && maybe_save_film()) { + shared_ptr film (new Film (d->path())); + film->copy_from (_film); + film->set_name (d->path().filename().generic_string()); + film->write_metadata (); + } + + d->Destroy (); + } + + void file_duplicate_and_open () + { + FilmNameLocationDialog* d = new FilmNameLocationDialog (this, _("Duplicate Film"), false); + int const r = d->ShowModal (); + + if (r == wxID_OK && d->check_path() && maybe_save_film()) { + shared_ptr film (new Film (d->path())); + film->copy_from (_film); + film->set_name (d->path().filename().generic_string()); + film->write_metadata (); + set_film (film); + } + + d->Destroy (); + } + void file_history (wxCommandEvent& event) { vector history = Config::instance()->history (); int n = event.GetId() - ID_file_history; - if (n >= 0 && n < static_cast (history.size ()) && maybe_save_then_delete_film()) { + if (n >= 0 && n < static_cast (history.size ()) && maybe_save_then_delete_film()) { load_film (history[n]); } } @@ -792,7 +838,7 @@ private: if (_film && _film->dirty ()) { - FilmChangedDialog* dialog = new FilmChangedDialog (_film->name ()); + FilmChangedClosingDialog* dialog = new FilmChangedClosingDialog (_film->name ()); int const r = dialog->run (); delete dialog; @@ -858,29 +904,39 @@ private: /** @return true if the operation that called this method * should continue, false to abort it. */ - bool maybe_save_then_delete_film () + template + bool maybe_save_film () { if (!_film) { return true; } if (_film->dirty ()) { - FilmChangedDialog d (_film->name ()); + T d (_film->name ()); switch (d.run ()) { case wxID_NO: - break; + return true; case wxID_YES: _film->write_metadata (); - break; + return true; case wxID_CANCEL: return false; } } - _film.reset (); return true; } + template + bool maybe_save_then_delete_film () + { + bool const r = maybe_save_film (); + if (r) { + _film.reset (); + } + return r; + } + void add_item (wxMenu* menu, wxString text, int id, int sens) { wxMenuItem* item = menu->Append (id, text); @@ -896,6 +952,8 @@ private: 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); + add_item (_file_menu, _("Duplicate..."), ID_file_duplicate, NEEDS_FILM); + add_item (_file_menu, _("Duplicate and open..."), ID_file_duplicate_and_open, NEEDS_FILM); _history_position = _file_menu->GetMenuItems().GetCount(); diff --git a/src/wx/new_film_dialog.cc b/src/wx/film_name_location_dialog.cc similarity index 52% rename from src/wx/new_film_dialog.cc rename to src/wx/film_name_location_dialog.cc index ef072c9fc..d91171acb 100644 --- a/src/wx/new_film_dialog.cc +++ b/src/wx/film_name_location_dialog.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2014 Carl Hetherington + Copyright (C) 2012-2017 Carl Hetherington This file is part of DCP-o-matic. @@ -18,12 +18,13 @@ */ -#include "lib/config.h" -#include "new_film_dialog.h" #include "wx_util.h" +#include "film_name_location_dialog.h" #ifdef DCPOMATIC_USE_OWN_PICKER #include "dir_picker_ctrl.h" #endif +#include "lib/config.h" +#include "lib/compose.hpp" #include #include #include @@ -31,10 +32,10 @@ using namespace std; using namespace boost; -boost::optional NewFilmDialog::_directory; +boost::optional FilmNameLocationDialog::_directory; -NewFilmDialog::NewFilmDialog (wxWindow* parent) - : TableDialog (parent, _("New Film"), 2, 1, true) +FilmNameLocationDialog::FilmNameLocationDialog (wxWindow* parent, wxString title, bool offer_templates) + : TableDialog (parent, title, 2, 1, true) { add (_("Film name"), true); _name = add (new wxTextCtrl (this, wxID_ANY)); @@ -54,36 +55,41 @@ NewFilmDialog::NewFilmDialog (wxWindow* parent) _folder->SetPath (std_to_wx (_directory.get().string())); add (_folder); - _use_template = new wxCheckBox (this, wxID_ANY, _("From template")); - add (_use_template); - _template_name = new wxChoice (this, wxID_ANY); - add (_template_name); + if (offer_templates) { + _use_template = new wxCheckBox (this, wxID_ANY, _("From template")); + add (_use_template); + _template_name = new wxChoice (this, wxID_ANY); + add (_template_name); + } _name->SetFocus (); - _template_name->Enable (false); - BOOST_FOREACH (string i, Config::instance()->templates ()) { - _template_name->Append (std_to_wx (i)); - } + if (offer_templates) { + _template_name->Enable (false); + + BOOST_FOREACH (string i, Config::instance()->templates ()) { + _template_name->Append (std_to_wx (i)); + } - _use_template->Bind (wxEVT_CHECKBOX, bind (&NewFilmDialog::use_template_clicked, this)); + _use_template->Bind (wxEVT_CHECKBOX, bind (&FilmNameLocationDialog::use_template_clicked, this)); + } layout (); } void -NewFilmDialog::use_template_clicked () +FilmNameLocationDialog::use_template_clicked () { _template_name->Enable (_use_template->GetValue ()); } -NewFilmDialog::~NewFilmDialog () +FilmNameLocationDialog::~FilmNameLocationDialog () { _directory = wx_to_std (_folder->GetPath ()); } boost::filesystem::path -NewFilmDialog::path () const +FilmNameLocationDialog::path () const { filesystem::path p; p /= wx_to_std (_folder->GetPath ()); @@ -92,7 +98,7 @@ NewFilmDialog::path () const } optional -NewFilmDialog::template_name () const +FilmNameLocationDialog::template_name () const { if (!_use_template->GetValue() || _template_name->GetSelection() == -1) { return optional (); @@ -100,3 +106,31 @@ NewFilmDialog::template_name () const return wx_to_std (_template_name->GetString(_template_name->GetSelection())); } + +/** Check the path that is in our controls and offer confirmations or errors as required. + * @return true if the path should be used. + */ +bool +FilmNameLocationDialog::check_path () +{ + if (boost::filesystem::is_directory (path()) && !boost::filesystem::is_empty(path())) { + if (!confirm_dialog ( + this, + std_to_wx ( + String::compose (wx_to_std (_("The directory %1 already exists and is not empty. " + "Are you sure you want to use it?")), + path().string().c_str()) + ) + )) { + return false; + } + } else if (boost::filesystem::is_regular_file (path())) { + error_dialog ( + this, + String::compose (wx_to_std (_("%1 already exists as a file, so you cannot use it for a film.")), path().c_str()) + ); + return false; + } + + return true; +} diff --git a/src/wx/new_film_dialog.h b/src/wx/film_name_location_dialog.h similarity index 87% rename from src/wx/new_film_dialog.h rename to src/wx/film_name_location_dialog.h index 81dd29fea..cfa296701 100644 --- a/src/wx/new_film_dialog.h +++ b/src/wx/film_name_location_dialog.h @@ -25,13 +25,14 @@ class DirPickerCtrl; -class NewFilmDialog : public TableDialog +class FilmNameLocationDialog : public TableDialog { public: - NewFilmDialog (wxWindow *); - ~NewFilmDialog (); + FilmNameLocationDialog (wxWindow *, wxString title, bool offer_templates); + ~FilmNameLocationDialog (); boost::filesystem::path path () const; + bool check_path (); boost::optional template_name () const; private: diff --git a/src/wx/wscript b/src/wx/wscript index 332a04ac5..8297f37c9 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -51,6 +51,7 @@ sources = """ download_certificate_panel.cc file_picker_ctrl.cc film_editor.cc + film_name_location_dialog.cc film_viewer.cc filter_dialog.cc filter_editor.cc @@ -70,7 +71,6 @@ sources = """ move_to_dialog.cc nag_dialog.cc name_format_editor.cc - new_film_dialog.cc normal_job_view.cc playhead_to_timecode_dialog.cc playhead_to_frame_dialog.cc -- 2.30.2