Draw reels in the timeline.
[dcpomatic.git] / src / wx / timeline.cc
index 9c70a001e0bc2709ebfea53cfe91fa02568056a6..e2cc1a249e2be71754ea693db4383edd4e409ee9 100644 (file)
 
 */
 
-#include <list>
-#include <wx/graphics.h>
-#include <boost/weak_ptr.hpp>
-#include "lib/film.h"
-#include "lib/playlist.h"
-#include "lib/image_content.h"
 #include "film_editor.h"
 #include "timeline.h"
 #include "timeline_time_axis_view.h"
+#include "timeline_reels_view.h"
 #include "timeline_video_content_view.h"
 #include "timeline_audio_content_view.h"
 #include "timeline_subtitle_content_view.h"
 #include "content_panel.h"
 #include "wx_util.h"
+#include "lib/film.h"
+#include "lib/playlist.h"
+#include "lib/image_content.h"
+#include "lib/audio_content.h"
+#include "lib/subtitle_content.h"
+#include <wx/graphics.h>
+#include <boost/weak_ptr.hpp>
+#include <boost/foreach.hpp>
+#include <list>
+#include <iostream>
 
 using std::list;
 using std::cout;
@@ -45,7 +50,8 @@ Timeline::Timeline (wxWindow* parent, ContentPanel* cp, shared_ptr<Film> film)
        : wxPanel (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
        , _content_panel (cp)
        , _film (film)
-       , _time_axis_view (new TimelineTimeAxisView (*this, 32))
+       , _time_axis_view (new TimelineTimeAxisView (*this, 64))
+       , _reels_view (new TimelineReelsView (*this, 32))
        , _tracks (0)
        , _left_down (false)
        , _down_view_position (0)
@@ -55,7 +61,7 @@ Timeline::Timeline (wxWindow* parent, ContentPanel* cp, shared_ptr<Film> film)
 {
 #ifndef __WXOSX__
        SetDoubleBuffered (true);
-#endif 
+#endif
 
        Bind (wxEVT_PAINT,      boost::bind (&Timeline::paint,       this));
        Bind (wxEVT_LEFT_DOWN,  boost::bind (&Timeline::left_down,   this, _1));
@@ -64,12 +70,12 @@ Timeline::Timeline (wxWindow* parent, ContentPanel* cp, shared_ptr<Film> film)
        Bind (wxEVT_MOTION,     boost::bind (&Timeline::mouse_moved, this, _1));
        Bind (wxEVT_SIZE,       boost::bind (&Timeline::resized,     this));
 
-       playlist_changed ();
+       film_changed (Film::CONTENT);
 
        SetMinSize (wxSize (640, tracks() * track_height() + 96));
 
-       _playlist_changed_connection = film->playlist()->Changed.connect (bind (&Timeline::playlist_changed, this));
-       _playlist_content_changed_connection = film->playlist()->ContentChanged.connect (bind (&Timeline::playlist_content_changed, this, _2));
+       _film_changed_connection = film->Changed.connect (bind (&Timeline::film_changed, this, _1));
+       _film_content_changed_connection = film->ContentChanged.connect (bind (&Timeline::film_content_changed, this, _2));
 }
 
 void
@@ -90,36 +96,37 @@ Timeline::paint ()
 }
 
 void
-Timeline::playlist_changed ()
+Timeline::film_changed (Film::Property p)
 {
-       ensure_ui_thread ();
-       recreate_views ();
+       if (p == Film::CONTENT) {
+               ensure_ui_thread ();
+               recreate_views ();
+       }
 }
 
 void
 Timeline::recreate_views ()
 {
-       shared_ptr<const Film> fl = _film.lock ();
-       if (!fl) {
+       shared_ptr<const Film> film = _film.lock ();
+       if (!film) {
                return;
        }
 
        _views.clear ();
        _views.push_back (_time_axis_view);
+       _views.push_back (_reels_view);
 
-       ContentList content = fl->playlist()->content ();
-
-       for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
-               if (dynamic_pointer_cast<VideoContent> (*i)) {
-                       _views.push_back (shared_ptr<TimelineView> (new TimelineVideoContentView (*this, *i)));
+       BOOST_FOREACH (shared_ptr<Content> i, film->content ()) {
+               if (dynamic_pointer_cast<VideoContent> (i)) {
+                       _views.push_back (shared_ptr<TimelineView> (new TimelineVideoContentView (*this, i)));
                }
 
-               shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (*i);
-               if (ac && !ac->audio_mapping().mapped_dcp_channels().empty ()) {
-                       _views.push_back (shared_ptr<TimelineView> (new TimelineAudioContentView (*this, *i)));
+               shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (i);
+               if (ac && !ac->audio_mapping().mapped_output_channels().empty ()) {
+                       _views.push_back (shared_ptr<TimelineView> (new TimelineAudioContentView (*this, i)));
                }
 
-               shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (*i);
+               shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (i);
                if (sc && sc->has_subtitles ()) {
                        _views.push_back (shared_ptr<TimelineView> (new TimelineSubtitleContentView (*this, sc)));
                }
@@ -131,7 +138,7 @@ Timeline::recreate_views ()
 }
 
 void
-Timeline::playlist_content_changed (int property)
+Timeline::film_content_changed (int property)
 {
        ensure_ui_thread ();
 
@@ -142,7 +149,7 @@ Timeline::playlist_content_changed (int property)
                        setup_pixels_per_second ();
                }
                Refresh ();
-       } else if (property == AudioContentProperty::AUDIO_MAPPING) {
+       } else if (property == AudioContentProperty::AUDIO_STREAMS) {
                recreate_views ();
        }
 }
@@ -164,6 +171,7 @@ Timeline::assign_tracks ()
                }
 
                shared_ptr<Content> content = cv->content();
+               DCPTimePeriod content_period (content->position(), content->end());
 
                int t = 0;
                while (true) {
@@ -174,21 +182,17 @@ Timeline::assign_tracks ()
                                        ++j;
                                        continue;
                                }
-                               
+
                                shared_ptr<Content> test_content = test->content();
-                                       
+
                                if (test && test->track() && test->track().get() == t) {
-                                       bool const no_overlap =
-                                               (content->position() < test_content->position() && content->end() < test_content->position()) ||
-                                               (content->position() > test_content->end()      && content->end() > test_content->end());
-                                       
-                                       if (!no_overlap) {
+                                       if (content_period.overlaps (DCPTimePeriod(test_content->position(), test_content->end()))) {
                                                /* we have an overlap on track `t' */
                                                ++t;
                                                break;
                                        }
                                }
-                               
+
                                ++j;
                        }
 
@@ -202,7 +206,8 @@ Timeline::assign_tracks ()
                _tracks = max (_tracks, t + 1);
        }
 
-       _time_axis_view->set_y (tracks() * track_height() + 32);
+       _time_axis_view->set_y (tracks() * track_height() + 64);
+       _reels_view->set_y (tracks() * track_height() + 32);
 }
 
 int
@@ -256,11 +261,11 @@ Timeline::left_down (wxMouseEvent& ev)
                if (!cv) {
                        continue;
                }
-               
+
                if (!ev.ShiftDown ()) {
                        cv->set_selected (view == *i);
                }
-               
+
                if (view == *i) {
                        _content_panel->set_selection (cv->content ());
                }
@@ -358,17 +363,17 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
        if (!_down_view) {
                return;
        }
-       
+
        DCPTime new_position = _down_view_position + DCPTime::from_seconds ((p.x - _down_point.x) / pps);
-       
+
        if (_snap) {
 
-               DCPTime const new_end = new_position + _down_view->content()->length_after_trim () - 1;
+               DCPTime const new_end = new_position + _down_view->content()->length_after_trim();
                /* Signed `distance' to nearest thing (i.e. negative is left on the timeline,
                   positive is right).
                */
                optional<DCPTime> nearest_distance;
-               
+
                /* Find the nearest content edge; this is inefficient */
                for (TimelineViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
                        shared_ptr<TimelineContentView> cv = dynamic_pointer_cast<TimelineContentView> (*i);
@@ -377,11 +382,11 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
                        }
 
                        maybe_snap (cv->content()->position(), new_position, nearest_distance);
-                       maybe_snap (cv->content()->position(), new_end + 1, nearest_distance);
-                       maybe_snap (cv->content()->end() + 1, new_position, nearest_distance);
-                       maybe_snap (cv->content()->end() + 1, new_end, nearest_distance);
+                       maybe_snap (cv->content()->position(), new_end, nearest_distance);
+                       maybe_snap (cv->content()->end(), new_position, nearest_distance);
+                       maybe_snap (cv->content()->end(), new_end, nearest_distance);
                }
-               
+
                if (nearest_distance) {
                        /* Snap if it's close; `close' means within a proportion of the time on the timeline */
                        if (nearest_distance.get().abs() < DCPTime::from_seconds ((width() / pps) / 64)) {
@@ -389,13 +394,13 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
                        }
                }
        }
-       
+
        if (new_position < DCPTime ()) {
                new_position = DCPTime ();
        }
 
        _down_view->content()->set_position (new_position);
-       
+
        shared_ptr<Film> film = _film.lock ();
        DCPOMATIC_ASSERT (film);
        film->set_sequence_video (false);
@@ -434,7 +439,7 @@ TimelineContentViewList
 Timeline::selected_views () const
 {
        TimelineContentViewList sel;
-       
+
        for (TimelineViewList::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                shared_ptr<TimelineContentView> cv = dynamic_pointer_cast<TimelineContentView> (*i);
                if (cv && cv->selected()) {
@@ -450,7 +455,7 @@ Timeline::selected_content () const
 {
        ContentList sel;
        TimelineContentViewList views = selected_views ();
-       
+
        for (TimelineContentViewList::const_iterator i = views.begin(); i != views.end(); ++i) {
                sel.push_back ((*i)->content ());
        }