void VDUIJobControlDialog::OnJobQueueStatusChanged(VDJobQueueStatus status) {
	VDStringW title(mStandardCaption);

	if (status == kVDJQS_Running)
		title.append_sprintf(L" (%d remaining)", g_VDJobQueue.GetPendingJobCount());

	if (g_VDJobQueue.IsAutoUpdateEnabled())
		title.append_sprintf(L" [%s] (%hs:%d)", g_VDJobQueue.GetJobFilePath(), g_VDJobQueue.GetRunnerName(), (uint32)g_VDJobQueue.GetRunnerId());

	switch(status) {
		case kVDJQS_Idle:
			EnableControl(IDC_START, true);
			SetControlText(IDC_START, L"Start");
			EnableControl(IDC_ABORT, false);
			EnableControl(IDC_RELOAD, true);
			break;

		case kVDJQS_Running:
			SetControlText(IDC_START, L"Stop");
			EnableControl(IDC_START, true);
			EnableControl(IDC_ABORT, true);
			EnableControl(IDC_RELOAD, false);
			break;

		case kVDJQS_Blocked:
			SetControlText(IDC_START, L"Start");
			EnableControl(IDC_START, false);
			EnableControl(IDC_ABORT, false);
			EnableControl(IDC_RELOAD, false);
			break;
	}

	VDSetWindowTextW32(mhdlg, title.c_str());
}
VDZINT_PTR VDUIJobControlDialog::DlgProc(VDZUINT msg, VDZWPARAM wParam, VDZLPARAM lParam) {
	int index;

	switch(msg) {
	case WM_COMMAND:
		// we have to filter out WM_COMMAND messages from the list view edit control
		// because some moron on the Windows team used IDOK as the edit control identifier
		if (lParam) {
			switch(HIWORD(wParam)) {
				case EN_SETFOCUS:
				case EN_KILLFOCUS:
				case EN_CHANGE:
				case EN_UPDATE:
				case EN_ERRSPACE:
				case EN_MAXTEXT:
				case EN_HSCROLL:
				case EN_VSCROLL:
					return FALSE;
			}
		}

		// fall through to default handler
		break;

	case WM_NOTIFY:
		{
			NMHDR *nm = (NMHDR *)lParam;

			if (nm->idFrom == IDC_JOBS) {
				NMLVDISPINFO *nldi = (NMLVDISPINFO *)nm;
				NMLISTVIEW *nmlv;
				VDJob *vdj;

				switch(nm->code) {
				case LVN_GETDISPINFOA:
					GetJobListDispInfoA(nldi);
					return TRUE;
				case LVN_GETDISPINFOW:
					GetJobListDispInfoW((NMLVDISPINFOW *)nldi);
					return TRUE;
				case LVN_ENDLABELEDITA:
					SetWindowLongPtr(mhdlg, DWLP_MSGRESULT, TRUE);
					vdj = g_VDJobQueue.ListGet(nldi->item.iItem);

					if (vdj && nldi->item.pszText)
						vdj->SetName(nldi->item.pszText);

					return TRUE;
				case LVN_ENDLABELEDITW:
					SetWindowLongPtr(mhdlg, DWLP_MSGRESULT, TRUE);
					vdj = g_VDJobQueue.ListGet(nldi->item.iItem);

					if (vdj && nldi->item.pszText)
						vdj->SetName(VDTextWToA(((NMLVDISPINFOW *)nldi)->item.pszText).c_str());

					return TRUE;
				case LVN_ITEMCHANGED:

					if (fUpdateDisable) return TRUE;

					nmlv = (NMLISTVIEW *)lParam;
					vdj = g_VDJobQueue.ListGet(nmlv->iItem);

					UpdateSelectedJobEnables(NULL);
					return TRUE;

				case LVN_KEYDOWN:
					switch(((LPNMLVKEYDOWN)lParam)->wVKey) {
					case VK_DELETE:
						SendMessage(mhdlg, WM_COMMAND, IDC_DELETE, (LPARAM)GetDlgItem(mhdlg, IDC_DELETE));
					}
					return TRUE;

				case NM_DBLCLK:

					//	Previous state		Next state		Action
					//	--------------		----------		------
					//	Error				Waiting			Show error message
					//	Done (warnings)		Done			Show log
					//	Done				Waiting
					//	Postponed			Waiting
					//	Aborted				Waiting
					//	All others			Postponed

					index = ListView_GetNextItem(GetDlgItem(mhdlg, IDC_JOBS), -1, LVNI_ALL | LVNI_SELECTED);
					if (index>=0) {
						vdj = g_VDJobQueue.ListGet(index);

						switch(vdj->GetState()) {
						case VDJob::kStateError:
							if (VDUIJobErrorDialog(*vdj).ShowDialog((VDGUIHandle)mhdlg)) {
								vdj->SetState(VDJob::kStateWaiting);
								vdj->Refresh();
								g_VDJobQueue.SetModified();
							}
							break;
						case VDJob::kStateCompleted:
							if (!vdj->mLogEntries.empty()) {
								if (!VDUIJobLogDialog(vdj->mLogEntries).ShowDialog((VDGUIHandle)mhdlg))
									break;
							}
						case VDJob::kStateAborted:
							vdj->SetState(VDJob::kStateWaiting);
							vdj->Refresh();
							g_VDJobQueue.SetModified();
							break;

						case VDJob::kStateInProgress:
							if (!vdj->IsLocal()) {
								vdj->SetState(VDJob::kStateAborting);
								vdj->Refresh();
							}
							g_VDJobQueue.SetModified();
							break;

						case VDJob::kStateAborting:
							if (!vdj->IsLocal()) {
								VDStringA msg;

								msg.sprintf("This job may be running on a different instance of VirtualDub on the machine named %hs. Are you sure you want to reset it to Waiting status?", vdj->GetRunnerName());
								if (IDOK == MessageBox(mhdlg, msg.c_str(), "VirtualDub Warning", MB_ICONEXCLAMATION | MB_OKCANCEL)) {
									vdj->SetState(VDJob::kStateWaiting);
									vdj->Refresh();
								}
							}
							break;
						default:
							SendMessage(mhdlg, WM_COMMAND, MAKELONG(IDC_POSTPONE, BN_CLICKED), (LPARAM)GetDlgItem(mhdlg, IDC_POSTPONE));
						}
					}

					return TRUE;
				}
			}
		}
		break;

	case WM_INITMENU:
		{
			HMENU hmenu = (HMENU)wParam;
			VDRegistryAppKey key;
			bool bShutdownWhenFinished = key.getBool(g_szRegKeyShutdownWhenFinished);
			int shutdownMode = key.getInt(g_szRegKeyShutdownMode);

			VDCheckRadioMenuItemByCommandW32(hmenu, ID_WHENFINISHED_DONOTHING, !bShutdownWhenFinished);
			VDCheckRadioMenuItemByCommandW32(hmenu, ID_WHENFINISHED_SHUTDOWN, bShutdownWhenFinished && shutdownMode == 0);
			VDCheckRadioMenuItemByCommandW32(hmenu, ID_WHENFINISHED_HIBERNATE, bShutdownWhenFinished && shutdownMode == 1);
			VDCheckRadioMenuItemByCommandW32(hmenu, ID_WHENFINISHED_SLEEP, bShutdownWhenFinished && shutdownMode == 2);

			bool isRemoteQueue = g_VDJobQueue.IsAutoUpdateEnabled();
			VDCheckRadioMenuItemByCommandW32(hmenu, ID_FILE_USELOCALJOBQUEUE, !isRemoteQueue);
			VDCheckRadioMenuItemByCommandW32(hmenu, ID_FILE_USEREMOTEJOBQUEUE, isRemoteQueue);
		}
		return 0;

	case WM_GETMINMAXINFO:
		{
			LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;

			lpmmi->ptMinTrackSize.x = rInitial.right - rInitial.left;
			lpmmi->ptMinTrackSize.y = rInitial.bottom - rInitial.top;
		}
		return TRUE;

	}

	return VDDialogFrameW32::DlgProc(msg, wParam, lParam);
}