/** Construct default configuration */
Config::Config ()
- : _num_local_encoding_threads (2)
+ : _num_local_encoding_threads (max (2U, boost::thread::hardware_concurrency()))
, _server_port (6192)
- , _reference_scaler (Scaler::from_id ("bicubic"))
- , _tms_path (".")
- , _sound_processor (SoundProcessor::from_id ("dolby_cp750"))
+ , _tms_path (N_("."))
+ , _sound_processor (SoundProcessor::from_id (N_("dolby_cp750")))
+ , _default_still_length (10)
+ , _default_container (Ratio::from_id ("185"))
+ , _default_dcp_content_type (DCPContentType::from_dci_name ("TST"))
+ , _default_j2k_bandwidth (200000000)
{
- ifstream f (read_file().c_str ());
- string line;
+ _allowed_dcp_frame_rates.push_back (24);
+ _allowed_dcp_frame_rates.push_back (25);
+ _allowed_dcp_frame_rates.push_back (30);
+ _allowed_dcp_frame_rates.push_back (48);
+ _allowed_dcp_frame_rates.push_back (50);
+ _allowed_dcp_frame_rates.push_back (60);
+
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB non-linearised"), 2.4, false, libdcp::colour_matrix::srgb_to_xyz, 2.6));
+ }
+
+ void
+ Config::read ()
+ {
+ if (!boost::filesystem::exists (file (false))) {
+ read_old_metadata ();
+ return;
+ }
- shared_ptr<Cinema> cinema;
- shared_ptr<Screen> screen;
+ cxml::Document f ("Config");
+ f.read_file (file (false));
+ optional<string> c;
+
+ _num_local_encoding_threads = f.number_child<int> ("NumLocalEncodingThreads");
+ _default_directory = f.string_child ("DefaultDirectory");
+ _server_port = f.number_child<int> ("ServerPort");
+ list<shared_ptr<cxml::Node> > servers = f.node_children ("Server");
+ for (list<shared_ptr<cxml::Node> >::iterator i = servers.begin(); i != servers.end(); ++i) {
+ _servers.push_back (ServerDescription (*i));
+ }
+
+ _tms_ip = f.string_child ("TMSIP");
+ _tms_path = f.string_child ("TMSPath");
+ _tms_user = f.string_child ("TMSUser");
+ _tms_password = f.string_child ("TMSPassword");
+
+ c = f.optional_string_child ("SoundProcessor");
+ if (c) {
+ _sound_processor = SoundProcessor::from_id (c.get ());
+ }
+
+ _language = f.optional_string_child ("Language");
+
+ c = f.optional_string_child ("DefaultContainer");
+ if (c) {
+ _default_container = Ratio::from_id (c.get ());
+ }
+
+ c = f.optional_string_child ("DefaultDCPContentType");
+ if (c) {
+ _default_dcp_content_type = DCPContentType::from_dci_name (c.get ());
+ }
+
+ _dcp_metadata.issuer = f.optional_string_child ("DCPMetadataIssuer").get_value_or ("");
+ _dcp_metadata.creator = f.optional_string_child ("DCPMetadataCreator").get_value_or ("");
+
+ _default_dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
+ _default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
+ _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
+
+ list<shared_ptr<cxml::Node> > cc = f.node_children ("ColourConversion");
+
+ if (!cc.empty ()) {
+ _colour_conversions.clear ();
+ }
+
+ for (list<shared_ptr<cxml::Node> >::iterator i = cc.begin(); i != cc.end(); ++i) {
+ _colour_conversions.push_back (PresetColourConversion (*i));
+ }
+ }
+
+ void
+ Config::read_old_metadata ()
+ {
+ ifstream f (file(true).c_str ());
+ string line;
++
while (getline (f, line)) {
if (line.empty ()) {
continue;
class Scaler;
class Filter;
class SoundProcessor;
+ class DCPContentType;
+ class Ratio;
+class Cinema;
/** @class Config
* @brief A singleton class holding configuration.
return _sound_processor;
}
+ std::list<boost::shared_ptr<Cinema> > cinemas () const {
+ return _cinemas;
+ }
++
+ std::list<int> allowed_dcp_frame_rates () const {
+ return _allowed_dcp_frame_rates;
+ }
+
+ DCIMetadata default_dci_metadata () const {
+ return _default_dci_metadata;
+ }
+
+ boost::optional<std::string> language () const {
+ return _language;
+ }
+
+ int default_still_length () const {
+ return _default_still_length;
+ }
+
+ Ratio const * default_container () const {
+ return _default_container;
+ }
+
+ DCPContentType const * default_dcp_content_type () const {
+ return _default_dcp_content_type;
+ }
+
+ libdcp::XMLMetadata dcp_metadata () const {
+ return _dcp_metadata;
+ }
+
+ int default_j2k_bandwidth () const {
+ return _default_j2k_bandwidth;
+ }
+
+ std::vector<PresetColourConversion> colour_conversions () const {
+ return _colour_conversions;
+ }
/** @param n New number of local encoding threads */
void set_num_local_encoding_threads (int n) {
_tms_password = p;
}
+ void add_cinema (boost::shared_ptr<Cinema> c) {
+ _cinemas.push_back (c);
+ }
+
+ void remove_cinema (boost::shared_ptr<Cinema> c) {
+ _cinemas.remove (c);
+ }
++
+ void set_allowed_dcp_frame_rates (std::list<int> const & r) {
+ _allowed_dcp_frame_rates = r;
+ }
+
+ void set_default_dci_metadata (DCIMetadata d) {
+ _default_dci_metadata = d;
+ }
+
+ void set_language (std::string l) {
+ _language = l;
+ }
+
+ void unset_language () {
+ _language = boost::none;
+ }
+
+ void set_default_still_length (int s) {
+ _default_still_length = s;
+ }
+
+ void set_default_container (Ratio const * c) {
+ _default_container = c;
+ }
+
+ void set_default_dcp_content_type (DCPContentType const * t) {
+ _default_dcp_content_type = t;
+ }
+
+ void set_dcp_metadata (libdcp::XMLMetadata m) {
+ _dcp_metadata = m;
+ }
+
+ void set_default_j2k_bandwidth (int b) {
+ _default_j2k_bandwidth = b;
+ }
+
+ void set_colour_conversions (std::vector<PresetColourConversion> const & c) {
+ _colour_conversions = c;
+ }
void write () const;
+ std::string crypt_chain_directory () const;
+
static Config* instance ();
+ static void drop ();
private:
Config ();
std::string _tms_password;
/** Our sound processor */
SoundProcessor const * _sound_processor;
+ std::list<int> _allowed_dcp_frame_rates;
+ /** Default DCI metadata for newly-created Films */
+ DCIMetadata _default_dci_metadata;
+ boost::optional<std::string> _language;
+ int _default_still_length;
+ Ratio const * _default_container;
+ DCPContentType const * _default_dcp_content_type;
+ libdcp::XMLMetadata _dcp_metadata;
+ int _default_j2k_bandwidth;
+ std::vector<PresetColourConversion> _colour_conversions;
+ std::list<boost::shared_ptr<Cinema> > _cinemas;
+
/** Singleton instance, or 0 */
static Config* _instance;
};
{}
};
+class KDMError : public StringError
+{
+public:
+ KDMError (std::string s)
+ : StringError (s)
+ {}
+};
++
+ class PixelFormatError : public StringError
+ {
+ public:
+ PixelFormatError (std::string o, AVPixelFormat f);
+ };
+
+ class ExceptionStore
+ {
+ public:
+ bool thrown () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _exception;
+ }
+
+ void rethrow () {
+ boost::mutex::scoped_lock lm (_mutex);
+ boost::rethrow_exception (_exception);
+ }
+
+ protected:
+
+ void store_current () {
+ boost::mutex::scoped_lock lm (_mutex);
+ _exception = boost::current_exception ();
+ }
+
+ private:
+ boost::exception_ptr _exception;
+ mutable boost::mutex _mutex;
+ };
+
+
+
+ #endif
#include <boost/lexical_cast.hpp>
#include <boost/date_time.hpp>
#include <libxml++/libxml++.h>
- #include <libdcp/certificates.h>
- #include "cinema.h"
+ #include <libcxml/cxml.h>
+#include <libdcp/crypt_chain.h>
++#include <libdcp/cpl.h>
#include "film.h"
- #include "format.h"
#include "job.h"
- #include "filter.h"
- #include "transcoder.h"
#include "util.h"
#include "job_manager.h"
- #include "ab_transcode_job.h"
#include "transcode_job.h"
#include "scp_dcp_job.h"
- #include "make_dcp_job.h"
#include "log.h"
- #include "options.h"
#include "exceptions.h"
#include "examine_content_job.h"
#include "scaler.h"
- #include "decoder_factory.h"
#include "config.h"
- #include "check_hashes_job.h"
#include "version.h"
#include "ui_signaller.h"
- #include "video_decoder.h"
- #include "audio_decoder.h"
- #include "external_audio_decoder.h"
+ #include "playlist.h"
+ #include "player.h"
+ #include "dcp_content_type.h"
+ #include "ratio.h"
+ #include "cross.h"
++#include "cinema.h"
+
+ #include "i18n.h"
using std::string;
using std::stringstream;
using boost::ends_with;
using boost::starts_with;
using boost::optional;
+ using libdcp::Size;
- int const Film::state_version = 1;
+ int const Film::state_version = 4;
- /** Construct a Film object in a given directory, reading any metadata
- * file that exists in that directory. An exception will be thrown if
- * must_exist is true and the specified directory does not exist.
+ /** Construct a Film object in a given directory.
*
- * @param d Film directory.
- * @param must_exist true to throw an exception if does not exist.
+ * @param dir Film directory.
*/
- Film::Film (string d, bool must_exist)
- : _use_dci_name (true)
- , _trust_content_header (true)
- , _dcp_content_type (0)
- , _format (0)
+ Film::Film (boost::filesystem::path dir)
+ : _playlist (new Playlist)
+ , _use_dci_name (true)
+ , _dcp_content_type (Config::instance()->default_dcp_content_type ())
+ , _container (Config::instance()->default_container ())
+ , _resolution (RESOLUTION_2K)
, _scaler (Scaler::from_id ("bicubic"))
- , _dcp_trim_start (0)
- , _dcp_trim_end (0)
- , _dcp_ab (false)
- , _use_content_audio (true)
- , _audio_gain (0)
- , _audio_delay (0)
- , _still_duration (10)
, _with_subtitles (false)
- , _subtitle_offset (0)
- , _subtitle_scale (1)
+ , _encrypted (false)
- , _colour_lut (0)
- , _j2k_bandwidth (200000000)
- , _frames_per_second (0)
+ , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
+ , _dci_metadata (Config::instance()->default_dci_metadata ())
+ , _video_frame_rate (24)
+ , _audio_channels (MAX_AUDIO_CHANNELS)
+ , _three_d (false)
+ , _sequence_video (true)
+ , _interop (false)
, _dirty (false)
{
+ set_dci_date_today ();
+
+ _playlist->Changed.connect (bind (&Film::playlist_changed, this));
+ _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
+
/* Make state.directory a complete path without ..s (where possible)
(Code swiped from Adam Bowen on stackoverflow)
*/
boost::filesystem::create_directories (directory());
- string const m = file ("metadata");
- ofstream f (m.c_str ());
- if (!f.good ()) {
- throw CreateFileError (m);
- }
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("Metadata");
- f << "version " << state_version << "\n";
+ root->add_child("Version")->add_child_text (lexical_cast<string> (state_version));
+ root->add_child("Name")->add_child_text (_name);
+ root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0");
- /* User stuff */
- f << "name " << _name << "\n";
- f << "use_dci_name " << _use_dci_name << "\n";
- f << "content " << _content << "\n";
- f << "trust_content_header " << (_trust_content_header ? "1" : "0") << "\n";
if (_dcp_content_type) {
- f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
- }
- if (_format) {
- f << "format " << _format->as_metadata () << "\n";
- }
- f << "left_crop " << _crop.left << "\n";
- f << "right_crop " << _crop.right << "\n";
- f << "top_crop " << _crop.top << "\n";
- f << "bottom_crop " << _crop.bottom << "\n";
- for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
- f << "filter " << (*i)->id () << "\n";
- }
- f << "scaler " << _scaler->id () << "\n";
- f << "dcp_trim_start " << _dcp_trim_start << "\n";
- f << "dcp_trim_end " << _dcp_trim_end << "\n";
- if (_reel_size) {
- f << "reel_size " << _reel_size.get() << "\n";
- }
- f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
- if (_content_audio_stream) {
- f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n";
- }
- for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) {
- f << "external_audio " << *i << "\n";
- }
- f << "use_content_audio " << (_use_content_audio ? "1" : "0") << "\n";
- f << "audio_gain " << _audio_gain << "\n";
- f << "audio_delay " << _audio_delay << "\n";
- f << "still_duration " << _still_duration << "\n";
- if (_subtitle_stream) {
- f << "selected_subtitle_stream " << _subtitle_stream->to_string() << "\n";
+ root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ());
}
- f << "with_subtitles " << _with_subtitles << "\n";
- f << "subtitle_offset " << _subtitle_offset << "\n";
- f << "subtitle_scale " << _subtitle_scale << "\n";
- f << "encrypted " << _encrypted << "\n";
- f << "colour_lut " << _colour_lut << "\n";
- f << "j2k_bandwidth " << _j2k_bandwidth << "\n";
- f << "audio_language " << _audio_language << "\n";
- f << "subtitle_language " << _subtitle_language << "\n";
- f << "territory " << _territory << "\n";
- f << "rating " << _rating << "\n";
- f << "studio " << _studio << "\n";
- f << "facility " << _facility << "\n";
- f << "package_type " << _package_type << "\n";
-
- f << "width " << _size.width << "\n";
- f << "height " << _size.height << "\n";
- f << "length " << _length.get_value_or(0) << "\n";
- f << "content_digest " << _content_digest << "\n";
-
- for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
- f << "content_audio_stream " << (*i)->to_string () << "\n";
- }
-
- f << "external_audio_stream " << _external_audio_stream->to_string() << "\n";
- for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
- f << "subtitle_stream " << (*i)->to_string () << "\n";
+ if (_container) {
+ root->add_child("Container")->add_child_text (_container->id ());
}
- f << "frames_per_second " << _frames_per_second << "\n";
+ root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
+ root->add_child("Scaler")->add_child_text (_scaler->id ());
+ root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
+ root->add_child("J2KBandwidth")->add_child_text (lexical_cast<string> (_j2k_bandwidth));
+ _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
+ root->add_child("VideoFrameRate")->add_child_text (lexical_cast<string> (_video_frame_rate));
+ root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
+ root->add_child("AudioChannels")->add_child_text (lexical_cast<string> (_audio_channels));
+ root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
+ root->add_child("SequenceVideo")->add_child_text (_sequence_video ? "1" : "0");
+ root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
++ root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
+ _playlist->as_xml (root->add_child ("Playlist"));
+
+ doc.write_to_file_formatted (file ("metadata.xml"));
_dirty = false;
}
}
void
- Film::set_content_audio_stream (shared_ptr<AudioStream> s)
+ Film::set_dci_date_today ()
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content_audio_stream = s;
- }
- signal_changed (CONTENT_AUDIO_STREAM);
+ _dci_date = boost::gregorian::day_clock::local_day ();
}
- void
- Film::set_external_audio (vector<string> a)
+ string
+ Film::info_path (int f, Eyes e) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _external_audio = a;
- }
+ boost::filesystem::path p;
+ p /= info_dir ();
- shared_ptr<DecodeOptions> o (new DecodeOptions);
- shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0));
- if (decoder->audio_stream()) {
- _external_audio_stream = decoder->audio_stream ();
+ stringstream s;
+ s.width (8);
+ s << setfill('0') << f;
+
+ if (e == EYES_LEFT) {
+ s << ".L";
+ } else if (e == EYES_RIGHT) {
+ s << ".R";
}
+
+ s << ".md5";
- signal_changed (EXTERNAL_AUDIO);
+ p /= s.str();
+
+ /* info_dir() will already have added any initial bit of the path,
+ so don't call file() on this.
+ */
+ return p.string ();
}
- void
- Film::set_use_content_audio (bool e)
+ string
+ Film::j2c_path (int f, Eyes e, bool t) const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _use_content_audio = e;
- }
+ boost::filesystem::path p;
+ p /= "j2c";
+ p /= video_identifier ();
- signal_changed (USE_CONTENT_AUDIO);
- }
+ stringstream s;
+ s.width (8);
+ s << setfill('0') << f;
- void
- Film::set_audio_gain (float g)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _audio_gain = g;
+ if (e == EYES_LEFT) {
+ s << ".L";
+ } else if (e == EYES_RIGHT) {
+ s << ".R";
}
- signal_changed (AUDIO_GAIN);
- }
+
+ s << ".j2c";
- void
- Film::set_audio_delay (int d)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _audio_delay = d;
+ if (t) {
+ s << ".tmp";
}
- signal_changed (AUDIO_DELAY);
- }
- void
- Film::set_still_duration (int d)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _still_duration = d;
- }
- signal_changed (STILL_DURATION);
+ p /= s.str();
+ return file (p.string ());
}
- void
- Film::set_subtitle_stream (shared_ptr<SubtitleStream> s)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_stream = s;
- }
- signal_changed (SUBTITLE_STREAM);
- }
+ /** Make an educated guess as to whether we have a complete DCP
+ * or not.
+ * @return true if we do.
+ */
- void
- Film::set_with_subtitles (bool w)
+ bool
+ Film::have_dcp () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _with_subtitles = w;
+ try {
+ libdcp::DCP dcp (dir (dcp_name()));
+ dcp.read ();
+ } catch (...) {
+ return false;
}
- signal_changed (WITH_SUBTITLES);
- }
- void
- Film::set_subtitle_offset (int o)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_offset = o;
- }
- signal_changed (SUBTITLE_OFFSET);
+ return true;
}
- void
- Film::set_subtitle_scale (float s)
+ shared_ptr<Player>
+ Film::make_player () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_scale = s;
- }
- signal_changed (SUBTITLE_SCALE);
+ return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
}
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _encrypted = e;
- }
+void
+Film::set_encrypted (bool e)
+{
- void
- Film::set_colour_lut (int i)
++ _encrypted = e;
+ signal_changed (ENCRYPTED);
+}
+
+ shared_ptr<Playlist>
+ Film::playlist () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _colour_lut = i;
- }
- signal_changed (COLOUR_LUT);
+ return _playlist;
}
- void
- Film::set_j2k_bandwidth (int b)
+ ContentList
+ Film::content () const
{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _j2k_bandwidth = b;
- }
- signal_changed (J2K_BANDWIDTH);
+ return _playlist->content ();
}
void
}
void
- Film::set_dci_date_today ()
+ Film::set_sequence_video (bool s)
{
- _dci_date = boost::gregorian::day_clock::local_day ();
+ _sequence_video = s;
+ _playlist->set_sequence_video (s);
+ signal_changed (SEQUENCE_VIDEO);
}
- boost::shared_ptr<AudioStream>
- Film::audio_stream () const
+ libdcp::Size
+ Film::full_frame () const
{
- if (use_content_audio()) {
- return _content_audio_stream;
+ switch (_resolution) {
+ case RESOLUTION_2K:
+ return libdcp::Size (2048, 1080);
+ case RESOLUTION_4K:
+ return libdcp::Size (4096, 2160);
}
- return _external_audio_stream;
+ assert (false);
+ return libdcp::Size ();
}
- shared_ptr<xmlpp::Document> kdm = dcp.cpls().front()->make_kdm (chain, signer_key.string(), (*i)->certificate, from, until);
+
+void
+Film::make_kdms (
+ list<shared_ptr<Screen> > screens,
+ boost::posix_time::ptime from,
+ boost::posix_time::ptime until,
+ string directory
+ ) const
+{
+ string const cd = Config::instance()->crypt_chain_directory ();
+ if (boost::filesystem::is_empty (cd)) {
+ libdcp::make_crypt_chain (cd);
+ }
+
+ libdcp::CertificateChain chain;
+
+ {
+ boost::filesystem::path p (cd);
+ p /= "ca.self-signed.pem";
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p.string ())));
+ }
+
+ {
+ boost::filesystem::path p (cd);
+ p /= "intermediate.signed.pem";
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p.string ())));
+ }
+
+ {
+ boost::filesystem::path p (cd);
+ p /= "leaf.signed.pem";
+ chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p.string ())));
+ }
+
+ boost::filesystem::path signer_key (cd);
+ signer_key /= "leaf.key";
+
+ /* Find the DCP to make the KDM for */
+ string const dir = this->directory ();
+ list<string> dcps;
+ for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
+ if (boost::filesystem::is_directory (*i) && i->path().leaf() != "j2c" && i->path().leaf() != "wavs") {
+ dcps.push_back (i->path().string());
+ }
+ }
+
+ if (dcps.empty()) {
+ throw KDMError ("Could not find DCP to make KDM for");
+ } else if (dcps.size() > 1) {
+ throw KDMError ("More than one possible DCP to make KDM for");
+ }
+
+ for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
+
+ libdcp::DCP dcp (dcps.front ());
+ dcp.read ();
+
+ /* XXX: single CPL only */
++ shared_ptr<xmlpp::Document> kdm = dcp.cpls().front()->make_kdm (
++ chain, signer_key.string(), (*i)->certificate, from, until, _interop, libdcp::MXFMetadata (), Config::instance()->dcp_metadata ()
++ );
+
+ boost::filesystem::path out = directory;
+ out /= "kdm.xml";
+ kdm->write_to_file_formatted (out.string());
+ }
+}
+
#include <string>
#include <vector>
#include <inttypes.h>
- #include <boost/thread/mutex.hpp>
- #include <boost/thread.hpp>
#include <boost/signals2.hpp>
#include <boost/enable_shared_from_this.hpp>
- #include <boost/date_time/posix_time/posix_time.hpp>
- extern "C" {
- #include <libavcodec/avcodec.h>
- }
- #include "dcp_content_type.h"
+ #include <boost/filesystem.hpp>
#include "util.h"
- #include "stream.h"
+ #include "types.h"
+ #include "dci_metadata.h"
- class Format;
- class Job;
- class Filter;
+ class DCPContentType;
class Log;
- class ExamineContentJob;
- class ExternalAudioStream;
+ class Content;
+ class Player;
+ class Playlist;
+ class AudioContent;
+ class Scaler;
+class Screen;
/** @class Film
- * @brief A representation of a video, maybe with sound.
*
- * A representation of a piece of video (maybe with sound), including naming,
- * the source content file, and how it should be presented in a DCP.
+ * @brief A representation of some audio and video content, and details of
+ * how they should be presented in a DCP.
+ *
+ * The content of a Film is held in a Playlist (created and managed by the Film).
*/
- class Film : public boost::enable_shared_from_this<Film>
+ class Film : public boost::enable_shared_from_this<Film>, public boost::noncopyable
{
public:
- Film (std::string d, bool must_exist = true);
- Film (Film const &);
- ~Film ();
+ Film (boost::filesystem::path);
- std::string j2k_dir () const;
+ std::string info_dir () const;
+ std::string j2c_path (int, Eyes, bool) const;
+ std::string info_path (int, Eyes) const;
+ std::string internal_video_mxf_dir () const;
+ std::string internal_video_mxf_filename () const;
+ boost::filesystem::path audio_analysis_path (boost::shared_ptr<const AudioContent>) const;
- void examine_content ();
- void send_dcp_to_tms ();
+ std::string video_mxf_filename () const;
+ std::string audio_mxf_filename () const;
- void make_dcp (bool);
+ void send_dcp_to_tms ();
+ void make_dcp ();
/** @return Logger.
* It is safe to call this from any thread.
return _dirty;
}
- int audio_channels () const;
+ libdcp::Size full_frame () const;
- void set_dci_date_today ();
+ bool have_dcp () const;
+
+ boost::shared_ptr<Player> make_player () const;
+ boost::shared_ptr<Playlist> playlist () const;
+
+ OutputAudioFrame audio_frame_rate () const;
+
+ OutputAudioFrame time_to_audio_frames (Time) const;
+ OutputVideoFrame time_to_video_frames (Time) const;
+ Time video_frames_to_time (OutputVideoFrame) const;
+ Time audio_frames_to_time (OutputAudioFrame) const;
+
+ /* Proxies for some Playlist methods */
+
+ ContentList content () const;
+
+ Time length () const;
+ bool has_subtitles () const;
+ OutputVideoFrame best_video_frame_rate () const;
+ void make_kdms (
+ std::list<boost::shared_ptr<Screen> >,
+ boost::posix_time::ptime from,
+ boost::posix_time::ptime until,
+ std::string directory
+ ) const;
+
/** Identifiers for the parts of our state;
used for signalling changes.
*/
NONE,
NAME,
USE_DCI_NAME,
+ /** The playlist's content list has changed (i.e. content has been added, moved around or removed) */
CONTENT,
- TRUST_CONTENT_HEADER,
DCP_CONTENT_TYPE,
- FORMAT,
- CROP,
- FILTERS,
+ CONTAINER,
+ RESOLUTION,
SCALER,
- DCP_TRIM_START,
- DCP_TRIM_END,
- REEL_SIZE,
- DCP_AB,
- CONTENT_AUDIO_STREAM,
- EXTERNAL_AUDIO,
- USE_CONTENT_AUDIO,
- AUDIO_GAIN,
- AUDIO_DELAY,
- STILL_DURATION,
- SUBTITLE_STREAM,
WITH_SUBTITLES,
- SUBTITLE_OFFSET,
- SUBTITLE_SCALE,
+ ENCRYPTED,
- COLOUR_LUT,
J2K_BANDWIDTH,
DCI_METADATA,
- SIZE,
- LENGTH,
- CONTENT_AUDIO_STREAMS,
- SUBTITLE_STREAMS,
- FRAMES_PER_SECOND,
+ VIDEO_FRAME_RATE,
+ AUDIO_CHANNELS,
+ /** The setting of _three_d has been changed */
+ THREE_D,
+ SEQUENCE_VIDEO,
+ INTEROP,
};
return _with_subtitles;
}
- int subtitle_offset () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_offset;
- }
-
- float subtitle_scale () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_scale;
- }
-
+ bool encrypted () const {
- boost::mutex::scoped_lock lm (_state_mutex);
+ return _encrypted;
+ }
+
- int colour_lut () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _colour_lut;
- }
-
int j2k_bandwidth () const {
- boost::mutex::scoped_lock lm (_state_mutex);
return _j2k_bandwidth;
}
void set_directory (std::string);
void set_name (std::string);
void set_use_dci_name (bool);
- void set_content (std::string);
- void set_trust_content_header (bool);
+ void examine_and_add_content (boost::shared_ptr<Content>);
+ void add_content (boost::shared_ptr<Content>);
+ void remove_content (boost::shared_ptr<Content>);
void set_dcp_content_type (DCPContentType const *);
- void set_format (Format const *);
- void set_crop (Crop);
- void set_left_crop (int);
- void set_right_crop (int);
- void set_top_crop (int);
- void set_bottom_crop (int);
- void set_filters (std::vector<Filter const *>);
+ void set_container (Ratio const *);
+ void set_resolution (Resolution);
void set_scaler (Scaler const *);
- void set_dcp_trim_start (int);
- void set_dcp_trim_end (int);
- void set_reel_size (uint64_t);
- void unset_reel_size ();
- void set_dcp_ab (bool);
- void set_content_audio_stream (boost::shared_ptr<AudioStream>);
- void set_external_audio (std::vector<std::string>);
- void set_use_content_audio (bool);
- void set_audio_gain (float);
- void set_audio_delay (int);
- void set_still_duration (int);
- void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
void set_with_subtitles (bool);
- void set_subtitle_offset (int);
- void set_subtitle_scale (float);
+ void set_encrypted (bool);
- void set_colour_lut (int);
void set_j2k_bandwidth (int);
- void set_audio_language (std::string);
- void set_subtitle_language (std::string);
- void set_territory (std::string);
- void set_rating (std::string);
- void set_studio (std::string);
- void set_facility (std::string);
- void set_package_type (std::string);
- void set_size (Size);
- void set_length (SourceFrame);
- void unset_length ();
- void set_content_digest (std::string);
- void set_content_audio_streams (std::vector<boost::shared_ptr<AudioStream> >);
- void set_subtitle_streams (std::vector<boost::shared_ptr<SubtitleStream> >);
- void set_frames_per_second (float);
-
- /** Emitted when some property has changed */
+ void set_dci_metadata (DCIMetadata);
+ void set_video_frame_rate (int);
+ void set_audio_channels (int);
+ void set_three_d (bool);
+ void set_dci_date_today ();
+ void set_sequence_video (bool);
+ void set_interop (bool);
+
+ /** Emitted when some property has of the Film has changed */
mutable boost::signals2::signal<void (Property)> Changed;
+ /** Emitted when some property of our content has changed */
+ mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged;
+
/** Current version number of the state file */
static int const state_version;
std::string _name;
/** True if a auto-generated DCI-compliant name should be used for our DCP */
bool _use_dci_name;
- /** File or directory containing content; may be relative to our directory
- * or an absolute path.
- */
- std::string _content;
- /** If this is true, we will believe the length specified by the content
- * file's header; if false, we will run through the whole content file
- * the first time we see it in order to obtain the length.
- */
- bool _trust_content_header;
/** The type of content that this Film represents (feature, trailer etc.) */
DCPContentType const * _dcp_content_type;
- /** The format to present this Film in (flat, scope, etc.) */
- Format const * _format;
- /** The crop to apply to the source */
- Crop _crop;
- /** Video filters that should be used when generating DCPs */
- std::vector<Filter const *> _filters;
+ /** The container to put this Film in (flat, scope, etc.) */
+ Ratio const * _container;
+ /** DCP resolution (2K or 4K) */
+ Resolution _resolution;
/** Scaler algorithm to use */
Scaler const * _scaler;
- /** Frames to trim off the start of the DCP */
- int _dcp_trim_start;
- /** Frames to trim off the end of the DCP */
- int _dcp_trim_end;
- /** Approximate target reel size in bytes; if not set, use a single reel */
- boost::optional<uint64_t> _reel_size;
- /** true to create an A/B comparison DCP, where the left half of the image
- is the video without any filters or post-processing, and the right half
- has the specified filters and post-processing.
- */
- bool _dcp_ab;
- /** The audio stream to use from our content */
- boost::shared_ptr<AudioStream> _content_audio_stream;
- /** List of filenames of external audio files, in channel order
- (L, R, C, Lfe, Ls, Rs)
- */
- std::vector<std::string> _external_audio;
- /** true to use audio from our content file; false to use external audio */
- bool _use_content_audio;
- /** Gain to apply to audio in dB */
- float _audio_gain;
- /** Delay to apply to audio (positive moves audio later) in milliseconds */
- int _audio_delay;
- /** Duration to make still-sourced films (in seconds) */
- int _still_duration;
- boost::shared_ptr<SubtitleStream> _subtitle_stream;
/** True if subtitles should be shown for this film */
bool _with_subtitles;
- /** y offset for placing subtitles, in source pixels; +ve is further down
- the frame, -ve is further up.
- */
- int _subtitle_offset;
- /** scale factor to apply to subtitles */
- float _subtitle_scale;
+ bool _encrypted;
-
- /** index of colour LUT to use when converting RGB to XYZ.
- * 0: sRGB
- * 1: Rec 709
- */
- int _colour_lut;
/** bandwidth for J2K files in bits per second */
int _j2k_bandwidth;
-
- /* DCI naming stuff */
- std::string _audio_language;
- std::string _subtitle_language;
- std::string _territory;
- std::string _rating;
- std::string _studio;
- std::string _facility;
- std::string _package_type;
-
- /* Data which are cached to speed things up */
-
- /** Size, in pixels, of the source (ignoring cropping) */
- Size _size;
- /** The length of the source, in video frames (as far as we know) */
- boost::optional<SourceFrame> _length;
- /** MD5 digest of our content file */
- std::string _content_digest;
- /** The audio streams in our content */
- std::vector<boost::shared_ptr<AudioStream> > _content_audio_streams;
- /** A stream to represent possible external audio (will always exist) */
- boost::shared_ptr<AudioStream> _external_audio_stream;
- /** the subtitle streams that we can use */
- std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams;
- /** Frames per second of the source */
- float _frames_per_second;
+ /** DCI naming stuff */
+ DCIMetadata _dci_metadata;
+ /** Frames per second to run our DCP at */
+ int _video_frame_rate;
+ /** The date that we should use in a DCI name */
+ boost::gregorian::date _dci_date;
+ /** Number of audio channels to put in the DCP */
+ int _audio_channels;
+ /** If true, the DCP will be written in 3D mode; otherwise in 2D.
+ This will be regardless of what content is on the playlist.
+ */
+ bool _three_d;
+ bool _sequence_video;
+ bool _interop;
/** true if our state has changed since we last saved it */
mutable bool _dirty;
--- /dev/null
- bool const have_dcp = film && film->have_dcp();
+ /*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program 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.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+ #include <iostream>
+ #include <fstream>
+ #include <boost/filesystem.hpp>
+ #ifdef __WXMSW__
+ #include <shellapi.h>
+ #endif
+ #ifdef __WXOSX__
+ #include <ApplicationServices/ApplicationServices.h>
+ #endif
+ #include <wx/generic/aboutdlgg.h>
+ #include <wx/stdpaths.h>
+ #include <wx/cmdline.h>
+ #include "wx/film_viewer.h"
+ #include "wx/film_editor.h"
+ #include "wx/job_manager_view.h"
+ #include "wx/config_dialog.h"
+ #include "wx/job_wrapper.h"
+ #include "wx/wx_util.h"
+ #include "wx/new_film_dialog.h"
+ #include "wx/properties_dialog.h"
+ #include "wx/wx_ui_signaller.h"
+ #include "wx/about_dialog.h"
++#include "wx/kdm_dialog.h"
+ #include "lib/film.h"
+ #include "lib/config.h"
+ #include "lib/util.h"
+ #include "lib/version.h"
+ #include "lib/ui_signaller.h"
+ #include "lib/log.h"
+ #include "lib/job_manager.h"
+ #include "lib/transcode_job.h"
++#include "lib/exceptions.h"
+
+ using std::cout;
+ using std::string;
+ using std::wstring;
+ using std::stringstream;
+ using std::map;
+ using std::make_pair;
+ using std::list;
+ using std::exception;
+ using std::ofstream;
+ using boost::shared_ptr;
+ using boost::dynamic_pointer_cast;
+
+ static FilmEditor* film_editor = 0;
+ static FilmViewer* film_viewer = 0;
+ static shared_ptr<Film> film;
+ static std::string log_level;
+ static std::string film_to_load;
+ static std::string film_to_create;
+ static wxMenu* jobs_menu = 0;
+
+ static void set_menu_sensitivity ();
+
+ class FilmChangedDialog
+ {
+ public:
+ FilmChangedDialog ()
+ {
+ _dialog = new wxMessageDialog (
+ 0,
+ wxString::Format (_("Save changes to film \"%s\" before closing?"), std_to_wx (film->name ()).data()),
+ _("Film changed"),
+ wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION
+ );
+ }
+
+ ~FilmChangedDialog ()
+ {
+ _dialog->Destroy ();
+ }
+
+ int run ()
+ {
+ return _dialog->ShowModal ();
+ }
+
+ private:
+ /* Not defined */
+ FilmChangedDialog (FilmChangedDialog const &);
+
+ wxMessageDialog* _dialog;
+ };
+
+
+ void
+ maybe_save_then_delete_film ()
+ {
+ if (!film) {
+ return;
+ }
+
+ if (film->dirty ()) {
+ FilmChangedDialog d;
+ switch (d.run ()) {
+ case wxID_NO:
+ break;
+ case wxID_YES:
+ film->write_metadata ();
+ break;
+ }
+ }
+
+ film.reset ();
+ }
+
+ #define ALWAYS 0x0
+ #define NEEDS_FILM 0x1
+ #define NOT_DURING_DCP_CREATION 0x2
+
+ map<wxMenuItem*, int> menu_items;
+
+ void
+ add_item (wxMenu* menu, wxString text, int id, int sens)
+ {
+ wxMenuItem* item = menu->Append (id, text);
+ menu_items.insert (make_pair (item, sens));
+ }
+
+ void
+ set_menu_sensitivity ()
+ {
+ list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
+ list<shared_ptr<Job> >::iterator i = jobs.begin();
+ while (i != jobs.end() && dynamic_pointer_cast<TranscodeJob> (*i) == 0) {
+ ++i;
+ }
+ bool const dcp_creation = (i != jobs.end ());
+
+ for (map<wxMenuItem*, int>::iterator j = menu_items.begin(); j != menu_items.end(); ++j) {
+
+ bool enabled = true;
+
+ if ((j->second & NEEDS_FILM) && film == 0) {
+ enabled = false;
+ }
+
+ if ((j->second & NOT_DURING_DCP_CREATION) && dcp_creation) {
+ enabled = false;
+ }
+
+ j->first->Enable (enabled);
+ }
+ }
+
+ enum {
+ ID_file_new = 1,
+ ID_file_open,
+ ID_file_save,
+ ID_file_properties,
+ ID_jobs_make_dcp,
++ ID_jobs_make_kdms,
+ ID_jobs_send_dcp_to_tms,
+ ID_jobs_show_dcp,
+ };
+
+ void
+ setup_menu (wxMenuBar* m)
+ {
+ wxMenu* file = new wxMenu;
+ add_item (file, _("New..."), ID_file_new, ALWAYS);
+ add_item (file, _("&Open..."), ID_file_open, ALWAYS);
+ file->AppendSeparator ();
+ add_item (file, _("&Save"), ID_file_save, NEEDS_FILM);
+ file->AppendSeparator ();
+ add_item (file, _("&Properties..."), ID_file_properties, NEEDS_FILM);
+ #ifndef __WXOSX__
+ file->AppendSeparator ();
+ #endif
+
+ #ifdef __WXOSX__
+ add_item (file, _("&Exit"), wxID_EXIT, ALWAYS);
+ #else
+ add_item (file, _("&Quit"), wxID_EXIT, ALWAYS);
+ #endif
+
+
+ #ifdef __WXOSX__
+ add_item (file, _("&Preferences..."), wxID_PREFERENCES, ALWAYS);
+ #else
+ wxMenu* edit = new wxMenu;
+ add_item (edit, _("&Preferences..."), wxID_PREFERENCES, ALWAYS);
+ #endif
+
+ jobs_menu = new wxMenu;
+ add_item (jobs_menu, _("&Make DCP"), ID_jobs_make_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
++ add_item (jobs_menu, _("Make &KDMs..."), ID_jobs_make_kdms, NEEDS_FILM);
+ add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM | NOT_DURING_DCP_CREATION);
+ add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
+
+ wxMenu* help = new wxMenu;
+ #ifdef __WXOSX__
+ add_item (help, _("About DCP-o-matic"), wxID_ABOUT, ALWAYS);
+ #else
+ add_item (help, _("About"), wxID_ABOUT, ALWAYS);
+ #endif
+
+ m->Append (file, _("&File"));
+ #ifndef __WXOSX__
+ m->Append (edit, _("&Edit"));
+ #endif
+ m->Append (jobs_menu, _("&Jobs"));
+ m->Append (help, _("&Help"));
+ }
+
+ class Frame : public wxFrame
+ {
+ public:
+ Frame (wxString const & title)
+ : wxFrame (NULL, -1, title)
+ {
+ wxMenuBar* bar = new wxMenuBar;
+ setup_menu (bar);
+ SetMenuBar (bar);
+
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_new, this), ID_file_new);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_open, this), ID_file_open);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_save, this), ID_file_save);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_properties, this), ID_file_properties);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_exit, this), wxID_EXIT);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::edit_preferences, this), wxID_PREFERENCES);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_make_dcp, this), ID_jobs_make_dcp);
++ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_make_kdms, this), ID_jobs_make_kdms);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_send_dcp_to_tms, this), ID_jobs_send_dcp_to_tms);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_show_dcp, this), ID_jobs_show_dcp);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::help_about, this), wxID_ABOUT);
+
+ Bind (wxEVT_MENU_OPEN, boost::bind (&Frame::menu_opened, this, _1));
+ Bind (wxEVT_CLOSE_WINDOW, boost::bind (&Frame::close, this, _1));
+
+ /* Use a panel as the only child of the Frame so that we avoid
+ the dark-grey background on Windows.
+ */
+ wxPanel* overall_panel = new wxPanel (this, wxID_ANY);
+
+ film_editor = new FilmEditor (film, overall_panel);
+ film_viewer = new FilmViewer (film, overall_panel);
+ JobManagerView* job_manager_view = new JobManagerView (overall_panel, static_cast<JobManagerView::Buttons> (0));
+
+ wxBoxSizer* right_sizer = new wxBoxSizer (wxVERTICAL);
+ right_sizer->Add (film_viewer, 2, wxEXPAND | wxALL, 6);
+ right_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6);
+
+ wxBoxSizer* main_sizer = new wxBoxSizer (wxHORIZONTAL);
+ main_sizer->Add (film_editor, 1, wxEXPAND | wxALL, 6);
+ main_sizer->Add (right_sizer, 2, wxEXPAND | wxALL, 6);
+
+ set_menu_sensitivity ();
+
+ film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1));
+ if (film) {
+ file_changed (film->directory ());
+ } else {
+ file_changed ("");
+ }
+
+ JobManager::instance()->ActiveJobsChanged.connect (boost::bind (set_menu_sensitivity));
+
+ set_film ();
+ overall_panel->SetSizer (main_sizer);
+ }
+
+ private:
+
+ void menu_opened (wxMenuEvent& ev)
+ {
+ if (ev.GetMenu() != jobs_menu) {
+ return;
+ }
+
++ bool const have_dcp = false;//film && film->have_dcp();
+ jobs_menu->Enable (ID_jobs_send_dcp_to_tms, have_dcp);
+ jobs_menu->Enable (ID_jobs_show_dcp, have_dcp);
+ }
+
+ void set_film ()
+ {
+ film_viewer->set_film (film);
+ film_editor->set_film (film);
+ set_menu_sensitivity ();
+ }
+
+ void file_changed (string f)
+ {
+ stringstream s;
+ s << wx_to_std (_("DCP-o-matic"));
+ if (!f.empty ()) {
+ s << " - " << f;
+ }
+
+ SetTitle (std_to_wx (s.str()));
+ }
+
+ void file_new ()
+ {
+ NewFilmDialog* d = new NewFilmDialog (this);
+ int const r = d->ShowModal ();
+
+ if (r == wxID_OK) {
+
+ if (boost::filesystem::is_directory (d->get_path()) && !boost::filesystem::is_empty(d->get_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().c_str())
+ )
+ )) {
+ return;
+ }
+ } else if (boost::filesystem::is_regular_file (d->get_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())
+ );
+ return;
+ }
+
+ maybe_save_then_delete_film ();
+ film.reset (new Film (d->get_path ()));
+ film->write_metadata ();
+ film->log()->set_level (log_level);
+ film->set_name (boost::filesystem::path (d->get_path()).filename().generic_string());
+ set_film ();
+ }
+
+ d->Destroy ();
+ }
+
+ void file_open ()
+ {
+ wxDirDialog* c = new wxDirDialog (this, _("Select film to open"), wxStandardPaths::Get().GetDocumentsDir(), wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST);
+ int r;
+ while (1) {
+ r = c->ShowModal ();
+ if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) {
+ error_dialog (this, _("You did not select a folder. Make sure that you select a folder before clicking Open."));
+ } else {
+ break;
+ }
+ }
+
+ if (r == wxID_OK) {
+ maybe_save_then_delete_film ();
+ try {
+ film.reset (new Film (wx_to_std (c->GetPath ())));
+ film->read_metadata ();
+ film->log()->set_level (log_level);
+ set_film ();
+ } catch (std::exception& e) {
+ wxString p = c->GetPath ();
+ wxCharBuffer b = p.ToUTF8 ();
+ error_dialog (this, wxString::Format (_("Could not open film at %s (%s)"), p.data(), std_to_wx (e.what()).data()));
+ }
+ }
+
+ c->Destroy ();
+ }
+
+ void file_save ()
+ {
+ film->write_metadata ();
+ }
+
+ void file_properties ()
+ {
+ PropertiesDialog* d = new PropertiesDialog (this, film);
+ d->ShowModal ();
+ d->Destroy ();
+ }
+
+ void file_exit ()
+ {
+ if (!should_close ()) {
+ return;
+ }
+
+ maybe_save_then_delete_film ();
+ Close (true);
+ }
+
+ void edit_preferences ()
+ {
+ ConfigDialog* d = new ConfigDialog (this);
+ d->ShowModal ();
+ d->Destroy ();
+ Config::instance()->write ();
+ }
+
+ void jobs_make_dcp ()
+ {
+ JobWrapper::make_dcp (this, film);
+ }
++
++ void jobs_make_kdms ()
++ {
++ if (!film) {
++ return;
++ }
++
++ KDMDialog* d = new KDMDialog (this);
++ if (d->ShowModal () == wxID_OK) {
++ try {
++ film->make_kdms (
++ d->screens (),
++ d->from (),
++ d->until (),
++ d->directory ()
++ );
++ } catch (KDMError& e) {
++ error_dialog (this, e.what ());
++ }
++ }
++
++ d->Destroy ();
++ }
+
+ void jobs_send_dcp_to_tms ()
+ {
+ film->send_dcp_to_tms ();
+ }
+
+ void jobs_show_dcp ()
+ {
+ #ifdef __WXMSW__
+ string d = film->directory();
+ wstring w;
+ w.assign (d.begin(), d.end());
+ ShellExecute (0, L"open", w.c_str(), 0, 0, SW_SHOWDEFAULT);
+ #else
+ int r = system ("which nautilus");
+ if (WEXITSTATUS (r) == 0) {
+ r = system (string ("nautilus " + film->directory()).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()).c_str ());
+ if (WEXITSTATUS (r)) {
+ error_dialog (this, _("Could not show DCP (could not run konqueror)"));
+ }
+ }
+ }
+ #endif
+ }
+
+ void help_about ()
+ {
+ AboutDialog* d = new AboutDialog (this);
+ d->ShowModal ();
+ d->Destroy ();
+ }
+
+ bool should_close ()
+ {
+ if (!JobManager::instance()->work_to_do ()) {
+ return true;
+ }
+
+ wxMessageDialog* d = new wxMessageDialog (
+ 0,
+ _("There are unfinished jobs; are you sure you want to quit?"),
+ _("Unfinished jobs"),
+ wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION
+ );
+
+ bool const r = d->ShowModal() == wxID_YES;
+ d->Destroy ();
+ return r;
+ }
+
+ void close (wxCloseEvent& ev)
+ {
+ if (!should_close ()) {
+ ev.Veto ();
+ return;
+ }
+
+ ev.Skip ();
+ }
+ };
+
+ #if wxMINOR_VERSION == 9
+ static const wxCmdLineEntryDesc command_line_description[] = {
+ { wxCMD_LINE_OPTION, "l", "log", "set log level (silent, verbose or timing)", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_SWITCH, "n", "new", "create new film", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_PARAM, 0, 0, "film to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
+ };
+ #else
+ static const wxCmdLineEntryDesc command_line_description[] = {
+ { wxCMD_LINE_OPTION, wxT("l"), wxT("log"), wxT("set log level (silent, verbose or timing)"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_SWITCH, wxT("n"), wxT("new"), wxT("create new film"), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_PARAM, 0, 0, wxT("film to load or create"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
+ { wxCMD_LINE_NONE, wxT(""), wxT(""), wxT(""), wxCmdLineParamType (0), 0 }
+ };
+ #endif
+
+ class App : public wxApp
+ {
+ bool OnInit ()
+ try
+ {
+ if (!wxApp::OnInit()) {
+ return false;
+ }
+
+ #ifdef DCPOMATIC_LINUX
+ unsetenv ("UBUNTU_MENUPROXY");
+ #endif
+
+ #ifdef __WXOSX__
+ ProcessSerialNumber serial;
+ GetCurrentProcess (&serial);
+ TransformProcessType (&serial, kProcessTransformToForegroundApplication);
+ #endif
+
+ wxInitAllImageHandlers ();
+
+ /* Enable i18n; this will create a Config object
+ to look for a force-configured language. This Config
+ object will be wrong, however, because dcpomatic_setup
+ hasn't yet been called and there aren't any scalers, filters etc.
+ set up yet.
+ */
+ dcpomatic_setup_i18n ();
+
+ /* Set things up, including scalers / filters etc.
+ which will now be internationalised correctly.
+ */
+ dcpomatic_setup ();
+
+ /* Force the configuration to be re-loaded correctly next
+ time it is needed.
+ */
+ Config::drop ();
+
+ if (!film_to_load.empty() && boost::filesystem::is_directory (film_to_load)) {
+ try {
+ film.reset (new Film (film_to_load));
+ film->read_metadata ();
+ film->log()->set_level (log_level);
+ } 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())));
+ }
+ }
+
+ if (!film_to_create.empty ()) {
+ film.reset (new Film (film_to_create));
+ film->write_metadata ();
+ film->log()->set_level (log_level);
+ film->set_name (boost::filesystem::path (film_to_create).filename().generic_string ());
+ }
+
+ Frame* f = new Frame (_("DCP-o-matic"));
+ SetTopWindow (f);
+ f->Maximize ();
+ f->Show ();
+
+ ui_signaller = new wxUISignaller (this);
+ this->Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
+
+ return true;
+ }
+ catch (exception& e)
+ {
+ error_dialog (0, wxString::Format ("DCP-o-matic could not start: %s", e.what ()));
+ return true;
+ }
+
+ void OnInitCmdLine (wxCmdLineParser& parser)
+ {
+ parser.SetDesc (command_line_description);
+ parser.SetSwitchChars (wxT ("-"));
+ }
+
+ bool OnCmdLineParsed (wxCmdLineParser& parser)
+ {
+ if (parser.GetParamCount() > 0) {
+ if (parser.Found (wxT ("new"))) {
+ film_to_create = wx_to_std (parser.GetParam (0));
+ } else {
+ film_to_load = wx_to_std (parser.GetParam(0));
+ }
+ }
+
+ wxString log;
+ if (parser.Found (wxT ("log"), &log)) {
+ log_level = wx_to_std (log);
+ }
+
+ return true;
+ }
+
+ void idle ()
+ {
+ ui_signaller->ui_idle ();
+ }
+ };
+
+ IMPLEMENT_APP (App)
--- /dev/null
- add_label_to_sizer (table, this, "Name");
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program 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.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "cinema_dialog.h"
+#include "wx_util.h"
+
+using std::string;
+
+CinemaDialog::CinemaDialog (wxWindow* parent, string title, string name, string email)
+ : wxDialog (parent, wxID_ANY, std_to_wx (title))
+{
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
+ table->AddGrowableCol (1, 1);
+
- add_label_to_sizer (table, this, "Email address for KDM delivery");
++ add_label_to_sizer (table, this, "Name", true);
+ _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (256, -1));
+ table->Add (_name, 1, wxEXPAND);
+
++ add_label_to_sizer (table, this, "Email address for KDM delivery", true);
+ _email = new wxTextCtrl (this, wxID_ANY, std_to_wx (email), wxDefaultPosition, wxSize (256, -1));
+ table->Add (_email, 1, wxEXPAND);
+
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ SetSizer (overall_sizer);
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
+}
+
+string
+CinemaDialog::name () const
+{
+ return wx_to_std (_name->GetValue());
+}
+
+string
+CinemaDialog::email () const
+{
+ return wx_to_std (_email->GetValue());
+}
}
void
- FilmEditor::make_film_panel ()
+ FilmEditor::make_dcp_panel ()
{
- _film_panel = new wxPanel (_notebook);
- _film_sizer = new wxFlexGridSizer (2, 4, 4);
- wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
- pad->Add (_film_sizer, 0, wxALL, 8);
- _film_panel->SetSizer (pad);
-
- add_label_to_sizer (_film_sizer, _film_panel, "Name");
- _name = new wxTextCtrl (_film_panel, wxID_ANY);
- _film_sizer->Add (_name, 1, wxEXPAND);
-
- add_label_to_sizer (_film_sizer, _film_panel, "DCP Name");
- _dcp_name = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK);
-
- _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Use DCI name"));
- _film_sizer->Add (_use_dci_name, 1, wxEXPAND);
- _edit_dci_button = new wxButton (_film_panel, wxID_ANY, wxT ("Details..."));
- _film_sizer->Add (_edit_dci_button, 0);
-
- add_label_to_sizer (_film_sizer, _film_panel, "Content");
- _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), wxT ("Select Content File"), wxT("*.*"));
- _film_sizer->Add (_content, 1, wxEXPAND);
-
- _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Trust content's header"));
- video_control (_trust_content_header);
- _film_sizer->Add (_trust_content_header, 1);
- _film_sizer->AddSpacer (0);
-
- add_label_to_sizer (_film_sizer, _film_panel, "Content Type");
- _dcp_content_type = new wxComboBox (_film_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _film_sizer->Add (_dcp_content_type);
-
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Frames Per Second"));
- _frames_per_second = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (video_control (_frames_per_second), 1, wxALIGN_CENTER_VERTICAL);
+ _dcp_panel = new wxPanel (_main_notebook);
+ _dcp_sizer = new wxBoxSizer (wxVERTICAL);
+ _dcp_panel->SetSizer (_dcp_sizer);
+
+ wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ _dcp_sizer->Add (grid, 0, wxEXPAND | wxALL, 8);
+
+ int r = 0;
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Original Size"));
- _original_size = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (video_control (_original_size), 1, wxALIGN_CENTER_VERTICAL);
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Name"), true, wxGBPosition (r, 0));
+ _name = new wxTextCtrl (_dcp_panel, wxID_ANY);
+ grid->Add (_name, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND | wxLEFT | wxRIGHT);
+ ++r;
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Length"));
- _length = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- _film_sizer->Add (video_control (_length), 1, wxALIGN_CENTER_VERTICAL);
-
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("DCP Name"), true, wxGBPosition (r, 0));
+ _dcp_name = new wxStaticText (_dcp_panel, wxID_ANY, wxT (""));
+ grid->Add (_dcp_name, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ ++r;
+
+ int flags = wxALIGN_CENTER_VERTICAL;
+ #ifdef __WXOSX__
+ flags |= wxALIGN_RIGHT;
+ #endif
+
+ _use_dci_name = new wxCheckBox (_dcp_panel, wxID_ANY, _("Use DCI name"));
+ grid->Add (_use_dci_name, wxGBPosition (r, 0), wxDefaultSpan, flags);
+ _edit_dci_button = new wxButton (_dcp_panel, wxID_ANY, _("Details..."));
+ grid->Add (_edit_dci_button, wxGBPosition (r, 1), wxDefaultSpan);
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Container"), true, wxGBPosition (r, 0));
+ _container = new wxChoice (_dcp_panel, wxID_ANY);
+ grid->Add (_container, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND);
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Content Type"), true, wxGBPosition (r, 0));
+ _dcp_content_type = new wxChoice (_dcp_panel, wxID_ANY);
+ grid->Add (_dcp_content_type, wxGBPosition (r, 1));
+ ++r;
{
- video_control (add_label_to_sizer (_film_sizer, _film_panel, "Trim frames"));
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Frame Rate"), true, wxGBPosition (r, 0));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- video_control (add_label_to_sizer (s, _film_panel, "Start"));
- _dcp_trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (video_control (_dcp_trim_start));
- video_control (add_label_to_sizer (s, _film_panel, "End"));
- _dcp_trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (video_control (_dcp_trim_end));
-
- _film_sizer->Add (s);
+ _frame_rate = new wxChoice (_dcp_panel, wxID_ANY);
+ s->Add (_frame_rate, 1, wxALIGN_CENTER_VERTICAL);
+ _best_frame_rate = new wxButton (_dcp_panel, wxID_ANY, _("Use best"));
+ s->Add (_best_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
+ grid->Add (s, wxGBPosition (r, 1));
}
+ ++r;
- _encrypted = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Encrypted"));
- _film_sizer->Add (_encrypted, 1);
- _film_sizer->AddSpacer (0);
++ _encrypted = new wxCheckBox (_dcp_panel, wxID_ANY, wxT ("Encrypted"));
++ grid->Add (_encrypted, wxGBPosition (r, 0), wxGBSpan (1, 2));
++ ++r;
+
- _multiple_reels = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Make multiple reels"));
- _film_sizer->Add (_multiple_reels);
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Audio channels"), true, wxGBPosition (r, 0));
+ _audio_channels = new wxSpinCtrl (_dcp_panel, wxID_ANY);
+ grid->Add (_audio_channels, wxGBPosition (r, 1));
+ ++r;
- {
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _reel_size = new wxSpinCtrl (_film_panel, wxID_ANY);
- s->Add (_reel_size);
- add_label_to_sizer (s, _film_panel, "Gb each");
- _film_sizer->Add (s);
- }
+ _three_d = new wxCheckBox (_dcp_panel, wxID_ANY, _("3D"));
+ grid->Add (_three_d, wxGBPosition (r, 0), wxGBSpan (1, 2));
+ ++r;
- _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, wxT ("A/B"));
- video_control (_dcp_ab);
- _film_sizer->Add (_dcp_ab, 1);
- _film_sizer->AddSpacer (0);
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Resolution"), true, wxGBPosition (r, 0));
+ _resolution = new wxChoice (_dcp_panel, wxID_ANY);
+ grid->Add (_resolution, wxGBPosition (r, 1));
+ ++r;
- /* STILL-only stuff */
{
- still_control (add_label_to_sizer (_film_sizer, _film_panel, "Duration"));
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("JPEG2000 bandwidth"), true, wxGBPosition (r, 0));
wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _still_duration = new wxSpinCtrl (_film_panel);
- still_control (_still_duration);
- s->Add (_still_duration, 1, wxEXPAND);
- still_control (add_label_to_sizer (s, _film_panel, "s"));
- _film_sizer->Add (s);
- }
-
- vector<DCPContentType const *> const ct = DCPContentType::all ();
- for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
- _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
- }
-
- _reel_size->SetRange(1, 1000);
- }
-
- void
- FilmEditor::connect_to_widgets ()
- {
- _name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this);
- _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this);
- _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this);
- _format->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
- _content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this);
- _trust_content_header->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_header_changed), 0, this);
- _left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this);
- _right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this);
- _top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this);
- _bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this);
- _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this);
- _scaler->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
- _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
- _dcp_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this);
- _encrypted->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::encrypted_toggled), 0, this);
- _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this);
- _dcp_trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_start_changed), 0, this);
- _dcp_trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_end_changed), 0, this);
- _multiple_reels->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::multiple_reels_toggled), 0, this);
- _reel_size->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::reel_size_changed), 0, this);
- _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this);
- _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this);
- _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this);
- _colour_lut->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this);
- _j2k_bandwidth->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this);
- _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this);
- _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this);
- _audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this);
- _audio_gain_calculate_button->Connect (
- wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::audio_gain_calculate_button_clicked), 0, this
- );
- _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this);
- _use_content_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this);
- _use_external_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this);
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- _external_audio[i]->Connect (
- wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::external_audio_changed), 0, this
- );
- }
- }
-
- void
- FilmEditor::make_video_panel ()
- {
- _video_panel = new wxPanel (_notebook);
- _video_sizer = new wxFlexGridSizer (2, 4, 4);
- wxBoxSizer* pad = new wxBoxSizer (wxVERTICAL);
- pad->Add (_video_sizer, 0, wxALL, 8);
- _video_panel->SetSizer (pad);
-
- add_label_to_sizer (_video_sizer, _video_panel, "Format");
- _format = new wxComboBox (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _video_sizer->Add (_format);
-
- {
- add_label_to_sizer (_video_sizer, _video_panel, "Crop");
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-
- add_label_to_sizer (s, _video_panel, "L");
- _left_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_left_crop, 0);
- add_label_to_sizer (s, _video_panel, "R");
- _right_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_right_crop, 0);
- add_label_to_sizer (s, _video_panel, "T");
- _top_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_top_crop, 0);
- add_label_to_sizer (s, _video_panel, "B");
- _bottom_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (_bottom_crop, 0);
-
- _video_sizer->Add (s);
+ _j2k_bandwidth = new wxSpinCtrl (_dcp_panel, wxID_ANY);
+ s->Add (_j2k_bandwidth, 1);
+ add_label_to_sizer (s, _dcp_panel, _("MBps"), false);
+ grid->Add (s, wxGBPosition (r, 1));
}
+ ++r;
- /* VIDEO-only stuff */
- {
- video_control (add_label_to_sizer (_video_sizer, _video_panel, "Filters"));
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _filters = new wxStaticText (_video_panel, wxID_ANY, wxT ("None"));
- video_control (_filters);
- s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
- _filters_button = new wxButton (_video_panel, wxID_ANY, wxT ("Edit..."));
- video_control (_filters_button);
- s->Add (_filters_button, 0);
- _video_sizer->Add (s, 1);
- }
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Standard"), true, wxGBPosition (r, 0));
+ _standard = new wxChoice (_dcp_panel, wxID_ANY);
+ grid->Add (_standard, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ ++r;
- video_control (add_label_to_sizer (_video_sizer, _video_panel, "Scaler"));
- _scaler = new wxComboBox (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _video_sizer->Add (video_control (_scaler), 1);
+ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Scaler"), true, wxGBPosition (r, 0));
+ _scaler = new wxChoice (_dcp_panel, wxID_ANY);
+ grid->Add (_scaler, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ ++r;
vector<Scaler const *> const sc = Scaler::all ();
for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
return;
}
- _film->set_dcp_ab (_dcp_ab->GetValue ());
+ _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1e6);
}
- FilmEditor::encrypted_toggled (wxCommandEvent &)
+void
++FilmEditor::encrypted_toggled ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_encrypted (_encrypted->GetValue ());
+}
+
+/** Called when the name widget has been changed */
void
- FilmEditor::name_changed (wxCommandEvent &)
+ FilmEditor::frame_rate_changed ()
{
if (!_film) {
return;
case Film::SCALER:
checked_set (_scaler, Scaler::as_index (_film->scaler ()));
break;
- case Film::DCP_TRIM_START:
- checked_set (_dcp_trim_start, _film->dcp_trim_start());
- break;
- case Film::DCP_TRIM_END:
- checked_set (_dcp_trim_end, _film->dcp_trim_end());
- break;
- case Film::REEL_SIZE:
- if (_film->reel_size()) {
- checked_set (_multiple_reels, true);
- checked_set (_reel_size, _film->reel_size().get() / 1e9);
- } else {
- checked_set (_multiple_reels, false);
- }
- setup_reel_control_sensitivity ();
- break;
- case Film::AUDIO_GAIN:
- checked_set (_audio_gain, _film->audio_gain ());
- break;
- case Film::AUDIO_DELAY:
- checked_set (_audio_delay, _film->audio_delay ());
- break;
- case Film::STILL_DURATION:
- checked_set (_still_duration, _film->still_duration ());
- break;
- case Film::WITH_SUBTITLES:
- checked_set (_with_subtitles, _film->with_subtitles ());
- setup_subtitle_control_sensitivity ();
- _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
- break;
- case Film::SUBTITLE_OFFSET:
- checked_set (_subtitle_offset, _film->subtitle_offset ());
- break;
- case Film::SUBTITLE_SCALE:
- checked_set (_subtitle_scale, _film->subtitle_scale() * 100);
- break;
+ case Film::ENCRYPTED:
+ checked_set (_encrypted, _film->encrypted ());
+ break;
- case Film::COLOUR_LUT:
- checked_set (_colour_lut, _film->colour_lut ());
+ case Film::RESOLUTION:
+ checked_set (_resolution, _film->resolution() == RESOLUTION_2K ? 0 : 1);
+ setup_dcp_name ();
break;
case Film::J2K_BANDWIDTH:
checked_set (_j2k_bandwidth, double (_film->j2k_bandwidth()) / 1e6);
film_changed (Film::NAME);
film_changed (Film::USE_DCI_NAME);
film_changed (Film::CONTENT);
- film_changed (Film::TRUST_CONTENT_HEADER);
film_changed (Film::DCP_CONTENT_TYPE);
- film_changed (Film::FORMAT);
- film_changed (Film::CROP);
- film_changed (Film::FILTERS);
+ film_changed (Film::CONTAINER);
+ film_changed (Film::RESOLUTION);
film_changed (Film::SCALER);
- film_changed (Film::DCP_TRIM_START);
- film_changed (Film::DCP_TRIM_END);
- film_changed (Film::REEL_SIZE);
- film_changed (Film::DCP_AB);
- film_changed (Film::CONTENT_AUDIO_STREAM);
- film_changed (Film::EXTERNAL_AUDIO);
- film_changed (Film::USE_CONTENT_AUDIO);
- film_changed (Film::AUDIO_GAIN);
- film_changed (Film::AUDIO_DELAY);
- film_changed (Film::STILL_DURATION);
film_changed (Film::WITH_SUBTITLES);
- film_changed (Film::SUBTITLE_OFFSET);
- film_changed (Film::SUBTITLE_SCALE);
+ film_changed (Film::ENCRYPTED);
- film_changed (Film::COLOUR_LUT);
film_changed (Film::J2K_BANDWIDTH);
film_changed (Film::DCI_METADATA);
- film_changed (Film::SIZE);
- film_changed (Film::LENGTH);
- film_changed (Film::CONTENT_AUDIO_STREAMS);
- film_changed (Film::SUBTITLE_STREAMS);
- film_changed (Film::FRAMES_PER_SECOND);
+ film_changed (Film::VIDEO_FRAME_RATE);
+ film_changed (Film::AUDIO_CHANNELS);
+ film_changed (Film::SEQUENCE_VIDEO);
+ film_changed (Film::THREE_D);
+ film_changed (Film::INTEROP);
+
+ if (!_film->content().empty ()) {
+ set_selection (_film->content().front ());
+ }
+
+ content_selection_changed ();
}
- /** Updates the sensitivity of lots of widgets to a given value.
- * @param s true to make sensitive, false to make insensitive.
- */
void
- FilmEditor::set_things_sensitive (bool s)
+ FilmEditor::set_general_sensitivity (bool s)
{
_generally_sensitive = s;
-
+
+ /* Stuff in the Content / DCP tabs */
_name->Enable (s);
_use_dci_name->Enable (s);
_edit_dci_button->Enable (s);
- _format->Enable (s);
_content->Enable (s);
- _trust_content_header->Enable (s);
- _left_crop->Enable (s);
- _right_crop->Enable (s);
- _top_crop->Enable (s);
- _bottom_crop->Enable (s);
- _filters_button->Enable (s);
- _scaler->Enable (s);
- _audio_stream->Enable (s);
+ _content_add_file->Enable (s);
+ _content_add_folder->Enable (s);
+ _content_remove->Enable (s);
+ _content_timeline->Enable (s);
_dcp_content_type->Enable (s);
- _dcp_trim_start->Enable (s);
- _dcp_trim_end->Enable (s);
- _multiple_reels->Enable (s);
- _reel_size->Enable (s);
- _dcp_ab->Enable (s);
+ _encrypted->Enable (s);
- _colour_lut->Enable (s);
+ _frame_rate->Enable (s);
+ _audio_channels->Enable (s);
_j2k_bandwidth->Enable (s);
- _audio_gain->Enable (s);
- _audio_gain_calculate_button->Enable (s);
- _audio_delay->Enable (s);
- _still_duration->Enable (s);
-
- setup_subtitle_control_sensitivity ();
- setup_audio_control_sensitivity ();
- setup_reel_control_sensitivity ();
- }
+ _container->Enable (s);
+ _best_frame_rate->Enable (s && _film && _film->best_video_frame_rate () != _film->video_frame_rate ());
+ _sequence_video->Enable (s);
+ _resolution->Enable (s);
+ _scaler->Enable (s);
+ _three_d->Enable (s);
+ _standard->Enable (s);
- /** Called when the `Edit filters' button has been clicked */
- void
- FilmEditor::edit_filters_clicked (wxCommandEvent &)
- {
- FilterDialog* d = new FilterDialog (this, _film->filters());
- d->ActiveChanged.connect (bind (&Film::set_filters, _film, _1));
- d->ShowModal ();
- d->Destroy ();
+ /* Set the panels in the content notebook */
+ for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
+ (*i)->Enable (s);
+ }
}
/** Called when the scaler widget has been changed */
void connect_to_widgets ();
/* Handle changes to the view */
- void name_changed (wxCommandEvent &);
- void use_dci_name_toggled (wxCommandEvent &);
- void edit_dci_button_clicked (wxCommandEvent &);
- void left_crop_changed (wxCommandEvent &);
- void right_crop_changed (wxCommandEvent &);
- void top_crop_changed (wxCommandEvent &);
- void bottom_crop_changed (wxCommandEvent &);
- void content_changed (wxCommandEvent &);
- void trust_content_header_changed (wxCommandEvent &);
- void format_changed (wxCommandEvent &);
- void dcp_trim_start_changed (wxCommandEvent &);
- void dcp_trim_end_changed (wxCommandEvent &);
- void multiple_reels_toggled (wxCommandEvent &);
- void reel_size_changed (wxCommandEvent &);
- void dcp_content_type_changed (wxCommandEvent &);
- void encrypted_toggled (wxCommandEvent &);
- void dcp_ab_toggled (wxCommandEvent &);
- void scaler_changed (wxCommandEvent &);
- void audio_gain_changed (wxCommandEvent &);
- void audio_gain_calculate_button_clicked (wxCommandEvent &);
- void audio_delay_changed (wxCommandEvent &);
- void with_subtitles_toggled (wxCommandEvent &);
- void subtitle_offset_changed (wxCommandEvent &);
- void subtitle_scale_changed (wxCommandEvent &);
- void colour_lut_changed (wxCommandEvent &);
- void j2k_bandwidth_changed (wxCommandEvent &);
- void still_duration_changed (wxCommandEvent &);
- void audio_stream_changed (wxCommandEvent &);
- void subtitle_stream_changed (wxCommandEvent &);
- void use_audio_changed (wxCommandEvent &);
- void external_audio_changed (wxCommandEvent &);
+ void name_changed ();
+ void use_dci_name_toggled ();
+ void edit_dci_button_clicked ();
+ void content_selection_changed ();
+ void content_add_file_clicked ();
+ void content_add_folder_clicked ();
+ void content_remove_clicked ();
+ void container_changed ();
+ void dcp_content_type_changed ();
+ void scaler_changed ();
+ void j2k_bandwidth_changed ();
+ void frame_rate_changed ();
+ void best_frame_rate_clicked ();
+ void content_timeline_clicked ();
+ void audio_channels_changed ();
+ void resolution_changed ();
+ void sequence_video_changed ();
+ void content_right_click (wxListEvent &);
+ void three_d_changed ();
+ void standard_changed ();
++ void encrypted_toggled ();
/* Handle changes to the model */
void film_changed (Film::Property);
wxTextCtrl* _name;
wxStaticText* _dcp_name;
wxCheckBox* _use_dci_name;
+ wxChoice* _container;
+ wxListCtrl* _content;
+ wxButton* _content_add_file;
+ wxButton* _content_add_folder;
+ wxButton* _content_remove;
+ wxButton* _content_earlier;
+ wxButton* _content_later;
+ wxButton* _content_timeline;
+ wxCheckBox* _sequence_video;
wxButton* _edit_dci_button;
- /** The Film's format */
- wxComboBox* _format;
- /** The Film's content file */
- wxFilePickerCtrl* _content;
- wxCheckBox* _trust_content_header;
- /** The Film's left crop */
- wxSpinCtrl* _left_crop;
- /** The Film's right crop */
- wxSpinCtrl* _right_crop;
- /** The Film's top crop */
- wxSpinCtrl* _top_crop;
- /** The Film's bottom crop */
- wxSpinCtrl* _bottom_crop;
- /** Currently-applied filters */
- wxStaticText* _filters;
- /** Button to open the filters dialogue */
- wxButton* _filters_button;
- /** The Film's scaler */
- wxComboBox* _scaler;
- wxRadioButton* _use_content_audio;
- wxComboBox* _audio_stream;
- wxRadioButton* _use_external_audio;
- wxFilePickerCtrl* _external_audio[MAX_AUDIO_CHANNELS];
- /** The Film's audio gain */
- wxSpinCtrl* _audio_gain;
- /** A button to open the gain calculation dialogue */
- wxButton* _audio_gain_calculate_button;
- /** The Film's audio delay */
- wxSpinCtrl* _audio_delay;
- wxCheckBox* _with_subtitles;
- wxComboBox* _subtitle_stream;
- wxSpinCtrl* _subtitle_offset;
- wxSpinCtrl* _subtitle_scale;
- wxComboBox* _colour_lut;
- wxSpinCtrl* _j2k_bandwidth;
- /** The Film's DCP content type */
- wxComboBox* _dcp_content_type;
- /** The Film's frames per second */
- wxStaticText* _frames_per_second;
- /** The Film's original size */
- wxStaticText* _original_size;
- /** The Film's length */
- wxStaticText* _length;
- /** The Film's audio details */
- wxStaticText* _audio;
- /** The Film's duration for still sources */
- wxSpinCtrl* _still_duration;
-
- wxSpinCtrl* _dcp_trim_start;
- wxSpinCtrl* _dcp_trim_end;
+ wxChoice* _scaler;
+ wxSpinCtrl* _j2k_bandwidth;
+ wxChoice* _dcp_content_type;
+ wxChoice* _frame_rate;
+ wxSpinCtrl* _audio_channels;
+ wxButton* _best_frame_rate;
+ wxCheckBox* _three_d;
+ wxChoice* _resolution;
+ wxChoice* _standard;
+ wxCheckBox* _encrypted;
- wxCheckBox* _multiple_reels;
- wxSpinCtrl* _reel_size;
- /** Selector to generate an A/B comparison DCP */
- wxCheckBox* _dcp_ab;
- std::list<wxControl*> _video_controls;
- std::list<wxControl*> _still_controls;
+ ContentMenu _menu;
- std::vector<Format const *> _formats;
+ std::vector<Ratio const *> _ratios;
bool _generally_sensitive;
+ TimelineDialog* _timeline_dialog;
};
--- /dev/null
-
- add_label_to_sizer (vertical, this, "Make KDMs for");
-
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program 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.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/treectrl.h>
+#include <wx/datectrl.h>
+#include <wx/timectrl.h>
+#include "lib/cinema.h"
+#include "lib/config.h"
+#include "kdm_dialog.h"
+#include "cinema_dialog.h"
+#include "screen_dialog.h"
+#include "wx_util.h"
+#ifdef __WXMSW__
+#include "dir_picker_ctrl.h"
+#else
+#include <wx/filepicker.h>
+#endif
+
+using std::string;
+using std::map;
+using std::list;
+using std::pair;
+using std::make_pair;
+using boost::shared_ptr;
+
+KDMDialog::KDMDialog (wxWindow* parent)
+ : wxDialog (parent, wxID_ANY, _("Make KDMs"))
+{
+ wxBoxSizer* vertical = new wxBoxSizer (wxVERTICAL);
- target_buttons->Add (_add_cinema, 1, 0, 6);
+ wxBoxSizer* targets = new wxBoxSizer (wxHORIZONTAL);
+
+ _targets = new wxTreeCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT | wxTR_MULTIPLE | wxTR_HAS_BUTTONS);
+ targets->Add (_targets, 1, wxEXPAND | wxALL, 6);
+
+ _root = _targets->AddRoot ("Foo");
+
+ list<shared_ptr<Cinema> > c = Config::instance()->cinemas ();
+ for (list<shared_ptr<Cinema> >::iterator i = c.begin(); i != c.end(); ++i) {
+ add_cinema (*i);
+ }
+
+ _targets->ExpandAll ();
+
+ wxBoxSizer* target_buttons = new wxBoxSizer (wxVERTICAL);
+
+ _add_cinema = new wxButton (this, wxID_ANY, _("Add Cinema..."));
- target_buttons->Add (_edit_cinema, 1, 0, 6);
++ target_buttons->Add (_add_cinema, 1, wxEXPAND, 6);
+ _edit_cinema = new wxButton (this, wxID_ANY, _("Edit Cinema..."));
- target_buttons->Add (_remove_cinema, 1, 0, 6);
++ target_buttons->Add (_edit_cinema, 1, wxEXPAND, 6);
+ _remove_cinema = new wxButton (this, wxID_ANY, _("Remove Cinema"));
- target_buttons->Add (_add_screen, 1, 0, 6);
++ target_buttons->Add (_remove_cinema, 1, wxEXPAND, 6);
+
+ _add_screen = new wxButton (this, wxID_ANY, _("Add Screen..."));
- target_buttons->Add (_edit_screen, 1, 0, 6);
++ target_buttons->Add (_add_screen, 1, wxEXPAND, 6);
+ _edit_screen = new wxButton (this, wxID_ANY, _("Edit Screen..."));
- target_buttons->Add (_remove_screen, 1, 0, 6);
++ target_buttons->Add (_edit_screen, 1, wxEXPAND, 6);
+ _remove_screen = new wxButton (this, wxID_ANY, _("Remove Screen"));
- add_label_to_sizer (table, this, "From");
++ target_buttons->Add (_remove_screen, 1, wxEXPAND, 6);
+
+ targets->Add (target_buttons, 0, 0, 6);
+
+ vertical->Add (targets, 1, wxEXPAND | wxALL, 6);
+
+ wxFlexGridSizer* table = new wxFlexGridSizer (3, 2, 6);
- add_label_to_sizer (table, this, "Until");
++ add_label_to_sizer (table, this, "From", true);
+ _from_date = new wxDatePickerCtrl (this, wxID_ANY);
+ table->Add (_from_date, 1, wxEXPAND);
+ _from_time = new wxTimePickerCtrl (this, wxID_ANY);
+ table->Add (_from_time, 1, wxEXPAND);
+
- add_label_to_sizer (table, this, "Write to");
++ add_label_to_sizer (table, this, "Until", true);
+ _until_date = new wxDatePickerCtrl (this, wxID_ANY);
+ table->Add (_until_date, 1, wxEXPAND);
+ _until_time = new wxTimePickerCtrl (this, wxID_ANY);
+ table->Add (_until_time, 1, wxEXPAND);
+
- wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
++ add_label_to_sizer (table, this, "Write to", true);
+
+#ifdef __WXMSW__
+ _folder = new DirPickerCtrl (this);
+#else
+ _folder = new wxDirPickerCtrl (this, wxDD_DIR_MUST_EXIST);
+#endif
+
+ table->Add (_folder, 1, wxEXPAND);
+
+ vertical->Add (table, 0, wxEXPAND | wxALL, 6);
+
++ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ vertical->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ _targets->Connect (wxID_ANY, wxEVT_COMMAND_TREE_SEL_CHANGED, wxCommandEventHandler (KDMDialog::targets_selection_changed), 0, this);
+
+ _add_cinema->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::add_cinema_clicked), 0, this);
+ _edit_cinema->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::edit_cinema_clicked), 0, this);
+ _remove_cinema->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::remove_cinema_clicked), 0, this);
+
+ _add_screen->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::add_screen_clicked), 0, this);
+ _edit_screen->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::edit_screen_clicked), 0, this);
+ _remove_screen->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (KDMDialog::remove_screen_clicked), 0, this);
+
+ setup_sensitivity ();
+
+ SetSizer (vertical);
+ vertical->Layout ();
+ vertical->SetSizeHints (this);
+}
+
+list<pair<wxTreeItemId, shared_ptr<Cinema> > >
+KDMDialog::selected_cinemas () const
+{
+ wxArrayTreeItemIds s;
+ _targets->GetSelections (s);
+
+ list<pair<wxTreeItemId, shared_ptr<Cinema> > > c;
+ for (size_t i = 0; i < s.GetCount(); ++i) {
+ map<wxTreeItemId, shared_ptr<Cinema> >::const_iterator j = _cinemas.find (s[i]);
+ if (j != _cinemas.end ()) {
+ c.push_back (make_pair (j->first, j->second));
+ }
+ }
+
+ return c;
+}
+
+list<pair<wxTreeItemId, shared_ptr<Screen> > >
+KDMDialog::selected_screens () const
+{
+ wxArrayTreeItemIds s;
+ _targets->GetSelections (s);
+
+ list<pair<wxTreeItemId, shared_ptr<Screen> > > c;
+ for (size_t i = 0; i < s.GetCount(); ++i) {
+ map<wxTreeItemId, shared_ptr<Screen> >::const_iterator j = _screens.find (s[i]);
+ if (j != _screens.end ()) {
+ c.push_back (make_pair (j->first, j->second));
+ }
+ }
+
+ return c;
+}
+
+void
+KDMDialog::targets_selection_changed (wxCommandEvent &)
+{
+ setup_sensitivity ();
+}
+
+void
+KDMDialog::setup_sensitivity ()
+{
+ bool const sc = selected_cinemas().size() == 1;
+ bool const ss = selected_screens().size() == 1;
+
+ _edit_cinema->Enable (sc);
+ _remove_cinema->Enable (sc);
+
+ _add_screen->Enable (sc);
+ _edit_screen->Enable (ss);
+ _remove_screen->Enable (ss);
+}
+
+void
+KDMDialog::add_cinema (shared_ptr<Cinema> c)
+{
+ _cinemas[_targets->AppendItem (_root, std_to_wx (c->name))] = c;
+
+ for (list<shared_ptr<Screen> >::iterator i = c->screens.begin(); i != c->screens.end(); ++i) {
+ add_screen (c, *i);
+ }
+}
+
+void
+KDMDialog::add_screen (shared_ptr<Cinema> c, shared_ptr<Screen> s)
+{
+ map<wxTreeItemId, shared_ptr<Cinema> >::const_iterator i = _cinemas.begin();
+ while (i != _cinemas.end() && i->second != c) {
+ ++i;
+ }
+
+ if (i == _cinemas.end()) {
+ return;
+ }
+
+ _screens[_targets->AppendItem (i->first, std_to_wx (s->name))] = s;
+}
+
+void
+KDMDialog::add_cinema_clicked (wxCommandEvent &)
+{
+ CinemaDialog* d = new CinemaDialog (this, "Add Cinema");
+ d->ShowModal ();
+
+ shared_ptr<Cinema> c (new Cinema (d->name(), d->email()));
+ Config::instance()->add_cinema (c);
+ add_cinema (c);
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::edit_cinema_clicked (wxCommandEvent &)
+{
+ if (selected_cinemas().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Cinema> > c = selected_cinemas().front();
+
+ CinemaDialog* d = new CinemaDialog (this, "Edit cinema", c.second->name, c.second->email);
+ d->ShowModal ();
+
+ c.second->name = d->name ();
+ c.second->email = d->email ();
+ _targets->SetItemText (c.first, std_to_wx (d->name()));
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::remove_cinema_clicked (wxCommandEvent &)
+{
+ if (selected_cinemas().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Cinema> > c = selected_cinemas().front();
+
+ Config::instance()->remove_cinema (c.second);
+ _targets->Delete (c.first);
+
+ Config::instance()->write ();
+}
+
+void
+KDMDialog::add_screen_clicked (wxCommandEvent &)
+{
+ if (selected_cinemas().size() != 1) {
+ return;
+ }
+
+ shared_ptr<Cinema> c = selected_cinemas().front().second;
+
+ ScreenDialog* d = new ScreenDialog (this, "Add Screen");
+ d->ShowModal ();
+
+ shared_ptr<Screen> s (new Screen (d->name(), d->certificate()));
+ c->screens.push_back (s);
+ add_screen (c, s);
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::edit_screen_clicked (wxCommandEvent &)
+{
+ if (selected_screens().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Screen> > s = selected_screens().front();
+
+ ScreenDialog* d = new ScreenDialog (this, "Edit screen", s.second->name, s.second->certificate);
+ d->ShowModal ();
+
+ s.second->name = d->name ();
+ s.second->certificate = d->certificate ();
+ _targets->SetItemText (s.first, std_to_wx (d->name()));
+
+ Config::instance()->write ();
+
+ d->Destroy ();
+}
+
+void
+KDMDialog::remove_screen_clicked (wxCommandEvent &)
+{
+ if (selected_screens().size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<Screen> > s = selected_screens().front();
+
+ map<wxTreeItemId, shared_ptr<Cinema> >::iterator i = _cinemas.begin ();
+ while (i != _cinemas.end() && find (i->second->screens.begin(), i->second->screens.end(), s.second) == i->second->screens.end()) {
+ ++i;
+ }
+
+ if (i == _cinemas.end()) {
+ return;
+ }
+
+ i->second->screens.remove (s.second);
+ _targets->Delete (s.first);
+
+ Config::instance()->write ();
+}
+
+list<shared_ptr<Screen> >
+KDMDialog::screens () const
+{
+ list<shared_ptr<Screen> > s;
+
+ list<pair<wxTreeItemId, shared_ptr<Cinema> > > cinemas = selected_cinemas ();
+ for (list<pair<wxTreeItemId, shared_ptr<Cinema> > >::iterator i = cinemas.begin(); i != cinemas.end(); ++i) {
+ for (list<shared_ptr<Screen> >::iterator j = i->second->screens.begin(); j != i->second->screens.end(); ++j) {
+ s.push_back (*j);
+ }
+ }
+
+ list<pair<wxTreeItemId, shared_ptr<Screen> > > screens = selected_screens ();
+ for (list<pair<wxTreeItemId, shared_ptr<Screen> > >::iterator i = screens.begin(); i != screens.end(); ++i) {
+ s.push_back (i->second);
+ }
+
+ s.sort ();
+ s.unique ();
+
+ return s;
+}
+
+boost::posix_time::ptime
+KDMDialog::from () const
+{
+ return posix_time (_from_date, _from_time);
+}
+
+boost::posix_time::ptime
+KDMDialog::posix_time (wxDatePickerCtrl* date_picker, wxTimePickerCtrl* time_picker)
+{
+ wxDateTime const date = date_picker->GetValue ();
+ wxDateTime const time = time_picker->GetValue ();
+ return boost::posix_time::ptime (
+ boost::gregorian::date (date.GetYear(), date.GetMonth() + 1, date.GetDay()),
+ boost::posix_time::time_duration (time.GetHour(), time.GetMinute(), time.GetSecond())
+ );
+}
+
+boost::posix_time::ptime
+KDMDialog::until () const
+{
+ return posix_time (_until_date, _until_time);
+}
+
+string
+KDMDialog::directory () const
+{
+ return wx_to_std (_folder->GetPath ());
+}
--- /dev/null
- add_label_to_sizer (table, this, "Name");
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program 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.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/filepicker.h>
+#include <wx/validate.h>
+#include <libdcp/exceptions.h>
+#include "lib/compose.hpp"
+#include "screen_dialog.h"
+#include "wx_util.h"
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+
+ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, shared_ptr<libdcp::Certificate> certificate)
+ : wxDialog (parent, wxID_ANY, std_to_wx (title))
+ , _certificate (certificate)
+{
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
+ table->AddGrowableCol (1, 1);
+
- add_label_to_sizer (table, this, "Certificate");
++ add_label_to_sizer (table, this, "Name", true);
+ _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (320, -1));
+ table->Add (_name, 1, wxEXPAND);
+
++ add_label_to_sizer (table, this, "Certificate", true);
+ _certificate_load = new wxButton (this, wxID_ANY, wxT ("Load from file..."));
+ table->Add (_certificate_load, 1, wxEXPAND);
+
+ table->AddSpacer (0);
+ _certificate_text = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxSize (320, 256), wxTE_MULTILINE | wxTE_READONLY);
+ if (certificate) {
+ _certificate_text->SetValue (certificate->certificate ());
+ }
+ wxFont font = wxSystemSettings::GetFont (wxSYS_ANSI_FIXED_FONT);
+ font.SetPointSize (font.GetPointSize() / 2);
+ _certificate_text->SetFont (font);
+ table->Add (_certificate_text, 1, wxEXPAND);
+
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ SetSizer (overall_sizer);
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
+
+ _certificate_load->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (ScreenDialog::load_certificate), 0, this);
+}
+
+string
+ScreenDialog::name () const
+{
+ return wx_to_std (_name->GetValue());
+}
+
+shared_ptr<libdcp::Certificate>
+ScreenDialog::certificate () const
+{
+ return _certificate;
+}
+
+void
+ScreenDialog::load_certificate (wxCommandEvent &)
+{
+ wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
+ d->ShowModal ();
+
+ try {
+ _certificate.reset (new libdcp::Certificate (wx_to_std (d->GetPath ())));
+ _certificate_text->SetValue (_certificate->certificate ());
+ } catch (libdcp::MiscError& e) {
+ error_dialog (this, String::compose ("Could not read certificate file (%1)", e.what()));
+ }
+
+ d->Destroy ();
+}
+ import os
+ import glob
+ from waflib import Logs
+ import i18n
+
+ sources = """
+ about_dialog.cc
+ audio_dialog.cc
+ audio_mapping_view.cc
+ audio_panel.cc
+ audio_plot.cc
++ cinema_dialog.cc
+ colour_conversion_editor.cc
+ config_dialog.cc
+ content_colour_conversion_dialog.cc
+ content_menu.cc
+ dci_metadata_dialog.cc
+ dir_picker_ctrl.cc
+ film_editor.cc
+ film_editor_panel.cc
+ film_viewer.cc
+ filter_dialog.cc
+ filter_editor.cc
+ gain_calculator_dialog.cc
+ job_manager_view.cc
+ job_wrapper.cc
++ kdm_dialog.cc
+ new_film_dialog.cc
+ preset_colour_conversion_dialog.cc
+ properties_dialog.cc
+ repeat_dialog.cc
++ screen_dialog.cc
+ server_dialog.cc
+ subtitle_panel.cc
+ timecode.cc
+ timeline.cc
+ timeline_dialog.cc
+ timing_panel.cc
+ video_panel.cc
+ wx_util.cc
+ wx_ui_signaller.cc
+ """
+
def configure(conf):
- conf.check_cfg(package = '', path = 'wx-config', args = '--cppflags --cxxflags --libs', uselib_store = 'WXWIDGETS', mandatory = True)
+ args = '--cppflags --cxxflags'
+ if not conf.env.STATIC:
+ args += ' --libs'
+
+ conf.check_cfg(msg='Checking for wxWidgets', package='', path=conf.options.wx_config, args=args,
+ uselib_store='WXWIDGETS', mandatory=True)
+
+ if conf.env.STATIC:
+ # wx-config returns its static libraries as full paths, without -l prefixes, which confuses
+ # check_cfg(), so just hard-code it all.
+ conf.env.STLIB_WXWIDGETS = ['wx_gtk2u_xrc-2.9', 'wx_gtk2u_qa-2.9', 'wx_baseu_net-2.9', 'wx_gtk2u_html-2.9',
+ 'wx_gtk2u_adv-2.9', 'wx_gtk2u_core-2.9', 'wx_baseu_xml-2.9', 'wx_baseu-2.9']
+ conf.env.LIB_WXWIDGETS = ['tiff', 'SM', 'dl', 'jpeg', 'png', 'X11']
+
+ conf.in_msg = 1
+ wx_version = conf.check_cfg(package='', path=conf.options.wx_config, args='--version').strip()
+ conf.im_msg = 0
+ if wx_version != '2.9.4' and wx_version != '2.9.5':
+ conf.fatal('wxwidgets version 2.9.4 or 2.9.5 is required; %s found' % wx_version)
def build(bld):
if bld.env.STATIC: