Make timeline non-modal.
[dcpomatic.git] / src / wx / timeline.cc
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <list>
21 #include <wx/graphics.h>
22 #include "timeline.h"
23 #include "wx_util.h"
24 #include "playlist.h"
25
26 using std::list;
27 using std::cout;
28 using std::max;
29 using boost::shared_ptr;
30 using boost::bind;
31
32 int const Timeline::_track_height = 64;
33
34 Timeline::Timeline (wxWindow* parent, shared_ptr<Playlist> pl)
35         : wxPanel (parent)
36         , _playlist (pl)
37 {
38         SetDoubleBuffered (true);
39         
40         Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (Timeline::paint), 0, this);
41
42         if (pl->audio_from() == Playlist::AUDIO_FFMPEG) {
43                 SetMinSize (wxSize (640, _track_height * 2 + 96));
44         } else {
45                 SetMinSize (wxSize (640, _track_height * (max (1UL, pl->audio().size()) + 1) + 96));
46         }
47
48         pl->Changed.connect (bind (&Timeline::playlist_changed, this));
49         pl->ContentChanged.connect (bind (&Timeline::playlist_changed, this));
50 }
51
52 template <class T>
53 int
54 plot_content_list (
55         list<shared_ptr<const T> > content, wxGraphicsContext* gc, int x, int y, double pixels_per_second, int track_height, wxString type, bool consecutive
56         )
57 {
58         Time t = 0;
59         for (typename list<shared_ptr<const T> >::iterator i = content.begin(); i != content.end(); ++i) {
60                 Time const len = (*i)->temporal_length ();
61                 wxGraphicsPath path = gc->CreatePath ();
62                 path.MoveToPoint (x + t * pixels_per_second, y);
63                 path.AddLineToPoint (x + (t + len) * pixels_per_second, y);
64                 path.AddLineToPoint (x + (t + len) * pixels_per_second, y + track_height);
65                 path.AddLineToPoint (x + t * pixels_per_second, y + track_height);
66                 path.AddLineToPoint (x + t * pixels_per_second, y);
67                 gc->StrokePath (path);
68                 gc->FillPath (path);
69
70                 wxString name = wxString::Format ("%s [%s]", std_to_wx ((*i)->file().filename().string()), type);
71                 wxDouble name_width;
72                 wxDouble name_height;
73                 wxDouble name_descent;
74                 wxDouble name_leading;
75                 gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading);
76                 
77                 gc->Clip (wxRegion (x + t * pixels_per_second, y, len * pixels_per_second, track_height));
78                 gc->DrawText (name, t * pixels_per_second + 12, y + track_height - name_height - 4);
79                 gc->ResetClip ();
80
81                 if (consecutive) {
82                         t += len;
83                 } else {
84                         y += track_height;
85                 }
86         }
87
88         if (consecutive) {
89                 y += track_height;
90         }
91
92         return y;
93 }
94
95 void
96 Timeline::paint (wxPaintEvent &)
97 {
98         wxPaintDC dc (this);
99
100         shared_ptr<Playlist> pl = _playlist.lock ();
101         if (!pl) {
102                 return;
103         }
104
105         wxGraphicsContext* gc = wxGraphicsContext::Create (dc);
106         if (!gc) {
107                 return;
108         }
109
110         int const x_offset = 8;
111         int y = 8;
112         int const width = GetSize().GetWidth();
113         double const pixels_per_second = (width - x_offset * 2) / (pl->content_length() / pl->video_frame_rate());
114
115         gc->SetFont (gc->CreateFont (*wxNORMAL_FONT));
116
117         gc->SetPen (*wxBLACK_PEN);
118 #if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
119         gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 4, wxPENSTYLE_SOLID));
120         gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (149, 121, 232, 255), wxBRUSHSTYLE_SOLID));
121 #else                   
122         gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 4, SOLID));
123         gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (149, 121, 232, 255), SOLID));
124 #endif
125         y = plot_content_list (pl->video (), gc, x_offset, y, pixels_per_second, _track_height, _("video"), true);
126         
127 #if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
128         gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (242, 92, 120, 255), wxBRUSHSTYLE_SOLID));
129 #else                   
130         gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (242, 92, 120, 255), SOLID));
131 #endif
132         y = plot_content_list (pl->audio (), gc, x_offset, y, pixels_per_second, _track_height, _("audio"), pl->audio_from() == Playlist::AUDIO_FFMPEG);
133
134         /* Time axis */
135
136 #if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
137         gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID));
138 #else               
139         gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, SOLID));
140 #endif              
141                     
142         int mark_interval = rint (128 / pixels_per_second);
143         if (mark_interval > 5) {
144                 mark_interval -= mark_interval % 5;
145         }
146         if (mark_interval > 10) {
147                 mark_interval -= mark_interval % 10;
148         }
149         if (mark_interval > 60) {
150                 mark_interval -= mark_interval % 60;
151         }
152         if (mark_interval > 3600) {
153                 mark_interval -= mark_interval % 3600;
154         }
155
156         if (mark_interval < 1) {
157                 mark_interval = 1;
158         }
159
160         wxGraphicsPath path = gc->CreatePath ();
161         path.MoveToPoint (x_offset, y + 40);
162         path.AddLineToPoint (width, y + 40);
163         gc->StrokePath (path);
164
165         double t = 0;
166         while ((t * pixels_per_second) < width) {
167                 wxGraphicsPath path = gc->CreatePath ();
168                 path.MoveToPoint (x_offset + t * pixels_per_second, y + 36);
169                 path.AddLineToPoint (x_offset + t * pixels_per_second, y + 44);
170                 gc->StrokePath (path);
171
172                 int tc = t;
173                 int const h = tc / 3600;
174                 tc -= h * 3600;
175                 int const m = tc / 60;
176                 tc -= m * 60;
177                 int const s = tc;
178
179                 wxString str = wxString::Format ("%02d:%02d:%02d", h, m, s);
180                 wxDouble str_width;
181                 wxDouble str_height;
182                 wxDouble str_descent;
183                 wxDouble str_leading;
184                 gc->GetTextExtent (str, &str_width, &str_height, &str_descent, &str_leading);
185
186                 int const tx = x_offset + t * pixels_per_second;
187                 if ((tx + str_width) < width) {
188                         gc->DrawText (str, x_offset + t * pixels_per_second, y + 60);
189                 }
190                 t += mark_interval;
191         }
192
193         delete gc;
194 }
195
196 void
197 Timeline::playlist_changed ()
198 {
199         Refresh ();
200 }