void VDUIJobControlDialog::UpdateSelectedJobEnables(const VDJob *vdjcheck) {
	HWND hwndItem = GetDlgItem(mhdlg, IDC_JOBS);
	if (!hwndItem)
		return;

	int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
	VDJob *vdj = NULL;

	if (index >= 0)
		vdj = g_VDJobQueue.ListGet(index);

	if (vdjcheck && vdjcheck != vdj)
		return;

	if (vdj) {
		EnableControl(IDC_DELETE, vdj->GetState() != VDJob::kStateInProgress);
		EnableControl(IDC_POSTPONE, vdj->GetState() != VDJob::kStateInProgress);
		EnableControl(IDC_RELOAD, !g_VDJobQueue.IsRunInProgress());
		EnableControl(IDC_MOVE_UP, index > 0);
		EnableControl(IDC_MOVE_DOWN, index < g_VDJobQueue.ListSize()-1);
	} else {
		EnableControl(IDC_DELETE, false);
		EnableControl(IDC_POSTPONE, false);
		EnableControl(IDC_RELOAD, false);
		EnableControl(IDC_MOVE_UP, false);
		EnableControl(IDC_MOVE_DOWN, false);
	}
}
bool VDUIJobControlDialog::OnCommand(uint32 id, uint32 extcode) {
	OnMenuHit(id);
	
	if (extcode == BN_CLICKED) {
		HWND hwndItem = GetDlgItem(mhdlg, IDC_JOBS);
		int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
		VDJob *vdj = NULL;

		if (index >= 0)
			vdj = g_VDJobQueue.ListGet(index);

		switch(id) {
		case IDOK:
			Destroy();
			return true;

		case IDC_DELETE:
			if (vdj) {
				// Do not delete jobs that are in progress!

				if (vdj->GetState() != VDJob::kStateInProgress) {
					fUpdateDisable = true;
					g_VDJobQueue.Delete(vdj, false);
					delete vdj;
					g_VDJobQueue.SetModified();
					fUpdateDisable = false;
					if (g_VDJobQueue.ListSize() > 0)
						ListView_SetItemState(hwndItem, index==g_VDJobQueue.ListSize() ? index-1 : index, LVIS_SELECTED, LVIS_SELECTED);
				}
			}

			return TRUE;

		case IDC_POSTPONE:
			if (vdj) {
				// Do not postpone jobs in progress

				int state = vdj->GetState();
				if (state != VDJob::kStateInProgress) {
					if (state == VDJob::kStatePostponed)
						vdj->SetState(VDJob::kStateWaiting);
					else
						vdj->SetState(VDJob::kStatePostponed);

					vdj->Refresh();

					g_VDJobQueue.SetModified();
				}
			}

			return TRUE;

		case IDC_MOVE_UP:
			if (!vdj || index <= 0)
				return TRUE;

			g_VDJobQueue.Swap(index-1, index);

			ListView_SetItemState(hwndItem, index  , 0, LVIS_SELECTED | LVIS_FOCUSED);
			ListView_SetItemState(hwndItem, index-1, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
			ListView_RedrawItems(hwndItem, index-1, index);

			g_VDJobQueue.SetModified();

			return TRUE;

		case IDC_MOVE_DOWN:
			if (!vdj || index >= g_VDJobQueue.ListSize()-1)
				return TRUE;

			g_VDJobQueue.Swap(index+1, index);
			
			ListView_SetItemState(hwndItem, index  , 0, LVIS_SELECTED | LVIS_FOCUSED);
			ListView_SetItemState(hwndItem, index+1, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
			ListView_RedrawItems(hwndItem, index, index+1);

			g_VDJobQueue.SetModified();

			return TRUE;

		case IDC_START:
			if (g_VDJobQueue.IsRunInProgress()) {
				g_VDJobQueue.RunAllStop();
				EnableControl(IDC_START, FALSE);
			} else
				g_VDJobQueue.RunAllStart();
			return TRUE;

		case IDC_ABORT:
			if (g_VDJobQueue.IsRunInProgress()) {
				g_VDJobQueue.RunAllStop();
				EnableControl(IDC_START, false);
				EnableControl(IDC_ABORT, false);
				if (g_dubber) g_dubber->Abort();
			}

			return TRUE;

		case IDC_RELOAD:
			if (!vdj)
				return TRUE;

			if (!vdj->IsReloadMarkerPresent())
				MessageBox(mhdlg, "This job was created with an older version of VirtualDub and cannot be reloaded.", g_szError, MB_ICONERROR|MB_OK);
			else
				vdj->Reload();

			return TRUE;

		case IDC_AUTOSTART:
			g_VDJobQueue.SetAutoRunEnabled(IsButtonChecked(IDC_AUTOSTART));
			return TRUE;
		}
	}

	return false;
}
bool VDUIJobControlDialog::OnMenuHit(uint32 id) {
	static const wchar_t fileFilters[]=
		L"VirtualDub job list (*.jobs)\0"			L"*.jobs\0"
		L"VirtualDub script (*.syl, *.vdscript)\0"	L"*.syl;*.vdscript\0"
		L"All files (*.*)\0"						L"*.*\0";

	try {
		switch(id) {

			case ID_FILE_LOADJOBLIST:
				{
					VDStringW filename(VDGetLoadFileName(kFileDialog_JobList, (VDGUIHandle)mhdlg, L"Load job list", fileFilters, NULL));

					if (!filename.empty())
						g_VDJobQueue.ListLoad(filename.c_str(), false);
				}
				break;

			case ID_FILE_SAVEJOBLIST:
				{
					VDStringW filename(VDGetSaveFileName(kFileDialog_JobList, (VDGUIHandle)mhdlg, L"Save job list", fileFilters, NULL));

					if (!filename.empty())
						g_VDJobQueue.Flush(filename.c_str());
				}
				break;

			case ID_FILE_USELOCALJOBQUEUE:
				if (g_VDJobQueue.IsRunInProgress())
					MessageBox(mhdlg, "Cannot switch job queues while a job is in progress.", g_szError, MB_ICONERROR | MB_OK);
				else
					g_VDJobQueue.SetJobFilePath(NULL, false, false);
				break;

			case ID_FILE_USEREMOTEJOBQUEUE:
				if (g_VDJobQueue.IsRunInProgress())
					MessageBox(mhdlg, "Cannot switch job queues while a job is in progress.", g_szError, MB_ICONERROR | MB_OK);
				else {
					const VDFileDialogOption opts[]={
						{ VDFileDialogOption::kConfirmFile, 0, NULL, 0, 0},
						{0}
					};

					int optvals[]={ false };

					VDStringW filename(VDGetSaveFileName(kFileDialog_JobList, (VDGUIHandle)mhdlg, L"Use shared job list", fileFilters, NULL, opts, optvals));

					if (!filename.empty()) {
						if (!_wcsicmp(filename.c_str(), g_VDJobQueue.GetDefaultJobFilePath())) {
							DWORD res = MessageBox(mhdlg,
								"Using the same job file that is normally used for local job queue operation is not recommended as "
								"it can cause job queue corruption.\n"
								"\n"
								"Are you sure you want to use this file for the remote queue too?",
								"VirtualDub Warning",
								MB_ICONEXCLAMATION | MB_YESNO);

							if (res != IDYES)
								break;
						}

						g_VDJobQueue.SetJobFilePath(filename.c_str(), true, true);
					}
				}
				break;

			case ID_EDIT_CLEARLIST:
				if (IDOK != MessageBox(mhdlg, "Really clear job list?", "VirtualDub job system", MB_OKCANCEL | MB_ICONEXCLAMATION))
					break;

				g_VDJobQueue.ListClear(false);
				break;

			case ID_EDIT_DELETEDONEJOBS:
				for(uint32 i=0; i<g_VDJobQueue.ListSize();) {
					VDJob *vdj = g_VDJobQueue.ListGet(i);

					if (vdj->GetState() == VDJob::kStateCompleted) {
						g_VDJobQueue.Delete(vdj, false);
						delete vdj;
					} else
						++i;
				}

				break;

			case ID_EDIT_FAILEDTOWAITING:
				g_VDJobQueue.Transform(VDJob::kStateAborted, VDJob::kStateWaiting);
				g_VDJobQueue.Transform(VDJob::kStateError, VDJob::kStateWaiting);
				break;

			case ID_EDIT_WAITINGTOPOSTPONED:
				g_VDJobQueue.Transform(VDJob::kStateWaiting, VDJob::kStatePostponed);
				break;

			case ID_EDIT_POSTPONEDTOWAITING:
				g_VDJobQueue.Transform(VDJob::kStatePostponed, VDJob::kStateWaiting);
				break;

			case ID_EDIT_DONETOWAITING:
				g_VDJobQueue.Transform(VDJob::kStateCompleted, VDJob::kStateWaiting);
				break;

			case ID_EDIT_PROCESSDIRECTORY:
				JobProcessDirectory(mhdlg);
				break;

			case ID_WHENFINISHED_DONOTHING:
				{
					VDRegistryAppKey appKey("");

					appKey.setBool(g_szRegKeyShutdownWhenFinished, false);
				}
				break;

			case ID_WHENFINISHED_SHUTDOWN:
				{
					VDRegistryAppKey appKey("");

					appKey.setBool(g_szRegKeyShutdownWhenFinished, true);
					appKey.setInt(g_szRegKeyShutdownMode, 0);
				}
				break;

			case ID_WHENFINISHED_HIBERNATE:
				{
					VDRegistryAppKey appKey("");

					appKey.setBool(g_szRegKeyShutdownWhenFinished, true);
					appKey.setInt(g_szRegKeyShutdownMode, 1);
				}
				break;
			case ID_WHENFINISHED_SLEEP:
				{
					VDRegistryAppKey appKey("");

					appKey.setBool(g_szRegKeyShutdownWhenFinished, true);
					appKey.setInt(g_szRegKeyShutdownMode, 2);
				}
				break;
		}
	} catch(const MyError& e) {
		e.post(mhdlg, "Job system error");
	}

	return true;
}
bool VDUIJobControlDialog::OnLoaded() {
	VDSetDialogDefaultIcons(mhdlg);

	mStandardCaption = VDGetWindowTextW32(mhdlg);

	static const char *const szColumnNames[]={ "Name","Source","Dest","Start","End","Status" };
	HWND hwndItem = GetDlgItem(mhdlg, IDC_JOBS);
	for (int i=0; i<6; i++) {
		LV_COLUMN lvc;
		lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
		lvc.fmt = LVCFMT_LEFT;
		lvc.cx = 1;
		lvc.pszText = (LPSTR)szColumnNames[i];

		ListView_InsertColumn(hwndItem, i, &lvc);
	}

	static const float kRelativeColumnWidths[]={ 50,100,100,50,50,100 };
	VDUISetListViewColumnsW32(hwndItem, kRelativeColumnWidths, 6);

	mResizer.Init(mhdlg);
	mResizer.Add(IDOK,				VDDialogResizerW32::kTR);
	mResizer.Add(IDC_MOVE_UP,		VDDialogResizerW32::kTR);
	mResizer.Add(IDC_MOVE_DOWN,		VDDialogResizerW32::kTR);
	mResizer.Add(IDC_POSTPONE,		VDDialogResizerW32::kTR);
	mResizer.Add(IDC_DELETE,		VDDialogResizerW32::kTR);
	mResizer.Add(IDC_START,			VDDialogResizerW32::kTR);
	mResizer.Add(IDC_ABORT,			VDDialogResizerW32::kTR);
	mResizer.Add(IDC_RELOAD,		VDDialogResizerW32::kTR);
	mResizer.Add(IDC_AUTOSTART,		VDDialogResizerW32::kTR);
	mResizer.Add(IDC_JOBS,			VDDialogResizerW32::kMC);
	mResizer.Add(IDC_CURRENTJOB,	VDDialogResizerW32::kBL);
	mResizer.Add(IDC_PROGRESS,		VDDialogResizerW32::kBC);
	mResizer.Add(IDC_PERCENT,		VDDialogResizerW32::kBR);

	GetWindowRect(mhdlg, &rInitial);
	VDUIRestoreWindowPlacementW32(mhdlg, "Job control", SW_SHOWNORMAL);
	VDUIRestoreListViewColumnsW32(hwndItem, "Job control: Columns");

	fUpdateDisable = false;

	ListView_SetExtendedListViewStyleEx(hwndItem, LVS_EX_FULLROWSELECT , LVS_EX_FULLROWSELECT);

	RECT rLV;
	GetClientRect(hwndItem, &rLV);

	OnJobQueueReloaded();
	OnJobQueueStatusChanged(g_VDJobQueue.GetQueueStatus());

	CheckButton(IDC_AUTOSTART, g_VDJobQueue.IsAutoRunEnabled());

	SendDlgItemMessage(mhdlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 16384));

	if (g_dubber || g_VDJobQueue.IsRunInProgress()) {
		EnableControl(IDC_PROGRESS, false);
		EnableControl(IDC_PERCENT, false);
	} else {
		EnableControl(IDC_PROGRESS, true);
		EnableControl(IDC_PERCENT, true);
	}

	g_hwndJobs = mhdlg;

	g_VDJobQueue.SetCallback(this);

	return VDDialogFrameW32::OnLoaded();
}