OSDN Git Service

Some more refactoring.
[x264-launcher/x264-launcher.git] / src / win_main.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
9 //
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.
14 //
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.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "win_main.h"
23
24 #include "model_jobList.h"
25 #include "model_options.h"
26 #include "model_preferences.h"
27 #include "model_recently.h"
28 #include "thread_avisynth.h"
29 #include "thread_ipc.h"
30 #include "thread_encode.h"
31 #include "taskbar7.h"
32 #include "win_addJob.h"
33 #include "win_preferences.h"
34 #include "resource.h"
35
36 #include <QDate>
37 #include <QTimer>
38 #include <QCloseEvent>
39 #include <QMessageBox>
40 #include <QDesktopServices>
41 #include <QUrl>
42 #include <QDir>
43 #include <QLibrary>
44 #include <QProcess>
45 #include <QProgressDialog>
46 #include <QScrollBar>
47 #include <QTextStream>
48 #include <QSettings>
49 #include <QFileDialog>
50
51 #include <Mmsystem.h>
52
53 const char *home_url = "http://muldersoft.com/";
54 const char *update_url = "http://code.google.com/p/mulder/downloads/list";
55 const char *tpl_last = "<LAST_USED>";
56
57 #define SET_FONT_BOLD(WIDGET,BOLD) { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); }
58 #define SET_TEXT_COLOR(WIDGET,COLOR) { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, (COLOR)); _palette.setColor(QPalette::Text, (COLOR)); WIDGET->setPalette(_palette); }
59
60 static int exceptionFilter(_EXCEPTION_RECORD *dst, _EXCEPTION_POINTERS *src) { memcpy(dst, src->ExceptionRecord, sizeof(_EXCEPTION_RECORD)); return EXCEPTION_EXECUTE_HANDLER; }
61
62 ///////////////////////////////////////////////////////////////////////////////
63 // Constructor & Destructor
64 ///////////////////////////////////////////////////////////////////////////////
65
66 /*
67  * Constructor
68  */
69 MainWindow::MainWindow(const x264_cpu_t *const cpuFeatures)
70 :
71         m_cpuFeatures(cpuFeatures),
72         m_appDir(QApplication::applicationDirPath()),
73         m_options(NULL),
74         m_jobList(NULL),
75         m_droppedFiles(NULL),
76         m_preferences(NULL),
77         m_recentlyUsed(NULL),
78         m_firstShow(true)
79 {
80         //Init the dialog, from the .ui file
81         setupUi(this);
82         setWindowFlags(windowFlags() & (~Qt::WindowMaximizeButtonHint));
83
84         //Register meta types
85         qRegisterMetaType<QUuid>("QUuid");
86         qRegisterMetaType<QUuid>("DWORD");
87         qRegisterMetaType<JobStatus>("EncodeThread::JobStatus");
88
89         //Load preferences
90         m_preferences = new PreferencesModel();
91         PreferencesModel::loadPreferences(m_preferences);
92
93         //Load recently used
94         m_recentlyUsed = new RecentlyUsed();
95         RecentlyUsed::loadRecentlyUsed(m_recentlyUsed);
96
97         //Create options object
98         m_options = new OptionsModel();
99         OptionsModel::loadTemplate(m_options, QString::fromLatin1(tpl_last));
100
101         //Create IPC thread object
102         m_ipcThread = new IPCThread();
103         connect(m_ipcThread, SIGNAL(instanceCreated(DWORD)), this, SLOT(instanceCreated(DWORD)), Qt::QueuedConnection);
104
105         //Freeze minimum size
106         setMinimumSize(size());
107         splitter->setSizes(QList<int>() << 16 << 196);
108
109         //Update title
110         labelBuildDate->setText(tr("Built on %1 at %2").arg(x264_version_date().toString(Qt::ISODate), QString::fromLatin1(x264_version_time())));
111         labelBuildDate->installEventFilter(this);
112         setWindowTitle(QString("%1 (%2 Mode)").arg(windowTitle(), m_cpuFeatures->x64 ? "64-Bit" : "32-Bit"));
113         if(X264_DEBUG)
114         {
115                 setWindowTitle(QString("%1 | !!! DEBUG VERSION !!!").arg(windowTitle()));
116                 setStyleSheet("QMenuBar, QMainWindow { background-color: yellow }");
117         }
118         else if(x264_is_prerelease())
119         {
120                 setWindowTitle(QString("%1 | PRE-RELEASE VERSION").arg(windowTitle()));
121         }
122         
123         //Create model
124         m_jobList = new JobListModel(m_preferences);
125         connect(m_jobList, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(jobChangedData(QModelIndex, QModelIndex)));
126         jobsView->setModel(m_jobList);
127         
128         //Setup view
129         jobsView->horizontalHeader()->setSectionHidden(3, true);
130         jobsView->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
131         jobsView->horizontalHeader()->setResizeMode(1, QHeaderView::Fixed);
132         jobsView->horizontalHeader()->setResizeMode(2, QHeaderView::Fixed);
133         jobsView->horizontalHeader()->resizeSection(1, 150);
134         jobsView->horizontalHeader()->resizeSection(2, 90);
135         jobsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
136         connect(jobsView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(jobSelected(QModelIndex, QModelIndex)));
137
138         //Create context menu
139         QAction *actionClipboard = new QAction(QIcon(":/buttons/page_paste.png"), tr("Copy to Clipboard"), logView);
140         actionClipboard->setEnabled(false);
141         logView->addAction(actionClipboard);
142         connect(actionClipboard, SIGNAL(triggered(bool)), this, SLOT(copyLogToClipboard(bool)));
143         jobsView->addActions(menuJob->actions());
144
145         //Enable buttons
146         connect(buttonAddJob, SIGNAL(clicked()), this, SLOT(addButtonPressed()));
147         connect(buttonStartJob, SIGNAL(clicked()), this, SLOT(startButtonPressed()));
148         connect(buttonAbortJob, SIGNAL(clicked()), this, SLOT(abortButtonPressed()));
149         connect(buttonPauseJob, SIGNAL(toggled(bool)), this, SLOT(pauseButtonPressed(bool)));
150         connect(actionJob_Delete, SIGNAL(triggered()), this, SLOT(deleteButtonPressed()));
151         connect(actionJob_Restart, SIGNAL(triggered()), this, SLOT(restartButtonPressed()));
152         connect(actionJob_Browse, SIGNAL(triggered()), this, SLOT(browseButtonPressed()));
153
154         //Enable menu
155         connect(actionOpen, SIGNAL(triggered()), this, SLOT(openActionTriggered()));
156         connect(actionAbout, SIGNAL(triggered()), this, SLOT(showAbout()));
157         connect(actionWebMulder, SIGNAL(triggered()), this, SLOT(showWebLink()));
158         connect(actionWebX264, SIGNAL(triggered()), this, SLOT(showWebLink()));
159         connect(actionWebKomisar, SIGNAL(triggered()), this, SLOT(showWebLink()));
160         connect(actionWebJarod, SIGNAL(triggered()), this, SLOT(showWebLink()));
161         connect(actionWebJEEB, SIGNAL(triggered()), this, SLOT(showWebLink()));
162         connect(actionWebAvisynth32, SIGNAL(triggered()), this, SLOT(showWebLink()));
163         connect(actionWebAvisynth64, SIGNAL(triggered()), this, SLOT(showWebLink()));
164         connect(actionWebWiki, SIGNAL(triggered()), this, SLOT(showWebLink()));
165         connect(actionWebBluRay, SIGNAL(triggered()), this, SLOT(showWebLink()));
166         connect(actionWebAvsWiki, SIGNAL(triggered()), this, SLOT(showWebLink()));
167         connect(actionWebSecret, SIGNAL(triggered()), this, SLOT(showWebLink()));
168         connect(actionWebSupport, SIGNAL(triggered()), this, SLOT(showWebLink()));
169         connect(actionPreferences, SIGNAL(triggered()), this, SLOT(showPreferences()));
170
171         //Create floating label
172         m_label = new QLabel(jobsView->viewport());
173         m_label->setText(tr("No job created yet. Please click the 'Add New Job' button!"));
174         m_label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
175         SET_TEXT_COLOR(m_label, Qt::darkGray);
176         SET_FONT_BOLD(m_label, true);
177         m_label->setVisible(true);
178         m_label->setContextMenuPolicy(Qt::ActionsContextMenu);
179         m_label->addActions(jobsView->actions());
180         connect(splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(updateLabelPos()));
181         updateLabelPos();
182 }
183
184 /*
185  * Destructor
186  */
187 MainWindow::~MainWindow(void)
188 {
189         OptionsModel::saveTemplate(m_options, QString::fromLatin1(tpl_last));
190         
191         X264_DELETE(m_jobList);
192         X264_DELETE(m_options);
193         X264_DELETE(m_droppedFiles);
194         X264_DELETE(m_label);
195
196         while(!m_toolsList.isEmpty())
197         {
198                 QFile *temp = m_toolsList.takeFirst();
199                 X264_DELETE(temp);
200         }
201
202         if(m_ipcThread->isRunning())
203         {
204                 m_ipcThread->setAbort();
205                 if(!m_ipcThread->wait(5000))
206                 {
207                         m_ipcThread->terminate();
208                         m_ipcThread->wait();
209                 }
210         }
211
212         X264_DELETE(m_ipcThread);
213         AvisynthCheckThread::unload();
214         X264_DELETE(m_preferences);
215         X264_DELETE(m_recentlyUsed);
216 }
217
218 ///////////////////////////////////////////////////////////////////////////////
219 // Slots
220 ///////////////////////////////////////////////////////////////////////////////
221
222 /*
223  * The "add" button was clicked
224  */
225 void MainWindow::addButtonPressed()
226 {
227         qDebug("MainWindow::addButtonPressed");
228         bool runImmediately = (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
229         QString sourceFileName, outputFileName;
230         
231         if(createJob(sourceFileName, outputFileName, m_options, runImmediately))
232         {
233                 appendJob(sourceFileName, outputFileName, m_options, runImmediately);
234         }
235 }
236
237 /*
238  * The "open" action was triggered
239  */
240 void MainWindow::openActionTriggered()
241 {
242         QStringList fileList = QFileDialog::getOpenFileNames(this, tr("Open Source File(s)"), m_recentlyUsed->sourceDirectory(), AddJobDialog::getInputFilterLst(), NULL, QFileDialog::DontUseNativeDialog);
243         if(!fileList.empty())
244         {
245                 m_recentlyUsed->setSourceDirectory(QFileInfo(fileList.last()).absolutePath());
246                 if(fileList.count() > 1)
247                 {
248                         createJobMultiple(fileList);
249                 }
250                 else
251                 {
252                         bool runImmediately = (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
253                         QString sourceFileName(fileList.first()), outputFileName;
254                         if(createJob(sourceFileName, outputFileName, m_options, runImmediately))
255                         {
256                                 appendJob(sourceFileName, outputFileName, m_options, runImmediately);
257                         }
258                 }
259         }
260 }
261
262 /*
263  * The "start" button was clicked
264  */
265 void MainWindow::startButtonPressed(void)
266 {
267         m_jobList->startJob(jobsView->currentIndex());
268 }
269
270 /*
271  * The "abort" button was clicked
272  */
273 void MainWindow::abortButtonPressed(void)
274 {
275         m_jobList->abortJob(jobsView->currentIndex());
276 }
277
278 /*
279  * The "delete" button was clicked
280  */
281 void MainWindow::deleteButtonPressed(void)
282 {
283         m_jobList->deleteJob(jobsView->currentIndex());
284         m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0);
285 }
286
287 /*
288  * The "browse" button was clicked
289  */
290 void MainWindow::browseButtonPressed(void)
291 {
292         QString outputFile = m_jobList->getJobOutputFile(jobsView->currentIndex());
293         if((!outputFile.isEmpty()) && QFileInfo(outputFile).exists() && QFileInfo(outputFile).isFile())
294         {
295                 QProcess::startDetached(QString::fromLatin1("explorer.exe"), QStringList() << QString::fromLatin1("/select,") << QDir::toNativeSeparators(outputFile), QFileInfo(outputFile).path());
296         }
297         else
298         {
299                 QMessageBox::warning(this, tr("Not Found"), tr("Sorry, the output file could not be found!"));
300         }
301 }
302
303 /*
304  * The "pause" button was clicked
305  */
306 void MainWindow::pauseButtonPressed(bool checked)
307 {
308         if(checked)
309         {
310                 m_jobList->pauseJob(jobsView->currentIndex());
311         }
312         else
313         {
314                 m_jobList->resumeJob(jobsView->currentIndex());
315         }
316 }
317
318 /*
319  * The "restart" button was clicked
320  */
321 void MainWindow::restartButtonPressed(void)
322 {
323         const QModelIndex index = jobsView->currentIndex();
324         const OptionsModel *options = m_jobList->getJobOptions(index);
325         QString sourceFileName = m_jobList->getJobSourceFile(index);
326         QString outputFileName = m_jobList->getJobOutputFile(index);
327
328         if((options) && (!sourceFileName.isEmpty()) && (!outputFileName.isEmpty()))
329         {
330                 bool runImmediately = true;
331                 OptionsModel *tempOptions = new OptionsModel(*options);
332                 if(createJob(sourceFileName, outputFileName, tempOptions, runImmediately, true))
333                 {
334                         appendJob(sourceFileName, outputFileName, tempOptions, runImmediately);
335                 }
336                 X264_DELETE(tempOptions);
337         }
338 }
339
340 /*
341  * Job item selected by user
342  */
343 void MainWindow::jobSelected(const QModelIndex & current, const QModelIndex & previous)
344 {
345         qDebug("Job selected: %d", current.row());
346         
347         if(logView->model())
348         {
349                 disconnect(logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int)));
350         }
351         
352         if(current.isValid())
353         {
354                 logView->setModel(m_jobList->getLogFile(current));
355                 connect(logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int)));
356                 logView->actions().first()->setEnabled(true);
357                 QTimer::singleShot(0, logView, SLOT(scrollToBottom()));
358
359                 progressBar->setValue(m_jobList->getJobProgress(current));
360                 editDetails->setText(m_jobList->data(m_jobList->index(current.row(), 3, QModelIndex()), Qt::DisplayRole).toString());
361                 updateButtons(m_jobList->getJobStatus(current));
362                 updateTaskbar(m_jobList->getJobStatus(current), m_jobList->data(m_jobList->index(current.row(), 0, QModelIndex()), Qt::DecorationRole).value<QIcon>());
363         }
364         else
365         {
366                 logView->setModel(NULL);
367                 logView->actions().first()->setEnabled(false);
368                 progressBar->setValue(0);
369                 editDetails->clear();
370                 updateButtons(JobStatus_Undefined);
371                 updateTaskbar(JobStatus_Undefined, QIcon());
372         }
373
374         progressBar->repaint();
375 }
376
377 /*
378  * Handle update of job info (status, progress, details, etc)
379  */
380 void MainWindow::jobChangedData(const QModelIndex &topLeft, const  QModelIndex &bottomRight)
381 {
382         int selected = jobsView->currentIndex().row();
383         
384         if(topLeft.column() <= 1 && bottomRight.column() >= 1) /*STATUS*/
385         {
386                 for(int i = topLeft.row(); i <= bottomRight.row(); i++)
387                 {
388                         JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex()));
389                         if(i == selected)
390                         {
391                                 qDebug("Current job changed status!");
392                                 updateButtons(status);
393                                 updateTaskbar(status, m_jobList->data(m_jobList->index(i, 0, QModelIndex()), Qt::DecorationRole).value<QIcon>());
394                         }
395                         if((status == JobStatus_Completed) || (status == JobStatus_Failed))
396                         {
397                                 if(m_preferences->autoRunNextJob()) QTimer::singleShot(0, this, SLOT(launchNextJob()));
398                                 if(m_preferences->shutdownComputer()) QTimer::singleShot(0, this, SLOT(shutdownComputer()));
399                                 if(m_preferences->saveLogFiles()) saveLogFile(m_jobList->index(i, 1, QModelIndex()));
400                         }
401                 }
402         }
403         if(topLeft.column() <= 2 && bottomRight.column() >= 2) /*PROGRESS*/
404         {
405                 for(int i = topLeft.row(); i <= bottomRight.row(); i++)
406                 {
407                         if(i == selected)
408                         {
409                                 progressBar->setValue(m_jobList->getJobProgress(m_jobList->index(i, 0, QModelIndex())));
410                                 WinSevenTaskbar::setTaskbarProgress(this, progressBar->value(), progressBar->maximum());
411                                 break;
412                         }
413                 }
414         }
415         if(topLeft.column() <= 3 && bottomRight.column() >= 3) /*DETAILS*/
416         {
417                 for(int i = topLeft.row(); i <= bottomRight.row(); i++)
418                 {
419                         if(i == selected)
420                         {
421                                 editDetails->setText(m_jobList->data(m_jobList->index(i, 3, QModelIndex()), Qt::DisplayRole).toString());
422                                 break;
423                         }
424                 }
425         }
426 }
427
428 /*
429  * Handle new log file content
430  */
431 void MainWindow::jobLogExtended(const QModelIndex & parent, int start, int end)
432 {
433         QTimer::singleShot(0, logView, SLOT(scrollToBottom()));
434 }
435
436 /*
437  * About screen
438  */
439 void MainWindow::showAbout(void)
440 {
441         QString text;
442
443         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());
444         text += QString().sprintf("Copyright (c) 2004-%04d LoRd_MuldeR &lt;mulder2@gmx.de&gt;. Some rights reserved.<br>", qMax(x264_version_date().year(),QDate::currentDate().year()));
445         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());
446         text += QString().sprintf("This program is free software: you can redistribute it and/or modify<br>");
447         text += QString().sprintf("it under the terms of the GNU General Public License &lt;http://www.gnu.org/&gt;.<br>");
448         text += QString().sprintf("Note that this program is distributed with ABSOLUTELY NO WARRANTY.<br><br>");
449         text += QString().sprintf("Please check the web-site at <a href=\"%s\">%s</a> for updates !!!<br></tt></nobr>", home_url, home_url);
450
451         QMessageBox aboutBox(this);
452         aboutBox.setIconPixmap(QIcon(":/images/movie.png").pixmap(64,64));
453         aboutBox.setWindowTitle(tr("About..."));
454         aboutBox.setText(text.replace("-", "&minus;"));
455         aboutBox.addButton(tr("About x264"), QMessageBox::NoRole);
456         aboutBox.addButton(tr("About AVS"), QMessageBox::NoRole);
457         aboutBox.addButton(tr("About Qt"), QMessageBox::NoRole);
458         aboutBox.setEscapeButton(aboutBox.addButton(tr("Close"), QMessageBox::NoRole));
459                 
460         forever
461         {
462                 MessageBeep(MB_ICONINFORMATION);
463                 switch(aboutBox.exec())
464                 {
465                 case 0:
466                         {
467                                 QString text2;
468                                 text2 += tr("<nobr><tt>x264 - the best H.264/AVC encoder. Copyright (c) 2003-2013 x264 project.<br>");
469                                 text2 += tr("Free software library for encoding video streams into the H.264/MPEG-4 AVC format.<br>");
470                                 text2 += tr("Released under the terms of the GNU General Public License.<br><br>");
471                                 text2 += tr("Please visit <a href=\"%1\">%1</a> for obtaining a commercial x264 license.<br>").arg("http://x264licensing.com/");
472                                 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");
473
474                                 QMessageBox x264Box(this);
475                                 x264Box.setIconPixmap(QIcon(":/images/x264.png").pixmap(48,48));
476                                 x264Box.setWindowTitle(tr("About x264"));
477                                 x264Box.setText(text2.replace("-", "&minus;"));
478                                 x264Box.setEscapeButton(x264Box.addButton(tr("Close"), QMessageBox::NoRole));
479                                 MessageBeep(MB_ICONINFORMATION);
480                                 x264Box.exec();
481                         }
482                         break;
483                 case 1:
484                         {
485                                 QString text2;
486                                 text2 += tr("<nobr><tt>Avisynth - powerful video processing scripting language.<br>");
487                                 text2 += tr("Copyright (c) 2000 Ben Rudiak-Gould and all subsequent developers.<br>");
488                                 text2 += tr("Released under the terms of the GNU General Public License.<br><br>");
489                                 text2 += tr("Please visit the web-site <a href=\"%1\">%1</a> for more information.<br>").arg("http://avisynth.org/");
490                                 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.org/mediawiki/First_script", "http://forum.doom9.org/forumdisplay.php?f=33");
491
492                                 QMessageBox x264Box(this);
493                                 x264Box.setIconPixmap(QIcon(":/images/avisynth.png").pixmap(48,67));
494                                 x264Box.setWindowTitle(tr("About Avisynth"));
495                                 x264Box.setText(text2.replace("-", "&minus;"));
496                                 x264Box.setEscapeButton(x264Box.addButton(tr("Close"), QMessageBox::NoRole));
497                                 MessageBeep(MB_ICONINFORMATION);
498                                 x264Box.exec();
499                         }
500                         break;
501                 case 2:
502                         QMessageBox::aboutQt(this);
503                         break;
504                 default:
505                         return;
506                 }
507         }
508 }
509
510 /*
511  * Open web-link
512  */
513 void MainWindow::showWebLink(void)
514 {
515         if(QObject::sender() == actionWebMulder)     QDesktopServices::openUrl(QUrl(home_url));
516         if(QObject::sender() == actionWebX264)       QDesktopServices::openUrl(QUrl("http://www.x264.com/"));
517         if(QObject::sender() == actionWebKomisar)    QDesktopServices::openUrl(QUrl("http://komisar.gin.by/"));
518         if(QObject::sender() == actionWebJarod)      QDesktopServices::openUrl(QUrl("http://www.x264.nl/"));
519         if(QObject::sender() == actionWebJEEB)       QDesktopServices::openUrl(QUrl("http://x264.fushizen.eu/"));
520         if(QObject::sender() == actionWebAvisynth32) QDesktopServices::openUrl(QUrl("http://sourceforge.net/projects/avisynth2/files/AviSynth%202.5/"));
521         if(QObject::sender() == actionWebAvisynth64) QDesktopServices::openUrl(QUrl("http://code.google.com/p/avisynth64/downloads/list"));
522         if(QObject::sender() == actionWebWiki)       QDesktopServices::openUrl(QUrl("http://mewiki.project357.com/wiki/X264_Settings"));
523         if(QObject::sender() == actionWebBluRay)     QDesktopServices::openUrl(QUrl("http://www.x264bluray.com/"));
524         if(QObject::sender() == actionWebAvsWiki)    QDesktopServices::openUrl(QUrl("http://avisynth.org/mediawiki/Main_Page#Usage"));
525         if(QObject::sender() == actionWebSupport)    QDesktopServices::openUrl(QUrl("http://forum.doom9.org/showthread.php?t=144140"));
526         if(QObject::sender() == actionWebSecret)     QDesktopServices::openUrl(QUrl("http://www.youtube.com/watch_popup?v=AXIeHY-OYNI"));
527 }
528
529 /*
530  * Pereferences dialog
531  */
532 void MainWindow::showPreferences(void)
533 {
534         PreferencesDialog *preferences = new PreferencesDialog(this, m_preferences, m_cpuFeatures->x64);
535         preferences->exec();
536         X264_DELETE(preferences);
537 }
538
539 /*
540  * Launch next job, after running job has finished
541  */
542 void MainWindow::launchNextJob(void)
543 {
544         qDebug("launchNextJob(void)");
545         
546         const int rows = m_jobList->rowCount(QModelIndex());
547
548         if(countRunningJobs() >= m_preferences->maxRunningJobCount())
549         {
550                 qDebug("Still have too many jobs running, won't launch next one yet!");
551                 return;
552         }
553
554         int startIdx= jobsView->currentIndex().isValid() ? qBound(0, jobsView->currentIndex().row(), rows-1) : 0;
555
556         for(int i = 0; i < rows; i++)
557         {
558                 int currentIdx = (i + startIdx) % rows;
559                 JobStatus status = m_jobList->getJobStatus(m_jobList->index(currentIdx, 0, QModelIndex()));
560                 if(status == JobStatus_Enqueued)
561                 {
562                         if(m_jobList->startJob(m_jobList->index(currentIdx, 0, QModelIndex())))
563                         {
564                                 jobsView->selectRow(currentIdx);
565                                 return;
566                         }
567                 }
568         }
569                 
570         qWarning("No enqueued jobs left!");
571 }
572
573 /*
574  * Save log to text file
575  */
576 void MainWindow::saveLogFile(const QModelIndex &index)
577 {
578         if(index.isValid())
579         {
580                 if(LogFileModel *log = m_jobList->getLogFile(index))
581                 {
582                         QDir(QString("%1/logs").arg(x264_data_path())).mkpath(".");
583                         QString logFilePath = QString("%1/logs/LOG.%2.%3.txt").arg(x264_data_path(), QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString(Qt::ISODate).replace(':', "-"));
584                         QFile outFile(logFilePath);
585                         if(outFile.open(QIODevice::WriteOnly))
586                         {
587                                 QTextStream outStream(&outFile);
588                                 outStream.setCodec("UTF-8");
589                                 outStream.setGenerateByteOrderMark(true);
590                                 
591                                 const int rows = log->rowCount(QModelIndex());
592                                 for(int i = 0; i < rows; i++)
593                                 {
594                                         outStream << log->data(log->index(i, 0, QModelIndex()), Qt::DisplayRole).toString() << QLatin1String("\r\n");
595                                 }
596                                 outFile.close();
597                         }
598                         else
599                         {
600                                 qWarning("Failed to open log file for writing:\n%s", logFilePath.toUtf8().constData());
601                         }
602                 }
603         }
604 }
605
606 /*
607  * Shut down the computer (with countdown)
608  */
609 void MainWindow::shutdownComputer(void)
610 {
611         qDebug("shutdownComputer(void)");
612         
613         if(countPendingJobs() > 0)
614         {
615                 qDebug("Still have pending jobs, won't shutdown yet!");
616                 return;
617         }
618         
619         const int iTimeout = 30;
620         const Qt::WindowFlags flags = Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowSystemMenuHint;
621         const QString text = QString("%1%2%1").arg(QString().fill(' ', 18), tr("Warning: Computer will shutdown in %1 seconds..."));
622         
623         qWarning("Initiating shutdown sequence!");
624         
625         QProgressDialog progressDialog(text.arg(iTimeout), tr("Cancel Shutdown"), 0, iTimeout + 1, this, flags);
626         QPushButton *cancelButton = new QPushButton(tr("Cancel Shutdown"), &progressDialog);
627         cancelButton->setIcon(QIcon(":/buttons/power_on.png"));
628         progressDialog.setModal(true);
629         progressDialog.setAutoClose(false);
630         progressDialog.setAutoReset(false);
631         progressDialog.setWindowIcon(QIcon(":/buttons/power_off.png"));
632         progressDialog.setWindowTitle(windowTitle());
633         progressDialog.setCancelButton(cancelButton);
634         progressDialog.show();
635         
636         QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
637         QApplication::setOverrideCursor(Qt::WaitCursor);
638         PlaySound(MAKEINTRESOURCE(IDR_WAVE1), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC);
639         QApplication::restoreOverrideCursor();
640         
641         QTimer timer;
642         timer.setInterval(1000);
643         timer.start();
644
645         QEventLoop eventLoop(this);
646         connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
647         connect(&progressDialog, SIGNAL(canceled()), &eventLoop, SLOT(quit()));
648
649         for(int i = 1; i <= iTimeout; i++)
650         {
651                 eventLoop.exec();
652                 if(progressDialog.wasCanceled())
653                 {
654                         progressDialog.close();
655                         return;
656                 }
657                 progressDialog.setValue(i+1);
658                 progressDialog.setLabelText(text.arg(iTimeout-i));
659                 if(iTimeout-i == 3) progressDialog.setCancelButton(NULL);
660                 QApplication::processEvents();
661                 PlaySound(MAKEINTRESOURCE((i < iTimeout) ? IDR_WAVE2 : IDR_WAVE3), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC);
662         }
663         
664         qWarning("Shutting down !!!");
665
666         if(x264_shutdown_computer("Simple x264 Launcher: All jobs completed, shutting down!", 10, true))
667         {
668                 qApp->closeAllWindows();
669         }
670 }
671
672 /*
673  * Main initialization function (called only once!)
674  */
675 void MainWindow::init(void)
676 {
677         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";
678         QStringList binaries = QString::fromLatin1(binFiles).split(":", QString::SkipEmptyParts);
679
680         updateLabelPos();
681
682         //Check for a running instance
683         bool firstInstance = false;
684         if(m_ipcThread->initialize(&firstInstance))
685         {
686                 m_ipcThread->start();
687                 if(!firstInstance)
688                 {
689                         if(!m_ipcThread->wait(5000))
690                         {
691                                 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"));
692                                 m_ipcThread->terminate();
693                                 m_ipcThread->wait();
694                         }
695                         close(); qApp->exit(-1); return;
696                 }
697         }
698
699         //Check all binaries
700         while(!binaries.isEmpty())
701         {
702                 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
703                 QString current = binaries.takeFirst();
704                 QFile *file = new QFile(QString("%1/toolset/%2").arg(m_appDir, current));
705                 if(file->open(QIODevice::ReadOnly))
706                 {
707                         bool binaryTypeOkay = false;
708                         DWORD binaryType;
709                         if(GetBinaryType(QWCHAR(QDir::toNativeSeparators(file->fileName())), &binaryType))
710                         {
711                                 binaryTypeOkay = (binaryType == SCS_32BIT_BINARY || binaryType == SCS_64BIT_BINARY);
712                         }
713                         if(!binaryTypeOkay)
714                         {
715                                 QMessageBox::critical(this, tr("Invalid File!"), tr("<nobr>At least on required tool is not a valid Win32 or Win64 binary:<br>%1<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("-", "&minus;"));
716                                 qFatal(QString("Binary is invalid: %1/toolset/%2").arg(m_appDir, current).toLatin1().constData());
717                                 close(); qApp->exit(-1); return;
718                         }
719                         m_toolsList << file;
720                 }
721                 else
722                 {
723                         X264_DELETE(file);
724                         QMessageBox::critical(this, tr("File Not Found!"), tr("<nobr>At least on required tool could not be found:<br>%1<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("-", "&minus;"));
725                         qFatal(QString("Binary not found: %1/toolset/%2").arg(m_appDir, current).toLatin1().constData());
726                         close(); qApp->exit(-1); return;
727                 }
728         }
729
730         //Check for portable mode
731         if(x264_portable())
732         {
733                 bool ok = false;
734                 static const char *data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
735                 QFile writeTest(QString("%1/%2").arg(x264_data_path(), QUuid::createUuid().toString()));
736                 if(writeTest.open(QIODevice::WriteOnly))
737                 {
738                         ok = (writeTest.write(data) == strlen(data));
739                         writeTest.remove();
740                 }
741                 if(!ok)
742                 {
743                         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"));
744                         if(val != 1) { close(); qApp->exit(-1); return; }
745                 }
746         }
747
748         //Pre-release popup
749         if(x264_is_prerelease())
750         {
751                 qsrand(time(NULL)); int rnd = qrand() % 3;
752                 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);
753                 if(rnd != val) { close(); qApp->exit(-1); return; }
754         }
755
756         //Make sure this CPU can run x264 (requires MMX + MMXEXT/iSSE to run x264 with ASM enabled, additionally requires SSE1 for most x264 builds)
757         if(!(m_cpuFeatures->mmx && m_cpuFeatures->mmx2))
758         {
759                 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"));
760                 qFatal("System does not support MMX and MMXEXT, x264 will not work !!!");
761                 close(); qApp->exit(-1); return;
762         }
763         else if(!(m_cpuFeatures->mmx && m_cpuFeatures->sse))
764         {
765                 qWarning("WARNING: System does not support SSE1, most x264 builds will not work !!!\n");
766                 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"));
767                 if(val != 1) { close(); qApp->exit(-1); return; }
768         }
769
770         //Check for Avisynth support
771         if(!qApp->arguments().contains("--skip-avisynth-check", Qt::CaseInsensitive))
772         {
773                 qDebug("[Check for Avisynth support]");
774                 volatile double avisynthVersion = 0.0;
775                 const int result = AvisynthCheckThread::detect(&avisynthVersion);
776                 if(result < 0)
777                 {
778                         QString text = tr("A critical error was encountered while checking your Avisynth version.").append("<br>");
779                         text += tr("This is most likely caused by an erroneous Avisynth Plugin, please try to clean your Plugins folder!").append("<br>");
780                         text += tr("We suggest to move all .dll and .avsi files out of your Avisynth Plugins folder and try again.");
781                         int val = QMessageBox::critical(this, tr("Avisynth Error"), QString("<nobr>%1</nobr>").arg(text).replace("-", "&minus;"), tr("Quit"), tr("Ignore"));
782                         if(val != 1) { close(); qApp->exit(-1); return; }
783                 }
784                 if((!result) || (avisynthVersion < 2.5))
785                 {
786                         int val = QMessageBox::warning(this, tr("Avisynth Missing"), tr("<nobr>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!<br><br>Please download and install Avisynth:<br><a href=\"http://sourceforge.net/projects/avisynth2/files/AviSynth%202.5/\">http://sourceforge.net/projects/avisynth2/files/AviSynth 2.5/</a></nobr>").replace("-", "&minus;"), tr("Quit"), tr("Ignore"));
787                         if(val != 1) { close(); qApp->exit(-1); return; }
788                 }
789                 qDebug("");
790         }
791
792         //Check for expiration
793         if(x264_version_date().addMonths(6) < QDate::currentDate())
794         {
795                 QMessageBox msgBox(this);
796                 msgBox.setIconPixmap(QIcon(":/images/update.png").pixmap(56,56));
797                 msgBox.setWindowTitle(tr("Update Notification"));
798                 msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
799                 msgBox.setText(tr("<nobr><tt>Your version of 'Simple x264 Launcher' is more than 6 months old!<br><br>Please download the most recent version from the official web-site at:<br><a href=\"%1\">%1</a><br></tt></nobr>").replace("-", "&minus;").arg(update_url));
800                 QPushButton *btn1 = msgBox.addButton(tr("Discard"), QMessageBox::NoRole);
801                 QPushButton *btn2 = msgBox.addButton(tr("Discard"), QMessageBox::AcceptRole);
802                 btn1->setEnabled(false);
803                 btn2->setVisible(false);
804                 QTimer::singleShot(5000, btn1, SLOT(hide()));
805                 QTimer::singleShot(5000, btn2, SLOT(show()));
806                 msgBox.exec();
807         }
808
809         //Add files from command-line
810         bool bAddFile = false;
811         QStringList files, args = qApp->arguments();
812         while(!args.isEmpty())
813         {
814                 QString current = args.takeFirst();
815                 if(!bAddFile)
816                 {
817                         bAddFile = (current.compare("--add", Qt::CaseInsensitive) == 0);
818                         continue;
819                 }
820                 if((!current.startsWith("--")) && QFileInfo(current).exists() && QFileInfo(current).isFile())
821                 {
822                         files << QFileInfo(current).canonicalFilePath();
823                 }
824         }
825         if(files.count() > 0)
826         {
827                 createJobMultiple(files);
828         }
829
830         //Enable drag&drop support for this window, required for Qt v4.8.4+
831         setAcceptDrops(true);
832 }
833
834 /*
835  * Update the label position
836  */
837 void MainWindow::updateLabelPos(void)
838 {
839         const QWidget *const viewPort = jobsView->viewport();
840         m_label->setGeometry(0, 0, viewPort->width(), viewPort->height());
841 }
842
843 /*
844  * Copy the complete log to the clipboard
845  */
846 void MainWindow::copyLogToClipboard(bool checked)
847 {
848         qDebug("copyLogToClipboard");
849         
850         if(LogFileModel *log = dynamic_cast<LogFileModel*>(logView->model()))
851         {
852                 log->copyToClipboard();
853                 MessageBeep(MB_ICONINFORMATION);
854         }
855 }
856
857 /*
858  * Process the dropped files
859  */
860 void MainWindow::handleDroppedFiles(void)
861 {
862         qDebug("MainWindow::handleDroppedFiles");
863         if(m_droppedFiles)
864         {
865                 QStringList droppedFiles(*m_droppedFiles);
866                 m_droppedFiles->clear();
867                 createJobMultiple(droppedFiles);
868         }
869         qDebug("Leave from MainWindow::handleDroppedFiles!");
870 }
871
872 void MainWindow::instanceCreated(DWORD pid)
873 {
874         qDebug("Notification from other instance (PID=0x%X) received!", pid);
875         
876         FLASHWINFO flashWinInfo;
877         memset(&flashWinInfo, 0, sizeof(FLASHWINFO));
878         flashWinInfo.cbSize = sizeof(FLASHWINFO);
879         flashWinInfo.hwnd = this->winId();
880         flashWinInfo.dwFlags = FLASHW_ALL;
881         flashWinInfo.dwTimeout = 125;
882         flashWinInfo.uCount = 5;
883
884         SwitchToThisWindow(this->winId(), TRUE);
885         SetForegroundWindow(this->winId());
886         qApp->processEvents();
887         FlashWindowEx(&flashWinInfo);
888 }
889
890 ///////////////////////////////////////////////////////////////////////////////
891 // Event functions
892 ///////////////////////////////////////////////////////////////////////////////
893
894 /*
895  * Window shown event
896  */
897 void MainWindow::showEvent(QShowEvent *e)
898 {
899         QMainWindow::showEvent(e);
900
901         if(m_firstShow)
902         {
903                 m_firstShow = false;
904                 QTimer::singleShot(0, this, SLOT(init()));
905         }
906 }
907
908 /*
909  * Window close event
910  */
911 void MainWindow::closeEvent(QCloseEvent *e)
912 {
913         if(countRunningJobs() > 0)
914         {
915                 e->ignore();
916                 QMessageBox::warning(this, tr("Jobs Are Running"), tr("Sorry, can not exit while there still are running jobs!"));
917                 return;
918         }
919         
920         if(countPendingJobs() > 0)
921         {
922                 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);
923                 if(ret != QMessageBox::Yes)
924                 {
925                         e->ignore();
926                         return;
927                 }
928         }
929
930         while(m_jobList->rowCount(QModelIndex()) > 0)
931         {
932                 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
933                 if(!m_jobList->deleteJob(m_jobList->index(0, 0, QModelIndex())))
934                 {
935                         e->ignore();
936                         QMessageBox::warning(this, tr("Failed To Exit"), tr("Sorry, at least one job could not be deleted!"));
937                         return;
938                 }
939         }
940
941         qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
942         QMainWindow::closeEvent(e);
943 }
944
945 /*
946  * Window resize event
947  */
948 void MainWindow::resizeEvent(QResizeEvent *e)
949 {
950         QMainWindow::resizeEvent(e);
951         updateLabelPos();
952 }
953
954 /*
955  * Event filter
956  */
957 bool MainWindow::eventFilter(QObject *o, QEvent *e)
958 {
959         if((o == labelBuildDate) && (e->type() == QEvent::MouseButtonPress))
960         {
961                 QTimer::singleShot(0, this, SLOT(showAbout()));
962                 return true;
963         }
964         return false;
965 }
966
967 /*
968  * Win32 message filter
969  */
970 bool MainWindow::winEvent(MSG *message, long *result)
971 {
972         return WinSevenTaskbar::handleWinEvent(message, result);
973 }
974
975 /*
976  * File dragged over window
977  */
978 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
979 {
980         bool accept[2] = {false, false};
981
982         foreach(const QString &fmt, event->mimeData()->formats())
983         {
984                 accept[0] = accept[0] || fmt.contains("text/uri-list", Qt::CaseInsensitive);
985                 accept[1] = accept[1] || fmt.contains("FileNameW", Qt::CaseInsensitive);
986         }
987
988         if(accept[0] && accept[1])
989         {
990                 event->acceptProposedAction();
991         }
992 }
993
994 /*
995  * File dropped onto window
996  */
997 void MainWindow::dropEvent(QDropEvent *event)
998 {
999         QStringList droppedFiles;
1000         QList<QUrl> urls = event->mimeData()->urls();
1001
1002         while(!urls.isEmpty())
1003         {
1004                 QUrl currentUrl = urls.takeFirst();
1005                 QFileInfo file(currentUrl.toLocalFile());
1006                 if(file.exists() && file.isFile())
1007                 {
1008                         qDebug("MainWindow::dropEvent: %s", file.canonicalFilePath().toUtf8().constData());
1009                         droppedFiles << file.canonicalFilePath();
1010                 }
1011         }
1012         
1013         if(droppedFiles.count() > 0)
1014         {
1015                 if(!m_droppedFiles)
1016                 {
1017                         m_droppedFiles = new QStringList();
1018                 }
1019                 m_droppedFiles->append(droppedFiles);
1020                 m_droppedFiles->sort();
1021                 QTimer::singleShot(0, this, SLOT(handleDroppedFiles()));
1022         }
1023 }
1024
1025 ///////////////////////////////////////////////////////////////////////////////
1026 // Private functions
1027 ///////////////////////////////////////////////////////////////////////////////
1028
1029 /*
1030  * Creates a new job
1031  */
1032 bool MainWindow::createJob(QString &sourceFileName, QString &outputFileName, OptionsModel *options, bool &runImmediately, const bool restart, int fileNo, int fileTotal, bool *applyToAll)
1033 {
1034         bool okay = false;
1035         AddJobDialog *addDialog = new AddJobDialog(this, options, m_recentlyUsed, m_cpuFeatures->x64, m_preferences->use10BitEncoding(), m_preferences->saveToSourcePath());
1036
1037         addDialog->setRunImmediately(runImmediately);
1038         if(!sourceFileName.isEmpty()) addDialog->setSourceFile(sourceFileName);
1039         if(!outputFileName.isEmpty()) addDialog->setOutputFile(outputFileName);
1040         if(restart) addDialog->setWindowTitle(tr("Restart Job"));
1041
1042         const bool multiFile = (fileNo >= 0) && (fileTotal > 1);
1043         if(multiFile)
1044         {
1045                 addDialog->setSourceEditable(false);
1046                 addDialog->setWindowTitle(addDialog->windowTitle().append(tr(" (File %1 of %2)").arg(QString::number(fileNo+1), QString::number(fileTotal))));
1047                 addDialog->setApplyToAllVisible(applyToAll);
1048         }
1049
1050         if(addDialog->exec() == QDialog::Accepted)
1051         {
1052                 sourceFileName = addDialog->sourceFile();
1053                 outputFileName = addDialog->outputFile();
1054                 runImmediately = addDialog->runImmediately();
1055                 if(applyToAll)
1056                 {
1057                         *applyToAll = addDialog->applyToAll();
1058                 }
1059                 okay = true;
1060         }
1061
1062         X264_DELETE(addDialog);
1063         return okay;
1064 }
1065
1066 /*
1067  * Creates a new job from *multiple* files
1068  */
1069 bool MainWindow::createJobMultiple(const QStringList &filePathIn)
1070 {
1071         QStringList::ConstIterator iter;
1072         bool applyToAll = false, runImmediately = false;
1073         int counter = 0;
1074
1075         //Add files individually
1076         for(iter = filePathIn.constBegin(); (iter != filePathIn.constEnd()) && (!applyToAll); iter++)
1077         {
1078                 runImmediately = (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
1079                 QString sourceFileName(*iter), outputFileName;
1080                 if(createJob(sourceFileName, outputFileName, m_options, runImmediately, false, counter++, filePathIn.count(), &applyToAll))
1081                 {
1082                         if(appendJob(sourceFileName, outputFileName, m_options, runImmediately))
1083                         {
1084                                 continue;
1085                         }
1086                 }
1087                 return false;
1088         }
1089
1090         //Add remaining files
1091         while(applyToAll && (iter != filePathIn.constEnd()))
1092         {
1093                 const bool runImmediatelyTmp = runImmediately && (countRunningJobs() < (m_preferences->autoRunNextJob() ? m_preferences->maxRunningJobCount() : 1));
1094                 const QString sourceFileName = *iter;
1095                 const QString outputFileName = AddJobDialog::generateOutputFileName(sourceFileName, m_recentlyUsed->outputDirectory(), m_recentlyUsed->filterIndex(), m_preferences->saveToSourcePath());
1096                 if(!appendJob(sourceFileName, outputFileName, m_options, runImmediatelyTmp))
1097                 {
1098                         return false;
1099                 }
1100                 iter++;
1101         }
1102
1103         return true;
1104 }
1105
1106
1107 /*
1108  * Append a new job
1109  */
1110 bool MainWindow::appendJob(const QString &sourceFileName, const QString &outputFileName, OptionsModel *options, const bool runImmediately)
1111 {
1112         bool okay = false;
1113         
1114         EncodeThread *thrd = new EncodeThread
1115         (
1116                 sourceFileName,
1117                 outputFileName,
1118                 options,
1119                 QString("%1/toolset").arg(m_appDir),
1120                 m_cpuFeatures->x64,
1121                 m_preferences->use10BitEncoding(),
1122                 m_cpuFeatures->x64 && m_preferences->useAvisyth64Bit(),
1123                 m_preferences->processPriority()
1124         );
1125
1126         QModelIndex newIndex = m_jobList->insertJob(thrd);
1127
1128         if(newIndex.isValid())
1129         {
1130                 if(runImmediately)
1131                 {
1132                         jobsView->selectRow(newIndex.row());
1133                         QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1134                         m_jobList->startJob(newIndex);
1135                 }
1136
1137                 okay = true;
1138         }
1139
1140         m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0);
1141         return okay;
1142 }
1143
1144 /*
1145  * Jobs that are not completed (or failed, or aborted) yet
1146  */
1147 unsigned int MainWindow::countPendingJobs(void)
1148 {
1149         unsigned int count = 0;
1150         const int rows = m_jobList->rowCount(QModelIndex());
1151
1152         for(int i = 0; i < rows; i++)
1153         {
1154                 JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex()));
1155                 if(status != JobStatus_Completed && status != JobStatus_Aborted && status != JobStatus_Failed)
1156                 {
1157                         count++;
1158                 }
1159         }
1160
1161         return count;
1162 }
1163
1164 /*
1165  * Jobs that are still active, i.e. not terminated or enqueued
1166  */
1167 unsigned int MainWindow::countRunningJobs(void)
1168 {
1169         unsigned int count = 0;
1170         const int rows = m_jobList->rowCount(QModelIndex());
1171
1172         for(int i = 0; i < rows; i++)
1173         {
1174                 JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex()));
1175                 if(status != JobStatus_Completed && status != JobStatus_Aborted && status != JobStatus_Failed && status != JobStatus_Enqueued)
1176                 {
1177                         count++;
1178                 }
1179         }
1180
1181         return count;
1182 }
1183
1184 /*
1185  * Update all buttons with respect to current job status
1186  */
1187 void MainWindow::updateButtons(JobStatus status)
1188 {
1189         qDebug("MainWindow::updateButtons(void)");
1190
1191         buttonStartJob->setEnabled(status == JobStatus_Enqueued);
1192         buttonAbortJob->setEnabled(status == JobStatus_Indexing || status == JobStatus_Running || status == JobStatus_Running_Pass1 || status == JobStatus_Running_Pass2 || status == JobStatus_Paused);
1193         buttonPauseJob->setEnabled(status == JobStatus_Indexing || status == JobStatus_Running || status == JobStatus_Paused || status == JobStatus_Running_Pass1 || status == JobStatus_Running_Pass2);
1194         buttonPauseJob->setChecked(status == JobStatus_Paused || status == JobStatus_Pausing);
1195
1196         actionJob_Delete->setEnabled(status == JobStatus_Completed || status == JobStatus_Aborted || status == JobStatus_Failed || status == JobStatus_Enqueued);
1197         actionJob_Restart->setEnabled(status == JobStatus_Completed || status == JobStatus_Aborted || status == JobStatus_Failed || status == JobStatus_Enqueued);
1198         actionJob_Browse->setEnabled(status == JobStatus_Completed);
1199
1200         actionJob_Start->setEnabled(buttonStartJob->isEnabled());
1201         actionJob_Abort->setEnabled(buttonAbortJob->isEnabled());
1202         actionJob_Pause->setEnabled(buttonPauseJob->isEnabled());
1203         actionJob_Pause->setChecked(buttonPauseJob->isChecked());
1204
1205         editDetails->setEnabled(status != JobStatus_Paused);
1206 }
1207
1208 /*
1209  * Update the taskbar with current job status
1210  */
1211 void MainWindow::updateTaskbar(JobStatus status, const QIcon &icon)
1212 {
1213         qDebug("MainWindow::updateTaskbar(void)");
1214
1215         switch(status)
1216         {
1217         case JobStatus_Undefined:
1218                 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarNoState);
1219                 break;
1220         case JobStatus_Aborting:
1221         case JobStatus_Starting:
1222         case JobStatus_Pausing:
1223         case JobStatus_Resuming:
1224                 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarIndeterminateState);
1225                 break;
1226         case JobStatus_Aborted:
1227         case JobStatus_Failed:
1228                 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarErrorState);
1229                 break;
1230         case JobStatus_Paused:
1231                 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarPausedState);
1232                 break;
1233         default:
1234                 WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarNormalState);
1235                 break;
1236         }
1237
1238         switch(status)
1239         {
1240         case JobStatus_Aborting:
1241         case JobStatus_Starting:
1242         case JobStatus_Pausing:
1243         case JobStatus_Resuming:
1244                 break;
1245         default:
1246                 WinSevenTaskbar::setTaskbarProgress(this, progressBar->value(), progressBar->maximum());
1247                 break;
1248         }
1249
1250         WinSevenTaskbar::setOverlayIcon(this, icon.isNull() ? NULL : &icon);
1251 }