Basic template support (#485).
authorCarl Hetherington <cth@carlh.net>
Wed, 24 Aug 2016 10:40:34 +0000 (11:40 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 24 Aug 2016 13:28:32 +0000 (14:28 +0100)
45 files changed:
src/lib/atmos_mxf_content.cc
src/lib/atmos_mxf_content.h
src/lib/audio_content.cc
src/lib/audio_content.h
src/lib/config.cc
src/lib/config.h
src/lib/content.cc
src/lib/content.h
src/lib/dcp_content.cc
src/lib/dcp_content.h
src/lib/dcp_subtitle_content.cc
src/lib/dcp_subtitle_content.h
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_content.h
src/lib/film.cc
src/lib/film.h
src/lib/image_content.cc
src/lib/image_content.h
src/lib/playlist.cc
src/lib/playlist.h
src/lib/reel_writer.cc
src/lib/subtitle_content.cc
src/lib/subtitle_content.h
src/lib/text_subtitle_content.cc
src/lib/text_subtitle_content.h
src/lib/video_content.cc
src/lib/video_content.h
src/lib/video_mxf_content.cc
src/lib/video_mxf_content.h
src/tools/dcpomatic.cc
src/tools/dcpomatic_batch.cc
src/tools/dcpomatic_cli.cc
src/tools/dcpomatic_create.cc
src/tools/dcpomatic_kdm_cli.cc
src/tools/server_test.cc
src/wx/film_editor.cc
src/wx/new_film_dialog.cc
src/wx/new_film_dialog.h
src/wx/save_template_dialog.cc [new file with mode: 0644]
src/wx/save_template_dialog.h [new file with mode: 0644]
src/wx/wscript
test/ffmpeg_dcp_test.cc
test/srt_subtitle_test.cc
test/ssa_subtitle_test.cc
test/test.cc

index f8cc05178ef2d4349580237d462bd39dfafcfdbc..2fd9ead068cf3f9130afcd0d9efe9ec486277e19 100644 (file)
@@ -79,10 +79,10 @@ AtmosMXFContent::summary () const
 }
 
 void
-AtmosMXFContent::as_xml (xmlpp::Node* node) const
+AtmosMXFContent::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        node->add_child("Type")->add_child_text ("AtmosMXF");
-       Content::as_xml (node);
+       Content::as_xml (node, with_paths);
 }
 
 DCPTime
index 10c969a7da595dd514db36f9b7bcb51f355690d3..0f5225c2ec7fa8ad871862b05d10399177a48a43 100644 (file)
@@ -32,7 +32,7 @@ public:
 
        void examine (boost::shared_ptr<Job> job);
        std::string summary () const;
-       void as_xml (xmlpp::Node* node) const;
+       void as_xml (xmlpp::Node* node, bool with_path) const;
        DCPTime full_length () const;
 
        static bool valid_mxf (boost::filesystem::path path);
index e66b8b998ded170d9ec2d56df47417c278c653b7..5d89719f77d49214dc230dac66afb9f80f941a65 100644 (file)
@@ -379,3 +379,19 @@ AudioContent::set_stream (AudioStreamPtr stream)
 
        _parent->signal_changed (AudioContentProperty::STREAMS);
 }
+
+void
+AudioContent::use_template (shared_ptr<const AudioContent> c)
+{
+       _gain = c->_gain;
+       _delay = c->_delay;
+
+       size_t i = 0;
+       size_t j = 0;
+
+       while (i < _streams.size() && j < c->_streams.size()) {
+               _streams[i]->set_mapping (c->_streams[j]->mapping());
+               ++i;
+               ++j;
+       }
+}
index 92491bf89414745648a3658b6a022fe01fd06139..1a2c2911deea6041bf6ba299109d63e7d7ab4a75 100644 (file)
@@ -48,6 +48,7 @@ public:
 
        void as_xml (xmlpp::Node *) const;
        std::string technical_summary () const;
+       void use_template (boost::shared_ptr<const AudioContent> c);
 
        AudioMapping mapping () const;
        void set_mapping (AudioMapping);
index 4184a7d1d53aab035368ffef2220156b33bb26f4..8f9cfab4b2543cdb330b21df2be533073981458e 100644 (file)
@@ -29,6 +29,7 @@
 #include "cinema.h"
 #include "util.h"
 #include "cross.h"
+#include "film.h"
 #include <dcp/raw_convert.h>
 #include <dcp/name_format.h>
 #include <dcp/colour_matrix.h>
@@ -588,3 +589,31 @@ Config::set_cinemas_file (boost::filesystem::path file)
 
        changed (OTHER);
 }
+
+void
+Config::save_template (shared_ptr<const Film> film, string name) const
+{
+       film->write_template (template_path (name));
+}
+
+list<string>
+Config::template_names () const
+{
+       list<string> n;
+       for (boost::filesystem::directory_iterator i (path("templates")); i != boost::filesystem::directory_iterator(); ++i) {
+               n.push_back (i->path().filename().string());
+       }
+       return n;
+}
+
+bool
+Config::existing_template (string name) const
+{
+       return boost::filesystem::exists (template_path (name));
+}
+
+boost::filesystem::path
+Config::template_path (string name) const
+{
+       return path("templates") / tidy_for_filename (name);
+}
index a988cda6b7f11b687303ac255dd97b337aa1c7b0..213c13a83db81beeee4e3b85957fed6f67914431 100644 (file)
@@ -39,6 +39,7 @@ class CinemaSoundProcessor;
 class DCPContentType;
 class Ratio;
 class Cinema;
