X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2Fwin_main.cpp;h=2495b8b49e5cd80267144dead2196ee07bbbbac8;hb=7fe6779177716755fd4ff282dc2f58024882bb0b;hp=db6a37e37cb208f761a16e0698deb3164502b8bb;hpb=aacf84b88a4eef67bce17e1cce29c4a64a575398;p=x264-launcher%2Fx264-launcher.git diff --git a/src/win_main.cpp b/src/win_main.cpp index db6a37e..2495b8b 100644 --- a/src/win_main.cpp +++ b/src/win_main.cpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// // Simple x264 Launcher -// Copyright (C) 2004-2012 LoRd_MuldeR +// Copyright (C) 2004-2020 LoRd_MuldeR // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -20,15 +20,41 @@ /////////////////////////////////////////////////////////////////////////////// #include "win_main.h" - +#include "UIC_win_main.h" + +//Internal +#include "global.h" +#include "cli.h" +#include "ipc.h" +#include "model_status.h" +#include "model_sysinfo.h" #include "model_jobList.h" #include "model_options.h" +#include "model_preferences.h" +#include "model_recently.h" +#include "thread_avisynth.h" +#include "thread_binaries.h" +#include "thread_vapoursynth.h" +#include "thread_encode.h" +#include "thread_ipc_recv.h" +#include "input_filter.h" #include "win_addJob.h" +#include "win_about.h" #include "win_preferences.h" -#include "thread_avisynth.h" -#include "taskbar7.h" +#include "win_updater.h" #include "resource.h" +//MUtils +#include +#include +#include +#include +#include +#include +#include +#include + +//Qt #include #include #include @@ -42,17 +68,30 @@ #include #include #include - -#include - -const char *home_url = "http://mulder.brhack.net/"; -const char *update_url = "http://code.google.com/p/mulder/downloads/list"; -const char *tpl_last = ""; - -#define SET_FONT_BOLD(WIDGET,BOLD) { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); } -#define SET_TEXT_COLOR(WIDGET,COLOR) { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, (COLOR)); _palette.setColor(QPalette::Text, (COLOR)); WIDGET->setPalette(_palette); } - -static int exceptionFilter(_EXCEPTION_RECORD *dst, _EXCEPTION_POINTERS *src) { memcpy(dst, src->ExceptionRecord, sizeof(_EXCEPTION_RECORD)); return EXCEPTION_EXECUTE_HANDLER; } +#include +#include +#include +#include +#include + +//Constants +static const char *tpl_last = ""; +static const char *home_url = "http://muldersoft.com/"; +static const char *update_url = "https://github.com/lordmulder/Simple-x264-Launcher/releases/latest"; +static const char *avs_dl_url = "http://sourceforge.net/projects/avisynth2/files/AviSynth%202.5/"; +static const char *python_url = "https://www.python.org/downloads/"; +static const char *vsynth_url = "http://www.vapoursynth.com/"; +static const int vsynth_rev = 24; + +//Macros +#define SET_FONT_BOLD(WIDGET,BOLD) do { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); } while(0) +#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) +#define LINK(URL) (QString("%1").arg((URL))) +#define INIT_ERROR_EXIT() do { close(); qApp->exit(-1); return; } while(0) +#define SETUP_WEBLINK(OBJ, URL) do { (OBJ)->setData(QVariant(QUrl(URL))); connect((OBJ), SIGNAL(triggered()), this, SLOT(showWebLink())); } while(0) +#define APP_IS_READY (m_initialized && (!m_fileTimer->isActive()) && (QApplication::activeModalWidget() == NULL)) +#define ENSURE_APP_IS_READY() do { if(!APP_IS_READY) { MUtils::Sound::beep(MUtils::Sound::BEEP_WRN); qWarning("Cannot perfrom this action at this time!"); return; } } while(0) +#define X264_STRCMP(X,Y) ((X).compare((Y), Qt::CaseInsensitive) == 0) /////////////////////////////////////////////////////////////////////////////// // Constructor & Destructor @@ -61,45 +100,58 @@ static int exceptionFilter(_EXCEPTION_RECORD *dst, _EXCEPTION_POINTERS *src) { m /* * Constructor */ -MainWindow::MainWindow(const x264_cpu_t *const cpuFeatures) +MainWindow::MainWindow(const MUtils::CPUFetaures::cpu_info_t &cpuFeatures, MUtils::IPCChannel *const ipcChannel) : - m_cpuFeatures(cpuFeatures), - m_appDir(QApplication::applicationDirPath()), + m_ipcChannel(ipcChannel), + m_sysinfo(NULL), m_options(NULL), m_jobList(NULL), - m_droppedFiles(NULL), - m_firstShow(true) + m_pendingFiles(new QStringList()), + m_preferences(NULL), + m_recentlyUsed(NULL), + m_postOperation(POST_OP_DONOTHING), + m_initialized(false), + ui(new Ui::MainWindow()) { //Init the dialog, from the .ui file - setupUi(this); + ui->setupUi(this); setWindowFlags(windowFlags() & (~Qt::WindowMaximizeButtonHint)); //Register meta types qRegisterMetaType("QUuid"); qRegisterMetaType("DWORD"); - qRegisterMetaType("EncodeThread::JobStatus"); + qRegisterMetaType("JobStatus"); + + //Create and initialize the sysinfo object + m_sysinfo.reset(new SysinfoModel()); + m_sysinfo->setAppPath(QApplication::applicationDirPath()); + m_sysinfo->setCPUFeatures(SysinfoModel::CPUFeatures_MMX, cpuFeatures.features & MUtils::CPUFetaures::FLAG_MMX); + m_sysinfo->setCPUFeatures(SysinfoModel::CPUFeatures_SSE, cpuFeatures.features & MUtils::CPUFetaures::FLAG_SSE); + m_sysinfo->setCPUFeatures(SysinfoModel::CPUFeatures_X64, cpuFeatures.x64 && (cpuFeatures.features & MUtils::CPUFetaures::FLAG_SSE2)); //X64 implies SSE2 //Load preferences - PreferencesDialog::initPreferences(&m_preferences); - PreferencesDialog::loadPreferences(&m_preferences); + m_preferences.reset(new PreferencesModel()); + PreferencesModel::loadPreferences(m_preferences.data()); + + //Load recently used + m_recentlyUsed.reset(new RecentlyUsed()); + RecentlyUsed::loadRecentlyUsed(m_recentlyUsed.data()); //Create options object - m_options = new OptionsModel(); - OptionsModel::loadTemplate(m_options, QString::fromLatin1(tpl_last)); + m_options.reset(new OptionsModel(m_sysinfo.data())); + OptionsModel::loadTemplate(m_options.data(), QString::fromLatin1(tpl_last)); - //Create IPC thread object - m_ipcThread = new IPCThread(); - connect(m_ipcThread, SIGNAL(instanceCreated(DWORD)), this, SLOT(instanceCreated(DWORD)), Qt::QueuedConnection); + //DPI scaling + MUtils::GUI::scale_widget(this); //Freeze minimum size setMinimumSize(size()); - splitter->setSizes(QList() << 16 << 196); + ui->splitter->setSizes(QList() << 16 << 196); //Update title - labelBuildDate->setText(tr("Built on %1 at %2").arg(x264_version_date().toString(Qt::ISODate), QString::fromLatin1(x264_version_time()))); - labelBuildDate->installEventFilter(this); - setWindowTitle(QString("%1 (%2 Mode)").arg(windowTitle(), m_cpuFeatures->x64 ? "64-Bit" : "32-Bit")); - if(X264_DEBUG) + ui->labelBuildDate->setText(tr("Built on %1 at %2").arg(MUtils::Version::app_build_date().toString(Qt::ISODate), MUtils::Version::app_build_time().toString(Qt::ISODate))); + + if(MUTILS_DEBUG) { setWindowTitle(QString("%1 | !!! DEBUG VERSION !!!").arg(windowTitle())); setStyleSheet("QMenuBar, QMainWindow { background-color: yellow }"); @@ -110,63 +162,152 @@ MainWindow::MainWindow(const x264_cpu_t *const cpuFeatures) } //Create model - m_jobList = new JobListModel(); - connect(m_jobList, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(jobChangedData(QModelIndex, QModelIndex))); - jobsView->setModel(m_jobList); + m_jobList.reset(new JobListModel(m_preferences.data())); + connect(m_jobList.data(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(jobChangedData(QModelIndex, QModelIndex))); + ui->jobsView->setModel(m_jobList.data()); //Setup view - jobsView->horizontalHeader()->setSectionHidden(3, true); - jobsView->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); - jobsView->horizontalHeader()->setResizeMode(1, QHeaderView::Fixed); - jobsView->horizontalHeader()->setResizeMode(2, QHeaderView::Fixed); - jobsView->horizontalHeader()->resizeSection(1, 150); - jobsView->horizontalHeader()->resizeSection(2, 90); - jobsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); - connect(jobsView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(jobSelected(QModelIndex, QModelIndex))); + ui->jobsView->horizontalHeader()->setSectionHidden(3, true); + ui->jobsView->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); + ui->jobsView->horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents); + ui->jobsView->horizontalHeader()->setResizeMode(2, QHeaderView::ResizeToContents); + ui->jobsView->horizontalHeader()->setMinimumSectionSize(96); + ui->jobsView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); + connect(ui->jobsView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(jobSelected(QModelIndex, QModelIndex))); + + //Setup key listener + m_inputFilter_jobList.reset(new InputEventFilter(ui->jobsView)); + m_inputFilter_jobList->addKeyFilter(Qt::ControlModifier | Qt::Key_Up, 1); + m_inputFilter_jobList->addKeyFilter(Qt::ControlModifier | Qt::Key_Down, 2); + connect(m_inputFilter_jobList.data(), SIGNAL(keyPressed(int)), this, SLOT(jobListKeyPressed(int))); + + //Setup mouse listener + m_inputFilter_version.reset(new InputEventFilter(ui->labelBuildDate)); + m_inputFilter_version->addMouseFilter(Qt::LeftButton, 0); + m_inputFilter_version->addMouseFilter(Qt::RightButton, 0); + connect(m_inputFilter_version.data(), SIGNAL(mouseClicked(int)), this, SLOT(versionLabelMouseClicked(int))); //Create context menu - QAction *actionClipboard = new QAction(QIcon(":/buttons/page_paste.png"), tr("Copy to Clipboard"), logView); + QAction *actionClipboard = new QAction(QIcon(":/buttons/page_paste.png"), tr("Copy to Clipboard"), ui->logView); + QAction *actionSaveToLog = new QAction(QIcon(":/buttons/disk.png"), tr("Save to File..."), ui->logView); + QAction *actionSeparator = new QAction(ui->logView); + QAction *actionWordwraps = new QAction(QIcon(":/buttons/text_wrapping.png"), tr("Enable Line-Wrapping"), ui->logView); + actionSeparator->setSeparator(true); + actionWordwraps->setCheckable(true); actionClipboard->setEnabled(false); - logView->addAction(actionClipboard); + actionSaveToLog->setEnabled(false); + actionWordwraps->setEnabled(false); + ui->logView->addAction(actionClipboard); + ui->logView->addAction(actionSaveToLog); + ui->logView->addAction(actionSeparator); + ui->logView->addAction(actionWordwraps); connect(actionClipboard, SIGNAL(triggered(bool)), this, SLOT(copyLogToClipboard(bool))); - jobsView->addActions(menuJob->actions()); + connect(actionSaveToLog, SIGNAL(triggered(bool)), this, SLOT(saveLogToLocalFile(bool))); + connect(actionWordwraps, SIGNAL(triggered(bool)), this, SLOT(toggleLineWrapping(bool))); + ui->jobsView->addActions(ui->menuJob->actions()); //Enable buttons - connect(buttonAddJob, SIGNAL(clicked()), this, SLOT(addButtonPressed())); - connect(buttonStartJob, SIGNAL(clicked()), this, SLOT(startButtonPressed())); - connect(buttonAbortJob, SIGNAL(clicked()), this, SLOT(abortButtonPressed())); - connect(buttonPauseJob, SIGNAL(toggled(bool)), this, SLOT(pauseButtonPressed(bool))); - connect(actionJob_Delete, SIGNAL(triggered()), this, SLOT(deleteButtonPressed())); - connect(actionJob_Restart, SIGNAL(triggered()), this, SLOT(restartButtonPressed())); - connect(actionJob_Browse, SIGNAL(triggered()), this, SLOT(browseButtonPressed())); + connect(ui->buttonAddJob, SIGNAL(clicked()), this, SLOT(addButtonPressed() )); + connect(ui->buttonStartJob, SIGNAL(clicked()), this, SLOT(startButtonPressed() )); + connect(ui->buttonAbortJob, SIGNAL(clicked()), this, SLOT(abortButtonPressed() )); + connect(ui->buttonPauseJob, SIGNAL(toggled(bool)), this, SLOT(pauseButtonPressed(bool))); + connect(ui->actionJob_Delete, SIGNAL(triggered()), this, SLOT(deleteButtonPressed() )); + connect(ui->actionJob_Restart, SIGNAL(triggered()), this, SLOT(restartButtonPressed() )); + connect(ui->actionJob_Browse, SIGNAL(triggered()), this, SLOT(browseButtonPressed() )); + connect(ui->actionJob_MoveUp, SIGNAL(triggered()), this, SLOT(moveButtonPressed() )); + connect(ui->actionJob_MoveDown, SIGNAL(triggered()), this, SLOT(moveButtonPressed() )); //Enable menu - connect(actionAbout, SIGNAL(triggered()), this, SLOT(showAbout())); - connect(actionWebMulder, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebX264, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebKomisar, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebJarod, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebJEEB, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebAvisynth32, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebAvisynth64, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebWiki, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebBluRay, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebAvsWiki, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebSecret, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionWebSupport, SIGNAL(triggered()), this, SLOT(showWebLink())); - connect(actionPreferences, SIGNAL(triggered()), this, SLOT(showPreferences())); + connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(openActionTriggered())); + connect(ui->actionCleanup_Finished, SIGNAL(triggered()), this, SLOT(cleanupActionTriggered())); + connect(ui->actionCleanup_Enqueued, SIGNAL(triggered()), this, SLOT(cleanupActionTriggered())); + connect(ui->actionPostOp_DoNothing, SIGNAL(triggered()), this, SLOT(postOpActionTriggered())); + connect(ui->actionPostOp_PowerDown, SIGNAL(triggered()), this, SLOT(postOpActionTriggered())); + connect(ui->actionPostOp_Hibernate, SIGNAL(triggered()), this, SLOT(postOpActionTriggered())); + connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAbout())); + connect(ui->actionPreferences, SIGNAL(triggered()), this, SLOT(showPreferences())); + connect(ui->actionCheckForUpdates, SIGNAL(triggered()), this, SLOT(checkUpdates())); + ui->actionCleanup_Finished->setData(QVariant(bool(0))); + ui->actionCleanup_Enqueued->setData(QVariant(bool(1))); + ui->actionPostOp_DoNothing->setData(QVariant(POST_OP_DONOTHING)); + ui->actionPostOp_PowerDown->setData(QVariant(POST_OP_POWERDOWN)); + ui->actionPostOp_Hibernate->setData(QVariant(POST_OP_HIBERNATE)); + ui->actionPostOp_Hibernate->setEnabled(MUtils::OS::is_hibernation_supported()); + + //Setup web-links + SETUP_WEBLINK(ui->actionWebMulder, home_url); + SETUP_WEBLINK(ui->actionWebX264, "http://www.videolan.org/developers/x264.html"); + SETUP_WEBLINK(ui->actionWebX265, "http://www.videolan.org/developers/x265.html"); + SETUP_WEBLINK(ui->actionWebX264LigH, "http://www.mediafire.com/?bxvu1vvld31k1"); + SETUP_WEBLINK(ui->actionWebX264VideoLAN, "http://artifacts.videolan.org/x264/"); + SETUP_WEBLINK(ui->actionWebX264Komisar, "http://komisar.gin.by/"); + SETUP_WEBLINK(ui->actionWebX265LigH, "http://www.mediafire.com/?6lfp2jlygogwa"); + SETUP_WEBLINK(ui->actionWebX264FreeCodecs, "http://www.free-codecs.com/x264_video_codec_download.htm"); + SETUP_WEBLINK(ui->actionWebX265Fllear, "http://x265.ru/en/builds/"); + SETUP_WEBLINK(ui->actionWebX265Snowfag, "http://builds.x265.eu/"); + SETUP_WEBLINK(ui->actionWebX265FreeCodecs, "http://www.free-codecs.com/x265_hevc_encoder_download.htm"); + SETUP_WEBLINK(ui->actionWebAvisynth32, "https://sourceforge.net/projects/avisynth2/files/AviSynth%202.6/"); + SETUP_WEBLINK(ui->actionWebAvisynth64, "http://forum.doom9.org/showthread.php?t=152800"); + SETUP_WEBLINK(ui->actionWebAvisynthPlus, "http://www.avs-plus.net/"); + SETUP_WEBLINK(ui->actionWebVapourSynth, "http://www.vapoursynth.com/"); + SETUP_WEBLINK(ui->actionWebVapourSynthDocs, "http://www.vapoursynth.com/doc/"); + SETUP_WEBLINK(ui->actionOnlineDocX264, "http://en.wikibooks.org/wiki/MeGUI/x264_Settings"); //http://mewiki.project357.com/wiki/X264_Settings + SETUP_WEBLINK(ui->actionOnlineDocX265, "http://x265.readthedocs.org/en/default/"); + SETUP_WEBLINK(ui->actionWebBluRay, "http://www.x264bluray.com/"); + SETUP_WEBLINK(ui->actionWebAvsWiki, "http://avisynth.nl/index.php/Main_Page#Usage"); + SETUP_WEBLINK(ui->actionWebSupport, "http://forum.doom9.org/showthread.php?t=144140"); + SETUP_WEBLINK(ui->actionWebSecret, "http://www.youtube.com/watch_popup?v=AXIeHY-OYNI"); //Create floating label - m_label = new QLabel(jobsView->viewport()); - m_label->setText(tr("No job created yet. Please click the 'Add New Job' button!")); - m_label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); - SET_TEXT_COLOR(m_label, Qt::darkGray); - SET_FONT_BOLD(m_label, true); - m_label->setVisible(true); - m_label->setContextMenuPolicy(Qt::ActionsContextMenu); - m_label->addActions(jobsView->actions()); - connect(splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(updateLabelPos())); + m_label[0].reset(new QLabel(ui->jobsView->viewport())); + m_label[1].reset(new QLabel(ui->logView->viewport())); + if(!m_label[0].isNull()) + { + m_label[0]->setText(tr("No job created yet. Please click the 'Add New Job' button!")); + m_label[0]->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + SET_TEXT_COLOR(m_label[0], Qt::darkGray); + SET_FONT_BOLD(m_label[0], true); + m_label[0]->setVisible(true); + m_label[0]->setContextMenuPolicy(Qt::ActionsContextMenu); + m_label[0]->addActions(ui->jobsView->actions()); + } + if(!m_label[1].isNull()) + { + m_animation.reset(new QMovie(":/images/spinner.gif")); + m_label[1]->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + if(!m_animation.isNull()) + { + m_label[1]->setMovie(m_animation.data()); + m_animation->start(); + } + } + connect(ui->splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(updateLabelPos())); updateLabelPos(); + + //Init system tray icon + m_sysTray.reset(new QSystemTrayIcon(this)); + m_sysTray->setToolTip(this->windowTitle()); + m_sysTray->setIcon(this->windowIcon()); + connect(m_sysTray.data(), SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(sysTrayActived())); + + //Init taskbar progress + m_taskbar.reset(new MUtils::Taskbar7(this)); + + //Create corner widget + QLabel *checkUp = new QLabel(ui->menubar); + checkUp->setText(QString(" %1   ").arg(tr("Check for Updates"))); + checkUp->setFixedHeight(ui->menubar->height()); + checkUp->setCursor(QCursor(Qt::PointingHandCursor)); + m_inputFilter_checkUp.reset(new InputEventFilter(checkUp)); + m_inputFilter_checkUp->addMouseFilter(Qt::LeftButton, 0); + m_inputFilter_checkUp->addMouseFilter(Qt::RightButton, 0); + connect(m_inputFilter_checkUp.data(), SIGNAL(mouseClicked(int)), this, SLOT(checkUpdates())); + checkUp->hide(); + ui->menubar->setCornerWidget(checkUp); + + //Create timer + m_fileTimer.reset(new QTimer(this)); + connect(m_fileTimer.data(), SIGNAL(timeout()), this, SLOT(handlePendingFiles())); } /* @@ -174,22 +315,11 @@ MainWindow::MainWindow(const x264_cpu_t *const cpuFeatures) */ MainWindow::~MainWindow(void) { - OptionsModel::saveTemplate(m_options, QString::fromLatin1(tpl_last)); + OptionsModel::saveTemplate(m_options.data(), QString::fromLatin1(tpl_last)); - X264_DELETE(m_jobList); - X264_DELETE(m_options); - X264_DELETE(m_droppedFiles); - X264_DELETE(m_label); - - while(!m_toolsList.isEmpty()) + if(!m_ipcThread.isNull()) { - QFile *temp = m_toolsList.takeFirst(); - X264_DELETE(temp); - } - - if(m_ipcThread->isRunning()) - { - m_ipcThread->setAbort(); + m_ipcThread->stop(); if(!m_ipcThread->wait(5000)) { m_ipcThread->terminate(); @@ -197,8 +327,7 @@ MainWindow::~MainWindow(void) } } - X264_DELETE(m_ipcThread); - AvisynthCheckThread::unload(); + delete ui; } /////////////////////////////////////////////////////////////////////////////// @@ -210,13 +339,113 @@ MainWindow::~MainWindow(void) */ void MainWindow::addButtonPressed() { + ENSURE_APP_IS_READY(); + qDebug("MainWindow::addButtonPressed"); - bool runImmediately = (countRunningJobs() < (m_preferences.autoRunNextJob ? m_preferences.maxRunningJobCount : 1)); + bool runImmediately = (countRunningJobs() < (m_preferences->getAutoRunNextJob() ? m_preferences->getMaxRunningJobCount() : 1)); QString sourceFileName, outputFileName; - - if(createJob(sourceFileName, outputFileName, m_options, runImmediately)) + + if(createJob(sourceFileName, outputFileName, m_options.data(), runImmediately)) + { + appendJob(sourceFileName, outputFileName, m_options.data(), runImmediately); + } +} + +/* + * The "open" action was triggered + */ +void MainWindow::openActionTriggered() +{ + ENSURE_APP_IS_READY(); + qWarning("openActionTriggered()"); + + QStringList fileList = QFileDialog::getOpenFileNames(this, tr("Open Source File(s)"), m_recentlyUsed->sourceDirectory(), AddJobDialog::getInputFilterLst(), NULL, QFileDialog::DontUseNativeDialog); + if(!fileList.empty()) + { + m_recentlyUsed->setSourceDirectory(QFileInfo(fileList.last()).absolutePath()); + if(fileList.count() > 1) + { + createJobMultiple(fileList); + } + else + { + bool runImmediately = (countRunningJobs() < (m_preferences->getAutoRunNextJob() ? m_preferences->getMaxRunningJobCount() : 1)); + QString sourceFileName(fileList.first()), outputFileName; + if(createJob(sourceFileName, outputFileName, m_options.data(), runImmediately)) + { + appendJob(sourceFileName, outputFileName, m_options.data(), runImmediately); + } + } + } +} + +/* +* The "clean-up" action was invoked +*/ +void MainWindow::cleanupActionTriggered(void) +{ + ENSURE_APP_IS_READY(); + + QAction *const sender = dynamic_cast(QObject::sender()); + if (sender) + { + const QVariant data = sender->data(); + if (data.isValid() && (data.type() == QVariant::Bool)) + { + const bool mode = data.toBool(); + const int rows = m_jobList->rowCount(QModelIndex()); + QList jobIndices; + for (int i = 0; i < rows; i++) + { + const JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex())); + if (mode && (status == JobStatus_Enqueued)) + { + jobIndices.append(i); + } + else if ((!mode) && ((status == JobStatus_Completed) || (status == JobStatus_Aborted) || (status == JobStatus_Failed))) + { + jobIndices.append(i); + } + } + if (!jobIndices.isEmpty()) + { + QListIterator iter(jobIndices); + iter.toBack(); + while(iter.hasPrevious()) + { + m_jobList->deleteJob(m_jobList->index(iter.previous(), 0, QModelIndex())); + } + } + else + { + MUtils::Sound::beep(MUtils::Sound::BEEP_WRN); + } + } + } +} + +/* +* The "clean-up" action was invoked +*/ +void MainWindow::postOpActionTriggered(void) +{ + ENSURE_APP_IS_READY(); + + QAction *const sender = dynamic_cast(QObject::sender()); + if (sender) { - appendJob(sourceFileName, outputFileName, m_options, runImmediately); + const QVariant data = sender->data(); + if (data.isValid() && (data.type() == QVariant::Int)) + { + const postOp_t mode = (postOp_t)data.toInt(); + if ((mode >= POST_OP_DONOTHING) && (mode <= POST_OP_HIBERNATE)) + { + m_postOperation = mode; + ui->actionPostOp_PowerDown->setChecked(mode == POST_OP_POWERDOWN); + ui->actionPostOp_Hibernate->setChecked(mode == POST_OP_HIBERNATE); + ui->actionPostOp_DoNothing->setChecked(mode == POST_OP_DONOTHING); + } + } } } @@ -225,7 +454,8 @@ void MainWindow::addButtonPressed() */ void MainWindow::startButtonPressed(void) { - m_jobList->startJob(jobsView->currentIndex()); + ENSURE_APP_IS_READY(); + m_jobList->startJob(ui->jobsView->currentIndex()); } /* @@ -233,7 +463,12 @@ void MainWindow::startButtonPressed(void) */ void MainWindow::abortButtonPressed(void) { - m_jobList->abortJob(jobsView->currentIndex()); + ENSURE_APP_IS_READY(); + + if(QMessageBox::question(this, tr("Abort Job?"), tr("Do you really want to abort the selected job now?"), tr("Back"), tr("Abort Job")) == 1) + { + m_jobList->abortJob(ui->jobsView->currentIndex()); + } } /* @@ -241,8 +476,10 @@ void MainWindow::abortButtonPressed(void) */ void MainWindow::deleteButtonPressed(void) { - m_jobList->deleteJob(jobsView->currentIndex()); - m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0); + ENSURE_APP_IS_READY(); + + m_jobList->deleteJob(ui->jobsView->currentIndex()); + m_label[0]->setVisible(m_jobList->rowCount(QModelIndex()) == 0); } /* @@ -250,7 +487,9 @@ void MainWindow::deleteButtonPressed(void) */ void MainWindow::browseButtonPressed(void) { - QString outputFile = m_jobList->getJobOutputFile(jobsView->currentIndex()); + ENSURE_APP_IS_READY(); + + QString outputFile = m_jobList->getJobOutputFile(ui->jobsView->currentIndex()); if((!outputFile.isEmpty()) && QFileInfo(outputFile).exists() && QFileInfo(outputFile).isFile()) { QProcess::startDetached(QString::fromLatin1("explorer.exe"), QStringList() << QString::fromLatin1("/select,") << QDir::toNativeSeparators(outputFile), QFileInfo(outputFile).path()); @@ -262,17 +501,55 @@ void MainWindow::browseButtonPressed(void) } /* + * The "browse" button was clicked + */ +void MainWindow::moveButtonPressed(void) +{ + ENSURE_APP_IS_READY(); + + if(sender() == ui->actionJob_MoveUp) + { + qDebug("Move job %d (direction: UP)", ui->jobsView->currentIndex().row()); + if(!m_jobList->moveJob(ui->jobsView->currentIndex(), JobListModel::MOVE_UP)) + { + MUtils::Sound::beep(MUtils::Sound::BEEP_ERR); + } + ui->jobsView->scrollTo(ui->jobsView->currentIndex(), QAbstractItemView::PositionAtCenter); + } + else if(sender() == ui->actionJob_MoveDown) + { + qDebug("Move job %d (direction: DOWN)", ui->jobsView->currentIndex().row()); + if(!m_jobList->moveJob(ui->jobsView->currentIndex(), JobListModel::MOVE_DOWN)) + { + MUtils::Sound::beep(MUtils::Sound::BEEP_ERR); + } + ui->jobsView->scrollTo(ui->jobsView->currentIndex(), QAbstractItemView::PositionAtCenter); + } + else + { + qWarning("[moveButtonPressed] Error: Unknown sender!"); + } +} + +/* * The "pause" button was clicked */ void MainWindow::pauseButtonPressed(bool checked) { + if(!APP_IS_READY) + { + MUtils::Sound::beep(MUtils::Sound::BEEP_WRN); + qWarning("Cannot perfrom this action at this time!"); + ui->buttonPauseJob->setChecked(!checked); + } + if(checked) { - m_jobList->pauseJob(jobsView->currentIndex()); + m_jobList->pauseJob(ui->jobsView->currentIndex()); } else { - m_jobList->resumeJob(jobsView->currentIndex()); + m_jobList->resumeJob(ui->jobsView->currentIndex()); } } @@ -281,20 +558,22 @@ void MainWindow::pauseButtonPressed(bool checked) */ void MainWindow::restartButtonPressed(void) { - const QModelIndex index = jobsView->currentIndex(); + ENSURE_APP_IS_READY(); + + const QModelIndex index = ui->jobsView->currentIndex(); const OptionsModel *options = m_jobList->getJobOptions(index); QString sourceFileName = m_jobList->getJobSourceFile(index); QString outputFileName = m_jobList->getJobOutputFile(index); if((options) && (!sourceFileName.isEmpty()) && (!outputFileName.isEmpty())) { - bool runImmediately = true; + bool runImmediately = (countRunningJobs() < (m_preferences->getAutoRunNextJob() ? m_preferences->getMaxRunningJobCount() : 1)); OptionsModel *tempOptions = new OptionsModel(*options); if(createJob(sourceFileName, outputFileName, tempOptions, runImmediately, true)) { appendJob(sourceFileName, outputFileName, tempOptions, runImmediately); } - X264_DELETE(tempOptions); + MUTILS_DELETE(tempOptions); } } @@ -305,34 +584,40 @@ void MainWindow::jobSelected(const QModelIndex & current, const QModelIndex & pr { qDebug("Job selected: %d", current.row()); - if(logView->model()) + if(ui->logView->model()) { - disconnect(logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int))); + disconnect(ui->logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int))); } if(current.isValid()) { - logView->setModel(m_jobList->getLogFile(current)); - connect(logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int))); - logView->actions().first()->setEnabled(true); - QTimer::singleShot(0, logView, SLOT(scrollToBottom())); + ui->logView->setModel(m_jobList->getLogFile(current)); + connect(ui->logView->model(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(jobLogExtended(QModelIndex, int, int))); + foreach(QAction *action, ui->logView->actions()) + { + action->setEnabled(true); + } + QTimer::singleShot(0, ui->logView, SLOT(scrollToBottom())); - progressBar->setValue(m_jobList->getJobProgress(current)); - editDetails->setText(m_jobList->data(m_jobList->index(current.row(), 3, QModelIndex()), Qt::DisplayRole).toString()); + ui->progressBar->setValue(m_jobList->getJobProgress(current)); + ui->editDetails->setText(m_jobList->data(m_jobList->index(current.row(), 3, QModelIndex()), Qt::DisplayRole).toString()); updateButtons(m_jobList->getJobStatus(current)); updateTaskbar(m_jobList->getJobStatus(current), m_jobList->data(m_jobList->index(current.row(), 0, QModelIndex()), Qt::DecorationRole).value()); } else { - logView->setModel(NULL); - logView->actions().first()->setEnabled(false); - progressBar->setValue(0); - editDetails->clear(); - updateButtons(EncodeThread::JobStatus_Undefined); - updateTaskbar(EncodeThread::JobStatus_Undefined, QIcon()); + ui->logView->setModel(NULL); + foreach(QAction *action, ui->logView->actions()) + { + action->setEnabled(false); + } + ui->progressBar->setValue(0); + ui->editDetails->clear(); + updateButtons(JobStatus_Undefined); + updateTaskbar(JobStatus_Undefined, QIcon()); } - progressBar->repaint(); + ui->progressBar->repaint(); } /* @@ -340,24 +625,23 @@ void MainWindow::jobSelected(const QModelIndex & current, const QModelIndex & pr */ void MainWindow::jobChangedData(const QModelIndex &topLeft, const QModelIndex &bottomRight) { - int selected = jobsView->currentIndex().row(); + int selected = ui->jobsView->currentIndex().row(); if(topLeft.column() <= 1 && bottomRight.column() >= 1) /*STATUS*/ { for(int i = topLeft.row(); i <= bottomRight.row(); i++) { - EncodeThread::JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex())); + JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex())); if(i == selected) { qDebug("Current job changed status!"); updateButtons(status); updateTaskbar(status, m_jobList->data(m_jobList->index(i, 0, QModelIndex()), Qt::DecorationRole).value()); } - if((status == EncodeThread::JobStatus_Completed) || (status == EncodeThread::JobStatus_Failed)) + if((status == JobStatus_Completed) || (status == JobStatus_Failed)) { - if(m_preferences.autoRunNextJob) QTimer::singleShot(0, this, SLOT(launchNextJob())); - if(m_preferences.shutdownComputer) QTimer::singleShot(0, this, SLOT(shutdownComputer())); - if(m_preferences.saveLogFiles) saveLogFile(m_jobList->index(i, 1, QModelIndex())); + if(m_preferences->getAutoRunNextJob()) QTimer::singleShot(0, this, SLOT(launchNextJob())); + if(m_preferences->getSaveLogFiles()) saveLogFile(m_jobList->index(i, 1, QModelIndex())); } } } @@ -367,8 +651,11 @@ void MainWindow::jobChangedData(const QModelIndex &topLeft, const QModelIndex & { if(i == selected) { - progressBar->setValue(m_jobList->getJobProgress(m_jobList->index(i, 0, QModelIndex()))); - WinSevenTaskbar::setTaskbarProgress(this, progressBar->value(), progressBar->maximum()); + ui->progressBar->setValue(m_jobList->getJobProgress(m_jobList->index(i, 0, QModelIndex()))); + if(!m_taskbar.isNull()) + { + m_taskbar->setTaskbarProgress(ui->progressBar->value(), ui->progressBar->maximum()); + } break; } } @@ -379,7 +666,7 @@ void MainWindow::jobChangedData(const QModelIndex &topLeft, const QModelIndex & { if(i == selected) { - editDetails->setText(m_jobList->data(m_jobList->index(i, 3, QModelIndex()), Qt::DisplayRole).toString()); + ui->editDetails->setText(m_jobList->data(m_jobList->index(i, 3, QModelIndex()), Qt::DisplayRole).toString()); break; } } @@ -391,7 +678,7 @@ void MainWindow::jobChangedData(const QModelIndex &topLeft, const QModelIndex & */ void MainWindow::jobLogExtended(const QModelIndex & parent, int start, int end) { - QTimer::singleShot(0, logView, SLOT(scrollToBottom())); + QTimer::singleShot(0, ui->logView, SLOT(scrollToBottom())); } /* @@ -399,72 +686,12 @@ void MainWindow::jobLogExtended(const QModelIndex & parent, int start, int end) */ void MainWindow::showAbout(void) { - QString text; - - text += QString().sprintf("Simple x264 Launcher v%u.%02u.%u - use 64-Bit x264 with 32-Bit Avisynth
", x264_version_major(), x264_version_minor(), x264_version_build()); - text += QString().sprintf("Copyright (c) 2004-%04d LoRd_MuldeR <mulder2@gmx.de>. Some rights reserved.
", qMax(x264_version_date().year(),QDate::currentDate().year())); - text += QString().sprintf("Built on %s at %s with %s for Win-%s.

