Basic if sketchy support for image sequences.
authorCarl Hetherington <cth@carlh.net>
Thu, 25 Jul 2013 23:20:21 +0000 (00:20 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 25 Jul 2013 23:20:21 +0000 (00:20 +0100)
17 files changed:
src/lib/content_factory.cc
src/lib/moving_image.h [new file with mode: 0644]
src/lib/moving_image_content.cc [new file with mode: 0644]
src/lib/moving_image_content.h [new file with mode: 0644]
src/lib/moving_image_decoder.cc [new file with mode: 0644]
src/lib/moving_image_decoder.h [new file with mode: 0644]
src/lib/moving_image_examiner.cc [new file with mode: 0644]
src/lib/moving_image_examiner.h [new file with mode: 0644]
src/lib/player.cc
src/lib/still_image.h
src/lib/still_image_content.cc
src/lib/still_image_content.h
src/lib/util.cc
src/lib/util.h
src/lib/wscript
src/wx/film_editor.cc
src/wx/film_editor.h

index bf447641d817165319e6c7fcd06e29ab6305579a..6ed01f174acb6e0068bdf9ca88d368a2de06455c 100644 (file)
@@ -20,6 +20,7 @@
 #include <libcxml/cxml.h>
 #include "ffmpeg_content.h"
 #include "still_image_content.h"
+#include "moving_image_content.h"
 #include "sndfile_content.h"
 
 using std::string;
@@ -36,6 +37,8 @@ content_factory (shared_ptr<const Film> film, shared_ptr<cxml::Node> node)
                content.reset (new FFmpegContent (film, node));
        } else if (type == "StillImage") {
                content.reset (new StillImageContent (film, node));
+       } else if (type == "MovingImage") {
+               content.reset (new MovingImageContent (film, node));
        } else if (type == "Sndfile") {
                content.reset (new SndfileContent (film, node));
        }
