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);
}
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;
}