shithub: aacenc

Download patch

ref: afc79ed1e5e7cfa86a0dd4489a2fbc6ca6186bc5
parent: fd55c325c9ba46120f48010ee1bda57281f730d2
author: menno <menno>
date: Tue Apr 10 10:30:17 EDT 2001

Big update of GUI

--- a/wingui/EncoderGeneralPageDialog.cpp
+++ b/wingui/EncoderGeneralPageDialog.cpp
@@ -24,6 +24,7 @@
 	CJobListUpdatable *poListContainer,
 	CWnd* pParent /*=NULL*/):
 	m_bInitialized(false),
+	m_bLastRecursiveCheckboxVisibility(false),
 	m_oJobsToConfigure(oJobsToConfigure),
 	m_poListContainer(poListContainer),
 	m_eCurCheckBox(eNone),
@@ -406,19 +407,32 @@
 	// TODO: Add your control notification handler code here
 	UpdateData(TRUE);
 
+	bool bPreviousVisiblity=m_bLastRecursiveCheckboxVisibility;
+
 	// check for a filter
 	if (CFilePathCalc::IsValidFileMask(m_oEditSourceFile))
 	{
 		// user entered a filter
 		m_ctrlCheckRecursive.ShowWindow(SW_SHOW);
+		m_bLastRecursiveCheckboxVisibility=true;
 	}
 	else
 	{
 		// user did not enter a filter
 		m_ctrlCheckRecursive.ShowWindow(SW_HIDE);
+		m_bLastRecursiveCheckboxVisibility=false;
 	}
 
 	UpdateData(FALSE);
+
+	// in case the source file changed from a regular file to a filter
+	// or vice versa we have to switch the "Expand Filter Job" button on
+	// the main dialog; the simples way to do is to update the list control
+	// with the below method call
+	if (bPreviousVisiblity^m_bLastRecursiveCheckboxVisibility)
+	{
+		m_poListContainer->EnableExpandFilterJobButton(m_bLastRecursiveCheckboxVisibility);
+	}
 }
 
 
--- a/wingui/EncoderGeneralPageDialog.h
+++ b/wingui/EncoderGeneralPageDialog.h
@@ -66,6 +66,7 @@
 
 private:
 	bool m_bInitialized;
+	bool m_bLastRecursiveCheckboxVisibility;
 
 	TItemList<CJob*> m_oJobsToConfigure;
 
--- a/wingui/EncoderJob.cpp
+++ b/wingui/EncoderJob.cpp
@@ -117,7 +117,7 @@
 	return toReturn;
 }
 
