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);
	}
}
void VDUIJobControlDialog::OnJobQueueReloaded() {
	HWND hwndItem = GetDlgItem(mhdlg, IDC_JOBS);
	if (!hwndItem)
		return;

	POINT pt={0,0};
	ListView_GetOrigin(hwndItem, &pt);

	int selindex = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
	int newselindex = -1;

	if (selindex >= 0) {
		VDJob *vdj = g_VDJobQueue.ListGet(selindex);

		if (vdj) {
			uint64 selid = vdj->mId;

			newselindex = g_VDJobQueue.GetJobIndexById(selid);
		}
	}

	ListView_DeleteAllItems(hwndItem);
	int n = g_VDJobQueue.ListSize();
	for(int i=0; i<n; i++) {
		LVITEM li;

		li.mask		= LVIF_TEXT;
		li.iSubItem	= 0;
		li.iItem	= i;
		li.pszText	= LPSTR_TEXTCALLBACK;

		ListView_InsertItem(hwndItem, &li);
	}

	if (newselindex >= 0)
		ListView_SetItemState(hwndItem, newselindex, LVIS_SELECTED, LVIS_SELECTED);

	ListView_Scroll(hwndItem, 0, -pt.y);

	UpdateSelectedJobEnables(NULL);
}
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::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;
}