", x264_version_date().toString(Qt::ISODate).toLatin1().constData(), x264_version_time(), x264_version_compiler(), x264_version_arch()); - text += QString().sprintf("This program is free software: you can redistribute it and/or modify
"); - text += QString().sprintf("it under the terms of the GNU General Public License <http://www.gnu.org/>.
"); - text += QString().sprintf("Note that this program is distributed with ABSOLUTELY NO WARRANTY.

"); - text += QString().sprintf("Please check the web-site at %s for updates !!!
", home_url, home_url); - - QMessageBox aboutBox(this); - aboutBox.setIconPixmap(QIcon(":/images/movie.png").pixmap(64,64)); - aboutBox.setWindowTitle(tr("About...")); - aboutBox.setText(text.replace("-", "−")); - aboutBox.addButton(tr("About x264"), QMessageBox::NoRole); - aboutBox.addButton(tr("About AVS"), QMessageBox::NoRole); - aboutBox.addButton(tr("About Qt"), QMessageBox::NoRole); - aboutBox.setEscapeButton(aboutBox.addButton(tr("Close"), QMessageBox::NoRole)); - - forever + ENSURE_APP_IS_READY(); + + if(AboutDialog *aboutDialog = new AboutDialog(this)) { - MessageBeep(MB_ICONINFORMATION); - switch(aboutBox.exec()) - { - case 0: - { - QString text2; - text2 += tr("x264 - the best H.264/AVC encoder. Copyright (c) 2003-2012 x264 project.
"); - text2 += tr("Free software library for encoding video streams into the H.264/MPEG-4 AVC format.
"); - text2 += tr("Released under the terms of the GNU General Public License.