-bool CEncoderJob::ProcessJob() const
+bool CEncoderJob::ProcessJob(CJobProcessingDynamicUserInputInfo &oUserInputInfo)
 {
 	if (!CFilePathCalc::IsValidFileMask(GetFiles().GetSourceFileName()))
 	{
@@ -124,7 +124,12 @@
 		// it's a regular single file encoder job
 
 		// create the manager that manages and performs the processing
-		CEncoderJobProcessingManager oManager(this);
+		CEncoderJobProcessingManager oManager(this, oUserInputInfo);
+		if (!oManager.MayStartProcessingWithStatusDialog())
+		{
+			// the manager advises that we do not even create the status dialog
+			return false;
+		}
 
 		// create the status dialog
 		CProcessJobStatusDialog oDlg(&oManager);
@@ -134,34 +139,88 @@
 	{
 		// it's a filter encoder job
 
+		// count which jobs we found
+		long lNumberOfSuccessJobs=0;
+		long lNumberOfAbortJobs=0;
+		long lNumberOfErrorJobs=0;
+		long lStartTime=::GetTickCount();
+
 		CJobList oExpandedJobList;
-		if (!ExpandFilterJob(oExpandedJobList)) return false;
+		if (!ExpandFilterJob(oExpandedJobList, lNumberOfErrorJobs)) return false;
 
 		CBListReader oReader(oExpandedJobList);
 		CJob *poCurrentJob;
+		bool bContinue=true;
 		while ((poCurrentJob=oExpandedJobList.GetNextJob(oReader))!=0)
 		{
-			// make sure the target directory for the current job exists
-			CString oTargetDir(poCurrentJob->GetEncoderJob()->GetFiles().GetTargetFileDirectory());
-			if (!CRecursiveDirectoryTraverser::MakeSureDirectoryExists(oTargetDir))
+			if (bContinue)
 			{
-				CString oError;
-				oError.Format(IDS_ErrorCreatingNestedEncoderJob, poCurrentJob->GetEncoderJob()->GetFiles().GetCompleteSourceFilePath(), GetFiles().GetCompleteSourceFilePath());
-				AfxMessageBox("Error creating target directory", MB_OK | MB_ICONSTOP);		// XXX insert resource string and ask user what to do
-				return false;
+				// process the job
+				if (poCurrentJob->ProcessJob(oUserInputInfo))
+				{
+					// job has been successfully processed
+					lNumberOfSuccessJobs++;
+				}
+				else
+				{
+					// error or user abort during processing
+					switch (poCurrentJob->GetProcessingOutcomeSimple())
+					{
+					case CAbstractJob::eUserAbort:
+						{
+							lNumberOfAbortJobs++;
+
+							// ask if to continue with remaining jobs
+							CString oError;
+							oError.Format(IDS_ErrorCreatingNestedEncoderJob, poCurrentJob->GetEncoderJob()->GetFiles().GetCompleteSourceFilePath(), GetFiles().GetCompleteSourceFilePath());
+							if (AfxMessageBox(oError, MB_YESNO)!=IDYES)
+							{
+								bContinue=false;
+							}
+							break;
+						}
+					case CAbstractJob::eError:
+						{
+							lNumberOfErrorJobs++;
+							break;
+						}
+					default:
+						{
+							// unknown return type of job processing
+							ASSERT(false);
+							lNumberOfErrorJobs++;
+							break;
+						}
+					}
+				}
 			}
+			else
+			{
+				// one of the remaining jobs after a "permanently aborted" one
+				lNumberOfAbortJobs++;
+			}
+		}
 
-			// process the job
-			if (!poCurrentJob->GetEncoderJob()->ProcessJob())
+		// save how well we processed the filter job
+		EJobProcessingOutcome eJobProcessingOutcome;
+		CString oSupplementaryInfo;
+		oSupplementaryInfo.Format(IDS_FilterJobSupplementaryInfo, lNumberOfSuccessJobs+lNumberOfAbortJobs+lNumberOfErrorJobs, lNumberOfSuccessJobs, lNumberOfErrorJobs, lNumberOfAbortJobs);
+		if (lNumberOfSuccessJobs==0)
+		{
+			eJobProcessingOutcome=eError;
+		}
+		else
+		{
+			if (lNumberOfAbortJobs>0 || lNumberOfErrorJobs>0)
 			{
-				CString oError;
-				oError.Format(IDS_ErrorCreatingNestedEncoderJob, poCurrentJob->GetEncoderJob()->GetFiles().GetCompleteSourceFilePath(), GetFiles().GetCompleteSourceFilePath());
-				if (AfxMessageBox(oError, MB_YESNO)!=IDYES)
-				{
-					return false;
-				}
+				eJobProcessingOutcome=ePartiallyProcessed;
 			}
+			else
+			{
+				eJobProcessingOutcome=eSuccessfullyProcessed;
+			}
 		}
+		SetProcessingOutcome(eJobProcessingOutcome, ::GetTickCount()-lStartTime, oSupplementaryInfo);
 
 		/*// first find out all files that actually belong to the job
 		TItemList<CString> oFiles=
@@ -384,11 +443,12 @@
 	return oToReturn;
 }
 
-bool CEncoderJob::ExpandFilterJob(CJobList &oTarget, bool bCreateDirectories) const
+bool CEncoderJob::ExpandFilterJob(CJobList &oTarget, long &lNumberOfErrorJobs, bool bCreateDirectories) const
 {
 	if (!CFilePathCalc::IsValidFileMask(GetFiles().GetSourceFileName()))
 	{
 		ASSERT(false);		// not a filter job
+		lNumberOfErrorJobs+=oTarget.GetNumber();
 		return false;
 	}
 	else
@@ -406,10 +466,8 @@
 
 		if (oFiles.GetNumber()==0)
 		{
-			CString oError;
-			oError.Format(IDS_FilterDidntFindFiles, GetFiles().GetCompleteSourceFilePath());
-			AfxMessageBox(oError);
-			return false;
+			// no files there that match the filter
+			return true;
 		}
 
 		long lTotalNumberOfSubJobsToProcess=oFiles.GetNumber();
@@ -419,75 +477,87 @@
 		CString oCurrentFilePath;
 		while (oFiles.GetNextElemContent(oReader, oCurrentFilePath))
 		{
+			bool bCurSubJobSuccess=true;
 			CEncoderJob oNewJob(*this);
+			oNewJob.ResetProcessingOutcome();
 			oNewJob.SetSubJobNumberInfo(lCurSubJobCount++, lTotalNumberOfSubJobsToProcess);
 			if (!oNewJob.GetFiles().SetCompleteSourceFilePath(oCurrentFilePath))
 			{
-				CString oError;
-				oError.Format(IDS_ErrorCreatingNestedEncoderJob, oCurrentFilePath, GetFiles().GetCompleteSourceFilePath());
-				if (AfxMessageBox(oError, MB_YESNO)!=IDYES)
-				{
-					return false;
-				}
+				bCurSubJobSuccess=false;
 			}
-			// assemble the target file name and apply it to the new job
+			else
 			{
-				// find out the long name of the source file directory
-				CString oSourceFileDir;
+				// assemble the target file name and apply it to the new job
 				{
-					oSourceFileDir=GetFiles().GetSourceFileDirectory();
-					CString oTemp;
-					LPTSTR pDummy;
-					::GetFullPathName(oSourceFileDir,
-						MAX_PATH, oTemp.GetBuffer(MAX_PATH),
-						&pDummy);
-					oTemp.ReleaseBuffer();
-					if (oTemp[oTemp.GetLength()-1]=='\\')
+					// find out the long name of the source file directory
+					CString oSourceFileDir;
 					{
-						oTemp.Delete(oTemp.GetLength()-1);
+						oSourceFileDir=GetFiles().GetSourceFileDirectory();
+						CString oTemp;
+						LPTSTR pDummy;
+						::GetFullPathName(oSourceFileDir,
+							MAX_PATH, oTemp.GetBuffer(MAX_PATH),
+							&pDummy);
+						oTemp.ReleaseBuffer();
+						if (oTemp[oTemp.GetLength()-1]=='\\')
+						{
+							oTemp.Delete(oTemp.GetLength()-1);
+						}
+
+						oSourceFileDir=oTemp;
 					}
 
-					oSourceFileDir=oTemp;
-				}
+					// find out the suffix to append to the target directory
+					// for our particular file
+					CString oDirSuffix;
+					{
+						CString oFileDir(oCurrentFilePath);
+						CFilePathCalc::MakePath(oFileDir);
+						int iLength=oFileDir.GetLength();
+						oDirSuffix=oFileDir.Right(iLength-oSourceFileDir.GetLength());
+						oDirSuffix.Delete(0);
+					}
 
-				// find out the suffix to append to the target directory
-				// for our particular file
-				CString oDirSuffix;
-				{
-					CString oFileDir(oCurrentFilePath);
-					CFilePathCalc::MakePath(oFileDir);
-					int iLength=oFileDir.GetLength();
-					oDirSuffix=oFileDir.Right(iLength-oSourceFileDir.GetLength());
-					oDirSuffix.Delete(0);
-				}
-
-				// determine the target directory for that particular file
-				CString oTargetDir(GetFiles().GetTargetFileDirectory());
-				CFilePathCalc::MakePath(oTargetDir, true);
-				oTargetDir+=oDirSuffix;
-				if (bCreateDirectories)
-				{
-					if (!CRecursiveDirectoryTraverser::MakeSureDirectoryExists(oTargetDir))
+					// determine the target directory for that particular file
+					CString oTargetDir(GetFiles().GetTargetFileDirectory());
+					CFilePathCalc::MakePath(oTargetDir, true);
+					oTargetDir+=oDirSuffix;
+					if (bCreateDirectories)
 					{
-						CString oError;
-						oError.Format(IDS_ErrorCreatingNestedEncoderJob, oCurrentFilePath, GetFiles().GetCompleteSourceFilePath());
-						AfxMessageBox("Error creating target directory", MB_OK | MB_ICONSTOP);		// XXX insert resource string and ask user what to do
-						return false;
+						// must display an error message at ASSERT(false) a few lines below
+						// and oprionally ask the user what to do if the functionality
+						// auto create directories should ever be used (i.e. we can reach
+						// here)
+						ASSERT(false);
+						if (!CRecursiveDirectoryTraverser::MakeSureDirectoryExists(oTargetDir))
+						{
+							// must display an error message and oprionally ask the user what to do
+							// if this functionality (auto create directories) should ever be used
+							ASSERT(false);
+						}
 					}
-				}
 
-				CString oSourceFileName;
-				CFilePathCalc::ExtractFileName(oCurrentFilePath, oSourceFileName);
-				CString oSourceFileNameRaw;
-				CString oSourceFileExtension;
-				CFilePathCalc::SplitFileAndExtension(oSourceFileName, oSourceFileNameRaw, oSourceFileExtension);
-				oNewJob.GetFiles().SetTargetFileDirectory(oTargetDir);
-				CString oDefaultExtension;
-				oDefaultExtension.LoadString(IDS_EndTargetFileStandardExtension);
-				oNewJob.GetFiles().SetTargetFileName(oSourceFileNameRaw+"."+oDefaultExtension);
+					CString oSourceFileName;
+					CFilePathCalc::ExtractFileName(oCurrentFilePath, oSourceFileName);
+					CString oSourceFileNameRaw;
+					CString oSourceFileExtension;
+					CFilePathCalc::SplitFileAndExtension(oSourceFileName, oSourceFileNameRaw, oSourceFileExtension);
+					oNewJob.GetFiles().SetTargetFileDirectory(oTargetDir);
+					CString oDefaultExtension;
+					oDefaultExtension.LoadString(IDS_EndTargetFileStandardExtension);
+					oNewJob.GetFiles().SetTargetFileName(oSourceFileNameRaw+"."+oDefaultExtension);
+				}
 			}
 
-			oTarget.AddJob(oNewJob);
+			// add the job or increment the errorneous jobs counter
+			if (bCurSubJobSuccess)
+			{
+				oTarget.AddJob(oNewJob);
+			}
+			else
+			{
+				lNumberOfErrorJobs++;
+			}
 		}
 
 		return true;
@@ -511,14 +581,22 @@
 
 void CEncoderJob::ApplyGeneralPageContents(const CEncoderGeneralPropertyPageContents &oPageContents)
 {
+	bool bModified=false;
+
 	// note: the use of getters on the righthand side is correct since they return references
-	oPageContents.m_oSourceDirectory.ApplyToJob(GetFiles().GetSourceFileDirectory());
-	oPageContents.m_oSourceFile.ApplyToJob(GetFiles().GetSourceFileName());
-	oPageContents.m_oTargetDirectory.ApplyToJob(GetFiles().GetTargetFileDirectory());
-	oPageContents.m_oTargetFile.ApplyToJob(GetFiles().GetTargetFileName());
-	oPageContents.m_oSourceFileFilterIsRecursive.ApplyToJob(m_bSourceFileFilterIsRecursive);
+	bModified=bModified || oPageContents.m_oSourceDirectory.ApplyToJob(GetFiles().GetSourceFileDirectory());
+	bModified=bModified || oPageContents.m_oSourceFile.ApplyToJob(GetFiles().GetSourceFileName());
+	bModified=bModified || oPageContents.m_oTargetDirectory.ApplyToJob(GetFiles().GetTargetFileDirectory());
+	bModified=bModified || oPageContents.m_oTargetFile.ApplyToJob(GetFiles().GetTargetFileName());
+	bModified=bModified || oPageContents.m_oSourceFileFilterIsRecursive.ApplyToJob(m_bSourceFileFilterIsRecursive);
 
 	// ignore oPageContents.m_oSourceFilterRecursiveCheckboxVisible
+
+	// when the job has been modified reset it to "unprocessed" state
+	if (bModified)
+	{
+		ResetProcessingOutcome();
+	}
 }
 
 CEncoderQualityPropertyPageContents CEncoderJob::GetQualityPageContents() const
@@ -537,16 +615,24 @@
 
 void CEncoderJob::ApplyQualityPageContents(const CEncoderQualityPropertyPageContents &oPageContents)
 {
+	bool bModified=false;
+
 	// note: the use of getters on the righthand side is correct since they return references
-	oPageContents.m_oBitRate.ApplyToJob((long&)m_ulBitRate);
-	oPageContents.m_oBandwidth.ApplyToJob((long&)m_ulBandwidth);
-	oPageContents.m_oAllowMidSide.ApplyToJob(m_bAllowMidSide);
-	oPageContents.m_oUseTns.ApplyToJob(m_bUseTns);
-	oPageContents.m_oUseLtp.ApplyToJob(m_bUseLtp);
-	oPageContents.m_oUseLfe.ApplyToJob(m_bUseLfe);
-	long lTemp=-1;
-	oPageContents.m_oAacProfile.ApplyToJob(lTemp);
-	if (lTemp>=0) SetAacProfile(lTemp);
+	bModified=bModified || oPageContents.m_oBitRate.ApplyToJob((long&)m_ulBitRate);
+	bModified=bModified || oPageContents.m_oBandwidth.ApplyToJob((long&)m_ulBandwidth);
+	bModified=bModified || oPageContents.m_oAllowMidSide.ApplyToJob(m_bAllowMidSide);
+	bModified=bModified || oPageContents.m_oUseTns.ApplyToJob(m_bUseTns);
+	bModified=bModified || oPageContents.m_oUseLtp.ApplyToJob(m_bUseLtp);
+	bModified=bModified || oPageContents.m_oUseLfe.ApplyToJob(m_bUseLfe);
+	long lTemp=GetAacProfileL();
+	bModified=bModified || oPageContents.m_oAacProfile.ApplyToJob(lTemp);
+	SetAacProfile(lTemp);
+
+	// when the job has been modified reset it to "unprocessed" state
+	if (bModified)
+	{
+		ResetProcessingOutcome();
+	}
 }
 
 CEncoderId3PropertyPageContents CEncoderJob::GetId3PageContents() const
@@ -570,17 +656,25 @@
 
 void CEncoderJob::ApplyId3PageContents(const CEncoderId3PropertyPageContents &oPageContents)
 {
+	bool bModified=false;
+
 	// note: the use of getters on the righthand side is correct since they return references
-	oPageContents.m_oArtist.ApplyToJob(GetTargetFileId3Info().GetArtist());
-	oPageContents.m_oTrackNo.ApplyToJob(GetTargetFileId3Info().GetTrackNoRef());
-	oPageContents.m_oAlbum.ApplyToJob(GetTargetFileId3Info().GetAlbum());
-	oPageContents.m_oYear.ApplyToJob(GetTargetFileId3Info().GetYearRef());
-	oPageContents.m_oTitle.ApplyToJob(GetTargetFileId3Info().GetSongTitle());
-	oPageContents.m_oCopyright.ApplyToJob(GetTargetFileId3Info().GetCopyrightRef());
-	oPageContents.m_oOriginalArtist.ApplyToJob(GetTargetFileId3Info().GetOriginalArtist());
-	oPageContents.m_oComposer.ApplyToJob(GetTargetFileId3Info().GetComposer());
-	oPageContents.m_oUrl.ApplyToJob(GetTargetFileId3Info().GetUrl());
-	oPageContents.m_oGenre.ApplyToJob(GetTargetFileId3Info().GetGenre());
-	oPageContents.m_oEncodedBy.ApplyToJob(GetTargetFileId3Info().GetEncodedBy());
-	oPageContents.m_oComment.ApplyToJob(GetTargetFileId3Info().GetComment());
+	bModified=bModified || oPageContents.m_oArtist.ApplyToJob(GetTargetFileId3Info().GetArtist());
+	bModified=bModified || oPageContents.m_oTrackNo.ApplyToJob(GetTargetFileId3Info().GetTrackNoRef());
+	bModified=bModified || oPageContents.m_oAlbum.ApplyToJob(GetTargetFileId3Info().GetAlbum());
+	bModified=bModified || oPageContents.m_oYear.ApplyToJob(GetTargetFileId3Info().GetYearRef());
+	bModified=bModified || oPageContents.m_oTitle.ApplyToJob(GetTargetFileId3Info().GetSongTitle());
+	bModified=bModified || oPageContents.m_oCopyright.ApplyToJob(GetTargetFileId3Info().GetCopyrightRef());
+	bModified=bModified || oPageContents.m_oOriginalArtist.ApplyToJob(GetTargetFileId3Info().GetOriginalArtist());
+	bModified=bModified || oPageContents.m_oComposer.ApplyToJob(GetTargetFileId3Info().GetComposer());
+	bModified=bModified || oPageContents.m_oUrl.ApplyToJob(GetTargetFileId3Info().GetUrl());
+	bModified=bModified || oPageContents.m_oGenre.ApplyToJob(GetTargetFileId3Info().GetGenre());
+	bModified=bModified || oPageContents.m_oEncodedBy.ApplyToJob(GetTargetFileId3Info().GetEncodedBy());
+	bModified=bModified || oPageContents.m_oComment.ApplyToJob(GetTargetFileId3Info().GetComment());
+
+	// when the job has been modified reset it to "unprocessed" state
+	if (bModified)
+	{
+		ResetProcessingOutcome();
+	}
 }
\ No newline at end of file
--- a/wingui/EncoderJob.h
+++ b/wingui/EncoderJob.h
@@ -87,7 +87,7 @@
 
 	// implementations to CAbstractJob
 	virtual CSupportedPropertyPagesData GetSupportedPropertyPages() const;
-	virtual bool ProcessJob() const;
+	virtual bool ProcessJob(CJobProcessingDynamicUserInputInfo &oUserInputInfo);
 	virtual CString GetDetailedDescriptionForStatusDialog() const;
 
 	// implementations to CFileSerializable
@@ -100,8 +100,11 @@
 	// this job to a list of single jobs to process;
 	// returns false in case of errors but displays error messages on its own;
 	// the bCreateDirectories parameter specifies if the target directory structure
-	// is already to create (otherwise executing the job could fail)
-	bool ExpandFilterJob(CJobList &oTarget, bool bCreateDirectories=false) const;
+	// is already to be created, normally the default value false should be appropriate;
+	// the lNumberOfErrorJobs parameter returns the number of jobs that failed to be created;
+	// note that this method only increments the previous value if errorneous jobs are
+	// encountered, it does not reset it in any kind;
+	bool ExpandFilterJob(CJobList &oTarget, long &lNumberOfErrorJobs, bool bCreateDirectories=false) const;
 
 private:
 	CSourceTargetFilePair m_oFiles;
--- a/wingui/EncoderJobProcessingManager.cpp
+++ b/wingui/EncoderJobProcessingManager.cpp
@@ -6,6 +6,7 @@
 #include "faac_wingui.h"
 #include "EncoderJobProcessingManager.h"
 #include "WindowUtil.h"
+#include "RecursiveDirectoryTraverser.h"
 
 #include <sndfile.h>
 #include "faac.h"
@@ -22,10 +23,13 @@
 // Construction/Destruction
 //////////////////////////////////////////////////////////////////////
 
-CEncoderJobProcessingManager::CEncoderJobProcessingManager(const CEncoderJob *poJobToProcess):
+CEncoderJobProcessingManager::CEncoderJobProcessingManager(
+	CEncoderJob *poJobToProcess, CJobProcessingDynamicUserInputInfo &oUserInputInfo):
+
 	m_poJobToProcess(poJobToProcess),
 	m_poInfoTarget(0),
-	m_eCurrentWorkingState(eInitial)
+	m_eCurrentWorkingState(eInitial),
+	m_oUserInputInfo(oUserInputInfo)
 {
 }
 
@@ -33,6 +37,60 @@
 {
 }
 
+bool CEncoderJobProcessingManager::MayStartProcessingWithStatusDialog()
+{
+	long lStartTimeMillis=::GetTickCount();
+	CEncoderJob *poJob=m_poJobToProcess;
+	// make sure the target directory exists at all
+	{
+		CString oTargetFileDir(poJob->GetFiles().GetTargetFileDirectory());
+		if (oTargetFileDir.GetLength()<1)
+		{
+			CString oError;
+			oError.LoadString(IDS_InvalidTargetDirectory);
+			poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, oError);
+			return false;
+		}
+		
+		while (oTargetFileDir.GetAt(oTargetFileDir.GetLength()-1)=='\\')
+		{
+			oTargetFileDir.Delete(oTargetFileDir.GetLength()-1);
+		}
+		if (CRecursiveDirectoryTraverser::CountMatchingFiles(oTargetFileDir)<1)
+		{
+			// the target directory doesn't exist;
+			// there are two possibilities: create it
+			// or
+			// abort with an error
+			CString oTargetDir(poJob->GetFiles().GetTargetFileDirectory());
+			if (m_oUserInputInfo.GetAutoCreateDirectoryBool(oTargetDir))
+			{
+				if (!CRecursiveDirectoryTraverser::MakeSureDirectoryExists(oTargetDir))
+				{
+					// directory couldn't be created;
+					// log the error
+					CString oError;
+					oError.Format(IDS_ErrorCreatingDirectory, poJob->GetFiles().GetTargetFileDirectory());
+					poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, oError);
+					return false;
+				}
+			}
+			else
+			{
+				// the directory doesn't exist and the user refused to create it
+				poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_UserRefusedToCreateTargetDirectory);
+				return false;
+			}
+		}
+		else
+		{
+			// the directory already exists so everything is ok up to here
+		}
+	}
+
+	return true;
+}
+
 void CEncoderJobProcessingManager::Start(
 	CProcessingStatusDialogInfoFeedbackCallbackInterface *poInfoTarget)
 {
@@ -117,7 +175,9 @@
 {
 	long lStartTimeMillis=::GetTickCount();
 
-	long lMaxCount=250*64000/(5+m_poJobToProcess->GetBitRate());
+	const CEncoderJob *poJob=m_poJobToProcess;
+
+	long lMaxCount=250*64000/(5+poJob->GetBitRate());
 	for (long lPos=0; lPos<lMaxCount; lPos++)
 	{
 		long lMultiplicationDummy;
@@ -151,6 +211,7 @@
 		case eStopped:
 			{
 				// must interrupt
+				poJob->SetProcessingOutcomeCurTime(CAbstractJob::eUserAbort, lStartTimeMillis, "");
 				return false;
 			}
 		}
@@ -158,6 +219,8 @@
 
 	m_eCurrentWorkingState=eCleanup;
 
+	poJob->SetProcessingOutcomeCurTime(CAbstractJob::eSuccessfullyProcessed, lStartTimeMillis, "");
+
 	return true;
 }
 
@@ -166,9 +229,19 @@
 bool CEncoderJobProcessingManager::DoProcessing()
 {
 	long lStartTimeMillis=::GetTickCount();
-	const CEncoderJob *poJob=m_poJobToProcess;
+	CEncoderJob *poJob=m_poJobToProcess;
 	bool bInterrupted=false;
 
+#ifdef _DEBUG
+	// make sure preprocessing has been done properly to warn the programmer if not
+	if (!MayStartProcessingWithStatusDialog())
+	{
+		// you should call MayStartProcessingWithStatusDialog() before starting
+		// a status dialog driven processing of the job
+		ASSERT(false);
+	}
+#endif
+
 	SNDFILE *phInFile;
 	SF_INFO sctSfInfo;
 
@@ -201,7 +274,7 @@
 				faacEncClose(hEncoder);
 				sf_close(phInFile);
 
-				AfxMessageBox("faacEncSetConfiguration failed!", MB_OK | MB_ICONSTOP);
+				poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_FaacEncSetConfigurationFailed);
 
 				return false;
 			}
@@ -270,6 +343,7 @@
 					if (bInterrupted) 
 					{
 						// Stop Pressed
+						poJob->SetProcessingOutcomeCurTime(CAbstractJob::eUserAbort, lStartTimeMillis, "");
 						break;
 					}
 
@@ -276,12 +350,13 @@
 					if (lSamplesInput==0 && lBytesWritten==0)
 					{
 						// all done, bail out
+						poJob->SetProcessingOutcomeCurTime(CAbstractJob::eSuccessfullyProcessed, lStartTimeMillis, "");
 						break;
 					}
 
 					if (lBytesWritten < 0)
 					{
-						AfxMessageBox("faacEncEncodeFrame failed!", MB_OK | MB_ICONSTOP);
+						poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_FaacEncEncodeFrameFailed);
 						bInterrupted=true;
 						break;
 					}
@@ -304,7 +379,7 @@
 	}
 	else
 	{
-		AfxMessageBox("Couldn't open input file!", MB_OK | MB_ICONSTOP);
+		poJob->SetProcessingOutcomeCurTime(CAbstractJob::eError, lStartTimeMillis, IDS_CouldntOpenInputFile);
 		bInterrupted=true;
 	}
 
@@ -321,7 +396,7 @@
 	CString oTopStatusText=m_poJobToProcess->GetDetailedDescriptionForStatusDialog();
 
 	long lElapsedTime=lCurTime-lOperationStartTickCount;
-	long lEstimateEntireTime=(long)((double)(lElapsedTime)/(dProgress/100));
+	long lEstimateEntireTime=(long)((100.*lElapsedTime)/dProgress);
 	long lETA=lEstimateEntireTime-lElapsedTime;
 	CString oElapsedTime(CWindowUtil::GetTimeDescription(lElapsedTime));
 	CString oEntireTime(CWindowUtil::GetTimeDescription(lEstimateEntireTime));
--- a/wingui/EncoderJobProcessingManager.h
+++ b/wingui/EncoderJobProcessingManager.h
@@ -20,9 +20,15 @@
 class CEncoderJobProcessingManager : public CProcessingStartStopPauseInteractable  
 {
 public:
-	CEncoderJobProcessingManager(const CEncoderJob *poJobToProcess);
+	CEncoderJobProcessingManager(CEncoderJob *poJobToProcess, CJobProcessingDynamicUserInputInfo &oUserInputInfo);
 	virtual ~CEncoderJobProcessingManager();
 
+	// encoder jobs should call this dialog before creating and starting the status dialog
+	// for this processing manager; if this member returns false you can stop processing
+	// of this job; this case is identical with that the the DoModal() function of the
+	// status returns something different from IDOK
+	bool MayStartProcessingWithStatusDialog();
+
 	virtual void Start(CProcessingStatusDialogInfoFeedbackCallbackInterface *poInfoTarget);
 
 	virtual void Stop();
@@ -38,8 +44,9 @@
 	} m_eCurrentWorkingState;
 
 private:
-	const CEncoderJob *m_poJobToProcess;
+	CEncoderJob *m_poJobToProcess;
 	CProcessingStatusDialogInfoFeedbackCallbackInterface *m_poInfoTarget;
+	CJobProcessingDynamicUserInputInfo &m_oUserInputInfo;
 
 	// returns true if the job has been completely processed
 	bool DoProcessing();
--- a/wingui/Job.cpp
+++ b/wingui/Job.cpp
@@ -130,10 +130,91 @@
 
 CString CJob::GetDetailedDescriptionForStatusDialog() const
 {
-	// doesn't need an implementation here
+	// doesn't need an implementation here; reaching here might
+	// be a mistake
+	ASSERT(false);
 	return "";
 }
 
+void CJob::SetJobNumberInfo(long lThisJobCountNumber, long lTotalNumberOfJobs)
+{
+	if (m_poJob!=0)
+	{
+		m_poJob->SetJobNumberInfo(lThisJobCountNumber, lTotalNumberOfJobs);
+	}
+	else
+	{
+		// must not call this method on uninitialized CJobs
+		ASSERT(false);
+	}
+}
+
+void CJob::SetSubJobNumberInfo(long lThisSubJobCountNumber, long lTotalNumberOfSubJobs)
+{
+	if (m_poJob!=0)
+	{
+		m_poJob->SetSubJobNumberInfo(lThisSubJobCountNumber, lTotalNumberOfSubJobs);
+	}
+	else
+	{
+		// must not call this method on uninitialized CJobs
+		ASSERT(false);
+	}
+}
+
+void CJob::GetProcessingNumberInformation(long &lThisJobCountNumber, long &lTotalNumberOfJobs, long &lThisSubJobCountNumber, long &lTotalNumberOfSubJobs) const
+{
+	if (m_poJob!=0)
+	{
+		m_poJob->GetProcessingNumberInformation(lThisJobCountNumber, lTotalNumberOfJobs, lThisSubJobCountNumber, lTotalNumberOfSubJobs);
+	}
+	else
+	{
+		// must not call this method on uninitialized CJobs
+		ASSERT(false);
+	}
+}
+
+void CJob::SetProcessingOutcome(EJobProcessingOutcome eJobProcessingOutcome, long lProcessingTime, const CString &oSupplementaryInfo)
+{
+	if (m_poJob!=0)
+	{
+		m_poJob->SetProcessingOutcome(eJobProcessingOutcome, lProcessingTime, oSupplementaryInfo);
+	}
+	else
+	{
+		// must not call this method on uninitialized CJobs
+		ASSERT(false);
+	}
+}
+
+void CJob::GetProcessingOutcome(EJobProcessingOutcome &eJobProcessingOutcome, long &lProcessingTime, CString &oSupplementaryInfo) const
+{
+	if (m_poJob!=0)
+	{
+		m_poJob->GetProcessingOutcome(eJobProcessingOutcome, lProcessingTime, oSupplementaryInfo);
+	}
+	else
+	{
+		// must not call this method on uninitialized CJobs
+		ASSERT(false);
+	}
+}
+
+void CJob::ResetProcessingOutcome()
+{
+	if (m_poJob!=0)
+	{
+		m_poJob->ResetProcessingOutcome();
+	}
+	else
+	{
+		// it's not an error when you reach here but it might be undesired so
+		// there's a little alarm here
+		ASSERT(false);
+	}
+}
+
 bool CJob::PutToArchive(CArchive &oArchive) const
 {
 	// put a class version flag
@@ -205,12 +286,11 @@
 	}
 }
 
-bool CJob::ProcessJob() const
+bool CJob::ProcessJob(CJobProcessingDynamicUserInputInfo &oUserInputInfo)
 {
 	if (m_poJob!=0)
 	{
-		m_poJob->CopyAllJobNumberInfoFromJob(*this);
-		return m_poJob->ProcessJob();
+		return m_poJob->ProcessJob(oUserInputInfo);
 	}
 	else
 	{
--- a/wingui/Job.h
+++ b/wingui/Job.h
@@ -51,8 +51,15 @@
 
 	// implementations to CAbstractJob
 	virtual CSupportedPropertyPagesData GetSupportedPropertyPages() const;
-	virtual bool ProcessJob() const;
+	virtual bool ProcessJob(CJobProcessingDynamicUserInputInfo &oUserInputInfo);
 	virtual CString GetDetailedDescriptionForStatusDialog() const;
+	// overrides to CAbstractJob
+	virtual void SetJobNumberInfo(long lThisJobCountNumber, long lTotalNumberOfJobs);
+	virtual void SetSubJobNumberInfo(long lThisSubJobCountNumber, long lTotalNumberOfSubJobs);
+	virtual void GetProcessingNumberInformation(long &lThisJobCountNumber, long &lTotalNumberOfJobs, long &lThisSubJobCountNumber, long &lTotalNumberOfSubJobs) const;
+	virtual void SetProcessingOutcome(EJobProcessingOutcome eJobProcessingOutcome, long lProcessingTime, const CString &oSupplementaryInfo);
+	virtual void GetProcessingOutcome(EJobProcessingOutcome &eJobProcessingOutcome, long &lProcessingTime, CString &oSupplementaryInfo) const;
+	virtual void ResetProcessingOutcome();
 
 	// implementations to CFileSerializable
 	virtual bool PutToArchive(CArchive &oArchive) const;
--- a/wingui/JobListUpdatable.h
+++ b/wingui/JobListUpdatable.h
@@ -24,6 +24,7 @@
 	// if no explicit selection state is specified the current
 	// selection is preserved
 	virtual void ReFillInJobListCtrl(CListCtrlStateSaver *poSelectionStateToUse=0, bool bSimpleUpdate=true)=0;
+	virtual void EnableExpandFilterJobButton(bool bEnable)=0;
 };
 
 #endif // !defined(AFX_JOBLISTUPDATABLE_H__A1444E81_1546_11D5_8402_0080C88C25BD__INCLUDED_)
--- a/wingui/PageCheckboxCtrlContent.cpp
+++ b/wingui/PageCheckboxCtrlContent.cpp
@@ -47,10 +47,12 @@
 	}
 }
 
-void CPageCheckboxCtrlContent::ApplyToJob(bool &bBool) const
+bool CPageCheckboxCtrlContent::ApplyToJob(bool &bBool) const
 {
+	bool bOld=bBool;
 	if (!Is3rdState())
 	{
 		bBool=GetCheckMark();
 	}
+	return bOld!=bBool;
 }
\ No newline at end of file
--- a/wingui/PageCheckboxCtrlContent.h
+++ b/wingui/PageCheckboxCtrlContent.h
@@ -31,7 +31,8 @@
 	int GetCheckCode() const				{ if (Is3rdState()) return 2; else return m_bCheckMark ? 1 : 0; }
 	void ApplyCheckCodeToButton(CButton *poButton) const;		// this member should prefered over GetCheckCode() because it also enables the 3rd state, if necessary
 
-	void ApplyToJob(bool &bBool) const;
+	// returns if the existing value has been modified
+	bool ApplyToJob(bool &bBool) const;
 
 	// implementation of CAbstractPageCtrlContent method
 	virtual CString GetHashString() const;
--- a/wingui/PageComboBoxCtrlContent.cpp
+++ b/wingui/PageComboBoxCtrlContent.cpp
@@ -77,21 +77,25 @@
 	SetContent(oText);
 }
 
-void CPageComboBoxCtrlContent::ApplyToJob(CString &oNativeJobPropertyTextString) const
+bool CPageComboBoxCtrlContent::ApplyToJob(CString &oNativeJobPropertyTextString) const
 {
+	CString oOld(oNativeJobPropertyTextString);
 	if (!Is3rdState())
 	{
 		oNativeJobPropertyTextString=GetContentText();
 	}
+	return oOld!=oNativeJobPropertyTextString;
 }
 
-void CPageComboBoxCtrlContent::ApplyToJob(long &lNativeJobPropertySelectionLong) const
+bool CPageComboBoxCtrlContent::ApplyToJob(long &lNativeJobPropertySelectionLong) const
 {
+	long lOld(lNativeJobPropertySelectionLong);
 	int iId=GetContentSelection();
 	if (iId>=0)
 	{
 		lNativeJobPropertySelectionLong=GetContentSelection();
 	}
+	return lOld!=lNativeJobPropertySelectionLong;
 }
 
 void CPageComboBoxCtrlContent::ApplyToComboBoxVariable(int &iSelectionVariable) const
--- a/wingui/PageComboBoxCtrlContent.h
+++ b/wingui/PageComboBoxCtrlContent.h
@@ -44,8 +44,9 @@
 	// SetCurrentText(const CString&)
 	void SetCurComboBoxSelectionText(CComboBox *poComboBox);
 
-	void ApplyToJob(CString &oNativeJobPropertyTextString) const;
-	void ApplyToJob(long &lNativeJobPropertySelectionLong) const;
+	// return if the previous setting was modified
+	bool ApplyToJob(CString &oNativeJobPropertyTextString) const;
+	bool ApplyToJob(long &lNativeJobPropertySelectionLong) const;
 
 	// apply to variables that are defined in the class wizard
 	void ApplyToComboBoxVariable(int &iSelectionVariable) const;
--- a/wingui/PageEditCtrlContent.cpp
+++ b/wingui/PageEditCtrlContent.cpp
@@ -83,16 +83,19 @@
 	}
 }
 
-void CPageEditCtrlContent::ApplyToJob(CString &oNativeJobPropertyString) const
+bool CPageEditCtrlContent::ApplyToJob(CString &oNativeJobPropertyString) const
 {
+	CString oOld(oNativeJobPropertyString);
 	if (!Is3rdState())
 	{
 		oNativeJobPropertyString=GetContent();
 	}
+	return oOld!=oNativeJobPropertyString;
 }
 
-void CPageEditCtrlContent::ApplyToJob(long &lNativeJobPropertyLong) const
+bool CPageEditCtrlContent::ApplyToJob(long &lNativeJobPropertyLong) const
 {
+	long lOld(lNativeJobPropertyLong);
 	if (!Is3rdState())
 	{
 		CString oContent(GetContent());
@@ -103,6 +106,7 @@
 			lNativeJobPropertyLong=lNumber;
 		}
 	}
+	return lOld!=lNativeJobPropertyLong;
 }
 
 CString CPageEditCtrlContent::GetHashString() const
--- a/wingui/PageEditCtrlContent.h
+++ b/wingui/PageEditCtrlContent.h
@@ -29,8 +29,9 @@
 	const CString& GetContent() const;
 	CString& GetContent();
 
-	void ApplyToJob(CString &oNativeJobPropertyString) const;
-	void ApplyToJob(long &lNativeJobPropertyLong) const;
+	// return if the previous setting was modified
+	bool ApplyToJob(CString &oNativeJobPropertyString) const;
+	bool ApplyToJob(long &lNativeJobPropertyLong) const;
 
 	// implementation of CAbstractPageCtrlContent method
 	virtual CString GetHashString() const;
--- a/wingui/PageRadioGroupCtrlContent.h
+++ b/wingui/PageRadioGroupCtrlContent.h
@@ -22,8 +22,8 @@
 	CPageRadioGroupCtrlContent();
 	virtual ~CPageRadioGroupCtrlContent();
 
-	// callers should only call the SetContent(long) and ApplyToJob(long) methods
-	// what are inherited from CPageEditCtrlContent
+	// jobs should only call the SetContent(long) and ApplyToJob(long) methods,
+	// which are inherited from CPageEditCtrlContent
 
 	void GetFromRadioGroupVariable(int iVariable, long lNumberOfRadiosInGroup)			{ SetContent(iVariable, true); if (iVariable>=lNumberOfRadiosInGroup) SetIs3rdState(true); }
 	void ApplyToRadioGroupVariable(int &iVariable) const								{ long lContent=-1; ApplyToJob(lContent); iVariable=lContent; }
--- a/wingui/RecursiveDirectoryTraverser.cpp
+++ b/wingui/RecursiveDirectoryTraverser.cpp
@@ -35,12 +35,13 @@
 	CString oSearchMask=oFilterString;
 	
 	if (oFileFind.FindFile(oSearchMask))
-	{		
-		iToReturn++;
-		while (oFileFind.FindNextFile())
+	{
+		BOOL bHaveMoreFiles;
+		do
 		{
+			bHaveMoreFiles=oFileFind.FindNextFile();
 			iToReturn++;
-		}
+		} while (bHaveMoreFiles);
 	}
 
 	oFileFind.Close();
@@ -48,7 +49,7 @@
 	return iToReturn;
 }
 
-TItemList<CString> CRecursiveDirectoryTraverser::FindFiles(const CString &oRootDirectory, const CString &oFileNameFilter, bool bRecursive)
+TItemList<CString> CRecursiveDirectoryTraverser::FindFiles(const CString &oRootDirectory, const CString &oFileNameFilter, bool bRecursive, bool bAcceptDirectories)
 {
 	TItemList<CString> oToReturn;
 	CString oRootDir(oRootDirectory);
@@ -69,11 +70,16 @@
 	if (oFileFind.FindFile(oSearchMask))
 	{	
 		CString oFileName;
-		while (oFileFind.FindNextFile())
+		BOOL bHaveMoreFiles;
+		do
 		{
-			oFileName=oFileFind.GetFilePath();
-			oToReturn.AddNewElem(oFileName);
-		}
+			bHaveMoreFiles=oFileFind.FindNextFile();
+			if (bAcceptDirectories || !oFileFind.IsDirectory())
+			{
+				oFileName=oFileFind.GetFilePath();
+				oToReturn.AddNewElem(oFileName);
+			}
+		} while (bHaveMoreFiles);
 	}
 
 	oFileFind.Close();
@@ -84,10 +90,11 @@
 		oSearchMask=oRootDir+"*";
 		if (oFileFind.FindFile(oSearchMask))
 		{
-		
 			CString oFileName;
-			while (oFileFind.FindNextFile())
+			BOOL bHaveMoreFiles;
+			do
 			{
+				bHaveMoreFiles=oFileFind.FindNextFile();
 				if (oFileFind.IsDirectory())
 				{
 					if (oFileFind.GetFileName()!="." &&
@@ -96,7 +103,7 @@
 						oToReturn+=FindFiles(oFileFind.GetFilePath(), oFileNameFilter, bRecursive);
 					}
 				}
-			}
+			} while (bHaveMoreFiles);
 		}
 	}
 
--- a/wingui/RecursiveDirectoryTraverser.h
+++ b/wingui/RecursiveDirectoryTraverser.h
@@ -31,8 +31,9 @@
 	// in this case directory names are NOT matched against the filter;
 	// return value: a list of complete paths of the files found;
 	// this method displays error messages and returns an empty list in case
-	// of errors
-	static TItemList<CString> FindFiles(const CString &oRootDirectory, const CString &oFileNameFilter, bool bRecursive);
+	// of errors; if bAcceptDirectories is true also directories that match
+	// the filter will be returned
+	static TItemList<CString> FindFiles(const CString &oRootDirectory, const CString &oFileNameFilter, bool bRecursive, bool bAcceptDirectories=false);
 
 	// returns if the directory was found or could be created. if false is
 	// returned this mostly means that there is a concurring file to 
--- a/wingui/WindowUtil.cpp
+++ b/wingui/WindowUtil.cpp
@@ -136,6 +136,42 @@
 	return poListCtrl->FindItem(&sctFindInfo, iStartAt);
 }
 
+void CWindowUtil::SetListCtrlFullRowSelectStyle(CListCtrl *poListCtrl, bool bFullRowSelectStyle)
+{
+	if (poListCtrl==0)
+	{
+		ASSERT(false);
+		return;
+	}
+
+	if (bFullRowSelectStyle)
+	{
+		ListView_SetExtendedListViewStyleEx(*poListCtrl, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
+	}
+	else
+	{
+		ListView_SetExtendedListViewStyleEx(*poListCtrl, LVS_EX_FULLROWSELECT, 0);
+	}
+}
+
+void CWindowUtil::SetListCtrlCheckBoxStyle(CListCtrl *poListCtrl, bool bCheckboxStyle)
+{
+	if (poListCtrl==0)
+	{
+		ASSERT(false);
+		return;
+	}
+
+	if (bCheckboxStyle)
+	{
+		ListView_SetExtendedListViewStyleEx(*poListCtrl, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
+	}
+	else
+	{
+		ListView_SetExtendedListViewStyleEx(*poListCtrl, LVS_EX_CHECKBOXES, 0);
+	}
+}
+
 
 void CWindowUtil::ForceNumericContent(CEdit *poEdit, bool bAllowNegative)
 {
--- a/wingui/WindowUtil.h
+++ b/wingui/WindowUtil.h
@@ -26,6 +26,8 @@
 	static void AddListCtrlColumn(CListCtrl *poListCtrl, int iColumnCount, const char *lpszText, double dWidth);	// for dWidth: <1: percent of the width of the list control; >1 width in pixels
 	static TItemList<long> GetAllSelectedListCtrlItemLParams(CListCtrl *poListCtrl, bool bDisableNoSelectionErrorMsg);
 	static int GetListCtrlItemIdByLParam(CListCtrl *poListCtrl, long lParam, int iStartAt=-1);		// returns a negative value if none is found
+	static void SetListCtrlFullRowSelectStyle(CListCtrl *poListCtrl, bool bFullRowSelectStyle=true);
+	static void SetListCtrlCheckBoxStyle(CListCtrl *poListCtrl, bool bCheckboxStyle=true);
 
 	static void ForceNumericContent(CEdit *poEdit, bool bAllowNegative);
 
--- a/wingui/faac_wingui.dsp
+++ b/wingui/faac_wingui.dsp
@@ -25,7 +25,7 @@
 # PROP AllowPerConfigDependencies 0
 # PROP Scc_ProjName ""
 # PROP Scc_LocalPath ""
-CPP=cl.exe
+CPP=xicl6.exe
 MTL=midl.exe
 RSC=rc.exe
 
@@ -51,9 +51,9 @@
 BSC32=bscmake.exe
 # ADD BASE BSC32 /nologo
 # ADD BSC32 /nologo
-LINK32=link.exe
+LINK32=xilink6.exe
 # ADD BASE LINK32 /nologo /subsystem:windows /machine:I386
-# ADD LINK32 /nologo /subsystem:windows /machine:I386
+# ADD LINK32 libsndfile.lib /nologo /subsystem:windows /machine:I386
 
 !ELSEIF  "$(CFG)" == "faac_wingui - Win32 Debug"
 
@@ -77,7 +77,7 @@
 BSC32=bscmake.exe
 # ADD BASE BSC32 /nologo
 # ADD BSC32 /nologo
-LINK32=link.exe
+LINK32=xilink6.exe
 # ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
 # ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
 
@@ -104,6 +104,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=.\AskCreateDirectoryDialog.cpp
+# End Source File
+# Begin Source File
+
 SOURCE=.\ConcreteJobBase.cpp
 # End Source File
 # Begin Source File
@@ -208,6 +212,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=.\JobProcessingDynamicUserInputInfo.cpp
+# End Source File
+# Begin Source File
+
 SOURCE=.\ListCtrlStateSaver.cpp
 # End Source File
 # Begin Source File
@@ -293,6 +301,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=.\AskCreateDirectoryDialog.h
+# End Source File
+# Begin Source File
+
 SOURCE=.\ConcreteJobBase.h
 # End Source File
 # Begin Source File
@@ -394,6 +406,10 @@
 # Begin Source File
 
 SOURCE=.\JobListUpdatable.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\JobProcessingDynamicUserInputInfo.h
 # End Source File
 # Begin Source File
 
--- a/wingui/faac_wingui.rc
+++ b/wingui/faac_wingui.rc
@@ -164,48 +164,54 @@
 // Dialog
 //
 
-IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 244, 55
+IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 244, 66
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "About faac_wingui"
 FONT 8, "MS Sans Serif"
 BEGIN
     ICON            IDR_MAINFRAME,IDC_STATIC,11,17,21,20
-    LTEXT           "faac_wingui Version 1.0",IDC_STATIC,40,10,119,8,
-                    SS_NOPREFIX
-    LTEXT           "Copyright (C) 2001",IDC_STATIC,39,33,119,8
-    DEFPUSHBUTTON   "OK",IDOK,187,34,50,14,WS_GROUP
+    LTEXT           "faac_wingui Version Alpha Release",IDC_STATIC,40,10,119,
+                    8,SS_NOPREFIX
+    LTEXT           "Copyright (C) 2001",IDC_STATIC,40,44,119,8
+    DEFPUSHBUTTON   "OK",IDOK,187,45,50,14,WS_GROUP
     LTEXT           "GUI written by Torsten Landmann as part of the faac Project",
                     IDC_STATIC,40,22,190,8
+    LTEXT           "refer to http://www.audiocoding.com",IDC_STATIC,40,32,
+                    118,8
 END
 
-IDD_FAAC_WINGUI_DIALOG DIALOGEX 0, 0, 422, 202
+IDD_FAAC_WINGUI_DIALOG DIALOGEX 0, 0, 479, 237
 STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
     WS_SYSMENU
 EXSTYLE WS_EX_APPWINDOW
 CAPTION "winfaac"
-FONT 8, "MS Sans Serif", 0, 0, 0x1
+FONT 8, "MS Sans Serif"
 BEGIN
-    DEFPUSHBUTTON   "Bye",IDOK,365,181,50,14
+    DEFPUSHBUTTON   "Bye",IDOK,422,216,50,14
     CONTROL         "List1",IDC_LISTJOBS,"SysListView32",LVS_REPORT | 
                     LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | 
-                    WS_TABSTOP,7,7,408,131
-    PUSHBUTTON      "&Add Encoder Jobs...",IDC_BUTTONADDENCODERJOB,7,143,71,
-                    14
-    PUSHBUTTON      "&Delete Jobs",IDC_BUTTONDELETEJOBS,83,143,50,14
-    PUSHBUTTON      "&Process Selected",IDC_BUTTONPROCESSSELECTED,63,161,66,
-                    14
+                    WS_TABSTOP,7,7,465,167
+    PUSHBUTTON      "&Add File Encoder Jobs...",IDC_BUTTONADDENCODERJOB,7,
+                    180,104,14
+    PUSHBUTTON      "&Delete Selection",IDC_BUTTONDELETEJOBS,117,198,63,14
+    PUSHBUTTON      "&Process Selection",IDC_BUTTONPROCESSSELECTED,249,198,
+                    69,14
     CONTROL         "Remove Processed Jobs From List",
                     IDC_CHECKREMOVEPROCESSEDJOBS,"Button",BS_AUTOCHECKBOX | 
-                    WS_TABSTOP,7,179,125,10
-    PUSHBUTTON      "&Save Joblist...",IDC_BUTTONSAVEJOBLIST,301,143,54,14
-    PUSHBUTTON      "&Load Joblist...",IDC_BUTTONLOADJOBLIST,361,143,54,14
-    PUSHBUTTON      "D&uplicate Selected",IDC_BUTTONDUPLICATESELECTED,139,
-                    143,69,14
-    PUSHBUTTON      "Pro&cess All",IDC_BUTTONPROCESSALL,7,161,50,14
-    PUSHBUTTON      "Open Pr&operties",IDC_BUTTONOPENPROPERTIES,356,161,59,
+                    WS_TABSTOP,249,215,125,10
+    PUSHBUTTON      "&Save Joblist...",IDC_BUTTONSAVEJOBLIST,359,180,54,14
+    PUSHBUTTON      "&Load Joblist...",IDC_BUTTONLOADJOBLIST,418,180,54,14
+    PUSHBUTTON      "D&uplicate Selection",IDC_BUTTONDUPLICATESELECTED,117,
+                    216,69,14
+    PUSHBUTTON      "Pro&cess All",IDC_BUTTONPROCESSALL,249,180,50,14
+    PUSHBUTTON      "Open Pr&operties",IDC_BUTTONOPENPROPERTIES,413,198,59,
                     14
-    PUSHBUTTON      "E&xpand Filter Job",IDC_BUTTONEXPANDFILTERJOB,215,143,
-                    65,14,WS_DISABLED
+    PUSHBUTTON      "E&xpand Directory Job",IDC_BUTTONEXPANDFILTERJOB,32,216,
+                    79,14,WS_DISABLED
+    PUSHBUTTON      "Add D&irectory Encoder Job...",
+                    IDC_BUTTONADDFILTERENCODERJOB,7,198,104,14
+    PUSHBUTTON      "Add E&mpty Encoder Job",IDC_BUTTONADDEMPTYENCODERJOB,
+                    117,180,104,14
 END
 
 IDD_PROPERTIESTABPARENTDIALOG DIALOG DISCARDABLE  0, 0, 186, 95
@@ -299,7 +305,22 @@
                     WS_VISIBLE | WS_DISABLED
 END
 
+IDD_ASKCREATEDIRECTORYDIALOG DIALOG DISCARDABLE  0, 0, 228, 85
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Target Directory not found"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CTEXT           "The target directory",IDC_STATIC,7,7,214,8
+    CTEXT           "has not been found. Do you want to create it now?",
+                    IDC_STATIC,7,38,214,8
+    CTEXT           "Static",IDC_LABELTARGETDIR,7,18,214,19
+    PUSHBUTTON      "&Yes",IDC_BUTTONYES,7,60,50,14
+    PUSHBUTTON      "&No",IDC_BUTTONNO,62,60,50,14
+    PUSHBUTTON      "&Always",IDC_BUTTONALWAYS,117,60,50,14
+    PUSHBUTTON      "Ne&ver",IDC_BUTTONNEVER,171,60,50,14
+END
 
+
 /////////////////////////////////////////////////////////////////////////////
 //
 // DESIGNINFO
@@ -314,17 +335,20 @@
         RIGHTMARGIN, 237
         VERTGUIDE, 40
         TOPMARGIN, 7
-        BOTTOMMARGIN, 48
+        BOTTOMMARGIN, 59
     END
 
     IDD_FAAC_WINGUI_DIALOG, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 415
+        RIGHTMARGIN, 472
+        VERTGUIDE, 111
+        VERTGUIDE, 117
+        VERTGUIDE, 249
         TOPMARGIN, 7
-        BOTTOMMARGIN, 195
-        HORZGUIDE, 150
-        HORZGUIDE, 168
+        BOTTOMMARGIN, 230
+        HORZGUIDE, 187
+        HORZGUIDE, 205
     END
 
     IDD_PROPERTIESTABPARENTDIALOG, DIALOG
@@ -381,6 +405,15 @@
         TOPMARGIN, 7
         BOTTOMMARGIN, 102
     END
+
+    IDD_ASKCREATEDIRECTORYDIALOG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 221
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 78
+        HORZGUIDE, 67
+    END
 END
 #endif    // APSTUDIO_INVOKED
 
@@ -823,6 +856,28 @@
     IDS_ErrorWhileLoadingJobList "Error while loading joblist."
     IDS_JobNofM             "%d of %d"
     IDS_SubJobNofM          "Sub Job %d of %d"
+    IDS_ErrorCreatingDirectory "The Directory ""%s"" could not be created."
+    IDS_FilterJobSupplementaryInfo 
+                            "Total of %d sub jobs: success %d, error %d, user abort %d"
+END
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_UserRefusedToCreateTargetDirectory 
+                            "The user refused to create the target directory."
+    IDS_FaacEncSetConfigurationFailed "faacEncSetConfiguration failed!"
+    IDS_CouldntOpenInputFile "Couldn't open input file!"
+    IDS_FaacEncEncodeFrameFailed "faacEncEncodeFrame failed!"
+    IDS_JobProcessInfoColumn "Last Processed"
+    IDS_OutcomeUnprocessed  "Unprocessed"
+    IDS_OutcomeSuccessfullyProcessed "Successfully processed"
+    IDS_OutcomePartiallyProcessed "Partially proccessed"
+    IDS_OutcomeUserAbort    "User abort"
+    IDS_OutcomeError        "Error"
+    IDS_FollowUp            "follow-up"
+    IDS_ErrorCreatingTargetDiretory "Error creating target directory"
+    IDS_InvalidTargetDirectory "Invalid target directory!"
+    IDS_HadUnsuccessfulJobs "There have been problems during job list processing. Refer to list column ""Last Processed"" for further information."
 END
 
 #endif    // English (U.S.) resources
--- a/wingui/faac_winguiDlg.cpp
+++ b/wingui/faac_winguiDlg.cpp
@@ -11,6 +11,7 @@
 #include "FileListQueryManager.h"
 #include "FileMaskAssembler.h"
 #include "FileSerializableJobList.h"
+#include "FolderDialog.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -18,6 +19,8 @@
 static char THIS_FILE[] = __FILE__;
 #endif
 
+//#define EXTENDED_JOB_TRACKING_DURING_PROCESSING		// doesn't work, yet
+
 /////////////////////////////////////////////////////////////////////////////
 // CAboutDlg dialog used for App About
 
@@ -113,6 +116,8 @@
 	ON_BN_CLICKED(IDC_BUTTONOPENPROPERTIES, OnButtonOpenProperties)
 	ON_WM_SHOWWINDOW()
 	ON_BN_CLICKED(IDC_BUTTONEXPANDFILTERJOB, OnButtonExpandFilterJob)
+	ON_BN_CLICKED(IDC_BUTTONADDFILTERENCODERJOB, OnButtonAddFilterEncoderJob)
+	ON_BN_CLICKED(IDC_BUTTONADDEMPTYENCODERJOB, OnButtonAddEmptyEncoderJob)
 	//}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
@@ -152,9 +157,12 @@
 		oTypeColumn.LoadString(IDS_JobTypeColumn);
 		CString oInfoColumn;
 		oInfoColumn.LoadString(IDS_JobInfoColumn);
-		CWindowUtil::AddListCtrlColumn(&m_ctrlListJobs, 0, oTypeColumn, 0.1);
-		CWindowUtil::AddListCtrlColumn(&m_ctrlListJobs, 1, oInfoColumn, 0.895);
-		ListView_SetExtendedListViewStyleEx(m_ctrlListJobs, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
+		CString oProcessInfoColumn;
+		oProcessInfoColumn.LoadString(IDS_JobProcessInfoColumn);
+		CWindowUtil::AddListCtrlColumn(&m_ctrlListJobs, 0, oTypeColumn, 0.075);
+		CWindowUtil::AddListCtrlColumn(&m_ctrlListJobs, 1, oInfoColumn, 0.75);
+		CWindowUtil::AddListCtrlColumn(&m_ctrlListJobs, 2, oProcessInfoColumn, 0.9999);
+		CWindowUtil::SetListCtrlFullRowSelectStyle(&m_ctrlListJobs);
 		ReFillInJobListCtrl();
 
 		// create a floating property window
@@ -283,6 +291,7 @@
 		{
 			long lCurListItem=CWindowUtil::AddListCtrlItem(&m_ctrlListJobs, poCurrentJob->DescribeJobTypeShort(), lCurrentJobIndex);
 			CWindowUtil::SetListCtrlItem(&m_ctrlListJobs, lCurListItem, 1, poCurrentJob->DescribeJob());
+			CWindowUtil::SetListCtrlItem(&m_ctrlListJobs, lCurListItem, 2, poCurrentJob->GetProcessingOutcomeString());
 		}
 
 		// restore the previous list selection
@@ -304,10 +313,23 @@
 			// get the job that belongs to the item
 			CJob *poCurJob=poJobList->GetJob(lItemLParam);
 			CWindowUtil::SetListCtrlItem(&m_ctrlListJobs, iCurItem, 1, poCurJob->DescribeJob());
+			CWindowUtil::SetListCtrlItem(&m_ctrlListJobs, iCurItem, 2, poCurJob->GetProcessingOutcomeString());
 		}
 	}
 }
 
+void CFaac_winguiDlg::EnableExpandFilterJobButton(bool bEnable)
+{
+	// before enabling it make sure only one job is selected
+	if (bEnable && CWindowUtil::GetAllSelectedListCtrlItemLParams(&m_ctrlListJobs, true).GetNumber()!=1)
+	{
+		// ignore or better to say disable
+		m_ctrlButtonExpandFilterJob.EnableWindow(FALSE);
+		return;
+	}
+	m_ctrlButtonExpandFilterJob.EnableWindow(bEnable ? TRUE : FALSE);
+}
+
 void CFaac_winguiDlg::OnJobListCtrlUserAction()
 {
 	// check if the selection has changed
@@ -402,14 +424,14 @@
 void CFaac_winguiDlg::OnJobListCtrlSelChange(TItemList<long> *poNewSelection)
 {
 	// determine which jobs are selected
-	TItemList<long> m_oSelectedJobsIds;
+	TItemList<long> oSelectedJobsIds;
 	if (poNewSelection!=0)
 	{
-		m_oSelectedJobsIds=*poNewSelection;
+		oSelectedJobsIds=*poNewSelection;
 	}
 	else
 	{
-		m_oSelectedJobsIds=CWindowUtil::GetAllSelectedListCtrlItemLParams(&m_ctrlListJobs, true);
+		oSelectedJobsIds=CWindowUtil::GetAllSelectedListCtrlItemLParams(&m_ctrlListJobs, true);
 	}
 	CJobList &oJobs=((CFaac_winguiApp*)AfxGetApp())->GetGlobalJobList();
 
@@ -421,11 +443,11 @@
 	}
 
 	// ask them all which pages they support
-	CBListReader oReader(m_oSelectedJobsIds);
+	CBListReader oReader(oSelectedJobsIds);
 	long lCurJobIndex;
 	CSupportedPropertyPagesData* poSupportedPages=0;
 	TItemList<CJob*> oSelectedJobs;
-	while (m_oSelectedJobsIds.GetNextElemContent(oReader, lCurJobIndex))
+	while (oSelectedJobsIds.GetNextElemContent(oReader, lCurJobIndex))
 	{
 		CJob *poCurJob=oJobs.GetJob(lCurJobIndex);
 		if (poSupportedPages==0)
@@ -455,10 +477,10 @@
 		delete poSupportedPages;
 	}
 
-	// enable/disable the expand button
+	// enable/disable the "expand filter job" button
 	{
 		bool bButtonEnabled=true;
-		if (m_oSelectedJobsIds.GetNumber()!=1)
+		if (oSelectedJobsIds.GetNumber()!=1)
 		{
 			bButtonEnabled=false;
 		}
@@ -466,7 +488,7 @@
 		{
 			long lSelectedJobId;
 			long lDummy;
-			m_oSelectedJobsIds.GetFirstElemContent(lSelectedJobId, lDummy);
+			oSelectedJobsIds.GetFirstElemContent(lSelectedJobId, lDummy);
 			CJob *poJob=oJobs.GetJob(lSelectedJobId);
 			if (poJob->GetEncoderJob()==0)
 			{
@@ -679,41 +701,102 @@
 	if (oJobIds.GetNumber()==0) return;
 	CJobList *poJobList=GetGlobalJobList();
 
+	// disable the properties window; important - otherwise the user
+	// could change selected jobs while they are being processed
+	CPropertiesDummyParentDialog *poPropertiesDialog=((CFaac_winguiApp*)AfxGetApp())->GetGlobalPropertiesDummyParentDialogSingleton();
+	poPropertiesDialog->EnableWindow(FALSE);
+
+	CListCtrlStateSaver oListCtrlState(&m_ctrlListJobs);
+
+#ifdef EXTENDED_JOB_TRACKING_DURING_PROCESSING
+	{
+		TItemList<long> oSelectedItems(oJobIds);
+		CWindowUtil::SetListCtrlCheckBoxStyle(&m_ctrlListJobs, true);
+		CListCtrlStateSaver oTempCheckBoxState;
+		oTempCheckBoxState.SetToChecked(oSelectedItems);
+		oTempCheckBoxState.RestoreState(&m_ctrlListJobs);
+	}
+#endif
+
 	TItemList<long> oItemsToRemove;
 
 	long lTotalNumberOfJobsToProcess=oJobIds.GetNumber();
 	long lCurJobCount=0;
+	long lSuccessfulJobs=0;
+	CJobProcessingDynamicUserInputInfo oUserInputInfo;	// saves dynamic input of the user
 
 	CBListReader oReader(oJobIds);
 	long lCurIndex;
 	bool bContinue=true;
-	while (bContinue && oJobIds.GetNextElemContent(oReader, lCurIndex))
+	while (oJobIds.GetNextElemContent(oReader, lCurIndex))
 	{
+		// only mark the currently processed job in the list control
+		{
+			CListCtrlStateSaver oSelection;
+			oSelection.SetToSelected(lCurIndex);
+			oSelection.RestoreState(&m_ctrlListJobs);
+			int iCurItem=CWindowUtil::GetListCtrlItemIdByLParam(&m_ctrlListJobs, lCurIndex);
+			if (iCurItem>=0)
+			{
+				m_ctrlListJobs.EnsureVisible(iCurItem, FALSE);
+			}
+		}
+
 		// get the current job
 		CJob *poJob=poJobList->GetJob(lCurIndex);
 		poJob->SetJobNumberInfo(lCurJobCount++, lTotalNumberOfJobsToProcess);
 
-		if (!poJob->ProcessJob())
+		if (bContinue)
 		{
-			if (AfxMessageBox(IDS_ErrorProcessingJobSelection, MB_YESNO)!=IDYES)
+			if (!poJob->ProcessJob(oUserInputInfo))
 			{
-				bContinue=false;
+				// show changes in process states
+				ReFillInJobListCtrl(0, true);
+				if (poJob->GetProcessingOutcomeSimple()==CAbstractJob::eUserAbort)
+				{
+					if (AfxMessageBox(IDS_ErrorProcessingJobSelection, MB_YESNO)!=IDYES)
+					{
+						bContinue=false;
+					}
+				}
 			}
+			else
+			{
+				// successfully processed
+				lSuccessfulJobs++;
+				if (bRemoveProcessJobs)
+				{
+					int iListItem=CWindowUtil::GetListCtrlItemIdByLParam(&m_ctrlListJobs, lCurIndex);
+					ASSERT(iListItem>=0);		// must exist (logically)
+					m_ctrlListJobs.DeleteItem(iListItem);
+
+					oItemsToRemove.AddNewElem(lCurIndex);
+				}
+				else
+				{
+					// show changes in process states
+					ReFillInJobListCtrl(0, true);
+				}
+			}
 		}
 		else
 		{
-			// successfully processed
-			if (bRemoveProcessJobs)
-			{
-				int iListItem=CWindowUtil::GetListCtrlItemIdByLParam(&m_ctrlListJobs, lCurIndex);
-				ASSERT(iListItem>=0);		// must exist (logically)
-				m_ctrlListJobs.DeleteItem(iListItem);
+			// mark job as aborted
 
-				oItemsToRemove.AddNewElem(lCurIndex);
-			}
+			// cast is necessary here because CJob masks the required overload
+			// due to overriding another one
+			((CAbstractJob*)poJob)->SetProcessingOutcome(CAbstractJob::eUserAbort, 0, IDS_FollowUp);
 		}
 	}
 
+#ifdef EXTENDED_JOB_TRACKING_DURING_PROCESSING
+	{
+		CWindowUtil::SetListCtrlCheckBoxStyle(&m_ctrlListJobs, false);
+	}
+#endif
+
+	oListCtrlState.RestoreState(&m_ctrlListJobs);
+
 	if (oItemsToRemove.GetNumber()>0)
 	{
 		OnJobListCtrlUserAction();	// this call is very important since the property dialog must be informed about the new selection before the underlying jobs are deleted
@@ -724,9 +807,22 @@
 		// dialog is updated
 		OnJobListCtrlUserAction();
 	}
+	else
+	{
+		// show changes in process states
+		ReFillInJobListCtrl(0, true);
+	}
 
 	// wake up the user
 	MessageBeep(MB_OK);
+
+	if (lSuccessfulJobs<lTotalNumberOfJobsToProcess)
+	{
+		AfxMessageBox(IDS_HadUnsuccessfulJobs);
+	}
+
+	// reenable the properties window we disabled at the beginning
+	poPropertiesDialog->EnableWindow(TRUE);
 }
 
 bool CFaac_winguiDlg::LoadJobList(const CString &oCompletePath)
@@ -791,12 +887,13 @@
 	CJobList *poGlobalJobList=GetGlobalJobList();
 	CJob *poJob=poGlobalJobList->GetJob(lSelectedJobId);
 	CJobList oExpanded;
+	long lExpandErrors=0;
 	if (poJob->GetEncoderJob()==0)
 	{
 		ASSERT(false);
 		return;
 	}
-	else if (poJob->GetEncoderJob()->ExpandFilterJob(oExpanded, false))
+	else if (poJob->GetEncoderJob()->ExpandFilterJob(oExpanded, lExpandErrors, false))
 	{
 		int iListItemToDelete=CWindowUtil::GetListCtrlItemIdByLParam(&m_ctrlListJobs, lSelectedJobId);
 		if (iListItemToDelete>=0)
@@ -810,6 +907,81 @@
 		CListCtrlStateSaver oListSelection;
 		oListSelection.SetToSelected(oExpanded.GetAllUsedIds());
 		ReFillInJobListCtrl(&oListSelection, false);
-		OnJobListCtrlSelChange();
+		OnJobListCtrlUserAction();		// again: very important call
 	}
+}
+
+void CFaac_winguiDlg::OnButtonAddFilterEncoderJob() 
+{
+	// TODO: Add your control notification handler code here
+	CJobList *poJobList=GetGlobalJobList();
+
+	TItemList<long> m_oIndicesOfAddedItems;
+
+	// select a directory
+	CString oCaption;
+	oCaption.LoadString(IDS_SelectSourceDirDlgCaption);
+	CFolderDialog oFolderDialog(this, oCaption);
+	if (oFolderDialog.DoModal()==IDOK)
+	{
+		CFilePathCalc::MakePath(oFolderDialog.m_oFolderPath);
+	}
+	else
+	{
+		if (oFolderDialog.m_bError)
+		{
+			AfxMessageBox(IDS_ErrorDuringDirectorySelection);
+		}
+		return;
+	}
+	CString oSourceFileExt;
+	oSourceFileExt.LoadString(IDS_EndSourceFileStandardExtension);
+	
+	// create a new job for the current file
+	CEncoderJob oNewJob;
+	GetGlobalProgramSettings()->ApplyToJob(oNewJob);
+
+	oNewJob.GetFiles().SetSourceFileDirectory(oFolderDialog.m_oFolderPath);
+	oNewJob.GetFiles().SetSourceFileName(CString("*.")+oSourceFileExt);
+	//oNewJob.GetFiles().SetTargetFileDirectory(oTargetDir);		// already set by GetGlobalProgramSettings()->ApplyToJob(oNewJob);
+	oNewJob.GetFiles().SetTargetFileName("");
+
+	long lNewIndex=poJobList->AddJob(oNewJob);
+	m_oIndicesOfAddedItems.AddNewElem(lNewIndex);
+	
+	CListCtrlStateSaver oSelection;
+	oSelection.SetToSelected(m_oIndicesOfAddedItems);
+	ReFillInJobListCtrl(&oSelection, false);
+
+	m_ctrlListJobs.SetFocus();
+
+	OnJobListCtrlUserAction();
+}
+
+void CFaac_winguiDlg::OnButtonAddEmptyEncoderJob() 
+{
+	// TODO: Add your control notification handler code here
+	CJobList *poJobList=GetGlobalJobList();
+
+	TItemList<long> m_oIndicesOfAddedItems;
+	
+	// create a new job for the current file
+	CEncoderJob oNewJob;
+	//GetGlobalProgramSettings()->ApplyToJob(oNewJob);
+
+	oNewJob.GetFiles().SetSourceFileDirectory("");
+	oNewJob.GetFiles().SetSourceFileName("");
+	oNewJob.GetFiles().SetTargetFileDirectory("");
+	oNewJob.GetFiles().SetTargetFileName("");
+
+	long lNewIndex=poJobList->AddJob(oNewJob);
+	m_oIndicesOfAddedItems.AddNewElem(lNewIndex);
+	
+	CListCtrlStateSaver oSelection;
+	oSelection.SetToSelected(m_oIndicesOfAddedItems);
+	ReFillInJobListCtrl(&oSelection, false);
+
+	m_ctrlListJobs.SetFocus();
+
+	OnJobListCtrlUserAction();
 }
--- a/wingui/faac_winguiDlg.h
+++ b/wingui/faac_winguiDlg.h
@@ -74,6 +74,8 @@
 	afx_msg void OnButtonOpenProperties();
 	afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
 	afx_msg void OnButtonExpandFilterJob();
+	afx_msg void OnButtonAddFilterEncoderJob();
+	afx_msg void OnButtonAddEmptyEncoderJob();
 	//}}AFX_MSG
 	DECLARE_MESSAGE_MAP()
 
@@ -85,7 +87,8 @@
 	// selection is preserved;
 	// bSimpleUpdate may be set to true; if it is, no items are added
 	// or removed and only the info column is updated
-	void ReFillInJobListCtrl(CListCtrlStateSaver *poSelectionStateToUse=0, bool bSimpleUpdate=true);
+	virtual void ReFillInJobListCtrl(CListCtrlStateSaver *poSelectionStateToUse=0, bool bSimpleUpdate=true);
+	virtual void EnableExpandFilterJobButton(bool bEnable);
 
 	TItemList<long> m_lLastJobListSelection;
 	void OnJobListCtrlUserAction();
--- a/wingui/resource.h
+++ b/wingui/resource.h
@@ -52,8 +52,25 @@
 #define IDS_FilterDidntFindFiles        137
 #define IDS_ErrorCreatingNestedEncoderJob 138
 #define IDS_ErrorWhileLoadingJobList    139
+#define IDD_ASKCREATEDIRECTORYDIALOG    139
 #define IDS_JobNofM                     140
 #define IDS_SubJobNofM                  141
+#define IDS_ErrorCreatingDirectory      142
+#define IDS_FilterJobSupplementaryInfo  143
+#define IDS_UserRefusedToCreateTargetDirectory 144
+#define IDS_FaacEncSetConfigurationFailed 145
+#define IDS_CouldntOpenInputFile        146
+#define IDS_FaacEncEncodeFrameFailed    147
+#define IDS_JobProcessInfoColumn        148
+#define IDS_OutcomeUnprocessed          149
+#define IDS_OutcomeSuccessfullyProcessed 150
+#define IDS_OutcomePartiallyProcessed   151
+#define IDS_OutcomeUserAbort            152
+#define IDS_OutcomeError                153
+#define IDS_FollowUp                    154
+#define IDS_ErrorCreatingTargetDiretory 155
+#define IDS_InvalidTargetDirectory      156
+#define IDS_HadUnsuccessfulJobs         157
 #define IDC_LISTJOBS                    1000
 #define IDC_BUTTONADDENCODERJOB         1001
 #define IDC_TAB1                        1002
@@ -106,6 +123,13 @@
 #define IDC_BUTTONOPENPROPERTIES        1054
 #define IDC_BUTTONMINIMIZEAPP           1055
 #define IDC_BUTTONEXPANDFILTERJOB       1056
+#define IDC_LABELTARGETDIR              1057
+#define IDC_BUTTONNO                    1058
+#define IDC_BUTTONALWAYS                1059
+#define IDC_BUTTONYES                   1060
+#define IDC_BUTTONNEVER                 1061
+#define IDC_BUTTONADDFILTERENCODERJOB   1062
+#define IDC_BUTTONADDEMPTYENCODERJOB    1063
 #define ID_BUTTON32771                  32771
 #define ID_BUTTON32772                  32772
 #define ID_BUTTON32773                  32773
@@ -115,9 +139,9 @@
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        139
+#define _APS_NEXT_RESOURCE_VALUE        140
 #define _APS_NEXT_COMMAND_VALUE         32775
-#define _APS_NEXT_CONTROL_VALUE         1057
+#define _APS_NEXT_CONTROL_VALUE         1064
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif