Exemplo n.º 1
0
VDStringW VDLoadStringW32(uint32 uID, bool doSubstitutions) {
	// Credit for this function goes to Raymond Chen, who described how
	// to directly access string resources in his blog.

	VDStringW str;

	HRSRC hrsrc = FindResourceEx(NULL, RT_STRING, MAKEINTRESOURCE(1 + (uID >> 4)), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
	if (hrsrc) {
		HGLOBAL hglob = LoadResource(NULL, hrsrc);
		if (hglob) {
			LPCWSTR pTable = (LPCWSTR)LockResource(hglob);
			if (pTable) {
				uID &= 15;

				while(uID--)
					pTable += 1 + (UINT)*pTable;

				str.assign(pTable+1, (UINT)*pTable);
			}
			// UnlockResource() is a NOP in Win32.
		}
		// FreeResource() is a NOP in Win32.
	}

	if (doSubstitutions)
		VDSubstituteStrings(str);

	return str;
}
Exemplo n.º 2
0
void JobAddConfigurationImages(const DubOptions *opt, const wchar_t *szFileInput, const wchar_t *pszInputDriver, const wchar_t *szFilePrefix, const wchar_t *szFileSuffix, int minDigits, int imageFormat, int quality, List2<InputFilenameNode> *pListAppended) {
	JobScriptOutput output;

	JobAddConfigurationInputs(output, szFileInput, pszInputDriver, pListAppended);
	JobCreateScript(output, opt);
	JobAddReloadMarker(output);

	// Add actual run option
	VDStringA s(strCify(VDTextWToU8(VDStringW(szFilePrefix)).c_str()));

	output.addf("VirtualDub.SaveImageSequence(\"%s\", \"%s\", %d, %d, %d);", s.c_str(), strCify(VDTextWToU8(VDStringW(szFileSuffix)).c_str()), minDigits, imageFormat, quality);

	JobAddClose(output);

	///////////////////

	vdautoptr<VDJob> vdj(new VDJob);
	vdj->SetInputFile(szFileInput);
	VDStringW outputFile;
	outputFile.sprintf(L"%ls*%ls", szFilePrefix, szFileSuffix);
	vdj->SetOutputFile(outputFile.c_str());

	const JobScriptOutput::Script& script = output.getscript();
	vdj->SetScript(script.data(), script.size(), true);
	g_VDJobQueue.Add(vdj.release(), false);
}
Exemplo n.º 3
0
void VDUIHotKeyExControlW32::OnPaint() {
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(mhwnd, &ps);
	if (!hdc)
		return;

	RECT r;
	if (GetClientRect(mhwnd, &r)) {
		VDVERIFY(DrawEdge(hdc, &r, EDGE_SUNKEN, BF_ADJUST | BF_RECT));
		VDVERIFY(FillRect(hdc, &r, (HBRUSH)(COLOR_WINDOW + 1)));

		int cx = GetSystemMetrics(SM_CXEDGE);
		int cy = GetSystemMetrics(SM_CYEDGE);

		r.left += cx;
		r.top += cy;
		r.right -= cx;
		r.bottom -= cy;

		if (r.right > r.left && r.bottom > r.top) {
			SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
			SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
			SetTextAlign(hdc, TA_TOP | TA_LEFT);

			HGDIOBJ holdFont = SelectObject(hdc, mhfont);
			if (holdFont) {
				ExtTextOutW(hdc, r.left, r.top, ETO_CLIPPED, &r, mBuffer.c_str(), mBuffer.size(), NULL);
				SelectObject(hdc, holdFont);
			}
		}
	}

	EndPaint(mhwnd, &ps);
}
Exemplo n.º 4
0
bool VDRegistryKey::getString(const char *pszName, VDStringW& str) const {
	if (!pHandle)
		return false;

	if (GetVersion() & 0x80000000) {
		VDStringA v;
		if (!getString(pszName, v))
			return false;
		str = VDTextAToW(v);
		return true;
	}

	const VDStringW wsName(VDTextAToW(pszName));
	DWORD type, s = sizeof(DWORD);

	if (!pHandle || RegQueryValueExW((HKEY)pHandle, wsName.c_str(), 0, &type, NULL, &s) || type != REG_SZ)
		return false;

	if (s <= 0)
		str.clear();
	else {
		str.resize((s + sizeof(wchar_t) - 1) / sizeof(wchar_t));

		if (RegQueryValueExW((HKEY)pHandle, wsName.c_str(), 0, NULL, (BYTE *)&str[0], &s))
			return false;

		str.resize(wcslen(str.c_str()));		// Trim off pesky terminating NULLs.
	}

	return true;
}
Exemplo n.º 5
0
void VDFileFixDirPath(VDStringW& path) {
    if (!path.empty()) {
        wchar_t c = path[path.size()-1];

        if (c != L'/' && c != L'\\' && c != L':')
            path += L'\\';
    }
}
Exemplo n.º 6
0
void VDLogF(int severity, const wchar_t *format, ...) {
	va_list val;
	va_start(val, format);
	VDStringW s;
	s.append_vsprintf(format, val);
	va_end(val);

	VDLog(severity, s);
}
Exemplo n.º 7
0
void ActivateFrameServerDialog(HWND hwnd) {
	static wchar_t fileFilters[]=
		L"VirtualDub AVIFile signpost (*.vdr,*.avi)\0"		L"*.vdr;*.avi\0"
		L"All files\0"										L"*.*\0"
		;

	char szServerName[128];

	if (!InitServerDLL()) return;

	if (!DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SERVER_SETUP), hwnd, FrameServerSetupDlgProc, (LPARAM)szServerName))
		return;

	try {
		vdrefptr<Frameserver> fs(new Frameserver(inputVideoAVI, inputAudio, hwnd, &g_dubOpts, g_project->GetTimeline().GetSubset()));

		const VDStringW fname(VDGetSaveFileName(kFileDialog_Signpost, (VDGUIHandle)hwnd, L"Save .VDR signpost for AVIFile handler", fileFilters, g_prefs.main.fAttachExtension ? L"vdr" : NULL, 0, 0));

		if (!fname.empty()) {
			long buf[5];
			char sname[128];
			int slen;

			ivdsl->GetComputerName(sname);
			strcat(sname,"/");
			strcat(sname,szServerName);
			slen = strlen(sname);
			slen += slen&1;

			buf[0] = 'FFIR';
			buf[1] = slen+12;
			buf[2] = 'MRDV';
			buf[3] = 'HTAP';
			buf[4] = slen;

			VDFile file(fname.c_str(), nsVDFile::kWrite | nsVDFile::kDenyRead | nsVDFile::kCreateAlways);

			file.write(buf, 20);
			file.write(sname, strlen(sname));
			if (strlen(sname) & 1)
				file.write("", 1);

			file.close();
		}

		VDDEBUG("Attempting to initialize frameserver...\n");

		fs->Go(ivdsl, szServerName);

		VDDEBUG("Frameserver exit.\n");

	} catch(const MyError& e) {
		e.post(hwnd, "Frameserver error");
	}
}
Exemplo n.º 8
0
void VDVideoWindow::UpdateSourcePARMenuItem() {
	VDStringW s;

	if (mSourcePAR <= 0)
		s = L"Unknown ratio";
	else
		s.sprintf(L"%.4g:1 pixel", mSourcePAR);

	VDStringW t(mSourcePARTextPattern);
	VDStringW::size_type pos = t.find('?');

	if (pos != VDStringW::npos)
		t.replace(pos, 1, s.data(), s.size());
	
	VDSetMenuItemTextByCommandW32(mhmenu, ID_DISPLAY_AR_PIXEL_SOURCE, t.c_str());
}
Exemplo n.º 9
0
void VDUINumericLabelW32::SetValue(int v) {
	if (v != mValue) {
		mValue = v;

		SetCaption(VDswprintf(mFormat.c_str(), 1, &v).c_str());
	}
}
Exemplo n.º 10
0
void VDSetWindowTextFW32(HWND hwnd, const wchar_t *format, ...) {
	va_list val;

	va_start(val, format);
	{
		wchar_t buf[512];
		int r = vswprintf(buf, 512, format, val);

		if ((unsigned)r < 512) {
			VDSetWindowTextW32(hwnd, buf);
			va_end(val);
			return;
		}
	}

	VDStringW s;
	s.append_vsprintf(format, val);
	VDSetWindowTextW32(hwnd, s.c_str());

	va_end(val);
}
Exemplo n.º 11
0
void VDUIHotKeyExControlW32::UpdateCaretPosition() {
	if (GetFocus() != mhwnd)
		return;

	int x = GetSystemMetrics(SM_CXEDGE) * 2;
	int y = GetSystemMetrics(SM_CYEDGE) * 2;

	HDC hdc = GetDC(mhwnd);
	if (hdc) {
		HGDIOBJ holdFont = SelectObject(hdc, mhfont);
		if (holdFont) {
			SIZE sz;

			if (GetTextExtentPoint32W(hdc, mBuffer.c_str(), mBuffer.size(), &sz))
				x += sz.cx;

			SelectObject(hdc, holdFont);
		}
	}

	SetCaretPos(x, y);
}
Exemplo n.º 12
0
void VDLog(int severity, const VDStringW& s) {
	int strSize = s.size() + 1;

	if (strSize >= 16384) {
		VDASSERT(false);
		return;
	}

	vdsynchronized(g_csLog) {
		for(;;) {
			int currentSize = (g_logTail - g_logHead) & 16383;

			if (currentSize + strSize < 16384)	// NOTE: This means that the last byte in the ring buffer can never be used.
				break;

			while(g_log[g_logHead++ & 16383])
				;

			g_logHead &= 16383;
		}

		const wchar_t *ps = s.data();

		g_log[g_logTail++] = severity;

		for(int i=1; i<strSize; ++i)
			g_log[g_logTail++ & 16383] = *ps++;

		g_log[g_logTail++ & 16383] = 0;

		g_logTail &= 16383;

		VDThreadID currentThread = VDGetCurrentThreadID();
		for(tVDLoggers::const_iterator it(g_loggers.begin()), itEnd(g_loggers.end()); it!=itEnd; ++it) {
			if (!(*it).second || currentThread == (*it).second)
				(*it).first->AddLogEntry(severity, s);
		}
	}
}
Exemplo n.º 13
0
void vdmove<VDStringW>(VDStringW& dst, VDStringW& src) {
	dst.move_from(src);
}
Exemplo n.º 14
0
VDStringW VDFileSplitRoot(const VDStringW& s) {
    return splitimpL(s, VDFileSplitRoot(s.c_str()));
}
Exemplo n.º 15
0
const wchar_t *VDGetDataPath() {
	return g_VDDataPath.c_str();
}
Exemplo n.º 16
0
void VDSubstituteStrings(VDStringW& s) {
	VDStringW::size_type posLast = 0;
	VDStringW::size_type pos = s.find('$');
	if (pos == VDStringW::npos)
		return;

	VDStringW t;

	for(;;) {
		t.append(s, posLast, pos - posLast);

		if (pos == VDStringW::npos || pos+1 >= s.size())
			break;

		wchar_t c = s[pos+1];
		switch(c) {
			case L'b':
				t.append_sprintf(L"%d", version_num);
				break;

			case L'n':
				t.append(VD_PROGRAM_NAMEW);
				break;

			case L'v':
				t.append(VD_PROGRAM_VERSIONW);
				break;

			case L's':
				t.append(VD_PROGRAM_SPECIAL_BUILDW);
				break;

			case L'c':
				t.append(VD_PROGRAM_CONFIGW);
				break;

			case L'C':
				t.append(VD_PROGRAM_GENERIC_CONFIGW);
				break;

			case L'p':
				t.append(VD_PROGRAM_PLATFORM_NAMEW);
				break;

			case L'k':
				#if VD_COMPILER_MSVC >= 1600
					#if VD_CPU_AMD64
						t.append(L"Microsoft Visual Studio 2010 for AMD64");
					#else
						t.append(L"Microsoft Visual Studio 2010 for X86");
					#endif
				#elif VD_COMPILER_MSVC >= 1500
					#if VD_CPU_AMD64
						t.append(L"Microsoft Visual Studio 2008 for AMD64");
					#else
						t.append(L"Microsoft Visual Studio 2008 for X86");
					#endif
				#elif VD_COMPILER_MSVC >= 1400
					#if VD_CPU_AMD64
						#if VD_COMPILER_MSVC_VC8_DDK
							t.append(L"Microsoft Visual C++ 8.0 for AMD64 (DDK version)");
						#elif VD_COMPILER_MSVC_VC8_PSDK
							t.append(L"Microsoft Visual C++ 8.0 for AMD64 (PSDK version)");
						#else
							t.append(L"Microsoft Visual Studio 2005 for AMD64");
						#endif
					#else
						t.append(L"Microsoft Visual Studio 2005 for X86");
					#endif
				#else
					t.append("Unknown compiler");
				#endif
				break;
		}

		posLast = pos + 2;
		pos = s.find('$', posLast);
	}

	s = t;
}
Exemplo n.º 17
0
VDStringW VDFileSplitExtRight(const VDStringW& s) {
    return splitimpR(s, VDFileSplitExt(s.c_str()));
}
Exemplo n.º 18
0
size_t vdhash<VDStringW>::operator()(const VDStringW& s) const {
	return VDHashString32(s.data(), s.length());
}
Exemplo n.º 19
0
void VDLog(int severity, const VDStringW& s) {
	VDLog(severity, s.c_str());
}
Exemplo n.º 20
0
void VDCreateTestPal8Video(VDGUIHandle h) {
	CPUEnableExtensions(CPUCheckForExtensions());

	try {
		tVDInputDrivers inputDrivers;
		std::vector<int> xlat;

		VDGetInputDrivers(inputDrivers, IVDInputDriver::kF_Video);

		const VDStringW filter(VDMakeInputDriverFileFilter(inputDrivers, xlat));

		const VDFileDialogOption opt[]={
			{ VDFileDialogOption::kSelectedFilter },
			0
		};

		int optval[1]={0};

		const VDStringW srcfile(VDGetLoadFileName('pl8s', h, L"Choose source file", filter.c_str(), NULL, opt, optval));

		if (srcfile.empty())
			return;

		IVDInputDriver *pDrv;
		int filtidx = xlat[optval[0] - 1];
		if (filtidx < 0)
			pDrv = VDAutoselectInputDriverForFile(srcfile.c_str(), IVDInputDriver::kF_Video);
		else {
			tVDInputDrivers::iterator itDrv(inputDrivers.begin());
			std::advance(itDrv, filtidx);

			pDrv = *itDrv;
		}

		vdrefptr<InputFile> pIF(pDrv->CreateInputFile(0));

		pIF->Init(srcfile.c_str());

		const VDStringW dstfile(VDGetSaveFileName('pl8d', h, L"Choose destination 8-bit file", L"Audio-video interleaved (*.avi)\0*.avi\0All files\0*.*", L"avi", NULL, NULL));
		if (dstfile.empty())
			return;

		vdrefptr<IVDVideoSource> pVS;
		pIF->GetVideoSource(0, ~pVS);
		IVDStreamSource *pVSS = pVS->asStream();
		const VDPosition frames = pVSS->getLength();

		if (!pVS->setTargetFormat(nsVDPixmap::kPixFormat_XRGB8888))
			throw MyError("Cannot set decompression format to 32-bit.");

		vdautoptr<IVDMediaOutputAVIFile> pOut(VDCreateMediaOutputAVIFile());

		IVDMediaOutputStream *pVSOut = pOut->createVideoStream();

		const VDPixmap& pxsrc = pVS->getTargetFormat();
		const uint32 rowbytes = (pxsrc.w+3) & ~3;

		AVIStreamHeader_fixed hdr;

		hdr.fccType		= 'sdiv';
		hdr.fccHandler	= 0;
		hdr.dwFlags		= 0;
		hdr.wPriority	= 0;
		hdr.wLanguage	= 0;
		hdr.dwScale		= pVSS->getStreamInfo().dwScale;
		hdr.dwRate		= pVSS->getStreamInfo().dwRate;
		hdr.dwStart		= 0;
		hdr.dwLength	= 0;
		hdr.dwInitialFrames = 0;
		hdr.dwSuggestedBufferSize = 0;
		hdr.dwQuality = -1;
		hdr.dwSampleSize = 0;
		hdr.rcFrame.left	= 0;
		hdr.rcFrame.top		= 0;
		hdr.rcFrame.right	= (short)pxsrc.w;
		hdr.rcFrame.bottom	= (short)pxsrc.h;

		pVSOut->setStreamInfo(hdr);

		vdstructex<BITMAPINFOHEADER> bih;

		bih.resize(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*252);

		bih->biSize = sizeof(BITMAPINFOHEADER);
		bih->biWidth = pxsrc.w;
		bih->biHeight = pxsrc.h;
		bih->biPlanes = 1;
		bih->biBitCount = 8;
		bih->biCompression = BI_RGB;
		bih->biSizeImage = rowbytes*pxsrc.h;
		bih->biXPelsPerMeter = 0;
		bih->biYPelsPerMeter = 0;
		bih->biClrUsed = 252;
		bih->biClrImportant = 252;

		RGBQUAD *pal = (RGBQUAD *)((char *)bih.data() + sizeof(BITMAPINFOHEADER));
		for(int i=0; i<252; ++i) {
			pal[i].rgbRed		= (BYTE)((i/42)*51);
			pal[i].rgbGreen		= (BYTE)((((i/6)%7)*85)>>1);
			pal[i].rgbBlue		= (BYTE)((i%6)*51);
			pal[i].rgbReserved	= 0;
		}

		pVSOut->setFormat(bih.data(), bih.size());

		pOut->init(dstfile.c_str());

		ProgressDialog dlg((HWND)h, "Processing video stream", "Palettizing frames", (long)frames, true);

		vdblock<uint8> outbuf(rowbytes * pxsrc.h);

		const vdpixsize w = pxsrc.w;
		const vdpixsize h = pxsrc.h;

		try {
			for(uint32 frame=0; frame<frames; ++frame) {
				pVS->getFrame(frame);

				const uint8 *src = (const uint8 *)pxsrc.data;
				ptrdiff_t srcpitch = pxsrc.pitch;
				uint8 *dst = &outbuf[rowbytes * (pxsrc.h - 1)];

				for(int y=0; y<h; ++y) {
					const uint8 *dr = ditherred[y & 15];
					const uint8 *dg = dithergrn[y & 15];
					const uint8 *db = ditherblu[y & 15];

					for(int x=0; x<w; ++x) {
						const uint8 b = (uint8)((((src[0] * 1286)>>8) + dr[x&15]) >> 8);
						const uint8 g = (uint8)((((src[1] * 1543)>>8) + dg[x&15]) >> 8);
						const uint8 r = (uint8)((((src[2] * 1286)>>8) + db[x&15]) >> 8);
						src += 4;

						dst[x] = (uint8)(r*42 + g*6 + b);
					}

					vdptrstep(dst, -(ptrdiff_t)rowbytes);
					vdptrstep(src, srcpitch - w*4);
				}

				pVSOut->write(AVIOutputStream::kFlagKeyFrame, outbuf.data(), outbuf.size(), 1);

				dlg.advance(frame);
				dlg.check();
			}
		} catch(const MyUserAbortError&) {
		}

		pVSOut->flush();
		pOut->finalize();

	} catch(const MyError& e) {
		e.post((HWND)h, g_szError);
	}
}
Exemplo n.º 21
0
VDStringW VDFileSplitPathLeft (const VDStringW& s) {
    return splitimpL(s, VDFileSplitPath(s.c_str()));
}
Exemplo n.º 22
0
bool VDFile::open_internal(const char *pszFilename, const wchar_t *pwszFilename, uint32 flags, bool throwOnError) {
	close();

	mpFilename = _wcsdup(VDFileSplitPath(pszFilename ? VDTextAToW(pszFilename).c_str() : pwszFilename));
	if (!mpFilename) {
		if (!throwOnError)
			return false;
		throw MyMemoryError();
	}

	// At least one of the read/write flags must be set.
	VDASSERT(flags & (kRead | kWrite));

	DWORD dwDesiredAccess = 0;

	if (flags & kRead)  dwDesiredAccess  = GENERIC_READ;
	if (flags & kWrite) dwDesiredAccess |= GENERIC_WRITE;

	// Win32 docs are screwed here -- FILE_SHARE_xxx is the inverse of a deny flag.

	DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
	if (flags & kDenyRead)	dwShareMode = FILE_SHARE_WRITE;
	if (flags & kDenyWrite) dwShareMode &= ~FILE_SHARE_WRITE;

	// One of the creation flags must be set.
	VDASSERT(flags & kCreationMask);

	DWORD dwCreationDisposition;

	uint32 creationType = flags & kCreationMask;

	switch(creationType) {
	case kOpenExisting:		dwCreationDisposition = OPEN_EXISTING; break;
	case kOpenAlways:		dwCreationDisposition = OPEN_ALWAYS; break;
	case kCreateAlways:		dwCreationDisposition = CREATE_ALWAYS; break;
	case kCreateNew:		dwCreationDisposition = CREATE_NEW; break;
	case kTruncateExisting:	dwCreationDisposition = TRUNCATE_EXISTING; break;
	default:
		VDNEVERHERE;
		return false;
	}

	VDASSERT((flags & (kSequential | kRandomAccess)) != (kSequential | kRandomAccess));

	DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL;

	if (flags & kSequential)	dwAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
	if (flags & kRandomAccess)	dwAttributes |= FILE_FLAG_RANDOM_ACCESS;
	if (flags & kWriteThrough)	dwAttributes |= FILE_FLAG_WRITE_THROUGH;
	if (flags & kUnbuffered)	dwAttributes |= FILE_FLAG_NO_BUFFERING;

	VDStringA tempFilenameA;
	VDStringW tempFilenameW;

	if (IsWindowsNT()) {
		if (pszFilename) {
			tempFilenameW = VDTextAToW(pszFilename);
			pwszFilename = tempFilenameW.c_str();
			pszFilename = NULL;
		}
	} else {
		if (pwszFilename) {
			tempFilenameA = VDTextWToA(pwszFilename);
			pszFilename = tempFilenameA.c_str();
			pwszFilename = NULL;
		}
	}

	if (pszFilename)
		mhFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
	else {
		if (!IsHardDrivePath(pwszFilename))
			flags &= ~FILE_FLAG_NO_BUFFERING;

		mhFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
	}

	DWORD err = GetLastError();

	// If we failed and FILE_FLAG_NO_BUFFERING was set, strip it and try again.
	// VPC and Novell shares sometimes do this....
	if (mhFile == INVALID_HANDLE_VALUE && err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND) {
		if (dwAttributes & FILE_FLAG_NO_BUFFERING) {
			dwAttributes &= ~FILE_FLAG_NO_BUFFERING;
			dwAttributes |= FILE_FLAG_WRITE_THROUGH;

			if (pszFilename)
				mhFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
			else
				mhFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);

			err = GetLastError();
		}
	}

	// INVALID_HANDLE_VALUE isn't NULL.  *sigh*

	if (mhFile == INVALID_HANDLE_VALUE) {
		mhFile = NULL;

		if (!throwOnError)
			return false;

		throw MyWin32Error("Cannot open file \"%ls\":\n%%s", err, mpFilename.get());
	}

	mFilePosition = 0;
	return true;
}
Exemplo n.º 23
0
bool VDDialogEditAccelerators::OnCommand(uint32 id, uint32 extcode) {
	if (id == IDC_FILTER) {
		if (extcode == EN_CHANGE) {
			VDStringA s("*");
			s += VDTextWToA(GetControlValueString(id)).c_str();
			s += '*';

			RefilterCommands(s.c_str());
			return true;
		}
	} else if (id == IDC_ADD) {
		VDUIAccelerator accel;

		int selIdx = LBGetSelectedIndex(IDC_AVAILCOMMANDS);

		if ((size_t)selIdx < mFilteredCommands.size()) {
			const VDAccelToCommandEntry *ace = mFilteredCommands[selIdx];

			if (mpHotKeyControl) {
				mpHotKeyControl->GetAccelerator(accel);

				// Look for a conflicting command.
				for(BoundCommands::iterator it(mBoundCommands.begin()), itEnd(mBoundCommands.end()); it != itEnd; ++it) {
					BoundCommand *obc = *it;

					if (obc->mAccel == accel) {
						VDStringW keyName;
						VDUIGetAcceleratorString(accel, keyName);

						VDStringA msg;
						msg.sprintf("The key %ls is already bound to %hs. Rebind it to %hs?", keyName.c_str(), obc->mpCommand, ace->mpName);

						if (IDOK != MessageBox(mhdlg, msg.c_str(), g_szWarning, MB_OKCANCEL | MB_ICONEXCLAMATION))
							return true;

						mBoundCommands.erase(it);
						obc->Release();
					}
				}

				vdrefptr<BoundCommand> bc(new_nothrow BoundCommand);
				
				if (bc) {
					bc->mpCommand = ace->mpName;
					bc->mCommandId = ace->mId;
					bc->mAccel = accel;

					mBoundCommands.push_back(bc.release());
					RefreshBoundList();
				}
			}
		}

		return true;
	} else if (id == IDC_REMOVE) {
		int selIdx = mListViewBoundCommands.GetSelectedIndex();

		if ((unsigned)selIdx < mBoundCommands.size()) {
			BoundCommand *bc = mBoundCommands[selIdx];

			mBoundCommands.erase(mBoundCommands.begin() + selIdx);

			bc->Release();

			RefreshBoundList();
		}

		return true;
	} else if (id == IDC_RESET) {
		if (IDOK == MessageBox(mhdlg, "Really reset?", g_szWarning, MB_OKCANCEL | MB_ICONEXCLAMATION))
			LoadTable(mBoundCommandsDefault);

		return true;
	}

	return false;
}