+class Film;
 
 /** @class Config
  *  @brief A singleton class holding configuration.
@@ -514,6 +515,11 @@ public:
 
        void write () const;
 
+       void save_template (boost::shared_ptr<const Film> film, std::string name) const;
+       bool existing_template (std::string name) const;
+       std::list<std::string> template_names () const;
+       boost::filesystem::path template_path (std::string name) const;
+
        static Config* instance ();
        static void drop ();
        static void restore_defaults ();
index 9083635f242eaea3f5aefe5445c1d25ccb6b8921..b5bae69b684f8a57c50a94dc96803ee74571c15a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -25,6 +25,9 @@
 #include "content.h"
 #include "util.h"
 #include "content_factory.h"
+#include "video_content.h"
+#include "audio_content.h"
+#include "subtitle_content.h"
 #include "exceptions.h"
 #include "film.h"
 #include "job.h"
@@ -135,12 +138,14 @@ Content::Content (shared_ptr<const Film> film, vector<shared_ptr<Content> > c)
 }
 
 void
-Content::as_xml (xmlpp::Node* node) const
+Content::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        boost::mutex::scoped_lock lm (_mutex);
 
-       for (vector<boost::filesystem::path>::const_iterator i = _paths.begin(); i != _paths.end(); ++i) {
-               node->add_child("Path")->add_child_text (i->string ());
+       if (with_paths) {
+               for (vector<boost::filesystem::path>::const_iterator i = _paths.begin(); i != _paths.end(); ++i) {
+                       node->add_child("Path")->add_child_text (i->string ());
+               }
        }
        node->add_child("Digest")->add_child_text (_digest);
        node->add_child("Position")->add_child_text (raw_convert<string> (_position.get ()));
@@ -231,7 +236,7 @@ Content::clone () const
        /* This is a bit naughty, but I can't think of a compelling reason not to do it ... */
        xmlpp::Document doc;
        xmlpp::Node* node = doc.create_root_node ("Content");
-       as_xml (node);
+       as_xml (node, true);
 
        /* notes is unused here (we assume) */
        list<string> notes;
@@ -383,3 +388,17 @@ Content::add_properties (list<UserProperty>& p) const
                }
        }
 }
+
+void
+Content::use_template (shared_ptr<const Content> c)
+{
+       if (video && c->video) {
+               video->use_template (c->video);
+       }
+       if (audio && c->audio) {
+               audio->use_template (c->audio);
+       }
+       if (subtitle && c->subtitle) {
+               subtitle->use_template (c->subtitle);
+       }
+}
index f8b5493c0eb400d4e32c995d83ce8733e90903e4..519ff8907478d4459ac2ed1723098a5565d957e5 100644 (file)
@@ -76,6 +76,8 @@ public:
         */
        virtual void examine (boost::shared_ptr<Job> job);
 
+       virtual void use_template (boost::shared_ptr<const Content> c);
+
        /** @return Quick one-line summary of the content, as will be presented in the
         *  film editor.
         */
@@ -86,7 +88,7 @@ public:
         */
        virtual std::string technical_summary () const;
 
-       virtual void as_xml (xmlpp::Node *) const;
+       virtual void as_xml (xmlpp::Node *, bool with_paths) const;
        virtual DCPTime full_length () const = 0;
        virtual std::string identifier () const;
        /** @return points at which to split this content when
index 03e6f1aaa5415e64f85cfdb5cbfc93eca72046bc..a5c0e5da2e9b20cd70a8dbc0f6809cc3ae64aae1 100644 (file)
@@ -51,6 +51,7 @@ using boost::shared_ptr;
 using boost::scoped_ptr;
 using boost::optional;
 using boost::function;
+using boost::dynamic_pointer_cast;
 using dcp::raw_convert;
 
 int const DCPContentProperty::CAN_BE_PLAYED      = 600;
@@ -192,11 +193,11 @@ DCPContent::technical_summary () const
 }
 
 void
-DCPContent::as_xml (xmlpp::Node* node) const
+DCPContent::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        node->add_child("Type")->add_child_text ("DCP");
 
-       Content::as_xml (node);
+       Content::as_xml (node, with_paths);
 
        if (video) {
                video->as_xml (node);
@@ -448,3 +449,14 @@ DCPContent::can_reference_subtitle (list<string>& why_not) const
 
         return can_reference (bind (&Content::subtitle, _1), _("There is other subtitle content overlapping this DCP; remove it."), why_not);
 }
+
+void
+DCPContent::use_template (shared_ptr<const Content> c)
+{
+       shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (c);
+       DCPOMATIC_ASSERT (dc);
+
+       _reference_video = dc->_reference_video;
+       _reference_audio = dc->_reference_audio;
+       _reference_subtitle = dc->_reference_subtitle;
+}
index f3a8236a2d4c0c77194640d4ec463c67a9bb2bc1..11096037c19eb651246b19ff91045b142a2308b7 100644 (file)
@@ -62,8 +62,9 @@ public:
        void examine (boost::shared_ptr<Job>);
        std::string summary () const;
        std::string technical_summary () const;
-       void as_xml (xmlpp::Node *) const;
+       void as_xml (xmlpp::Node *, bool with_paths) const;
        std::string identifier () const;
+       void use_template (boost::shared_ptr<const Content> c);
 
        void set_default_colour_conversion ();
        std::list<DCPTime> reel_split_points () const;
index 015aebd722c9d973196043f9aafcbb5add93ffaf..8fee0b2ed29a7ee8ce836d548bc5b88676a613a6 100644 (file)
@@ -101,10 +101,10 @@ DCPSubtitleContent::technical_summary () const
 }
 
 void
-DCPSubtitleContent::as_xml (xmlpp::Node* node) const
+DCPSubtitleContent::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        node->add_child("Type")->add_child_text ("DCPSubtitle");
-       Content::as_xml (node);
+       Content::as_xml (node, with_paths);
 
        if (subtitle) {
                subtitle->as_xml (node);
index c29944605f6f359525d6e4e375827847a847e026..36945adcdb4af8bdba3d696679ee811a1adc43a1 100644 (file)
@@ -30,7 +30,7 @@ public:
        void examine (boost::shared_ptr<Job>);
        std::string summary () const;
        std::string technical_summary () const;
-       void as_xml (xmlpp::Node *) const;
+       void as_xml (xmlpp::Node *, bool with_paths) const;
        DCPTime full_length () const;
 
 private:
index b5c5ce0a8a45e64da5d52aae6a4aa3aaf482fb91..77c7c9ecd9e88ea9fd9cfbdec88eb4e4649d0cf8 100644 (file)
@@ -181,10 +181,10 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> film, vector<shared_ptr<Con
 }
 
 void
-FFmpegContent::as_xml (xmlpp::Node* node) const
+FFmpegContent::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        node->add_child("Type")->add_child_text ("FFmpeg");
-       Content::as_xml (node);
+       Content::as_xml (node, with_paths);
 
        if (video) {
                video->as_xml (node);
@@ -599,3 +599,12 @@ FFmpegContent::ffmpeg_audio_streams () const
 
        return fa;
 }
+
+void
+FFmpegContent::use_template (shared_ptr<const Content> c)
+{
+       Content::use_template (c);
+
+       shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (c);
+       _filters = fc->_filters;
+}
index 666322669c071015bc5d9d864727adeeb72c5a07..91caac27cd8ab4fa785c5fb3b0c1f19fba6fb2a3 100644 (file)
@@ -55,9 +55,10 @@ public:
        }
 
        void examine (boost::shared_ptr<Job>);
+       void use_template (boost::shared_ptr<const Content> c);
        std::string summary () const;
        std::string technical_summary () const;
-       void as_xml (xmlpp::Node *) const;
+       void as_xml (xmlpp::Node *, bool with_paths) const;
        DCPTime full_length () const;
 
        std::string identifier () const;
index edb9112177ef7362fdc5da2585a7565552c1772a..e9788d09e623e00ce424e55b4392b3bc688696e5 100644 (file)
@@ -124,7 +124,7 @@ int const Film::current_state_version = 36;
  *  @param dir Film directory.
  */
 