"); - text2 += tr("Please visit %1 for obtaining a commercial x264 license.
").arg("http://x264licensing.com/"); - text2 += tr("Read the user's manual to get started and use the support forum for help!
").arg("http://mewiki.project357.com/wiki/X264_Settings", "http://forum.doom9.org/forumdisplay.php?f=77"); - - QMessageBox x264Box(this); - x264Box.setIconPixmap(QIcon(":/images/x264.png").pixmap(48,48)); - x264Box.setWindowTitle(tr("About x264")); - x264Box.setText(text2.replace("-", "−")); - x264Box.setEscapeButton(x264Box.addButton(tr("Close"), QMessageBox::NoRole)); - MessageBeep(MB_ICONINFORMATION); - x264Box.exec(); - } - break; - case 1: - { - QString text2; - text2 += tr("Avisynth - powerful video processing scripting language.
"); - text2 += tr("Copyright (c) 2000 Ben Rudiak-Gould and all subsequent developers.
"); - text2 += tr("Released under the terms of the GNU General Public License.

"); - text2 += tr("Please visit the web-site %1 for more information.
").arg("http://avisynth.org/"); - text2 += tr("Read the guide to get started and use the support forum for help!
").arg("http://avisynth.org/mediawiki/First_script", "http://forum.doom9.org/forumdisplay.php?f=33"); - - QMessageBox x264Box(this); - x264Box.setIconPixmap(QIcon(":/images/avisynth.png").pixmap(48,67)); - x264Box.setWindowTitle(tr("About Avisynth")); - x264Box.setText(text2.replace("-", "−")); - x264Box.setEscapeButton(x264Box.addButton(tr("Close"), QMessageBox::NoRole)); - MessageBeep(MB_ICONINFORMATION); - x264Box.exec(); - } - break; - case 2: - QMessageBox::aboutQt(this); - break; - default: - return; - } + aboutDialog->exec(); + MUTILS_DELETE(aboutDialog); } } @@ -473,18 +700,18 @@ void MainWindow::showAbout(void) */ void MainWindow::showWebLink(void) { - if(QObject::sender() == actionWebMulder) QDesktopServices::openUrl(QUrl(home_url)); - if(QObject::sender() == actionWebX264) QDesktopServices::openUrl(QUrl("http://www.x264.com/")); - if(QObject::sender() == actionWebKomisar) QDesktopServices::openUrl(QUrl("http://komisar.gin.by/")); - if(QObject::sender() == actionWebJarod) QDesktopServices::openUrl(QUrl("http://www.x264.nl/")); - if(QObject::sender() == actionWebJEEB) QDesktopServices::openUrl(QUrl("http://x264.fushizen.eu/")); - if(QObject::sender() == actionWebAvisynth32) QDesktopServices::openUrl(QUrl("http://sourceforge.net/projects/avisynth2/files/AviSynth%202.5/")); - if(QObject::sender() == actionWebAvisynth64) QDesktopServices::openUrl(QUrl("http://code.google.com/p/avisynth64/downloads/list")); - if(QObject::sender() == actionWebWiki) QDesktopServices::openUrl(QUrl("http://mewiki.project357.com/wiki/X264_Settings")); - if(QObject::sender() == actionWebBluRay) QDesktopServices::openUrl(QUrl("http://www.x264bluray.com/")); - if(QObject::sender() == actionWebAvsWiki) QDesktopServices::openUrl(QUrl("http://avisynth.org/mediawiki/Main_Page#Usage")); - if(QObject::sender() == actionWebSupport) QDesktopServices::openUrl(QUrl("http://forum.doom9.org/showthread.php?t=144140")); - if(QObject::sender() == actionWebSecret) QDesktopServices::openUrl(QUrl("http://www.youtube.com/watch_popup?v=AXIeHY-OYNI")); + ENSURE_APP_IS_READY(); + + if(QObject *obj = QObject::sender()) + { + if(QAction *action = dynamic_cast(obj)) + { + if(action->data().type() == QVariant::Url) + { + QDesktopServices::openUrl(action->data().toUrl()); + } + } + } } /* @@ -492,9 +719,12 @@ void MainWindow::showWebLink(void) */ void MainWindow::showPreferences(void) { - PreferencesDialog *preferences = new PreferencesDialog(this, &m_preferences, m_cpuFeatures->x64); + ENSURE_APP_IS_READY(); + + PreferencesDialog *preferences = new PreferencesDialog(this, m_preferences.data(), m_sysinfo.data()); preferences->exec(); - X264_DELETE(preferences); + + MUTILS_DELETE(preferences); } /* @@ -502,33 +732,36 @@ void MainWindow::showPreferences(void) */ void MainWindow::launchNextJob(void) { - qDebug("launchNextJob(void)"); - - const int rows = m_jobList->rowCount(QModelIndex()); + qDebug("Launching next job..."); - if(countRunningJobs() >= m_preferences.maxRunningJobCount) + if(countRunningJobs() >= m_preferences->getMaxRunningJobCount()) { qDebug("Still have too many jobs running, won't launch next one yet!"); return; } - int startIdx= jobsView->currentIndex().isValid() ? qBound(0, jobsView->currentIndex().row(), rows-1) : 0; + const int rows = m_jobList->rowCount(QModelIndex()); for(int i = 0; i < rows; i++) { - int currentIdx = (i + startIdx) % rows; - EncodeThread::JobStatus status = m_jobList->getJobStatus(m_jobList->index(currentIdx, 0, QModelIndex())); - if(status == EncodeThread::JobStatus_Enqueued) + const QModelIndex currentIndex = m_jobList->index(i, 0, QModelIndex()); + if(m_jobList->getJobStatus(currentIndex) == JobStatus_Enqueued) { - if(m_jobList->startJob(m_jobList->index(currentIdx, 0, QModelIndex()))) + if(m_jobList->startJob(currentIndex)) { - jobsView->selectRow(currentIdx); + ui->jobsView->selectRow(currentIndex.row()); return; } } } - qWarning("No enqueued jobs left!"); + qWarning("No enqueued jobs left to be started!"); + + if(m_postOperation) + { + qDebug("Post operation has been scheduled! (m_postOperation: %d)", m_postOperation); + QTimer::singleShot(0, this, SLOT(shutdownComputer())); + } } /* @@ -538,27 +771,31 @@ void MainWindow::saveLogFile(const QModelIndex &index) { if(index.isValid()) { - if(LogFileModel *log = m_jobList->getLogFile(index)) + const LogFileModel *const logData = m_jobList->getLogFile(index); + const QString &outputFilePath = m_jobList->getJobOutputFile(index); + if(logData && (!outputFilePath.isEmpty())) { - QDir(QString("%1/logs").arg(x264_data_path())).mkpath("."); - QString logFilePath = QString("%1/logs/LOG.%2.%3.txt").arg(x264_data_path(), QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString(Qt::ISODate).replace(':', "-")); - QFile outFile(logFilePath); - if(outFile.open(QIODevice::WriteOnly)) + const QFileInfo outputFileInfo(outputFilePath); + if (outputFileInfo.absoluteDir().exists()) { - QTextStream outStream(&outFile); - outStream.setCodec("UTF-8"); - outStream.setGenerateByteOrderMark(true); - - const int rows = log->rowCount(QModelIndex()); - for(int i = 0; i < rows; i++) + const QString outputDir = outputFileInfo.absolutePath(), outputName = outputFileInfo.fileName(); + const QString logFilePath = MUtils::make_unique_file(outputDir, outputName, QLatin1String("log"), true); + if (!logFilePath.isEmpty()) { - outStream << log->data(log->index(i, 0, QModelIndex()), Qt::DisplayRole).toString() << QLatin1String("\r\n"); + qDebug("Saving log file to: \"%s\"", MUTILS_UTF8(logFilePath)); + if (!logData->saveToLocalFile(logFilePath)) + { + qWarning("Failed to open log file for writing:\n%s", logFilePath.toUtf8().constData()); + } + } + else + { + qWarning("Failed to generate log file name. Giving up!"); } - outFile.close(); } else { - qWarning("Failed to open log file for writing:\n%s", logFilePath.toUtf8().constData()); + qWarning("Output directory does not seem to exist. Giving up!"); } } } @@ -569,17 +806,24 @@ void MainWindow::saveLogFile(const QModelIndex &index) */ void MainWindow::shutdownComputer(void) { - qDebug("shutdownComputer(void)"); - + ENSURE_APP_IS_READY(); + qDebug("shutdownComputer (m_postOperation: %d)", m_postOperation); + if(countPendingJobs() > 0) { - qDebug("Still have pending jobs, won't shutdown yet!"); + qWarning("Still have pending jobs, won't shutdown yet!"); return; } - + + if ((m_postOperation != POST_OP_POWERDOWN) && (m_postOperation != POST_OP_HIBERNATE)) + { + qWarning("No post-operation has been schedule!"); + } + const int iTimeout = 30; const Qt::WindowFlags flags = Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowSystemMenuHint; - const QString text = QString("%1%2%1").arg(QString().fill(' ', 18), tr("Warning: Computer will shutdown in %1 seconds...")); + const bool hibernate = (m_postOperation == POST_OP_HIBERNATE); + const QString text = QString("%1%2%1").arg(QString().fill(' ', 18), hibernate ? tr("Warning: Computer will hibernate in %1 seconds...") : tr("Warning: Computer will shutdown in %1 seconds...")); qWarning("Initiating shutdown sequence!"); @@ -596,7 +840,7 @@ void MainWindow::shutdownComputer(void) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QApplication::setOverrideCursor(Qt::WaitCursor); - PlaySound(MAKEINTRESOURCE(IDR_WAVE1), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC); + MUtils::Sound::play_sound("shutdown", false); QApplication::restoreOverrideCursor(); QTimer timer; @@ -619,15 +863,19 @@ void MainWindow::shutdownComputer(void) progressDialog.setLabelText(text.arg(iTimeout-i)); if(iTimeout-i == 3) progressDialog.setCancelButton(NULL); QApplication::processEvents(); - PlaySound(MAKEINTRESOURCE((i < iTimeout) ? IDR_WAVE2 : IDR_WAVE3), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC); + MUtils::Sound::play_sound(((i < iTimeout) ? "beep" : "beep2"), false); } qWarning("Shutting down !!!"); - if(x264_shutdown_computer("Simple x264 Launcher: All jobs completed, shutting down!", 10, true)) + if(MUtils::OS::shutdown_computer("Simple x264 Launcher: All jobs completed, shutting down!", 10, true, hibernate)) { - qApp->closeAllWindows(); + if (!hibernate) + { + qApp->closeAllWindows(); + } } + } /* @@ -635,61 +883,34 @@ void MainWindow::shutdownComputer(void) */ void MainWindow::init(void) { - 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"; - QStringList binaries = QString::fromLatin1(binFiles).split(":", QString::SkipEmptyParts); - - updateLabelPos(); - - //Check for a running instance - bool firstInstance = false; - if(m_ipcThread->initialize(&firstInstance)) + if(m_initialized) { - m_ipcThread->start(); - if(!firstInstance) - { - if(!m_ipcThread->wait(5000)) - { - QMessageBox::warning(this, tr("Not Responding"), tr("Another instance of this application is already running, but did not respond in time.
If the problem persists, please kill the running instance from the task manager!
"), tr("Quit")); - m_ipcThread->terminate(); - m_ipcThread->wait(); - } - close(); qApp->exit(-1); return; - } + qWarning("Already initialized -> skipping!"); + return; } - //Check all binaries - while(!binaries.isEmpty()) + updateLabelPos(); + const MUtils::OS::ArgumentMap &arguments = MUtils::OS::arguments(); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + + //--------------------------------------- + // Check required binaries + //--------------------------------------- + + qDebug("[Validating binaries]"); + QString failedPath; + if(!BinariesCheckThread::check(m_sysinfo.data(), &failedPath)) { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - QString current = binaries.takeFirst(); - QFile *file = new QFile(QString("%1/toolset/%2").arg(m_appDir, current)); - if(file->open(QIODevice::ReadOnly)) - { - bool binaryTypeOkay = false; - DWORD binaryType; - if(GetBinaryType(QWCHAR(QDir::toNativeSeparators(file->fileName())), &binaryType)) - { - binaryTypeOkay = (binaryType == SCS_32BIT_BINARY || binaryType == SCS_64BIT_BINARY); - } - if(!binaryTypeOkay) - { - QMessageBox::critical(this, tr("Invalid File!"), tr("At least on required tool is not a valid Win32 or Win64 binary:
%1