diff --git a/src/lib/moving_image.h b/src/lib/moving_image.h
new file mode 100644 (file)
index 0000000..a81403d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+    Copyright (C) 2012 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.
+
+*/
+
+class MovingImageContent;
+
+class MovingImage
+{
+public:
+       MovingImage (boost::shared_ptr<const MovingImageContent> c)
+               : _moving_image_content (c)
+       {}
+
+       boost::shared_ptr<const MovingImageContent> content () const {
+               return _moving_image_content;
+       }
+
+protected:
+       boost::shared_ptr<const MovingImageContent> _moving_image_content;
+};
diff --git a/src/lib/moving_image_content.cc b/src/lib/moving_image_content.cc
new file mode 100644 (file)
index 0000000..30030d9
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+    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 <libcxml/cxml.h>
+#include "moving_image_content.h"
+#include "moving_image_examiner.h"
+#include "config.h"
+#include "compose.hpp"
+#include "film.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using std::list;
+using std::stringstream;
+using std::vector;
+using boost::shared_ptr;
+
+MovingImageContent::MovingImageContent (shared_ptr<const Film> f, boost::filesystem::path p)
+       : Content (f, p)
+       , VideoContent (f, p)
+{
+
+}
+
+MovingImageContent::MovingImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+       : Content (f, node)
+       , VideoContent (f, node)
+{
+       list<shared_ptr<cxml::Node> > c = node->node_children ("File");
+       for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
+               _files.push_back ((*i)->content ());
+       }
+}
+
+string
+MovingImageContent::summary () const
+{
+       /* Get the string() here so that the name does not have quotes around it */
+       return String::compose (_("%1 [moving images]"), path().filename().string());
+}
+
+string
+MovingImageContent::technical_summary () const
+{
+       return Content::technical_summary() + " - "
+               + VideoContent::technical_summary() + " - "
+               + "moving";
+}
+
+void
+MovingImageContent::as_xml (xmlpp::Node* node) const
+{
+       node->add_child("Type")->add_child_text ("MovingImage");
+       Content::as_xml (node);
+       VideoContent::as_xml (node);
+
+       for (vector<boost::filesystem::path>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+               node->add_child("File")->add_child_text (i->filename().string());
+       }
+}
+
+void
+MovingImageContent::examine (shared_ptr<Job> job)
+{
+       Content::examine (job);
+
+       shared_ptr<const Film> film = _film.lock ();
+       assert (film);
+       
+       shared_ptr<MovingImageExaminer> examiner (new MovingImageExaminer (film, shared_from_this(), job));
+
+       take_from_video_examiner (examiner);
+
+       _video_length = examiner->files().size ();
+       _files = examiner->files ();
+}
+
+Time
+MovingImageContent::length () const
+{
+       shared_ptr<const Film> film = _film.lock ();
+       assert (film);
+
+       FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
+       return video_length() * frc.factor() * TIME_HZ / video_frame_rate();
+}
+
+string
+MovingImageContent::identifier () const
+{
+       stringstream s;
+       s << VideoContent::identifier ();
+       s << "_" << video_length();
+       return s.str ();
+}
diff --git a/src/lib/moving_image_content.h b/src/lib/moving_image_content.h
new file mode 100644 (file)
index 0000000..2b3c48d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    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.
+
+*/
+
+#ifndef DCPOMATIC_MOVING_IMAGE_CONTENT_H
+#define DCPOMATIC_MOVING_IMAGE_CONTENT_H
+
+#include <boost/enable_shared_from_this.hpp>
+#include "video_content.h"
+
+namespace cxml {
+       class Node;
+}
+
+/** A directory of image files which are to be presented as a movie */
+class MovingImageContent : public VideoContent
+{
+public:
+       MovingImageContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+       MovingImageContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+
+       boost::shared_ptr<MovingImageContent> shared_from_this () {
+               return boost::dynamic_pointer_cast<MovingImageContent> (Content::shared_from_this ());
+       };
+       
+       void examine (boost::shared_ptr<Job>);
+       std::string summary () const;
+       std::string technical_summary () const;
+       void as_xml (xmlpp::Node *) const;
+       Time length () const;
+
+       std::string identifier () const;
+
+       std::vector<boost::filesystem::path> const & files () const {
+               return _files;
+       }
+       
+private:
+       std::vector<boost::filesystem::path> _files;
+};
+
+#endif
diff --git a/src/lib/moving_image_decoder.cc b/src/lib/moving_image_decoder.cc
new file mode 100644 (file)
index 0000000..0960960
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    Copyright (C) 2012-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 <iostream>
+#include <boost/filesystem.hpp>
+#include <Magick++.h>
+#include "moving_image_content.h"
+#include "moving_image_decoder.h"
+#include "image.h"
+#include "film.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using std::cout;
+using boost::shared_ptr;
+using libdcp::Size;
+
+MovingImageDecoder::MovingImageDecoder (shared_ptr<const Film> f, shared_ptr<const MovingImageContent> c)
+       : Decoder (f)
+       , VideoDecoder (f, c)
+       , MovingImage (c)
+{
+
+}
+
+void
+MovingImageDecoder::pass ()
+{
+       if (_video_position >= _moving_image_content->video_length ()) {
+               return;
+       }
+
+       boost::filesystem::path path = _moving_image_content->path ();
+       path /= _moving_image_content->files()[_video_position];
+
+       Magick::Image* magick_image = new Magick::Image (path.string());
+       libdcp::Size size (magick_image->columns(), magick_image->rows());
+
+       shared_ptr<Image> image (new Image (PIX_FMT_RGB24, size, false));
+
+       using namespace MagickCore;
+       
+       uint8_t* p = image->data()[0];
+       for (int y = 0; y < size.height; ++y) {
+               for (int x = 0; x < size.width; ++x) {
+                       Magick::Color c = magick_image->pixelColor (x, y);
+                       *p++ = c.redQuantum() * 255 / QuantumRange;
+                       *p++ = c.greenQuantum() * 255 / QuantumRange;
+                       *p++ = c.blueQuantum() * 255 / QuantumRange;
+               }
+       }
+
+       delete magick_image;
+
+       video (image, false, _video_position);
+}
+
+void
+MovingImageDecoder::seek (VideoContent::Frame frame, bool)
+{
+       _video_position = frame;
+}
+
+bool
+MovingImageDecoder::done () const
+{
+       return _video_position >= _moving_image_content->video_length ();
+}
diff --git a/src/lib/moving_image_decoder.h b/src/lib/moving_image_decoder.h
new file mode 100644 (file)
index 0000000..5cc8b32
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    Copyright (C) 2012-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 "video_decoder.h"
+#include "moving_image.h"
+
+namespace Magick {
+       class Image;
+}
+
+class MovingImageContent;
+
+class MovingImageDecoder : public VideoDecoder, public MovingImage
+{
+public:
+       MovingImageDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const MovingImageContent>);
+
+       /* Decoder */
+
+       void pass ();
+       void seek (VideoContent::Frame, bool);
+       bool done () const;
+};
+
diff --git a/src/lib/moving_image_examiner.cc b/src/lib/moving_image_examiner.cc
new file mode 100644 (file)
index 0000000..077545a
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+    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 <iostream>
+#include <boost/lexical_cast.hpp>
+#include <Magick++.h>
+#include "moving_image_content.h"
+#include "moving_image_examiner.h"
+#include "film.h"
+#include "job.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using std::cout;
+using std::list;
+using std::sort;
+using boost::shared_ptr;
+using boost::lexical_cast;
+
+MovingImageExaminer::MovingImageExaminer (shared_ptr<const Film> film, shared_ptr<const MovingImageContent> content, shared_ptr<Job> job)
+       : MovingImage (content)
+       , _film (film)
+       , _video_length (0)
+{
+       list<unsigned int> frames;
+       unsigned int files = 0;
+       
+       for (boost::filesystem::directory_iterator i(content->path()); i != boost::filesystem::directory_iterator(); ++i) {
+               if (boost::filesystem::is_regular_file (i->path ())) {
+                       ++files;
+               }
+       }
+
+       int j = 0;
+       for (boost::filesystem::directory_iterator i(content->path()); i != boost::filesystem::directory_iterator(); ++i) {
+               if (!boost::filesystem::is_regular_file (i->path ())) {
+                       continue;
+               }
+
+               if (valid_image_file (i->path ())) {
+                       int n = lexical_cast<int> (i->path().stem().string());
+                       frames.push_back (n);
+                       _files.push_back (i->path().filename ());
+
+                       if (!_video_size) {
+                               using namespace MagickCore;
+                               Magick::Image* image = new Magick::Image (i->path().string());
+                               _video_size = libdcp::Size (image->columns(), image->rows());
+                               delete image;
+                       }
+               }
+
+               job->set_progress (float (j) / files);
+               ++j;
+       }
+
+       frames.sort ();
+       sort (_files.begin(), _files.end ());
+       
+       if (frames.size() < 2) {
+               throw StringError (String::compose (_("only %1 file(s) found in moving image directory"), frames.size ()));
+       }
+
+       if (frames.front() != 0 && frames.front() != 1) {
+               throw StringError (String::compose (_("first frame in moving image directory is number %1"), frames.front ()));
+       }
+
+       if (frames.back() != frames.size() && frames.back() != (frames.size() - 1)) {
+               throw StringError (String::compose (_("there are %1 images in the directory but the last one is number %2"), frames.size(), frames.back ()));
+       }
+
+       _video_length = frames.size ();
+}
+
+libdcp::Size
+MovingImageExaminer::video_size () const
+{
+       return _video_size.get ();
+}
+
+int
+MovingImageExaminer::video_length () const
+{
+       cout << "ex video length is " << _video_length << "\n";
+       return _video_length;
+}
+
+float
+MovingImageExaminer::video_frame_rate () const
+{
+       return 24;
+}
+
diff --git a/src/lib/moving_image_examiner.h b/src/lib/moving_image_examiner.h
new file mode 100644 (file)
index 0000000..db6845e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    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 "moving_image.h"
+#include "video_examiner.h"
+
+namespace Magick {
+       class Image;
+}
+
+class MovingImageContent;
+
+class MovingImageExaminer : public MovingImage, public VideoExaminer
+{
+public:
+       MovingImageExaminer (boost::shared_ptr<const Film>, boost::shared_ptr<const MovingImageContent>, boost::shared_ptr<Job>);
+
+       float video_frame_rate () const;
+       libdcp::Size video_size () const;
+       VideoContent::Frame video_length () const;
+
+       std::vector<boost::filesystem::path> const & files () const {
+               return _files;
+       }
+
+private:
+       boost::weak_ptr<const Film> _film;
+       boost::optional<libdcp::Size> _video_size;
+       VideoContent::Frame _video_length;
+       std::vector<boost::filesystem::path> _files;
+};
index 620efbd0dc30cb17bcde648f75dadfbf71fae5c1..647095793b9ac7e2874c8e3169d28f2de376c4c6 100644 (file)
@@ -24,6 +24,8 @@
 #include "ffmpeg_content.h"
 #include "still_image_decoder.h"
 #include "still_image_content.h"