-Film::Film (boost::filesystem::path dir, bool log)
+Film::Film (optional<boost::filesystem::path> dir)
        : _playlist (new Playlist)
        , _use_isdcf_name (true)
        , _dcp_content_type (Config::instance()->default_dcp_content_type ())
@@ -152,27 +152,30 @@ Film::Film (boost::filesystem::path dir, bool log)
        _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
        _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
 
-       /* Make state.directory a complete path without ..s (where possible)
-          (Code swiped from Adam Bowen on stackoverflow)
-          XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
-       */
+       if (dir) {
+               /* Make state.directory a complete path without ..s (where possible)
+                  (Code swiped from Adam Bowen on stackoverflow)
+                  XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
+               */
 
-       boost::filesystem::path p (boost::filesystem::system_complete (dir));
-       boost::filesystem::path result;
-       for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
-               if (*i == "..") {
-                       if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
+               boost::filesystem::path p (boost::filesystem::system_complete (dir.get()));
+               boost::filesystem::path result;
+               for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
+                       if (*i == "..") {
+                               if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
+                                       result /= *i;
+                               } else {
+                                       result = result.parent_path ();
+                               }
+                       } else if (*i != ".") {
                                result /= *i;
-                       } else {
-                               result = result.parent_path ();
                        }
-               } else if (*i != ".") {
-                       result /= *i;
                }
+
+               set_directory (result.make_preferred ());
        }
 
