Sign in to follow this  
ts-m

Win32: Mouse capture problem

Recommended Posts

ts-m    122
Hi, I have a problem with losing the mouse capture in certain situations. I want to use the edit control when the user double clicks an item in a listbox. Left and right mouse button clicks outside the edit control should end the editing. The problem: if I double click in the listbox, it appears and when I right click it will be closed. So far so good, but when I double click an item, then select something in the edit control and right click outside the edit control it doesn't close as it's lost the mouse capture (WM_CAPTURECHANGED messages are sent and GetCapture() returns 0). Why does it happen and what's the best way to handle it?
WNDPROC oldproc, oldeditproc;
HWND hedit = NULL;

void EndEdit()
{
	MSG msg;
	if (!hedit)
		return;
	ReleaseCapture();
	while (PeekMessage(&msg, hedit, 0, 0, PM_REMOVE))
		;
	DestroyWindow(hedit);
	hedit = NULL;
}

LRESULT CALLBACK TestEditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	POINT pt;
	RECT r;
	switch (message) {
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
			pt.x = LOWORD(lParam);
			pt.y = HIWORD(lParam);
			GetClientRect(hWnd, &r);
			if (!PtInRect(&r, pt)) {
				EndEdit();
				return 0;
			}
			break;
		case WM_KILLFOCUS:
			EndEdit();
			break;
		case WM_DESTROY:
			SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)oldeditproc);
			break;
	}
	return CallWindowProc(oldeditproc, hWnd, message, wParam, lParam); 
}

void StartEdit(HWND hWnd, int x, int y)
{
	int cursel = SendMessage(hWnd, LB_GETCURSEL, 0, 0);
	RECT r;
	HFONT hfont;
	if (hedit || cursel < 0)
		return;
	SendMessage(hWnd, LB_GETITEMRECT, cursel, (LPARAM)&r);
	hedit = CreateWindow("EDIT", "Test", WS_BORDER | WS_CHILD | WS_VISIBLE | ES_WANTRETURN, r.left, r.top, r.right-r.left, r.bottom-r.top, hWnd, NULL, hInst, NULL);
	oldeditproc = (WNDPROC)SetWindowLongPtr(hedit, GWL_WNDPROC, (LONG_PTR)TestEditProc);
	hfont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0);
	SendMessage(hedit, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
	SetWindowText(hedit, "testtesttest");
	SetFocus(hedit);
	SetCapture(hedit);
}

LRESULT CALLBACK TestLBProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) {
		case WM_LBUTTONDBLCLK:
			StartEdit(hWnd, LOWORD(lParam), HIWORD(lParam));
			return 0;
	}
	return CallWindowProc(oldproc, hWnd, message, wParam, lParam); 
}

INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	HWND hwnd;

	switch (message)
	{
	case WM_INITDIALOG:
		hwnd = GetDlgItem(hDlg, IDC_TESTLB);
		oldproc = (WNDPROC)SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)TestLBProc);
		SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)"line 1");
		SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)"line 2");
		SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)"line 3");
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	case WM_DESTROY:
		hwnd = GetDlgItem(hDlg, IDC_TESTLB);
		SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)oldproc);
		break;
	}
	return (INT_PTR)FALSE;
}

IDD_TESTDLG DIALOGEX 0, 0, 300, 100
STYLE DS_LOCALEDIT | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_VISIBLE
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "Test Dialog"
FONT 8, "Courier New", 0, 0, 0x0
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,20,3,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,230,3,50,14
    LISTBOX         IDC_TESTLB,10,20,280,80,LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT
END

Share this post


Link to post
Share on other sites
Will-O    122
The functions r.left, r.top etc...
- do they return values or references?
- use the same logic to the data being passed to CreateWindow( ... )

If you pass by value the calculations will be done but data will be lost because the operations are performed on copies, where as to pass data by reference would mean you're altering and using the very values you take into a function - does that make any sense?

Also you're doing this
StartEdit( ... int x, int y)
- not (... int &i, int &j)


This might also be possible...

RECT *r = new RECT(...) // whatever goes in the constructor


... then in CreateWindow:

CreateWindow( ... &r.left, &r.top ...etc )// reference the pointer to the object

Share this post


Link to post
Share on other sites
ts-m    122
That definitely is not the problem, the edit control is created just fine and x/y values are not relevant for the mouse capture problem. Btw. plain C has no call by reference, you would need a pointer, but I don't need the values outside the function.

The problem is that when I select something in the edit control, the mouse capture, that I aquired with SetCapture(hedit), so mouse messages outside the control are being sent to the edit control, is lost without having ReleaseCapture() called in my code.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this