PlainText -> PlainTextFile.
[dcpomatic.git] / src / wx / timing_panel.cc
index cad34682373697fcb1d1eab4ffb13fe07f0671f7..93ac0278438bac6ff8932f4e0ecaa094fc123be3 100644 (file)
@@ -1,19 +1,20 @@
 /*
     Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
-    This program is free software; you can redistribute it and/or modify
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 
-    This program is distributed in the hope that it will be useful,
+    DCP-o-matic is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
 #include "film_viewer.h"
 #include "timecode.h"
 #include "content_panel.h"
+#include "move_to_dialog.h"
 #include "lib/content.h"
 #include "lib/image_content.h"
-#include "lib/raw_convert.h"
-#include "lib/subtitle_content.h"
-#include "lib/dcp_subtitle_content.h"
+#include "lib/text_content.h"
+#include "lib/dcp_text_content.h"
 #include "lib/audio_content.h"
-#include "lib/text_subtitle_content.h"
+#include "lib/plain_text_file_content.h"
+#include "lib/video_content.h"
+#include <dcp/locale_convert.h>
 #include <boost/foreach.hpp>
 #include <set>
 #include <iostream>
@@ -39,6 +42,7 @@ using std::set;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
 using boost::optional;
+using dcp::locale_convert;
 
 TimingPanel::TimingPanel (ContentPanel* p, FilmViewer* viewer)
        /* horrid hack for apparent lack of context support with wxWidgets i18n code */
@@ -86,6 +90,9 @@ TimingPanel::TimingPanel (ContentPanel* p, FilmViewer* viewer)
        add_label_to_sizer (grid, this, _("Position"), true);
        _position = new Timecode<DCPTime> (this);
        grid->Add (_position);
+       _move_to_start_of_reel = new wxButton (this, wxID_ANY, _("Move to start of reel"));
+       grid->AddSpacer (0);
+       grid->Add (_move_to_start_of_reel);
        add_label_to_sizer (grid, this, _("Full length"), true);
        _full_length = new Timecode<DCPTime> (this);
        grid->Add (_full_length);
@@ -122,7 +129,7 @@ TimingPanel::TimingPanel (ContentPanel* p, FilmViewer* viewer)
         * http://trac.wxwidgets.org/ticket/13389
         */
 
-       wxString in = _("<i>Only change this if it the content's frame rate has been read incorrectly.</i>");
+       wxString in = _("<i>Only change this if the content's frame rate has been read incorrectly.</i>");
        wxString out;
        int const width = 20;
        int current = 0;
@@ -138,17 +145,25 @@ TimingPanel::TimingPanel (ContentPanel* p, FilmViewer* viewer)
 
        t = new wxStaticText (this, wxID_ANY, wxT (""));
        t->SetLabelMarkup (out);
+#ifdef DCPOMATIC_OSX
+       /* Hack to stop hidden text on some versions of OS X */
+       t->SetMinSize (wxSize (-1, 256));
+#endif
        grid->Add (t, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 6);
 
+       /* Completely speculative fix for #891 */
+       grid->Layout ();
+
        _position->Changed.connect    (boost::bind (&TimingPanel::position_changed, this));
+       _move_to_start_of_reel->Bind  (wxEVT_BUTTON, boost::bind (&TimingPanel::move_to_start_of_reel_clicked, this));
        _full_length->Changed.connect (boost::bind (&TimingPanel::full_length_changed, this));
        _trim_start->Changed.connect  (boost::bind (&TimingPanel::trim_start_changed, this));
-       _trim_start_to_playhead->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&TimingPanel::trim_start_to_playhead_clicked, this));
+       _trim_start_to_playhead->Bind (wxEVT_BUTTON, boost::bind (&TimingPanel::trim_start_to_playhead_clicked, this));
        _trim_end->Changed.connect    (boost::bind (&TimingPanel::trim_end_changed, this));
-       _trim_end_to_playhead->Bind   (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&TimingPanel::trim_end_to_playhead_clicked, this));
+       _trim_end_to_playhead->Bind   (wxEVT_BUTTON, boost::bind (&TimingPanel::trim_end_to_playhead_clicked, this));
        _play_length->Changed.connect (boost::bind (&TimingPanel::play_length_changed, this));
