2017-05-09 Carl Hetherington <cth@carlh.net>
+ * Basic save-as (duplicate) feature (#746).
+
* Write a simple cover sheet when making a DCP (#1039).
2017-05-08 Carl Hetherington <cth@carlh.net>
#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
DCPOMATIC_ASSERT (directory());
boost::filesystem::create_directories (directory().get());
shared_ptr<xmlpp::Document> doc = metadata ();
- doc->write_to_file_formatted (file("metadata.xml").string ());
+ doc->write_to_file_formatted (file(metadata_file).string ());
_dirty = false;
}
Film::read_metadata (optional<boost::filesystem::path> 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");
{
return _playlist->speed_up_range (dcp_frame_rate);
}
+
+void
+Film::copy_from (shared_ptr<const Film> film)
+{
+ read_metadata (film->file (metadata_file));
+}
void write_template (boost::filesystem::path path) const;
boost::shared_ptr<xmlpp::Document> metadata (bool with_content_paths = true) const;
+ void copy_from (boost::shared_ptr<const Film> film);
+
std::string isdcf_name (bool if_created_now) const;
std::string dcp_name (bool if_created_now = false) const;
void maybe_add_content (boost::weak_ptr<Job>, boost::weak_ptr<Content>);
void audio_analysis_finished ();
+ static std::string const metadata_file;
+
/** Log to write to */
boost::shared_ptr<Log> _log;
boost::shared_ptr<Playlist> _playlist;
#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"
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,
);
}
- ~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 ();
}
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,
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);
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);
}
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<FilmChangedClosingDialog>()) {
+ new_film (d->path(), d->template_name());
}
d->Destroy ();
}
}
- if (r == wxID_OK && maybe_save_then_delete_film()) {
+ if (r == wxID_OK && maybe_save_then_delete_film<FilmChangedClosingDialog>()) {
load_film (wx_to_std (c->GetPath ()));
}
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<FilmChangedDuplicatingDialog>()) {
+ shared_ptr<Film> 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<FilmChangedDuplicatingDialog>()) {
+ shared_ptr<Film> 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<boost::filesystem::path> history = Config::instance()->history ();
int n = event.GetId() - ID_file_history;
- if (n >= 0 && n < static_cast<int> (history.size ()) && maybe_save_then_delete_film()) {
+ if (n >= 0 && n < static_cast<int> (history.size ()) && maybe_save_then_delete_film<FilmChangedClosingDialog>()) {
load_film (history[n]);
}
}
if (_film && _film->dirty ()) {
- FilmChangedDialog* dialog = new FilmChangedDialog (_film->name ());
+ FilmChangedClosingDialog* dialog = new FilmChangedClosingDialog (_film->name ());
int const r = dialog->run ();
delete dialog;
/** @return true if the operation that called this method
* should continue, false to abort it.
*/
- bool maybe_save_then_delete_film ()
+ template <class T>
+ 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 <class T>
+ bool maybe_save_then_delete_film ()
+ {
+ bool const r = maybe_save_film<T> ();
+ if (r) {
+ _film.reset ();
+ }
+ return r;
+ }
+
void add_item (wxMenu* menu, wxString text, int id, int sens)
{
wxMenuItem* item = menu->Append (id, text);
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();
--- /dev/null
+/*
+ Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#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 <wx/stdpaths.h>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+
+using namespace std;
+using namespace boost;
+
+boost::optional<boost::filesystem::path> FilmNameLocationDialog::_directory;
+
+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));
+
+ add (_("Create in folder"), true);
+
+#ifdef DCPOMATIC_USE_OWN_PICKER
+ _folder = new DirPickerCtrl (this);
+#else
+ _folder = new wxDirPickerCtrl (this, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
+#endif
+
+ if (!_directory) {
+ _directory = Config::instance()->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir()));
+ }
+
+ _folder->SetPath (std_to_wx (_directory.get().string()));
+ add (_folder);
+
+ 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 ();
+
+ 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 (&FilmNameLocationDialog::use_template_clicked, this));
+ }
+
+ layout ();
+}
+
+void
+FilmNameLocationDialog::use_template_clicked ()
+{
+ _template_name->Enable (_use_template->GetValue ());
+}
+
+FilmNameLocationDialog::~FilmNameLocationDialog ()
+{
+ _directory = wx_to_std (_folder->GetPath ());
+}
+
+boost::filesystem::path
+FilmNameLocationDialog::path () const
+{
+ filesystem::path p;
+ p /= wx_to_std (_folder->GetPath ());
+ p /= wx_to_std (_name->GetValue ());
+ return p;
+}
+
+optional<string>
+FilmNameLocationDialog::template_name () const
+{
+ if (!_use_template->GetValue() || _template_name->GetSelection() == -1) {
+ return optional<string> ();
+ }
+
+ 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;
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <wx/wx.h>
+#include <wx/filepicker.h>
+#include "wx_util.h"
+#include "table_dialog.h"
+
+class DirPickerCtrl;
+
+class FilmNameLocationDialog : public TableDialog
+{
+public:
+ FilmNameLocationDialog (wxWindow *, wxString title, bool offer_templates);
+ ~FilmNameLocationDialog ();
+
+ boost::filesystem::path path () const;
+ bool check_path ();
+ boost::optional<std::string> template_name () const;
+
+private:
+ void use_template_clicked ();
+
+ wxTextCtrl* _name;
+#ifdef DCPOMATIC_USE_OWN_PICKER
+ DirPickerCtrl* _folder;
+#else
+ wxDirPickerCtrl* _folder;
+#endif
+ wxCheckBox* _use_template;
+ wxChoice* _template_name;
+ static boost::optional<boost::filesystem::path> _directory;
+};
+++ /dev/null
-/*
- Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "lib/config.h"
-#include "new_film_dialog.h"
-#include "wx_util.h"
-#ifdef DCPOMATIC_USE_OWN_PICKER
-#include "dir_picker_ctrl.h"
-#endif
-#include <wx/stdpaths.h>
-#include <boost/filesystem.hpp>
-#include <boost/foreach.hpp>
-
-using namespace std;
-using namespace boost;
-
-boost::optional<boost::filesystem::path> NewFilmDialog::_directory;
-
-NewFilmDialog::NewFilmDialog (wxWindow* parent)
- : TableDialog (parent, _("New Film"), 2, 1, true)
-{
- add (_("Film name"), true);
- _name = add (new wxTextCtrl (this, wxID_ANY));
-
- add (_("Create in folder"), true);
-
-#ifdef DCPOMATIC_USE_OWN_PICKER
- _folder = new DirPickerCtrl (this);
-#else
- _folder = new wxDirPickerCtrl (this, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
-#endif
-
- if (!_directory) {
- _directory = Config::instance()->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir()));
- }
-
- _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);
-
- _name->SetFocus ();
- _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));
-
- layout ();
-}
-
-void
-NewFilmDialog::use_template_clicked ()
-{
- _template_name->Enable (_use_template->GetValue ());
-}
-
-NewFilmDialog::~NewFilmDialog ()
-{
- _directory = wx_to_std (_folder->GetPath ());
-}
-
-boost::filesystem::path
-NewFilmDialog::path () const
-{
- filesystem::path p;
- p /= wx_to_std (_folder->GetPath ());
- p /= wx_to_std (_name->GetValue ());
- return p;
-}
-
-optional<string>
-NewFilmDialog::template_name () const
-{
- if (!_use_template->GetValue() || _template_name->GetSelection() == -1) {
- return optional<string> ();
- }
-
- return wx_to_std (_template_name->GetString(_template_name->GetSelection()));
-}
+++ /dev/null
-/*
- Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include <wx/wx.h>
-#include <wx/filepicker.h>
-#include "wx_util.h"
-#include "table_dialog.h"
-
-class DirPickerCtrl;
-
-class NewFilmDialog : public TableDialog
-{
-public:
- NewFilmDialog (wxWindow *);
- ~NewFilmDialog ();
-
- boost::filesystem::path path () const;
- boost::optional<std::string> template_name () const;
-
-private:
- void use_template_clicked ();
-
- wxTextCtrl* _name;
-#ifdef DCPOMATIC_USE_OWN_PICKER
- DirPickerCtrl* _folder;
-#else
- wxDirPickerCtrl* _folder;
-#endif
- wxCheckBox* _use_template;
- wxChoice* _template_name;
- static boost::optional<boost::filesystem::path> _directory;
-};
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
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