Basic implementation of a tree view for DKDMs (#1012).
authorCarl Hetherington <cth@carlh.net>
Tue, 31 Jan 2017 20:54:13 +0000 (20:54 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 24 May 2017 11:23:37 +0000 (12:23 +0100)
src/lib/config.cc
src/lib/config.h
src/lib/wscript
src/tools/dcpomatic.cc
src/tools/dcpomatic_kdm.cc
src/wx/wscript

index 1baba956e3bdc0efb47aeb03e94243a6fdcee667..c2c6592cb9f48c3228020ee8526e7070ea42be1c 100644 (file)
@@ -30,6 +30,7 @@
 #include "util.h"
 #include "cross.h"
 #include "film.h"
+#include "dkdm_wrapper.h"
 #include <dcp/raw_convert.h>
 #include <dcp/name_format.h>
 #include <dcp/certificate_chain.h>
@@ -57,6 +58,7 @@ using std::exception;
 using std::cerr;
 using boost::shared_ptr;
 using boost::optional;
+using boost::dynamic_pointer_cast;
 using boost::algorithm::trim;
 using dcp::raw_convert;
 
@@ -65,6 +67,8 @@ boost::signals2::signal<void ()> Config::FailedToLoad;
 
 /** Construct default configuration */
 Config::Config ()
+        /* DKDMs are not considered a thing to reset on set_defaults() */
+       : _dkdms (new DKDMGroup ("root"))
 {
        set_defaults ();
 }
@@ -315,11 +319,16 @@ try
                _decryption_chain = create_certificate_chain ();
        }
 
-       list<cxml::NodePtr> dkdm = f.node_children ("DKDM");
-       BOOST_FOREACH (cxml::NodePtr i, f.node_children ("DKDM")) {
-               _dkdms.push_back (dcp::EncryptedKDM (i->content ()));
+       if (f.optional_node_child("DKDMGroup")) {
+               /* New-style: all DKDMs in a group */
+               _dkdms = dynamic_pointer_cast<DKDMGroup> (DKDMBase::read (f.node_child("DKDMGroup")));
+       } else {
+               /* Old-style: one or more DKDM nodes */
+               _dkdms.reset (new DKDMGroup ("root"));
+               BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("DKDM")) {
+                       _dkdms->add (DKDMBase::read (i));
+               }
        }
-
        _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);
        _confirm_kdm_email = f.optional_bool_child("ConfirmKDMEmail").get_value_or (true);
@@ -367,7 +376,6 @@ catch (...) {
        write ();
 }
 
-
 /** @return Filename to write configuration to */
 boost::filesystem::path
 Config::path (string file, bool create_directories)
@@ -506,9 +514,7 @@ Config::write_config () const
                root->add_child("History")->add_child_text (i->string ());
        }
 
-       BOOST_FOREACH (dcp::EncryptedKDM i, _dkdms) {
-               root->add_child("DKDM")->add_child_text (i.as_xml ());
-       }
+       _dkdms->as_xml (root);
 
        root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
        root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
index 4406f164993915b12e987fb790213820f5e93d8d..44708a24fc231b561dcebe0efb84e2ade175fb9d 100644 (file)
@@ -40,6 +40,7 @@ class DCPContentType;
 class Ratio;
 class Cinema;
 class Film;
