Merge master.
authorCarl Hetherington <cth@carlh.net>
Tue, 16 Apr 2013 10:43:29 +0000 (11:43 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 16 Apr 2013 10:43:29 +0000 (11:43 +0100)
1  2 
src/lib/film.cc
src/lib/film.h
src/lib/po/sv_SE.po
src/lib/writer.cc
src/lib/wscript
src/tools/dcpomatic.cc
src/wx/config_dialog.cc
src/wx/film_editor.cc
src/wx/film_editor.h
test/test.cc

diff --cc src/lib/film.cc
index 67605ffcaba2c6836fd69be1be20d7202db5792a,a42b874e856d3470e9d7526bc8d5b6b53a9cd771..c8aee7a0aa6bdf6bc6b925209a6ae92043f72098
@@@ -98,9 -93,12 +98,10 @@@ Film::Film (string d, bool must_exist
        , _scaler (Scaler::from_id ("bicubic"))
        , _trim_start (0)
        , _trim_end (0)
 -      , _dcp_ab (false)
 -      , _use_content_audio (true)
+       , _trim_type (CPL)
 +      , _ab (false)
        , _audio_gain (0)
        , _audio_delay (0)
 -      , _still_duration (10)
        , _with_subtitles (false)
        , _subtitle_offset (0)
        , _subtitle_scale (1)
@@@ -165,9 -164,15 +166,10 @@@ Film::Film (Film const & o
        , _scaler            (o._scaler)
        , _trim_start        (o._trim_start)
        , _trim_end          (o._trim_end)
 -      , _dcp_ab            (o._dcp_ab)
 -      , _content_audio_stream (o._content_audio_stream)
 -      , _external_audio    (o._external_audio)
 -      , _use_content_audio (o._use_content_audio)
+       , _trim_type         (o._trim_type)
 +      , _ab                (o._ab)
        , _audio_gain        (o._audio_gain)
        , _audio_delay       (o._audio_delay)
 -      , _still_duration    (o._still_duration)
 -      , _subtitle_stream   (o._subtitle_stream)
        , _with_subtitles    (o._with_subtitles)
        , _subtitle_offset   (o._subtitle_offset)
        , _subtitle_scale    (o._subtitle_scale)
@@@ -378,49 -429,81 +408,60 @@@ Film::write_metadata () cons
  
        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 << endl;
 +      root->add_child("Version")->add_child_text (boost::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");
 +      root->add_child("TrustContentHeaders")->add_child_text (_trust_content_headers ? "1" : "0");
 -      /* User stuff */
 -      f << "name " << _name << endl;
 -      f << "use_dci_name " << _use_dci_name << endl;
 -      f << "content " << _content << endl;
 -      f << "trust_content_header " << (_trust_content_header ? "1" : "0") << endl;
        if (_dcp_content_type) {
 -              f << "dcp_content_type " << _dcp_content_type->dci_name () << endl;
 +              root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ());
        }
++
        if (_format) {
 -              f << "format " << _format->as_metadata () << endl;
 -      }
 -      f << "left_crop " << _crop.left << endl;
 -      f << "right_crop " << _crop.right << endl;
 -      f << "top_crop " << _crop.top << endl;
 -      f << "bottom_crop " << _crop.bottom << endl;
 -      for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
 -              f << "filter " << (*i)->id () << endl;
 +              root->add_child("Format")->add_child_text (_format->id ());
        }
 -      f << "scaler " << _scaler->id () << endl;
 -      f << "trim_start " << _trim_start << endl;
 -      f << "trim_end " << _trim_end << endl;
++
+       switch (_trim_type) {
+       case CPL:
 -              f << "trim_type cpl\n";
++              root->add_child("TrimType")->add_child_text ("CPL");
+               break;
+       case ENCODE:
 -              f << "trim_type encode\n";
 -              break;
 -      }
 -      f << "dcp_ab " << (_dcp_ab ? "1" : "0") << endl;
 -      if (_content_audio_stream) {
 -              f << "selected_content_audio_stream " << _content_audio_stream->to_string() << endl;
++              root->add_child("TrimType")->add_child_text ("Encode");
+       }
 -      for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) {
 -              f << "external_audio " << *i << endl;
 -      }
 -      f << "use_content_audio " << (_use_content_audio ? "1" : "0") << endl;
 -      f << "audio_gain " << _audio_gain << endl;
 -      f << "audio_delay " << _audio_delay << endl;
 -      f << "still_duration " << _still_duration << endl;
 -      if (_subtitle_stream) {
 -              f << "selected_subtitle_stream " << _subtitle_stream->to_string() << endl;
 -      }
 -      f << "with_subtitles " << _with_subtitles << endl;
 -      f << "subtitle_offset " << _subtitle_offset << endl;
 -      f << "subtitle_scale " << _subtitle_scale << endl;
 -      f << "colour_lut " << _colour_lut << endl;
 -      f << "j2k_bandwidth " << _j2k_bandwidth << endl;
 -      _dci_metadata.write (f);
 -      f << "dci_date " << boost::gregorian::to_iso_string (_dci_date) << endl;
 -      f << "dcp_frame_rate " << _dcp_frame_rate << endl;
 -      f << "width " << _size.width << endl;
 -      f << "height " << _size.height << endl;
 -      f << "length " << _length.get_value_or(0) << endl;
 -      f << "content_digest " << _content_digest << endl;
 -
 -      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 () << endl;
 -      }
 -
 -      f << "external_audio_stream " << _sndfile_stream->to_string() << endl;
