1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2024 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "win_addJob.h"
23 #include "UIC_win_addJob.h"
27 #include "model_options.h"
28 #include "model_preferences.h"
29 #include "model_sysinfo.h"
30 #include "model_recently.h"
31 #include "encoder_factory.h"
32 #include "mediainfo.h"
33 #include "string_validator.h"
35 #include "win_editor.h"
38 #include <MUtils/Global.h>
39 #include <MUtils/OSSupport.h>
40 #include <MUtils/Exception.h>
45 #include <QCloseEvent>
46 #include <QMessageBox>
47 #include <QFileDialog>
48 #include <QDesktopServices>
51 #include <QInputDialog>
60 #define ARRAY_SIZE(ARRAY) (sizeof((ARRAY))/sizeof((ARRAY[0])))
61 #define VALID_DIR(PATH) ((!(PATH).isEmpty()) && QFileInfo(PATH).exists() && QFileInfo(PATH).isDir())
62 #define SHFL(X) ((*reinterpret_cast<int*>(&(X))) <<= 1)
64 #define REMOVE_USAFED_ITEM \
66 for(int i = 0; i < ui->cbxTemplate->count(); i++) \
68 const OptionsModel* temp = reinterpret_cast<const OptionsModel*>(ui->cbxTemplate->itemData(i).value<const void*>()); \
71 ui->cbxTemplate->blockSignals(true); \
72 ui->cbxTemplate->removeItem(i); \
73 ui->cbxTemplate->blockSignals(false); \
79 #define ADD_CONTEXTMENU_ACTION(WIDGET, ICON, TEXT, SLOTNAME) \
81 QAction *_action = new QAction((ICON), (TEXT), this); \
82 _action->setData(QVariant::fromValue<void*>(WIDGET)); \
83 WIDGET->addAction(_action); \
84 connect(_action, SIGNAL(triggered(bool)), this, SLOT(SLOTNAME())); \
87 #define ADD_CONTEXTMENU_SEPARATOR(WIDGET) \
89 QAction *_action = new QAction(this); \
90 _action->setSeparator(true); \
91 WIDGET->addAction(_action); \
94 Q_DECLARE_METATYPE(const void*)
96 ///////////////////////////////////////////////////////////////////////////////
97 // Disable Monitoring RAII
98 ///////////////////////////////////////////////////////////////////////////////
100 class DisableHelperRAII
103 DisableHelperRAII(bool *const flag
) : m_flag(flag
)
108 ~DisableHelperRAII(void)
117 ///////////////////////////////////////////////////////////////////////////////
118 // Constructor & Destructor
119 ///////////////////////////////////////////////////////////////////////////////
121 AddJobDialog::AddJobDialog(QWidget
*parent
, OptionsModel
*const options
, RecentlyUsed
*const recentlyUsed
, const SysinfoModel
*const sysinfo
, const PreferencesModel
*const preferences
)
125 m_recentlyUsed(recentlyUsed
),
127 m_preferences(preferences
),
128 m_defaults(new OptionsModel(sysinfo
)),
129 ui(new Ui::AddJobDialog()),
130 m_monitorConfigChanges(false)
132 //Init the dialog, from the .ui file
134 setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint
));
137 ui
->buttonSaveTemplate
->setMaximumHeight(20);
138 ui
->buttonDeleteTemplate
->setMaximumHeight(20);
139 resize(width(), minimumHeight());
140 setMinimumSize(size());
141 setMaximumHeight(height());
143 //Init encoder combobox
144 ui
->cbxEncoderType
->addItem(tr("x264 (AVC)"), OptionsModel::EncType_X264
);
145 ui
->cbxEncoderType
->addItem(tr("x265 (HEVC)"), OptionsModel::EncType_X265
);
146 ui
->cbxEncoderType
->addItem(tr("NVEncC"), OptionsModel::EncType_NVEnc
);
148 //Init combobox items
149 ui
->cbxTuning
->addItem(QString::fromLatin1(OptionsModel::SETTING_UNSPECIFIED
));
150 ui
->cbxProfile
->addItem(QString::fromLatin1(OptionsModel::PROFILE_UNRESTRICTED
));
152 //Hide optional controls
153 ui
->checkBoxApplyToAll
->setVisible(false);
155 //Monitor combobox changes
156 connect(ui
->cbxEncoderType
, SIGNAL(currentIndexChanged(int)), this, SLOT(encoderIndexChanged(int)));
157 connect(ui
->cbxEncoderVariant
, SIGNAL(currentIndexChanged(int)), this, SLOT(variantIndexChanged(int)));
158 connect(ui
->cbxRateControlMode
, SIGNAL(currentIndexChanged(int)), this, SLOT(modeIndexChanged(int)));
161 connect(ui
->buttonBrowseSource
, SIGNAL(clicked()), this, SLOT(browseButtonClicked()));
162 connect(ui
->buttonBrowseOutput
, SIGNAL(clicked()), this, SLOT(browseButtonClicked()));
163 connect(ui
->buttonSaveTemplate
, SIGNAL(clicked()), this, SLOT(saveTemplateButtonClicked()));
164 connect(ui
->buttonDeleteTemplate
, SIGNAL(clicked()), this, SLOT(deleteTemplateButtonClicked()));
167 ui
->editCustomX264Params
->installEventFilter(this);
168 ui
->editCustomX264Params
->setValidator(new StringValidatorEncoder(ui
->labelNotificationX264
, ui
->iconNotificationX264
));
169 ui
->editCustomX264Params
->clear();
170 ui
->editCustomAvs2YUVParams
->installEventFilter(this);
171 ui
->editCustomAvs2YUVParams
->setValidator(new StringValidatorSource(ui
->labelNotificationAvs2YUV
, ui
->iconNotificationAvs2YUV
));
172 ui
->editCustomAvs2YUVParams
->clear();
174 //Install event filter
175 ui
->labelHelpScreenX264
->installEventFilter(this);
176 ui
->labelHelpScreenAvs2YUV
->installEventFilter(this);
178 //Monitor for options changes
179 connect(ui
->cbxEncoderType
, SIGNAL(currentIndexChanged(int)), this, SLOT(configurationChanged()));
180 connect(ui
->cbxEncoderArch
, SIGNAL(currentIndexChanged(int)), this, SLOT(configurationChanged()));
181 connect(ui
->cbxEncoderVariant
, SIGNAL(currentIndexChanged(int)), this, SLOT(configurationChanged()));
182 connect(ui
->cbxRateControlMode
, SIGNAL(currentIndexChanged(int)), this, SLOT(configurationChanged()));
183 connect(ui
->spinQuantizer
, SIGNAL(valueChanged(double)), this, SLOT(configurationChanged()));
184 connect(ui
->spinBitrate
, SIGNAL(valueChanged(int)), this, SLOT(configurationChanged()));
185 connect(ui
->cbxPreset
, SIGNAL(currentIndexChanged(int)), this, SLOT(configurationChanged()));
186 connect(ui
->cbxTuning
, SIGNAL(currentIndexChanged(int)), this, SLOT(configurationChanged()));
187 connect(ui
->cbxProfile
, SIGNAL(currentIndexChanged(int)), this, SLOT(configurationChanged()));
188 connect(ui
->editCustomX264Params
, SIGNAL(textChanged(QString
)), this, SLOT(configurationChanged()));
189 connect(ui
->editCustomAvs2YUVParams
, SIGNAL(textChanged(QString
)), this, SLOT(configurationChanged()));
191 //Create context menus
192 ADD_CONTEXTMENU_ACTION(ui
->editCustomX264Params
, QIcon(":/buttons/page_edit.png"), tr("Open the Text-Editor"), editorActionTriggered
);
193 ADD_CONTEXTMENU_ACTION(ui
->editCustomAvs2YUVParams
, QIcon(":/buttons/page_edit.png"), tr("Open the Text-Editor"), editorActionTriggered
);
194 ADD_CONTEXTMENU_SEPARATOR(ui
->editCustomX264Params
);
195 ADD_CONTEXTMENU_SEPARATOR(ui
->editCustomAvs2YUVParams
);
196 ADD_CONTEXTMENU_ACTION(ui
->editCustomX264Params
, QIcon(":/buttons/page_copy.png"), tr("Copy to Clipboard"), copyActionTriggered
);
197 ADD_CONTEXTMENU_ACTION(ui
->editCustomAvs2YUVParams
, QIcon(":/buttons/page_copy.png"), tr("Copy to Clipboard"), copyActionTriggered
);
198 ADD_CONTEXTMENU_ACTION(ui
->editCustomX264Params
, QIcon(":/buttons/page_paste.png"), tr("Paste from Clipboard"), pasteActionTriggered
);
199 ADD_CONTEXTMENU_ACTION(ui
->editCustomAvs2YUVParams
, QIcon(":/buttons/page_paste.png"), tr("Paste from Clipboard"), pasteActionTriggered
);
201 //Setup template selector
203 connect(ui
->cbxTemplate
, SIGNAL(currentIndexChanged(int)), this, SLOT(templateSelected()));
205 //Force initial UI update
206 encoderIndexChanged(ui
->cbxEncoderType
->currentIndex());
207 m_monitorConfigChanges
= true;
210 AddJobDialog::~AddJobDialog(void)
213 for(int i
= 0; i
< ui
->cbxTemplate
->model()->rowCount(); i
++)
215 if(ui
->cbxTemplate
->itemText(i
).startsWith("<") || ui
->cbxTemplate
->itemText(i
).endsWith(">"))
219 const OptionsModel
*item
= reinterpret_cast<const OptionsModel
*>(ui
->cbxTemplate
->itemData(i
).value
<const void*>());
220 ui
->cbxTemplate
->setItemData(i
, QVariant::fromValue
<const void*>(NULL
));
225 if(const QValidator
*tmp
= ui
->editCustomX264Params
->validator())
227 ui
->editCustomX264Params
->setValidator(NULL
);
230 if(const QValidator
*tmp
= ui
->editCustomAvs2YUVParams
->validator())
232 ui
->editCustomAvs2YUVParams
->setValidator(NULL
);
236 MUTILS_DELETE(m_defaults
);
240 ///////////////////////////////////////////////////////////////////////////////
242 ///////////////////////////////////////////////////////////////////////////////
244 void AddJobDialog::showEvent(QShowEvent
*event
)
246 QDialog::showEvent(event
);
249 if((!ui
->editSource
->text().isEmpty()) && ui
->editOutput
->text().isEmpty())
251 QString outPath
= generateOutputFileName(QDir::fromNativeSeparators(ui
->editSource
->text()), m_recentlyUsed
->outputDirectory(), m_recentlyUsed
->filterIndex(), m_preferences
->getSaveToSourcePath());
252 ui
->editOutput
->setText(QDir::toNativeSeparators(outPath
));
253 ui
->buttonAccept
->setFocus();
256 ui
->labelNotificationX264
->hide();
257 ui
->iconNotificationX264
->hide();
258 ui
->labelNotificationAvs2YUV
->hide();
259 ui
->iconNotificationAvs2YUV
->hide();
261 //Enable drag&drop support for this window, required for Qt v4.8.4+
262 setAcceptDrops(true);
265 bool AddJobDialog::eventFilter(QObject
*o
, QEvent
*e
)
267 if((o
== ui
->labelHelpScreenX264
) && (e
->type() == QEvent::MouseButtonPress
))
269 OptionsModel
options(m_sysinfo
); saveOptions(&options
);
270 QScopedPointer
<HelpDialog
> helpScreen(new HelpDialog(this, false, m_sysinfo
, &options
, m_preferences
));
273 else if((o
== ui
->labelHelpScreenAvs2YUV
) && (e
->type() == QEvent::MouseButtonPress
))
275 OptionsModel
options(m_sysinfo
); saveOptions(&options
);
276 QScopedPointer
<HelpDialog
> helpScreen(new HelpDialog(this, true, m_sysinfo
, &options
, m_preferences
));
279 else if((o
== ui
->editCustomX264Params
) && (e
->type() == QEvent::FocusOut
))
281 ui
->editCustomX264Params
->setText(ui
->editCustomX264Params
->text().simplified());
283 else if((o
== ui
->editCustomAvs2YUVParams
) && (e
->type() == QEvent::FocusOut
))
285 ui
->editCustomAvs2YUVParams
->setText(ui
->editCustomAvs2YUVParams
->text().simplified());
290 void AddJobDialog::dragEnterEvent(QDragEnterEvent
*event
)
292 bool accept
[2] = {false, false};
294 foreach(const QString
&fmt
, event
->mimeData()->formats())
296 accept
[0] = accept
[0] || fmt
.contains("text/uri-list", Qt::CaseInsensitive
);
297 accept
[1] = accept
[1] || fmt
.contains("FileNameW", Qt::CaseInsensitive
);
300 if(accept
[0] && accept
[1])
302 event
->acceptProposedAction();
306 void AddJobDialog::dropEvent(QDropEvent
*event
)
309 QList
<QUrl
> urls
= event
->mimeData()->urls();
313 QDragEnterEvent
dragEvent(event
->pos(), event
->proposedAction(), event
->mimeData(), Qt::NoButton
, Qt::NoModifier
);
314 if(qApp
->notify(parent(), &dragEvent
))
316 qApp
->notify(parent(), event
);
321 while((!urls
.isEmpty()) && droppedFile
.isEmpty())
323 QUrl currentUrl
= urls
.takeFirst();
324 QFileInfo
file(currentUrl
.toLocalFile());
325 if(file
.exists() && file
.isFile())
327 qDebug("AddJobDialog::dropEvent: %s", file
.canonicalFilePath().toUtf8().constData());
328 droppedFile
= file
.canonicalFilePath();
332 if(!droppedFile
.isEmpty())
334 const QString outFileName
= generateOutputFileName(droppedFile
, currentOutputPath(), currentOutputIndx(), m_preferences
->getSaveToSourcePath());
335 ui
->editSource
->setText(QDir::toNativeSeparators(droppedFile
));
336 ui
->editOutput
->setText(QDir::toNativeSeparators(outFileName
));
340 ///////////////////////////////////////////////////////////////////////////////
342 ///////////////////////////////////////////////////////////////////////////////
344 void AddJobDialog::encoderIndexChanged(int index
)
346 const OptionsModel::EncType encType
= static_cast<OptionsModel::EncType
>(ui
->cbxEncoderType
->itemData(ui
->cbxEncoderType
->currentIndex()).toInt());
347 const AbstractEncoderInfo
&encoderInfo
= EncoderFactory::getEncoderInfo(encType
);
349 //Update encoder architectures
350 const QList
<AbstractEncoderInfo::ArchId
> archs
= encoderInfo
.getArchitectures();
351 ui
->cbxEncoderArch
->clear();
352 for (quint32 archIdx
= 0; archIdx
< quint32(archs
.count()); ++archIdx
)
354 ui
->cbxEncoderArch
->addItem(archs
[archIdx
].first
, archIdx
);
357 //Update encoder variants
358 const QStringList variants
= encoderInfo
.getVariants();
359 ui
->cbxEncoderVariant
->clear();
360 for(quint32 varntIdx
= 0; varntIdx
< quint32(variants
.count()); ++varntIdx
)
362 ui
->cbxEncoderVariant
->addItem(variants
[varntIdx
], varntIdx
);
365 //Update encoder RC modes
366 const QList
<AbstractEncoderInfo::RCMode
> rcModes
= encoderInfo
.getRCModes();
367 ui
->cbxRateControlMode
->clear();
368 for (quint32 rcIndex
= 0; rcIndex
< quint32(rcModes
.count()); ++rcIndex
)
370 ui
->cbxRateControlMode
->addItem(rcModes
[rcIndex
].first
, rcIndex
);
374 const QStringList presets
= encoderInfo
.getPresets();
377 ui
->cbxPreset
->setEnabled(false);
378 ui
->cbxPreset
->setCurrentIndex(0);
382 ui
->cbxPreset
->setEnabled(true);
383 ui
->cbxPreset
->clear();
384 ui
->cbxPreset
->addItem(QString::fromLatin1(OptionsModel::SETTING_UNSPECIFIED
));
385 ui
->cbxPreset
->addItems(presets
);
389 const QStringList tunings
= encoderInfo
.getTunings();
392 ui
->cbxTuning
->setEnabled(false);
393 ui
->cbxTuning
->setCurrentIndex(0);
397 ui
->cbxTuning
->setEnabled(true);
398 ui
->cbxTuning
->clear();
399 ui
->cbxTuning
->addItem(QString::fromLatin1(OptionsModel::SETTING_UNSPECIFIED
));
400 ui
->cbxTuning
->addItems(tunings
);
403 variantIndexChanged(ui
->cbxEncoderVariant
->currentIndex());
406 void AddJobDialog::variantIndexChanged(int index
)
408 const OptionsModel::EncType encType
= static_cast<OptionsModel::EncType
>(ui
->cbxEncoderType
->itemData(ui
->cbxEncoderType
->currentIndex()).toInt());
409 const AbstractEncoderInfo
&encoderInfo
= EncoderFactory::getEncoderInfo(encType
);
411 //Update encoder profiles
412 const QStringList profiles
= encoderInfo
.getProfiles(ui
->cbxEncoderVariant
->itemData(index
).toUInt());
415 ui
->cbxProfile
->setEnabled(false);
416 ui
->cbxProfile
->setCurrentIndex(0);
420 ui
->cbxProfile
->setEnabled(true);
421 ui
->cbxProfile
->clear();
422 ui
->cbxProfile
->addItem(QString::fromLatin1(OptionsModel::PROFILE_UNRESTRICTED
));
423 ui
->cbxProfile
->addItems(profiles
);
426 modeIndexChanged(ui
->cbxRateControlMode
->currentIndex());
429 void AddJobDialog::modeIndexChanged(int index
)
431 const OptionsModel::EncType encType
= static_cast<OptionsModel::EncType
>(ui
->cbxEncoderType
->itemData(ui
->cbxEncoderType
->currentIndex()).toInt());
432 const AbstractEncoderInfo
&encoderInfo
= EncoderFactory::getEncoderInfo(encType
);
434 //Update bitrate/quantizer boxes
435 const AbstractEncoderInfo::RCType rcType
= encoderInfo
.rcModeToType(ui
->cbxRateControlMode
->itemData(index
).toUInt());
436 ui
->spinQuantizer
->setEnabled(rcType
== AbstractEncoderInfo::RC_TYPE_QUANTIZER
);
437 ui
->spinBitrate
->setEnabled(rcType
!= AbstractEncoderInfo::RC_TYPE_QUANTIZER
);
440 void AddJobDialog::accept(void)
443 const OptionsModel::EncType encType
= static_cast<OptionsModel::EncType
>(ui
->cbxEncoderType
->itemData(ui
->cbxEncoderType
->currentIndex()).toInt());
444 const AbstractEncoderInfo
&encoderInfo
= EncoderFactory::getEncoderInfo(encType
);
446 //Check 64-Bit support
447 if (encoderInfo
.archToType(ui
->cbxEncoderArch
->itemData(ui
->cbxEncoderArch
->currentIndex()).toUInt()) == AbstractEncoderInfo::ARCH_TYPE_X64
)
449 if (!m_sysinfo
->getCPUFeatures(SysinfoModel::CPUFeatures_X64
))
451 QMessageBox::warning(this, tr("64-Bit unsupported!"), tr("<nobr>Sorry, this computer does <b>not</b> support 64-Bit encoders!</nobr>"));
452 ui
->cbxEncoderArch
->setCurrentIndex(AbstractEncoderInfo::ARCH_TYPE_X86
);
457 //Selection complete?
458 if(ui
->editSource
->text().trimmed().isEmpty())
460 QMessageBox::warning(this, tr("Not Found!"), tr("<nobr>Please select a valid source file first!<(nobr>"));
463 if(ui
->editOutput
->text().trimmed().isEmpty())
465 QMessageBox::warning(this, tr("Not Selected!"), tr("<nobr>Please select a valid output file first!</nobr>"));
470 QFileInfo sourceFile
= QFileInfo(this->sourceFile());
471 if(!(sourceFile
.exists() && sourceFile
.isFile()))
473 QMessageBox::warning(this, tr("Not Found!"), tr("<nobr>The selected source file could not be found!</nobr>"));
477 //Is the type of the source file supported?
478 const int sourceType
= MediaInfo::analyze(sourceFile
.canonicalFilePath());
479 if(sourceType
== MediaInfo::FILETYPE_AVISYNTH
)
481 if(!m_sysinfo
->hasAvisynth())
483 if(QMessageBox::warning(this, tr("Avisynth unsupported!"), tr("<nobr>An Avisynth script was selected as input, although Avisynth is <b>not</b> available!</nobr>"), tr("Abort"), tr("Ignore (at your own risk!)")) != 1)
489 else if(sourceType
== MediaInfo::FILETYPE_VAPOURSYNTH
)
491 if(!m_sysinfo
->hasVapourSynth())
493 if(QMessageBox::warning(this, tr("VapurSynth unsupported!"), tr("<nobr>A VapourSynth script was selected as input, although VapourSynth is <b>not/<b> available!</nobr>"), tr("Abort"), tr("Ignore (at your own risk!)")) != 1)
499 else if(!encoderInfo
.isInputTypeSupported(sourceType
))
501 if(QMessageBox::warning(this, tr("Unsupported input format"), tr("<nobr>The selected encoder does <b>not</b> support the selected input format!</nobr>"), tr("Abort"), tr("Ignore (at your own risk!)")) != 1)
507 //Is output file extension supported by encoder?
508 const QStringList outputFormats
= encoderInfo
.supportedOutputFormats();
509 QFileInfo outputFile
= QFileInfo(this->outputFile());
510 if(!outputFormats
.contains(outputFile
.suffix(), Qt::CaseInsensitive
))
512 QMessageBox::warning(this, tr("Unsupported output format"), tr("<nobr>Sorry, the selected encoder does not support the selected output format!</nobr>"));
513 ui
->editOutput
->setText(QDir::toNativeSeparators(QString("%1/%2.%3").arg(outputFile
.absolutePath(), outputFile
.completeBaseName(), outputFormats
.first())));
517 //Does output file already exist?
518 if(outputFile
.exists() && outputFile
.isFile())
520 int ret
= QMessageBox::question(this, tr("Already Exists!"), tr("<nobr>Output file already exists! Overwrite?</nobr>"), QMessageBox::Yes
| QMessageBox::No
, QMessageBox::No
);
521 if(ret
!= QMessageBox::Yes
) return;
523 if(outputFile
.exists() && (!outputFile
.isFile()))
525 QMessageBox::warning(this, tr("Not a File!"), tr("<nobr>Selected output file does not appear to be a valid file!</nobr>"));
529 //Is destination dir writable?
530 QFileInfo outputDir
= QFileInfo(outputFile
.absolutePath());
531 if(!(outputDir
.exists() && outputDir
.isDir() && outputDir
.isWritable()))
533 QMessageBox::warning(this, tr("Not Writable!"), tr("<nobr>Output directory does not exist or is not writable!</nobr>"));
537 //Custom parameters okay?
538 if(!ui
->editCustomX264Params
->hasAcceptableInput())
540 int ret
= QMessageBox::warning(this, tr("Invalid Params"), tr("<nobr>Your custom parameters are invalid and will be discarded!</nobr>"), QMessageBox::Ignore
| QMessageBox::Cancel
, QMessageBox::Cancel
);
541 if(ret
!= QMessageBox::Ignore
) return;
544 //Update recently used
545 m_recentlyUsed
->setFilterIndex(currentOutputIndx());
546 m_recentlyUsed
->setSourceDirectory(currentSourcePath());
547 m_recentlyUsed
->setOutputDirectory(currentOutputPath());
548 RecentlyUsed::saveRecentlyUsed(m_recentlyUsed
);
551 saveOptions(m_options
);
555 void AddJobDialog::browseButtonClicked(void)
557 if(QObject::sender() == ui
->buttonBrowseSource
)
559 QString filePath
= QFileDialog::getOpenFileName(this, tr("Open Source File"), currentSourcePath(true), getInputFilterLst(), NULL
, QFileDialog::DontUseNativeDialog
);
560 if(!(filePath
.isNull() || filePath
.isEmpty()))
562 QString destFile
= generateOutputFileName(filePath
, currentOutputPath(), currentOutputIndx(), m_preferences
->getSaveToSourcePath());
563 ui
->editSource
->setText(QDir::toNativeSeparators(filePath
));
564 ui
->editOutput
->setText(QDir::toNativeSeparators(destFile
));
567 else if(QObject::sender() == ui
->buttonBrowseOutput
)
569 QString selectedType
= getFilterStr(currentOutputIndx());
570 QString filePath
= QFileDialog::getSaveFileName(this, tr("Choose Output File"), currentOutputPath(true), getFilterLst(), &selectedType
, QFileDialog::DontUseNativeDialog
| QFileDialog::DontConfirmOverwrite
);
572 if(!(filePath
.isNull() || filePath
.isEmpty()))
574 if(getFilterIdx(QFileInfo(filePath
).suffix()) < 0)
577 QRegExp
regExp("\\(\\*\\.(\\w+)\\)");
578 if(regExp
.lastIndexIn(selectedType
) >= 0)
580 tempIndex
= getFilterIdx(regExp
.cap(1));
584 tempIndex
= m_recentlyUsed
->filterIndex();
586 filePath
= QString("%1.%2").arg(filePath
, getFilterExt(tempIndex
));
588 ui
->editOutput
->setText(QDir::toNativeSeparators(filePath
));
593 void AddJobDialog::configurationChanged(void)
595 if(!m_monitorConfigChanges
)
600 const OptionsModel
* options
= reinterpret_cast<const OptionsModel
*>(ui
->cbxTemplate
->itemData(ui
->cbxTemplate
->currentIndex()).value
<const void*>());
603 ui
->cbxTemplate
->blockSignals(true);
604 ui
->cbxTemplate
->insertItem(0, tr("<Modified Configuration>"), QVariant::fromValue
<const void*>(NULL
));
605 ui
->cbxTemplate
->setCurrentIndex(0);
606 ui
->cbxTemplate
->blockSignals(false);
610 void AddJobDialog::templateSelected(void)
612 const OptionsModel
* options
= reinterpret_cast<const OptionsModel
*>(ui
->cbxTemplate
->itemData(ui
->cbxTemplate
->currentIndex()).value
<const void*>());
615 qDebug("Loading options!");
616 m_lastTemplateName
= ui
->cbxTemplate
->itemText(ui
->cbxTemplate
->currentIndex());
618 restoreOptions(options
);
622 void AddJobDialog::saveTemplateButtonClicked(void)
624 qDebug("Saving template");
626 QString name
= m_lastTemplateName
;
627 if(name
.isEmpty() || name
.contains('<') || name
.contains('>'))
629 name
= tr("New Template");
631 while(OptionsModel::templateExists(name
))
633 name
= tr("New Template (%1)").arg(QString::number(++n
));
637 QScopedPointer
<OptionsModel
> options(new OptionsModel(m_sysinfo
));
638 saveOptions(options
.data());
640 if(options
->equals(m_defaults
))
642 QMessageBox::warning (this, tr("Oups"), tr("<nobr>It makes no sense to save the default settings!</nobr>"));
643 ui
->cbxTemplate
->blockSignals(true);
644 ui
->cbxTemplate
->setCurrentIndex(0);
645 ui
->cbxTemplate
->blockSignals(false);
650 for(int i
= 0; i
< ui
->cbxTemplate
->count(); i
++)
652 const QString tempName
= ui
->cbxTemplate
->itemText(i
);
653 if(tempName
.contains('<') || tempName
.contains('>'))
657 const OptionsModel
* test
= reinterpret_cast<const OptionsModel
*>(ui
->cbxTemplate
->itemData(i
).value
<const void*>());
660 if(options
->equals(test
))
662 QMessageBox::information (this, tr("Oups"), tr("<nobr>The current settings are already saved as template:<br><b>%1</b></nobr>").arg(ui
->cbxTemplate
->itemText(i
)));
663 ui
->cbxTemplate
->blockSignals(true);
664 ui
->cbxTemplate
->setCurrentIndex(i
);
665 ui
->cbxTemplate
->blockSignals(false);
678 for(int i
= 0; i
< ui
->cbxTemplate
->count(); i
++)
680 const QString tempName
= ui
->cbxTemplate
->itemText(i
);
681 if(!(tempName
.contains('<') || tempName
.contains('>')))
687 name
= QInputDialog::getItem(this, tr("Save Template"), tr("Please enter the name of the template:").leftJustified(144, ' '), items
, 0, true, &ok
).simplified();
696 if(name
.contains('<') || name
.contains('>') || name
.contains('\\') || name
.contains('/') || name
.contains('"'))
698 QMessageBox::warning (this, tr("Invalid Name"), tr("<nobr>Sorry, the name you have entered is invalid!</nobr>"));
699 while(name
.contains('<')) name
.remove('<');
700 while(name
.contains('>')) name
.remove('>');
701 while(name
.contains('\\')) name
.remove('\\');
702 while(name
.contains('/')) name
.remove('/');
703 while(name
.contains('"')) name
.remove('"');
704 name
= name
.simplified();
707 if(OptionsModel::templateExists(name
))
709 int ret
= QMessageBox::warning (this, tr("Already Exists"), tr("<nobr>A template of that name already exists! Overwrite?</nobr>"), QMessageBox::Yes
| QMessageBox::No
, QMessageBox::No
);
710 if(ret
!= QMessageBox::Yes
)
718 if(!OptionsModel::saveTemplate(options
.data(), name
))
720 QMessageBox::critical(this, tr("Save Failed"), tr("Sorry, the template could not be saved!"));
724 ui
->cbxTemplate
->blockSignals(true);
725 for(int i
= 0; i
< ui
->cbxTemplate
->count(); i
++)
727 if(ui
->cbxTemplate
->itemText(i
).compare(name
, Qt::CaseInsensitive
) == 0)
729 QScopedPointer
<const OptionsModel
> oldItem(reinterpret_cast<const OptionsModel
*>(ui
->cbxTemplate
->itemData(i
).value
<const void*>()));
730 ui
->cbxTemplate
->setItemData(i
, QVariant::fromValue
<const void*>(options
.take()));
731 ui
->cbxTemplate
->setCurrentIndex(i
);
734 if(!options
.isNull())
736 const int index
= ui
->cbxTemplate
->model()->rowCount();
737 ui
->cbxTemplate
->insertItem(index
, name
, QVariant::fromValue
<const void*>(options
.take()));
738 ui
->cbxTemplate
->setCurrentIndex(index
);
740 ui
->cbxTemplate
->blockSignals(false);
742 m_lastTemplateName
= name
;
746 void AddJobDialog::deleteTemplateButtonClicked(void)
748 const int index
= ui
->cbxTemplate
->currentIndex();
749 QString name
= ui
->cbxTemplate
->itemText(index
);
751 if(name
.contains('<') || name
.contains('>') || name
.contains('\\') || name
.contains('/'))
753 QMessageBox::warning (this, tr("Invalid Item"), tr("Sorry, the selected item cannot be deleted!"));
757 int ret
= QMessageBox::question (this, tr("Delete Template"), tr("<nobr>Do you really want to delete the selected template?<br><b>%1</b></nobr>").arg(name
), QMessageBox::Yes
| QMessageBox::No
, QMessageBox::No
);
758 if(ret
!= QMessageBox::Yes
)
763 OptionsModel::deleteTemplate(name
);
764 const OptionsModel
*item
= reinterpret_cast<const OptionsModel
*>(ui
->cbxTemplate
->itemData(index
).value
<const void*>());
765 ui
->cbxTemplate
->removeItem(index
);
769 void AddJobDialog::editorActionTriggered(void)
772 if(QAction
*action
= dynamic_cast<QAction
*>(QObject::sender()))
774 QLineEdit
*lineEdit
= reinterpret_cast<QLineEdit
*>(action
->data().value
<void*>());
776 EditorDialog
*editor
= new EditorDialog(this);
777 editor
->setEditText(lineEdit
->text());
779 if(editor
->exec() == QDialog::Accepted
)
781 lineEdit
->setText(editor
->getEditText());
784 MUTILS_DELETE(editor
);
788 void AddJobDialog::copyActionTriggered(void)
790 if(QAction
*action
= dynamic_cast<QAction
*>(QObject::sender()))
792 QClipboard
*clipboard
= QApplication::clipboard();
793 QLineEdit
*lineEdit
= reinterpret_cast<QLineEdit
*>(action
->data().value
<void*>());
794 QString text
= lineEdit
->hasSelectedText() ? lineEdit
->selectedText() : lineEdit
->text();
795 clipboard
->setText(text
);
799 void AddJobDialog::pasteActionTriggered(void)
801 if(QAction
*action
= dynamic_cast<QAction
*>(QObject::sender()))
803 QClipboard
*clipboard
= QApplication::clipboard();
804 QLineEdit
*lineEdit
= reinterpret_cast<QLineEdit
*>(action
->data().value
<void*>());
805 QString text
= clipboard
->text();
806 if(!text
.isEmpty()) lineEdit
->setText(text
);
810 ///////////////////////////////////////////////////////////////////////////////
812 ///////////////////////////////////////////////////////////////////////////////
814 QString
AddJobDialog::sourceFile(void)
816 return QDir::fromNativeSeparators(ui
->editSource
->text());
819 QString
AddJobDialog::outputFile(void)
821 return QDir::fromNativeSeparators(ui
->editOutput
->text());
824 bool AddJobDialog::runImmediately(void)
826 return ui
->checkBoxRun
->isChecked();
829 bool AddJobDialog::applyToAll(void)
831 return ui
->checkBoxApplyToAll
->isChecked();
834 void AddJobDialog::setRunImmediately(bool run
)
836 ui
->checkBoxRun
->setChecked(run
);
839 void AddJobDialog::setSourceFile(const QString
&path
)
841 ui
->editSource
->setText(QDir::toNativeSeparators(path
));
844 void AddJobDialog::setOutputFile(const QString
&path
)
846 ui
->editOutput
->setText(QDir::toNativeSeparators(path
));}
848 void AddJobDialog::setSourceEditable(const bool editable
)
850 ui
->buttonBrowseSource
->setEnabled(editable
);
853 void AddJobDialog::setApplyToAllVisible(const bool visible
)
855 ui
->checkBoxApplyToAll
->setVisible(visible
);
858 ///////////////////////////////////////////////////////////////////////////////
860 ///////////////////////////////////////////////////////////////////////////////
862 void AddJobDialog::loadTemplateList(void)
864 ui
->cbxTemplate
->addItem(tr("<Default>"), QVariant::fromValue
<const void*>(m_defaults
));
865 ui
->cbxTemplate
->setCurrentIndex(0);
867 QMap
<QString
, OptionsModel
*> templates
= OptionsModel::loadAllTemplates(m_sysinfo
);
868 QStringList templateNames
= templates
.keys();
869 templateNames
.sort();
871 for(QStringList::ConstIterator current
= templateNames
.constBegin(); current
!= templateNames
.constEnd(); current
++)
873 OptionsModel
*currentTemplate
= templates
.take(*current
);
874 ui
->cbxTemplate
->addItem(*current
, QVariant::fromValue
<const void*>(currentTemplate
));
875 if(currentTemplate
->equals(m_options
))
877 ui
->cbxTemplate
->setCurrentIndex(ui
->cbxTemplate
->count() - 1);
881 if((ui
->cbxTemplate
->currentIndex() == 0) && (!m_options
->equals(m_defaults
)))
883 qWarning("Not the default -> recently used!");
884 ui
->cbxTemplate
->insertItem(1, tr("<Recently Used>"), QVariant::fromValue
<const void*>(m_options
));
885 ui
->cbxTemplate
->setCurrentIndex(1);
889 void AddJobDialog::updateComboBox(QComboBox
*const cbox
, const QString
&text
)
892 if(QAbstractItemModel
*model
= cbox
->model())
894 for(int i
= 0; i
< cbox
->model()->rowCount(); i
++)
896 if(model
->data(model
->index(i
, 0, QModelIndex())).toString().compare(text
, Qt::CaseInsensitive
) == 0)
903 cbox
->setCurrentIndex(index
);
906 void AddJobDialog::updateComboBox(QComboBox
*const cbox
, const int &data
)
909 if (QAbstractItemModel
*model
= cbox
->model())
911 for (int i
= 0; i
< cbox
->model()->rowCount(); i
++)
913 if (cbox
->itemData(i
).toInt() == data
)
920 cbox
->setCurrentIndex(index
);
923 void AddJobDialog::updateComboBox(QComboBox
*const cbox
, const quint32
&data
)
926 if(QAbstractItemModel
*model
= cbox
->model())
928 for(int i
= 0; i
< cbox
->model()->rowCount(); i
++)
930 if(cbox
->itemData(i
).toUInt() == data
)
937 cbox
->setCurrentIndex(index
);
940 void AddJobDialog::restoreOptions(const OptionsModel
*options
)
942 DisableHelperRAII
disable(&m_monitorConfigChanges
);
944 updateComboBox(ui
->cbxEncoderType
, options
->encType());
945 updateComboBox(ui
->cbxEncoderArch
, options
->encArch());
946 updateComboBox(ui
->cbxEncoderVariant
, options
->encVariant());
947 updateComboBox(ui
->cbxRateControlMode
, options
->rcMode());
949 ui
->spinQuantizer
->setValue(options
->quantizer());
950 ui
->spinBitrate
->setValue(options
->bitrate());
952 updateComboBox(ui
->cbxPreset
, options
->preset());
953 updateComboBox(ui
->cbxTuning
, options
->tune());
954 updateComboBox(ui
->cbxProfile
, options
->profile());
956 ui
->editCustomX264Params
->setText(options
->customEncParams());
957 ui
->editCustomAvs2YUVParams
->setText(options
->customAvs2YUV());
960 void AddJobDialog::saveOptions(OptionsModel
*options
)
962 options
->setEncType(static_cast<OptionsModel::EncType
>(ui
->cbxEncoderType
->itemData(ui
->cbxEncoderType
->currentIndex()).toInt()));
964 options
->setEncArch (ui
->cbxEncoderArch
->itemData(ui
->cbxEncoderArch
->currentIndex()).toUInt());
965 options
->setEncVariant(ui
->cbxEncoderVariant
->itemData(ui
->cbxEncoderVariant
->currentIndex()).toUInt());
966 options
->setRCMode (ui
->cbxRateControlMode
->itemData(ui
->cbxRateControlMode
->currentIndex()).toUInt());
968 options
->setQuantizer(ui
->spinQuantizer
->value());
969 options
->setBitrate (ui
->spinBitrate
->value());
971 options
->setPreset (ui
->cbxPreset
->model()->data(ui
->cbxPreset
->model()->index(ui
->cbxPreset
->currentIndex(), 0)).toString());
972 options
->setTune (ui
->cbxTuning
->model()->data(ui
->cbxTuning
->model()->index(ui
->cbxTuning
->currentIndex(), 0)).toString());
973 options
->setProfile(ui
->cbxProfile
->model()->data(ui
->cbxProfile
->model()->index(ui
->cbxProfile
->currentIndex(), 0)).toString());
975 options
->setCustomEncParams(ui
->editCustomX264Params
->hasAcceptableInput() ? ui
->editCustomX264Params
->text().simplified() : QString());
976 options
->setCustomAvs2YUV(ui
->editCustomAvs2YUVParams
->hasAcceptableInput() ? ui
->editCustomAvs2YUVParams
->text().simplified() : QString());
979 QString
AddJobDialog::currentSourcePath(const bool bWithName
)
981 QString path
= m_recentlyUsed
->sourceDirectory();
982 QString currentSourceFile
= this->sourceFile();
984 if(!currentSourceFile
.isEmpty())
986 QString currentSourceDir
= QFileInfo(currentSourceFile
).absolutePath();
987 if(VALID_DIR(currentSourceDir
))
989 path
= currentSourceDir
;
993 path
.append("/").append(QFileInfo(currentSourceFile
).fileName());
1000 QString
AddJobDialog::currentOutputPath(const bool bWithName
)
1002 QString path
= m_recentlyUsed
->outputDirectory();
1003 QString currentOutputFile
= this->outputFile();
1005 if(!currentOutputFile
.isEmpty())
1007 QString currentOutputDir
= QFileInfo(currentOutputFile
).absolutePath();
1008 if(VALID_DIR(currentOutputDir
))
1010 path
= currentOutputDir
;
1014 path
.append("/").append(QFileInfo(currentOutputFile
).fileName());
1021 int AddJobDialog::currentOutputIndx(void)
1023 const OptionsModel::EncType encType
= static_cast<OptionsModel::EncType
>(ui
->cbxEncoderType
->itemData(ui
->cbxEncoderType
->currentIndex()).toInt());
1024 if(encType
== OptionsModel::EncType_X265
)
1026 return ARRAY_SIZE(X264_FILE_TYPE_FILTERS
) - 1;
1029 int index
= m_recentlyUsed
->filterIndex();
1030 const QString currentOutputFile
= this->outputFile();
1032 if(!currentOutputFile
.isEmpty())
1034 const QString currentOutputExtn
= QFileInfo(currentOutputFile
).suffix();
1035 const int tempIndex
= getFilterIdx(currentOutputExtn
);
1045 ///////////////////////////////////////////////////////////////////////////////
1047 ///////////////////////////////////////////////////////////////////////////////
1049 QString
AddJobDialog::generateOutputFileName(const QString
&sourceFilePath
, const QString
&destinationDirectory
, const int filterIndex
, const bool saveToSourceDir
)
1051 QString name
= QFileInfo(sourceFilePath
).completeBaseName();
1052 QString path
= saveToSourceDir
? QFileInfo(sourceFilePath
).canonicalPath() : destinationDirectory
;
1053 QString fext
= getFilterExt(filterIndex
);
1055 if(!VALID_DIR(path
))
1057 RecentlyUsed defaults
;
1058 path
= defaults
.outputDirectory();
1061 QString outPath
= QString("%1/%2.%3").arg(path
, name
, fext
);
1064 while(QFileInfo(outPath
).exists())
1066 outPath
= QString("%1/%2 (%3).%4").arg(path
, name
, QString::number(n
++), fext
);
1072 /* ------------------------------------------------------------------------- */
1074 QString
AddJobDialog::getFilterExt(const int filterIndex
)
1076 const int count
= ARRAY_SIZE(X264_FILE_TYPE_FILTERS
);
1078 if((filterIndex
>= 0) && (filterIndex
< count
))
1080 return QString::fromLatin1(X264_FILE_TYPE_FILTERS
[filterIndex
].pcExt
);
1083 return QString::fromLatin1(X264_FILE_TYPE_FILTERS
[0].pcExt
);
1086 int AddJobDialog::getFilterIdx(const QString
&fileExt
)
1088 const int count
= ARRAY_SIZE(X264_FILE_TYPE_FILTERS
);
1090 for(int i
= 0; i
< count
; i
++)
1092 if(fileExt
.compare(QString::fromLatin1(X264_FILE_TYPE_FILTERS
[i
].pcExt
), Qt::CaseInsensitive
) == 0)
1101 QString
AddJobDialog::getFilterStr(const int filterIndex
)
1103 const int count
= ARRAY_SIZE(X264_FILE_TYPE_FILTERS
);
1105 if((filterIndex
>= 0) && (filterIndex
< count
))
1107 return QString("%1 (*.%2)").arg(QString::fromLatin1(X264_FILE_TYPE_FILTERS
[filterIndex
].pcStr
), QString::fromLatin1(X264_FILE_TYPE_FILTERS
[filterIndex
].pcExt
));
1110 return QString("%1 (*.%2)").arg(QString::fromLatin1(X264_FILE_TYPE_FILTERS
[0].pcStr
), QString::fromLatin1(X264_FILE_TYPE_FILTERS
[0].pcExt
));
1113 QString
AddJobDialog::getFilterLst(void)
1115 QStringList filters
;
1116 const int count
= ARRAY_SIZE(X264_FILE_TYPE_FILTERS
);
1118 for(int i
= 0; i
< count
; i
++)
1120 filters
<< QString("%1 (*.%2)").arg(QString::fromLatin1(X264_FILE_TYPE_FILTERS
[i
].pcStr
), QString::fromLatin1(X264_FILE_TYPE_FILTERS
[i
].pcExt
));
1123 return filters
.join(";;");
1126 QString
AddJobDialog::getInputFilterLst(void)
1135 {"Avisynth Scripts", "avs"},
1136 {"VapourSynth Scripts", "vpy"},
1137 {"Matroska Files", "mkv"},
1138 {"MPEG-4 Part 14 Container", "mp4"},
1139 {"Audio Video Interleaved", "avi"},
1140 {"Flash Video", "flv"},
1141 {"YUV4MPEG2 Stream", "y4m"},
1142 {"Uncompresses YUV Data", "yuv"},
1145 const int count
= ARRAY_SIZE(s_filters
);
1148 for(size_t index
= 0; index
< count
; index
++)
1151 allTypes
+= QString((index
> 0) ? " *.%1" : "*.%1").arg(QString::fromLatin1(s_filters
[index
].fext
));
1154 QStringList filters
;
1155 filters
<< QString("All supported files (%1)").arg(allTypes
);
1157 for(size_t index
= 0; index
< count
; index
++)
1159 filters
<< QString("%1 (*.%2)").arg(QString::fromLatin1(s_filters
[index
].name
), QString::fromLatin1(s_filters
[index
].fext
));
1162 filters
<< QString("All files (*.*)");
1163 return filters
.join(";;");