+#include "moving_image_decoder.h"
+#include "moving_image_content.h"
 #include "sndfile_decoder.h"
 #include "sndfile_content.h"
 #include "subtitle_content.h"
@@ -462,6 +464,18 @@ Player::setup_pieces ()
                        piece->decoder = id;
                }
 
+               shared_ptr<const MovingImageContent> mc = dynamic_pointer_cast<const MovingImageContent> (*i);
+               if (mc) {
+                       shared_ptr<MovingImageDecoder> md;
+
+                       if (!md) {
+                               md.reset (new MovingImageDecoder (_film, mc));
+                               md->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4));
+                       }
+
+                       piece->decoder = md;
+               }
+
                shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
                if (sc) {
                        shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
index b60069419b9d5faab58c63e8e323b0f458b86cc7..366d693319c8edfe1b153fafeae7e9142da22fed 100644 (file)
@@ -17,6 +17,9 @@
 
 */
 
+#ifndef DCPOMATIC_STILL_IMAGE_H
+#define DCPOMATIC_STILL_IMAGE_H
+
 class StillImageContent;
 
 class StillImage
@@ -33,3 +36,5 @@ public:
 protected:
        boost::shared_ptr<const StillImageContent> _still_image_content;
 };
+
+#endif
index d0600f52e078cfa04a5792808cabf0e6d13830a3..5a3fed1b3988f52f1539f09831c2ddd7d9034b67 100644 (file)
@@ -60,14 +60,6 @@ StillImageContent::technical_summary () const
                + "still";
 }
 
