diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-05-06 14:10:14 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-05-06 14:10:14 +0100 |
| commit | 454a961e1a03f60cf05040b832c4f8f01b481fa7 (patch) | |
| tree | c1e50792318179d99f4a0750ec3f4bac2dac6346 /src | |
| parent | ee191ec1dbea1fda4a93338c5a86e5f53c35efdc (diff) | |
Add basic timeline window.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/audio_content.cc | 6 | ||||
| -rw-r--r-- | src/lib/audio_content.h | 2 | ||||
| -rw-r--r-- | src/lib/content.h | 2 | ||||
| -rw-r--r-- | src/lib/ffmpeg_content.cc | 6 | ||||
| -rw-r--r-- | src/lib/ffmpeg_content.h | 1 | ||||
| -rw-r--r-- | src/lib/film.cc | 7 | ||||
| -rw-r--r-- | src/lib/film.h | 1 | ||||
| -rw-r--r-- | src/lib/types.h | 1 | ||||
| -rw-r--r-- | src/lib/video_content.cc | 6 | ||||
| -rw-r--r-- | src/lib/video_content.h | 1 | ||||
| -rw-r--r-- | src/wx/film_editor.cc | 13 | ||||
| -rw-r--r-- | src/wx/film_editor.h | 2 | ||||
| -rw-r--r-- | src/wx/timeline.cc | 191 | ||||
| -rw-r--r-- | src/wx/timeline.h | 37 | ||||
| -rw-r--r-- | src/wx/timeline_dialog.cc | 41 | ||||
| -rw-r--r-- | src/wx/timeline_dialog.h | 34 | ||||
| -rw-r--r-- | src/wx/wscript | 2 |
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 """ |