-       set_directory (result.make_preferred ());
-       if (log) {
+       if (_directory) {
                _log.reset (new FileLog (file ("log")));
        } else {
                _log.reset (new NullLog);
@@ -329,7 +332,7 @@ Film::send_dcp_to_tms ()
 }
 
 shared_ptr<xmlpp::Document>
-Film::metadata () const
+Film::metadata (bool with_content_paths) const
 {
        shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
        xmlpp::Element* root = doc->create_root_node ("Metadata");
@@ -364,7 +367,7 @@ Film::metadata () const
        root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
        root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
        root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
-       _playlist->as_xml (root->add_child ("Playlist"));
+       _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
 }
@@ -373,24 +376,38 @@ Film::metadata () const
 void
 Film::write_metadata () const
 {
-       boost::filesystem::create_directories (directory ());
+       DCPOMATIC_ASSERT (directory());
+       boost::filesystem::create_directories (directory().get());
        shared_ptr<xmlpp::Document> doc = metadata ();
        doc->write_to_file_formatted (file("metadata.xml").string ());
        _dirty = false;
 }
 
+/** Write a template from this film */
+void
+Film::write_template (boost::filesystem::path path) const
+{
+       boost::filesystem::create_directories (path.parent_path());
+       shared_ptr<xmlpp::Document> doc = metadata (false);
+       doc->write_to_file_formatted (path.string ());
+}
+
 /** Read state from our metadata file.
  *  @return Notes about things that the user should know about, or empty.
  */
 list<string>
-Film::read_metadata ()
+Film::read_metadata (optional<boost::filesystem::path> path)
 {
-       if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
-               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!"));
+       if (!path) {
+               if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
+                       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");
        }
 
        cxml::Document f ("Metadata");
-       f.read_file (file ("metadata.xml"));
+       f.read_file (path.get ());
 
        _state_version = f.number_child<int> ("Version");
        if (_state_version > current_state_version) {
@@ -462,7 +479,9 @@ Film::read_metadata ()
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
 
        /* Write backtraces to this film's directory, until another film is loaded */
-       set_backtrace_file (file ("backtrace.txt"));
+       if (_directory) {
+               set_backtrace_file (file ("backtrace.txt"));
+       }
 
        _dirty = false;
        return notes;
@@ -474,8 +493,10 @@ Film::read_metadata ()
 boost::filesystem::path
 Film::dir (boost::filesystem::path d) const
 {
+       DCPOMATIC_ASSERT (_directory);
+
        boost::filesystem::path p;
-       p /= _directory;
+       p /= _directory.get();
        p /= d;
 
        boost::filesystem::create_directories (p);
@@ -489,8 +510,10 @@ Film::dir (boost::filesystem::path d) const
 boost::filesystem::path
 Film::file (boost::filesystem::path f) const
 {
+       DCPOMATIC_ASSERT (_directory);
+
        boost::filesystem::path p;
-       p /= _directory;
+       p /= _directory.get();
        p /= f;
 
        boost::filesystem::create_directories (p.parent_path ());
@@ -943,9 +966,13 @@ Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
 vector<CPLSummary>
 Film::cpls () const
 {
+       if (!directory ()) {
+               return vector<CPLSummary> ();
+       }
+
        vector<CPLSummary> out;
 
-       boost::filesystem::path const dir = directory ();
+       boost::filesystem::path const dir = directory().get();
        for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
                if (
                        boost::filesystem::is_directory (*i) &&
@@ -1010,7 +1037,7 @@ Film::examine_content (shared_ptr<Content> c)
 void
 Film::examine_and_add_content (shared_ptr<Content> c)
 {
-       if (dynamic_pointer_cast<FFmpegContent> (c) && !_directory.empty ()) {
+       if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
                run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
        }
 
@@ -1037,6 +1064,7 @@ Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
        }
 
        add_content (content);
+
        if (Config::instance()->automatic_audio_analysis() && content->audio) {
                shared_ptr<Playlist> playlist (new Playlist);
                playlist->add (content);
@@ -1058,6 +1086,15 @@ Film::add_content (shared_ptr<Content> c)
                c->set_position (_playlist->subtitle_end ());
        }
 
+       if (_template_film) {
+               /* Take settings from the first piece of content of c's type in _template */
+               BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
+                       if (typeid(i.get()) == typeid(c.get())) {
+                               c->use_template (i);
+                       }
+               }
+       }
+
        _playlist->add (c);
 }
 
@@ -1487,3 +1524,26 @@ Film::fix_conflicting_settings ()
 
        return notes;
 }
+
+void
+Film::use_template (string name)
+{
+       _template_film.reset (new Film (optional<boost::filesystem::path>()));
+       _template_film->read_metadata (Config::instance()->template_path (name));
+       _use_isdcf_name = _template_film->_use_isdcf_name;
+       _dcp_content_type = _template_film->_dcp_content_type;
+       _container = _template_film->_container;
+       _resolution = _template_film->_resolution;
+       _j2k_bandwidth = _template_film->_j2k_bandwidth;
+       _video_frame_rate = _template_film->_video_frame_rate;
+       _signed = _template_film->_signed;
+       _encrypted = _template_film->_encrypted;
+       _audio_channels = _template_film->_audio_channels;
+       _sequence = _template_film->_sequence;
+       _three_d = _template_film->_three_d;
+       _interop = _template_film->_interop;
+       _audio_processor = _template_film->_audio_processor;
+       _reel_type = _template_film->_reel_type;
+       _reel_length = _template_film->_reel_length;
+       _upload_after_make_dcp = _template_film->_upload_after_make_dcp;
+}
index 82d1f78c043fe21329a0c80a1d74ff0e4921a1ad..5fa35b0653d67b46d1bd9845eb86d33b61ad8b19 100644 (file)
@@ -68,7 +68,7 @@ struct isdcf_name_test;
 class Film : public boost::enable_shared_from_this<Film>, public Signaller, public boost::noncopyable
 {
 public:
-       Film (boost::filesystem::path, bool log = true);
+       Film (boost::optional<boost::filesystem::path> dir);
        ~Film ();
 
        boost::filesystem::path info_file (DCPTimePeriod p) const;
@@ -91,9 +91,11 @@ public:
        boost::filesystem::path file (boost::filesystem::path f) const;
        boost::filesystem::path dir (boost::filesystem::path d) const;
 
-       std::list<std::string> read_metadata ();
+       void use_template (std::string name);
+       std::list<std::string> read_metadata (boost::optional<boost::filesystem::path> path = boost::optional<boost::filesystem::path> ());
        void write_metadata () const;
-       boost::shared_ptr<xmlpp::Document> metadata () const;
+       void write_template (boost::filesystem::path path) const;
+       boost::shared_ptr<xmlpp::Document> metadata (bool with_content_paths = true) const;
 
        std::string isdcf_name (bool if_created_now) const;
        std::string dcp_name (bool if_created_now = false) const;
@@ -200,7 +202,7 @@ public:
 
        /* GET */
 
-       boost::filesystem::path directory () const {
+       boost::optional<boost::filesystem::path> directory () const {
                return _directory;
        }
 
@@ -341,7 +343,7 @@ private:
        /** Complete path to directory containing the film metadata;
         *  must not be relative.
         */
-       boost::filesystem::path _directory;
+       boost::optional<boost::filesystem::path> _directory;
 
        /** Name for DCP-o-matic */
        std::string _name;
@@ -382,6 +384,8 @@ private:
 
        /** true if our state has changed since we last saved it */
        mutable bool _dirty;
+       /** film being used as a template, or 0 */
+       boost::shared_ptr<Film> _template_film;
 
        boost::signals2::scoped_connection _playlist_changed_connection;
        boost::signals2::scoped_connection _playlist_order_changed_connection;
index 825f6da25aab1267bffe75f8fba442585c4a44e6..b27483978c157f8ca86b934c8b37d5e6e5bd9e58 100644 (file)
@@ -100,10 +100,10 @@ ImageContent::technical_summary () const
 }
 
 void
-ImageContent::as_xml (xmlpp::Node* node) const
+ImageContent::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        node->add_child("Type")->add_child_text ("Image");
-       Content::as_xml (node);
+       Content::as_xml (node, with_paths);
 
        if (video) {
                video->as_xml (node);
index edcbec6ddb04122cc3211797d24edf40757510e2..660d2ef9fc21883e09918ddedee29009ae7c5a4b 100644 (file)
@@ -36,7 +36,7 @@ public:
        void examine (boost::shared_ptr<Job>);
        std::string summary () const;
        std::string technical_summary () const;
-       void as_xml (xmlpp::Node *) const;
+       void as_xml (xmlpp::Node *, bool with_paths) const;
        DCPTime full_length () const;
 
        std::string identifier () const;
index a8b5a26ebbc0c4e91a9385d2aa67b5a724cf9bb2..a30dde633cde3120bc29cdc2b762e3c723f802de 100644 (file)
@@ -175,10 +175,10 @@ Playlist::set_from_xml (shared_ptr<const Film> film, cxml::ConstNodePtr node, in
 
 /** @param node <Playlist> node */
 void
-Playlist::as_xml (xmlpp::Node* node)
+Playlist::as_xml (xmlpp::Node* node, bool with_content_paths)
 {
        BOOST_FOREACH (shared_ptr<Content> i, _content) {
-               i->as_xml (node->add_child ("Content"));
+               i->as_xml (node->add_child ("Content"), with_content_paths);
        }
 }
 
index e84b51a73e84c91372fb132ca05e59de258ddba4..0a5c087dea67ed076f0833e584ef2c5b3359a823 100644 (file)
@@ -45,7 +45,7 @@ public:
        Playlist ();
        ~Playlist ();
 
-       void as_xml (xmlpp::Node *);
+       void as_xml (xmlpp::Node *, bool with_content_paths);
        void set_from_xml (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int, std::list<std::string> &);
 
        void add (boost::shared_ptr<Content>);
index 692134654a84b3301f40ec02fbae8660b19cc852..72e10e86ba78a7170e5902c506cd70ab0d4fe788 100644 (file)
@@ -112,11 +112,13 @@ ReelWriter::ReelWriter (
                        _sound_asset->set_key (_film->key ());
                }
 
+               DCPOMATIC_ASSERT (_film->directory());
+
                /* Write the sound asset into the film directory so that we leave the creation
                   of the DCP directory until the last minute.
                */
                _sound_asset_writer = _sound_asset->start_write (
-                       _film->directory() / audio_asset_filename (_sound_asset, _reel_index, _reel_count, _content_summary),
+                       _film->directory().get() / audio_asset_filename (_sound_asset, _reel_index, _reel_count, _content_summary),
                        _film->interop() ? dcp::INTEROP : dcp::SMPTE
                        );
        }
index c57c72fb1996560d0dd2aa5d3f0b8bcd8e6cb7f4..34ffa3f4ba3790f278950b6d9d60c50292a330a8 100644 (file)
@@ -395,3 +395,22 @@ SubtitleContent::set_fade_out (ContentTime t)
 {
        maybe_set (_fade_out, t, SubtitleContentProperty::FADE_OUT);
 }
+
+void
+SubtitleContent::use_template (shared_ptr<const SubtitleContent> c)
+{
+       _use = c->_use;
+       _burn = c->_burn;
+       _x_offset = c->_x_offset;
+       _y_offset = c->_y_offset;
+       _x_scale = c->_x_scale;
+       _y_scale = c->_y_scale;
+       _fonts = c->_fonts;
+       _colour = c->_colour;
+       _outline = c->_outline;
+       _shadow = c->_shadow;
+       _effect_colour = c->_effect_colour;
+       _line_spacing = c->_line_spacing;
+       _fade_in = c->_fade_in;
+       _fade_out = c->_fade_out;
+}
index fd02d032ae79f384463210a65d036034411b496d..ddc97ce9fdd0c69ab7be5813a2396d3d007cdc62 100644 (file)
@@ -62,6 +62,7 @@ public:
 
        void as_xml (xmlpp::Node *) const;
        std::string identifier () const;
+       void use_template (boost::shared_ptr<const SubtitleContent> c);
 
        void add_font (boost::shared_ptr<Font> font);
 
index 63144766a50cffe172a671ca24b64a9dfe9c9eb6..08722a065d0f66bdfdce8e907cf0c4603b4075fa 100644 (file)
@@ -75,10 +75,10 @@ TextSubtitleContent::technical_summary () const
 }
 
 void
-TextSubtitleContent::as_xml (xmlpp::Node* node) const
+TextSubtitleContent::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        node->add_child("Type")->add_child_text ("TextSubtitle");
-       Content::as_xml (node);
+       Content::as_xml (node, with_paths);
 
        if (subtitle) {
                subtitle->as_xml (node);
index 3b9d396f610dd2f56f7d5b069f099d23734d9371..fd0bad12a310957ff23e657d4cfe2e9e313278c4 100644 (file)
@@ -38,7 +38,7 @@ public:
        void examine (boost::shared_ptr<Job>);
        std::string summary () const;
        std::string technical_summary () const;
-       void as_xml (xmlpp::Node *) const;
+       void as_xml (xmlpp::Node *, bool with_paths) const;
        DCPTime full_length () const;
 
 private:
index 2b953f75bf8b60e9707ccb12a00e9a8b2b4dacc7..ed14026087beacc25ac4e71df52f58bb97417df1 100644 (file)
@@ -525,3 +525,14 @@ VideoContent::set_fade_out (Frame t)
 {
        maybe_set (_fade_out, t, VideoContentProperty::FADE_OUT);
 }
+
+void
+VideoContent::use_template (shared_ptr<const VideoContent> c)
+{
+       _colour_conversion = c->_colour_conversion;
+       _frame_type = c->_frame_type;
+       _crop = c->_crop;
+       _scale = c->_scale;
+       _fade_in = c->_fade_in;
+       _fade_out = c->_fade_out;
+}
index d5ba38022b883e16facf06094681994e1b6324e6..623a1858bc48c6031b602c05bdb27f5814de8eb0 100644 (file)
@@ -57,6 +57,7 @@ public:
        void as_xml (xmlpp::Node *) const;
        std::string technical_summary () const;
        std::string identifier () const;
+       void use_template (boost::shared_ptr<const VideoContent> c);
 
        Frame length () const {
                boost::mutex::scoped_lock lm (_mutex);
index d86fc4cf6e296ead657f49b98f9ab275a49f463c..1f416312233f3efe21edae87ccfbc248abfa2d3a 100644 (file)
@@ -102,10 +102,10 @@ VideoMXFContent::identifier () const
 }
 
 void
-VideoMXFContent::as_xml (xmlpp::Node* node) const
+VideoMXFContent::as_xml (xmlpp::Node* node, bool with_paths) const
 {
        node->add_child("Type")->add_child_text ("VideoMXF");
-       Content::as_xml (node);
+       Content::as_xml (node, with_paths);
        video->as_xml (node);
 }
 
index ab5375c6bc471c7be4925cdfd3e5afffed5651f5..08946242cddc8272feae8aa060b0daf916915324 100644 (file)
@@ -34,7 +34,7 @@ public:
        std::string summary () const;
        std::string technical_summary () const;
        std::string identifier () const;
-       void as_xml (xmlpp::Node* node) const;
+       void as_xml (xmlpp::Node* node, bool with_paths) const;
        DCPTime full_length () const;
        void add_properties (std::list<UserProperty>& p) const;
        void set_default_colour_conversion ();
index d4695f1fa08b04475dd4210a3ff7ee40c5b02601..63e5ca6b14834e85dc1acbed2aac0a2845cace52 100644 (file)
@@ -38,6 +38,7 @@
 #include "wx/content_panel.h"
 #include "wx/report_problem_dialog.h"
 #include "wx/video_waveform_dialog.h"
+#include "wx/save_template_dialog.h"
 #include "lib/film.h"
 #include "lib/config.h"
 #include "lib/util.h"
@@ -143,6 +144,7 @@ enum {
        ID_file_new = 1,
        ID_file_open,
        ID_file_save,
+       ID_file_save_as_template,
        ID_file_history,
        /* Allow spare IDs after _history for the recent files list */
        ID_content_scale_to_fit_width = 100,
@@ -210,6 +212,7 @@ public:
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&DOMFrame::file_new, this),                ID_file_new);
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&DOMFrame::file_open, this),               ID_file_open);
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&DOMFrame::file_save, this),               ID_file_save);
+               Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&DOMFrame::file_save_as_template, this),   ID_file_save_as_template);
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&DOMFrame::file_history, this, _1),        ID_file_history, ID_file_history + HISTORY_SIZE);
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&DOMFrame::file_exit, this),               wxID_EXIT);
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&DOMFrame::edit_preferences, this),        wxID_PREFERENCES);
@@ -278,9 +281,12 @@ public:
                }
        }
 
