Basics of custom DCP filename components.
authorCarl Hetherington <cth@carlh.net>
Fri, 29 Jul 2016 14:54:09 +0000 (15:54 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 29 Jul 2016 14:54:09 +0000 (15:54 +0100)
20 files changed:
src/lib/cinema_kdms.cc
src/lib/cinema_kdms.h
src/lib/config.cc
src/lib/config.h
src/lib/kdm_name_format.h
src/lib/name_format.cc [deleted file]
src/lib/name_format.h [deleted file]
src/lib/screen_kdm.cc
src/lib/screen_kdm.h
src/lib/send_kdm_email_job.cc
src/lib/send_kdm_email_job.h
src/lib/util.cc
src/lib/writer.cc
src/lib/wscript
src/tools/dcpomatic_kdm.cc
src/tools/dcpomatic_kdm_cli.cc
src/wx/config_dialog.cc
src/wx/kdm_dialog.cc
src/wx/kdm_output_panel.cc
src/wx/name_format_editor.h

index a91fc1c1e09edd25745975cc7c2e50e81825cd35..cbfad4bb3706c4c824d6bd5fdd04c1a79684734b 100644 (file)
@@ -40,7 +40,7 @@ using std::runtime_error;
 using boost::shared_ptr;
 
 void
-CinemaKDMs::make_zip_file (boost::filesystem::path zip_file, KDMNameFormat name_format, NameFormat::Map name_values) const
+CinemaKDMs::make_zip_file (boost::filesystem::path zip_file, KDMNameFormat name_format, dcp::NameFormat::Map name_values) const
 {
        int error;
        struct zip* zip = zip_open (zip_file.string().c_str(), ZIP_CREATE | ZIP_EXCL, &error);
@@ -120,7 +120,7 @@ CinemaKDMs::write_zip_files (
        list<CinemaKDMs> cinema_kdms,
        boost::filesystem::path directory,
        KDMNameFormat name_format,
-       NameFormat::Map name_values
+       dcp::NameFormat::Map name_values
        )
 {
        /* No specific screen */
@@ -141,7 +141,7 @@ void
 CinemaKDMs::email (
        list<CinemaKDMs> cinema_kdms,
        KDMNameFormat name_format,
-       NameFormat::Map name_values,
+       dcp::NameFormat::Map name_values,
        string cpl_name,
        shared_ptr<Log> log
        )
index a9ab4867ed9b203a6c95d47c6f48e5843cae4d17..53cf84d66036bef1f31cac2a6d71c047bda509fa 100644 (file)
@@ -27,7 +27,7 @@ class Log;
 class CinemaKDMs
 {
 public:
-       void make_zip_file (boost::filesystem::path zip_file, KDMNameFormat name_format, NameFormat::Map name_values) const;
+       void make_zip_file (boost::filesystem::path zip_file, KDMNameFormat name_format, dcp::NameFormat::Map name_values) const;
 
        static std::list<CinemaKDMs> collect (std::list<ScreenKDM> kdms);
 
@@ -35,13 +35,13 @@ public:
                std::list<CinemaKDMs> cinema_kdms,
                boost::filesystem::path directory,
                KDMNameFormat name_format,
-               NameFormat::Map name_values
+               dcp::NameFormat::Map name_values
                );
 
        static void email (
                std::list<CinemaKDMs> cinema_kdms,
                KDMNameFormat name_format,
-               NameFormat::Map name_values,
+               dcp::NameFormat::Map name_values,
                std::string cpl_name,
                boost::shared_ptr<Log> log
                );
index a5a42436b24355c9f4e1605b277a49f53fcc700d..b456c605c93bd685351ddcf007e70bcec4100294 100644 (file)
@@ -31,6 +31,7 @@
 #include "cross.h"
 #include "raw_convert.h"
 #include "kdm_name_format.h"
+#include <dcp/filename_format.h>
 #include <dcp/colour_matrix.h>
 #include <dcp/certificate_chain.h>
 #include <libcxml/cxml.h>
@@ -110,6 +111,7 @@ Config::set_defaults ()
        _cinemas_file = path ("cinemas.xml");
        _show_hints_before_make_dcp = true;
        _kdm_filename_format = KDMNameFormat ("KDM %f %c %s");
+       _dcp_filename_format = dcp::FilenameFormat ("%t_%i");
 
        _allowed_dcp_frame_rates.clear ();
        _allowed_dcp_frame_rates.push_back (24);
@@ -293,6 +295,7 @@ try
        _cinemas_file = f.optional_string_child("CinemasFile").get_value_or (path ("cinemas.xml").string ());
        _show_hints_before_make_dcp = f.optional_bool_child("ShowHintsBeforeMakeDCP").get_value_or (true);
        _kdm_filename_format = KDMNameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
+       _dcp_filename_format = dcp::FilenameFormat (f.optional_string_child("DCPFilenameFormat").get_value_or ("%t_%i.mxf"));
 
        /* Replace any cinemas from config.xml with those from the configured file */
        if (boost::filesystem::exists (_cinemas_file)) {
@@ -451,6 +454,7 @@ Config::write_config_xml () const
        root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
        root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
        root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
+       root->add_child("DCPFilenameFormat")->add_child_text (_dcp_filename_format.specification ());
 
        try {
                doc.write_to_file_formatted (path("config.xml").string ());
index d89adf4916b083087261dac9f99f6f8a26dd8d36..37a55a6954f7f9f7db41fc1b8ebceef19fb5cdc2 100644 (file)
@@ -28,6 +28,7 @@
 #include "isdcf_metadata.h"
 #include "kdm_name_format.h"
 #include "types.h"
+#include <dcp/filename_format.h>
 #include <dcp/certificate_chain.h>
 #include <dcp/encrypted_kdm.h>
 #include <boost/shared_ptr.hpp>
@@ -271,6 +272,10 @@ public:
                return _kdm_filename_format;
        }
 
+       dcp::FilenameFormat dcp_filename_format () const {
+               return _dcp_filename_format;
+       }
+
        /** @param n New number of local encoding threads */
        void set_num_local_encoding_threads (int n) {
                maybe_set (_num_local_encoding_threads, n);
@@ -483,6 +488,10 @@ public:
                maybe_set (_kdm_filename_format, n);
        }
 
+       void set_dcp_filename_format (dcp::FilenameFormat n) {
+               maybe_set (_dcp_filename_format, n);
+       }
+
        void clear_history () {
                _history.clear ();
                changed ();
@@ -595,6 +604,7 @@ private:
        boost::filesystem::path _cinemas_file;
        bool _show_hints_before_make_dcp;
        KDMNameFormat _kdm_filename_format;
+       dcp::FilenameFormat _dcp_filename_format;
 
        /** Singleton instance, or 0 */
        static Config* _instance;
index fad5f7265d57344eefa67f8391d287c1c6a4683d..1f5a9ab61a66321dc4206a40a28e150df63f00ea 100644 (file)
@@ -21,9 +21,9 @@
 #ifndef DCPOMATIC_KDM_NAME_FORMAT
 #define DCPOMATIC_KDM_NAME_FORMAT
 
-#include "name_format.h"
+#include <dcp/name_format.h>
 
-class KDMNameFormat : public NameFormat
+class KDMNameFormat : public dcp::NameFormat
 {
 public:
        KDMNameFormat () {}
diff --git a/src/lib/name_format.cc b/src/lib/name_format.cc
deleted file mode 100644 (file)
index 07909c5..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
-    Copyright (C) 2016 Carl Hetherington <cth@carlh.net>
-
-    This file is part of DCP-o-matic.
-
-    DCP-o-matic 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.
-
-    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "name_format.h"
-#include <boost/optional.hpp>
-#include <boost/foreach.hpp>
-
-using std::string;
-using std::map;
-using boost::optional;
-
-static char
-filter (char c)
-{
-       if (c == '/' || c == ':') {
-               c = '-';
-       } else if (c == ' ') {
-               c = '_';
-       }
-
-       return c;
-}
-
-static string
-filter (string c)
-{
-       string o;
-
-       for (size_t i = 0; i < c.length(); ++i) {
-               o += filter (c[i]);
-       }
-
-       return o;
-}
-
-void
-NameFormat::add (string name, char placeholder, string title)
-{
-       _components.push_back (Component (name, placeholder, title));
-}
-
-optional<NameFormat::Component>
-NameFormat::component_by_placeholder (char p) const
-{
-       BOOST_FOREACH (Component const & i, _components) {
-               if (i.placeholder == p) {
-                       return i;
-               }
-       }
-
-       return optional<Component> ();
-}
-
-string
-NameFormat::get (Map values) const
-{
-       string result;
-       for (size_t i = 0; i < _specification.length(); ++i) {
-               bool done = false;
-               if (_specification[i] == '%' && (i < _specification.length() - 1)) {
-                       optional<Component> c = component_by_placeholder (_specification[i + 1]);
-                       if (c) {
-                               result += filter (values[c->name]);
-                               ++i;
-                               done = true;
-                       }
-               }
-
-               if (!done) {
-                       result += filter (_specification[i]);
-               }
-       }
-
-       return result;
-}
-
-bool
-operator== (NameFormat const & a, NameFormat const & b)
-{
-       return a.specification() == b.specification();
-}
diff --git a/src/lib/name_format.h b/src/lib/name_format.h
deleted file mode 100644 (file)
index 7e06dc0..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-    Copyright (C) 2016 Carl Hetherington <cth@carlh.net>
-
-    This file is part of DCP-o-matic.
-
-    DCP-o-matic 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.
-
-    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef DCPOMATIC_NAME_FORMAT
-#define DCPOMATIC_NAME_FORMAT
-
-#include <boost/optional.hpp>
-#include <map>
-#include <list>
-
-class NameFormat
-{
-public:
-       struct Component
-       {
-               Component (std::string name_, char placeholder_, std::string title_)
-                       : name (name_)
-                       , placeholder (placeholder_)
-                       , title (title_)
-               {}
-
-               std::string name;
-               char placeholder;
-               std::string title;
-       };
-
-       std::list<Component> components () const {
-               return _components;
-       }
-
-       std::string specification () const {
-               return _specification;
-       }
-
-       void set_specification (std::string specification) {
-               _specification = specification;
-       }
-
-       typedef std::map<std::string, std::string> Map;
-
-       std::string get (Map) const;
-
-protected:
-       NameFormat () {}
-
-       NameFormat (std::string specification)
-               : _specification (specification)
-       {}
-
-       void add (std::string name, char placeholder, std::string title);
-
-private:
-       boost::optional<NameFormat::Component> component_by_placeholder (char p) const;
-
-       std::list<Component> _components;
-       std::string _specification;
-};
-
-extern bool operator== (NameFormat const & a, NameFormat const & b);
-
-#endif
index 52590bc55a1fb1c9499b473214a4b7371fdf41b2..22081fc81d399e8987214028a9dda0ccdc8dcaf2 100644 (file)
@@ -35,7 +35,7 @@ operator== (ScreenKDM const & a, ScreenKDM const & b)
 }
 
 void
-ScreenKDM::write_files (list<ScreenKDM> screen_kdms, boost::filesystem::path directory, KDMNameFormat name_format, NameFormat::Map name_values)
+ScreenKDM::write_files (list<ScreenKDM> screen_kdms, boost::filesystem::path directory, KDMNameFormat name_format, dcp::NameFormat::Map name_values)
 {
        /* Write KDMs to the specified directory */
        BOOST_FOREACH (ScreenKDM const & i, screen_kdms) {
index 2e015e63ea50b0a363b4576ea2365ca9eb73d4df..d9031f18855ef04f770f805e7889b962967bcd8d 100644 (file)
@@ -38,7 +38,7 @@ public:
 
        static void write_files (
                std::list<ScreenKDM> screen_kdms, boost::filesystem::path directory,
-               KDMNameFormat name_format, NameFormat::Map name_values
+               KDMNameFormat name_format, dcp::NameFormat::Map name_values
                );
 
        boost::shared_ptr<Screen> screen;
index 4834657edbf592064d4c764643c92ae02f0e02c0..3bf1887f49df2bdf12bf9bade02a2026a03fa7f8 100644 (file)
@@ -34,7 +34,7 @@ using boost::shared_ptr;
 SendKDMEmailJob::SendKDMEmailJob (
        list<CinemaKDMs> cinema_kdms,
        KDMNameFormat name_format,
-       NameFormat::Map name_values,
+       dcp::NameFormat::Map name_values,
        string cpl_name,
        shared_ptr<Log> log
        )
@@ -51,7 +51,7 @@ SendKDMEmailJob::SendKDMEmailJob (
 string
 SendKDMEmailJob::name () const
 {
-       NameFormat::Map::const_iterator i = _name_values.find ("film_name");
+       dcp::NameFormat::Map::const_iterator i = _name_values.find ("film_name");
        if (i == _name_values.end() || i->second.empty ()) {
                return _("Email KDMs");
        }
index e16716a6675cb60cb471f05b18325bda53fb96ed..bfb4d6c522837ef07f4ee34a7522ee95e60bd345 100644 (file)
@@ -33,7 +33,7 @@ public:
        SendKDMEmailJob (
                std::list<CinemaKDMs> cinema_kdms,
                KDMNameFormat name_format,
-               NameFormat::Map name_values,
+               dcp::NameFormat::Map name_values,
                std::string cpl_name,
                boost::shared_ptr<Log> log
                );
@@ -44,7 +44,7 @@ public:
 
 private:
        KDMNameFormat _name_format;
-       NameFormat::Map _name_values;
+       dcp::NameFormat::Map _name_values;
        std::string _cpl_name;
        std::list<CinemaKDMs> _cinema_kdms;
        boost::shared_ptr<Log> _log;
index 4a0965858eabbf07aa0c156f5714eeb50d2f7ec7..09f32dfda5d4b541fe5c4874a041fc47aeb6f3bf 100644 (file)
@@ -619,13 +619,19 @@ split_get_request (string url)
 string
 video_asset_filename (shared_ptr<dcp::PictureAsset> asset)
 {
-       return "j2c_" + asset->id() + ".mxf";
+       dcp::NameFormat::Map values;
+       values["type"] = "j2c";
+       values["id"] = asset->id();
+       return Config::instance()->dcp_filename_format().get(values) + ".mxf";
 }
 
 string
 audio_asset_filename (shared_ptr<dcp::SoundAsset> asset)
 {
-       return "pcm_" + asset->id() + ".mxf";
+       dcp::NameFormat::Map values;
+       values["type"] = "pcm";
+       values["id"] = asset->id();
+       return Config::instance()->dcp_filename_format().get(values) + ".mxf";
 }
 
 float
index 1874e68f498cb9302ebe98aea990233c05000a19..00dfcdcbe9319c4022379866ed1d4de021ac1621 100644 (file)
@@ -512,7 +512,7 @@ Writer::finish ()
                }
        }
 
-       dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, signer);
+       dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, signer, Config::instance()->dcp_filename_format());
 
        LOG_GENERAL (
                N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT, %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk
index 84843992bfa1a0c63a91782e3f880be1c2241bc9..6a8945d8483bbe50307e16d5ae107e87818d0a6e 100644 (file)
@@ -99,7 +99,6 @@ sources = """
           log_entry.cc
           magick_image_proxy.cc
           mid_side_decoder.cc
-          name_format.cc
           overlaps.cc
           player.cc
           player_subtitles.cc
index 5afcb17a4d077d56306257917c83aeb6fa048ef8..01f8ef0a3acb6e839b434314fded4fcb883ec961 100644 (file)
@@ -297,7 +297,7 @@ private:
                                screen_kdms.push_back (ScreenKDM (i, kdm.encrypt (signer, i->recipient.get(), i->trusted_devices, _output->formulation())));
                        }
 
-                       NameFormat::Map name_values;
+                       dcp::NameFormat::Map name_values;
                        name_values["film_name"] = decrypted.content_title_text();
                        name_values["from"] = dcp::LocalTime(_timing->from()).date() + " " + dcp::LocalTime(_timing->from()).time_of_day();
                        name_values["to"] = dcp::LocalTime(_timing->until()).date() + " " + dcp::LocalTime(_timing->until()).time_of_day();
index ad5ee1c7bbcdae99ae298f1cba7359038a92fc3e..38808596ec25f01605fc0d48217e44823388fa6d 100644 (file)
@@ -284,7 +284,7 @@ int main (int argc, char* argv[])
                        output = ".";
                }
 
-               NameFormat::Map values;
+               dcp::NameFormat::Map values;
                values["film_name"] = film->name();
                values["from"] = dcp::LocalTime(valid_from.get()).date() + " " + dcp::LocalTime(valid_from.get()).time_of_day();
                values["to"] = dcp::LocalTime(valid_to.get()).date() + " " + dcp::LocalTime(valid_to.get()).time_of_day();
index 8b7cbdae5a7713eefcfb82ea7469685515e17558..c82956c608a2cf1ad86c85434ad58034c76a956b 100644 (file)
@@ -32,6 +32,7 @@
 #include "server_dialog.h"
 #include "make_chain_dialog.h"
 #include "email_dialog.h"
+#include "name_format_editor.h"
 #include "lib/config.h"
 #include "lib/ratio.h"
 #include "lib/filter.h"
@@ -1398,6 +1399,24 @@ private:
                table->Add (_only_servers_encode, 1, wxEXPAND | wxALL);
                table->AddSpacer (0);
 
+               {
+                       int flags = wxALIGN_TOP | wxTOP | wxLEFT;
+                       wxString t = _("DCP filename format");
+#ifdef __WXOSX__
+                       flags |= wxALIGN_RIGHT;
+                       t += wxT (":");
+#endif
+                       wxStaticText* m = new wxStaticText (_panel, wxID_ANY, t);
+                       table->Add (m, 0, flags, DCPOMATIC_SIZER_Y_GAP);
+               }
+
+               _dcp_filename_format = new NameFormatEditor<dcp::FilenameFormat> (_panel, Config::instance()->dcp_filename_format());
+               dcp::NameFormat::Map example;
+               example["type"] = "j2c";
+               example["id"] = "eb1c112c-ca3c-4ae6-9263-c6714ff05d64";
+               _dcp_filename_format->set_example (example);
+               table->Add (_dcp_filename_format->panel(), 1, wxEXPAND | wxALL);
+
 #ifdef __WXOSX__
                wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Log:"));
                table->Add (m, 0, wxALIGN_TOP | wxLEFT | wxRIGHT | wxEXPAND | wxALL | wxALIGN_RIGHT, 6);
@@ -1436,6 +1455,7 @@ private:
                _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
                _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
                _only_servers_encode->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::only_servers_encode_changed, this));
+               _dcp_filename_format->Changed.connect (boost::bind (&AdvancedPage::dcp_filename_format_changed, this));
                _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
                _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
                _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
@@ -1482,6 +1502,11 @@ private:
                Config::instance()->set_only_servers_encode (_only_servers_encode->GetValue ());
        }
 
+       void dcp_filename_format_changed ()
+       {
+               Config::instance()->set_dcp_filename_format (_dcp_filename_format->get ());
+       }
+
        void log_changed ()
        {
                int types = 0;
@@ -1519,6 +1544,7 @@ private:
        wxSpinCtrl* _maximum_j2k_bandwidth;
        wxCheckBox* _allow_any_dcp_frame_rate;
        wxCheckBox* _only_servers_encode;
+       NameFormatEditor<dcp::FilenameFormat>* _dcp_filename_format;
        wxCheckBox* _log_general;
        wxCheckBox* _log_warning;
        wxCheckBox* _log_error;
index 253050ddf52de2444b85fa99cee26342c2cd4327..cd018686ed4a9a6308c059b310ba2e4f7f4b3661 100644 (file)
@@ -133,7 +133,7 @@ KDMDialog::make_clicked ()
                        _screens->screens(), _cpl->cpl(), _timing->from(), _timing->until(), _output->formulation()
                        );
 
-               NameFormat::Map name_values;
+               dcp::NameFormat::Map name_values;
                name_values["film_name"] = film->name();
                name_values["from"] = dcp::LocalTime(_timing->from()).date() + " " + dcp::LocalTime(_timing->from()).time_of_day();
                name_values["to"] = dcp::LocalTime(_timing->until()).date() + " " + dcp::LocalTime(_timing->until()).time_of_day();
index e2510c929f5730838b33f892dbbb5ecbc70050e6..1d9e56f4bac48cf0cea67f19478c10b8ea8f9bef 100644 (file)
@@ -57,7 +57,7 @@ KDMOutputPanel::KDMOutputPanel (wxWindow* parent, bool interop)
        }
 
        _filename_format = new NameFormatEditor<KDMNameFormat> (this, Config::instance()->kdm_filename_format());
-       NameFormat::Map ex;
+       dcp::NameFormat::Map ex;
        ex["film_name"] = "Bambi";
        ex["cinema"] = "Lumière";
        ex["screen"] = "Screen 1";
index ab4787f64a5349021ca10ef43286c55ab9a1e794..1ca4c0b719864b0ae9adcb934f30fbed18130f8d 100644 (file)
@@ -21,8 +21,8 @@
 #ifndef DCPOMATIC_NAME_FORMAT_EDITOR_H
 #define DCPOMATIC_NAME_FORMAT_EDITOR_H
 
-#include "lib/name_format.h"
 #include "lib/compose.hpp"
+#include <dcp/name_format.h>
 #include <wx/wx.h>
 #include <boost/foreach.hpp>
 
@@ -41,7 +41,7 @@ public:
                _sizer->Add (_example, 0, wxBOTTOM, DCPOMATIC_SIZER_Y_GAP);
                _panel->SetSizer (_sizer);
 
-               BOOST_FOREACH (NameFormat::Component c, name.components ()) {
+               BOOST_FOREACH (dcp::NameFormat::Component c, name.components ()) {
                        wxStaticText* t = new wxStaticText (_panel, wxID_ANY, std_to_wx (String::compose ("%%%1 %2", c.placeholder, c.title)));
                        _sizer->Add (t);
                        wxFont font = t->GetFont();
@@ -52,7 +52,7 @@ public:
                }
 
                _specification->SetValue (std_to_wx (_name.specification ()));
-               _specification->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&NameFormatEditor::update_example, this));
+               _specification->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&NameFormatEditor::changed, this));
 
                update_example ();
        }
@@ -62,7 +62,7 @@ public:
                return _panel;
        }
 
-       void set_example (NameFormat::Map v) {
+       void set_example (dcp::NameFormat::Map v) {
                _example_values = v;
                update_example ();
        }
@@ -71,8 +71,16 @@ public:
                return _name;
        }
 
+       boost::signals2::signal<void ()> Changed;
+
 private:
 
+       void changed ()
+       {
+               update_example ();
+               Changed ();
+       }
+
        virtual void update_example ()
        {
                _name.set_specification (wx_to_std (_specification->GetValue ()));
@@ -95,7 +103,7 @@ private:
        wxTextCtrl* _specification;
 
        T _name;
-       NameFormat::Map _example_values;
+       dcp::NameFormat::Map _example_values;
 };
 
 #endif