++                      
 +      root->add_child("LeftCrop")->add_child_text (boost::lexical_cast<string> (_crop.left));
 +      root->add_child("RightCrop")->add_child_text (boost::lexical_cast<string> (_crop.right));
 +      root->add_child("TopCrop")->add_child_text (boost::lexical_cast<string> (_crop.top));
 +      root->add_child("BottomCrop")->add_child_text (boost::lexical_cast<string> (_crop.bottom));
  
 -      for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
 -              f << "subtitle_stream " << (*i)->to_string () << endl;
 +      for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
 +              root->add_child("Filter")->add_child_text ((*i)->id ());
        }
 -
 -      f << "source_frame_rate " << _source_frame_rate << endl;
 +      
 +      root->add_child("Scaler")->add_child_text (_scaler->id ());
 +      root->add_child("TrimStart")->add_child_text (boost::lexical_cast<string> (_trim_start));
 +      root->add_child("TrimEnd")->add_child_text (boost::lexical_cast<string> (_trim_end));
 +      root->add_child("AB")->add_child_text (_ab ? "1" : "0");
 +      root->add_child("AudioGain")->add_child_text (boost::lexical_cast<string> (_audio_gain));
 +      root->add_child("AudioDelay")->add_child_text (boost::lexical_cast<string> (_audio_delay));
 +      root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
 +      root->add_child("SubtitleOffset")->add_child_text (boost::lexical_cast<string> (_subtitle_offset));
 +      root->add_child("SubtitleScale")->add_child_text (boost::lexical_cast<string> (_subtitle_scale));
 +      root->add_child("ColourLUT")->add_child_text (boost::lexical_cast<string> (_colour_lut));
 +      root->add_child("J2KBandwidth")->add_child_text (boost::lexical_cast<string> (_j2k_bandwidth));
 +      _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
 +      root->add_child("DCPFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_frame_rate));
 +      root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
 +      _audio_mapping.as_xml (root->add_child("AudioMapping"));
 +
 +      for (ContentList::iterator i = the_content.begin(); i != the_content.end(); ++i) {
 +              (*i)->as_xml (root->add_child ("Content"));
 +      }
 +
 +      doc.write_to_file_formatted (file ("metadata.xml"));
        
        _dirty = false;
  }
