1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2014 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 ///////////////////////////////////////////////////////////////////////////////
23 #include "uic_win_main.h"
26 #include "model_status.h"
27 #include "model_jobList.h"
28 #include "model_options.h"
29 #include "model_preferences.h"
30 #include "model_recently.h"
31 #include "thread_avisynth.h"
32 #include "thread_vapoursynth.h"
33 #include "thread_encode.h"
35 #include "win_addJob.h"
36 #include "win_preferences.h"
37 #include "win_updater.h"
42 #include <QCloseEvent>
43 #include <QMessageBox>
44 #include <QDesktopServices>
49 #include <QProgressDialog>
51 #include <QTextStream>
53 #include <QFileDialog>
57 const char *home_url = "http://muldersoft.com/";
58 const char *update_url = "https://github.com/lordmulder/Simple-x264-Launcher/releases/latest";
59 const char *tpl_last = "<LAST_USED>";
61 #define SET_FONT_BOLD(WIDGET,BOLD) do { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); } while(0)
62 #define SET_TEXT_COLOR(WIDGET,COLOR) do { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, (COLOR)); _palette.setColor(QPalette::Text, (COLOR)); WIDGET->setPalette(_palette); } while(0)
63 #define LINK(URL) "<a href=\"" URL "\">" URL "</a>"
64 #define INIT_ERROR_EXIT() do { m_initialized = true; close(); qApp->exit(-1); return; } while(0)
66 //static int exceptionFilter(_EXCEPTION_RECORD *dst, _EXCEPTION_POINTERS *src) { memcpy(dst, src->ExceptionRecord, sizeof(_EXCEPTION_RECORD)); return EXCEPTION_EXECUTE_HANDLER; }
68 ///////////////////////////////////////////////////////////////////////////////
69 // Constructor & Destructor
70 ///////////////////////////////////////////////////////////////////////////////
75 MainWindow::MainWindow(const x264_cpu_t *const cpuFeatures)
77 m_cpuFeatures(cpuFeatures),
78 m_appDir(QApplication::applicationDirPath()),
84 m_skipVersionTest(false),
85 m_abortOnTimeout(true),
88 ui(new Ui::MainWindow())
90 //Init the dialog, from the .ui file
92 setWindowFlags(windowFlags() & (~Qt::WindowMaximizeButtonHint));
95 qRegisterMetaType<QUuid>("QUuid");
96 qRegisterMetaType<QUuid>("DWORD");
97 qRegisterMetaType<JobStatus>("JobStatus");
100 m_preferences = new PreferencesModel();
101 PreferencesModel::loadPreferences(m_preferences);
104 m_recentlyUsed = new RecentlyUsed();
105 RecentlyUsed::loadRecentlyUsed(m_recentlyUsed);
107 //Create options object
108 m_options = new OptionsModel();
109 OptionsModel::loadTemplate(m_options, QString::fromLatin1(tpl_last));
111 //Create IPC thread object
112 //m_ipcThread = new IPCThread();
113 //connect(m_ipcThread, SIGNAL(instanceCreated(unsigned int)), this, SLOT(instanceCreated(unsigned int)), Qt::QueuedConnection);
115 //Freeze minimum size
116 setMinimumSize(size());
117 ui->splitter->setSizes(QList<int>() << 16 << 196);
120 ui->labelBuildDate->setText(tr("Built on %1 at %2").arg(x264_version_date().toString(Qt::ISODate), QString::fromLatin1(x264_version_time())));
121 ui->labelBuildDate->installEventFilter(this);
122 setWindowTitle(QString("%1 (%2 Mode)").arg(windowTitle(), m_cpuFeatures->x64 ? "64-Bit" : "32-Bit"));
125 setWindowTitle(QString("%1 | !!! DEBUG VERSION !!!").arg(windowTitle()));
126 setStyleSheet("QMenuBar, QMainWindow { background-color: yellow }");
128 else if(x264_is_prerelease())
130 setWindowTitle(QString("%1 | PRE-RELEASE VERSION").arg(windowTitle()));
134 m_jobList = new JobListModel(m_preferences);
135 connect(m_jobList, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(jobChangedData(QModelIndex, QModelIndex)));
136 ui->jobsView->setModel(m_jobList);
139 ui->jobsView->horizontalHeader()->setSectionHidden(3, true);
140 ui->jobsView->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
141 ui->jobsView->horizontalHeader()->setResizeMode(1, QHeaderView::Fixed);
142 ui->jobsView->horizontalHeader()->setResizeMode(2, QHeaderView::Fixed);
143 ui->jobsView->horizontalHeader()->resizeSection(1, 150);
144 ui->jobsView->horizontalHeader()->resizeSection(2, 90);
145 ui->jobsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
146 connect(ui->jobsView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(jobSelected(QModelIndex, QModelIndex)));
148 //Create context menu
149 QAction *actionClipboard = new QAction(QIcon(":/buttons/page_paste.png"), tr("Copy to Clipboard"), ui->logView);
150 actionClipboard->setEnabled(false);
151 ui->logView->addAction(actionClipboard);
152 connect(actionClipboard, SIGNAL(triggered(bool)), this, SLOT(copyLogToClipboard(bool)));
153 ui->jobsView->addActions(ui->menuJob->actions());
156 connect(ui->buttonAddJob, SIGNAL(clicked()), this, SLOT(addButtonPressed()));
157 connect(ui->buttonStartJob, SIGNAL(clicked()), this, SLOT(startButtonPressed()));
158 connect(ui->buttonAbortJob, SIGNAL(clicked()), this, SLOT(abortButtonPressed()));
159 connect(ui->buttonPauseJob, SIGNAL(toggled(bool)), this, SLOT(pauseButtonPressed(bool)));
160 connect(ui->actionJob_Delete, SIGNAL(triggered()), this, SLOT(deleteButtonPressed()));
161 connect(ui->actionJob_Restart, SIGNAL(triggered()), this, SLOT(restartButtonPressed()));
162 connect(ui->actionJob_Browse, SIGNAL(triggered()), this, SLOT(browseButtonPressed()));
165 connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(openActionTriggered()));
166 connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAbout()));
167 connect(ui->actionWebMulder, SIGNAL(triggered()), this, SLOT(showWebLink()));
168 connect(ui->actionWebX264, SIGNAL(triggered()), this, SLOT(showWebLink()));
169 connect(ui->actionWebKomisar, SIGNAL(triggered()), this, SLOT(showWebLink()));
170 connect(ui->actionWebVideoLAN, SIGNAL(triggered()), this, SLOT(showWebLink()));
171 connect(ui->actionWebJEEB, SIGNAL(triggered()), this, SLOT(showWebLink()));
172 connect(ui->actionWebAvisynth32, SIGNAL(triggered()), this, SLOT(showWebLink()));
173 connect(ui->actionWebAvisynth64, SIGNAL(triggered()), this, SLOT(showWebLink()));
174 connect(ui->actionWebVapourSynth, SIGNAL(triggered()), this, SLOT(showWebLink()));
175 connect(ui->actionWebVapourSynthDocs, SIGNAL(triggered()), this, SLOT(showWebLink()));
176 connect(ui->actionWebWiki, SIGNAL(triggered()), this, SLOT(showWebLink()));
177 connect(ui->actionWebBluRay, SIGNAL(triggered()), this, SLOT(showWebLink()));
178 connect(ui->actionWebAvsWiki, SIGNAL(triggered()), this, SLOT(showWebLink()));
179 connect(ui->actionWebSecret, SIGNAL(triggered()), this, SLOT(showWebLink()));
180 connect(ui->actionWebSupport, SIGNAL(triggered()), this, SLOT(showWebLink()));
181 connect(ui->actionPreferences, SIGNAL(triggered()), this, SLOT(showPreferences()));
182 connect(ui->actionCheckForUpdates, SIGNAL(triggered()), this, SLOT(checkUpdates()));
184 //Create floating label
185 m_label = new QLabel(ui->jobsView->viewport());
186 m_label->setText(tr("No job created yet. Please click the 'Add New Job' button!"));
187 m_label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
188 SET_TEXT_COLOR(m_label, Qt::darkGray);
189 SET_FONT_BOLD(m_label, true);
190 m_label->setVisible(true);
191 m_label->setContextMenuPolicy(Qt::ActionsContextMenu);
192 m_label->addActions(ui->jobsView->actions());
193 connect(ui->splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(updateLabelPos()));
200 MainWindow::~MainWindow(void)
202 OptionsModel::saveTemplate(m_options, QString::fromLatin1(tpl_last));
204 X264_DELETE(m_jobList);
205 X264_DELETE(m_options);
206 X264_DELETE(m_droppedFiles);
207 X264_DELETE(m_label);
209 while(!m_toolsList.isEmpty())
211 QFile *temp = m_toolsList.takeFirst();
216 if(m_ipcThread->isRunning())
218 m_ipcThread->setAbort();
219 if(!m_ipcThread->wait(5000))
221 m_ipcThread->terminate();
227 //X264_DELETE(m_ipcThread);
228 X264_DELETE(m_preferences);
229 X264_DELETE(m_recentlyUsed);
230 VapourSynthCheckThread::unload();
231 AvisynthCheckThread::unload();
236 ///////////////////////////////////////////////////////////////////////////////
238 ///////////////////////////////////////////////////////////////////////////////
241 * The "add" button was clicked
243 void MainWindow::addButtonPressed()
245 qDebug("MainWindow::addButtonPressed");
246 bool runImmediately = (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
247 QString sourceFileName, outputFileName;
249 if(createJob(sourceFileName, outputFileName, m_options, runImmediately))
251 appendJob(sourceFileName, outputFileName, m_options, runImmediately);
256 * The "open" action was triggered
258 void MainWindow::openActionTriggered()
260 QStringList fileList = QFileDialog::getOpenFileNames(this, tr("Open Source File(s)"), m_recentlyUsed->sourceDirectory(), AddJobDialog::getInputFilterLst(), NULL, QFileDialog::DontUseNativeDialog);
261 if(!fileList.empty())
263 m_recentlyUsed->setSourceDirectory(QFileInfo(fileList.last()).absolutePath());
264 if(fileList.count() > 1)
266 createJobMultiple(fileList);
270 bool runImmediately = (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
271 QString sourceFileName(fileList.first()), outputFileName;
272 if(createJob(sourceFileName, outputFileName, m_options, runImmediately))
274 appendJob(sourceFileName, outputFileName, m_options, runImmediately);
281 * The "start" button was clicked
283 void MainWindow::startButtonPressed(void)
285 m_jobList->startJob(ui->jobsView->currentIndex());
289 * The "abort" button was clicked
291 void MainWindow::abortButtonPressed(void)
293 m_jobList->abortJob(ui->jobsView->currentIndex());
297 * The "delete" button was clicked
299 void MainWindow::deleteButtonPressed(void)
301 m_jobList->deleteJob(ui->jobsView->currentIndex());
302 m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0);
306 * The "browse" button was clicked
308 void MainWindow::browseButtonPressed(void)
310 QString outputFile = m_jobList->getJobOutputFile(ui->jobsView->currentIndex());
311 if((!outputFile.isEmpty()) && QFileInfo(outputFile).exists() && QFileInfo(outputFile).isFile())
313 QProcess::startDetached(QString::fromLatin1("explorer.exe"), QStringList() << QString::fromLatin1("/select,") << QDir::toNativeSeparators(outputFile), QFileInfo(outputFile).path());
317 QMessageBox::warning(this, tr("Not Found"), tr("Sorry, the output file could not be found!"));
322 * The "pause" button was clicked
324 void MainWindow::pauseButtonPressed(bool checked)
328 m_jobList->pauseJob(ui->jobsView->currentIndex());
332 m_jobList->resumeJob(ui->jobsView->currentIndex());
337 * The "restart" button was clicked
339 void MainWindow::restartButtonPressed(void)
341 const QModelIndex index = ui->jobsView->currentIndex();
342 const OptionsModel *options = m_jobList->getJobOptions(index);
343 QString sourceFileName = m_jobList->getJobSourceFile(index);
344 QString outputFileName = m_jobList->getJobOutputFile(index);
346 if((options) && (!sourceFileName.isEmpty()) && (!outputFileName.isEmpty()))
348 bool runImmediately = (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1)); //bool runImmediately = true;
349 OptionsModel *tempOptions = new OptionsModel(*options);
350 if(createJob(sourceFileName, outputFileName, tempOptions, runImmediately, true))
352 appendJob(sourceFileName, outputFileName, tempOptions, runImmediately);
354 X264_DELETE(tempOptions);
359 * Job item selected by user
361 void MainWindow::jobSelected(const QModelIndex & current, const QModelIndex & previous)
363 qDebug("Job selected: %d", current.row());
365 if(ui->logView->model())
367 disconnect(ui->logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int)));
370 if(current.isValid())
372 ui->logView->setModel(m_jobList->getLogFile(current));
373 connect(ui->logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int)));
374 ui->logView->actions().first()->setEnabled(true);
375 QTimer::singleShot(0, ui->logView, SLOT(scrollToBottom()));
377 ui->progressBar->setValue(m_jobList->getJobProgress(current));
378 ui->editDetails->setText(m_jobList->data(m_jobList->index(current.row(), 3, QModelIndex()), Qt::DisplayRole).toString());
379 updateButtons(m_jobList->getJobStatus(current));
380 updateTaskbar(m_jobList->getJobStatus(current), m_jobList->data(m_jobList->index(current.row(), 0, QModelIndex()), Qt::DecorationRole).value<QIcon>());
384 ui->logView->setModel(NULL);
385 ui->logView->actions().first()->setEnabled(false);
386 ui->progressBar->setValue(0);
387 ui->editDetails->clear();
388 updateButtons(JobStatus_Undefined);
389 updateTaskbar(JobStatus_Undefined, QIcon());
392 ui->progressBar->repaint();
396 * Handle update of job info (status, progress, details, etc)
398 void MainWindow::jobChangedData(const QModelIndex &topLeft, const QModelIndex &bottomRight)
400 int selected = ui->jobsView->currentIndex().row();
402 if(topLeft.column() <= 1 && bottomRight.column() >= 1) /*STATUS*/
404 for(int i = topLeft.row(); i <= bottomRight.row(); i++)
406 JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex()));
409 qDebug("Current job changed status!");
410 updateButtons(status);
411 updateTaskbar(status, m_jobList->data(m_jobList->index(i, 0, QModelIndex()), Qt::DecorationRole).value<QIcon>());
413 if((status == JobStatus_Completed) || (status == JobStatus_Failed))
415 if(m_preferences->autoRunNextJob()) QTimer::singleShot(0, this, SLOT(launchNextJob()));
416 if(m_preferences->shutdownComputer()) QTimer::singleShot(0, this, SLOT(shutdownComputer()));
417 if(m_preferences->saveLogFiles()) saveLogFile(m_jobList->index(i, 1, QModelIndex()));
421 if(topLeft.column() <= 2 && bottomRight.column() >= 2) /*PROGRESS*/
423 for(int i = topLeft.row(); i <= bottomRight.row(); i++)
427 ui->progressBar->setValue(m_jobList->getJobProgress(m_jobList->index(i, 0, QModelIndex())));
428 WinSevenTaskbar::setTaskbarProgress(this, ui->progressBar->value(), ui->progressBar->maximum());
433 if(topLeft.column() <= 3 && bottomRight.column() >= 3) /*DETAILS*/
435 for(int i = topLeft.row(); i <= bottomRight.row(); i++)
439 ui->editDetails->setText(m_jobList->data(m_jobList->index(i, 3, QModelIndex()), Qt::DisplayRole).toString());
447 * Handle new log file content
449 void MainWindow::jobLogExtended(const QModelIndex & parent, int start, int end)
451 QTimer::singleShot(0, ui->logView, SLOT(scrollToBottom()));
457 void MainWindow::showAbout(void)
461 text += QString().sprintf("<nobr><tt>Simple x264 Launcher v%u.%02u.%u - use 64-Bit x264 with 32-Bit Avisynth<br>", x264_version_major(), x264_version_minor(), x264_version_build());
462 text += QString().sprintf("Copyright (c) 2004-%04d LoRd_MuldeR <mulder2@gmx.de>. Some rights reserved.<br>", qMax(x264_version_date().year(),QDate::currentDate().year()));
463 text += QString().sprintf("Built on %s at %s with %s for Win-%s.<br><br>", x264_version_date().toString(Qt::ISODate).toLatin1().constData(), x264_version_time(), x264_version_compiler(), x264_version_arch());
464 text += QString().sprintf("This program is free software: you can redistribute it and/or modify<br>");
465 text += QString().sprintf("it under the terms of the GNU General Public License <http://www.gnu.org/>.<br>");
466 text += QString().sprintf("Note that this program is distributed with ABSOLUTELY NO WARRANTY.<br><br>");
467 text += QString().sprintf("Please check the web-site at <a href=\"%s\">%s</a> for updates !!!<br></tt></nobr>", home_url, home_url);
469 QMessageBox aboutBox(this);
470 aboutBox.setIconPixmap(QIcon(":/images/movie.png").pixmap(64,64));
471 aboutBox.setWindowTitle(tr("About..."));
472 aboutBox.setText(text.replace("-", "−"));
473 aboutBox.addButton(tr("About x264"), QMessageBox::NoRole);
474 aboutBox.addButton(tr("About AVS"), QMessageBox::NoRole);
475 aboutBox.addButton(tr("About VPY"), QMessageBox::NoRole);
476 aboutBox.addButton(tr("About Qt"), QMessageBox::NoRole);
477 aboutBox.setEscapeButton(aboutBox.addButton(tr("Close"), QMessageBox::NoRole));
481 x264_beep(x264_beep_info);
482 switch(aboutBox.exec())
487 text2 += tr("<nobr><tt>x264 - the best H.264/AVC encoder. Copyright (c) 2003-2013 x264 project.<br>");
488 text2 += tr("Free software library for encoding video streams into the H.264/MPEG-4 AVC format.<br>");
489 text2 += tr("Released under the terms of the GNU General Public License.<br><br>");
490 text2 += tr("Please visit <a href=\"%1\">%1</a> for obtaining a commercial x264 license.<br>").arg("http://x264licensing.com/");
491 text2 += tr("Read the <a href=\"%1\">user's manual</a> to get started and use the <a href=\"%2\">support forum</a> for help!<br></tt></nobr>").arg("http://mewiki.project357.com/wiki/X264_Settings", "http://forum.doom9.org/forumdisplay.php?f=77");
493 QMessageBox x264Box(this);
494 x264Box.setIconPixmap(QIcon(":/images/x264.png").pixmap(48,48));
495 x264Box.setWindowTitle(tr("About x264"));
496 x264Box.setText(text2.replace("-", "−"));
497 x264Box.setEscapeButton(x264Box.addButton(tr("Close"), QMessageBox::NoRole));
498 x264_beep(x264_beep_info);
505 text2 += tr("<nobr><tt>Avisynth - powerful video processing scripting language.<br>");
506 text2 += tr("Copyright (c) 2000 Ben Rudiak-Gould and all subsequent developers.<br>");
507 text2 += tr("Released under the terms of the GNU General Public License.<br><br>");
508 text2 += tr("Please visit the web-site <a href=\"%1\">%1</a> for more information.<br>").arg("http://avisynth.org/");
509 text2 += tr("Read the <a href=\"%1\">guide</a> to get started and use the <a href=\"%2\">support forum</a> for help!<br></tt></nobr>").arg("http://avisynth.nl/index.php/First_script", "http://forum.doom9.org/forumdisplay.php?f=33");
511 QMessageBox x264Box(this);
512 x264Box.setIconPixmap(QIcon(":/images/avisynth.png").pixmap(48,67));
513 x264Box.setWindowTitle(tr("About Avisynth"));
514 x264Box.setText(text2.replace("-", "−"));
515 x264Box.setEscapeButton(x264Box.addButton(tr("Close"), QMessageBox::NoRole));
516 x264_beep(x264_beep_info);
523 text2 += tr("<nobr><tt>VapourSynth - application for video manipulation based on Python.<br>");
524 text2 += tr("Copyright (c) 2012 Fredrik Mellbin.<br>");
525 text2 += tr("Released under the terms of the GNU Lesser General Public.<br><br>");
526 text2 += tr("Please visit the web-site <a href=\"%1\">%1</a> for more information.<br>").arg("http://www.vapoursynth.com/");
527 text2 += tr("Read the <a href=\"%1\">documentation</a> to get started and use the <a href=\"%2\">support forum</a> for help!<br></tt></nobr>").arg("http://www.vapoursynth.com/doc/", "http://forum.doom9.org/showthread.php?t=165771");
529 QMessageBox x264Box(this);
530 x264Box.setIconPixmap(QIcon(":/images/python.png").pixmap(48,48));
531 x264Box.setWindowTitle(tr("About VapourSynth"));
532 x264Box.setText(text2.replace("-", "−"));
533 x264Box.setEscapeButton(x264Box.addButton(tr("Close"), QMessageBox::NoRole));
534 x264_beep(x264_beep_info);
539 QMessageBox::aboutQt(this);
550 void MainWindow::showWebLink(void)
552 if(QObject::sender() == ui->actionWebMulder) QDesktopServices::openUrl(QUrl(home_url));
553 if(QObject::sender() == ui->actionWebX264) QDesktopServices::openUrl(QUrl("http://www.x264.com/"));
554 if(QObject::sender() == ui->actionWebKomisar) QDesktopServices::openUrl(QUrl("http://komisar.gin.by/"));
555 if(QObject::sender() == ui->actionWebVideoLAN) QDesktopServices::openUrl(QUrl("http://download.videolan.org/pub/x264/binaries/"));
556 if(QObject::sender() == ui->actionWebJEEB) QDesktopServices::openUrl(QUrl("http://x264.fushizen.eu/"));
557 if(QObject::sender() == ui->actionWebAvisynth32) QDesktopServices::openUrl(QUrl("http://sourceforge.net/projects/avisynth2/files/AviSynth%202.5/"));
558 if(QObject::sender() == ui->actionWebAvisynth64) QDesktopServices::openUrl(QUrl("http://code.google.com/p/avisynth64/downloads/list"));
559 if(QObject::sender() == ui->actionWebVapourSynth) QDesktopServices::openUrl(QUrl("http://www.vapoursynth.com/"));
560 if(QObject::sender() == ui->actionWebVapourSynthDocs) QDesktopServices::openUrl(QUrl("http://www.vapoursynth.com/doc/"));
561 if(QObject::sender() == ui->actionWebWiki) QDesktopServices::openUrl(QUrl("http://mewiki.project357.com/wiki/X264_Settings"));
562 if(QObject::sender() == ui->actionWebBluRay) QDesktopServices::openUrl(QUrl("http://www.x264bluray.com/"));
563 if(QObject::sender() == ui->actionWebAvsWiki) QDesktopServices::openUrl(QUrl("http://avisynth.nl/index.php/Main_Page#Usage"));
564 if(QObject::sender() == ui->actionWebSupport) QDesktopServices::openUrl(QUrl("http://forum.doom9.org/showthread.php?t=144140"));
565 if(QObject::sender() == ui->actionWebSecret) QDesktopServices::openUrl(QUrl("http://www.youtube.com/watch_popup?v=AXIeHY-OYNI"));
569 * Pereferences dialog
571 void MainWindow::showPreferences(void)
573 PreferencesDialog *preferences = new PreferencesDialog(this, m_preferences, m_cpuFeatures->x64);
575 X264_DELETE(preferences);
579 * Launch next job, after running job has finished
581 void MainWindow::launchNextJob(void)
583 qDebug("launchNextJob(void)");
585 const int rows = m_jobList->rowCount(QModelIndex());
587 if(countRunningJobs() >= m_preferences->maxRunningJobCount())
589 qDebug("Still have too many jobs running, won't launch next one yet!");
593 int startIdx= ui->jobsView->currentIndex().isValid() ? qBound(0, ui->jobsView->currentIndex().row(), rows-1) : 0;
595 for(int i = 0; i < rows; i++)
597 int currentIdx = (i + startIdx) % rows;
598 JobStatus status = m_jobList->getJobStatus(m_jobList->index(currentIdx, 0, QModelIndex()));
599 if(status == JobStatus_Enqueued)
601 if(m_jobList->startJob(m_jobList->index(currentIdx, 0, QModelIndex())))
603 ui->jobsView->selectRow(currentIdx);
609 qWarning("No enqueued jobs left!");
613 * Save log to text file
615 void MainWindow::saveLogFile(const QModelIndex &index)
619 if(LogFileModel *log = m_jobList->getLogFile(index))
621 QDir(QString("%1/logs").arg(x264_data_path())).mkpath(".");
622 QString logFilePath = QString("%1/logs/LOG.%2.%3.txt").arg(x264_data_path(), QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString(Qt::ISODate).replace(':', "-"));
623 QFile outFile(logFilePath);
624 if(outFile.open(QIODevice::WriteOnly))
626 QTextStream outStream(&outFile);
627 outStream.setCodec("UTF-8");
628 outStream.setGenerateByteOrderMark(true);
630 const int rows = log->rowCount(QModelIndex());
631 for(int i = 0; i < rows; i++)
633 outStream << log->data(log->index(i, 0, QModelIndex()), Qt::DisplayRole).toString() << QLatin1String("\r\n");
639 qWarning("Failed to open log file for writing:\n%s", logFilePath.toUtf8().constData());
646 * Shut down the computer (with countdown)
648 void MainWindow::shutdownComputer(void)
650 qDebug("shutdownComputer(void)");
652 if(countPendingJobs() > 0)
654 qDebug("Still have pending jobs, won't shutdown yet!");
658 const int iTimeout = 30;
659 const Qt::WindowFlags flags = Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowSystemMenuHint;
660 const QString text = QString("%1%2%1").arg(QString().fill(' ', 18), tr("Warning: Computer will shutdown in %1 seconds..."));
662 qWarning("Initiating shutdown sequence!");
664 QProgressDialog progressDialog(text.arg(iTimeout), tr("Cancel Shutdown"), 0, iTimeout + 1, this, flags);
665 QPushButton *cancelButton = new QPushButton(tr("Cancel Shutdown"), &progressDialog);
666 cancelButton->setIcon(QIcon(":/buttons/power_on.png"));
667 progressDialog.setModal(true);
668 progressDialog.setAutoClose(false);
669 progressDialog.setAutoReset(false);
670 progressDialog.setWindowIcon(QIcon(":/buttons/power_off.png"));
671 progressDialog.setWindowTitle(windowTitle());
672 progressDialog.setCancelButton(cancelButton);
673 progressDialog.show();
675 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
676 QApplication::setOverrideCursor(Qt::WaitCursor);
677 x264_play_sound(IDR_WAVE1, false);
678 QApplication::restoreOverrideCursor();
681 timer.setInterval(1000);
684 QEventLoop eventLoop(this);
685 connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
686 connect(&progressDialog, SIGNAL(canceled()), &eventLoop, SLOT(quit()));
688 for(int i = 1; i <= iTimeout; i++)
691 if(progressDialog.wasCanceled())
693 progressDialog.close();
696 progressDialog.setValue(i+1);
697 progressDialog.setLabelText(text.arg(iTimeout-i));
698 if(iTimeout-i == 3) progressDialog.setCancelButton(NULL);
699 QApplication::processEvents();
700 x264_play_sound(((i < iTimeout) ? IDR_WAVE2 : IDR_WAVE3), false);
703 qWarning("Shutting down !!!");
705 if(x264_shutdown_computer("Simple x264 Launcher: All jobs completed, shutting down!", 10, true))
707 qApp->closeAllWindows();
712 * Main initialization function (called only once!)
714 void MainWindow::init(void)
716 static const char *binFiles = "x86/x264_8bit_x86.exe:x64/x264_8bit_x64.exe:x86/x264_10bit_x86.exe:x64/x264_10bit_x64.exe:x86/avs2yuv_x86.exe:x64/avs2yuv_x64.exe";
717 QStringList binaries = QString::fromLatin1(binFiles).split(":", QString::SkipEmptyParts);
721 //Check for a running instance
723 bool firstInstance = false;
724 if(m_ipcThread->initialize(&firstInstance))
726 m_ipcThread->start();
729 if(!m_ipcThread->wait(5000))
731 QMessageBox::warning(this, tr("Not Responding"), tr("<nobr>Another instance of this application is already running, but did not respond in time.<br>If the problem persists, please kill the running instance from the task manager!</nobr>"), tr("Quit"));
732 m_ipcThread->terminate();
741 while(!binaries.isEmpty())
743 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
744 QString current = binaries.takeFirst();
745 QFile *file = new QFile(QString("%1/toolset/%2").arg(m_appDir, current));
746 if(file->open(QIODevice::ReadOnly))
748 if(!x264_is_executable(file->fileName()))
750 QMessageBox::critical(this, tr("Invalid File!"), tr("<nobr>At least on required tool is not a valid Win32 or Win64 binary:<br><tt style=\"whitespace:nowrap\">%1</tt><br><br>Please re-install the program in order to fix the problem!</nobr>").arg(QDir::toNativeSeparators(QString("%1/toolset/%2").arg(m_appDir, current))).replace("-", "−"));
751 qFatal(QString("Binary is invalid: %1/toolset/%2").arg(m_appDir, current).toLatin1().constData());
759 QMessageBox::critical(this, tr("File Not Found!"), tr("<nobr>At least on required tool could not be found:<br><tt style=\"whitespace:nowrap\">%1</tt><br><br>Please re-install the program in order to fix the problem!</nobr>").arg(QDir::toNativeSeparators(QString("%1/toolset/%2").arg(m_appDir, current))).replace("-", "−"));
760 qFatal(QString("Binary not found: %1/toolset/%2").arg(m_appDir, current).toLatin1().constData());
765 //Check for portable mode
769 static const char *data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
770 QFile writeTest(QString("%1/%2").arg(x264_data_path(), QUuid::createUuid().toString()));
771 if(writeTest.open(QIODevice::WriteOnly))
773 ok = (writeTest.write(data) == strlen(data));
778 int val = QMessageBox::warning(this, tr("Write Test Failed"), tr("<nobr>The application was launched in portable mode, but the program path is <b>not</b> writable!</nobr>"), tr("Quit"), tr("Ignore"));
779 if(val != 1) INIT_ERROR_EXIT();
784 if(x264_is_prerelease())
786 qsrand(time(NULL)); int rnd = qrand() % 3;
787 int val = QMessageBox::information(this, tr("Pre-Release Version"), tr("Note: This is a pre-release version. Please do NOT use for production!<br>Click the button #%1 in order to continue...<br><br>(There will be no such message box in the final version of this application)").arg(QString::number(rnd + 1)), tr("(1)"), tr("(2)"), tr("(3)"), qrand() % 3);
788 if(rnd != val) INIT_ERROR_EXIT();
791 //Make sure this CPU can run x264 (requires MMX + MMXEXT/iSSE to run x264 with ASM enabled, additionally requires SSE1 for most x264 builds)
792 if(!(m_cpuFeatures->mmx && m_cpuFeatures->mmx2))
794 QMessageBox::critical(this, tr("Unsupported CPU"), tr("<nobr>Sorry, but this machine is <b>not</b> physically capable of running x264 (with assembly).<br>Please get a CPU that supports at least the MMX and MMXEXT instruction sets!</nobr>"), tr("Quit"));
795 qFatal("System does not support MMX and MMXEXT, x264 will not work !!!");
798 else if(!(m_cpuFeatures->mmx && m_cpuFeatures->sse))
800 qWarning("WARNING: System does not support SSE1, most x264 builds will not work !!!\n");
801 int val = QMessageBox::warning(this, tr("Unsupported CPU"), tr("<nobr>It appears that this machine does <b>not</b> support the SSE1 instruction set.<br>Thus most builds of x264 will <b>not</b> run on this computer at all.<br><br>Please get a CPU that supports the MMX and SSE1 instruction sets!</nobr>"), tr("Quit"), tr("Ignore"));
802 if(val != 1) INIT_ERROR_EXIT();
805 //Skip version check (not recommended!)
806 if(qApp->arguments().contains("--skip-x264-version-check", Qt::CaseInsensitive))
808 qWarning("x264 version check disabled, you have been warned!\n");
809 m_skipVersionTest = true;
812 //Don't abort encoding process on timeout (not recommended!)
813 if(qApp->arguments().contains("--no-deadlock-detection", Qt::CaseInsensitive))
815 qWarning("Deadlock detection disabled, you have been warned!\n");
816 m_abortOnTimeout = false;
819 //Check for Avisynth support
820 if(!qApp->arguments().contains("--skip-avisynth-check", Qt::CaseInsensitive))
822 qDebug("[Check for Avisynth support]");
823 volatile double avisynthVersion = 0.0;
824 const int result = AvisynthCheckThread::detect(&avisynthVersion);
827 QString text = tr("A critical error was encountered while checking your Avisynth version.").append("<br>");
828 text += tr("This is most likely caused by an erroneous Avisynth Plugin, please try to clean your Plugins folder!").append("<br>");
829 text += tr("We suggest to move all .dll and .avsi files out of your Avisynth Plugins folder and try again.");
830 int val = QMessageBox::critical(this, tr("Avisynth Error"), QString("<nobr>%1</nobr>").arg(text).replace("-", "−"), tr("Quit"), tr("Ignore"));
831 if(val != 1) INIT_ERROR_EXIT();
833 if((!result) || (avisynthVersion < 2.5))
835 if(!m_preferences->disableWarnings())
837 QString text = tr("It appears that Avisynth is <b>not</b> currently installed on your computer.<br>Therefore Avisynth (.avs) input will <b>not</b> be working at all!").append("<br><br>");
838 text += tr("Please download and install Avisynth:").append("<br>").append(LINK("http://sourceforge.net/projects/avisynth2/files/AviSynth%202.5/"));
839 int val = QMessageBox::warning(this, tr("Avisynth Missing"), QString("<nobr>%1</nobr>").arg(text).replace("-", "−"), tr("Quit"), tr("Ignore"));
840 if(val != 1) INIT_ERROR_EXIT();
846 //Check for VapourSynth support
847 if(!qApp->arguments().contains("--skip-vapoursynth-check", Qt::CaseInsensitive))
849 qDebug("[Check for VapourSynth support]");
850 volatile double avisynthVersion = 0.0;
851 const int result = VapourSynthCheckThread::detect(m_vapoursynthPath);
854 QString text = tr("A critical error was encountered while checking your VapourSynth installation.").append("<br>");
855 text += tr("This is most likely caused by an erroneous VapourSynth Plugin, please try to clean your Filters folder!").append("<br>");
856 text += tr("We suggest to move all .dll files out of your VapourSynth Filters folder and try again.");
857 int val = QMessageBox::critical(this, tr("VapourSynth Error"), QString("<nobr>%1</nobr>").arg(text).replace("-", "−"), tr("Quit"), tr("Ignore"));
858 if(val != 1) INIT_ERROR_EXIT();
860 if((!result) || (m_vapoursynthPath.isEmpty()))
862 if(!m_preferences->disableWarnings())
864 QString text = tr("It appears that VapourSynth is <b>not</b> currently installed on your computer.<br>Therefore VapourSynth (.vpy) input will <b>not</b> be working at all!").append("<br><br>");
865 text += tr("Please download and install VapourSynth for Windows (R19 or later):").append("<br>").append(LINK("http://www.vapoursynth.com/")).append("<br><br>");
866 text += tr("Note that Python 3.3 (x86) is a prerequisite for installing VapourSynth:").append("<br>").append(LINK("http://www.python.org/getit/")).append("<br>");
867 int val = QMessageBox::warning(this, tr("VapourSynth Missing"), QString("<nobr>%1</nobr>").arg(text).replace("-", "−"), tr("Quit"), tr("Ignore"));
868 if(val != 1) INIT_ERROR_EXIT();
874 //Update initialized flag (must do this before update check!)
875 m_initialized = true;
877 //Enable drag&drop support for this window, required for Qt v4.8.4+
878 setAcceptDrops(true);
880 //Check for expiration
881 if(x264_version_date().addMonths(6) < x264_current_date_safe())
884 text += QString("<nobr><tt>%1</tt></nobr><br><br>").arg(tr("Your version of Simple x264 Launcher is more than 6 months old!").replace("-", "−"));
885 text += QString("<nobr><tt>%1<br><a href=\"%2\">%2</a><br><br>").arg(tr("You can download the most recent version from the official web-site now:").replace("-", "−"), QString::fromLatin1(update_url));
886 text += QString("<nobr><tt>%1</tt></nobr><br>").arg(tr("Alternatively, click 'Check for Updates' to run the auto-update utility.").replace("-", "−"));
887 QMessageBox msgBox(this);
888 msgBox.setIconPixmap(QIcon(":/images/update.png").pixmap(56,56));
889 msgBox.setWindowTitle(tr("Update Notification"));
890 msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
891 msgBox.setText(text);
892 QPushButton *btn1 = msgBox.addButton(tr("Check for Updates"), QMessageBox::AcceptRole);
893 QPushButton *btn2 = msgBox.addButton(tr("Discard"), QMessageBox::NoRole);
894 QPushButton *btn3 = msgBox.addButton(btn2->text(), QMessageBox::RejectRole);
895 btn2->setEnabled(false);
896 btn3->setVisible(false);
897 QTimer::singleShot(7500, btn2, SLOT(hide()));
898 QTimer::singleShot(7500, btn3, SLOT(show()));
899 if(msgBox.exec() == 0)
901 QTimer::singleShot(0, this, SLOT(checkUpdates()));
905 else if((!m_preferences->noUpdateReminder()) && (m_recentlyUsed->lastUpdateCheck() + 14 < x264_current_date_safe().toJulianDay()))
907 if(QMessageBox::warning(this, tr("Update Notification"), QString("<nobr>%1</nobr>").arg(tr("Your last update check was more than 14 days ago. Check for updates now?")), tr("Check for Updates"), tr("Discard")) == 0)
909 QTimer::singleShot(0, this, SLOT(checkUpdates()));
914 //Add files from command-line
915 parseCommandLineArgs();
919 * Update the label position
921 void MainWindow::updateLabelPos(void)
923 const QWidget *const viewPort = ui->jobsView->viewport();
924 m_label->setGeometry(0, 0, viewPort->width(), viewPort->height());
928 * Copy the complete log to the clipboard
930 void MainWindow::copyLogToClipboard(bool checked)
932 qDebug("copyLogToClipboard");
934 if(LogFileModel *log = dynamic_cast<LogFileModel*>(ui->logView->model()))
936 log->copyToClipboard();
937 x264_beep(x264_beep_info);
942 * Process the dropped files
944 void MainWindow::handleDroppedFiles(void)
946 qDebug("MainWindow::handleDroppedFiles");
949 QStringList droppedFiles(*m_droppedFiles);
950 m_droppedFiles->clear();
951 createJobMultiple(droppedFiles);
953 qDebug("Leave from MainWindow::handleDroppedFiles!");
956 void MainWindow::instanceCreated(unsigned int pid)
958 qDebug("Notification from other instance (PID=0x%X) received!", pid);
960 x264_blink_window(this, 5, 125);
961 x264_bring_to_front(this);
962 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
963 x264_blink_window(this, 5, 125);
966 void MainWindow::checkUpdates(void)
973 if(countRunningJobs() > 0)
975 QMessageBox::warning(this, tr("Jobs Are Running"), tr("Sorry, can not update while there still are running jobs!"));
979 UpdaterDialog *updater = new UpdaterDialog(this, QString("%1/toolset").arg(m_appDir));
980 const int ret = updater->exec();
982 if(updater->getSuccess())
984 m_recentlyUsed->setLastUpdateCheck(x264_current_date_safe().toJulianDay());
985 RecentlyUsed::saveRecentlyUsed(m_recentlyUsed);
988 if(ret == UpdaterDialog::READY_TO_INSTALL_UPDATE)
990 qWarning("Exitting program to install update...");
992 QApplication::quit();
995 X264_DELETE(updater);
998 ///////////////////////////////////////////////////////////////////////////////
1000 ///////////////////////////////////////////////////////////////////////////////
1003 * Window shown event
1005 void MainWindow::showEvent(QShowEvent *e)
1007 QMainWindow::showEvent(e);
1011 m_firstShow = false;
1012 QTimer::singleShot(0, this, SLOT(init()));
1017 * Window close event
1019 void MainWindow::closeEvent(QCloseEvent *e)
1027 if(countRunningJobs() > 0)
1030 QMessageBox::warning(this, tr("Jobs Are Running"), tr("Sorry, can not exit while there still are running jobs!"));
1034 if(countPendingJobs() > 0)
1036 int ret = QMessageBox::question(this, tr("Jobs Are Pending"), tr("Do you really want to quit and discard the pending jobs?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
1037 if(ret != QMessageBox::Yes)
1044 while(m_jobList->rowCount(QModelIndex()) > 0)
1046 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
1047 if(!m_jobList->deleteJob(m_jobList->index(0, 0, QModelIndex())))
1050 QMessageBox::warning(this, tr("Failed To Exit"), tr("Sorry, at least one job could not be deleted!"));
1055 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
1056 QMainWindow::closeEvent(e);
1060 * Window resize event
1062 void MainWindow::resizeEvent(QResizeEvent *e)
1064 QMainWindow::resizeEvent(e);
1071 bool MainWindow::eventFilter(QObject *o, QEvent *e)
1073 if((o == ui->labelBuildDate) && (e->type() == QEvent::MouseButtonPress))
1075 QTimer::singleShot(0, this, SLOT(showAbout()));
1082 * Win32 message filter
1084 bool MainWindow::winEvent(MSG *message, long *result)
1086 return WinSevenTaskbar::handleWinEvent(message, result);
1090 * File dragged over window
1092 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
1094 bool accept[2] = {false, false};
1096 foreach(const QString &fmt, event->mimeData()->formats())
1098 accept[0] = accept[0] || fmt.contains("text/uri-list", Qt::CaseInsensitive);
1099 accept[1] = accept[1] || fmt.contains("FileNameW", Qt::CaseInsensitive);
1102 if(accept[0] && accept[1])
1104 event->acceptProposedAction();
1109 * File dropped onto window
1111 void MainWindow::dropEvent(QDropEvent *event)
1113 QStringList droppedFiles;
1114 QList<QUrl> urls = event->mimeData()->urls();
1116 while(!urls.isEmpty())
1118 QUrl currentUrl = urls.takeFirst();
1119 QFileInfo file(currentUrl.toLocalFile());
1120 if(file.exists() && file.isFile())
1122 qDebug("MainWindow::dropEvent: %s", file.canonicalFilePath().toUtf8().constData());
1123 droppedFiles << file.canonicalFilePath();
1127 if(droppedFiles.count() > 0)
1131 m_droppedFiles = new QStringList();
1133 m_droppedFiles->append(droppedFiles);
1134 m_droppedFiles->sort();
1135 QTimer::singleShot(0, this, SLOT(handleDroppedFiles()));
1139 ///////////////////////////////////////////////////////////////////////////////
1140 // Private functions
1141 ///////////////////////////////////////////////////////////////////////////////
1146 bool MainWindow::createJob(QString &sourceFileName, QString &outputFileName, OptionsModel *options, bool &runImmediately, const bool restart, int fileNo, int fileTotal, bool *applyToAll)
1149 AddJobDialog *addDialog = new AddJobDialog(this, options, m_recentlyUsed, m_cpuFeatures->x64, m_preferences->use10BitEncoding(), m_preferences->saveToSourcePath());
1151 addDialog->setRunImmediately(runImmediately);
1152 if(!sourceFileName.isEmpty()) addDialog->setSourceFile(sourceFileName);
1153 if(!outputFileName.isEmpty()) addDialog->setOutputFile(outputFileName);
1154 if(restart) addDialog->setWindowTitle(tr("Restart Job"));
1156 const bool multiFile = (fileNo >= 0) && (fileTotal > 1);
1159 addDialog->setSourceEditable(false);
1160 addDialog->setWindowTitle(addDialog->windowTitle().append(tr(" (File %1 of %2)").arg(QString::number(fileNo+1), QString::number(fileTotal))));
1161 addDialog->setApplyToAllVisible(applyToAll);
1164 if(addDialog->exec() == QDialog::Accepted)
1166 sourceFileName = addDialog->sourceFile();
1167 outputFileName = addDialog->outputFile();
1168 runImmediately = addDialog->runImmediately();
1171 *applyToAll = addDialog->applyToAll();
1176 X264_DELETE(addDialog);
1181 * Creates a new job from *multiple* files
1183 bool MainWindow::createJobMultiple(const QStringList &filePathIn)
1185 QStringList::ConstIterator iter;
1186 bool applyToAll = false, runImmediately = false;
1189 //Add files individually
1190 for(iter = filePathIn.constBegin(); (iter != filePathIn.constEnd()) && (!applyToAll); iter++)
1192 runImmediately = (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
1193 QString sourceFileName(*iter), outputFileName;
1194 if(createJob(sourceFileName, outputFileName, m_options, runImmediately, false, counter++, filePathIn.count(), &applyToAll))
1196 if(appendJob(sourceFileName, outputFileName, m_options, runImmediately))
1204 //Add remaining files
1205 while(applyToAll && (iter != filePathIn.constEnd()))
1207 const bool runImmediatelyTmp = runImmediately && (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
1208 const QString sourceFileName = *iter;
1209 const QString outputFileName = AddJobDialog::generateOutputFileName(sourceFileName, m_recentlyUsed->outputDirectory(), m_recentlyUsed->filterIndex(), m_preferences->saveToSourcePath());
1210 if(!appendJob(sourceFileName, outputFileName, m_options, runImmediatelyTmp))
1224 bool MainWindow::appendJob(const QString &sourceFileName, const QString &outputFileName, OptionsModel *options, const bool runImmediately)
1228 EncodeThread *thrd = new EncodeThread
1233 QString("%1/toolset").arg(m_appDir),
1236 m_preferences->use10BitEncoding(),
1237 m_cpuFeatures->x64 && m_preferences->useAvisyth64Bit(),
1239 m_preferences->processPriority(),
1243 QModelIndex newIndex = m_jobList->insertJob(thrd);
1245 if(newIndex.isValid())
1249 ui->jobsView->selectRow(newIndex.row());
1250 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1251 m_jobList->startJob(newIndex);
1257 m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0);
1262 * Jobs that are not completed (or failed, or aborted) yet
1264 unsigned int MainWindow::countPendingJobs(void)
1266 unsigned int count = 0;
1267 const int rows = m_jobList->rowCount(QModelIndex());
1269 for(int i = 0; i < rows; i++)
1271 JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex()));
1272 if(status != JobStatus_Completed && status != JobStatus_Aborted && status != JobStatus_Failed)
1282 * Jobs that are still active, i.e. not terminated or enqueued
1284 unsigned int MainWindow::countRunningJobs(void)
1286 unsigned int count = 0;
1287 const int rows = m_jobList->rowCount(QModelIndex());
1289 for(int i = 0; i < rows; i++)
1291 JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex()));
1292 if(status != JobStatus_Completed && status != JobStatus_Aborted && status != JobStatus_Failed && status != JobStatus_Enqueued)
1302 * Update all buttons with respect to current job status
1304 void MainWindow::updateButtons(JobStatus status)
1306 qDebug("MainWindow::updateButtons(void)");
1308 ui->buttonStartJob->setEnabled(status == JobStatus_Enqueued);
1309 ui->buttonAbortJob->setEnabled(status == JobStatus_Indexing || status == JobStatus_Running || status == JobStatus_Running_Pass1 || status == JobStatus_Running_Pass2 || status == JobStatus_Paused);
1310 ui->buttonPauseJob->setEnabled(status == JobStatus_Indexing || status == JobStatus_Running || status == JobStatus_Paused || status == JobStatus_Running_Pass1 || status == JobStatus_Running_Pass2);
1311 ui->buttonPauseJob->setChecked(status == JobStatus_Paused || status == JobStatus_Pausing);
1313 ui->actionJob_Delete->setEnabled(status == JobStatus_Completed || status == JobStatus_Aborted || status == JobStatus_Failed || status == JobStatus_Enqueued);
1314 ui->actionJob_Restart->setEnabled(status == JobStatus_Completed || status == JobStatus_Aborted || status == JobStatus_Failed || status == JobStatus_Enqueued);
1315 ui->actionJob_Browse->setEnabled(status == JobStatus_Completed);
1317 ui->actionJob_Start->setEnabled(ui->buttonStartJob->isEnabled());
1318 ui->actionJob_Abort->setEnabled(ui->buttonAbortJob->isEnabled());
1319 ui->actionJob_Pause->setEnabled(ui->buttonPauseJob->isEnabled());
1320 ui->actionJob_Pause->setChecked(ui->buttonPauseJob->isChecked());
1322 ui->editDetails->setEnabled(status != JobStatus_Paused);
1326 * Update the taskbar with current job status
1328 void MainWindow::updateTaskbar(JobStatus status, const QIcon &icon)
1330 qDebug("MainWindow::updateTaskbar(void)");
1334 case JobStatus_Undefined:
1335 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarNoState);
1337 case JobStatus_Aborting:
1338 case JobStatus_Starting:
1339 case JobStatus_Pausing:
1340 case JobStatus_Resuming:
1341 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarIndeterminateState);
1343 case JobStatus_Aborted:
1344 case JobStatus_Failed:
1345 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarErrorState);
1347 case JobStatus_Paused:
1348 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarPausedState);
1351 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarNormalState);
1357 case JobStatus_Aborting:
1358 case JobStatus_Starting:
1359 case JobStatus_Pausing:
1360 case JobStatus_Resuming:
1363 WinSevenTaskbar::setTaskbarProgress(this, ui->progressBar->value(), ui->progressBar->maximum());
1367 WinSevenTaskbar::setOverlayIcon(this, icon.isNull() ? NULL : &icon);
1371 * Parse command-line arguments
1373 void MainWindow::parseCommandLineArgs(void)
1375 QStringList files, args = x264_arguments();
1376 while(!args.isEmpty())
1378 QString current = args.takeFirst();
1379 if((current.compare("--add", Qt::CaseInsensitive) == 0) || (current.compare("--add-file", Qt::CaseInsensitive) == 0))
1383 current = args.takeFirst();
1384 if(QFileInfo(current).exists() && QFileInfo(current).isFile())
1386 files << QFileInfo(current).canonicalFilePath();
1390 qWarning("File '%s' not found!", current.toUtf8().constData());
1395 qWarning("Argument for '--add-file' is missing!");
1398 else if(current.compare("--add-job", Qt::CaseInsensitive) == 0)
1400 if(args.size() >= 3)
1402 const QString fileSrc = args.takeFirst();
1403 const QString fileOut = args.takeFirst();
1404 const QString templId = args.takeFirst();
1405 if(QFileInfo(fileSrc).exists() && QFileInfo(fileSrc).isFile())
1407 OptionsModel options;
1408 if(!(templId.isEmpty() || (templId.compare("-", Qt::CaseInsensitive) == 0)))
1410 if(!OptionsModel::loadTemplate(&options, templId.trimmed()))
1412 qWarning("Template '%s' could not be found -> using defaults!", templId.trimmed().toUtf8().constData());
1415 appendJob(fileSrc, fileOut, &options, true);
1419 qWarning("Source file '%s' not found!", fileSrc.toUtf8().constData());
1424 qWarning("Argument(s) for '--add-job' are missing!");
1430 qWarning("Unknown argument: %s", current.toUtf8().constData());
1434 if(files.count() > 0)
1436 createJobMultiple(files);