21cba5e8a31041ec2d97c58684b8fc5255f90232
[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 "content_advanced_dialog.h"
23 #include "dcpomatic_button.h"
24 #include "filter_dialog.h"
25 #include "language_tag_widget.h"
26 #include "static_text.h"
27 #include "wx_util.h"
28 #include "lib/content.h"
29 #include "lib/dcp_content.h"
30 #include "lib/filter.h"
31 #include "lib/ffmpeg_content.h"
32 #include "lib/image_content.h"
33 #include "lib/scope_guard.h"
34 #include "lib/video_content.h"
35 #include <dcp/warnings.h>
36 LIBDCP_DISABLE_WARNINGS
37 #include <wx/gbsizer.h>
38 #include <wx/propgrid/property.h>
39 #include <wx/propgrid/props.h>
40 #include <wx/wx.h>
41 LIBDCP_ENABLE_WARNINGS
42 #include <boost/bind/bind.hpp>
43
44
45 using std::dynamic_pointer_cast;
46 using std::shared_ptr;
47 using std::string;
48 using std::vector;
49 using boost::bind;
50 using boost::optional;
51 #if BOOST_VERSION >= 106100
52 using namespace boost::placeholders;
53 #endif
54 using dcp::locale_convert;
55
56
57
58 ContentAdvancedDialog::ContentAdvancedDialog (wxWindow* parent, shared_ptr<Content> content)
59         : wxDialog (parent, wxID_ANY, _("Advanced content settings"))
60         , _content (content)
61 {
62         auto sizer = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
63
64         int r = 0;
65
66         wxClientDC dc (this);
67         auto size = dc.GetTextExtent (wxT ("A quite long name"));
68 #ifdef __WXGTK3__
69         size.SetWidth (size.GetWidth() + 64);
70 #endif
71         size.SetHeight (-1);
72
73         add_label_to_sizer (sizer, this, _("Video filters"), true, wxGBPosition(r, 0));
74         _filters = new StaticText (this, _("None"), wxDefaultPosition, size);
75         _filters_button = new Button (this, _("Edit..."));
76         auto filters = new wxBoxSizer (wxHORIZONTAL);
77         filters->Add (_filters, 1, wxALL | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
78         filters->Add (_filters_button, 0, wxALL, DCPOMATIC_SIZER_GAP);
79         sizer->Add (filters, wxGBPosition(r, 1), wxGBSpan(1, 2));
80         ++r;
81
82         wxStaticText* video_frame_rate_label;
83         if (_content->video) {
84                 video_frame_rate_label = add_label_to_sizer (sizer, this, _("Override detected video frame rate"), true, wxGBPosition(r, 0));
85         } else {
86                 video_frame_rate_label = add_label_to_sizer (sizer, this, _("Video frame rate that content was prepared for"), true, wxGBPosition(r, 0));
87         }
88         _video_frame_rate = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0, wxNumericPropertyValidator(wxNumericPropertyValidator::Float));
89         sizer->Add (_video_frame_rate, wxGBPosition(r, 1));
90         _set_video_frame_rate = new Button (this, _("Set"));
91         _set_video_frame_rate->Enable (false);
92         sizer->Add (_set_video_frame_rate, wxGBPosition(r, 2));
93         ++r;
94
95         /// TRANSLATORS: next to this control is a language selector, so together they will read, for example
96         /// "Video has burnt-in subtitles in the language fr-FR"
97         _burnt_subtitle = new wxCheckBox (this, wxID_ANY, _("Video has burnt-in subtitles in the language"));
98         sizer->Add (_burnt_subtitle, wxGBPosition(r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
99         _burnt_subtitle_language = new LanguageTagWidget (this, _("Language of burnt-in subtitles in this content"), content->video ? content->video->burnt_subtitle_language() : boost::none);
100         sizer->Add (_burnt_subtitle_language->sizer(), wxGBPosition(r, 1), wxGBSpan(1, 2), wxEXPAND);
101         ++r;
102
103         _ignore_video = new wxCheckBox (this, wxID_ANY, _("Ignore this content's video and use only audio, subtitles and closed captions"));
104         sizer->Add(_ignore_video, wxGBPosition(r, 0), wxGBSpan(1, 3));
105         ++r;
106
107         auto overall = new wxBoxSizer (wxVERTICAL);
108         overall->Add (sizer, 1, wxALL, DCPOMATIC_DIALOG_BORDER);
109         auto buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
110         if (buttons) {
111                 overall->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
112         }
113
114         SetSizerAndFit (overall);
115
116         _ignore_video->Enable(static_cast<bool>(_content->video));
117         _ignore_video->SetValue(_content->video ? !content->video->use() : false);
118
119         auto fcs = dynamic_pointer_cast<FFmpegContent>(content);
120         _filters_allowed = static_cast<bool>(fcs);
121         if (fcs) {
122                 _filters_list = fcs->filters();
123         }
124         setup_filters ();
125
126         bool const single_frame_image_content = dynamic_pointer_cast<const ImageContent>(_content) && _content->number_of_paths() == 1;
127         video_frame_rate_label->Enable (!single_frame_image_content);
128         _video_frame_rate->Enable (!single_frame_image_content);
129
130         auto vfr = _content->video_frame_rate ();
131         if (vfr) {
132                 _video_frame_rate->SetValue (std_to_wx(locale_convert<string>(*vfr)));
133         }
134
135         _burnt_subtitle->SetValue (_content->video && static_cast<bool>(_content->video->burnt_subtitle_language()));
136         _burnt_subtitle_language->set (_content->video ? _content->video->burnt_subtitle_language() : boost::none);
137
138         _filters_button->Bind (wxEVT_BUTTON, bind(&ContentAdvancedDialog::edit_filters, this));
139         _set_video_frame_rate->Bind (wxEVT_BUTTON, bind(&ContentAdvancedDialog::set_video_frame_rate, this));
140         _video_frame_rate->Bind (wxEVT_TEXT, boost::bind(&ContentAdvancedDialog::video_frame_rate_changed, this));
141         _burnt_subtitle->Bind (wxEVT_CHECKBOX, boost::bind(&ContentAdvancedDialog::burnt_subtitle_changed, this));
142         _burnt_subtitle_language->Changed.connect (boost::bind(&ContentAdvancedDialog::burnt_subtitle_language_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 void
201 ContentAdvancedDialog::set_video_frame_rate ()
202 {
203         if (_video_frame_rate->GetValue() != wxT("")) {
204                 _content->set_video_frame_rate (locale_convert<double>(wx_to_std(_video_frame_rate->GetValue())));
205         } else {
206                 _content->unset_video_frame_rate ();
207         }
208
209         _set_video_frame_rate->Enable (false);
210 }
211
212
213 void
214 ContentAdvancedDialog::video_frame_rate_changed ()
215 {
216        bool enable = true;
217        /* If the user clicks "set" now, with no frame rate entered, it would unset the video
218           frame rate in the selected content.  This can't be allowed for some content types.
219        */
220        if (_video_frame_rate->GetValue() == wxT("") && (dynamic_pointer_cast<DCPContent>(_content) || dynamic_pointer_cast<FFmpegContent>(_content))) {
221                enable = false;
222        }
223
224        _set_video_frame_rate->Enable (enable);
225 }
226
227
228 void
229 ContentAdvancedDialog::setup_sensitivity ()
230 {
231         _burnt_subtitle->Enable (static_cast<bool>(_content->video));
232         _burnt_subtitle_language->enable (_content->video && _burnt_subtitle->GetValue());
233 }
234
235
236 void
237 ContentAdvancedDialog::burnt_subtitle_changed ()
238 {
239         setup_sensitivity ();
240 }
241
242
243 void
244 ContentAdvancedDialog::burnt_subtitle_language_changed ()
245 {
246         DCPOMATIC_ASSERT (_content->video);
247         _content->video->set_burnt_subtitle_language (_burnt_subtitle_language->get());
248 }
249