@@@ -431,80 -514,166 +472,89 @@@ Film::read_metadata (
  {
        boost::mutex::scoped_lock lm (_state_mutex);
  
 -      _external_audio.clear ();
 -      _content_audio_streams.clear ();
 -      _subtitle_streams.clear ();
 -
 -      boost::optional<int> version;
 -
 -      /* Backward compatibility things */
 -      boost::optional<int> audio_sample_rate;
 -      boost::optional<int> audio_stream_index;
 -      boost::optional<int> subtitle_stream_index;
 -
 -      ifstream f (file ("metadata").c_str());
 -      if (!f.good()) {
 -              throw OpenFileError (file ("metadata"));
 +      if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
 +              throw StringError (_("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!"));
        }
 -      
 -      multimap<string, string> kv = read_key_value (f);
  
 -      /* We need version before anything else */
 -      multimap<string, string>::iterator v = kv.find ("version");
 -      if (v != kv.end ()) {
 -              version = atoi (v->second.c_str());
 -      }
 +      cxml::File f (file ("metadata.xml"), "Metadata");
        
 -      for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
 -              string const k = i->first;
 -              string const v = i->second;
 +      _name = f.string_child ("Name");
 +      _use_dci_name = f.bool_child ("UseDCIName");
 +      _trust_content_headers = f.bool_child ("TrustContentHeaders");
  
 -              if (k == "audio_sample_rate") {
 -                      audio_sample_rate = atoi (v.c_str());
 +      {
 +              optional<string> c = f.optional_string_child ("DCPContentType");
 +              if (c) {
 +                      _dcp_content_type = DCPContentType::from_dci_name (c.get ());
                }
 +      }
  
 -              /* User-specified stuff */
 -              if (k == "name") {
 -                      _name = v;
 -              } else if (k == "use_dci_name") {
 -                      _use_dci_name = (v == "1");
 -              } else if (k == "content") {
 -                      _content = v;
 -              } else if (k == "trust_content_header") {
 -                      _trust_content_header = (v == "1");
 -              } else if (k == "dcp_content_type") {
 -                      if (version < 3) {
 -                              _dcp_content_type = DCPContentType::from_pretty_name (v);
 -                      } else {
 -                              _dcp_content_type = DCPContentType::from_dci_name (v);
 -                      }
 -              } else if (k == "format") {
 -                      _format = Format::from_metadata (v);
 -              } else if (k == "left_crop") {
 -                      _crop.left = atoi (v.c_str ());
 -              } else if (k == "right_crop") {
 -                      _crop.right = atoi (v.c_str ());
 -              } else if (k == "top_crop") {
 -                      _crop.top = atoi (v.c_str ());
 -              } else if (k == "bottom_crop") {
 -                      _crop.bottom = atoi (v.c_str ());
 -              } else if (k == "filter") {
 -                      _filters.push_back (Filter::from_id (v));
 -              } else if (k == "scaler") {
 -                      _scaler = Scaler::from_id (v);
 -              } else if ( ((!version || version < 2) && k == "dcp_trim_start") || k == "trim_start") {
 -                      _trim_start = atoi (v.c_str ());
 -              } else if ( ((!version || version < 2) && k == "dcp_trim_end") || k == "trim_end") {
 -                      _trim_end = atoi (v.c_str ());
 -              } else if (k == "trim_type") {
 -                      if (v == "cpl") {
 -                              _trim_type = CPL;
 -                      } else if (v == "encode") {
 -                              _trim_type = ENCODE;
 -                      }
 -              } else if (k == "dcp_ab") {
 -                      _dcp_ab = (v == "1");
 -              } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
 -                      if (!version) {
 -                              audio_stream_index = atoi (v.c_str ());
 -                      } else {
 -                              _content_audio_stream = audio_stream_factory (v, version);
 -                      }
 -              } else if (k == "external_audio") {
 -                      _external_audio.push_back (v);
 -              } else if (k == "use_content_audio") {
 -                      _use_content_audio = (v == "1");
 -              } else if (k == "audio_gain") {
 -                      _audio_gain = atof (v.c_str ());
 -              } else if (k == "audio_delay") {
 -                      _audio_delay = atoi (v.c_str ());
 -              } else if (k == "still_duration") {
 -                      _still_duration = atoi (v.c_str ());
 -              } else if (k == "selected_subtitle_stream") {
 -                      if (!version) {
 -                              subtitle_stream_index = atoi (v.c_str ());
 -                      } else {
 -                              _subtitle_stream = subtitle_stream_factory (v, version);
 -                      }
 -              } else if (k == "with_subtitles") {
 -                      _with_subtitles = (v == "1");
 -              } else if (k == "subtitle_offset") {
 -                      _subtitle_offset = atoi (v.c_str ());
 -              } else if (k == "subtitle_scale") {
 -                      _subtitle_scale = atof (v.c_str ());
 -              } else if (k == "colour_lut") {
 -                      _colour_lut = atoi (v.c_str ());
 -              } else if (k == "j2k_bandwidth") {
 -                      _j2k_bandwidth = atoi (v.c_str ());
 -              } else if (k == "dci_date") {
 -                      _dci_date = boost::gregorian::from_undelimited_string (v);
 -              } else if (k == "dcp_frame_rate") {
 -                      _dcp_frame_rate = atoi (v.c_str ());
 +      {
 +              optional<string> c = f.optional_string_child ("Format");
 +              if (c) {
 +                      _format = Format::from_id (c.get ());
                }
 +      }
  
 -              _dci_metadata.read (k, v);
 -              
 -              /* Cached stuff */
 -              if (k == "width") {
 -                      _size.width = atoi (v.c_str ());
 -              } else if (k == "height") {
 -                      _size.height = atoi (v.c_str ());
 -              } else if (k == "length") {
 -                      int const vv = atoi (v.c_str ());
 -                      if (vv) {
 -                              _length = vv;
 -                      }
 -              } else if (k == "content_digest") {
 -                      _content_digest = v;
 -              } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
 -                      _content_audio_streams.push_back (audio_stream_factory (v, version));
 -              } else if (k == "external_audio_stream") {
 -                      _sndfile_stream = audio_stream_factory (v, version);
 -              } else if (k == "subtitle_stream") {
 -                      _subtitle_streams.push_back (subtitle_stream_factory (v, version));
 -              } else if (k == "source_frame_rate") {
 -                      _source_frame_rate = atof (v.c_str ());
 -              } else if (version < 4 && k == "frames_per_second") {
 -                      _source_frame_rate = atof (v.c_str ());
 -                      /* Fill in what would have been used for DCP frame rate by the older version */
 -                      _dcp_frame_rate = best_dcp_frame_rate (_source_frame_rate);
++      {
++              optional<string> c = f.optional_string_child ("TrimType");
++              if (!c || c.get() == "CPL") {
++                      _trim_type = CPL;
++              } else if (c && c.get() == "Encode") {
++                      _trim_type = ENCODE;
+               }
+       }
 -      if (!version) {
 -              if (audio_sample_rate) {
 -                      /* version < 1 didn't specify sample rate in the audio streams, so fill it in here */
 -                      for (vector<shared_ptr<AudioStream> >::iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
 -                              (*i)->set_sample_rate (audio_sample_rate.get());
 -                      }
 -              }
 +      _crop.left = f.number_child<int> ("LeftCrop");
 +      _crop.right = f.number_child<int> ("RightCrop");
 +      _crop.top = f.number_child<int> ("TopCrop");
 +      _crop.bottom = f.number_child<int> ("BottomCrop");
  
 -              /* also the selected stream was specified as an index */
 -              if (audio_stream_index && audio_stream_index.get() >= 0 && audio_stream_index.get() < (int) _content_audio_streams.size()) {
 -                      _content_audio_stream = _content_audio_streams[audio_stream_index.get()];
 +      {
 +              list<shared_ptr<cxml::Node> > c = f.node_children ("Filter");
 +              for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
 +                      _filters.push_back (Filter::from_id ((*i)->content ()));
                }
 +      }
  
 -              /* similarly the subtitle */
 -              if (subtitle_stream_index && subtitle_stream_index.get() >= 0 && subtitle_stream_index.get() < (int) _subtitle_streams.size()) {
 -                      _subtitle_stream = _subtitle_streams[subtitle_stream_index.get()];
 +      _scaler = Scaler::from_id (f.string_child ("Scaler"));
 +      _trim_start = f.number_child<int> ("TrimStart");
 +      _trim_end = f.number_child<int> ("TrimEnd");
 +      _ab = f.bool_child ("AB");
 +      _audio_gain = f.number_child<float> ("AudioGain");
 +      _audio_delay = f.number_child<int> ("AudioDelay");
 +      _with_subtitles = f.bool_child ("WithSubtitles");
 +      _subtitle_offset = f.number_child<float> ("SubtitleOffset");
 +      _subtitle_scale = f.number_child<float> ("SubtitleScale");
 +      _colour_lut = f.number_child<int> ("ColourLUT");
 +      _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
 +      _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
 +      _dcp_frame_rate = f.number_child<int> ("DCPFrameRate");
 +      _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
 +
 +      list<shared_ptr<cxml::Node> > c = f.node_children ("Content");
 +      for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
 +
 +              string const type = (*i)->string_child ("Type");
 +              boost::shared_ptr<Content> c;
 +              
 +              if (type == "FFmpeg") {
 +                      c.reset (new FFmpegContent (*i));
 +              } else if (type == "ImageMagick") {
 +                      c.reset (new ImageMagickContent (*i));
 +              } else if (type == "Sndfile") {
 +                      c.reset (new SndfileContent (*i));
                }
 +
 +              _content.push_back (c);
        }
 -              
 +
 +      /* This must come after we've loaded the content, as we're looking things up in _content */
 +      _audio_mapping.set_from_xml (_content, f.node_child ("AudioMapping"));
 +
        _dirty = false;
 +
 +      _playlist->setup (_content);
  }
  
  libdcp::Size
@@@ -847,8 -1140,54 +897,18 @@@ Film::set_trim_end (int t
        signal_changed (TRIM_END);
  }
  
+ void
+ Film::set_trim_type (TrimType t)
+ {
+       {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               _trim_type = t;
+       }
+       signal_changed (TRIM_TYPE);
+ }
  void
 -Film::set_dcp_ab (bool a)
 -{
 -      {
 -              boost::mutex::scoped_lock lm (_state_mutex);
 -              _dcp_ab = a;
 -      }
 -      signal_changed (DCP_AB);
 -}
 -
 -void
 -Film::set_content_audio_stream (shared_ptr<AudioStream> s)
 -{
 -      {
 -              boost::mutex::scoped_lock lm (_state_mutex);
 -              _content_audio_stream = s;
 -      }
 -      signal_changed (CONTENT_AUDIO_STREAM);
 -}
 -
 -void
 -Film::set_external_audio (vector<string> a)
 -{
 -      {
 -              boost::mutex::scoped_lock lm (_state_mutex);
 -              _external_audio = a;
 -      }
 -
 -      shared_ptr<SndfileDecoder> decoder (new SndfileDecoder (shared_from_this(), DecodeOptions()));
 -      if (decoder->audio_stream()) {
 -              _sndfile_stream = decoder->audio_stream ();
 -      }
 -      
 -      signal_changed (EXTERNAL_AUDIO);
 -}
 -
 -void
 -Film::set_use_content_audio (bool e)
 +Film::set_ab (bool a)
  {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
diff --cc src/lib/film.h
index ffa5d0690ef108a23a78773d660be03b62599bf8,dd0a83d94e473f5da6d5b9e98d1378e247c0c3eb..071f474aca6f645a097dee2360e88e0a05cc6141
@@@ -63,13 -64,17 +63,16 @@@ public
        std::string info_dir () const;
        std::string j2c_path (int f, bool t) const;
        std::string info_path (int f) const;
-       std::string video_mxf_dir () const;
-       std::string video_mxf_filename () const;
+       std::string internal_video_mxf_dir () const;
+       std::string internal_video_mxf_filename () const;
        std::string audio_analysis_path () const;
  
 -      void examine_content ();
 +      void examine_content (boost::shared_ptr<Content>);
+       std::string dcp_video_mxf_filename () const;
+       std::string dcp_audio_mxf_filename () const;
        void analyse_audio ();
        void send_dcp_to_tms ();
 -
        void make_dcp ();
  
        /** @return Logger.
                return _dirty;
        }
  
 +      bool have_dcp () const;
 +
 +      boost::shared_ptr<Player> player () const;
 +
 +      /* Proxies for some Playlist methods */
 +
 +      ContentAudioFrame audio_length () const;
        int audio_channels () const;
 +      int audio_frame_rate () const;
 +      bool has_audio () const;
 +      
 +      float video_frame_rate () const;
 +      libdcp::Size video_size () const;
 +      ContentVideoFrame video_length () const;        
  
 -      void set_dci_date_today ();
 +      std::vector<FFmpegSubtitleStream> ffmpeg_subtitle_streams () const;
 +      boost::optional<FFmpegSubtitleStream> ffmpeg_subtitle_stream () const;
 +      std::vector<FFmpegAudioStream> ffmpeg_audio_streams () const;
 +      boost::optional<FFmpegAudioStream> ffmpeg_audio_stream () const;
  
 -      bool have_dcp () const;
 +      void set_ffmpeg_subtitle_stream (FFmpegSubtitleStream);
 +      void set_ffmpeg_audio_stream (FFmpegAudioStream);
  
+       enum TrimType {
+               CPL,
+               ENCODE
+       };
        /** Identifiers for the parts of our state;
            used for signalling changes.
        */
                SCALER,
                TRIM_START,
                TRIM_END,
 -              DCP_AB,
 -              CONTENT_AUDIO_STREAM,
 -              EXTERNAL_AUDIO,
 -              USE_CONTENT_AUDIO,
 +              AB,
+               TRIM_TYPE,
                AUDIO_GAIN,
                AUDIO_DELAY,
 -              STILL_DURATION,
 -              SUBTITLE_STREAM,
                WITH_SUBTITLES,
                SUBTITLE_OFFSET,
                SUBTITLE_SCALE,
                return _trim_end;
        }
  
 -      bool dcp_ab () const {
+       TrimType trim_type () const {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               return _trim_type;
+       }
 +      bool ab () const {
                boost::mutex::scoped_lock lm (_state_mutex);
 -              return _dcp_ab;
 +              return _ab;
        }
  
 -      boost::shared_ptr<AudioStream> content_audio_stream () const {
 -              boost::mutex::scoped_lock lm (_state_mutex);
 -              return _content_audio_stream;
 -      }
 -
 -      std::vector<std::string> external_audio () const {
 -              boost::mutex::scoped_lock lm (_state_mutex);
 -              return _external_audio;
 -      }
 -
 -      bool use_content_audio () const {
 -              boost::mutex::scoped_lock lm (_state_mutex);
 -              return _use_content_audio;
 -      }
 -      
        float audio_gain () const {
                boost::mutex::scoped_lock lm (_state_mutex);
                return _audio_gain;
        void set_scaler (Scaler const *);
        void set_trim_start (int);
        void set_trim_end (int);
 -      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_ab (bool);
+       void set_trim_type (TrimType);
        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);
  
  private:
        
 -      /** Log to write to */
 -      boost::shared_ptr<Log> _log;
 -
 -      /** Any running ExamineContentJob, or 0 */
 -      boost::shared_ptr<ExamineContentJob> _examine_content_job;
 -      /** Any running AnalyseAudioJob, or 0 */
 -      boost::shared_ptr<AnalyseAudioJob> _analyse_audio_job;
 -
        void signal_changed (Property);
 -      void examine_content_finished ();
        void analyse_audio_finished ();
        std::string video_state_identifier () const;
 +      void read_metadata ();
 +      void content_changed (boost::weak_ptr<Content>, int);
 +      boost::shared_ptr<FFmpegContent> ffmpeg () const;
 +      void setup_default_audio_mapping ();
+       std::string filename_safe_name () const;
  
 +      /** Log to write to */
 +      boost::shared_ptr<Log> _log;
 +      /** Any running AnalyseAudioJob, or 0 */
 +      boost::shared_ptr<AnalyseAudioJob> _analyse_audio_job;
 +      boost::shared_ptr<Playlist> _playlist;
 +
        /** Complete path to directory containing the film metadata;
         *  must not be relative.
         */
index c8695ce4dc24423fe05676e05d14a86e7d203ed8,ef8109dfa6ada42f22cf0485af3f677707e6cb31..f89874e35e9b952399a227c4f8999a2bbec2d025
@@@ -5,13 -5,12 +5,12 @@@
  #
  msgid ""
  msgstr ""
 -"Project-Id-Version: DVD-o-matic\n"
 +"Project-Id-Version: DCP-o-matic\n"
  "Report-Msgid-Bugs-To: \n"
  "POT-Creation-Date: 2013-04-09 11:14+0100\n"
- "PO-Revision-Date: 2013-04-09 10:13+0100\n"
+ "PO-Revision-Date: 2013-04-10 15:35+0100\n"
  "Last-Translator: Adam Klotblixt <adam.klotblixt@gmail.com>\n"
  "Language-Team: \n"
- "Language: \n"
  "MIME-Version: 1.0\n"
  "Content-Type: text/plain; charset=UTF-8\n"
  "Content-Transfer-Encoding: 8bit\n"
index 7258826ba1774f5a9fd3e32656bb32a2a761edd3,ad81686d108a9e4ea3603a90250b4a2046383925..8e771a5e239e73044de2456310e754d042d0ff90
@@@ -82,10 -80,10 +82,10 @@@ Writer::Writer (shared_ptr<Film> f
                _sound_asset.reset (
                        new libdcp::SoundAsset (
                                _film->dir (_film->dcp_name()),
-                               N_("audio.mxf"),
+                               _film->dcp_audio_mxf_filename (),
                                _film->dcp_frame_rate (),
 -                              m.dcp_channels (),
 -                              dcp_audio_sample_rate (_film->audio_stream()->sample_rate())
 +                              _film->audio_mapping().dcp_channels (),
 +                              dcp_audio_sample_rate (_film->audio_frame_rate())
                                )
                        );
  
diff --cc src/lib/wscript
index fddebe8e648180a2744631a80ed88b6cd891ed0a,51b103afdc3e639fb357a8e09551551e6f3864b9..e53ac5a840d705381fafa16955a592f9cc68ff1a
@@@ -51,10 -45,9 +51,11 @@@ sources = ""
            timer.cc
            transcode_job.cc
            transcoder.cc
 +          types.cc
+           trimmer.cc
            ui_signaller.cc
            util.cc
 +          video_content.cc
            video_decoder.cc
            video_source.cc
            writer.cc
index ee9f9977cb93ee1fef332fbff0728816e6f91f38,0000000000000000000000000000000000000000..e053da5340d8fc2e45335c610e5b155f3c7cd1d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,578 -1,0 +1,589 @@@
-       if (Config::instance()->language()) {
-               wxLanguageInfo const * li = wxLocale::FindLanguageInfo (std_to_wx (Config::instance()->language().get()));
 +/*
 +    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
 +#include <wx/aboutdlg.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 "lib/film.h"
 +#include "lib/format.h"
 +#include "lib/config.h"
 +#include "lib/filter.h"
 +#include "lib/util.h"
 +#include "lib/scaler.h"
 +#include "lib/exceptions.h"
 +#include "lib/version.h"
 +#include "lib/ui_signaller.h"
 +#include "lib/log.h"
 +
 +using std::cout;
 +using std::string;
 +using std::wstring;
 +using std::stringstream;
 +using std::map;
 +using std::make_pair;
 +using std::exception;
++using std::ofstream;
 +using boost::shared_ptr;
 +
 +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 wxLocale* locale = 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:      
 +      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 ();
 +}
 +
 +enum Sensitivity {
 +      ALWAYS,
 +      NEEDS_FILM
 +};
 +
 +map<wxMenuItem*, Sensitivity> menu_items;
 +      
 +void
 +add_item (wxMenu* menu, wxString text, int id, Sensitivity sens)
 +{
 +      wxMenuItem* item = menu->Append (id, text);
 +      menu_items.insert (make_pair (item, sens));
 +}
 +
 +void
 +set_menu_sensitivity ()
 +{
 +      for (map<wxMenuItem*, Sensitivity>::iterator i = menu_items.begin(); i != menu_items.end(); ++i) {
 +              if (i->second == NEEDS_FILM) {
 +                      i->first->Enable (film != 0);
 +              } else {
 +                      i->first->Enable (true);
 +              }
 +      }
 +}
 +
 +enum {
 +      ID_file_new = 1,
 +      ID_file_open,
 +      ID_file_save,
 +      ID_file_properties,
 +      ID_file_quit,
 +      ID_edit_preferences,
 +      ID_jobs_make_dcp,
 +      ID_jobs_send_dcp_to_tms,
 +      ID_jobs_show_dcp,
 +      ID_jobs_analyse_audio,
 +      ID_help_about
 +};
 +
 +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);
 +      file->AppendSeparator ();
 +      add_item (file, _("&Quit"), ID_file_quit, ALWAYS);
 +
 +      wxMenu* edit = new wxMenu;
 +      add_item (edit, _("&Preferences..."), ID_edit_preferences, ALWAYS);
 +
 +      jobs_menu = new wxMenu;
 +      add_item (jobs_menu, _("&Make DCP"), ID_jobs_make_dcp, NEEDS_FILM);
 +      add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM);
 +      add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM);
 +      jobs_menu->AppendSeparator ();
 +      add_item (jobs_menu, _("&Analyse audio"), ID_jobs_analyse_audio, NEEDS_FILM);
 +
 +      wxMenu* help = new wxMenu;
 +      add_item (help, _("About"), ID_help_about, ALWAYS);
 +
 +      m->Append (file, _("&File"));
 +      m->Append (edit, _("&Edit"));
 +      m->Append (jobs_menu, _("&Jobs"));
 +      m->Append (help, _("&Help"));
 +}
 +
 +bool
 +window_closed (wxCommandEvent &)
 +{
 +      maybe_save_then_delete_film ();
 +      return false;
 +}
 +
 +class Frame : public wxFrame
 +{
 +public:
 +      Frame (wxString const & title)
 +              : wxFrame (NULL, -1, title)
 +      {
 +              wxMenuBar* bar = new wxMenuBar;
 +              setup_menu (bar);
 +              SetMenuBar (bar);
 +
 +              Connect (ID_file_new, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_new));
 +              Connect (ID_file_open, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_open));
 +              Connect (ID_file_save, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_save));
 +              Connect (ID_file_properties, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_properties));
 +              Connect (ID_file_quit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_quit));
 +              Connect (ID_edit_preferences, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::edit_preferences));
 +              Connect (ID_jobs_make_dcp, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_make_dcp));
 +              Connect (ID_jobs_send_dcp_to_tms, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_send_dcp_to_tms));
 +              Connect (ID_jobs_show_dcp, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_show_dcp));
 +              Connect (ID_jobs_analyse_audio, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_analyse_audio));
 +              Connect (ID_help_about, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::help_about));
 +
 +              Connect (wxID_ANY, wxEVT_MENU_OPEN, wxMenuEventHandler (Frame::menu_opened));
 +
 +              wxPanel* panel = new wxPanel (this);
 +              wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
 +              s->Add (panel, 1, wxEXPAND);
 +              SetSizer (s);
 +
 +              film_editor = new FilmEditor (film, panel);
 +              film_viewer = new FilmViewer (film, panel);
 +              JobManagerView* job_manager_view = new JobManagerView (panel);
 +
 +              _top_sizer = new wxBoxSizer (wxHORIZONTAL);
 +              _top_sizer->Add (film_editor, 0, wxALL, 6);
 +              _top_sizer->Add (film_viewer, 1, wxEXPAND | wxALL, 6);
 +
 +              wxBoxSizer* main_sizer = new wxBoxSizer (wxVERTICAL);
 +              main_sizer->Add (_top_sizer, 2, wxEXPAND | wxALL, 6);
 +              main_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6);
 +              panel->SetSizer (main_sizer);
 +
 +              set_menu_sensitivity ();
 +
 +              film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1));
 +              if (film) {
 +                      file_changed (film->directory ());
 +              } else {
 +                      file_changed ("");
 +              }
 +
 +              set_film ();
 +
 +              film_editor->Connect (wxID_ANY, wxEVT_SIZE, wxSizeEventHandler (Frame::film_editor_sized), 0, this);
 +      }
 +
 +private:
 +
 +      void film_editor_sized (wxSizeEvent &)
 +      {
 +              static bool in_layout = false;
 +              if (!in_layout) {
 +                      in_layout = true;
 +                      _top_sizer->Layout ();
 +                      in_layout = false;
 +              }
 +      }
 +
 +      void menu_opened (wxMenuEvent& ev)
 +      {
 +              if (ev.GetMenu() != jobs_menu) {
 +                      return;
 +              }
 +
 +              bool const have_dcp = 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 (wxCommandEvent &)
 +      {
 +              NewFilmDialog* d = new NewFilmDialog (this);
 +              int const r = d->ShowModal ();
 +              
 +              if (r == wxID_OK) {
 +
 +                      if (boost::filesystem::exists (d->get_path())) {
 +                              error_dialog (this, std_to_wx (String::compose (wx_to_std (_("The directory %1 already exists.")), d->get_path().c_str())));
 +                              return;
 +                      }
 +                      
 +                      maybe_save_then_delete_film ();
 +                      film.reset (new Film (d->get_path (), false));
 +                      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 (wxCommandEvent &)
 +      {
 +              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->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 (wxCommandEvent &)
 +      {
 +              film->write_metadata ();
 +      }
 +
 +      void file_properties (wxCommandEvent &)
 +      {
 +              PropertiesDialog* d = new PropertiesDialog (this, film);
 +              d->ShowModal ();
 +              d->Destroy ();
 +      }
 +      
 +      void file_quit (wxCommandEvent &)
 +      {
 +              maybe_save_then_delete_film ();
 +              Close (true);
 +      }
 +
 +      void edit_preferences (wxCommandEvent &)
 +      {
 +              ConfigDialog* d = new ConfigDialog (this);
 +              d->ShowModal ();
 +              d->Destroy ();
 +              Config::instance()->write ();
 +      }
 +
 +      void jobs_make_dcp (wxCommandEvent &)
 +      {
 +              JobWrapper::make_dcp (this, film);
 +      }
 +      
 +      void jobs_send_dcp_to_tms (wxCommandEvent &)
 +      {
 +              film->send_dcp_to_tms ();
 +      }
 +
 +      void jobs_show_dcp (wxCommandEvent &)
 +      {
 +#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) {
 +                      system (string ("nautilus " + film->directory()).c_str ());
 +              } else {
 +                      int r = system ("which konqueror");
 +                      if (WEXITSTATUS (r) == 0) {
 +                              system (string ("konqueror " + film->directory()).c_str ());
 +                      }
 +              }
 +#endif                
 +      }
 +
 +      void jobs_analyse_audio (wxCommandEvent &)
 +      {
 +              film->analyse_audio ();
 +      }
 +      
 +      void help_about (wxCommandEvent &)
 +      {
 +              wxAboutDialogInfo info;
 +              info.SetName (_("DCP-o-matic"));
 +              if (strcmp (dcpomatic_git_commit, "release") == 0) {
 +                      info.SetVersion (std_to_wx (String::compose ("version %1", dcpomatic_version)));
 +              } else {
 +                      info.SetVersion (std_to_wx (String::compose ("version %1 git %2", dcpomatic_version, dcpomatic_git_commit)));
 +              }
 +              info.SetDescription (_("Free, open-source DCP generation from almost anything."));
 +              info.SetCopyright (_("(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"));
 +
 +              wxArrayString authors;
 +              authors.Add (wxT ("Carl Hetherington"));
 +              authors.Add (wxT ("Terrence Meiczinger"));
 +              authors.Add (wxT ("Paul Davis"));
 +              authors.Add (wxT ("Ole Laursen"));
 +              info.SetDevelopers (authors);
 +
 +              wxArrayString translators;
 +              translators.Add (wxT ("Olivier Perriere"));
 +              translators.Add (wxT ("Lilian Lefranc"));
 +              translators.Add (wxT ("Thierry Journet"));
 +              translators.Add (wxT ("Massimiliano Broggi"));
 +              translators.Add (wxT ("Manuel AC"));
 +              translators.Add (wxT ("Adam Klotblixt"));
 +              info.SetTranslators (translators);
 +              
 +              info.SetWebSite (wxT ("http://carlh.net/software/dcpomatic"));
 +              wxAboutBox (info);
 +      }
 +
 +      wxSizer* _top_sizer;
 +};
 +
 +#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
 +
 +void
 +setup_i18n ()
 +{
 +      int language = wxLANGUAGE_DEFAULT;
 +
++      ofstream f ("c:/users/carl hetherington/foo", std::ios::app);
++      f << "Hello.\n";
++
++      boost::optional<string> config_lang = Config::instance()->language ();
++      if (config_lang && !config_lang->empty ()) {
++              f << "Configured language " << config_lang.get() << "\n";
++              wxLanguageInfo const * li = wxLocale::FindLanguageInfo (std_to_wx (config_lang.get ()));
++              f << "LanguageInfo " << li << "\n";
 +              if (li) {
 +                      language = li->Language;
++                      f << "language=" << language << " cf " << wxLANGUAGE_DEFAULT << " " << wxLANGUAGE_ENGLISH << "\n";
 +              }
 +      }
 + 
 +      if (wxLocale::IsAvailable (language)) {
++              f << "Language is available.\n";
 +              locale = new wxLocale (language, wxLOCALE_LOAD_DEFAULT);
 +
 +#ifdef DCPOMATIC_WINDOWS
 +              locale->AddCatalogLookupPathPrefix (std_to_wx (mo_path().string()));
 +#endif                
 +
 +              locale->AddCatalog (wxT ("libdcpomatic-wx"));
 +              locale->AddCatalog (wxT ("dcpomatic"));
 +              
 +              if (!locale->IsOk()) {
++                      f << "Locale is not ok.\n";
 +                      delete locale;
 +                      locale = new wxLocale (wxLANGUAGE_ENGLISH);
 +                      language = wxLANGUAGE_ENGLISH;
 +              }
 +      }
 +
 +      if (locale) {
 +              dcpomatic_setup_i18n (wx_to_std (locale->GetCanonicalName ()));
 +      }
 +}
 +
 +class App : public wxApp
 +{
 +      bool OnInit ()
 +      {
 +              if (!wxApp::OnInit()) {
 +                      return false;
 +              }
 +              
 +#ifdef DCPOMATIC_POSIX                
 +              unsetenv ("UBUNTU_MENUPROXY");
 +#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.
 +              */
 +              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->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, false));
 +                      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->Connect (-1, wxEVT_IDLE, wxIdleEventHandler (App::idle));
 +
 +              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 (wxIdleEvent &)
 +      {
 +              ui_signaller->ui_idle ();
 +      }
 +};
 +
 +IMPLEMENT_APP (App)
Simple merge
index f960abcfa02f616ef289742cfdc4b0affeae54da,a21782a6f9da188530d9e4b472461e8cf363f19e..ad028c930c52510b86036a2988a1a03128222c2f
@@@ -174,8 -176,27 +174,13 @@@ FilmEditor::make_film_panel (
        }
        ++r;
  
 -      video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim method"), wxGBPosition (r, 0)));
++      add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim method"), wxGBPosition (r, 0));
+       _trim_type = new wxChoice (_film_panel, wxID_ANY);
 -      grid->Add (video_control (_trim_type), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
++      grid->Add (_trim_type, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+       ++r;
 -      _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B"));
 -      video_control (_dcp_ab);
 -      grid->Add (_dcp_ab, wxGBPosition (r, 0));
 -      ++r;
 -
 -      /* STILL-only stuff */
 -      {
 -              still_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Duration"), wxGBPosition (r, 0)));
 -              wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
 -              _still_duration = new wxSpinCtrl (_film_panel);
 -              still_control (_still_duration);
 -              s->Add (_still_duration, 1, wxEXPAND);
 -              /// TRANSLATORS: `s' here is an abbreviation for seconds, the unit of time
 -              still_control (add_label_to_sizer (s, _film_panel, _("s")));
 -              grid->Add (s, wxGBPosition (r, 1));
 -      }
 +      _ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B"));
 +      grid->Add (_ab, wxGBPosition (r, 0));
        ++r;
  
        vector<DCPContentType const *> const ct = DCPContentType::all ();
  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_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
-       _trust_content_headers->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_headers_changed), 0, this);
-       _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this);
-       _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this);
-       _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler (FilmEditor::content_activated), 0, this);
-       _content_add->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_add_clicked), 0, this);
-       _content_remove->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_remove_clicked), 0, this);
-       _content_edit->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_edit_clicked), 0, this);
-       _content_earlier->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_earlier_clicked), 0, this);
-       _content_later->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_later_clicked), 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_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
-       _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
-       _dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this);
-       _best_dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this);
-       _ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::ab_toggled), 0, this);
-       _trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
-       _trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_end_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_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this);
-       _j2k_bandwidth->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this);
-       _ffmpeg_subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::ffmpeg_subtitle_stream_changed), 0, this);
-       _ffmpeg_audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::ffmpeg_audio_stream_changed), 0, this);
-       _audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this);
 -      _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_CHOICE_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_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
 -      _dcp_content_type->Connect     (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
 -      _dcp_frame_rate->Connect       (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this);
 -      _best_dcp_frame_rate->Connect  (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this);
 -      _dcp_ab->Connect               (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED,     wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this);
 -      _still_duration->Connect       (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this);
 -      _trim_start->Connect           (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
 -      _trim_end->Connect             (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::trim_end_changed), 0, this);
 -      _trim_type->Connect            (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::trim_type_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_CHOICE_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_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this);
 -      _audio_stream->Connect         (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this);
 -      _audio_gain->Connect           (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this);
++      _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_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::format_changed), 0, this);
++      _trust_content_headers->Connect  (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED,     wxCommandEventHandler (FilmEditor::trust_content_headers_changed), 0, this);
++      _content->Connect                (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_SELECTED,   wxListEventHandler    (FilmEditor::content_selection_changed), 0, this);
++      _content->Connect                (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler    (FilmEditor::content_selection_changed), 0, this);
++      _content->Connect                (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_ACTIVATED,  wxListEventHandler    (FilmEditor::content_activated), 0, this);
++      _content_add->Connect            (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::content_add_clicked), 0, this);
++      _content_remove->Connect         (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::content_remove_clicked), 0, this);
++      _content_edit->Connect           (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::content_edit_clicked), 0, this);
++      _content_earlier->Connect        (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::content_earlier_clicked), 0, this);
++      _content_later->Connect          (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::content_later_clicked), 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_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
++      _dcp_content_type->Connect       (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
++      _dcp_frame_rate->Connect         (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this);
++      _best_dcp_frame_rate->Connect    (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this);
++      _ab->Connect                     (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED,     wxCommandEventHandler (FilmEditor::ab_toggled), 0, this);
++      _trim_start->Connect             (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
++      _trim_end->Connect               (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::trim_end_changed), 0, this);
++      _trim_type->Connect              (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::trim_type_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_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this);
++      _j2k_bandwidth->Connect          (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this);
++      _ffmpeg_subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::ffmpeg_subtitle_stream_changed), 0, this);
++      _ffmpeg_audio_stream->Connect    (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::ffmpeg_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
                );