-       _video_frame_rate->Bind       (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TimingPanel::video_frame_rate_changed, this));
-       _set_video_frame_rate->Bind   (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&TimingPanel::set_video_frame_rate, this));
+       _video_frame_rate->Bind       (wxEVT_TEXT, boost::bind (&TimingPanel::video_frame_rate_changed, this));
+       _set_video_frame_rate->Bind   (wxEVT_BUTTON, boost::bind (&TimingPanel::set_video_frame_rate, this));
 
        _viewer->ImageChanged.connect (boost::bind (&TimingPanel::setup_sensitivity, this));
 
@@ -209,10 +224,8 @@ TimingPanel::film_content_changed (int property)
 
        } else if (
                property == ContentProperty::LENGTH ||
-               property == VideoContentProperty::VIDEO_FRAME_RATE ||
-               property == VideoContentProperty::VIDEO_FRAME_TYPE ||
-               property == AudioContentProperty::AUDIO_VIDEO_FRAME_RATE ||
-               property == SubtitleContentProperty::SUBTITLE_VIDEO_FRAME_RATE
+               property == ContentProperty::VIDEO_FRAME_RATE ||
+               property == VideoContentProperty::FRAME_TYPE
                ) {
 
                update_full_length ();
@@ -248,51 +261,38 @@ TimingPanel::film_content_changed (int property)
                property == ContentProperty::LENGTH ||
                property == ContentProperty::TRIM_START ||
                property == ContentProperty::TRIM_END ||
-               property == VideoContentProperty::VIDEO_FRAME_RATE ||
-               property == VideoContentProperty::VIDEO_FRAME_TYPE ||
-               property == AudioContentProperty::AUDIO_VIDEO_FRAME_RATE ||
-               property == SubtitleContentProperty::SUBTITLE_VIDEO_FRAME_RATE
+               property == ContentProperty::VIDEO_FRAME_RATE ||
+               property == VideoContentProperty::FRAME_TYPE
                ) {
 
                update_play_length ();
        }
 