-bool
-StillImageContent::valid_file (boost::filesystem::path f)
-{
-       string ext = f.extension().string();
-       transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
-       return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp");
-}
-
 void
 StillImageContent::as_xml (xmlpp::Node* node) const
 {
index b818792797d3b0226f0f60394c8d9ac1081d484a..24d5174a098b2f2bc2eee28a84ffbdd4e1bd80df 100644 (file)
@@ -27,6 +27,7 @@ namespace cxml {
        class Node;
 }
 
+/** A single image which is to be held on screen for some time (i.e. a slide) */
 class StillImageContent : public VideoContent
 {
 public:
@@ -46,8 +47,6 @@ public:
        std::string identifier () const;
        
        void set_video_length (VideoContent::Frame);
-
-       static bool valid_file (boost::filesystem::path);
 };
 
 #endif
index 183ff7d8b6431aeda75aaf235a5fda1c7c58ac3f..26dfa8bf7912561a74812c17d1ca0d0e6e8609c4 100644 (file)
@@ -775,3 +775,12 @@ LocaleGuard::~LocaleGuard ()
        setlocale (LC_NUMERIC, _old);
        free (_old);
 }
+
+bool
+valid_image_file (boost::filesystem::path f)
+{
+       string ext = f.extension().string();
+       transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
+       return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp");
+}
+
index 65fb3d0dd25bd8318d738f0d10821000fe6e9875..121deb532eb62cae1ab1e7f5e40eddc3fa2da10c 100644 (file)
@@ -67,6 +67,7 @@ extern std::string md5_digest_directory (boost::filesystem::path);
 extern std::string md5_digest (void const *, int);
 extern void ensure_ui_thread ();
 extern std::string audio_channel_name (int);