+class DKDMGroup;
 
 /** @class Config
  *  @brief A singleton class holding configuration.
@@ -281,7 +282,7 @@ public:
                return _history;
        }
 
-       std::vector<dcp::EncryptedKDM> dkdms () const {
+       boost::shared_ptr<DKDMGroup> dkdms () const {
                return _dkdms;
        }
 
@@ -546,7 +547,7 @@ public:
        }
 #endif
 
-       void set_dkdms (std::vector<dcp::EncryptedKDM> dkdms) {
+       void set_dkdms (boost::shared_ptr<DKDMGroup> dkdms) {
                _dkdms = dkdms;
                changed ();
        }
@@ -744,7 +745,7 @@ private:
        bool _win32_console;
 #endif
        std::vector<boost::filesystem::path> _history;
-       std::vector<dcp::EncryptedKDM> _dkdms;
+       boost::shared_ptr<DKDMGroup> _dkdms;
        boost::filesystem::path _cinemas_file;
        bool _show_hints_before_make_dcp;
        bool _confirm_kdm_email;
index dc6e1017aa0ca0c3911e3c29c88d9833f3858cac..cbb01730f247e70cc19ca0aefd3eca3807880e44 100644 (file)
@@ -64,6 +64,7 @@ sources = """
           decoder_factory.cc
           decoder_part.cc
           digester.cc
+          dkdm_wrapper.cc
           dolby_cp750.cc
           emailer.cc
           encoder.cc
index 1e2dd47d45006c703c36e69d1bf83b3cf6b24c47..b5f317942749a2089a2ab94bc87cf697263fb8c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -66,6 +66,7 @@
 #include "lib/dcp_content.h"
 #include "lib/ffmpeg_encoder.h"
 #include "lib/transcode_job.h"
+#include "lib/dkdm_wrapper.h"
 #include <dcp/exceptions.h>
 #include <dcp/raw_convert.h>
 #include <wx/generic/aboutdlgg.h>
@@ -690,9 +691,9 @@ private:
 
                if (kdm) {
                        if (d->internal ()) {
-                               vector<dcp::EncryptedKDM> dkdms = Config::instance()->dkdms ();
-                               dkdms.push_back (kdm.get());
-                               Config::instance()->set_dkdms (dkdms);
+                               shared_ptr<DKDMGroup> dkdms = Config::instance()->dkdms ();
+                               dkdms->add (shared_ptr<DKDM> (new DKDM (kdm.get())));
+                               Config::instance()->changed ();
                        } else {
                                boost::filesystem::path path = d->directory() / (_film->dcp_name(false) + "_DKDM.xml");
                                kdm->as_xml (path);
index e79448be7b376398c0562f22da8702e3bb7b8ff3..75149a718a6550066ce4d6960b9dad775db78fa3 100644 (file)
@@ -29,6 +29,7 @@
 #include "wx/kdm_output_panel.h"
 #include "wx/job_view_dialog.h"
 #include "wx/file_dialog_wrapper.h"
+#include "wx/new_dkdm_folder_dialog.h"
 #include "wx/editable_list.h"
 #include "lib/config.h"
 #include "lib/util.h"
@@ -40,6 +41,7 @@
 #include "lib/send_kdm_email_job.h"
 #include "lib/compose.hpp"
 #include "lib/cinema.h"
+#include "lib/dkdm_wrapper.h"
 #include <dcp/encrypted_kdm.h>
 #include <dcp/decrypted_kdm.h>
 #include <dcp/exceptions.h>
@@ -61,42 +63,17 @@ using std::list;
 using std::string;
 using std::vector;
 using std::pair;
+using std::map;
 using boost::shared_ptr;
 using boost::bind;
 using boost::optional;
 using boost::ref;
+using boost::dynamic_pointer_cast;
 
 enum {
        ID_help_report_a_problem = 1,
 };
 
-class KDMFileDialogWrapper : public FileDialogWrapper<dcp::EncryptedKDM>
-{
-public:
-       KDMFileDialogWrapper (wxWindow* parent)
-               : FileDialogWrapper<dcp::EncryptedKDM> (parent, _("Select DKDM file"))
-       {
-
-       }
-
-       optional<dcp::EncryptedKDM> get ()
-       {
-               try {
-                       return dcp::EncryptedKDM (dcp::file_to_string (wx_to_std (_dialog->GetPath ()), MAX_KDM_SIZE));
-               } catch (cxml::Error& e) {
-                       error_dialog (_parent, wxString::Format ("This file does not look like a KDM (%s)", std_to_wx (e.what()).data()));
-               }
-
-               return optional<dcp::EncryptedKDM> ();
-       }
-};
-
-static string
-column (dcp::EncryptedKDM k)
-{
-       return String::compose ("%1 (%2)", k.content_title_text(), k.cpl_id());
-}
-
 class DOMFrame : public wxFrame
 {
 public:
@@ -166,13 +143,23 @@ public:
                h = new wxStaticText (overall_panel, wxID_ANY, _("DKDM"));
                h->SetFont (subheading_font);
                right->Add (h, 0, wxALIGN_CENTER_VERTICAL | wxTOP, DCPOMATIC_SIZER_Y_GAP * 2);
-
-               vector<string> columns;
-               columns.push_back (wx_to_std (_("CPL")));
-               _dkdm = new EditableList<dcp::EncryptedKDM, KDMFileDialogWrapper> (
-                       overall_panel, columns, bind (&DOMFrame::dkdms, this), bind (&DOMFrame::set_dkdms, this, _1), bind (&column, _1), false
-                       );
-               right->Add (_dkdm, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_Y_GAP);
+               wxBoxSizer* dkdm_sizer = new wxBoxSizer (wxHORIZONTAL);
+               _dkdm = new wxTreeCtrl (
+                       overall_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT
+               );
+               dkdm_sizer->Add (_dkdm, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_Y_GAP);
+               wxBoxSizer* dkdm_buttons = new wxBoxSizer(wxVERTICAL);
+               _add_dkdm = new wxButton (overall_panel, wxID_ANY, _("Add..."));
+               dkdm_buttons->Add (_add_dkdm, 0, wxALL | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
+               _add_dkdm_folder = new wxButton (overall_panel, wxID_ANY, _("Add folder..."));
+               dkdm_buttons->Add (_add_dkdm_folder, 0, wxALL | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
+               _remove_dkdm = new wxButton (overall_panel, wxID_ANY, _("Remove"));
+               dkdm_buttons->Add (_remove_dkdm, 0, wxALL | wxEXPAND, DCPOMATIC_BUTTON_STACK_GAP);
+               dkdm_sizer->Add (dkdm_buttons, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
+               right->Add (dkdm_sizer, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_Y_GAP);
+
+               _dkdm_id[_dkdm->AddRoot("root")] = Config::instance()->dkdms();
+               add_dkdm_view (Config::instance()->dkdms(), shared_ptr<DKDMGroup> ());
 
                h = new wxStaticText (overall_panel, wxID_ANY, _("Output"));
                h->SetFont (subheading_font);
@@ -192,22 +179,15 @@ public:
 
                _screens->ScreensChanged.connect (boost::bind (&DOMFrame::setup_sensitivity, this));
                _create->Bind (wxEVT_BUTTON, bind (&DOMFrame::create_kdms, this));
-               _dkdm->SelectionChanged.connect (boost::bind (&DOMFrame::setup_sensitivity, this));
+               _dkdm->Bind (wxEVT_TREE_SEL_CHANGED, boost::bind (&DOMFrame::setup_sensitivity, this));
+               _add_dkdm->Bind (wxEVT_BUTTON, bind (&DOMFrame::add_dkdm_clicked, this));
+               _add_dkdm_folder->Bind (wxEVT_BUTTON, bind (&DOMFrame::add_dkdm_folder_clicked, this));
+               _remove_dkdm->Bind (wxEVT_BUTTON, bind (&DOMFrame::remove_dkdm_clicked, this));
 
                setup_sensitivity ();
        }
 
 private:
-       vector<dcp::EncryptedKDM> dkdms () const
-       {
-               return Config::instance()->dkdms ();
-       }
-
-       void set_dkdms (vector<dcp::EncryptedKDM> dkdms)
-       {
-               Config::instance()->set_dkdms (dkdms);
-       }
-
        void file_exit ()
        {
                /* false here allows the close handler to veto the close request */
