/*
- Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "dcp_content_type.h"
#include "colour_conversion.h"
#include "cinema.h"
+#include "dkdm_recipient.h"
#include "util.h"
#include "cross.h"
#include "film.h"
_win32_console = false;
#endif
_cinemas_file = path ("cinemas.xml");
+ _dkdm_recipients_file = path ("dkdm_recipients.xml");
_show_hints_before_make_dcp = true;
_confirm_kdm_email = true;
_kdm_container_name_format = dcp::NameFormat ("KDM %f %c");
_kdm_filename_format = dcp::NameFormat ("KDM %f %c %s");
+ _dkdm_filename_format = dcp::NameFormat ("DKDM %f");
_dcp_metadata_filename_format = dcp::NameFormat ("%t");
_dcp_asset_filename_format = dcp::NameFormat ("%t");
_jump_to_selected = true;
boost::filesystem::copy_file(path("config.xml", false), path(String::compose("config.xml.%1", n), false));
boost::filesystem::copy_file(path("cinemas.xml", false), path(String::compose("cinemas.xml.%1", n), false));
+ boost::filesystem::copy_file(path("dkdm_recipients.xml", false), path(String::compose("dkdm_recipients.xml.%1", n), false));
} catch (...) {}
}
_default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
_default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
_default_kdm_directory = f.optional_string_child("DefaultKDMDirectory");
+ _default_dkdm_directory = f.optional_string_child("DefaultDKDMDirectory");
/* Load any cinemas from config.xml */
read_cinemas (f);
}
}
_cinemas_file = f.optional_string_child("CinemasFile").get_value_or (path ("cinemas.xml").string ());
+ _dkdm_recipients_file = f.optional_string_child("DKDMRecipientsFile").get_value_or(path("dkdm_recipients.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);
_kdm_container_name_format = dcp::NameFormat (f.optional_string_child("KDMContainerNameFormat").get_value_or ("KDM %f %c"));
_kdm_filename_format = dcp::NameFormat (f.optional_string_child("KDMFilenameFormat").get_value_or ("KDM %f %c %s"));
+ _dkdm_filename_format = dcp::NameFormat (f.optional_string_child("DKDMFilenameFormat").get_value_or("DKDM %f"));
_dcp_metadata_filename_format = dcp::NameFormat (f.optional_string_child("DCPMetadataFilenameFormat").get_value_or ("%t"));
_dcp_asset_filename_format = dcp::NameFormat (f.optional_string_child("DCPAssetFilenameFormat").get_value_or ("%t"));
_jump_to_selected = f.optional_bool_child("JumpToSelected").get_value_or (true);
_player_lock_file = f.optional_string_child("PlayerLockFile");
#endif
- /* Replace any cinemas from config.xml with those from the configured file */
if (boost::filesystem::exists (_cinemas_file)) {
cxml::Document f ("Cinemas");
f.read_file (_cinemas_file);
read_cinemas (f);
}
+
+ if (boost::filesystem::exists (_dkdm_recipients_file)) {
+ cxml::Document f ("DKDMRecipients");
+ f.read_file (_dkdm_recipients_file);
+ read_dkdm_recipients (f);
+ }
}
catch (...) {
if (have_existing ("config.xml")) {
{
write_config ();
write_cinemas ();
+ write_dkdm_recipients ();
}
void
/* [XML:opt] DefaultKDMDirectory Default directory to write KDMs to. */
root->add_child("DefaultKDMDirectory")->add_child_text (_default_kdm_directory->string ());
}
+ if (_default_dkdm_directory) {
+ /* [XML:opt] DefaultDKDMDirectory Default directory to write DKDMs to. */
+ root->add_child("DefaultDKDMDirectory")->add_child_text (_default_dkdm_directory->string());
+ }
/* [XML] MailServer Hostname of SMTP server to use. */
root->add_child("MailServer")->add_child_text (_mail_server);
/* [XML] MailPort Port number to use on SMTP server. */
/* [XML] CinemasFile Filename of cinemas list file. */
root->add_child("CinemasFile")->add_child_text (_cinemas_file.string());
+ /* [XML] DKDMRecipientsFile Filename of DKDM recipients list file. */
+ root->add_child("DKDMRecipientsFile")->add_child_text (_dkdm_recipients_file.string());
/* [XML] ShowHintsBeforeMakeDCP 1 to show hints in the GUI before making a DCP, otherwise 0. */
root->add_child("ShowHintsBeforeMakeDCP")->add_child_text (_show_hints_before_make_dcp ? "1" : "0");
/* [XML] ConfirmKDMEmail 1 to confirm before sending KDM emails in the GUI, otherwise 0. */
root->add_child("ConfirmKDMEmail")->add_child_text (_confirm_kdm_email ? "1" : "0");
/* [XML] KDMFilenameFormat Format for KDM filenames. */
root->add_child("KDMFilenameFormat")->add_child_text (_kdm_filename_format.specification ());
+ /* [XML] DKDMFilenameFormat Format for DKDM filenames. */
+ root->add_child("DKDMFilenameFormat")->add_child_text (_dkdm_filename_format.specification());
/* [XML] KDMContainerNameFormat Format for KDM containers (directories or ZIP files). */
root->add_child("KDMContainerNameFormat")->add_child_text (_kdm_container_name_format.specification ());
/* [XML] DCPMetadataFilenameFormat Format for DCP metadata filenames. */
}
}
+
+template <class T>
void
-Config::write_cinemas () const
+write_list (string root_node, string version, string child_node, list<shared_ptr<T> > const& children, boost::filesystem::path file)
{
xmlpp::Document doc;
- xmlpp::Element* root = doc.create_root_node ("Cinemas");
- root->add_child("Version")->add_child_text ("1");
+ xmlpp::Element* root = doc.create_root_node(root_node);
+ root->add_child("Version")->add_child_text(version);
- BOOST_FOREACH (shared_ptr<Cinema> i, _cinemas) {
- i->as_xml (root->add_child ("Cinema"));
+ BOOST_FOREACH (shared_ptr<T> i, children) {
+ i->as_xml (root->add_child(child_node));
}
try {
- doc.write_to_file_formatted (_cinemas_file.string() + ".tmp");
- boost::filesystem::remove (_cinemas_file);
- boost::filesystem::rename (_cinemas_file.string() + ".tmp", _cinemas_file);
+ doc.write_to_file_formatted (file.string() + ".tmp");
+ boost::filesystem::remove (file);
+ boost::filesystem::rename (file.string() + ".tmp", file);
} catch (xmlpp::exception& e) {
string s = e.what ();
trim (s);
- throw FileError (s, _cinemas_file);
+ throw FileError (s, file);
}
}
+
+void
+Config::write_cinemas () const
+{
+ write_list<Cinema>("Cinemas", "1", "Cinema", _cinemas, _cinemas_file);
+}
+
+
+void
+Config::write_dkdm_recipients () const
+{
+ write_list<DKDMRecipient>("DKDMRecipients", "1", "DKDMRecipient", _dkdm_recipients, _dkdm_recipients_file);
+}
+
+
boost::filesystem::path
Config::default_directory_or (boost::filesystem::path a) const
{
return directory_or (_default_kdm_directory, a);
}
+
+boost::filesystem::path
+Config::default_dkdm_directory_or (boost::filesystem::path a) const
+{
+ return directory_or (_default_dkdm_directory, a);
+}
+
+
boost::filesystem::path
Config::directory_or (optional<boost::filesystem::path> dir, boost::filesystem::path a) const
{
return boost::filesystem::exists (path (file, false));
}
+
void
Config::read_cinemas (cxml::Document const & f)
{
_cinemas.clear ();
- list<cxml::NodePtr> cin = f.node_children ("Cinema");
BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("Cinema")) {
/* Slightly grotty two-part construction of Cinema here so that we can use
shared_from_this.
}
}
+
+void
+Config::read_dkdm_recipients (cxml::Document const & f)
+{
+ _dkdm_recipients.clear ();
+ BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("DKDMRecipient")) {
+ _dkdm_recipients.push_back (shared_ptr<DKDMRecipient>(new DKDMRecipient(i)));
+ }
+}
+
+
void
Config::set_cinemas_file (boost::filesystem::path file)
{
changed (OTHER);
}
+
+void
+Config::set_dkdm_recipients_file (boost::filesystem::path file)
+{
+ if (file == _dkdm_recipients_file) {
+ return;
+ }
+
+ _dkdm_recipients_file = file;
+
+ if (boost::filesystem::exists (_dkdm_recipients_file)) {
+ /* Existing file; read it in */
+ cxml::Document f ("DKDMRecipients");
+ f.read_file (_dkdm_recipients_file);
+ read_dkdm_recipients (f);
+ }
+
+ changed (OTHER);
+}
+
+
void
Config::save_template (shared_ptr<const Film> film, string name) const
{
class DCPContentType;
class Ratio;
class Cinema;
+class DKDMRecipient;
class Film;
class DKDMGroup;
return _default_kdm_directory;
}
+ boost::optional<boost::filesystem::path> default_dkdm_directory () const {
+ return _default_dkdm_directory;
+ }
+
boost::filesystem::path default_directory_or (boost::filesystem::path a) const;
boost::filesystem::path default_kdm_directory_or (boost::filesystem::path a) const;
+ boost::filesystem::path default_dkdm_directory_or (boost::filesystem::path a) const;
enum Property {
USE_ANY_SERVERS,
SERVERS,
CINEMAS,
+ DKDM_RECIPIENTS,
SOUND,
SOUND_OUTPUT,
INTERFACE_COMPLEXITY,
return _cinemas;
}
+ std::list<boost::shared_ptr<DKDMRecipient> > dkdm_recipients () const {
+ return _dkdm_recipients;
+ }
+
std::list<int> allowed_dcp_frame_rates () const {
return _allowed_dcp_frame_rates;
}
changed ();
}
+ void set_default_dkdm_directory (boost::filesystem::path d) {
+ if (_default_dkdm_directory && _default_dkdm_directory.get() == d) {
+ return;
+ }
+ _default_dkdm_directory = d;
+ changed ();
+ }
+
std::string mail_server () const {
return _mail_server;
}
return _cinemas_file;
}
+ boost::filesystem::path dkdm_recipients_file () const {
+ return _dkdm_recipients_file;
+ }
+
bool show_hints_before_make_dcp () const {
return _show_hints_before_make_dcp;
}
return _kdm_filename_format;
}
+ dcp::NameFormat dkdm_filename_format () const {
+ return _dkdm_filename_format;
+ }
+
dcp::NameFormat dcp_metadata_filename_format () const {
return _dcp_metadata_filename_format;
}
changed (CINEMAS);
}
+ void add_dkdm_recipient (boost::shared_ptr<DKDMRecipient> r) {
+ _dkdm_recipients.push_back (r);
+ changed (DKDM_RECIPIENTS);
+ }
+
+ void remove_dkdm_recipient (boost::shared_ptr<DKDMRecipient> r) {
+ _dkdm_recipients.remove (r);
+ changed (DKDM_RECIPIENTS);
+ }
+
void set_allowed_dcp_frame_rates (std::list<int> const & r) {
maybe_set (_allowed_dcp_frame_rates, r);
}
void set_cinemas_file (boost::filesystem::path file);
+ void set_dkdm_recipients_file (boost::filesystem::path file);
+
void set_show_hints_before_make_dcp (bool s) {
maybe_set (_show_hints_before_make_dcp, s);
}
maybe_set (_kdm_filename_format, n);
}
+ void set_dkdm_filename_format (dcp::NameFormat n) {
+ maybe_set (_dkdm_filename_format, n);
+ }
+
void set_dcp_metadata_filename_format (dcp::NameFormat n) {
maybe_set (_dcp_metadata_filename_format, n);
}
void write () const;
void write_config () const;
void write_cinemas () const;
+ void write_dkdm_recipients () const;
void link (boost::filesystem::path new_file) const;
void copy_and_link (boost::filesystem::path new_file) const;
bool have_write_permission () const;
void set_notification_email_to_default ();
void set_cover_sheet_to_default ();
void read_cinemas (cxml::Document const & f);
+ void read_dkdm_recipients (cxml::Document const & f);
boost::shared_ptr<dcp::CertificateChain> create_certificate_chain ();
boost::filesystem::path directory_or (boost::optional<boost::filesystem::path> dir, boost::filesystem::path a) const;
void add_to_history_internal (std::vector<boost::filesystem::path>& h, boost::filesystem::path p);
the home directory will be offered.
*/
boost::optional<boost::filesystem::path> _default_kdm_directory;
+ boost::optional<boost::filesystem::path> _default_dkdm_directory;
bool _default_upload_after_make_dcp;
std::list<boost::shared_ptr<Cinema> > _cinemas;
+ std::list<boost::shared_ptr<DKDMRecipient> > _dkdm_recipients;
std::string _mail_server;
int _mail_port;
EmailProtocol _mail_protocol;
std::vector<boost::filesystem::path> _player_history;
boost::shared_ptr<DKDMGroup> _dkdms;
boost::filesystem::path _cinemas_file;
+ boost::filesystem::path _dkdm_recipients_file;
bool _show_hints_before_make_dcp;
bool _confirm_kdm_email;
dcp::NameFormat _kdm_filename_format;
+ dcp::NameFormat _dkdm_filename_format;
dcp::NameFormat _kdm_container_name_format;
dcp::NameFormat _dcp_metadata_filename_format;
dcp::NameFormat _dcp_asset_filename_format;
--- /dev/null
+/*
+ Copyright (C) 2020 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 "dkdm_recipient.h"
+#include "dcpomatic_assert.h"
+#include <dcp/raw_convert.h>
+#include <libxml++/libxml++.h>
+#include <boost/foreach.hpp>
+
+using std::string;
+using dcp::raw_convert;
+
+DKDMRecipient::DKDMRecipient (cxml::ConstNodePtr node)
+ : KDMRecipient (node)
+{
+ BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Email")) {
+ emails.push_back (i->content());
+ }
+
+ _utc_offset_hour = node->number_child<int>("UTCOffsetHour");
+ _utc_offset_minute = node->number_child<int>("UTCOffsetMinute");
+}
+
+
+void
+DKDMRecipient::as_xml (xmlpp::Element* parent) const
+{
+ KDMRecipient::as_xml (parent);
+
+ BOOST_FOREACH (string i, emails) {
+ parent->add_child("Email")->add_child_text (i);
+ }
+
+ parent->add_child("UTCOffsetHour")->add_child_text(raw_convert<string>(_utc_offset_hour));
+ parent->add_child("UTCOffsetMinute")->add_child_text(raw_convert<string>(_utc_offset_minute));
+}
+
+
+void
+DKDMRecipient::set_utc_offset_hour (int h)
+{
+ DCPOMATIC_ASSERT (h >= -11 && h <= 12);
+ _utc_offset_hour = h;
+}
+
+
+void
+DKDMRecipient::set_utc_offset_minute (int m)
+{
+ DCPOMATIC_ASSERT (m >= 0 && m <= 59);
+ _utc_offset_minute = m;
+}
--- /dev/null
+/*
+ Copyright (C) 2020 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_DKDM_RECIPIENT_H
+#define DCPOMATIC_DKDM_RECIPIENT_H
+
+#include "kdm_recipient.h"
+#include <dcp/certificate.h>
+#include <libcxml/cxml.h>
+
+class DKDMRecipient : public KDMRecipient
+{
+public:
+ DKDMRecipient (
+ std::string const& name_,
+ std::string const& notes_,
+ boost::optional<dcp::Certificate> recipient_,
+ std::list<std::string> const& emails_,
+ int utc_offset_hour_,
+ int utc_offset_minute_)
+ : KDMRecipient (name_, notes_, recipient_)
+ , emails (emails_)
+ , _utc_offset_hour (utc_offset_hour_)
+ , _utc_offset_minute (utc_offset_minute_)
+ {}
+
+ explicit DKDMRecipient (cxml::ConstNodePtr node);
+
+ void as_xml (xmlpp::Element *) const;
+
+ void set_utc_offset_hour (int h);
+ void set_utc_offset_minute (int m);
+
+ int utc_offset_hour () const {
+ return _utc_offset_hour;
+ }
+
+ int utc_offset_minute () const {
+ return _utc_offset_minute;
+ }
+
+ std::list<std::string> emails;
+
+private:
+ /** Offset such that the equivalent time in UTC can be determined
+ by subtracting the offset from the local time.
+ */
+ int _utc_offset_hour;
+ /** Additional minutes to add to _utc_offset_hour if _utc_offset_hour is
+ positive, or to subtract if _utc_offset_hour is negative.
+ */
+ int _utc_offset_minute;
+};
+
+#endif
*/
+#ifndef DCPOMATIC_KDM_RECIPIENT_H
+#define DCPOMATIC_KDM_RECIPIENT_H
+
#include <dcp/certificate.h>
#include <libcxml/cxml.h>
#include <libxml++/libxml++.h>
std::string notes;
boost::optional<dcp::Certificate> recipient;
};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2020 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 "recipient_with_dkdm.h"
+#include "config.h"
+#include "dcpomatic_log.h"
+#include "emailer.h"
+#include "exceptions.h"
+#include "zipper.h"
+#include "util.h"
+#include <boost/foreach.hpp>
+
+#include "i18n.h"
+
+using std::list;
+using std::vector;
+using std::string;
+using boost::shared_ptr;
+
+void
+RecipientWithDKDM::make_zip_file (boost::filesystem::path zip_file, dcp::NameFormat name_format, dcp::NameFormat::Map name_values) const
+{
+ Zipper zipper (zip_file);
+
+ name_values['i'] = kdm.cpl_id ();
+ string const name = careful_string_filter(name_format.get(name_values, ".xml"));
+ zipper.add (name, kdm.as_xml());
+
+ zipper.close ();
+}
+
+
+/** Email one ZIP file per recipient.
+ * @param kdms KDMs to email.
+ * @param filename_format Format of filenames to use.
+ * @param name_values Values to substitute into \p container_name_format and \p filename_format.
+ * @param cpl_name Name of the CPL that the KDMs are for.
+ */
+void
+RecipientWithDKDM::email (
+ vector<RecipientWithDKDM> kdms,
+ dcp::NameFormat filename_format,
+ dcp::NameFormat::Map name_values,
+ string cpl_name
+ )
+{
+ Config* config = Config::instance ();
+
+ if (config->mail_server().empty()) {
+ throw NetworkError (_("No mail server configured in preferences"));
+ }
+
+ BOOST_FOREACH (RecipientWithDKDM const & i, kdms) {
+
+ if (i.recipient->emails.empty()) {
+ continue;
+ }
+
+ boost::filesystem::path zip_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
+ boost::filesystem::create_directories (zip_file);
+ zip_file /= filename_format.get(name_values, ".zip");
+ i.make_zip_file (zip_file, filename_format, name_values);
+
+ string subject = config->kdm_subject();
+ boost::algorithm::replace_all (subject, "$CPL_NAME", cpl_name);
+ boost::algorithm::replace_all (subject, "$START_TIME", name_values['b']);
+ boost::algorithm::replace_all (subject, "$END_TIME", name_values['e']);
+
+ string body = config->kdm_email().c_str();
+ boost::algorithm::replace_all (body, "$CPL_NAME", cpl_name);
+ boost::algorithm::replace_all (body, "$START_TIME", name_values['b']);
+ boost::algorithm::replace_all (body, "$END_TIME", name_values['e']);
+
+ Emailer email (config->kdm_from(), i.recipient->emails, subject, body);
+
+ BOOST_FOREACH (string i, config->kdm_cc()) {
+ email.add_cc (i);
+ }
+ if (!config->kdm_bcc().empty ()) {
+ email.add_bcc (config->kdm_bcc ());
+ }
+
+ email.add_attachment (zip_file, filename_format.get(name_values, ".zip"), "application/zip");
+
+ try {
+ email.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
+ } catch (...) {
+ boost::filesystem::remove (zip_file);
+ dcpomatic_log->log ("Email content follows", LogEntry::TYPE_DEBUG_EMAIL);
+ dcpomatic_log->log (email.email(), LogEntry::TYPE_DEBUG_EMAIL);
+ dcpomatic_log->log ("Email session follows", LogEntry::TYPE_DEBUG_EMAIL);
+ dcpomatic_log->log (email.notes(), LogEntry::TYPE_DEBUG_EMAIL);
+ throw;
+ }
+
+ boost::filesystem::remove (zip_file);
+
+ dcpomatic_log->log ("Email content follows", LogEntry::TYPE_DEBUG_EMAIL);
+ dcpomatic_log->log (email.email(), LogEntry::TYPE_DEBUG_EMAIL);
+ dcpomatic_log->log ("Email session follows", LogEntry::TYPE_DEBUG_EMAIL);
+ dcpomatic_log->log (email.notes(), LogEntry::TYPE_DEBUG_EMAIL);
+ }
+}
+
+
+int
+RecipientWithDKDM::write_files (
+ vector<RecipientWithDKDM> kdms,
+ boost::filesystem::path directory,
+ dcp::NameFormat name_format,
+ dcp::NameFormat::Map name_values,
+ boost::function<bool (boost::filesystem::path)> confirm_overwrite
+ )
+{
+ int written = 0;
+
+ if (!boost::filesystem::exists (directory)) {
+ boost::filesystem::create_directories (directory);
+ }
+
+ /* Write KDMs to the specified directory */
+ BOOST_FOREACH (RecipientWithDKDM const& i, kdms) {
+ name_values['i'] = i.kdm.cpl_id ();
+ boost::filesystem::path out = directory / careful_string_filter(name_format.get(name_values, ".xml"));
+ if (!boost::filesystem::exists (out) || confirm_overwrite (out)) {
+ i.kdm.as_xml (out);
+ ++written;
+ }
+ }
+
+ return written;
+}
--- /dev/null
+/*
+ Copyright (C) 2020 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_RECIPIENT_WITH_DKDM_H
+#define DCPOMATIC_RECIPIENT_WITH_DKDM_H
+
+#include "dkdm_recipient.h"
+#include <dcp/encrypted_kdm.h>
+#include <dcp/name_format.h>
+
+class RecipientWithDKDM
+{
+public:
+ RecipientWithDKDM (boost::shared_ptr<DKDMRecipient> r, dcp::EncryptedKDM k)
+ : recipient (r)
+ , kdm (k)
+ {}
+
+ void make_zip_file (boost::filesystem::path zip_file, dcp::NameFormat name_format, dcp::NameFormat::Map name_values) const;
+
+ static int write_files (
+ std::vector<RecipientWithDKDM> kdms, boost::filesystem::path directory,
+ dcp::NameFormat name_format, dcp::NameFormat::Map name_values,
+ boost::function<bool (boost::filesystem::path)> confirm_overwrite
+ );
+
+ static void email (
+ std::vector<RecipientWithDKDM> kdms,
+ dcp::NameFormat filename_format,
+ dcp::NameFormat::Map name_values,
+ std::string cpl_name
+ );
+
+ boost::shared_ptr<DKDMRecipient> recipient;
+ dcp::EncryptedKDM kdm;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2013 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 "send_dkdm_email_job.h"
+#include "compose.hpp"
+#include "film.h"
+#include "cinema_kdms.h"
+#include <list>
+
+#include "i18n.h"
+
+using std::string;
+using std::list;
+using std::vector;
+using boost::shared_ptr;
+
+/** @param kdms KDMs to email.
+ * @param container_name_format Format to ues for folders / ZIP files.
+ * @param filename_format Format to use for filenames.
+ * @param name_values Values to substitute into \p container_name_format and \p filename_format.
+ * @param cpl_name Name of the CPL that the KDMs are for.
+ */
+SendDKDMEmailJob::SendDKDMEmailJob (
+ vector<RecipientWithDKDM> kdms,
+ dcp::NameFormat filename_format,
+ dcp::NameFormat::Map name_values,
+ string cpl_name
+ )
+ : Job (shared_ptr<Film>())
+ , _filename_format (filename_format)
+ , _name_values (name_values)
+ , _cpl_name (cpl_name)
+ , _kdms (kdms)
+{
+
+}
+
+
+SendDKDMEmailJob::~SendDKDMEmailJob ()
+{
+ stop_thread ();
+}
+
+
+string
+SendDKDMEmailJob::name () const
+{
+ dcp::NameFormat::Map::const_iterator i = _name_values.find ('f');
+ if (i == _name_values.end() || i->second.empty()) {
+ return _("Email DKDMs");
+ }
+
+ return String::compose (_("Email DKDMs for %1"), i->second);
+}
+
+
+string
+SendDKDMEmailJob::json_name () const
+{
+ return N_("send_dkdm_email");
+}
+
+
+void
+SendDKDMEmailJob::run ()
+{
+ set_progress_unknown ();
+ RecipientWithDKDM::email (_kdms, _filename_format, _name_values, _cpl_name);
+ set_progress (1);
+ set_state (FINISHED_OK);
+}
--- /dev/null
+/*
+ Copyright (C) 2013-2020 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 "job.h"
+#include "recipient_with_dkdm.h"
+#include <dcp/types.h>
+#include <dcp/name_format.h>
+#include <boost/filesystem.hpp>
+
+namespace dcpomatic {
+ class Screen;
+}
+
+class CinemaKDMs;
+class Log;
+
+class SendDKDMEmailJob : public Job
+{
+public:
+ SendDKDMEmailJob (
+ std::vector<RecipientWithDKDM> kdms,
+ dcp::NameFormat filename_format,
+ dcp::NameFormat::Map name_values,
+ std::string cpl_name
+ );
+ ~SendDKDMEmailJob ();
+
+ std::string name () const;
+ std::string json_name () const;
+ void run ();
+
+private:
+ dcp::NameFormat _filename_format;
+ dcp::NameFormat::Map _name_values;
+ std::string _cpl_name;
+ std::vector<RecipientWithDKDM> _kdms;
+};
decoder_part.cc
decrypted_ecinema_kdm.cc
digester.cc
+ dkdm_recipient.cc
dkdm_wrapper.cc
dolby_cp750.cc
edid.cc
position_image.cc
ratio.cc
raw_image_proxy.cc
+ recipient_with_dkdm.cc
reel_writer.cc
render_text.cc
resampler.cc
transcode_job.cc
trusted_device.cc
types.cc
+ send_dkdm_email_job.cc
signal_manager.cc
stdout_log.cc
update_checker.cc
#include "wx/recreate_chain_dialog.h"
#include "wx/about_dialog.h"
#include "wx/kdm_dialog.h"
+#include "wx/dkdm_dialog.h"
#include "wx/self_dkdm_dialog.h"
#include "wx/servers_list_dialog.h"
#include "wx/hints_dialog.h"
ID_jobs_make_dcp,
ID_jobs_make_dcp_batch,
ID_jobs_make_kdms,
+ ID_jobs_make_dkdms,
ID_jobs_make_self_dkdm,
ID_jobs_export,
ID_jobs_send_dcp_to_tms,
, _servers_list_dialog (0)
, _config_dialog (0)
, _kdm_dialog (0)
+ , _dkdm_dialog (0)
, _templates_dialog (0)
, _file_menu (0)
, _history_items (0)
Bind (wxEVT_MENU, boost::bind (&DOMFrame::content_scale_to_fit_height, this), ID_content_scale_to_fit_height);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dcp, this), ID_jobs_make_dcp);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_kdms, this), ID_jobs_make_kdms);
+ Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dkdms, this), ID_jobs_make_dkdms);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_dcp_batch, this), ID_jobs_make_dcp_batch);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_make_self_dkdm, this), ID_jobs_make_self_dkdm);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_export, this), ID_jobs_export);
_kdm_dialog->Show ();
}
+
+ void jobs_make_dkdms ()
+ {
+ if (!_film) {
+ return;
+ }
+
+ if (_dkdm_dialog) {
+ _dkdm_dialog->Destroy ();
+ _dkdm_dialog = 0;
+ }
+
+ _dkdm_dialog = new DKDMDialog (this, _film);
+ _dkdm_dialog->Show ();
+ }
+
+
/** @return false if we succeeded, true if not */
bool send_to_other_tool (int port, function<void(boost::filesystem::path)> start, string message)
{
add_item (jobs_menu, _("Make DCP in &batch converter\tCtrl-B"), ID_jobs_make_dcp_batch, NEEDS_FILM | NOT_DURING_DCP_CREATION);
jobs_menu->AppendSeparator ();
add_item (jobs_menu, _("Make &KDMs...\tCtrl-K"), ID_jobs_make_kdms, NEEDS_FILM);
+ add_item (jobs_menu, _("Make DKDMs...\t"), ID_jobs_make_dkdms, NEEDS_FILM);
add_item (jobs_menu, _("Make DKDM for DCP-o-matic..."), ID_jobs_make_self_dkdm, NEEDS_FILM | NEEDS_ENCRYPTION);
jobs_menu->AppendSeparator ();
add_item (jobs_menu, _("Export...\tCtrl-E"), ID_jobs_export, NEEDS_FILM);
)
);
}
+ } else if (what == Config::DKDM_RECIPIENTS) {
+ try {
+ Config::instance()->write_dkdm_recipients();
+ } catch (exception& e) {
+ error_dialog (
+ this,
+ wxString::Format (
+ _("Could not write to DKDM recipients file at %s. Your changes have not been saved."),
+ std_to_wx (Config::instance()->dkdm_recipients_file().string()).data()
+ )
+ );
+ }
} else {
try {
Config::instance()->write_config();
ServersListDialog* _servers_list_dialog;
wxPreferencesEditor* _config_dialog;
KDMDialog* _kdm_dialog;
+ DKDMDialog* _dkdm_dialog;
TemplatesDialog* _templates_dialog;
wxMenu* _file_menu;
shared_ptr<Film> _film;
overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
}
- _offsets.push_back (Offset (_("UTC-11"), -11, 0));
- _offsets.push_back (Offset (_("UTC-10"), -10, 0));
- _offsets.push_back (Offset (_("UTC-9"), -9, 0));
- _offsets.push_back (Offset (_("UTC-8"), -8, 0));
- _offsets.push_back (Offset (_("UTC-7"), -7, 0));
- _offsets.push_back (Offset (_("UTC-6"), -6, 0));
- _offsets.push_back (Offset (_("UTC-5"), -5, 0));
- _offsets.push_back (Offset (_("UTC-4:30"), -4, 30));
- _offsets.push_back (Offset (_("UTC-4"), -4, 0));
- _offsets.push_back (Offset (_("UTC-3:30"), -3, 30));
- _offsets.push_back (Offset (_("UTC-3"), -3, 0));
- _offsets.push_back (Offset (_("UTC-2"), -2, 0));
- _offsets.push_back (Offset (_("UTC-1"), -1, 0));
- _offsets.push_back (Offset (_("UTC") , 0, 0));
- _offsets.push_back (Offset (_("UTC+1"), 1, 0));
- _offsets.push_back (Offset (_("UTC+2"), 2, 0));
- _offsets.push_back (Offset (_("UTC+3"), 3, 0));
- _offsets.push_back (Offset (_("UTC+4"), 4, 0));
- _offsets.push_back (Offset (_("UTC+5"), 5, 0));
- _offsets.push_back (Offset (_("UTC+5:30"), 5, 30));
- _offsets.push_back (Offset (_("UTC+6"), 6, 0));
- _offsets.push_back (Offset (_("UTC+7"), 7, 0));
- _offsets.push_back (Offset (_("UTC+8"), 8, 0));
- _offsets.push_back (Offset (_("UTC+9"), 9, 0));
- _offsets.push_back (Offset (_("UTC+9:30"), 9, 30));
- _offsets.push_back (Offset (_("UTC+10"), 10, 0));
- _offsets.push_back (Offset (_("UTC+11"), 11, 0));
- _offsets.push_back (Offset (_("UTC+12"), 12, 0));
-
- /* Default to UTC */
- size_t sel = 13;
+ size_t sel = all_offsets (_offsets);
+
for (size_t i = 0; i < _offsets.size(); ++i) {
_utc_offset->Append (_offsets[i].name);
if (_offsets[i].hour == utc_offset_hour && _offsets[i].minute == utc_offset_minute) {
std::vector<std::string> _emails;
wxChoice* _utc_offset;
- struct Offset
- {
- Offset (wxString n, int h, int m)
- : name (n)
- , hour (h)
- , minute (m)
- {}
-
- wxString name;
- int hour;
- int minute;
- };
-
std::vector<Offset> _offsets;
};
--- /dev/null
+/*
+ Copyright (C) 2012-2020 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 "dkdm_dialog.h"
+#include "wx_util.h"
+#include "recipients_panel.h"
+#include "kdm_timing_panel.h"
+#include "dkdm_output_panel.h"
+#include "kdm_cpl_panel.h"
+#include "confirm_kdm_email_dialog.h"
+#include "static_text.h"
+#include "dcpomatic_button.h"
+#include "lib/film.h"
+#include "lib/screen.h"
+#include "lib/screen_with_kdm.h"
+#include "lib/job_manager.h"
+#include "lib/cinema_kdms.h"
+#include "lib/config.h"
+#include "lib/cinema.h"
+#include "lib/dkdm_recipient.h"
+#include <libcxml/cxml.h>
+#include <dcp/exceptions.h>
+#include <wx/treectrl.h>
+#include <wx/listctrl.h>
+#include <iostream>
+
+using std::string;
+using std::exception;
+using std::map;
+using std::list;
+using std::pair;
+using std::cout;
+using std::vector;
+using std::make_pair;
+using std::runtime_error;
+using boost::shared_ptr;
+using boost::bind;
+using boost::optional;
+
+DKDMDialog::DKDMDialog (wxWindow* parent, shared_ptr<const Film> film)
+ : wxDialog (parent, wxID_ANY, _("Make DKDMs"))
+ , _film (film)
+{
+ /* Main sizers */
+ wxBoxSizer* horizontal = new wxBoxSizer (wxHORIZONTAL);
+ wxBoxSizer* left = new wxBoxSizer (wxVERTICAL);
+ wxBoxSizer* right = new wxBoxSizer (wxVERTICAL);
+
+ horizontal->Add (left, 1, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_X_GAP * 4);
+ horizontal->Add (right, 1, wxEXPAND);
+
+ /* Font for sub-headings */
+ wxFont subheading_font (*wxNORMAL_FONT);
+ subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
+
+ /* Sub-heading: Recipients */
+ wxStaticText* h = new StaticText (this, _("Recipients"));
+ h->SetFont (subheading_font);
+ left->Add (h, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM, DCPOMATIC_SIZER_Y_GAP);
+ _recipients = new RecipientsPanel (this);
+ left->Add (_recipients, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_SIZER_Y_GAP);
+
+ /* Sub-heading: Timing */
+ /// TRANSLATORS: translate the word "Timing" here; do not include the "KDM|" prefix
+ h = new StaticText (this, S_("KDM|Timing"));
+ h->SetFont (subheading_font);
+ right->Add (h, 0, wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_Y_GAP * 2);
+ _timing = new KDMTimingPanel (this);
+ right->Add (_timing);
+
+ /* Sub-heading: CPL */
+ h = new StaticText (this, _("CPL"));
+ h->SetFont (subheading_font);
+ right->Add (h, 0, wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_Y_GAP * 2);
+
+ vector<CPLSummary> cpls;
+ BOOST_FOREACH (CPLSummary const & i, film->cpls()) {
+ if (i.encrypted) {
+ cpls.push_back (i);
+ }
+ }
+
+ _cpl = new KDMCPLPanel (this, cpls);
+ right->Add (_cpl, 0, wxEXPAND);
+
+ /* Sub-heading: Output */
+ h = new StaticText (this, _("Output"));
+ h->SetFont (subheading_font);
+ right->Add (h, 0, wxALIGN_CENTER_VERTICAL | wxTOP, DCPOMATIC_SIZER_Y_GAP * 2);
+ _output = new DKDMOutputPanel (this);
+ right->Add (_output, 0, wxEXPAND | wxTOP, DCPOMATIC_SIZER_GAP);
+
+ _make = new Button (this, _("Make KDMs"));
+ right->Add (_make, 0, wxTOP | wxBOTTOM, DCPOMATIC_SIZER_GAP);
+
+ /* Make an overall sizer to get a nice border */
+
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ overall_sizer->Add (horizontal, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, DCPOMATIC_DIALOG_BORDER);
+
+ /* Bind */
+
+ _recipients->RecipientsChanged.connect (boost::bind (&DKDMDialog::setup_sensitivity, this));
+ _timing->TimingChanged.connect (boost::bind (&DKDMDialog::setup_sensitivity, this));
+ _make->Bind (wxEVT_BUTTON, boost::bind (&DKDMDialog::make_clicked, this));
+
+ setup_sensitivity ();
+
+ SetSizer (overall_sizer);
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
+}
+
+void
+DKDMDialog::setup_sensitivity ()
+{
+ _recipients->setup_sensitivity ();
+ _output->setup_sensitivity ();
+ _make->Enable (!_recipients->recipients().empty() && _timing->valid() && _cpl->has_selected());
+}
+
+bool
+DKDMDialog::confirm_overwrite (boost::filesystem::path path)
+{
+ return confirm_dialog (
+ this,
+ wxString::Format (_("File %s already exists. Do you want to overwrite it?"), std_to_wx(path.string()).data())
+ );
+}
+
+void
+DKDMDialog::make_clicked ()
+{
+ shared_ptr<const Film> film = _film.lock ();
+ DCPOMATIC_ASSERT (film);
+
+ vector<RecipientWithDKDM> recipient_kdms;
+ try {
+ BOOST_FOREACH (shared_ptr<DKDMRecipient> i, _recipients->recipients()) {
+ if (i->recipient) {
+ dcp::EncryptedKDM const kdm = film->make_kdm (
+ i->recipient.get(),
+ vector<std::string>(),
+ _cpl->cpl(),
+ dcp::LocalTime(_timing->from(), i->utc_offset_hour(), i->utc_offset_minute()),
+ dcp::LocalTime(_timing->until(), i->utc_offset_hour(), i->utc_offset_minute()),
+ dcp::MODIFIED_TRANSITIONAL_1,
+ true,
+ optional<int>()
+ );
+
+ recipient_kdms.push_back (RecipientWithDKDM(i, kdm));
+ }
+ }
+ } catch (dcp::BadKDMDateError& e) {
+ if (e.starts_too_early()) {
+ error_dialog (this, _("The KDM start period is before (or close to) the start of the signing certificate's validity period. Use a later start time for this KDM."));
+ } else {
+ error_dialog (this, _("The KDM end period is after (or close to) the end of the signing certficates' validity period. Either use an earlier end time for this KDM or re-create your signing certificates in the DCP-o-matic preferences window."));
+ }
+ return;
+ } catch (runtime_error& e) {
+ error_dialog (this, std_to_wx(e.what()));
+ return;
+ }
+
+ pair<shared_ptr<Job>, int> result = _output->make (recipient_kdms, film->name(), _timing, bind(&DKDMDialog::confirm_overwrite, this, _1));
+ if (result.first) {
+ JobManager::instance()->add (result.first);
+ }
+
+ if (result.second > 0) {
+ /* XXX: proper plural form support in wxWidgets? */
+ wxString s = result.second == 1 ? _("%d DKDM written to %s") : _("%d DKDMs written to %s");
+ message_dialog (
+ this,
+ wxString::Format (s, result.second, std_to_wx(_output->directory().string()).data())
+ );
+ }
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2020 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 "wx_util.h"
+#include <dcp/types.h>
+#include <wx/wx.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <map>
+
+class RecipientsPanel;
+class Film;
+class KDMTimingPanel;
+class DKDMOutputPanel;
+class KDMCPLPanel;
+struct CPLSummary;
+
+class DKDMDialog : public wxDialog
+{
+public:
+ DKDMDialog (wxWindow *, boost::shared_ptr<const Film> film);
+
+private:
+ void setup_sensitivity ();
+ void make_clicked ();
+ bool confirm_overwrite (boost::filesystem::path path);
+
+ boost::weak_ptr<const Film> _film;
+ RecipientsPanel* _recipients;
+ KDMTimingPanel* _timing;
+ KDMCPLPanel* _cpl;
+ DKDMOutputPanel* _output;
+ wxButton* _make;
+};
--- /dev/null
+/*
+ Copyright (C) 2015-2020 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 "lib/config.h"
+#include "lib/cinema.h"
+#include "lib/cinema_kdms.h"
+#include "lib/send_dkdm_email_job.h"
+#include "lib/recipient_with_dkdm.h"
+#include "dkdm_output_panel.h"
+#include "kdm_timing_panel.h"
+#include "confirm_kdm_email_dialog.h"
+#include "wx_util.h"
+#include "kdm_advanced_dialog.h"
+#include "name_format_editor.h"
+#include "check_box.h"
+#include "dcpomatic_button.h"
+#include <dcp/exceptions.h>
+#include <dcp/types.h>
+#ifdef DCPOMATIC_USE_OWN_PICKER
+#include "dir_picker_ctrl.h"
+#else
+#include <wx/filepicker.h>
+#endif
+#include <wx/stdpaths.h>
+
+using std::pair;
+using std::string;
+using std::list;
+using std::exception;
+using std::make_pair;
+using std::vector;
+using boost::shared_ptr;
+using boost::function;
+
+DKDMOutputPanel::DKDMOutputPanel (wxWindow* parent)
+ : wxPanel (parent, wxID_ANY)
+{
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, 0);
+
+ add_label_to_sizer (table, this, _("KDM type"), true);
+
+ add_label_to_sizer (table, this, _("Filename format"), true, 0, wxALIGN_TOP | wxTOP | wxLEFT | wxRIGHT);
+ dcp::NameFormat::Map titles;
+ titles['f'] = wx_to_std (_("film name"));
+ titles['b'] = wx_to_std (_("from date/time"));
+ titles['e'] = wx_to_std (_("to date/time"));
+ dcp::NameFormat::Map ex;
+ ex['s'] = "Screen 1";
+ ex['b'] = "2012/03/15 12:30";
+ ex['e'] = "2012/03/22 02:30";
+ _filename_format = new NameFormatEditor (this, Config::instance()->dkdm_filename_format(), titles, ex, ".xml");
+ table->Add (_filename_format->panel(), 1, wxEXPAND);
+
+ _write_to = new CheckBox (this, _("Write to"));
+ table->Add (_write_to, 1, wxEXPAND);
+
+#ifdef DCPOMATIC_USE_OWN_PICKER
+ _folder = new DirPickerCtrl (this);
+#else
+ _folder = new wxDirPickerCtrl (this, wxID_ANY, wxEmptyString, wxDirSelectorPromptStr, wxDefaultPosition, wxSize (300, -1));
+#endif
+
+ boost::optional<boost::filesystem::path> path = Config::instance()->default_dkdm_directory ();
+ if (path) {
+ _folder->SetPath (std_to_wx (path->string ()));
+ } else {
+ _folder->SetPath (wxStandardPaths::Get().GetDocumentsDir());
+ }
+
+ table->Add (_folder, 1, wxEXPAND);
+
+ _email = new CheckBox (this, _("Send by email"));
+ table->Add (_email, 1, wxEXPAND);
+ table->AddSpacer (0);
+
+ _write_to->SetValue (true);
+
+ _write_to->Bind (wxEVT_CHECKBOX, boost::bind (&DKDMOutputPanel::setup_sensitivity, this));
+ _email->Bind (wxEVT_CHECKBOX, boost::bind (&DKDMOutputPanel::setup_sensitivity, this));
+
+ SetSizer (table);
+}
+
+
+void
+DKDMOutputPanel::setup_sensitivity ()
+{
+ _folder->Enable (_write_to->GetValue());
+}
+
+
+pair<shared_ptr<Job>, int>
+DKDMOutputPanel::make (
+ vector<RecipientWithDKDM> recipient_kdms, string name, KDMTimingPanel* timing, function<bool (boost::filesystem::path)> confirm_overwrite
+ )
+{
+ /* Decide whether to proceed */
+
+ bool proceed = true;
+
+ if (_email->GetValue ()) {
+
+ if (Config::instance()->mail_server().empty()) {
+ proceed = false;
+ error_dialog (this, _("You must set up a mail server in Preferences before you can send emails."));
+ }
+
+ bool recipients_with_no_email = false;
+ BOOST_FOREACH (RecipientWithDKDM const& i, recipient_kdms) {
+ if (i.recipient->emails.empty ()) {
+ recipients_with_no_email = true;
+ }
+ }
+
+ if (proceed && recipients_with_no_email && !confirm_dialog (
+ this,
+ _("You have selected some recipients that have no configured email address. Do you want to continue?")
+ )) {
+ proceed = false;
+ }
+
+ if (proceed && Config::instance()->confirm_kdm_email()) {
+ list<string> emails;
+ BOOST_FOREACH (RecipientWithDKDM i, recipient_kdms) {
+ BOOST_FOREACH (string j, i.recipient->emails) {
+ emails.push_back (j);
+ }
+ }
+
+ if (!emails.empty()) {
+ ConfirmKDMEmailDialog* d = new ConfirmKDMEmailDialog (this, emails);
+ if (d->ShowModal() == wxID_CANCEL) {
+ proceed = false;
+ }
+ }
+ }
+ }
+
+ if (!proceed) {
+ return make_pair (shared_ptr<Job>(), 0);
+ }
+
+ Config::instance()->set_dkdm_filename_format (_filename_format->get());
+
+ int written = 0;
+ shared_ptr<Job> job;
+
+ try {
+ dcp::NameFormat::Map name_values;
+ name_values['f'] = name;
+ name_values['b'] = dcp::LocalTime(timing->from()).date() + " " + dcp::LocalTime(timing->from()).time_of_day(false, false);
+ name_values['e'] = dcp::LocalTime(timing->until()).date() + " " + dcp::LocalTime(timing->until()).time_of_day(false, false);
+
+ written = RecipientWithDKDM::write_files (
+ recipient_kdms,
+ directory(),
+ _filename_format->get(),
+ name_values,
+ confirm_overwrite
+ );
+
+ if (_email->GetValue()) {
+ job.reset (
+ new SendDKDMEmailJob (
+ recipient_kdms,
+ _filename_format->get(),
+ name_values,
+ name
+ )
+ );
+ }
+
+ } 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."));
+ }
+
+ return make_pair (job, written);
+}
+
+
+boost::filesystem::path
+DKDMOutputPanel::directory () const
+{
+ return wx_to_std (_folder->GetPath());
+}
+
--- /dev/null
+/*
+ Copyright (C) 2015-2020 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 "wx_util.h"
+#include "name_format_editor.h"
+#include "lib/screen_with_kdm.h"
+#include "lib/dkdm_recipient.h"
+#include "lib/recipient_with_dkdm.h"
+#include <dcp/types.h>
+#include <wx/wx.h>
+#include <boost/filesystem.hpp>
+
+class wxRadioButton;
+class wxDirPickerCtrl;
+class DirPickerCtrl;
+class KDMTimingPanel;
+class Job;
+class Log;
+
+class DKDMOutputPanel : public wxPanel
+{
+public:
+ DKDMOutputPanel (wxWindow* parent);
+
+ void setup_sensitivity ();
+
+ boost::filesystem::path directory () const;
+
+ std::pair<boost::shared_ptr<Job>, int> make (
+ std::vector<RecipientWithDKDM> recipient_kdms,
+ std::string name,
+ KDMTimingPanel* timing,
+ boost::function<bool (boost::filesystem::path)> confirm_overwrite
+ );
+
+private:
+ NameFormatEditor* _filename_format;
+ wxCheckBox* _write_to;
+#ifdef DCPOMATIC_USE_OWN_PICKER
+ DirPickerCtrl* _folder;
+#else
+ wxDirPickerCtrl* _folder;
+#endif
+ wxCheckBox* _email;
+};
--- /dev/null
+/*
+ Copyright (C) 2012-2020 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 "recipient_dialog.h"
+#include "wx_util.h"
+#include "static_text.h"
+#include "lib/dcpomatic_assert.h"
+#include "lib/util.h"
+#include <dcp/exceptions.h>
+#include <dcp/certificate_chain.h>
+#include <boost/foreach.hpp>
+
+using std::string;
+using std::vector;
+using std::copy;
+using std::back_inserter;
+using std::list;
+using std::cout;
+using boost::bind;
+using boost::optional;
+
+static string
+column (string s)
+{
+ return s;
+}
+
+RecipientDialog::RecipientDialog (
+ wxWindow* parent,
+ wxString title,
+ string name,
+ list<string> emails,
+ string notes,
+ int utc_offset_hour,
+ int utc_offset_minute,
+ optional<dcp::Certificate> recipient)
+ : wxDialog (parent, wxID_ANY, title)
+{
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ SetSizer (overall_sizer);
+
+ _sizer = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ int r = 0;
+
+ add_label_to_sizer (_sizer, this, _("Name"), true, wxGBPosition (r, 0));
+ _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (500, -1));
+ _sizer->Add (_name, wxGBPosition (r, 1));
+ ++r;
+
+ add_label_to_sizer (_sizer, this, _("UTC offset (time zone)"), true, wxGBPosition(r, 0));
+ _utc_offset = new wxChoice (this, wxID_ANY);
+ _sizer->Add (_utc_offset, wxGBPosition (r, 1));
+ ++r;
+
+ add_label_to_sizer (_sizer, this, _("Notes"), true, wxGBPosition(r, 0));
+ _notes = new wxTextCtrl (this, wxID_ANY, std_to_wx (notes), wxDefaultPosition, wxSize(500, -1));
+ _sizer->Add (_notes, wxGBPosition (r, 1));
+ ++r;
+
+ add_label_to_sizer (_sizer, this, _("Email addresses for DKDM delivery"), false, wxGBPosition(r, 0), wxGBSpan(1, 2));
+ ++r;
+
+ copy (emails.begin(), emails.end(), back_inserter(_emails));
+
+ vector<EditableListColumn> columns;
+ columns.push_back (EditableListColumn(wx_to_std(_("Address"))));
+ _email_list = new EditableList<string, EmailDialog> (
+ this, columns, bind(&RecipientDialog::get_emails, this), bind(&RecipientDialog::set_emails, this, _1), bind(&column, _1)
+ );
+
+ _sizer->Add (_email_list, wxGBPosition(r, 0), wxGBSpan(1, 2), wxEXPAND);
+ ++r;
+
+ wxClientDC dc (this);
+ wxFont font = _name->GetFont ();
+ font.SetFamily (wxFONTFAMILY_TELETYPE);
+ dc.SetFont (font);
+ wxSize size = dc.GetTextExtent (wxT ("1234567890123456789012345678"));
+ size.SetHeight (-1);
+
+ add_label_to_sizer (_sizer, this, _("Recipient certificate"), true, wxGBPosition (r, 0));
+ wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ _recipient_thumbprint = new StaticText (this, wxT(""), wxDefaultPosition, size);
+ _recipient_thumbprint->SetFont (font);
+ set_recipient (recipient);
+ _get_recipient = new Button (this, _("Get from file..."));
+ s->Add (_recipient_thumbprint, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
+ s->Add (_get_recipient, 0, wxLEFT | wxRIGHT | wxEXPAND, DCPOMATIC_SIZER_X_GAP);
+ _sizer->Add (s, wxGBPosition(r, 1));
+ ++r;
+
+ overall_sizer->Add (_sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ size_t sel = all_offsets (_offsets);
+
+ for (size_t i = 0; i < _offsets.size(); ++i) {
+ _utc_offset->Append (_offsets[i].name);
+ if (_offsets[i].hour == utc_offset_hour && _offsets[i].minute == utc_offset_minute) {
+ sel = i;
+ }
+ }
+
+ _utc_offset->SetSelection (sel);
+
+ _get_recipient->Bind (wxEVT_BUTTON, boost::bind (&RecipientDialog::get_recipient_from_file, this));
+
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
+}
+
+string
+RecipientDialog::name () const
+{
+ return wx_to_std (_name->GetValue());
+}
+
+void
+RecipientDialog::set_emails (vector<string> e)
+{
+ _emails = e;
+}
+
+vector<string>
+RecipientDialog::get_emails () const
+{
+ return _emails;
+}
+
+list<string>
+RecipientDialog::emails () const
+{
+ list<string> e;
+ copy (_emails.begin(), _emails.end(), back_inserter(e));
+ return e;
+}
+
+int
+RecipientDialog::utc_offset_hour () const
+{
+ int const sel = _utc_offset->GetSelection();
+ if (sel < 0 || sel > int(_offsets.size())) {
+ return 0;
+ }
+
+ return _offsets[sel].hour;
+}
+
+int
+RecipientDialog::utc_offset_minute () const
+{
+ int const sel = _utc_offset->GetSelection();
+ if (sel < 0 || sel > int(_offsets.size())) {
+ return 0;
+ }
+
+ return _offsets[sel].minute;
+}
+
+string
+RecipientDialog::notes () const
+{
+ return wx_to_std (_notes->GetValue ());
+}
+
+
+void
+RecipientDialog::load_recipient (boost::filesystem::path file)
+{
+ try {
+ /* Load this as a chain, in case it is one, and then pick the leaf certificate */
+ dcp::CertificateChain c (dcp::file_to_string(file));
+ if (c.unordered().empty()) {
+ error_dialog (this, _("Could not read certificate file."));
+ return;
+ }
+ set_recipient (c.leaf());
+ } catch (dcp::MiscError& e) {
+ error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
+ }
+}
+
+
+void
+RecipientDialog::get_recipient_from_file ()
+{
+ wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
+ if (d->ShowModal() == wxID_OK) {
+ load_recipient (boost::filesystem::path(wx_to_std(d->GetPath())));
+ }
+ d->Destroy ();
+}
+
+
+void
+RecipientDialog::set_recipient (optional<dcp::Certificate> r)
+{
+ _recipient = r;
+
+ if (_recipient) {
+ _recipient_thumbprint->SetLabel (std_to_wx(_recipient->thumbprint()));
+ _sizer->Layout ();
+ }
+}
+
+
+optional<dcp::Certificate>
+RecipientDialog::recipient () const
+{
+ return _recipient;
+}
+
+
--- /dev/null
+/*
+ Copyright (C) 2012-2020 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 "table_dialog.h"
+#include "editable_list.h"
+#include "email_dialog.h"
+#include <dcp/certificate.h>
+#include <wx/wx.h>
+#include <list>
+#include <vector>
+
+class RecipientDialog : public wxDialog
+{
+public:
+ RecipientDialog (
+ wxWindow *,
+ wxString,
+ std::string name = "",
+ std::list<std::string> emails = std::list<std::string>(),
+ std::string notes = "",
+ int utc_offset_hour = 0,
+ int utc_offset_minute = 0,
+ boost::optional<dcp::Certificate> c = boost::optional<dcp::Certificate> ()
+ );
+
+ std::string name () const;
+ std::list<std::string> emails () const;
+ std::string notes () const;
+ int utc_offset_hour () const;
+ int utc_offset_minute () const;
+ boost::optional<dcp::Certificate> recipient () const;
+
+private:
+ std::vector<std::string> get_emails () const;
+ void set_emails (std::vector<std::string>);
+ void set_recipient (boost::optional<dcp::Certificate>);
+ void get_recipient_from_file ();
+ void load_recipient (boost::filesystem::path);
+
+ wxGridBagSizer* _sizer;
+ wxTextCtrl* _name;
+ wxTextCtrl* _notes;
+ EditableList<std::string, EmailDialog>* _email_list;
+ std::vector<std::string> _emails;
+ wxChoice* _utc_offset;
+
+ std::vector<Offset> _offsets;
+ boost::optional<dcp::Certificate> _recipient;
+ wxStaticText* _recipient_thumbprint;
+ wxButton* _get_recipient;
+};
--- /dev/null
+/*
+ Copyright (C) 2015-2020 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 "recipients_panel.h"
+#include "recipient_dialog.h"
+#include "wx_util.h"
+#include "dcpomatic_button.h"
+#include "lib/config.h"
+#include "lib/dkdm_recipient.h"
+#include <boost/foreach.hpp>
+
+using std::list;
+using std::pair;
+using std::cout;
+using std::map;
+using std::string;
+using std::make_pair;
+using boost::shared_ptr;
+using boost::optional;
+using namespace dcpomatic;
+
+RecipientsPanel::RecipientsPanel (wxWindow* parent)
+ : wxPanel (parent, wxID_ANY)
+ , _ignore_selection_change (false)
+{
+ wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+
+ _search = new wxSearchCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (200, -1));
+ _search->ShowCancelButton (true);
+ sizer->Add (_search, 0, wxBOTTOM, DCPOMATIC_SIZER_GAP);
+
+ wxBoxSizer* targets = new wxBoxSizer (wxHORIZONTAL);
+ _targets = new wxTreeCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT | wxTR_MULTIPLE | wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT);
+ targets->Add (_targets, 1, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_GAP);
+
+ add_recipients_from_config ();
+
+ wxBoxSizer* target_buttons = new wxBoxSizer (wxVERTICAL);
+
+ _add_recipient = new Button (this, _("Add Recipient..."));
+ target_buttons->Add (_add_recipient, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+ _edit_recipient = new Button (this, _("Edit Recipient..."));
+ target_buttons->Add (_edit_recipient, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+ _remove_recipient = new Button (this, _("Remove Recipient"));
+ target_buttons->Add (_remove_recipient, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+
+ targets->Add (target_buttons, 0, 0);
+
+ sizer->Add (targets, 1, wxEXPAND);
+
+ _search->Bind (wxEVT_TEXT, boost::bind (&RecipientsPanel::search_changed, this));
+ _targets->Bind (wxEVT_TREE_SEL_CHANGED, &RecipientsPanel::selection_changed_shim, this);
+
+ _add_recipient->Bind (wxEVT_BUTTON, boost::bind (&RecipientsPanel::add_recipient_clicked, this));
+ _edit_recipient->Bind (wxEVT_BUTTON, boost::bind (&RecipientsPanel::edit_recipient_clicked, this));
+ _remove_recipient->Bind (wxEVT_BUTTON, boost::bind (&RecipientsPanel::remove_recipient_clicked, this));
+
+ SetSizer (sizer);
+}
+
+
+RecipientsPanel::~RecipientsPanel ()
+{
+ _targets->Unbind (wxEVT_TREE_SEL_CHANGED, &RecipientsPanel::selection_changed_shim, this);
+}
+
+
+void
+RecipientsPanel::setup_sensitivity ()
+{
+ bool const s = _selected.size() == 1;
+
+ _edit_recipient->Enable (s);
+ _remove_recipient->Enable (_selected.size() >= 1);
+}
+
+
+void
+RecipientsPanel::add_recipient (shared_ptr<DKDMRecipient> r)
+{
+ string search = wx_to_std (_search->GetValue());
+ transform (search.begin(), search.end(), search.begin(), ::tolower);
+
+ if (!search.empty()) {
+ string name = r->name;
+ transform (name.begin(), name.end(), name.begin(), ::tolower);
+ if (name.find(search) == string::npos) {
+ return;
+ }
+ }
+
+ _recipients[_targets->AppendItem(_root, std_to_wx(r->name))] = r;
+
+ _targets->SortChildren (_root);
+}
+
+
+void
+RecipientsPanel::add_recipient_clicked ()
+{
+ RecipientDialog* d = new RecipientDialog (GetParent(), _("Add Recipient"));
+ if (d->ShowModal() == wxID_OK) {
+ shared_ptr<DKDMRecipient> r (new DKDMRecipient(d->name(), d->notes(), d->recipient(), d->emails(), d->utc_offset_hour(), d->utc_offset_minute()));
+ Config::instance()->add_dkdm_recipient (r);
+ add_recipient (r);
+ }
+
+ d->Destroy ();
+}
+
+
+void
+RecipientsPanel::edit_recipient_clicked ()
+{
+ if (_selected.size() != 1) {
+ return;
+ }
+
+ pair<wxTreeItemId, shared_ptr<DKDMRecipient> > r = *_selected.begin();
+
+ RecipientDialog* d = new RecipientDialog (
+ GetParent(), _("Edit recipient"), r.second->name, r.second->emails, r.second->notes, r.second->utc_offset_hour(), r.second->utc_offset_minute(), r.second->recipient
+ );
+
+ if (d->ShowModal() == wxID_OK) {
+ r.second->name = d->name ();
+ r.second->emails = d->emails ();
+ r.second->notes = d->notes ();
+ r.second->set_utc_offset_hour (d->utc_offset_hour());
+ r.second->set_utc_offset_minute (d->utc_offset_minute());
+ r.second->recipient = d->recipient ();
+ _targets->SetItemText (r.first, std_to_wx(d->name()));
+ Config::instance()->changed (Config::DKDM_RECIPIENTS);
+ }
+
+ d->Destroy ();
+}
+
+
+void
+RecipientsPanel::remove_recipient_clicked ()
+{
+ for (RecipientMap::iterator i = _selected.begin(); i != _selected.end(); ++i) {
+ Config::instance()->remove_dkdm_recipient (i->second);
+ _targets->Delete (i->first);
+ }
+
+ selection_changed ();
+}
+
+
+list<shared_ptr<DKDMRecipient> >
+RecipientsPanel::recipients () const
+{
+ list<shared_ptr<DKDMRecipient> > s;
+
+ for (RecipientMap::const_iterator i = _selected.begin(); i != _selected.end(); ++i) {
+ s.push_back (i->second);
+ }
+
+ s.sort ();
+ s.unique ();
+
+ return s;
+}
+
+
+void
+RecipientsPanel::selection_changed_shim (wxTreeEvent &)
+{
+ selection_changed ();
+}
+
+
+void
+RecipientsPanel::selection_changed ()
+{
+ if (_ignore_selection_change) {
+ return;
+ }
+
+ wxArrayTreeItemIds s;
+ _targets->GetSelections (s);
+
+ _selected.clear ();
+
+ for (size_t i = 0; i < s.GetCount(); ++i) {
+ RecipientMap::const_iterator j = _recipients.find (s[i]);
+ if (j != _recipients.end()) {
+ _selected[j->first] = j->second;
+ }
+ }
+
+ setup_sensitivity ();
+ RecipientsChanged ();
+}
+
+
+void
+RecipientsPanel::add_recipients_from_config ()
+{
+ _root = _targets->AddRoot ("Foo");
+
+ BOOST_FOREACH (shared_ptr<DKDMRecipient> i, Config::instance()->dkdm_recipients()) {
+ add_recipient (i);
+ }
+}
+
+
+void
+RecipientsPanel::search_changed ()
+{
+ _targets->DeleteAllItems ();
+ _recipients.clear ();
+
+ add_recipients_from_config ();
+
+ _ignore_selection_change = true;
+
+ for (RecipientMap::const_iterator i = _selected.begin(); i != _selected.end(); ++i) {
+ RecipientMap::const_iterator j = _recipients.begin ();
+ while (j != _recipients.end() && j->second != i->second) {
+ ++j;
+ }
+
+ if (j != _recipients.end()) {
+ _targets->SelectItem (j->first);
+ }
+ }
+
+ _ignore_selection_change = false;
+}
--- /dev/null
+/*
+ Copyright (C) 2015-2020 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 <wx/wx.h>
+#include <wx/srchctrl.h>
+#include <wx/treectrl.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/signals2.hpp>
+#include <list>
+#include <map>
+
+class DKDMRecipient;
+
+class RecipientsPanel : public wxPanel
+{
+public:
+ explicit RecipientsPanel (wxWindow* parent);
+ ~RecipientsPanel ();
+
+ std::list<boost::shared_ptr<DKDMRecipient> > recipients () const;
+ void setup_sensitivity ();
+
+ boost::signals2::signal<void ()> RecipientsChanged;
+
+private:
+ void add_recipients_from_config ();
+ void add_recipient (boost::shared_ptr<DKDMRecipient>);
+ void add_recipient_clicked ();
+ void edit_recipient_clicked ();
+ void remove_recipient_clicked ();
+ void selection_changed_shim (wxTreeEvent &);
+ void selection_changed ();
+ void search_changed ();
+
+ wxSearchCtrl* _search;
+ wxTreeCtrl* _targets;
+ wxButton* _add_recipient;
+ wxButton* _edit_recipient;
+ wxButton* _remove_recipient;
+ wxTreeItemId _root;
+
+ typedef std::map<wxTreeItemId, boost::shared_ptr<DKDMRecipient> > RecipientMap;
+
+ RecipientMap _recipients;
+ RecipientMap _selected;
+
+ bool _ignore_selection_change;
+};
dcp_panel.cc
dcpomatic_button.cc
disk_warning_dialog.cc
+ dkdm_dialog.cc
+ dkdm_output_panel.cc
drive_wipe_warning_dialog.cc
email_dialog.cc
image_sequence_dialog.cc
question_dialog.cc
rating_dialog.cc
qube_certificate_panel.cc
+ recipient_dialog.cc
+ recipients_panel.cc
recreate_chain_dialog.cc
repeat_dialog.cc
report_problem_dialog.cc
/*
- Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
return ok;
}
+
+
+size_t
+all_offsets (vector<Offset>& offsets)
+{
+ size_t utc_index = 0;
+
+ offsets.push_back (Offset(_("UTC-11"), -11, 0));
+ offsets.push_back (Offset(_("UTC-10"), -10, 0));
+ offsets.push_back (Offset(_("UTC-9"), -9, 0));
+ offsets.push_back (Offset(_("UTC-8"), -8, 0));
+ offsets.push_back (Offset(_("UTC-7"), -7, 0));
+ offsets.push_back (Offset(_("UTC-6"), -6, 0));
+ offsets.push_back (Offset(_("UTC-5"), -5, 0));
+ offsets.push_back (Offset(_("UTC-4:30"), -4, 30));
+ offsets.push_back (Offset(_("UTC-4"), -4, 0));
+ offsets.push_back (Offset(_("UTC-3:30"), -3, 30));
+ offsets.push_back (Offset(_("UTC-3"), -3, 0));
+ offsets.push_back (Offset(_("UTC-2"), -2, 0));
+ offsets.push_back (Offset(_("UTC-1"), -1, 0));
+ utc_index = offsets.size();
+ offsets.push_back (Offset(_("UTC") , 0, 0));
+ offsets.push_back (Offset(_("UTC+1"), 1, 0));
+ offsets.push_back (Offset(_("UTC+2"), 2, 0));
+ offsets.push_back (Offset(_("UTC+3"), 3, 0));
+ offsets.push_back (Offset(_("UTC+4"), 4, 0));
+ offsets.push_back (Offset(_("UTC+5"), 5, 0));
+ offsets.push_back (Offset(_("UTC+5:30"), 5, 30));
+ offsets.push_back (Offset(_("UTC+6"), 6, 0));
+ offsets.push_back (Offset(_("UTC+7"), 7, 0));
+ offsets.push_back (Offset(_("UTC+8"), 8, 0));
+ offsets.push_back (Offset(_("UTC+9"), 9, 0));
+ offsets.push_back (Offset(_("UTC+9:30"), 9, 30));
+ offsets.push_back (Offset(_("UTC+10"), 10, 0));
+ offsets.push_back (Offset(_("UTC+11"), 11, 0));
+ offsets.push_back (Offset(_("UTC+12"), 12, 0));
+
+ return utc_index;
+}
+
extern bool display_progress (wxString title, wxString task);
extern bool report_errors_from_last_job (wxWindow* parent);
+struct Offset
+{
+ Offset (wxString n, int h, int m)
+ : name (n)
+ , hour (h)
+ , minute (m)
+ {}
+
+ wxString name;
+ int hour;
+ int minute;
+};
+
+extern size_t all_offsets(std::vector<Offset> &);
+
extern void checked_set (FilePickerCtrl* widget, boost::filesystem::path value);
extern void checked_set (wxDirPickerCtrl* widget, boost::filesystem::path value);
extern void checked_set (wxSpinCtrl* widget, int value);