Please re-install the program in order to fix the problem!
").arg(QDir::toNativeSeparators(QString("%1/toolset/%2").arg(m_appDir, current))).replace("-", "−")); - qFatal(QString("Binary is invalid: %1/toolset/%2").arg(m_appDir, current).toLatin1().constData()); - close(); qApp->exit(-1); return; - } - m_toolsList << file; - } - else - { - X264_DELETE(file); - QMessageBox::critical(this, tr("File Not Found!"), tr("At least on required tool could not be found:
%1

Please re-install the program in order to fix the problem!
").arg(QDir::toNativeSeparators(QString("%1/toolset/%2").arg(m_appDir, current))).replace("-", "−")); - qFatal(QString("Binary not found: %1/toolset/%2").arg(m_appDir, current).toLatin1().constData()); - close(); qApp->exit(-1); return; - } + QMessageBox::critical(this, tr("Invalid File!"), tr("At least one tool is missing or is not a valid Win32/Win64 binary:
%1

Please re-install the program in order to fix the problem!").replace("-", "−").arg(Qt::escape(QDir::toNativeSeparators(failedPath)))); + qFatal("At least one tool is missing or is not a valid Win32/Win64 binary. Program will exit now!"); } + qDebug(" "); + + //--------------------------------------- + // Check for portable mode + //--------------------------------------- - //Check for portable mode - if(x264_portable()) + if(x264_is_portable()) { bool ok = false; static const char *data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; @@ -702,7 +923,7 @@ void MainWindow::init(void) if(!ok) { int val = QMessageBox::warning(this, tr("Write Test Failed"), tr("The application was launched in portable mode, but the program path is not writable!"), tr("Quit"), tr("Ignore")); - if(val != 1) { close(); qApp->exit(-1); return; } + if(val != 1) INIT_ERROR_EXIT(); } } @@ -711,92 +932,196 @@ void MainWindow::init(void) { qsrand(time(NULL)); int rnd = qrand() % 3; int val = QMessageBox::information(this, tr("Pre-Release Version"), tr("Note: This is a pre-release version. Please do NOT use for production!
Click the button #%1 in order to continue...