@@ -278,15 +258,44 @@ private:
                        );
        }
 
+       /** @id if not 0 this is filled in with the wxTreeItemId of the selection */
+       shared_ptr<DKDMBase> selected_dkdm (wxTreeItemId* id = 0) const
+       {
+               wxArrayTreeItemIds selections;
+               _dkdm->GetSelections (selections);
+               if (selections.GetCount() != 1) {
+                       if (id) {
+                               *id = 0;
+                       }
+                       return shared_ptr<DKDMBase> ();
+               }
+
+               if (id) {
+                       *id = selections[0];
+               }
+
+               DKDMMap::const_iterator i = _dkdm_id.find (selections[0]);
+               if (i == _dkdm_id.end()) {
+                       return shared_ptr<DKDMBase> ();
+               }
+
+               return i->second;
+       }
+
        void create_kdms ()
        {
                try {
-                       if (!_dkdm->selection()) {
+                       shared_ptr<DKDMBase> dkdm_base = selected_dkdm ();
+                       if (!dkdm_base) {
+                               return;
+                       }
+                       shared_ptr<DKDM> dkdm = boost::dynamic_pointer_cast<DKDM> (dkdm_base);
+                       if (!dkdm) {
                                return;
                        }
 
                        /* Decrypt the DKDM */
-                       dcp::DecryptedKDM decrypted (_dkdm->selection().get(), Config::instance()->decryption_chain()->key().get());
+                       dcp::DecryptedKDM decrypted (dkdm->dkdm(), Config::instance()->decryption_chain()->key().get());
 
                        /* This is the signer for our new KDMs */
                        shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
@@ -354,13 +363,105 @@ private:
        {
                _screens->setup_sensitivity ();
                _output->setup_sensitivity ();
-               _create->Enable (!_screens->screens().empty() && _dkdm->selection());
+               wxArrayTreeItemIds sel;
+               _dkdm->GetSelections (sel);
+               _create->Enable (!_screens->screens().empty() && sel.GetCount() > 0);
+       }
+
+       void add_dkdm_clicked ()
+       {
+               wxFileDialog* d = new wxFileDialog (this, _("Select DKDM file"));
+               if (d->ShowModal() == wxID_OK) {
+                       shared_ptr<DKDMBase> new_dkdm (new DKDM (dcp::EncryptedKDM (dcp::file_to_string (wx_to_std (d->GetPath ()), MAX_KDM_SIZE))));
+                       shared_ptr<DKDMGroup> group = dynamic_pointer_cast<DKDMGroup> (selected_dkdm ());
+                       if (!group) {
+                               group = Config::instance()->dkdms ();
+                       }
+                       add_dkdm_model (new_dkdm, group);
+                       add_dkdm_view (new_dkdm, group);
+               }
+               d->Destroy ();
+       }
+
+       void add_dkdm_folder_clicked ()
+       {
+               NewDKDMFolderDialog* d = new NewDKDMFolderDialog (this);
+               if (d->ShowModal() == wxID_OK) {
+                       shared_ptr<DKDMBase> new_dkdm (new DKDMGroup (wx_to_std (d->get ())));
+                       shared_ptr<DKDMGroup> parent = dynamic_pointer_cast<DKDMGroup> (selected_dkdm ());
+                       if (!parent) {
+                               parent = Config::instance()->dkdms ();
+                       }
+                       add_dkdm_model (new_dkdm, parent);
+                       add_dkdm_view (new_dkdm, parent);
+               }
+               d->Destroy ();
+       }
+
+       /** @param dkdm Thing to add.
+        *  @param parent Parent group, or 0.
+        */
+       void add_dkdm_view (shared_ptr<DKDMBase> base, shared_ptr<DKDMGroup> parent)
+       {
+               if (!parent) {
+                       /* This is the root group */
+                       _dkdm_id[_dkdm->AddRoot("root")] = base;
+               } else {
+                       /* Add base to the view */
+                       _dkdm_id[_dkdm->AppendItem(dkdm_to_id(parent), std_to_wx(base->name()))] = base;
+               }
+
+               /* Add children */
+               shared_ptr<DKDMGroup> g = dynamic_pointer_cast<DKDMGroup> (base);
+               if (g) {
+                       BOOST_FOREACH (shared_ptr<DKDMBase> i, g->children()) {
+                               add_dkdm_view (i, g);
+                       }
+               }
+       }
+
+       /** @param group Group to add dkdm to */
+       void add_dkdm_model (shared_ptr<DKDMBase> dkdm, shared_ptr<DKDMGroup> group)
+       {
+               group->add (dkdm);
+               /* We're messing with a Config-owned object here, so tell it that something has changed.
+                  This isn't nice.
+               */
+               Config::instance()->changed ();
+       }
+
+       wxTreeItemId dkdm_to_id (shared_ptr<DKDMBase> dkdm)
+       {
+               for (DKDMMap::iterator i = _dkdm_id.begin(); i != _dkdm_id.end(); ++i) {
+                       if (i->second == dkdm) {
+                               return i->first;
+                       }
+               }
+               DCPOMATIC_ASSERT (false);
+       }
+
+       void remove_dkdm_clicked ()
+       {
+               shared_ptr<DKDMBase> removed = selected_dkdm ();
+               if (!removed) {
+                       return;
+               }
+
+               _dkdm->Delete (dkdm_to_id (removed));
+               shared_ptr<DKDMGroup> dkdms = Config::instance()->dkdms ();
+               dkdms->remove (removed);
+               Config::instance()->changed ();
        }
 
        wxPreferencesEditor* _config_dialog;
        ScreensPanel* _screens;
        KDMTimingPanel* _timing;
-       EditableList<dcp::EncryptedKDM, KDMFileDialogWrapper>* _dkdm;
+       wxTreeCtrl* _dkdm;
+       typedef std::map<wxTreeItemId, boost::shared_ptr<DKDMBase> > DKDMMap;
+       DKDMMap _dkdm_id;
+       wxButton* _add_dkdm;
+       wxButton* _add_dkdm_folder;
+       wxButton* _remove_dkdm;
        wxButton* _create;
        KDMOutputPanel* _output;
        JobViewDialog* _job_view;
index abe65734172ac4b601562e003b34b6a988cf0a06..142f0a24eb3f28bc4c64727e88a5585c4fcf53e2 100644 (file)
@@ -72,6 +72,7 @@ sources = """
           move_to_dialog.cc
           nag_dialog.cc
           name_format_editor.cc
+          new_dkdm_folder_dialog.cc
           normal_job_view.cc
           playhead_to_timecode_dialog.cc
           playhead_to_frame_dialog.cc