+extern bool valid_image_file (boost::filesystem::path);
 #ifdef DCPOMATIC_WINDOWS
 extern boost::filesystem::path mo_path ();
 #endif
index 100663b8742251e04c5a4999626692d9255b95cf..04dae7587403a29608d8163dd8ccc12b5d54921a 100644 (file)
@@ -28,12 +28,12 @@ sources = """
           film.cc
           filter.cc
           image.cc
-          still_image_content.cc
-          still_image_decoder.cc
-          still_image_examiner.cc
           job.cc
           job_manager.cc
           log.cc
+          moving_image_content.cc
+          moving_image_decoder.cc
+          moving_image_examiner.cc
           player.cc
           playlist.cc
           ratio.cc
@@ -44,6 +44,9 @@ sources = """
           sndfile_content.cc
           sndfile_decoder.cc
           sound_processor.cc
+          still_image_content.cc
+          still_image_decoder.cc
+          still_image_examiner.cc
           subtitle_content.cc
           subtitle_decoder.cc
           timer.cc
index cb14f70e786676af544b46afea0737eae26a2778..d82013d8a78aa92a48018955cbed1b6a5072b8c2 100644 (file)
@@ -37,6 +37,7 @@
 #include "lib/ratio.h"
 #include "lib/config.h"
 #include "lib/still_image_content.h"
+#include "lib/moving_image_content.h"
 #include "lib/sndfile_content.h"
 #include "lib/dcp_content_type.h"
 #include "lib/sound_processor.h"
@@ -212,7 +213,8 @@ FilmEditor::connect_to_widgets ()
        _content->Bind          (wxEVT_COMMAND_LIST_ITEM_SELECTED,    boost::bind (&FilmEditor::content_selection_changed, this));
        _content->Bind          (wxEVT_COMMAND_LIST_ITEM_DESELECTED,  boost::bind (&FilmEditor::content_selection_changed, this));
        _content->Bind          (wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, boost::bind (&FilmEditor::content_right_click, this, _1));
-       _content_add->Bind      (wxEVT_COMMAND_BUTTON_CLICKED,        boost::bind (&FilmEditor::content_add_clicked, this));
+       _content_add_file->Bind (wxEVT_COMMAND_BUTTON_CLICKED,        boost::bind (&FilmEditor::content_add_file_clicked, this));
+       _content_add_folder->Bind (wxEVT_COMMAND_BUTTON_CLICKED,      boost::bind (&FilmEditor::content_add_folder_clicked, this));
        _content_remove->Bind   (wxEVT_COMMAND_BUTTON_CLICKED,        boost::bind (&FilmEditor::content_remove_clicked, this));
        _content_timeline->Bind (wxEVT_COMMAND_BUTTON_CLICKED,        boost::bind (&FilmEditor::content_timeline_clicked, this));
        _scaler->Bind           (wxEVT_COMMAND_CHOICE_SELECTED,       boost::bind (&FilmEditor::scaler_changed, this));
@@ -243,8 +245,10 @@ FilmEditor::make_content_panel ()
                _content->SetColumnWidth (0, 512);
 
                wxBoxSizer* b = new wxBoxSizer (wxVERTICAL);
