summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-05-06 14:10:14 +0100
committerCarl Hetherington <cth@carlh.net>2013-05-06 14:10:14 +0100
commit454a961e1a03f60cf05040b832c4f8f01b481fa7 (patch)
treec1e50792318179d99f4a0750ec3f4bac2dac6346 /src
parentee191ec1dbea1fda4a93338c5a86e5f53c35efdc (diff)
Add basic timeline window.
Diffstat (limited to 'src')
-rw-r--r--src/lib/audio_content.cc6
-rw-r--r--src/lib/audio_content.h2
-rw-r--r--src/lib/content.h2
-rw-r--r--src/lib/ffmpeg_content.cc6
-rw-r--r--src/lib/ffmpeg_content.h1
-rw-r--r--src/lib/film.cc7
-rw-r--r--src/lib/film.h1
-rw-r--r--src/lib/types.h1
-rw-r--r--src/lib/video_content.cc6
-rw-r--r--src/lib/video_content.h1
-rw-r--r--src/wx/film_editor.cc13
-rw-r--r--src/wx/film_editor.h2
-rw-r--r--src/wx/timeline.cc191
-rw-r--r--src/wx/timeline.h37
-rw-r--r--src/wx/timeline_dialog.cc41
-rw-r--r--src/wx/timeline_dialog.h34
-rw-r--r--src/wx/wscript2
17 files changed, 353 insertions, 0 deletions
diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc
index 9968f4725..dfa48d97e 100644
--- a/src/lib/audio_content.cc
+++ b/src/lib/audio_content.cc
@@ -43,3 +43,9 @@ AudioContent::AudioContent (AudioContent const & o)
{
}
+
+Time
+AudioContent::temporal_length () const
+{
+ return audio_length() / audio_frame_rate();
+}
diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h
index 2362786d9..18107843c 100644
--- a/src/lib/audio_content.h
+++ b/src/lib/audio_content.h
@@ -45,6 +45,8 @@ public:
virtual int audio_channels () const = 0;
virtual ContentAudioFrame audio_length () const = 0;
virtual int audio_frame_rate () const = 0;
+
+ Time temporal_length () const;
};
#endif
diff --git a/src/lib/content.h b/src/lib/content.h
index d39fc9e1a..e1cf41df0 100644
--- a/src/lib/content.h
+++ b/src/lib/content.h
@@ -25,6 +25,7 @@
#include <boost/thread/mutex.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <libxml++/libxml++.h>
+#include "types.h"
namespace cxml {
class Node;
@@ -45,6 +46,7 @@ public:
virtual std::string information () const = 0;
virtual void as_xml (xmlpp::Node *) const;
virtual boost::shared_ptr<Content> clone () const = 0;
+ virtual Time temporal_length () const = 0;
boost::filesystem::path file () const {
boost::mutex::scoped_lock lm (_mutex);
diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc
index 719c4cb53..a61f777c8 100644
--- a/src/lib/ffmpeg_content.cc
+++ b/src/lib/ffmpeg_content.cc
@@ -280,3 +280,9 @@ FFmpegContent::clone () const
{
return shared_ptr<Content> (new FFmpegContent (*this));
}
+
+double
+FFmpegContent::temporal_length () const
+{
+ return video_length() / video_frame_rate();
+}
diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h
index 8bf4d42a5..6d7151498 100644
--- a/src/lib/ffmpeg_content.h
+++ b/src/lib/ffmpeg_content.h
@@ -89,6 +89,7 @@ public:
std::string information () const;
void as_xml (xmlpp::Node *) const;
boost::shared_ptr<Content> clone () const;
+ double temporal_length () const;
/* AudioContent */
int audio_channels () const;
diff --git a/src/lib/film.cc b/src/lib/film.cc
index 2dc97c1b3..a385625e7 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -1075,6 +1075,13 @@ Film::player () const
return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
}
+shared_ptr<Playlist>
+Film::playlist () const
+{
+ boost::mutex::scoped_lock lm (_state_mutex);
+ return _playlist;
+}
+
ContentList
Film::content () const
{
diff --git a/src/lib/film.h b/src/lib/film.h
index 8748e18f5..e2f9b101a 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -103,6 +103,7 @@ public:
bool have_dcp () const;
boost::shared_ptr<Player> player () const;
+ boost::shared_ptr<Playlist> playlist () const;
/* Proxies for some Playlist methods */
diff --git a/src/lib/types.h b/src/lib/types.h
index c2bb9d853..f9e9b2f4b 100644
--- a/src/lib/types.h
+++ b/src/lib/types.h
@@ -29,6 +29,7 @@ class Content;
typedef std::vector<boost::shared_ptr<Content> > ContentList;
typedef int64_t ContentAudioFrame;
typedef int ContentVideoFrame;
+typedef double Time;
/** @struct Crop
* @brief A description of the crop of an image or video.
diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc
index 9fb2b9bce..2af6ba908 100644
--- a/src/lib/video_content.cc
+++ b/src/lib/video_content.cc
@@ -104,3 +104,9 @@ VideoContent::information () const
return s.str ();
}
+
+Time
+VideoContent::temporal_length () const
+{
+ return video_length() / video_frame_rate();
+}
diff --git a/src/lib/video_content.h b/src/lib/video_content.h
index 75e507d4d..b2ec87e2b 100644
--- a/src/lib/video_content.h
+++ b/src/lib/video_content.h
@@ -42,6 +42,7 @@ public:
void as_xml (xmlpp::Node *) const;
virtual std::string information () const;
+ Time temporal_length () const;
ContentVideoFrame video_length () const {
boost::mutex::scoped_lock lm (_mutex);
diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc
index db3e03d78..c50782452 100644
--- a/src/wx/film_editor.cc
+++ b/src/wx/film_editor.cc
@@ -52,6 +52,7 @@
#include "imagemagick_content_dialog.h"
#include "ffmpeg_content_dialog.h"
#include "audio_mapping_view.h"
+#include "timeline_dialog.h"
using std::string;
using std::cout;
@@ -215,6 +216,7 @@ FilmEditor::connect_to_widgets ()
_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);
+ _timeline_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::timeline_clicked), 0, this);
_loop_content->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::loop_content_toggled), 0, this);
_loop_count->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::loop_count_changed), 0, this);
_left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this);
@@ -388,6 +390,9 @@ FilmEditor::make_content_panel ()
font.SetPointSize(font.GetPointSize() - 1);
_playlist_description->SetFont(font);
+ _timeline_button = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
+ _content_sizer->Add (_timeline_button, 0, wxALL, 6);
+
_loop_count->SetRange (2, 1024);
}
@@ -1449,3 +1454,11 @@ FilmEditor::setup_playlist_description ()
_playlist_description->SetLabel (std_to_wx (_film->playlist_description ()));
}
+
+void
+FilmEditor::timeline_clicked (wxCommandEvent &)
+{
+ TimelineDialog* d = new TimelineDialog (this, _film->playlist ());
+ d->ShowModal ();
+ d->Destroy ();
+}
diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h
index baaeb46d7..fddd213b9 100644
--- a/src/wx/film_editor.h
+++ b/src/wx/film_editor.h
@@ -93,6 +93,7 @@ private:
void edit_filters_clicked (wxCommandEvent &);
void loop_content_toggled (wxCommandEvent &);
void loop_count_changed (wxCommandEvent &);
+ void timeline_clicked (wxCommandEvent &);
/* Handle changes to the model */
void film_changed (Film::Property);
@@ -145,6 +146,7 @@ private:
wxTextCtrl* _content_information;
wxCheckBox* _loop_content;
wxSpinCtrl* _loop_count;
+ wxButton* _timeline_button;
wxStaticText* _playlist_description;
wxButton* _edit_dci_button;
wxChoice* _format;
diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc
new file mode 100644
index 000000000..828756362
--- /dev/null
+++ b/src/wx/timeline.cc
@@ -0,0 +1,191 @@
+/*
+ Copyright (C) 2013 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 <list>
+#include <wx/graphics.h>
+#include "timeline.h"
+#include "wx_util.h"
+#include "playlist.h"
+
+using std::list;
+using std::cout;
+using std::max;
+using boost::shared_ptr;
+
+int const Timeline::_track_height = 64;
+
+Timeline::Timeline (wxWindow* parent, shared_ptr<Playlist> pl)
+ : wxPanel (parent)
+ , _playlist (pl)
+{
+ SetDoubleBuffered (true);
+
+ Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (Timeline::paint), 0, this);
+
+ if (pl->audio_from() == Playlist::AUDIO_FFMPEG) {
+ SetMinSize (wxSize (640, _track_height * 2 + 96));
+ } else {
+ SetMinSize (wxSize (640, _track_height * (max (1UL, pl->audio().size()) + 1) + 96));
+ }
+}
+
+template <class T>
+int
+plot_content_list (
+ list<shared_ptr<const T> > content, wxGraphicsContext* gc, int x, int y, double pixels_per_second, int track_height, wxString type, bool consecutive
+ )
+{
+ Time t = 0;
+ for (typename list<shared_ptr<const T> >::iterator i = content.begin(); i != content.end(); ++i) {
+ Time const len = (*i)->temporal_length ();
+ wxGraphicsPath path = gc->CreatePath ();
+ path.MoveToPoint (x + t * pixels_per_second, y);
+ path.AddLineToPoint (x + (t + len) * pixels_per_second, y);
+ path.AddLineToPoint (x + (t + len) * pixels_per_second, y + track_height);
+ path.AddLineToPoint (x + t * pixels_per_second, y + track_height);
+ path.AddLineToPoint (x + t * pixels_per_second, y);
+ gc->StrokePath (path);
+ gc->FillPath (path);
+
+ wxString name = wxString::Format ("%s [%s]", std_to_wx ((*i)->file().filename().string()), type);
+ wxDouble name_width;
+ wxDouble name_height;
+ wxDouble name_descent;
+ wxDouble name_leading;
+ gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading);
+
+ gc->Clip (wxRegion (x + t * pixels_per_second, y, len * pixels_per_second, track_height));
+ gc->DrawText (name, t * pixels_per_second + 12, y + track_height - name_height - 4);
+ gc->ResetClip ();
+
+ if (consecutive) {
+ t += len;
+ } else {
+ y += track_height;
+ }
+ }
+
+ if (consecutive) {
+ y += track_height;
+ }
+
+ return y;
+}
+
+void
+Timeline::paint (wxPaintEvent &)
+{
+ wxPaintDC dc (this);
+
+ shared_ptr<Playlist> pl = _playlist.lock ();
+ if (!pl) {
+ return;
+ }
+
+ wxGraphicsContext* gc = wxGraphicsContext::Create (dc);
+ if (!gc) {
+ return;
+ }
+
+ int const x_offset = 8;
+ int y = 8;
+ int const width = GetSize().GetWidth();
+ double const pixels_per_second = (width - x_offset * 2) / (pl->content_length() / pl->video_frame_rate());
+
+ gc->SetFont (gc->CreateFont (*wxNORMAL_FONT));
+
+ gc->SetPen (*wxBLACK_PEN);
+#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
+ gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 4, wxPENSTYLE_SOLID));
+ gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (149, 121, 232, 255), wxBRUSHSTYLE_SOLID));
+#else
+ gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 4, SOLID));
+ gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (149, 121, 232, 255), SOLID));
+#endif
+ y = plot_content_list (pl->video (), gc, x_offset, y, pixels_per_second, _track_height, _("video"), true);
+
+#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
+ gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (242, 92, 120, 255), wxBRUSHSTYLE_SOLID));
+#else
+ gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (242, 92, 120, 255), SOLID));
+#endif
+ y = plot_content_list (pl->audio (), gc, x_offset, y, pixels_per_second, _track_height, _("audio"), pl->audio_from() == Playlist::AUDIO_FFMPEG);
+
+ /* Time axis */
+
+#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9
+ gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID));
+#else
+ gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, SOLID));
+#endif
+
+ int mark_interval = rint (128 / pixels_per_second);
+ if (mark_interval > 5) {
+ mark_interval -= mark_interval % 5;
+ }
+ if (mark_interval > 10) {
+ mark_interval -= mark_interval % 10;
+ }
+ if (mark_interval > 60) {
+ mark_interval -= mark_interval % 60;
+ }
+ if (mark_interval > 3600) {
+ mark_interval -= mark_interval % 3600;
+ }
+
+ if (mark_interval < 1) {
+ mark_interval = 1;
+ }
+
+ wxGraphicsPath path = gc->CreatePath ();
+ path.MoveToPoint (x_offset, y + 40);
+ path.AddLineToPoint (width, y + 40);
+ gc->StrokePath (path);
+
+ double t = 0;
+ while ((t * pixels_per_second) < width) {
+ wxGraphicsPath path = gc->CreatePath ();
+ path.MoveToPoint (x_offset + t * pixels_per_second, y + 36);
+ path.AddLineToPoint (x_offset + t * pixels_per_second, y + 44);
+ gc->StrokePath (path);
+
+ int tc = t;
+ int const h = tc / 3600;
+ tc -= h * 3600;
+ int const m = tc / 60;
+ tc -= m * 60;
+ int const s = tc;
+
+ wxString str = wxString::Format ("%02d:%02d:%02d", h, m, s);
+ wxDouble str_width;
+ wxDouble str_height;
+ wxDouble str_descent;
+ wxDouble str_leading;
+ gc->GetTextExtent (str, &str_width, &str_height, &str_descent, &str_leading);
+
+ int const tx = x_offset + t * pixels_per_second;
+ if ((tx + str_width) < width) {
+ gc->DrawText (str, x_offset + t * pixels_per_second, y + 60);
+ }
+ t += mark_interval;
+ }
+
+ delete gc;
+}
+
diff --git a/src/wx/timeline.h b/src/wx/timeline.h
new file mode 100644
index 000000000..48aebc637
--- /dev/null
+++ b/src/wx/timeline.h
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2013 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 <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <wx/wx.h>
+
+class Playlist;
+
+class Timeline : public wxPanel
+{
+public:
+ Timeline (wxWindow *, boost::shared_ptr<Playlist>);
+
+private:
+ void paint (wxPaintEvent &);
+
+ static int const _track_height;
+
+ boost::weak_ptr<Playlist> _playlist;
+};
diff --git a/src/wx/timeline_dialog.cc b/src/wx/timeline_dialog.cc
new file mode 100644
index 000000000..5633c29e7
--- /dev/null
+++ b/src/wx/timeline_dialog.cc
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2013 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 <list>
+#include <wx/graphics.h>
+#include "timeline_dialog.h"
+#include "wx_util.h"
+#include "playlist.h"
+
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+
+TimelineDialog::TimelineDialog (wxWindow* parent, shared_ptr<Playlist> pl)
+ : wxDialog (parent, wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+ , _timeline (this, pl)
+{
+ wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+
+ sizer->Add (&_timeline, 1, wxEXPAND | wxALL, 12);
+
+ SetSizer (sizer);
+ sizer->Layout ();
+ sizer->SetSizeHints (this);
+}
diff --git a/src/wx/timeline_dialog.h b/src/wx/timeline_dialog.h
new file mode 100644
index 000000000..e58de5540
--- /dev/null
+++ b/src/wx/timeline_dialog.h
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2013 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 <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <wx/wx.h>
+#include "timeline.h"
+
+class Playlist;
+
+class TimelineDialog : public wxDialog
+{
+public:
+ TimelineDialog (wxWindow *, boost::shared_ptr<Playlist>);
+
+private:
+ Timeline _timeline;
+};
diff --git a/src/wx/wscript b/src/wx/wscript
index 001e8469e..c63ef128d 100644
--- a/src/wx/wscript
+++ b/src/wx/wscript
@@ -22,6 +22,8 @@ sources = """
new_film_dialog.cc
properties_dialog.cc
server_dialog.cc
+ timeline.cc
+ timeline_dialog.cc
wx_util.cc
wx_ui_signaller.cc
"""