-       void new_film (boost::filesystem::path path)
+       void new_film (boost::filesystem::path path, optional<string> template_name)
        {
                shared_ptr<Film> film (new Film (path));
+               if (template_name) {
+                       film->use_template (template_name.get());
+               }
                film->write_metadata ();
                film->set_name (path.filename().generic_string());
                set_film (film);
@@ -320,7 +326,9 @@ public:
                delete _video_waveform_dialog;
                _video_waveform_dialog = 0;
                set_menu_sensitivity ();
-               Config::instance()->add_to_history (_film->directory ());
+               if (_film->directory()) {
+                       Config::instance()->add_to_history (_film->directory().get());
+               }
        }
 
        shared_ptr<Film> film () const {
@@ -346,27 +354,27 @@ private:
 
                if (r == wxID_OK) {
 
-                       if (boost::filesystem::is_directory (d->get_path()) && !boost::filesystem::is_empty(d->get_path())) {
+                       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->get_path().string().c_str())
+                                                                    d->path().string().c_str())
                                                    )
                                            )) {
                                        return;
                                }
-                       } else if (boost::filesystem::is_regular_file (d->get_path())) {
+                       } 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->get_path().c_str())
+                                       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->get_path ());
+                               new_film (d->path(), d->template_name());
                        }
                }
 