-               _content_add = new wxButton (_content_panel, wxID_ANY, _("Add..."));
-               b->Add (_content_add, 1, wxEXPAND | wxLEFT | wxRIGHT);
+               _content_add_file = new wxButton (_content_panel, wxID_ANY, _("Add file..."));
+               b->Add (_content_add_file, 1, wxEXPAND | wxLEFT | wxRIGHT);
+               _content_add_folder = new wxButton (_content_panel, wxID_ANY, _("Add folder..."));
+               b->Add (_content_add_folder, 1, wxEXPAND | wxLEFT | wxRIGHT);
                _content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
                b->Add (_content_remove, 1, wxEXPAND | wxLEFT | wxRIGHT);
                _content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
@@ -546,7 +550,8 @@ FilmEditor::set_general_sensitivity (bool s)
        _use_dci_name->Enable (s);
        _edit_dci_button->Enable (s);
        _content->Enable (s);
-       _content_add->Enable (s);
+       _content_add_file->Enable (s);
+       _content_add_folder->Enable (s);
        _content_remove->Enable (s);
        _content_timeline->Enable (s);
        _dcp_content_type->Enable (s);
@@ -658,7 +663,7 @@ FilmEditor::setup_content ()
 }
 
 void
-FilmEditor::content_add_clicked ()
+FilmEditor::content_add_file_clicked ()
 {
        wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE);
        int const r = d->ShowModal ();
@@ -671,12 +676,14 @@ FilmEditor::content_add_clicked ()
        wxArrayString paths;
        d->GetPaths (paths);
 
+       /* XXX: check for lots of files here and do something */
+
        for (unsigned int i = 0; i < paths.GetCount(); ++i) {
                boost::filesystem::path p (wx_to_std (paths[i]));
 
                shared_ptr<Content> c;
 
-               if (StillImageContent::valid_file (p)) {
+               if (valid_image_file (p)) {
                        c.reset (new StillImageContent (_film, p));
                } else if (SndfileContent::valid_file (p)) {
                        c.reset (new SndfileContent (_film, p));
@@ -688,6 +695,24 @@ FilmEditor::content_add_clicked ()
        }
 }
 
+void
+FilmEditor::content_add_folder_clicked ()
+{
+       wxDirDialog* d = new wxDirDialog (this, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
+       int const r = d->ShowModal ();
+       d->Destroy ();
+       
+       if (r != wxID_OK) {
+               return;
+       }
+
+       _film->examine_and_add_content (
+               shared_ptr<MovingImageContent> (
+                       new MovingImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ())))
+                       )
+               );
+}
+
 void
 FilmEditor::content_remove_clicked ()
 {
@@ -729,7 +754,8 @@ FilmEditor::content_selection_changed ()
 void
 FilmEditor::setup_content_sensitivity ()
 {
-       _content_add->Enable (_generally_sensitive);
+       _content_add_file->Enable (_generally_sensitive);
+       _content_add_folder->Enable (_generally_sensitive);
 
        shared_ptr<Content> selection = selected_content ();
 
index 0f62f4479b61dbf62da49c856cc2f30b46461ac9..f9f15e2acaa8117aa9f435867a3d14d471b3da7b 100644 (file)
@@ -76,7 +76,8 @@ private:
        void use_dci_name_toggled ();
        void edit_dci_button_clicked ();
        void content_selection_changed ();
-       void content_add_clicked ();
+       void content_add_file_clicked ();
+       void content_add_folder_clicked ();
        void content_remove_clicked ();
        void container_changed ();
        void dcp_content_type_changed ();
@@ -123,7 +124,8 @@ private:
        wxCheckBox* _use_dci_name;
        wxChoice* _container;
        wxListCtrl* _content;
-       wxButton* _content_add;
+       wxButton* _content_add_file;
+       wxButton* _content_add_folder;
        wxButton* _content_remove;
        wxButton* _content_earlier;
        wxButton* _content_later;