-       _show_audio->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::show_audio_clicked), 0, this);
-       _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this);
 -      _show_audio->Connect           (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::show_audio_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
 -                      );
 -      }
++      _show_audio->Connect             (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,       wxCommandEventHandler (FilmEditor::show_audio_clicked), 0, this);
++      _audio_delay->Connect            (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this);
  }
  
  void
@@@ -876,9 -899,14 +888,10 @@@ FilmEditor::set_film (shared_ptr<Film> 
        film_changed (Film::SCALER);
        film_changed (Film::TRIM_START);
        film_changed (Film::TRIM_END);
 -      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::AB);
+       film_changed (Film::TRIM_TYPE);
        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);
@@@ -920,7 -946,8 +933,8 @@@ FilmEditor::set_things_sensitive (bool 
        _dcp_frame_rate->Enable (s);
        _trim_start->Enable (s);
        _trim_end->Enable (s);
 -      _dcp_ab->Enable (s);
 +      _ab->Enable (s);
+       _trim_type->Enable (s);
        _colour_lut->Enable (s);
        _j2k_bandwidth->Enable (s);
        _audio_gain->Enable (s);
index 0f3d8eb507a5eabc0c6e316f81fd7b03c4f6924c,e2a4d583641eb9ae23ea5d76afc0262b602be20e..ffffc1e76de853ffb03b045ff2c84d96517bb34e
@@@ -75,8 -65,9 +75,9 @@@ private
        void format_changed (wxCommandEvent &);
        void trim_start_changed (wxCommandEvent &);
        void trim_end_changed (wxCommandEvent &);
+       void trim_type_changed (wxCommandEvent &);
        void dcp_content_type_changed (wxCommandEvent &);
 -      void dcp_ab_toggled (wxCommandEvent &);
 +      void ab_toggled (wxCommandEvent &);
        void scaler_changed (wxCommandEvent &);
        void audio_gain_changed (wxCommandEvent &);
        void audio_gain_calculate_button_clicked (wxCommandEvent &);
  
        wxSpinCtrl* _trim_start;
        wxSpinCtrl* _trim_end;
+       wxChoice* _trim_type;
        /** Selector to generate an A/B comparison DCP */
 -      wxCheckBox* _dcp_ab;
 -
 -      std::list<wxControl*> _video_controls;
 -      std::list<wxControl*> _still_controls;
 +      wxCheckBox* _ab;
  
        std::vector<Format const *> _formats;
  
diff --cc test/test.cc
index 39a921d3682149a88f7485c5cc584b5688512b53,ac7ab85fe5bd2df8a205181dcfe778c722b7553c..a2c0de25048443675eb61dc55c4a6a80c8fdb953
  #include "scaler.h"
  #include "ffmpeg_decoder.h"
  #include "sndfile_decoder.h"
 +#include "dcp_content_type.h"
+ #include "trimmer.h"
  #define BOOST_TEST_DYN_LINK
 -#define BOOST_TEST_MODULE dvdomatic_test
 +#define BOOST_TEST_MODULE dcpomatic_test
  #include <boost/test/unit_test.hpp>
  
  using std::string;