f6e0b2cfc60928d28c09cb6e483c4ee6852b6fed
[dcpomatic.git] / src / wx / recipient_dialog.cc
1 /*
2     Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "dcpomatic_button.h"
23 #include "download_certificate_dialog.h"
24 #include "recipient_dialog.h"
25 #include "static_text.h"
26 #include "table_dialog.h"
27 #include "wx_util.h"
28 #include "lib/compose.hpp"
29 #include "lib/util.h"
30 #include <dcp/exceptions.h>
31 #include <dcp/certificate_chain.h>
32 #include <dcp/warnings.h>
33 LIBDCP_DISABLE_WARNINGS
34 #include <wx/filepicker.h>
35 #include <wx/validate.h>
36 LIBDCP_ENABLE_WARNINGS
37 #include <iostream>
38
39
40 using std::cout;
41 using std::list;
42 using std::string;
43 using std::vector;
44 using boost::bind;
45 using boost::optional;
46 #if BOOST_VERSION >= 106100
47 using namespace boost::placeholders;
48 #endif
49
50
51 static string
52 column (string s)
53 {
54         return s;
55 }
56
57
58 RecipientDialog::RecipientDialog (
59         wxWindow* parent, wxString title, string name, string notes, list<string> emails, int utc_offset_hour, int utc_offset_minute, optional<dcp::Certificate> recipient
60         )
61         : wxDialog (parent, wxID_ANY, title)
62         , _recipient (recipient)
63 {
64         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
65         SetSizer (overall_sizer);
66
67         _sizer = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
68         int r = 0;
69
70         add_label_to_sizer (_sizer, this, _("Name"), true, wxGBPosition (r, 0));
71         _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (320, -1));
72         _sizer->Add (_name, wxGBPosition (r, 1));
73         ++r;
74
75         add_label_to_sizer (_sizer, this, _("Notes"), true, wxGBPosition (r, 0));
76         _notes = new wxTextCtrl (this, wxID_ANY, std_to_wx (notes), wxDefaultPosition, wxSize (320, -1));
77         _sizer->Add (_notes, wxGBPosition (r, 1));
78         ++r;
79
80         add_label_to_sizer (_sizer, this, _("UTC offset (time zone)"), true, wxGBPosition (r, 0));
81         _utc_offset = new wxChoice (this, wxID_ANY);
82         _sizer->Add (_utc_offset, wxGBPosition (r, 1));
83         ++r;
84
85         add_label_to_sizer (_sizer, this, _("Email addresses for KDM delivery"), false, wxGBPosition (r, 0), wxGBSpan (1, 2));
86         ++r;
87
88         copy (emails.begin(), emails.end(), back_inserter (_emails));
89
90         vector<EditableListColumn> columns;
91         columns.push_back (EditableListColumn(_("Address")));
92         _email_list = new EditableList<string, EmailDialog> (
93                 this, columns, bind(&RecipientDialog::get_emails, this), bind(&RecipientDialog::set_emails, this, _1), bind(&column, _1)
94                 );
95
96         _sizer->Add (_email_list, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND);
97         ++r;
98
99         wxClientDC dc (this);
100         auto font = _name->GetFont ();
101         font.SetFamily (wxFONTFAMILY_TELETYPE);
102         dc.SetFont (font);
103         auto size = dc.GetTextExtent(wxT("1234567890123456789012345678"));
104         size.SetHeight (-1);
105
106         add_label_to_sizer (_sizer, this, _("Recipient certificate"), true, wxGBPosition (r, 0));
107         auto s = new wxBoxSizer (wxHORIZONTAL);
108         _recipient_thumbprint = new StaticText (this, wxT (""), wxDefaultPosition, size);
109         _recipient_thumbprint->SetFont (font);
110         set_recipient (recipient);
111         _get_recipient_from_file = new Button (this, _("Get from file..."));
112         s->Add (_recipient_thumbprint, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
113         s->Add (_get_recipient_from_file, 0, wxLEFT | wxRIGHT | wxEXPAND, DCPOMATIC_SIZER_X_GAP);
114         _sizer->Add (s, wxGBPosition (r, 1));
115         ++r;
116
117         add_label_to_sizer (_sizer, this, _("Other trusted devices"), true, wxGBPosition (r, 0));
118         ++r;
119
120         _name->Bind (wxEVT_TEXT, boost::bind (&RecipientDialog::setup_sensitivity, this));
121         _get_recipient_from_file->Bind (wxEVT_BUTTON, boost::bind (&RecipientDialog::get_recipient_from_file, this));
122
123         overall_sizer->Add (_sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
124
125         auto buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
126         if (buttons) {
127                 overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
128         }
129
130         /* Default to UTC */
131         size_t sel = get_offsets (_offsets);
132         for (size_t i = 0; i < _offsets.size(); ++i) {
133                 _utc_offset->Append (_offsets[i].name);
134                 if (_offsets[i].hour == utc_offset_hour && _offsets[i].minute == utc_offset_minute) {
135                         sel = i;
136                 }
137         }
138
139         _utc_offset->SetSelection (sel);
140
141         overall_sizer->Layout ();
142         overall_sizer->SetSizeHints (this);
143
144         setup_sensitivity ();
145 }
146
147
148 string
149 RecipientDialog::name () const
150 {
151         return wx_to_std (_name->GetValue());
152 }
153
154
155 string
156 RecipientDialog::notes () const
157 {
158         return wx_to_std (_notes->GetValue());
159 }
160
161
162 optional<dcp::Certificate>
163 RecipientDialog::recipient () const
164 {
165         return _recipient;
166 }
167
168
169 void
170 RecipientDialog::load_recipient (boost::filesystem::path file)
171 {
172         try {
173                 /* Load this as a chain, in case it is one, and then pick the leaf certificate */
174                 dcp::CertificateChain c (dcp::file_to_string (file));
175                 if (c.unordered().empty()) {
176                         error_dialog (this, _("Could not read certificate file."));
177                         return;
178                 }
179                 set_recipient (c.leaf ());
180         } catch (dcp::MiscError& e) {
181                 error_dialog (this, _("Could not read certificate file."), std_to_wx(e.what()));
182         }
183 }
184
185
186 void
187 RecipientDialog::get_recipient_from_file ()
188 {
189         auto d = new wxFileDialog (this, _("Select Certificate File"));
190         if (d->ShowModal () == wxID_OK) {
191                 load_recipient (boost::filesystem::path (wx_to_std (d->GetPath ())));
192         }
193         d->Destroy ();
194
195         setup_sensitivity ();
196 }
197
198
199 void
200 RecipientDialog::setup_sensitivity ()
201 {
202         auto ok = dynamic_cast<wxButton*> (FindWindowById (wxID_OK, this));
203         if (ok) {
204                 ok->Enable (static_cast<bool>(_recipient) && !_name->GetValue().IsEmpty());
205         }
206 }
207
208
209 void
210 RecipientDialog::set_recipient (optional<dcp::Certificate> r)
211 {
212         _recipient = r;
213
214         if (_recipient) {
215                 _recipient_thumbprint->SetLabel (std_to_wx (_recipient->thumbprint ()));
216                 _sizer->Layout ();
217         }
218 }
219
220
221 vector<string>
222 RecipientDialog::get_emails () const
223 {
224         return _emails;
225 }
226
227
228 void
229 RecipientDialog::set_emails (vector<string> e)
230 {
231         _emails = e;
232 }
233
234
235 list<string>
236 RecipientDialog::emails () const
237 {
238         list<string> e;
239         copy (_emails.begin(), _emails.end(), back_inserter(e));
240         return e;
241 }
242
243
244 int
245 RecipientDialog::utc_offset_hour () const
246 {
247         int const sel = _utc_offset->GetSelection();
248         if (sel < 0 || sel > int (_offsets.size())) {
249                 return 0;
250         }
251
252         return _offsets[sel].hour;
253 }
254
255 int
256 RecipientDialog::utc_offset_minute () const
257 {
258         int const sel = _utc_offset->GetSelection();
259         if (sel < 0 || sel > int (_offsets.size())) {
260                 return 0;
261         }
262
263         return _offsets[sel].minute;
264 }
265
266