+ 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->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 ();
+ if (!signer->valid ()) {
+ throw InvalidSignerError ();
+ }
+
+ list<ScreenKDM> screen_kdms;
+ BOOST_FOREACH (shared_ptr<Screen> i, _screens->screens()) {
+
+ if (!i->recipient) {
+ continue;
+ }
+
+ /* Make an empty KDM */
+ dcp::DecryptedKDM kdm (
+ dcp::LocalTime (_timing->from(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
+ dcp::LocalTime (_timing->until(), i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
+ decrypted.annotation_text().get_value_or (""),
+ decrypted.content_title_text(),
+ dcp::LocalTime().as_string()
+ );
+
+ /* Add keys from the DKDM */
+ BOOST_FOREACH (dcp::DecryptedKDMKey const & j, decrypted.keys()) {
+ kdm.add_key (j);
+ }
+
+ /* Encrypt */
+ screen_kdms.push_back (
+ ScreenKDM (
+ i,
+ kdm.encrypt (
+ signer, i->recipient.get(), i->trusted_devices, _output->formulation(),
+ !_output->forensic_mark_video(), _output->forensic_mark_audio() ? boost::optional<int>() : 0
+ )
+ )
+ );
+ }
+
+ pair<shared_ptr<Job>, int> result = _output->make (
+ screen_kdms, decrypted.content_title_text(), _timing, bind (&DOMFrame::confirm_overwrite, this, _1), shared_ptr<Log> ()
+ );
+
+ if (result.first) {
+ JobManager::instance()->add (result.first);
+ if (_job_view) {
+ _job_view->Destroy ();
+ _job_view = 0;
+ }
+ _job_view = new JobViewDialog (this, _("Send KDM emails"), result.first);
+ _job_view->ShowModal ();
+ }
+
+ if (result.second > 0) {
+ /* XXX: proper plural form support in wxWidgets? */
+ wxString s = result.second == 1 ? _("%d KDM written to %s") : _("%d KDMs written to %s");
+ message_dialog (
+ this,
+ wxString::Format (s, result.second, std_to_wx(_output->directory().string()).data())
+ );
+ }
+ } catch (dcp::NotEncryptedError& e) {
+ error_dialog (this, _("CPL's content is not encrypted."));
+ } catch (exception& e) {
+ error_dialog (this, std_to_wx(e.what()));
+ } catch (...) {
+ error_dialog (this, _("An unknown exception occurred."));
+ }
+ }
+
+ void setup_sensitivity ()
+ {
+ _screens->setup_sensitivity ();
+ _output->setup_sensitivity ();
+ wxArrayTreeItemIds sel;
+ _dkdm->GetSelections (sel);
+ _create->Enable (!_screens->screens().empty() && sel.GetCount() > 0);
+ }
+
+ void dkdm_begin_drag (wxTreeEvent& ev)
+ {
+ ev.Allow ();
+ }
+
+ void dkdm_end_drag (wxTreeEvent& ev)
+ {
+ DKDMMap::iterator from = _dkdm_id.find (_dkdm->GetSelection ());
+ DKDMMap::iterator to = _dkdm_id.find (ev.GetItem ());
+ if (from == _dkdm_id.end() || to == _dkdm_id.end() || from->first == to->first) {
+ return;
+ }
+
+ shared_ptr<DKDMGroup> group = dynamic_pointer_cast<DKDMGroup> (to->second);
+ if (!group) {
+ group = to->second->parent();
+ }
+
+ DCPOMATIC_ASSERT (group);
+ DCPOMATIC_ASSERT (from->second->parent ());
+
+ from->second->parent()->remove (from->second);
+ add_dkdm_model (from->second, group, dynamic_pointer_cast<DKDM>(to->second));
+
+ _dkdm->Delete (from->first);
+ _dkdm_id.erase (from->first);
+ add_dkdm_view (from->second, dynamic_pointer_cast<DKDM>(to->second) ? to->first : optional<wxTreeItemId>());
+ }
+
+ void add_dkdm_clicked ()
+ {
+ wxFileDialog* d = new wxFileDialog (this, _("Select DKDM file"));
+ if (d->ShowModal() == wxID_OK) {
+ shared_ptr<DKDMBase> new_dkdm;
+ try {
+ dcp::EncryptedKDM ekdm(dcp::file_to_string (wx_to_std (d->GetPath ()), MAX_KDM_SIZE));
+
+ /* Decrypt the DKDM to make sure that we can */
+ shared_ptr<const dcp::CertificateChain> chain = Config::instance()->decryption_chain();
+ DCPOMATIC_ASSERT (chain->key());
+ dcp::DecryptedKDM dkdm(ekdm, chain->key().get());
+
+ new_dkdm.reset(new DKDM(ekdm));
+ 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);
+ } catch (dcp::KDMFormatError& e) {
+ error_dialog (
+ this,
+ _("Could not read file as a KDM. Perhaps it is badly formatted, or not a KDM at all."),
+ std_to_wx(e.what())
+ );
+ return;
+ } catch (dcp::KDMDecryptionError) {
+ error_dialog (
+ this,
+ _("Could not decrypt the DKDM. Perhaps it was not created with the correct certificate.")
+ );
+ }
+ }
+ 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);
+ }
+ d->Destroy ();
+ }
+
+ /** @param dkdm Thing to add.
+ * @param parent Parent group, or 0.
+ */
+ void add_dkdm_view (shared_ptr<DKDMBase> base, optional<wxTreeItemId> previous = optional<wxTreeItemId>())
+ {
+ if (!base->parent()) {
+ /* This is the root group */
+ _dkdm_id[_dkdm->AddRoot("root")] = base;
+ } else {
+ /* Add base to the view */
+ wxTreeItemId added;
+ if (previous) {
+ added = _dkdm->InsertItem(dkdm_to_id(base->parent()), *previous, std_to_wx(base->name()));