(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); - if(rnd != val) { close(); qApp->exit(-1); return; } + if(rnd != val) INIT_ERROR_EXIT(); } + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + + //--------------------------------------- + // Check CPU capabilities + //--------------------------------------- + //Make sure this CPU can run x264 (requires MMX + MMXEXT/iSSE to run x264 with ASM enabled, additionally requires SSE1 for most x264 builds) - if(!(m_cpuFeatures->mmx && m_cpuFeatures->mmx2)) + if(!m_sysinfo->getCPUFeatures(SysinfoModel::CPUFeatures_MMX)) { QMessageBox::critical(this, tr("Unsupported CPU"), tr("Sorry, but this machine is not physically capable of running x264 (with assembly).
Please get a CPU that supports at least the MMX and MMXEXT instruction sets!
"), tr("Quit")); qFatal("System does not support MMX and MMXEXT, x264 will not work !!!"); - close(); qApp->exit(-1); return; + INIT_ERROR_EXIT(); } - else if(!(m_cpuFeatures->mmx && m_cpuFeatures->sse)) + else if(!m_sysinfo->getCPUFeatures(SysinfoModel::CPUFeatures_SSE)) { - qWarning("WARNING: System does not support SSE1, most x264 builds will not work !!!\n"); - int val = QMessageBox::warning(this, tr("Unsupported CPU"), tr("It appears that this machine does not support the SSE1 instruction set.
Thus most builds of x264 will not run on this computer at all.

Please get a CPU that supports the MMX and SSE1 instruction sets!
"), tr("Quit"), tr("Ignore")); - if(val != 1) { close(); qApp->exit(-1); return; } + qWarning("WARNING: System does not support SSE (v1), x264/x265 probably will *not* work !!!\n"); + int val = QMessageBox::warning(this, tr("Unsupported CPU"), tr("It appears that this machine does not support the SSE1 instruction set.
Thus most builds of x264/x265 will not run on this computer at all.

Please get a CPU that supports the MMX and SSE1 instruction sets!
"), tr("Quit"), tr("Ignore")); + if(val != 1) INIT_ERROR_EXIT(); } - //Check for Avisynth support - if(!qApp->arguments().contains("--skip-avisynth-check", Qt::CaseInsensitive)) + //Skip version check (not recommended!) + if(arguments.contains(CLI_PARAM_SKIP_VERSION_CHECK)) + { + qWarning("Version checks are disabled now, you have been warned!\n"); + m_preferences->setSkipVersionTest(true); + } + + //Don't abort encoding process on timeout (not recommended!) + if(arguments.contains(CLI_PARAM_NO_DEADLOCK)) + { + qWarning("Deadlock detection disabled, you have been warned!\n"); + m_preferences->setAbortOnTimeout(false); + } + + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + + //--------------------------------------- + // Check Avisynth support + //--------------------------------------- + + if(!arguments.contains(CLI_PARAM_SKIP_AVS_CHECK)) { qDebug("[Check for Avisynth support]"); - volatile double avisynthVersion = 0.0; - const int result = AvisynthCheckThread::detect(&avisynthVersion); - if(result < 0) + if(!AvisynthCheckThread::detect(m_sysinfo.data())) { QString text = tr("A critical error was encountered while checking your Avisynth version.").append("
"); text += tr("This is most likely caused by an erroneous Avisynth Plugin, please try to clean your Plugins folder!").append("
"); text += tr("We suggest to move all .dll and .avsi files out of your Avisynth Plugins folder and try again."); int val = QMessageBox::critical(this, tr("Avisynth Error"), QString("%1").arg(text).replace("-", "−"), tr("Quit"), tr("Ignore")); - if(val != 1) { close(); qApp->exit(-1); return; } + if(val != 1) INIT_ERROR_EXIT(); } - if((!result) || (avisynthVersion < 2.5)) + else if((!m_sysinfo->hasAvisynth()) && (!m_preferences->getDisableWarnings())) { - int val = QMessageBox::warning(this, tr("Avisynth Missing"), tr("It appears that Avisynth is not currently installed on your computer.
Therefore Avisynth (.avs) input will not be working at all!

Please download and install Avisynth:
http://sourceforge.net/projects/avisynth2/files/AviSynth 2.5/
").replace("-", "−"), tr("Quit"), tr("Ignore")); - if(val != 1) { close(); qApp->exit(-1); return; } + QString text = tr("It appears that Avisynth is not currently installed on your computer.
Therefore Avisynth (.avs) input will not be working at all!").append("