-       if (property == VideoContentProperty::VIDEO_FRAME_RATE || property == SubtitleContentProperty::SUBTITLE_VIDEO_FRAME_RATE) {
+       if (property == ContentProperty::VIDEO_FRAME_RATE) {
                set<double> check_vc;
-               shared_ptr<const VideoContent> vc;
+               shared_ptr<const Content> content;
                int count_ac = 0;
-               shared_ptr<const AudioContent> ac;
                int count_sc = 0;
-               shared_ptr<const SubtitleContent> sc;
                BOOST_FOREACH (shared_ptr<const Content> i, _parent->selected ()) {
-                       shared_ptr<const VideoContent> vt = dynamic_pointer_cast<const VideoContent> (i);
-                       if (vt) {
-                               check_vc.insert (vt->video_frame_rate ());
-                               vc = vt;
+                       if (i->video && i->video_frame_rate()) {
+                               check_vc.insert (i->video_frame_rate().get());
+                               content = i;
                        }
-                       shared_ptr<const AudioContent> at = dynamic_pointer_cast<const AudioContent> (i);
-                       if (at) {
+                       if (i->audio && i->video_frame_rate()) {
                                ++count_ac;
-                               ac = at;
+                               content = i;
                        }
-                       shared_ptr<const SubtitleContent> st = dynamic_pointer_cast<const SubtitleContent> (i);
-                       if (st) {
+                       if (i->subtitle && i->video_frame_rate()) {
                                ++count_sc;
-                               sc = st;
+                               content = i;
                        }
 
                }
 
-               bool const single_frame_image_content = vc && dynamic_pointer_cast<const ImageContent> (vc) && vc->number_of_paths() == 1;
+               bool const single_frame_image_content = content && dynamic_pointer_cast<const ImageContent> (content) && content->number_of_paths() == 1;
 
                if ((check_vc.size() == 1 || count_ac == 1 || count_sc == 1) && !single_frame_image_content) {
-                       if (vc) {
-                               checked_set (_video_frame_rate, raw_convert<string> (vc->video_frame_rate (), 5));
-                       } else if (ac) {
-                               checked_set (_video_frame_rate, raw_convert<string> (ac->audio_video_frame_rate (), 5));
-                       } else if (sc) {
-                               checked_set (_video_frame_rate, raw_convert<string> (sc->subtitle_video_frame_rate (), 5));
-                       }
+                       checked_set (_video_frame_rate, locale_convert<string> (content->video_frame_rate().get(), 5));
                        _video_frame_rate->Enable (true);
                } else {
                        checked_set (_video_frame_rate, wxT (""));
@@ -317,19 +317,21 @@ TimingPanel::film_content_changed (int property)
 void
 TimingPanel::position_changed ()
 {
+       DCPTime const pos = _position->get (_parent->film()->video_frame_rate ());
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
-               i->set_position (_position->get (_parent->film()->video_frame_rate ()));
+               i->set_position (pos);
        }
 }
 
 void
 TimingPanel::full_length_changed ()
 {
+       int const vfr = _parent->film()->video_frame_rate ();
+       Frame const len = _full_length->get (vfr).frames_round (vfr);
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
                shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (i);
                if (ic && ic->still ()) {
-                       int const vfr = _parent->film()->video_frame_rate ();
-                       ic->set_video_length (_full_length->get (vfr).frames_round (vfr));
+                       ic->video->set_length (len);
                }
        }
 }
@@ -344,6 +346,9 @@ TimingPanel::trim_start_changed ()
        shared_ptr<Content> ref;
        optional<FrameRateChange> ref_frc;
        optional<DCPTime> ref_ph;
+
+       ContentTime const trim = _trim_start->get (_parent->film()->video_frame_rate ());
+
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
                if (i->position() <= ph && ph < i->end()) {
                        /* The playhead is in i.  Use it as a reference to work out
@@ -355,7 +360,7 @@ TimingPanel::trim_start_changed ()
                        ref_ph = ph - i->position() + DCPTime (i->trim_start(), ref_frc.get());
                }
 
-               i->set_trim_start (_trim_start->get (_parent->film()->video_frame_rate ()));
+               i->set_trim_start (trim);
        }
 
        if (ref) {
@@ -370,8 +375,9 @@ TimingPanel::trim_end_changed ()
 {
        _viewer->set_coalesce_player_changes (true);
 
+       ContentTime const trim = _trim_end->get (_parent->film()->video_frame_rate ());
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
-               i->set_trim_end (_trim_end->get (_parent->film()->video_frame_rate ()));
+               i->set_trim_end (trim);
        }
 
        /* XXX: maybe playhead-off-the-end-of-the-film should be handled elsewhere */
@@ -385,11 +391,11 @@ TimingPanel::trim_end_changed ()
 void
 TimingPanel::play_length_changed ()
 {
+       DCPTime const play_length = _play_length->get (_parent->film()->video_frame_rate());
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
                FrameRateChange const frc = _parent->film()->active_frame_rate_change (i->position ());
                i->set_trim_end (
-                       ContentTime (i->full_length() - _play_length->get (_parent->film()->video_frame_rate()), frc)
-                       - i->trim_start ()
+                       ContentTime (max(DCPTime(), i->full_length() - play_length), frc) - i->trim_start ()
                        );
        }
 }
@@ -403,24 +409,19 @@ TimingPanel::video_frame_rate_changed ()
 void
 TimingPanel::set_video_frame_rate ()
 {
+       optional<double> fr;
+       if (_video_frame_rate->GetValue() != wxT("")) {
+               fr = locale_convert<double> (wx_to_std (_video_frame_rate->GetValue ()));
+       }
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
-               shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (i);
-               shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (i);
-               shared_ptr<DCPSubtitleContent> dsc = dynamic_pointer_cast<DCPSubtitleContent> (i);
-               shared_ptr<TextSubtitleContent> tsc = dynamic_pointer_cast<TextSubtitleContent> (i);
-               double const fr = raw_convert<double> (wx_to_std (_video_frame_rate->GetValue ()));
-               if (vc) {
-                       vc->set_video_frame_rate (fr);
-               } else if (ac) {
-                       /* Audio but not video, i.e. SndfileContent */
-                       ac->set_audio_video_frame_rate (fr);
-               } else if (dsc) {
-                       dsc->set_subtitle_video_frame_rate (fr);
-               } else if (tsc) {
-                       tsc->set_subtitle_video_frame_rate (fr);
+               if (fr) {
+                       i->set_video_frame_rate (*fr);
+               } else {
+                       i->unset_video_frame_rate ();
                }
-               _set_video_frame_rate->Enable (false);
        }
+
+       _set_video_frame_rate->Enable (false);
 }
 
 void
@@ -432,9 +433,7 @@ TimingPanel::content_selection_changed ()
        film_content_changed (ContentProperty::LENGTH);
        film_content_changed (ContentProperty::TRIM_START);
        film_content_changed (ContentProperty::TRIM_END);
-       film_content_changed (VideoContentProperty::VIDEO_FRAME_RATE);
-       film_content_changed (SubtitleContentProperty::SUBTITLE_VIDEO_FRAME_RATE);
-       film_content_changed (AudioContentProperty::AUDIO_VIDEO_FRAME_RATE);
+       film_content_changed (ContentProperty::VIDEO_FRAME_RATE);
 }
 
 void
@@ -449,14 +448,15 @@ TimingPanel::film_changed (Film::Property p)
 void
 TimingPanel::trim_start_to_playhead_clicked ()
 {
-       DCPTime const ph = _viewer->position ();
+       shared_ptr<const Film> film = _parent->film ();
+       DCPTime const ph = _viewer->position().floor (film->video_frame_rate ());
        optional<DCPTime> new_ph;
 
        _viewer->set_coalesce_player_changes (true);
 
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
                if (i->position() < ph && ph < i->end ()) {
-                       FrameRateChange const frc = _parent->film()->active_frame_rate_change (i->position ());
+                       FrameRateChange const frc = film->active_frame_rate_change (i->position ());
                        i->set_trim_start (i->trim_start() + ContentTime (ph - i->position (), frc));
                        new_ph = i->position ();
                }
@@ -472,10 +472,11 @@ TimingPanel::trim_start_to_playhead_clicked ()
 void
 TimingPanel::trim_end_to_playhead_clicked ()
 {
-       DCPTime const ph = _viewer->position ();
+       shared_ptr<const Film> film = _parent->film ();
+       DCPTime const ph = _viewer->position().floor (film->video_frame_rate ());
        BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
                if (i->position() < ph && ph < i->end ()) {
-                       FrameRateChange const frc = _parent->film()->active_frame_rate_change (i->position ());
+                       FrameRateChange const frc = film->active_frame_rate_change (i->position ());
                        i->set_trim_end (ContentTime (i->position() + i->full_length() - ph - DCPTime::from_frames (1, frc.dcp), frc) - i->trim_start());
                }
        }
@@ -487,6 +488,7 @@ TimingPanel::setup_sensitivity ()
        bool const e = !_parent->selected().empty ();
 
        _position->Enable (e);
+       _move_to_start_of_reel->Enable (e);
        _full_length->Enable (e);
        _trim_start->Enable (e);
        _trim_end->Enable (e);
@@ -504,3 +506,30 @@ TimingPanel::setup_sensitivity ()
        _trim_start_to_playhead->Enable (any_over_ph);
        _trim_end_to_playhead->Enable (any_over_ph);
 }
+
+void
+TimingPanel::move_to_start_of_reel_clicked ()
+{
+       /* Find common position of all selected content, if it exists */
+
+       optional<DCPTime> position;
+       BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
+               if (!position) {
+                       position = i->position();
+               } else {
+                       if (position.get() != i->position()) {
+                               position.reset ();
+                               break;
+                       }
+               }
+       }
+
+       MoveToDialog* d = new MoveToDialog (this, position, _parent->film());
+
+       if (d->ShowModal() == wxID_OK) {
+               BOOST_FOREACH (shared_ptr<Content> i, _parent->selected ()) {
+                       i->set_position (d->position ());
+               }
+       }
+       d->Destroy ();
+}