@@ -404,6 +412,22 @@ private:
                _film->write_metadata ();
        }
 
+       void file_save_as_template ()
+       {
+               SaveTemplateDialog* d = new SaveTemplateDialog (this);
+               int const r = d->ShowModal ();
+               if (r == wxID_OK) {
+                       bool ok = true;
+                       if (Config::instance()->existing_template (d->name ())) {
+                               ok = confirm_dialog (d, _("There is already a template with this name.  Do you want to overwrite it?"));
+                       }
+                       if (ok) {
+                               Config::instance()->save_template (_film, d->name ());
+                       }
+               }
+               d->Destroy ();
+       }
+
        void file_history (wxCommandEvent& event)
        {
                vector<boost::filesystem::path> history = Config::instance()->history ();
@@ -528,7 +552,8 @@ private:
                                boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query);
                                Socket socket (5);
                                socket.connect (*endpoint_iterator);
-                               string s = _film->directory().string ();
+                               DCPOMATIC_ASSERT (_film->directory ());
+                               string s = _film->directory()->string ();
                                socket.write (s.length() + 1);
                                socket.write ((uint8_t *) s.c_str(), s.length() + 1);
                                /* OK\0 */
@@ -616,6 +641,7 @@ private:
 
        void jobs_show_dcp ()
        {
+               DCPOMATIC_ASSERT (_film->directory ());
 #ifdef DCPOMATIC_WINDOWS
                wstringstream args;
                args << "/select," << _film->dir (_film->dcp_name(false));
@@ -625,14 +651,14 @@ private:
 #ifdef DCPOMATIC_LINUX
                int r = system ("which nautilus");
                if (WEXITSTATUS (r) == 0) {
-                       r = system (string ("nautilus " + _film->directory().string()).c_str ());
+                       r = system (string ("nautilus " + _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 ("konqueror " + _film->directory().string()).c_str ());
+                               r = system (string ("konqueror " + _film->directory()->string()).c_str ());
                                if (WEXITSTATUS (r)) {
                                        error_dialog (this, _("Could not show DCP (could not run konqueror)"));
                                }
@@ -826,6 +852,8 @@ private:
                add_item (_file_menu, _("&Open...\tCtrl-O"), ID_file_open, ALWAYS);
                _file_menu->AppendSeparator ();
                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();
 
@@ -1055,7 +1083,7 @@ private:
                }
 
                if (!_film_to_create.empty ()) {
-                       _frame->new_film (_film_to_create);
+                       _frame->new_film (_film_to_create, optional<string> ());
                        if (!_content_to_add.empty ()) {
                                _frame->film()->examine_and_add_content (content_factory (_frame->film(), _content_to_add));
                        }
index a112e457a4af514463a69e2fc425f4c98a6c50b1..9e2d6969d9c2c39ce2a1d5d7111c412490545279 100644 (file)
@@ -45,7 +45,7 @@ using boost::shared_ptr;
 using boost::thread;
 using boost::scoped_array;
 
-static std::string film_to_load;
+static boost::optional<boost::filesystem::path> film_to_load;
 
 enum {
        ID_file_add_film = 1,
@@ -318,13 +318,15 @@ class App : public wxApp
                this->Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
 
                shared_ptr<Film> film;
-               if (!film_to_load.empty() && boost::filesystem::is_directory (film_to_load)) {
+               if (film_to_load && boost::filesystem::is_directory (film_to_load.get())) {
                        try {
-                               film.reset (new Film (film_to_load));
+                               film.reset (new Film (film_to_load.get()));
                                film->read_metadata ();
                                film->make_dcp ();
                        } catch (exception& e) {
-                               error_dialog (0, std_to_wx (String::compose (wx_to_std (_("Could not load film %1 (%2)")), film_to_load, e.what())));
+                               error_dialog (
+                                       0, std_to_wx (String::compose (wx_to_std (_("Could not load film %1 (%2)")), film_to_load->string(), e.what()))
+                                       );
                        }
                }
 
index bd1a4e4aa60b8aceed165f708e631984d0de699c..00087d6a0c9425a2a0e9db46b757c6e7215dbbda 100644 (file)
@@ -173,7 +173,7 @@ show_servers ()
 int
 main (int argc, char* argv[])
 {
-       string film_dir;
+       boost::filesystem::path film_dir;
        bool progress = true;
        bool no_remote = false;
        optional<int> threads;
@@ -278,7 +278,7 @@ main (int argc, char* argv[])
                film.reset (new Film (film_dir));
                film->read_metadata ();
        } catch (std::exception& e) {
-               cerr << argv[0] << ": error reading film `" << film_dir << "' (" << e.what() << ")\n";
+               cerr << argv[0] << ": error reading film `" << film_dir.string() << "' (" << e.what() << ")\n";
                exit (EXIT_FAILURE);
        }
 
index fd950807c3eed51480ed75ecdccea160c0e935a6..a3adc8e3e32312567c5bd0411abeba232ec05ac7 100644 (file)
@@ -44,6 +44,7 @@ using std::list;
 using std::exception;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
+using boost::optional;
 
 static void
 syntax (string n)
@@ -52,6 +53,7 @@ syntax (string n)
             << "  -v, --version                 show DCP-o-matic version\n"
             << "  -h, --help                    show this help\n"
             << "  -n, --name <name>             film name\n"
+            << "  -t, --template <name>         template name\n"
             << "  -c, --dcp-content-type <type> FTR, SHR, TLR, TST, XSN, RTG, TSR, POL, PSA or ADV\n"
             << "      --container-ratio <ratio> 119, 133, 137, 138, 166, 178, 185 or 239\n"
             << "      --content-ratio <ratio>   119, 133, 137, 138, 166, 178, 185 or 239\n"
@@ -88,6 +90,7 @@ main (int argc, char* argv[])
        dcpomatic_setup ();
 
        string name;
+       optional<string> template_name;
        DCPContentType const * dcp_content_type = DCPContentType::from_isdcf_name ("TST");
        Ratio const * container_ratio = 0;
        Ratio const * content_ratio = 0;
@@ -103,6 +106,7 @@ main (int argc, char* argv[])
                        { "version", no_argument, 0, 'v'},
                        { "help", no_argument, 0, 'h'},
                        { "name", required_argument, 0, 'n'},
+                       { "template", required_argument, 0, 'f'},
                        { "dcp-content-type", required_argument, 0, 'c'},
                        { "container-ratio", required_argument, 0, 'A'},
                        { "content-ratio", required_argument, 0, 'B'},
@@ -114,7 +118,7 @@ main (int argc, char* argv[])
                        { 0, 0, 0, 0}
                };
 
-               int c = getopt_long (argc, argv, "vhn:c:A:B:C:s:o:DE", long_options, &option_index);
+               int c = getopt_long (argc, argv, "vhn:f:c:A:B:C:s:o:DE", long_options, &option_index);
                if (c == -1) {
                        break;
                }
@@ -129,6 +133,9 @@ main (int argc, char* argv[])
                case 'n':
                        name = optarg;
                        break;
+               case 't':
+                       template_name = optarg;
+                       break;
                case 'c':
                        dcp_content_type = DCPContentType::from_isdcf_name (optarg);
                        if (dcp_content_type == 0) {
@@ -206,7 +213,10 @@ main (int argc, char* argv[])
        }
 
        try {
-               shared_ptr<Film> film (new Film (output, false));
+               shared_ptr<Film> film (new Film (output));
+               if (template_name) {
+                       film->use_template (template_name.get());
+               }
                film->set_name (name);
 
                film->set_container (container_ratio);
index 65b98ee424dffea948573b578538a3c1518a4014..f0097d7de38cd14b06256009d174ca60100f8a96 100644 (file)
@@ -39,6 +39,7 @@ using std::cerr;
 using std::list;
 using std::vector;
 using boost::shared_ptr;
+using boost::optional;
 
 static void
 help ()
@@ -217,7 +218,7 @@ int main (int argc, char* argv[])
                valid_to = valid_from.get() + duration_from_string (duration_string);
        }
 
-       string const film_dir = argv[optind];
+       boost::filesystem::path const film_dir = argv[optind];
 
        dcpomatic_setup_path_encoding ();
        dcpomatic_setup ();
@@ -230,7 +231,7 @@ int main (int argc, char* argv[])
                        cout << "Read film " << film->name () << "\n";
                }
        } catch (std::exception& e) {
-               cerr << program_name << ": error reading film `" << film_dir << "' (" << e.what() << ")\n";
+               cerr << program_name << ": error reading film `" << film_dir.string() << "' (" << e.what() << ")\n";
                exit (EXIT_FAILURE);
        }
 
index 5e50df227ed703f87d229005e861d418df00c630..cb3d49f3135dd0e722114049e660f079508ece7d 100644 (file)
@@ -41,6 +41,7 @@ using std::cerr;
 using std::string;
 using std::pair;
 using boost::shared_ptr;
+using boost::optional;
 using dcp::Data;
 
 static shared_ptr<Film> film;
@@ -101,7 +102,7 @@ help (string n)
 int
 main (int argc, char* argv[])
 {
-       string film_dir;
+       boost::filesystem::path film_dir;
        string server_host;
 
        while (true) {
@@ -132,7 +133,7 @@ main (int argc, char* argv[])
                }
        }
 
-       if (server_host.empty() || film_dir.empty()) {
+       if (server_host.empty() || film_dir.string().empty()) {
                help (argv[0]);
                exit (EXIT_FAILURE);
        }
index a5e1e82e9b5ed747d4847d7e52bd2550cee1886c..edf5d3bd71fe9cfabb13ef37e86a6300bba8dc3a 100644 (file)
@@ -127,8 +127,8 @@ FilmEditor::set_film (shared_ptr<Film> film)
                _film->ContentChanged.connect (bind (&FilmEditor::film_content_changed, this, _2));
        }
 
-       if (_film) {
-               FileChanged (_film->directory ());
+       if (_film && _film->directory()) {
+               FileChanged (_film->directory().get());
        } else {
                FileChanged ("");
        }
index 04b9e10578889d8f43b59f50f1d178dfd193d9ce..907b289b5c5fba150902771f1df7125ef0353784 100644 (file)
 
 */
 
-#include <boost/filesystem.hpp>
-#include <wx/stdpaths.h>
 #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;
@@ -53,21 +54,49 @@ 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);
+
        _name->SetFocus ();
+       _template_name->Enable (false);
+
+       BOOST_FOREACH (string i, Config::instance()->template_names ()) {
+               _template_name->Append (std_to_wx (i));
+       }
+
+       _use_template->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, 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::get_path () const
+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()));
+}
index 6dc83d815b546528ee54c7ae2706a32024fc036a..81dd29feab59747dd18fbb090a728620a3e25782 100644 (file)
@@ -31,14 +31,19 @@ public:
        NewFilmDialog (wxWindow *);
        ~NewFilmDialog ();
 