"); + text += tr("Please download and install Avisynth:").append("
").append(LINK(avs_dl_url)); + int val = QMessageBox::warning(this, tr("Avisynth Missing"), QString("%1").arg(text).replace("-", "−"), tr("Close"), tr("Disable this Warning")); + if(val == 1) + { + m_preferences->setDisableWarnings(true); + PreferencesModel::savePreferences(m_preferences.data()); + } } - qDebug(""); + qDebug(" "); } - //Check for expiration - if(x264_version_date().addMonths(6) < QDate::currentDate()) + //--------------------------------------- + // Check VapurSynth support + //--------------------------------------- + + if(!arguments.contains(CLI_PARAM_SKIP_VPS_CHECK)) { + qDebug("[Check for VapourSynth support]"); + if(!VapourSynthCheckThread::detect(m_sysinfo.data())) + { + QString text = tr("A critical error was encountered while checking your VapourSynth installation.").append("
"); + text += tr("This is most likely caused by an erroneous VapourSynth Plugin, please try to clean your Filters folder!").append("
"); + text += tr("We suggest to move all .dll files out of your VapourSynth Filters folder and try again."); + const int val = QMessageBox::critical(this, tr("VapourSynth Error"), QString("%1").arg(text).replace("-", "−"), tr("Quit"), tr("Ignore")); + if(val != 1) INIT_ERROR_EXIT(); + } + else if((!m_sysinfo->hasVapourSynth()) && (!m_preferences->getDisableWarnings())) + { + QString text = tr("It appears that VapourSynth is not currently installed on your computer.
Therefore VapourSynth (.vpy) input will not be working at all!").append("

"); + text += tr("Please download and install VapourSynth (r%1 or later) for Windows:").arg(QString::number(vsynth_rev)).append("
").append(LINK(vsynth_url)).append("

"); + text += tr("Note that Python v3.4 is a prerequisite for installing VapourSynth:").append("
").append(LINK(python_url)).append("
"); + const int val = QMessageBox::warning(this, tr("VapourSynth Missing"), QString("%1").arg(text).replace("-", "−"), tr("Close"), tr("Disable this Warning")); + if(val == 1) + { + m_preferences->setDisableWarnings(true); + PreferencesModel::savePreferences(m_preferences.data()); + } + } + qDebug(" "); + } + + //--------------------------------------- + // Create the IPC listener thread + //--------------------------------------- + + if(m_ipcChannel) + { + m_ipcThread.reset(new IPCThread_Recv(m_ipcChannel)); + connect(m_ipcThread.data(), SIGNAL(receivedCommand(int,QStringList,quint32)), this, SLOT(handleCommand(int,QStringList,quint32)), Qt::QueuedConnection); + m_ipcThread->start(); + } + + //--------------------------------------- + // Finish initialization + //--------------------------------------- + + //Set Window title + setWindowTitle(QString("%1 (%2)").arg(windowTitle(), m_sysinfo->getCPUFeatures(SysinfoModel::CPUFeatures_X64) ? "64-Bit" : "32-Bit")); + + //Enable drag&drop support for this window, required for Qt v4.8.4+ + setAcceptDrops(true); + + //Update flag + m_initialized = true; + + //Hide the spinner animation + if(!m_label[1].isNull()) + { + if(!m_animation.isNull()) + { + m_animation->stop(); + } + m_label[1]->setVisible(false); + } + + //--------------------------------------- + // Check for Expiration + //--------------------------------------- + + if(MUtils::Version::app_build_date().addMonths(6) < MUtils::OS::current_date()) + { + if(QWidget *cornerWidget = ui->menubar->cornerWidget()) cornerWidget->show(); + QString text; + text += QString("%1

").arg(tr("Your version of Simple x264 Launcher is more than 6 months old!").replace('-', "−")); + text += QString("%1
%3

").arg(tr("You can download the most recent version from the official web-site now:").replace('-', "−"), QString::fromLatin1(update_url), QString::fromLatin1(update_url).replace("-", "−")); + text += QString("
%1
").arg(tr("Alternatively, click 'Check for Updates' to run the auto-update utility.").replace('-', "−")); QMessageBox msgBox(this); msgBox.setIconPixmap(QIcon(":/images/update.png").pixmap(56,56)); msgBox.setWindowTitle(tr("Update Notification")); msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); - msgBox.setText(tr("Your version of 'Simple x264 Launcher' is more than 6 months old!

