Supporters update.
[dcpomatic.git] / src / wx / content_advanced_dialog.cc
1 /*
2     Copyright (C) 2020-2021 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 "check_box.h"
23 #include "content_advanced_dialog.h"
24 #include "dcpomatic_button.h"
25 #include "filter_dialog.h"
26 #include "language_tag_widget.h"
27 #include "static_text.h"
28 #include "wx_util.h"
29 #include "lib/content.h"
30 #include "lib/dcp_content.h"
31 #include "lib/filter.h"
32 #include "lib/ffmpeg_content.h"
33 #include "lib/image_content.h"
34 #include "lib/scope_guard.h"
35 #include "lib/video_content.h"
36 #include <dcp/warnings.h>
37 LIBDCP_DISABLE_WARNINGS
38 #include <wx/gbsizer.h>
39 #include <wx/propgrid/property.h>
40 #include <wx/propgrid/props.h>
41 #include <wx/wx.h>
42 LIBDCP_ENABLE_WARNINGS
43 #include <boost/bind/bind.hpp>
44
45
46 using std::dynamic_pointer_cast;
47 using std::shared_ptr;
48 using std::string;
49 using std::vector;
50 using boost::bind;
51 using boost::optional;
52 #if BOOST_VERSION >= 106100
53 using namespace boost::placeholders;
54 #endif
55 using dcp::locale_convert;
56
57
58
59 ContentAdvancedDialog::ContentAdvancedDialog (wxWindow* parent, shared_ptr<Content> content)
60         : wxDialog (parent, wxID_ANY, _("Advanced content settings"))
61         , _content (content)
62 {
63         auto sizer = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
64
65         int r = 0;
66
67         wxClientDC dc (this);
68         auto size = dc.GetTextExtent (wxT ("A quite long name"));
69 #ifdef __WXGTK3__
70         size.SetWidth (size.GetWidth() + 64);
71 #endif
72         size.SetHeight (-1);
73
74         add_label_to_sizer (sizer, this, _("Video filters"), true, wxGBPosition(r, 0));
75         _filters = new StaticText (this, _("None"), wxDefaultPosition, size);
76         _filters_button = new Button (this, _("Edit..."));
77         auto filters = new wxBoxSizer (wxHORIZONTAL);
78         filters->Add (_filters, 1, wxALL | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
79         filters->Add (_filters_button, 0, wxALL, DCPOMATIC_SIZER_GAP);
80         sizer->Add (filters, wxGBPosition(r, 1), wxGBSpan(1, 2));
81         ++r;
82
83         wxStaticText* video_frame_rate_label;
84         if (_content->video) {
85                 video_frame_rate_label = add_label_to_sizer (sizer, this, _("Override detected video frame rate"), true, wxGBPosition(r, 0));
86         } else {
87                 video_frame_rate_label = add_label_to_sizer (sizer, this, _("Video frame rate that content was prepared for"), true, wxGBPosition(r, 0));
88         }
89         _video_frame_rate = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0, wxNumericPropertyValidator(wxNumericPropertyValidator::Float));
90         sizer->Add (_video_frame_rate, wxGBPosition(r, 1));
91         _set_video_frame_rate = new Button (this, _("Set"));
92         _set_video_frame_rate->Enable (false);
93         sizer->Add (_set_video_frame_rate, wxGBPosition(r, 2));
94         ++r;
95
96         /// TRANSLATORS: next to this control is a language selector, so together they will read, for example
97         /// "Video has burnt-in subtitles in the language fr-FR"
98         _burnt_subtitle = new CheckBox(this, _("Video has burnt-in subtitles in the language"));
99         sizer->Add (_burnt_subtitle, wxGBPosition(r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
100         _burnt_subtitle_language = new LanguageTagWidget (this, _("Language of burnt-in subtitles in this content"), content->video ? content->video->burnt_subtitle_language() : boost::none);
101         sizer->Add (_burnt_subtitle_language->sizer(), wxGBPosition(r, 1), wxGBSpan(1, 2), wxEXPAND);
102         ++r;
103
104         _ignore_video = new CheckBox(this, _("Ignore this content's video and use only audio, subtitles and closed captions"));
105         sizer->Add(_ignore_video, wxGBPosition(r, 0), wxGBSpan(1, 3));
106         ++r;
107
108         auto overall = new wxBoxSizer (wxVERTICAL);
109         overall->Add (sizer, 1, wxALL, DCPOMATIC_DIALOG_BORDER);
110         auto buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
111         if (buttons) {
112                 overall->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
113         }
114
115         SetSizerAndFit (overall);
116
117         _ignore_video->Enable(static_cast<bool>(_content->video));
118         _ignore_video->SetValue(_content->video ? !content->video->use() : false);
119
120         auto fcs = dynamic_pointer_cast<FFmpegContent>(content);
121         _filters_allowed = static_cast<bool>(fcs);
122         if (fcs) {
123                 _filters_list = fcs->filters();
124         }
125         setup_filters ();
126
127         bool const single_frame_image_content = dynamic_pointer_cast<const ImageContent>(_content) && _content->number_of_paths() == 1;
128         video_frame_rate_label->Enable (!single_frame_image_content);
129         _video_frame_rate->Enable (!single_frame_image_content);
130
131         auto vfr = _content->video_frame_rate ();
132         if (vfr) {
133                 _video_frame_rate->SetValue (std_to_wx(locale_convert<string>(*vfr)));
134         }
135
136         _burnt_subtitle->SetValue (_content->video && static_cast<bool>(_content->video->burnt_subtitle_language()));
137         _burnt_subtitle_language->set (_content->video ? _content->video->burnt_subtitle_language() : boost::none);
138
139         _filters_button->Bind (wxEVT_BUTTON, bind(&ContentAdvancedDialog::edit_filters, this));
140         _set_video_frame_rate->Bind (wxEVT_BUTTON, bind(&ContentAdvancedDialog::set_video_frame_rate, this));
141         _video_frame_rate->Bind (wxEVT_TEXT, boost::bind(&ContentAdvancedDialog::video_frame_rate_changed, this));
142         _burnt_subtitle->bind(&ContentAdvancedDialog::burnt_subtitle_changed, this);
143
144         setup_sensitivity ();
145 }
146
147
148 bool
149 ContentAdvancedDialog::ignore_video() const
150 {
151         return _ignore_video->GetValue();
152 }
153
154
155 void
156 ContentAdvancedDialog::setup_filters ()
157 {
158         if (!_filters_allowed) {
159                 checked_set (_filters, _("None"));
160                 _filters->Enable (false);
161                 _filters_button->Enable (false);
162                 return;
163         }
164
165         auto p = Filter::ffmpeg_string(_filters_list);
166         if (p.empty()) {
167                 checked_set (_filters, _("None"));
168         } else {
169                 if (p.length() > 25) {
170                         p = p.substr(0, 25) + "...";
171                 }
172                 checked_set (_filters, p);
173         }
174 }
175
176
177 void
178 ContentAdvancedDialog::edit_filters ()
179 {
180         if (!_filters_allowed) {
181                 return;
182         }
183
184         auto dialog = new FilterDialog(this, _filters_list);
185         ScopeGuard sg = [dialog]() { dialog->Destroy(); };
186
187         dialog->ActiveChanged.connect(bind(&ContentAdvancedDialog::filters_changed, this, _1));
188         dialog->ShowModal();
189 }
190
191
192 void
193 ContentAdvancedDialog::filters_changed (vector<Filter const *> filters)
194 {
195         _filters_list = filters;
196         setup_filters ();
197 }
198
199
200 optional<double>
201 ContentAdvancedDialog::video_frame_rate() const
202 {
203         if (_video_frame_rate->GetValue() == wxT("")) {
204                 return {};
205         }
206
207         return locale_convert<double>(wx_to_std(_video_frame_rate->GetValue()));
208 }
209
210
211 void
212 ContentAdvancedDialog::set_video_frame_rate ()
213 {
214         _set_video_frame_rate->Enable (false);
215 }
216
217
218 void
219 ContentAdvancedDialog::video_frame_rate_changed ()
220 {
221        bool enable = true;
222        /* If the user clicks "set" now, with no frame rate entered, it would unset the video
223           frame rate in the selected content.  This can't be allowed for some content types.
224        */
225        if (_video_frame_rate->GetValue() == wxT("") && (dynamic_pointer_cast<DCPContent>(_content) || dynamic_pointer_cast<FFmpegContent>(_content))) {
226                enable = false;
227        }
228
229        _set_video_frame_rate->Enable (enable);
230 }
231
232
233 void
234 ContentAdvancedDialog::setup_sensitivity ()
235 {
236         _burnt_subtitle->Enable (static_cast<bool>(_content->video));
237         _burnt_subtitle_language->enable (_content->video && _burnt_subtitle->GetValue());
238 }
239
240
241 void
242 ContentAdvancedDialog::burnt_subtitle_changed ()
243 {
244         setup_sensitivity ();
245 }
246
247
248 optional<dcp::LanguageTag>
249 ContentAdvancedDialog::burnt_subtitle_language() const
250 {
251         return _burnt_subtitle_language->get();
252 }
253