-       boost::filesystem::path get_path () const;
+       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;
 };
diff --git a/src/wx/save_template_dialog.cc b/src/wx/save_template_dialog.cc
new file mode 100644 (file)
index 0000000..eff61a2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    Copyright (C) 2016 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 "save_template_dialog.h"
+#include "wx_util.h"
+#include <boost/foreach.hpp>
+
+using std::string;
+
+SaveTemplateDialog::SaveTemplateDialog (wxWindow* parent)
+       : TableDialog (parent, _("Save template"), 2, 1, true)
+{
+       add (_("Template name"), true);
+       _name = add (new wxTextCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (300, -1)));
+       _name->SetFocus ();
+       layout ();
+}
+
+string
+SaveTemplateDialog::name () const
+{
+       return wx_to_std (_name->GetValue ());
+}
diff --git a/src/wx/save_template_dialog.h b/src/wx/save_template_dialog.h
new file mode 100644 (file)
index 0000000..8069fa2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+    Copyright (C) 2016 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 "table_dialog.h"
+
+class SaveTemplateDialog : public TableDialog
+{
+public:
+       SaveTemplateDialog (wxWindow* parent);
+
+       std::string name () const;
+
+private:
+       wxTextCtrl* _name;
+};
index ee77e6c20b5c9eba1e3a840d1c79437145d66622..1f9aed6b3c4c79aebaf453630ce9ad36e73bb1fe 100644 (file)
@@ -75,6 +75,7 @@ sources = """
           repeat_dialog.cc
           report_problem_dialog.cc
           rgba_colour_picker.cc
+          save_template_dialog.cc
           screen_dialog.cc
           screens_panel.cc
           self_dkdm_dialog.cc
index a28119110f874bd3b728641447577ca4286bef51..748695efe79d19c955c0be9b8ff45dce54ef1a3f 100644 (file)
@@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_dcp_test)
 BOOST_AUTO_TEST_CASE (ffmpeg_have_dcp_test)
 {
        boost::filesystem::path p = test_film_dir ("ffmpeg_dcp_test");
-       shared_ptr<Film> film (new Film (p.string ()));
+       shared_ptr<Film> film (new Film (p));
        film->read_metadata ();
        BOOST_CHECK (!film->cpls().empty());
 
index d29a91ed2b60ba6818745800bb0611bdbffb91f9..f733567d5b0f50922e4e4fccdd96cfe71c241bc7 100644 (file)
@@ -87,7 +87,7 @@ check_subtitle_file (shared_ptr<Film> film, boost::filesystem::path ref)
 {
        /* Find the subtitle file and check it */
        for (
-               boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (film->directory() / film->dcp_name (false));
+               boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (film->directory().get() / film->dcp_name (false));
                i != boost::filesystem::directory_iterator ();
                ++i) {
 
index f6c65b1af67d34008744d868cc30b38bb4dfde40..ed8c2042492af83dca8534a28cacf42577440546 100644 (file)
@@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE (ssa_subtitle_test1)
 
        /* Find the subtitle file and check it */
        for (
-               boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (film->directory() / film->dcp_name (false));
+               boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (film->directory().get() / film->dcp_name (false));
                i != boost::filesystem::directory_iterator ();
                ++i) {
 
index 0b98f7b8226ba4cdc58a3f5705455e765eccb000..86dddbeda07c6c8cbf47e52649f6d5abf3974e01 100644 (file)
@@ -112,7 +112,7 @@ new_test_film (string name)
                boost::filesystem::remove_all (p);
        }
 
-       shared_ptr<Film> film = shared_ptr<Film> (new Film (p.string()));
+       shared_ptr<Film> film = shared_ptr<Film> (new Film (p));
        film->write_metadata ();
        return film;
 }