Please download the most recent version from the official web-site at:
%1
").replace("-", "−").arg(update_url)); - QPushButton *btn1 = msgBox.addButton(tr("Discard"), QMessageBox::NoRole); - QPushButton *btn2 = msgBox.addButton(tr("Discard"), QMessageBox::AcceptRole); - btn1->setEnabled(false); - btn2->setVisible(false); - QTimer::singleShot(5000, btn1, SLOT(hide())); - QTimer::singleShot(5000, btn2, SLOT(show())); - msgBox.exec(); - } - - //Add files from command-line - bool bAddFile = false; - QStringList files, args = qApp->arguments(); - while(!args.isEmpty()) - { - QString current = args.takeFirst(); - if(!bAddFile) + msgBox.setText(text); + QPushButton *btn1 = msgBox.addButton(tr("Check for Updates"), QMessageBox::AcceptRole); + QPushButton *btn2 = msgBox.addButton(tr("Discard"), QMessageBox::NoRole); + QPushButton *btn3 = msgBox.addButton(btn2->text(), QMessageBox::RejectRole); + btn2->setEnabled(false); + btn3->setVisible(false); + QTimer::singleShot(7500, btn2, SLOT(hide())); + QTimer::singleShot(7500, btn3, SLOT(show())); + if(msgBox.exec() == 0) { - bAddFile = (current.compare("--add", Qt::CaseInsensitive) == 0); - continue; - } - if((!current.startsWith("--")) && QFileInfo(current).exists() && QFileInfo(current).isFile()) - { - files << QFileInfo(current).canonicalFilePath(); + QTimer::singleShot(0, this, SLOT(checkUpdates())); + return; } } - if(int totalFiles = files.count()) + else if(!parseCommandLineArgs()) { - bool ok = true; int n = 0; - while((!files.isEmpty()) && ok) + //Update reminder + if(arguments.contains(CLI_PARAM_FIRST_RUN)) { - QString currentFile = files.takeFirst(); - qDebug("Adding file: %s", currentFile.toUtf8().constData()); - /*TODO: Add multiple files!*/ - //ok = createJob(currentFile, QString(), NULL, n++, totalFiles); + qWarning("First run -> resetting update check now!"); + m_recentlyUsed->setLastUpdateCheck(0); + RecentlyUsed::saveRecentlyUsed(m_recentlyUsed.data()); + } + else if(m_recentlyUsed->lastUpdateCheck() + 14 < MUtils::OS::current_date().toJulianDay()) + { + if(QWidget *cornerWidget = ui->menubar->cornerWidget()) cornerWidget->show(); + if(!m_preferences->getNoUpdateReminder()) + { + if(QMessageBox::warning(this, tr("Update Notification"), QString("%1").arg(tr("Your last update check was more than 14 days ago. Check for updates now?")), tr("Check for Updates"), tr("Discard")) == 0) + { + QTimer::singleShot(0, this, SLOT(checkUpdates())); + return; + } + } } } - //Enable drag&drop support for this window, required for Qt v4.8.4+ - setAcceptDrops(true); + //Load queued jobs + if(m_jobList->loadQueuedJobs(m_sysinfo.data()) > 0) + { + m_label[0]->setVisible(m_jobList->rowCount(QModelIndex()) == 0); + m_jobList->clearQueuedJobs(); + } } /* @@ -804,8 +1129,15 @@ void MainWindow::init(void) */ void MainWindow::updateLabelPos(void) { - const QWidget *const viewPort = jobsView->viewport(); - m_label->setGeometry(0, 0, viewPort->width(), viewPort->height()); + for(int i = 0; i < 2; i++) + { + //const QWidget *const viewPort = ui->jobsView->viewport(); + const QWidget *const viewPort = dynamic_cast(m_label[i]->parent()); + if(viewPort) + { + m_label[i]->setGeometry(0, 0, viewPort->width(), viewPort->height()); + } + } } /* @@ -813,56 +1145,212 @@ void MainWindow::updateLabelPos(void) */ void MainWindow::copyLogToClipboard(bool checked) { - qDebug("copyLogToClipboard"); + qDebug("Coyping logfile to clipboard..."); - if(LogFileModel *log = dynamic_cast(logView->model())) + if(LogFileModel *log = dynamic_cast(ui->logView->model())) { log->copyToClipboard(); - MessageBeep(MB_ICONINFORMATION); + MUtils::Sound::beep(MUtils::Sound::BEEP_NFO); } } /* - * Process the dropped files + * Save log to local file */ -void MainWindow::handleDroppedFiles(void) +void MainWindow::saveLogToLocalFile(bool checked) { - qDebug("MainWindow::handleDroppedFiles"); - if(m_droppedFiles) - { - QStringList droppedFiles(*m_droppedFiles); - m_droppedFiles->clear(); - /* - int totalFiles = droppedFiles.count(); - bool ok = true; int n = 0; - while((!droppedFiles.isEmpty()) && ok) + ENSURE_APP_IS_READY(); + + const QModelIndex index = ui->jobsView->currentIndex(); + const QString initialName = index.isValid() ? QFileInfo(m_jobList->getJobOutputFile(index)).completeBaseName() : tr("Logfile"); + const QString fileName = QFileDialog::getSaveFileName(this, tr("Save Log File"), initialName, tr("Log File (*.log)")); + if(!fileName.isEmpty()) + { + if(LogFileModel *log = dynamic_cast(ui->logView->model())) { - QString currentFile = droppedFiles.takeFirst(); - qDebug("Adding file: %s", currentFile.toUtf8().constData()); - ok = createJob(currentFile, QString(), NULL, n++, totalFiles); + if(!log->saveToLocalFile(fileName)) + { + QMessageBox::warning(this, this->windowTitle(), tr("Error: Log file could not be saved!")); + } } - */ - //createJobMultiple(droppedFiles); } - qDebug("Leave from MainWindow::handleDroppedFiles!"); } -void MainWindow::instanceCreated(DWORD pid) +/* + * Toggle line-wrapping + */ +void MainWindow::toggleLineWrapping(bool checked) { - qDebug("Notification from other instance (PID=0x%X) received!", pid); + ui->logView->setWordWrap(checked); +} + +/* + * Process the dropped files + */ +void MainWindow::handlePendingFiles(void) +{ + qDebug("MainWindow::handlePendingFiles"); + + if(!m_pendingFiles->isEmpty()) + { + QStringList pendingFiles(*m_pendingFiles); + m_pendingFiles->clear(); + createJobMultiple(pendingFiles); + } + + qDebug("Leave from MainWindow::handlePendingFiles!"); +} + +/* + * Handle incoming IPC command + */ +void MainWindow::handleCommand(const int &command, const QStringList &args, const quint32 &flags) +{ + if(!(m_initialized && (QApplication::activeModalWidget() == NULL))) + { + qWarning("Cannot accapt commands at this time -> discarding!"); + return; + } + + if((!isVisible()) || m_sysTray->isVisible()) + { + sysTrayActived(); + } + + MUtils::GUI::bring_to_front(this); - FLASHWINFO flashWinInfo; - memset(&flashWinInfo, 0, sizeof(FLASHWINFO)); - flashWinInfo.cbSize = sizeof(FLASHWINFO); - flashWinInfo.hwnd = this->winId(); - flashWinInfo.dwFlags = FLASHW_ALL; - flashWinInfo.dwTimeout = 125; - flashWinInfo.uCount = 5; - - SwitchToThisWindow(this->winId(), TRUE); - SetForegroundWindow(this->winId()); - qApp->processEvents(); - FlashWindowEx(&flashWinInfo); +#ifdef IPC_LOGGING + qDebug("\n---------- IPC ----------"); + qDebug("CommandId: %d", command); + for(QStringList::ConstIterator iter = args.constBegin(); iter != args.constEnd(); iter++) + { + qDebug("Arguments: %s", iter->toUtf8().constData()); + } + qDebug("The Flags: 0x%08X", flags); + qDebug("---------- IPC ----------\n"); +#endif //IPC_LOGGING + + switch(command) + { + case IPC_OPCODE_PING: + qDebug("Received a PING request from another instance!"); + MUtils::GUI::blink_window(this, 5, 125); + break; + case IPC_OPCODE_ADD_FILE: + if(!args.isEmpty()) + { + if(QFileInfo(args[0]).exists() && QFileInfo(args[0]).isFile()) + { + *m_pendingFiles << QFileInfo(args[0]).canonicalFilePath(); + if(!m_fileTimer->isActive()) + { + m_fileTimer->setSingleShot(true); + m_fileTimer->start(5000); + } + } + else + { + qWarning("File '%s' not found!", args[0].toUtf8().constData()); + } + } + break; + case IPC_OPCODE_ADD_JOB: + if(args.size() >= 3) + { + if(QFileInfo(args[0]).exists() && QFileInfo(args[0]).isFile()) + { + OptionsModel options(m_sysinfo.data()); + bool runImmediately = (countRunningJobs() < (m_preferences->getAutoRunNextJob() ? m_preferences->getMaxRunningJobCount() : 1)); + if(!(args[2].isEmpty() || X264_STRCMP(args[2], "-"))) + { + if(!OptionsModel::loadTemplate(&options, args[2].trimmed())) + { + qWarning("Template '%s' could not be found -> using defaults!", args[2].trimmed().toUtf8().constData()); + } + } + if((flags & IPC_FLAG_FORCE_START) && (!(flags & IPC_FLAG_FORCE_ENQUEUE))) runImmediately = true; + if((flags & IPC_FLAG_FORCE_ENQUEUE) && (!(flags & IPC_FLAG_FORCE_START))) runImmediately = false; + appendJob(args[0], args[1], &options, runImmediately); + } + else + { + qWarning("Source file '%s' not found!", args[0].toUtf8().constData()); + } + } + break; + default: + MUTILS_THROW("Unknown command received!"); + } +} + +/* + * Check for new updates + */ +void MainWindow::checkUpdates(void) +{ + ENSURE_APP_IS_READY(); + + if(countRunningJobs() > 0) + { + QMessageBox::warning(this, tr("Jobs Are Running"), tr("Sorry, can not update while there still are running jobs!")); + return; + } + + UpdaterDialog *updater = new UpdaterDialog(this, m_sysinfo.data(), update_url); + const int ret = updater->exec(); + + if(updater->getSuccess()) + { + m_recentlyUsed->setLastUpdateCheck(MUtils::OS::current_date().toJulianDay()); + RecentlyUsed::saveRecentlyUsed(m_recentlyUsed.data()); + if(QWidget *cornerWidget = ui->menubar->cornerWidget()) cornerWidget->hide(); + } + + if(ret == UpdaterDialog::READY_TO_INSTALL_UPDATE) + { + qWarning("Exitting program to install update..."); + close(); + QApplication::quit(); + } + + MUTILS_DELETE(updater); +} + +/* + * Handle mouse event for version label + */ +void MainWindow::versionLabelMouseClicked(const int &tag) +{ + if(tag == 0) + { + QTimer::singleShot(0, this, SLOT(showAbout())); + } +} + +/* + * Handle key event for job list + */ +void MainWindow::jobListKeyPressed(const int &tag) +{ + switch(tag) + { + case 1: + ui->actionJob_MoveUp->trigger(); + break; + case 2: + ui->actionJob_MoveDown->trigger(); + break; + } +} + +/* + * System tray was activated + */ +void MainWindow::sysTrayActived(void) +{ + m_sysTray->hide(); + showNormal(); + MUtils::GUI::bring_to_front(this); } /////////////////////////////////////////////////////////////////////////////// @@ -876,9 +1364,8 @@ void MainWindow::showEvent(QShowEvent *e) { QMainWindow::showEvent(e); - if(m_firstShow) + if(!m_initialized) { - m_firstShow = false; QTimer::singleShot(0, this, SLOT(init())); } } @@ -888,34 +1375,66 @@ void MainWindow::showEvent(QShowEvent *e) */ void MainWindow::closeEvent(QCloseEvent *e) { + if(!APP_IS_READY) + { + e->ignore(); + qWarning("Cannot close window at this time!"); + return; + } + + //Make sure we have no running jobs left! if(countRunningJobs() > 0) { e->ignore(); - QMessageBox::warning(this, tr("Jobs Are Running"), tr("Sorry, can not exit while there still are running jobs!")); + if(!m_preferences->getNoSystrayWarning()) + { + if(QMessageBox::warning(this, tr("Jobs Are Running"), tr("You still have running jobs, application will be minimized to notification area!"), tr("OK"), tr("Don't Show Again")) == 1) + { + m_preferences->setNoSystrayWarning(true); + PreferencesModel::savePreferences(m_preferences.data()); + } + } + hide(); + m_sysTray->show(); return; } - + + //Save pending jobs for next time, if desired by user if(countPendingJobs() > 0) { - 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); - if(ret != QMessageBox::Yes) + if (!m_preferences->getSaveQueueNoConfirm()) { - e->ignore(); - return; + const int ret = QMessageBox::question(this, tr("Jobs Are Pending"), tr("You still have some pending jobs in your queue. How do you want to proceed?"), tr("Save Jobs"), tr("Always Save Jobs"), tr("Discard Jobs")); + if ((ret >= 0) && (ret <= 1)) + { + if (ret > 0) + { + m_preferences->setSaveQueueNoConfirm(true); + PreferencesModel::savePreferences(m_preferences.data()); + } + m_jobList->saveQueuedJobs(); + } + } + else + { + m_jobList->saveQueuedJobs(); } } - + + //Delete remaining jobs while(m_jobList->rowCount(QModelIndex()) > 0) { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + if((m_jobList->rowCount(QModelIndex()) % 10) == 0) + { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + } if(!m_jobList->deleteJob(m_jobList->index(0, 0, QModelIndex()))) { e->ignore(); - QMessageBox::warning(this, tr("Failed To Exit"), tr("Sorry, at least one job could not be deleted!")); - return; + QMessageBox::warning(this, tr("Failed To Exit"), tr("Warning: At least one job could not be deleted!")); } } - + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); QMainWindow::closeEvent(e); } @@ -930,27 +1449,6 @@ void MainWindow::resizeEvent(QResizeEvent *e) } /* - * Event filter - */ -bool MainWindow::eventFilter(QObject *o, QEvent *e) -{ - if((o == labelBuildDate) && (e->type() == QEvent::MouseButtonPress)) - { - QTimer::singleShot(0, this, SLOT(showAbout())); - return true; - } - return false; -} - -/* - * Win32 message filter - */ -bool MainWindow::winEvent(MSG *message, long *result) -{ - return WinSevenTaskbar::handleWinEvent(message, result); -} - -/* * File dragged over window */ void MainWindow::dragEnterEvent(QDragEnterEvent *event) @@ -974,6 +1472,12 @@ void MainWindow::dragEnterEvent(QDragEnterEvent *event) */ void MainWindow::dropEvent(QDropEvent *event) { + if(!(m_initialized && (QApplication::activeModalWidget() == NULL))) + { + qWarning("Cannot accept dropped files at this time -> discarding!"); + return; + } + QStringList droppedFiles; QList urls = event->mimeData()->urls(); @@ -990,13 +1494,13 @@ void MainWindow::dropEvent(QDropEvent *event) if(droppedFiles.count() > 0) { - if(!m_droppedFiles) + m_pendingFiles->append(droppedFiles); + m_pendingFiles->sort(); + if(!m_fileTimer->isActive()) { - m_droppedFiles = new QStringList(); + m_fileTimer->setSingleShot(true); + m_fileTimer->start(5000); } - m_droppedFiles->append(droppedFiles); - m_droppedFiles->sort(); - QTimer::singleShot(0, this, SLOT(handleDroppedFiles())); } } @@ -1010,7 +1514,7 @@ void MainWindow::dropEvent(QDropEvent *event) bool MainWindow::createJob(QString &sourceFileName, QString &outputFileName, OptionsModel *options, bool &runImmediately, const bool restart, int fileNo, int fileTotal, bool *applyToAll) { bool okay = false; - AddJobDialog *addDialog = new AddJobDialog(this, options, m_cpuFeatures->x64, m_preferences.use10BitEncoding, m_preferences.saveToSourcePath); + AddJobDialog *addDialog = new AddJobDialog(this, options, m_recentlyUsed.data(), m_sysinfo.data(), m_preferences.data()); addDialog->setRunImmediately(runImmediately); if(!sourceFileName.isEmpty()) addDialog->setSourceFile(sourceFileName); @@ -1037,35 +1541,64 @@ bool MainWindow::createJob(QString &sourceFileName, QString &outputFileName, Opt okay = true; } - X264_DELETE(addDialog); + MUTILS_DELETE(addDialog); return okay; } /* + * Creates a new job from *multiple* files + */ +bool MainWindow::createJobMultiple(const QStringList &filePathIn) +{ + QStringList::ConstIterator iter; + bool applyToAll = false, runImmediately = false; + int counter = 0; + + //Add files individually + for(iter = filePathIn.constBegin(); (iter != filePathIn.constEnd()) && (!applyToAll); iter++) + { + runImmediately = (countRunningJobs() < (m_preferences->getAutoRunNextJob() ? m_preferences->getMaxRunningJobCount() : 1)); + QString sourceFileName(*iter), outputFileName; + if(createJob(sourceFileName, outputFileName, m_options.data(), runImmediately, false, counter++, filePathIn.count(), &applyToAll)) + { + if(appendJob(sourceFileName, outputFileName, m_options.data(), runImmediately)) + { + continue; + } + } + return false; + } + + //Add remaining files + while(applyToAll && (iter != filePathIn.constEnd())) + { + const bool runImmediatelyTmp = runImmediately && (countRunningJobs() < (m_preferences->getAutoRunNextJob() ? m_preferences->getMaxRunningJobCount() : 1)); + const QString sourceFileName = *iter; + const QString outputFileName = AddJobDialog::generateOutputFileName(sourceFileName, m_recentlyUsed->outputDirectory(), m_recentlyUsed->filterIndex(), m_preferences->getSaveToSourcePath()); + if(!appendJob(sourceFileName, outputFileName, m_options.data(), runImmediatelyTmp)) + { + return false; + } + iter++; + } + + return true; +} + +/* * Append a new job */ bool MainWindow::appendJob(const QString &sourceFileName, const QString &outputFileName, OptionsModel *options, const bool runImmediately) { bool okay = false; - - EncodeThread *thrd = new EncodeThread - ( - sourceFileName, - outputFileName, - options, - QString("%1/toolset").arg(m_appDir), - m_cpuFeatures->x64, - m_preferences.use10BitEncoding, - m_cpuFeatures->x64 && m_preferences.useAvisyth64Bit - ); - + EncodeThread *thrd = new EncodeThread(sourceFileName, outputFileName, options, m_sysinfo.data(), m_preferences.data()); QModelIndex newIndex = m_jobList->insertJob(thrd); if(newIndex.isValid()) { if(runImmediately) { - jobsView->selectRow(newIndex.row()); + ui->jobsView->selectRow(newIndex.row()); QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); m_jobList->startJob(newIndex); } @@ -1073,53 +1606,11 @@ bool MainWindow::appendJob(const QString &sourceFileName, const QString &outputF okay = true; } - m_label->setVisible(m_jobList->rowCount(QModelIndex()) == 0); + m_label[0]->setVisible(m_jobList->rowCount(QModelIndex()) == 0); return okay; } /* - * Creates a new job - */ -bool MainWindow::createJobMultiple(const QStringList &filePathIn) -{ - //bool ok = true, force = false; int counter = 0; - // - ////Add files - //QStringList::ConstIterator iter = filePathIn.constBegin(); - //while(ok && (!force) && (iter != filePathIn.constEnd())) - //{ - // ok = createJob(*iter, QString(), NULL, ++counter, filePathIn.count(), &force); - // iter++; - //} - - //if(force) qWarning("Force mode!"); - - ////Add remaining files - //if(force && (iter != filePathIn.constEnd())) - //{ - // QSettings settings(QString("%1/last.ini").arg(x264_data_path()), QSettings::IniFormat); - // QString outDirectory = settings.value("path/directory_saveTo", QDesktopServices::storageLocation(QDesktopServices::MoviesLocation)).toString(); - // - // while(iter != filePathIn.constEnd()) - // { - // int n = 2; - // QString outBaseName = QFileInfo(*iter).completeBaseName(); - // QString outPath = QString("%1/%2.mkv").arg(outDirectory, outBaseName); - // while(QFileInfo(outPath).exists()) - // { - // outPath = QString("%1/%2 (%3).mkv").arg(outDirectory, outBaseName, QString::number(n++)); - // } - // ok = createJob(*iter, outPath, NULL, ++counter, filePathIn.count(), &force); - // iter++; - // } - //} - - //return ok; - - return true; -} - -/* * Jobs that are not completed (or failed, or aborted) yet */ unsigned int MainWindow::countPendingJobs(void) @@ -1129,8 +1620,8 @@ unsigned int MainWindow::countPendingJobs(void) for(int i = 0; i < rows; i++) { - EncodeThread::JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex())); - if(status != EncodeThread::JobStatus_Completed && status != EncodeThread::JobStatus_Aborted && status != EncodeThread::JobStatus_Failed) + JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex())); + if(status != JobStatus_Completed && status != JobStatus_Aborted && status != JobStatus_Failed) { count++; } @@ -1149,8 +1640,8 @@ unsigned int MainWindow::countRunningJobs(void) for(int i = 0; i < rows; i++) { - EncodeThread::JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex())); - if(status != EncodeThread::JobStatus_Completed && status != EncodeThread::JobStatus_Aborted && status != EncodeThread::JobStatus_Failed && status != EncodeThread::JobStatus_Enqueued) + JobStatus status = m_jobList->getJobStatus(m_jobList->index(i, 0, QModelIndex())); + if(status != JobStatus_Completed && status != JobStatus_Aborted && status != JobStatus_Failed && status != JobStatus_Enqueued) { count++; } @@ -1162,68 +1653,124 @@ unsigned int MainWindow::countRunningJobs(void) /* * Update all buttons with respect to current job status */ -void MainWindow::updateButtons(EncodeThread::JobStatus status) +void MainWindow::updateButtons(JobStatus status) { qDebug("MainWindow::updateButtons(void)"); - buttonStartJob->setEnabled(status == EncodeThread::JobStatus_Enqueued); - buttonAbortJob->setEnabled(status == EncodeThread::JobStatus_Indexing || status == EncodeThread::JobStatus_Running || status == EncodeThread::JobStatus_Running_Pass1 || status == EncodeThread::JobStatus_Running_Pass2 || status == EncodeThread::JobStatus_Paused); - buttonPauseJob->setEnabled(status == EncodeThread::JobStatus_Indexing || status == EncodeThread::JobStatus_Running || status == EncodeThread::JobStatus_Paused || status == EncodeThread::JobStatus_Running_Pass1 || status == EncodeThread::JobStatus_Running_Pass2); - buttonPauseJob->setChecked(status == EncodeThread::JobStatus_Paused || status == EncodeThread::JobStatus_Pausing); + ui->buttonStartJob->setEnabled(status == JobStatus_Enqueued); + ui->buttonAbortJob->setEnabled(status == JobStatus_Indexing || status == JobStatus_Running || status == JobStatus_Running_Pass1 || status == JobStatus_Running_Pass2 || status == JobStatus_Paused); + ui->buttonPauseJob->setEnabled(status == JobStatus_Indexing || status == JobStatus_Running || status == JobStatus_Paused || status == JobStatus_Running_Pass1 || status == JobStatus_Running_Pass2); + ui->buttonPauseJob->setChecked(status == JobStatus_Paused || status == JobStatus_Pausing); - actionJob_Delete->setEnabled(status == EncodeThread::JobStatus_Completed || status == EncodeThread::JobStatus_Aborted || status == EncodeThread::JobStatus_Failed || status == EncodeThread::JobStatus_Enqueued); - actionJob_Restart->setEnabled(status == EncodeThread::JobStatus_Completed || status == EncodeThread::JobStatus_Aborted || status == EncodeThread::JobStatus_Failed || status == EncodeThread::JobStatus_Enqueued); - actionJob_Browse->setEnabled(status == EncodeThread::JobStatus_Completed); + ui->actionJob_Delete->setEnabled(status == JobStatus_Completed || status == JobStatus_Aborted || status == JobStatus_Failed || status == JobStatus_Enqueued); + ui->actionJob_Restart->setEnabled(status == JobStatus_Completed || status == JobStatus_Aborted || status == JobStatus_Failed || status == JobStatus_Enqueued); + ui->actionJob_Browse->setEnabled(status == JobStatus_Completed); + ui->actionJob_MoveUp->setEnabled(status != JobStatus_Undefined); + ui->actionJob_MoveDown->setEnabled(status != JobStatus_Undefined); - actionJob_Start->setEnabled(buttonStartJob->isEnabled()); - actionJob_Abort->setEnabled(buttonAbortJob->isEnabled()); - actionJob_Pause->setEnabled(buttonPauseJob->isEnabled()); - actionJob_Pause->setChecked(buttonPauseJob->isChecked()); + ui->actionJob_Start->setEnabled(ui->buttonStartJob->isEnabled()); + ui->actionJob_Abort->setEnabled(ui->buttonAbortJob->isEnabled()); + ui->actionJob_Pause->setEnabled(ui->buttonPauseJob->isEnabled()); + ui->actionJob_Pause->setChecked(ui->buttonPauseJob->isChecked()); - editDetails->setEnabled(status != EncodeThread::JobStatus_Paused); + ui->editDetails->setEnabled(status != JobStatus_Paused); } /* * Update the taskbar with current job status */ -void MainWindow::updateTaskbar(EncodeThread::JobStatus status, const QIcon &icon) +void MainWindow::updateTaskbar(JobStatus status, const QIcon &icon) { qDebug("MainWindow::updateTaskbar(void)"); + if(m_taskbar.isNull()) + { + return; /*taskbar object not created yet*/ + } + switch(status) { - case EncodeThread::JobStatus_Undefined: - WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarNoState); + case JobStatus_Undefined: + m_taskbar->setTaskbarState(MUtils::Taskbar7::TASKBAR_STATE_NONE); break; - case EncodeThread::JobStatus_Aborting: - case EncodeThread::JobStatus_Starting: - case EncodeThread::JobStatus_Pausing: - case EncodeThread::JobStatus_Resuming: - WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarIndeterminateState); + case JobStatus_Aborting: + case JobStatus_Starting: + case JobStatus_Pausing: + case JobStatus_Resuming: + m_taskbar->setTaskbarState(MUtils::Taskbar7::TASKBAR_STATE_INTERMEDIATE); break; - case EncodeThread::JobStatus_Aborted: - case EncodeThread::JobStatus_Failed: - WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarErrorState); + case JobStatus_Aborted: + case JobStatus_Failed: + m_taskbar->setTaskbarState(MUtils::Taskbar7::TASKBAR_STATE_ERROR); break; - case EncodeThread::JobStatus_Paused: - WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarPausedState); + case JobStatus_Paused: + m_taskbar->setTaskbarState(MUtils::Taskbar7::TASKBAR_STATE_PAUSED); break; default: - WinSevenTaskbar::setTaskbarState(this, WinSevenTaskbar::WinSevenTaskbarNormalState); + m_taskbar->setTaskbarState(MUtils::Taskbar7::TASKBAR_STATE_NORMAL); break; } switch(status) { - case EncodeThread::JobStatus_Aborting: - case EncodeThread::JobStatus_Starting: - case EncodeThread::JobStatus_Pausing: - case EncodeThread::JobStatus_Resuming: + case JobStatus_Aborting: + case JobStatus_Starting: + case JobStatus_Pausing: + case JobStatus_Resuming: break; default: - WinSevenTaskbar::setTaskbarProgress(this, progressBar->value(), progressBar->maximum()); + m_taskbar->setTaskbarProgress(ui->progressBar->value(), ui->progressBar->maximum()); break; } - WinSevenTaskbar::setOverlayIcon(this, icon.isNull() ? NULL : &icon); + m_taskbar->setOverlayIcon(icon.isNull() ? NULL : &icon); +} + +/* + * Parse command-line arguments + */ +bool MainWindow::parseCommandLineArgs(void) +{ + const MUtils::OS::ArgumentMap &args = MUtils::OS::arguments(); + + quint32 flags = 0; + bool commandSent = false; + + //Handle flags + if(args.contains(CLI_PARAM_FORCE_START)) + { + flags = ((flags | IPC_FLAG_FORCE_START) & (~IPC_FLAG_FORCE_ENQUEUE)); + } + if(args.contains(CLI_PARAM_FORCE_ENQUEUE)) + { + flags = ((flags | IPC_FLAG_FORCE_ENQUEUE) & (~IPC_FLAG_FORCE_START)); + } + + //Process all command-line arguments + if(args.contains(CLI_PARAM_ADD_FILE)) + { + foreach(const QString &fileName, args.values(CLI_PARAM_ADD_FILE)) + { + handleCommand(IPC_OPCODE_ADD_FILE, QStringList() << fileName, flags); + } + commandSent = true; + } + if(args.contains(CLI_PARAM_ADD_JOB)) + { + foreach(const QString &options, args.values(CLI_PARAM_ADD_JOB)) + { + const QStringList optionValues = options.split('|', QString::SkipEmptyParts); + if(optionValues.count() == 3) + { + handleCommand(IPC_OPCODE_ADD_JOB, optionValues, flags); + } + else + { + qWarning("Invalid number of arguments for parameter \"--%s\" detected!", CLI_PARAM_ADD_JOB); + } + } + commandSent = true; + } + + return commandSent; }