Vuo  2.4.0
VuoInputEditorMovieFormat.cc
Go to the documentation of this file.
1
11#include "VuoComboBox.hh"
12
13const QColor VuoInputEditorMovieFormat::darkModeEnabledLabelTextColor = QColor(207, 207, 207);
14const QColor VuoInputEditorMovieFormat::darkModeDisabledLabelTextColor = QColor(127, 127, 127);
15
16extern "C"
17{
18 #include "VuoMovieFormat.h"
19 #include "VuoReal.h"
20}
21
26{
27 return new VuoInputEditorMovieFormat();
28}
29
34void VuoInputEditorMovieFormat::setUpDialog(QDialog &dialog, json_object *originalValue, json_object *details)
35{
36 const int decimalPrecision = DBL_MAX_10_EXP + DBL_DIG;
37
38 suggestedMinImageQuality = 0;
39 suggestedMaxImageQuality = 1;
40
41 suggestedMinAudioQuality = 0;
42 suggestedMaxAudioQuality = 1;
43
44 double suggestedStepImageQuality = 0.1;
45 double suggestedStepAudioQuality = 0.1;
46
47 QDoubleValidator *validator = new QDoubleValidator(this);
48 validator->setDecimals(decimalPrecision);
49
50 isDark = false;
51 tabCycleForward = true;
52
53 // Parse supported port annotations from the port's "details" JSON object:
54 if (details)
55 {
56 json_object *darkMode = NULL;
57 if (json_object_object_get_ex(details, "isDark", &darkMode))
58 isDark = json_object_get_boolean(darkMode);
59
60 json_object *forwardTabTraversal = NULL;
61 if (json_object_object_get_ex(details, "forwardTabTraversal", &forwardTabTraversal))
62 tabCycleForward = json_object_get_boolean(forwardTabTraversal);
63
64 // "suggestedMin"
65 json_object *suggestedMinValue = NULL;
66 if (json_object_object_get_ex(details, "suggestedMin", &suggestedMinValue))
67 {
68 suggestedMinImageQuality = VuoMovieFormat_makeFromJson(suggestedMinValue).imageQuality;
69 suggestedMinAudioQuality = VuoMovieFormat_makeFromJson(suggestedMinValue).audioQuality;
70 }
71
72 // "suggestedMax"
73 json_object *suggestedMaxValue = NULL;
74 if (json_object_object_get_ex(details, "suggestedMax", &suggestedMaxValue))
75 {
76 suggestedMaxImageQuality = VuoMovieFormat_makeFromJson(suggestedMaxValue).imageQuality;
77 suggestedMaxAudioQuality = VuoMovieFormat_makeFromJson(suggestedMaxValue).audioQuality;
78 }
79
80 // "suggestedStep"
81 json_object *suggestedStepValue = NULL;
82 if (json_object_object_get_ex(details, "suggestedStep", &suggestedStepValue))
83 {
84 suggestedStepImageQuality = VuoMovieFormat_makeFromJson(suggestedStepValue).imageQuality;
85 suggestedStepAudioQuality = VuoMovieFormat_makeFromJson(suggestedStepValue).audioQuality;
86 }
87 }
88
89 QFont font = getDefaultFont();
90
91 // Display menus for "Image Encoding" and "Audio Encoding".
92 {
93 string originalMovieImageEncodingAsString = json_object_to_json_string_ext(VuoMovieImageEncoding_getJson(VuoMovieFormat_makeFromJson(originalValue).imageEncoding), JSON_C_TO_STRING_PLAIN);
94 comboBoxImageEncoding = new VuoComboBox(&dialog);
95 comboBoxImageEncoding->setFont(font);
96 comboBoxImageEncoding->resize(220, comboBoxImageEncoding->height());
97 setUpComboBoxForType(comboBoxImageEncoding, "VuoMovieImageEncoding", originalMovieImageEncodingAsString);
98 connect(comboBoxImageEncoding, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &VuoInputEditorMovieFormat::updateQualitySliderEnabledStatusAndEmitValueChanged);
99
100 string originalAudioEncodingAsString = json_object_to_json_string_ext(VuoAudioEncoding_getJson(VuoMovieFormat_makeFromJson(originalValue).audioEncoding), JSON_C_TO_STRING_PLAIN);
101 comboBoxAudioEncoding = new VuoComboBox(&dialog);
102 comboBoxAudioEncoding->setFont(font);
103 setUpComboBoxForType(comboBoxAudioEncoding, "VuoAudioEncoding", originalAudioEncodingAsString);
104 connect(comboBoxAudioEncoding, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &VuoInputEditorMovieFormat::updateQualitySliderEnabledStatusAndEmitValueChanged);
105 }
106
107 // Display sliders for "Image Quality" and "Audio Quality".
108 {
109 // Image Quality
110 lineEditImageQuality = new QLineEdit(&dialog);
111 lineEditImageQuality->setFont(font);
112 lineEditImageQuality->setText( convertToLineEditFormat( VuoReal_getJson(VuoMovieFormat_makeFromJson(originalValue).imageQuality) ) );
113 lineEditImageQuality->adjustSize();
114
115 lineEditImageQuality->setValidator(validator);
116 lineEditImageQuality->installEventFilter(this);
117
118 sliderImageQuality = new QSlider(&dialog);
119 sliderImageQuality->setOrientation(Qt::Horizontal);
120 sliderImageQuality->setFocusPolicy(Qt::NoFocus);
121 sliderImageQuality->setMinimum(0);
122 sliderImageQuality->setMaximum(sliderImageQuality->width());
123 sliderImageQuality->setSingleStep(fmax(1, suggestedStepImageQuality*(sliderImageQuality->maximum()-sliderImageQuality->minimum())/(suggestedMaxImageQuality-suggestedMinImageQuality)));
124
125 double lineEditValueImageQuality = VuoMovieFormat_makeFromJson(originalValue).imageQuality;
126 int sliderValueImageQuality = lineEditValueToScaledSliderValue(lineEditValueImageQuality, imageQuality);
127 sliderImageQuality->setValue(sliderValueImageQuality);
128
129 connect(sliderImageQuality, &QSlider::valueChanged, this, static_cast<void (VuoInputEditorMovieFormat::*)(int)>(&VuoInputEditorMovieFormat::updateLineEditValue));
130 connect(lineEditImageQuality, &QLineEdit::textEdited, this, &VuoInputEditorMovieFormat::updateSliderValue);
131 connect(lineEditImageQuality, &QLineEdit::editingFinished, this, &VuoInputEditorMovieFormat::emitValueChanged);
132
133 // Audio Quality
134 lineEditAudioQuality = new QLineEdit(&dialog);
135 lineEditAudioQuality->setFont(font);
136 lineEditAudioQuality->setText( convertToLineEditFormat( VuoReal_getJson(VuoMovieFormat_makeFromJson(originalValue).audioQuality) ) );
137 lineEditAudioQuality->adjustSize();
138
139 lineEditAudioQuality->setValidator(validator);
140 lineEditAudioQuality->installEventFilter(this);
141
142 sliderAudioQuality = new QSlider(&dialog);
143 sliderAudioQuality->setOrientation(Qt::Horizontal);
144 sliderAudioQuality->setFocusPolicy(Qt::NoFocus);
145 sliderAudioQuality->setMinimum(0);
146 sliderAudioQuality->setMaximum(sliderAudioQuality->width());
147 sliderAudioQuality->setSingleStep(fmax(1, suggestedStepAudioQuality*(sliderAudioQuality->maximum()-sliderAudioQuality->minimum())/(suggestedMaxAudioQuality-suggestedMinAudioQuality)));
148
149 double lineEditValueAudioQuality = VuoMovieFormat_makeFromJson(originalValue).audioQuality;
150 int sliderValueAudioQuality = lineEditValueToScaledSliderValue(lineEditValueAudioQuality, audioQuality);
151 sliderAudioQuality->setValue(sliderValueAudioQuality);
152
153 connect(sliderAudioQuality, &QSlider::valueChanged, this, static_cast<void (VuoInputEditorMovieFormat::*)(int)>(&VuoInputEditorMovieFormat::updateLineEditValue));
154 connect(lineEditAudioQuality, &QLineEdit::textEdited, this, &VuoInputEditorMovieFormat::updateSliderValue);
155 connect(lineEditAudioQuality, &QLineEdit::editingFinished, this, &VuoInputEditorMovieFormat::emitValueChanged);
156 }
157
158 // Layout details
159 {
160 const int widgetVerticalSpacing = 4;
161 const int widgetHorizontalSpacing = 5;
162
163 // Tweak to better align the text of labels with the text in their associated widgets.
164 const int comboLabelVerticalCorrection = 3;
165 const int lineEditLabelVerticalCorrection = 1;
166
167 QLabel *labelImageEncoding = new QLabel("Image Encoding", &dialog);
168 QLabel *labelAudioEncoding = new QLabel("Audio Encoding", &dialog);
169 labelImageQuality = new QLabel("Image Quality", &dialog);
170 labelAudioQuality = new QLabel("Audio Quality", &dialog);
171
172 setFirstWidgetInTabOrder(lineEditImageQuality);
173 setLastWidgetInTabOrder(lineEditAudioQuality);
174
175 updateQualitySliderEnabledStatus();
176
177 labelImageEncoding->move( labelImageEncoding->pos().x(),
178 labelImageEncoding->pos().y() + comboLabelVerticalCorrection);
179 comboBoxImageEncoding->move(labelImageEncoding->pos().x()+labelImageEncoding->width() + widgetHorizontalSpacing,
180 labelImageEncoding->pos().y() - comboLabelVerticalCorrection);
181
182 labelImageQuality->move( labelImageEncoding->pos().x(),
183 comboBoxImageEncoding->pos().y() + comboBoxImageEncoding->height() + widgetVerticalSpacing + lineEditLabelVerticalCorrection);
184 lineEditImageQuality->move( comboBoxImageEncoding->pos().x(),
185 labelImageQuality->pos().y() - lineEditLabelVerticalCorrection);
186 sliderImageQuality->move( lineEditImageQuality->pos().x(),
187 lineEditImageQuality->pos().y() + lineEditImageQuality->height() + widgetVerticalSpacing - lineEditLabelVerticalCorrection);
188
189 sliderImageQuality->resize(sliderImageQuality->width(), sliderImageQuality->height() - 10);
190 lineEditImageQuality->resize(sliderImageQuality->width(), lineEditImageQuality->height());
191
192 labelAudioEncoding->move( labelImageQuality->pos().x(),
193 sliderImageQuality->pos().y() + sliderImageQuality->height() + widgetVerticalSpacing + comboLabelVerticalCorrection);
194 comboBoxAudioEncoding->move(labelAudioEncoding->pos().x() + labelAudioEncoding->width() + widgetHorizontalSpacing,
195 labelAudioEncoding->pos().y() - comboLabelVerticalCorrection);
196
197
198 labelAudioQuality->move( labelAudioEncoding->pos().x(),
199 comboBoxAudioEncoding->pos().y() + comboBoxAudioEncoding->height() + widgetVerticalSpacing + lineEditLabelVerticalCorrection);
200 lineEditAudioQuality->move( comboBoxAudioEncoding->pos().x(),
201 labelAudioQuality->pos().y() - lineEditLabelVerticalCorrection);
202 sliderAudioQuality->move( lineEditAudioQuality->pos().x(),
203 lineEditAudioQuality->pos().y() + lineEditAudioQuality->height() + widgetVerticalSpacing - lineEditLabelVerticalCorrection);
204
205 sliderAudioQuality->resize(sliderAudioQuality->width(), sliderAudioQuality->height() - 10);
206 lineEditAudioQuality->resize(sliderAudioQuality->width(), lineEditAudioQuality->height());
207
208 labelImageEncoding->show();
209 comboBoxImageEncoding->show();
210
211 labelImageQuality->show();
212 sliderImageQuality->show();
213
214 labelAudioEncoding->show();
215 comboBoxAudioEncoding->show();
216
217 labelAudioQuality->show();
218 sliderAudioQuality->show();
219
220 dialog.adjustSize();
221 }
222}
223
228{
229 return convertFromSubwidgetFormats(comboBoxImageEncoding->currentData(),
230 lineEditImageQuality->text(),
231 comboBoxAudioEncoding->currentData(),
232 lineEditAudioQuality->text());
233}
234
239{
240 double realValue = VuoReal_makeFromJson(value);
241 QString valueAsStringInUserLocale = QLocale().toString(realValue);
242
243 if (qAbs(realValue) >= 1000.0)
244 valueAsStringInUserLocale.remove(QLocale().groupSeparator());
245
246 return valueAsStringInUserLocale;
247}
248
253 const QString &imageQualityAsString,
254 const QVariant &audioEncoding,
255 const QString &audioQualityAsString)
256{
257 VuoMovieFormat format;
258 format.imageEncoding = VuoMovieImageEncoding_makeFromJson(imageEncoding.value<json_object *>());
259 format.audioEncoding = VuoAudioEncoding_makeFromJson(audioEncoding.value<json_object *>());
260 format.imageQuality = QLocale().toDouble(imageQualityAsString);
261 format.audioQuality = QLocale().toDouble(audioQualityAsString);
262 return VuoMovieFormat_getJson(format);
263}
264
269void VuoInputEditorMovieFormat::updateSliderValue(QString newLineEditText)
270{
271 QObject *sender = QObject::sender();
272 qualityAttribute whichQualityAttribute = (sender == lineEditImageQuality? imageQuality : audioQuality);
273
274 double newLineEditValue = QLocale().toDouble(newLineEditText);
275 int newSliderValue = lineEditValueToScaledSliderValue(newLineEditValue, whichQualityAttribute);
276
277 QSlider *targetSlider = (whichQualityAttribute == imageQuality? sliderImageQuality : sliderAudioQuality);
278
279 disconnect(targetSlider, &QSlider::valueChanged, this, static_cast<void (VuoInputEditorMovieFormat::*)(int)>(&VuoInputEditorMovieFormat::updateLineEditValue));
280 targetSlider->setValue(newSliderValue);
281 connect(targetSlider, &QSlider::valueChanged, this, static_cast<void (VuoInputEditorMovieFormat::*)(int)>(&VuoInputEditorMovieFormat::updateLineEditValue));
282}
283
290void VuoInputEditorMovieFormat::updateLineEditValue()
291{
292 QObject *sender = QObject::sender();
293 qualityAttribute whichQualityAttribute = (sender == sliderImageQuality? imageQuality: audioQuality);
294 updateLineEditValue(((QSlider *)sender)->value(), whichQualityAttribute);
295}
296
297void VuoInputEditorMovieFormat::updateLineEditValue(int newSliderValue)
298{
299 QObject *sender = QObject::sender();
300 qualityAttribute whichQualityAttribute = (sender == sliderImageQuality? imageQuality: audioQuality);
301
302 updateLineEditValue(newSliderValue, whichQualityAttribute);
303}
304
314void VuoInputEditorMovieFormat::updateLineEditValue(int newSliderValue, qualityAttribute whichQualityAttribute)
315{
316 double newLineEditValue = sliderValueToScaledLineEditValue(newSliderValue, whichQualityAttribute);
317 QLineEdit *targetLineEdit = (whichQualityAttribute == imageQuality? lineEditImageQuality : lineEditAudioQuality);
318 const QString originalLineEditText = targetLineEdit->text();
319 QString newLineEditText = QLocale().toString(newLineEditValue, 'g');
320
321 if (qAbs(newLineEditValue) >= 1000.0)
322 newLineEditText.remove(QLocale().groupSeparator());
323
324 if (originalLineEditText != newLineEditText)
325 {
326 targetLineEdit->setText(newLineEditText);
327 targetLineEdit->setFocus();
328 targetLineEdit->selectAll();
329
330 emitValueChanged();
331 }
332}
333
337int VuoInputEditorMovieFormat::lineEditValueToScaledSliderValue(double lineEditValue, qualityAttribute whichQualityAttribute)
338{
339 QSlider *targetSlider = (whichQualityAttribute == imageQuality? sliderImageQuality : sliderAudioQuality);
340 double suggestedMin = (whichQualityAttribute == imageQuality? suggestedMinImageQuality : suggestedMinAudioQuality);
341 double suggestedMax = (whichQualityAttribute == imageQuality? suggestedMaxImageQuality : suggestedMaxAudioQuality);
342 const double lineEditRange = suggestedMax - suggestedMin;
343
344 const int sliderRange = targetSlider->maximum() - targetSlider->minimum();
345 int scaledSliderValue = targetSlider->minimum() + ((lineEditValue-suggestedMin)/(1.0*(lineEditRange)))*sliderRange;
346
347 return scaledSliderValue;
348}
349
354double VuoInputEditorMovieFormat::sliderValueToScaledLineEditValue(int sliderValue, qualityAttribute whichQualityAttribute)
355{
356 QSlider *targetSlider = (whichQualityAttribute == imageQuality? sliderImageQuality : sliderAudioQuality);
357 double suggestedMin = (whichQualityAttribute == imageQuality? suggestedMinImageQuality : suggestedMinAudioQuality);
358 double suggestedMax = (whichQualityAttribute == imageQuality? suggestedMaxImageQuality : suggestedMaxAudioQuality);
359 const double lineEditRange = suggestedMax - suggestedMin;
360
361 const int sliderRange = targetSlider->maximum() - targetSlider->minimum();
362 double scaledLineEditValue = suggestedMin + ((sliderValue-targetSlider->minimum())/(1.0*sliderRange))*lineEditRange;
363
364 return scaledLineEditValue;
365}
366
367void VuoInputEditorMovieFormat::updateQualitySliderEnabledStatusAndEmitValueChanged()
368{
369 updateQualitySliderEnabledStatus();
370 emitValueChanged();
371}
372
376void VuoInputEditorMovieFormat::updateQualitySliderEnabledStatus()
377{
378 // Image Quality
379 json_object *acceptedFormatJson = getAcceptedValue();
380 VuoMovieFormat acceptedFormat = VuoMovieFormat_makeFromJson(acceptedFormatJson);
381 bool enableImageQualityWidgets = ((acceptedFormat.imageEncoding == VuoMovieImageEncoding_JPEG) ||
382 (acceptedFormat.imageEncoding == VuoMovieImageEncoding_H264) ||
383 (acceptedFormat.imageEncoding == VuoMovieImageEncoding_HEVC) ||
384 (acceptedFormat.imageEncoding == VuoMovieImageEncoding_HEVCAlpha));
385
386 labelImageQuality->setEnabled(enableImageQualityWidgets);
387 lineEditImageQuality->setEnabled(enableImageQualityWidgets);
388 sliderImageQuality->setEnabled(enableImageQualityWidgets);
389
390 if (!enableImageQualityWidgets)
391 {
392 int sliderValueImageQuality = lineEditValueToScaledSliderValue(1., imageQuality);
393 sliderImageQuality->setValue(sliderValueImageQuality);
394 }
395
396 // Audio Quality
397 bool enableAudioQualityWidgets = (acceptedFormat.audioEncoding != VuoAudioEncoding_LinearPCM);
398
399 labelAudioQuality->setEnabled(enableAudioQualityWidgets);
400 lineEditAudioQuality->setEnabled(enableAudioQualityWidgets);
401 sliderAudioQuality->setEnabled(enableAudioQualityWidgets);
402
403 if (!enableAudioQualityWidgets)
404 {
405 int sliderValueAudioQuality = lineEditValueToScaledSliderValue(1., audioQuality);
406 sliderAudioQuality->setValue(sliderValueAudioQuality);
407 }
408
409 QLineEdit *firstEnabledLineEdit = (enableImageQualityWidgets? lineEditImageQuality :
410 (enableAudioQualityWidgets? lineEditAudioQuality :
411 NULL));
412
413 QLineEdit *lastEnabledLineEdit = (enableAudioQualityWidgets? lineEditAudioQuality :
414 (enableImageQualityWidgets? lineEditImageQuality :
415 NULL));
416
417 // Return focus to the topmost line edit by default, or to the bottommost
418 // line edit if tab-cycling backwards.
419 // To be handled properly for https://b33p.net/kosada/node/6365 .
420 if (tabCycleForward && firstEnabledLineEdit)
421 {
422 firstEnabledLineEdit->setFocus();
423 firstEnabledLineEdit->selectAll();
424 }
425 else if (!tabCycleForward && lastEnabledLineEdit)
426 {
427 lastEnabledLineEdit->setFocus();
428 lastEnabledLineEdit->selectAll();
429 }
430
431 setFirstWidgetInTabOrder(firstEnabledLineEdit);
432 setLastWidgetInTabOrder(lastEnabledLineEdit);
433}
434
435void VuoInputEditorMovieFormat::emitValueChanged()
436{
438}
439
443bool VuoInputEditorMovieFormat::eventFilter(QObject *object, QEvent *event)
444{
445 QSlider *targetSlider = (object==lineEditImageQuality? sliderImageQuality :
446 (object==lineEditAudioQuality? sliderAudioQuality :
447 NULL));
448
449 if (event->type()==QEvent::Wheel && targetSlider)
450 {
451 // Let the slider handle mouse wheel events.
452 QApplication::sendEvent(targetSlider, event);
453 return true;
454 }
455
456 else if (event->type()==QEvent::KeyPress && targetSlider)
457 {
458 // Let the slider handle keypresses of the up and down arrows.
459 QKeyEvent *keyEvent = (QKeyEvent *)(event);
460 if ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))
461 {
462 QApplication::sendEvent(targetSlider, event);
463 return true;
464 }
465 }
466
467 return VuoInputEditorWithLineEdit::eventFilter(object, event);
468}
469
476QComboBox * VuoInputEditorMovieFormat::setUpComboBoxForType(QComboBox *comboBox, QString type, string originalValueAsString)
477{
478 QString allowedValuesFunctionName = type + "_getAllowedValues";
479 typedef void *(*allowedValuesFunctionType)(void);
480 allowedValuesFunctionType allowedValuesFunction = (allowedValuesFunctionType)dlsym(RTLD_SELF, allowedValuesFunctionName.toUtf8().constData());
481
482 QString summaryFunctionName = type + "_getSummary";
483 typedef char *(*summaryFunctionType)(int);
484 summaryFunctionType summaryFunction = (summaryFunctionType)dlsym(RTLD_SELF, summaryFunctionName.toUtf8().constData());
485
486 QString jsonFunctionName = type + "_getJson";
487 typedef json_object *(*jsonFunctionType)(int);
488 jsonFunctionType jsonFunction = (jsonFunctionType)dlsym(RTLD_SELF, jsonFunctionName.toUtf8().constData());
489
490 QString listCountFunctionName = "VuoListGetCount_" + type;
491 typedef unsigned long (*listCountFunctionType)(void *);
492 listCountFunctionType listCountFunction = (listCountFunctionType)dlsym(RTLD_SELF, listCountFunctionName.toUtf8().constData());
493
494 QString listValueFunctionName = "VuoListGetValue_" + type;
495 typedef int (*listValueFunctionType)(void *, unsigned long);
496 listValueFunctionType listValueFunction = (listValueFunctionType)dlsym(RTLD_SELF, listValueFunctionName.toUtf8().constData());
497
498 if (allowedValuesFunction && summaryFunction && jsonFunction && listCountFunction && listValueFunction)
499 {
500 void *allowedValues = allowedValuesFunction();
501 unsigned long count = listCountFunction(allowedValues);
502 for (unsigned long i=1; i<=count; ++i)
503 {
504 int value = listValueFunction(allowedValues, i);
505 char *summary = summaryFunction(value);
506 QVariant variantValue = QVariant::fromValue(jsonFunction(value));
507
508 // Hide HEVC on OS versions where it isn't supported.
509 if (type == "VuoMovieImageEncoding"
510 && value == VuoMovieImageEncoding_HEVC
511 && QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSHighSierra) // 10.13
512 {
513 free(summary);
514 continue;
515 }
516 if (type == "VuoMovieImageEncoding"
517 && (value == VuoMovieImageEncoding_HEVCAlpha
518 || value == VuoMovieImageEncoding_ProRes422HQ
519 || value == VuoMovieImageEncoding_ProRes422LT
520 || value == VuoMovieImageEncoding_ProRes422Proxy)
521 && QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSCatalina) // 10.15
522 {
523 free(summary);
524 continue;
525 }
526
527 comboBox->addItem(summary, variantValue);
528
529 if (json_object_to_json_string_ext(jsonFunction(value), JSON_C_TO_STRING_PLAIN) == originalValueAsString)
530 comboBox->setCurrentText(summary);
531
532 free(summary);
533 }
534 }
535
536 return comboBox;
537}