I need to handle the event of changing caret pos in edit control (not richedit control).
I know how to handle event of changing text in the edit, but I don't know how to handle the event of changing caret position in edit control.
I hope someone can help me. Thanks to all.
Once I have done this for displaying line number on status bar by subclassing Edit Control (SetWindowLongPtr) and sending message to parent window on messages that could potentialy move caret.
#define CARET_MOVED_COMMAND_ID 50001
WNDPROC OriginalEditProc = 0;
HWND OriginalEditParent = 0;
LRESULT WINAPI MyEditProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
// Handle message by original edit control procedure.
LRESULT result = CallWindowProc( OriginalEditProc, hwnd, msg, wparam, lparam );
switch ( msg )
{
case WM_CHAR:
case WM_KEYDOWN:
case WM_KEYUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
case EM_SETSEL:
// And maybe others...
// Notify parent of posible caret move.
// You can recreate EN_SELCHANGE behavior, I have been too lazy
// and was just sending command (like from menu).
SendMessage( OriginalEditParent, WM_COMMAND, MAKEWPARAM( CARET_MOVED_COMMAND_ID, 0 ), 0 );
break;
}
return result;
}
// ---- In initialization code ----
// subclass
OriginalEditParent = MyMainWindow;
OriginalEditProc = (WNDPROC) SetWindowLongPtr( EditBoxHwnd, GWLP_WNDPROC, (LONG_PTR)MyEditProc );
// ---- In parent window message handling ----
case WM_COMMAND:
switch ( LOWORD( wparam ) )
{
case CARET_MOVED_COMMAND_ID:
{
// You sould execute this code in EN_CHANGE handler,
// so it can handle cuting, pasting, undo etc.
int line = (int) SendMessage( EditBoxHwnd, EM_LINEFROMCHAR, -1, 0 );
// Update status bar. Could be optimized by remembering
// displayed line number and updating only when it changes.
wchar_t buff[50];
wsprintf( buff, L"%d", line );
SendMessage( StatusBarHwnd, SB_SETTEXT, MAKEWPARAM( 1, SBT_NOBORDERS ), (LPARAM) buff );
}
break;
}
break;
You can set the position of the caret in the edit control by sending an EM_SETSEL message, that selects from the desired position to the same position:
SendMessage(hWnd, EM_SETSEL, pos, pos);
Getting the position of the caret requires to send an EM_GETSEL message. Note that the if the start and the end are at same position, you know the precise position of the caret. If there's a difference, it means that text is selected, and you could take as position the last one.
There's no specific event for tracking only caret/selection changes. In your event loop, you need to catch key press and mouse events, and check if they changed the position. But wouldn't it possible for you to read the caret position only when you need it instead of actively tracking it ?
Related
I'm just starting to experiment with win32 and I've run into a problem.
BOOL CALLBACK UnsavedChangesProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND dHandle = GetActiveWindow();
switch (msg)
{
case WM_INITDIALOG:
MessageBox(NULL, "In InitDialog", 0, 0);
SetDlgItemText(dHandle, 1004, ("There are unsaved changes to \""));
char error[10];
sprintf_s(error, "%d", GetLastError());
MessageBox(NULL, error, 0, 0);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDSAVE:
DoFileSave(hwnd);
EndDialog(hwnd, TRUE);
PostQuitMessage(0);
break;
case IDEXIT:
EndDialog(hwnd, TRUE);
PostQuitMessage(0);
break;
}
break;
case WM_CLOSE:
EndDialog(hwnd, FALSE);
break;
default:
return FALSE;
}
return TRUE;
}
The GetLastError() returns 1421, control ID not found, but the ID (1004) definitely corresponds to the static control I'm trying to alter. I've also tried calling the function with the control name (IDC_STATIC_UNSAVED) with no luck. The strange part is that if I move the function call to where dHandle is declared (or get rid of dHandle and just call GetActiveWindow() inside the function there) the text is changed but it flickers because the function is being called every time the message loop iterates.
Is there a simple reason that this shouldn't work that I'm missing?
Any help will be appreciated.
Edit: Here is an image of the Resource Symbols: Resource Symbols
And here is an image of the Dialog Template: Dialog Template
Note that all of the other controls work as expected.
The dialog window is passed to your handler in the hwnd parameter. There is no need to call GetActiveWindow() - in fact, that will give you the HWND of another window when the dialog is not the active window.
So, replace
HWND dHandle = GetActiveWindow();
SetDlgItemText(dHandle, 1004, ("There are unsaved changes to \""));
with
SetDlgItemText(hwnd, 1004, "There are unsaved changes to \"");
I agree with Sid S.
Besides changing the first parameter of SetDlgItemText from dHandle to hwnd, I would also suggest using IDC_STATIC_UNSAVED instead of the hardcoded value 1004. So, the SetDlgItemText() call becomes:
SetDlgItemText(hwnd, IDC_STATIC_UNSAVED, ("There are unsaved changes to \""));
I have a main window in a process that is not owned by the program I'm creating. I'm using a Windows Hook to inject a DLL into this process for the purpose of adding a child window to this main window.
My end goal was to create a WS_EX_LAYERED window that allows me to create an internal colored border but allow the center portion to be transparent and allow mouse clicks through. This part works perfectly.
WNDCLASS wndClass = {};
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = OverlayProc;
wndClass.hInstance = g_TargetInstance;
wndClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 255, 255));
wndClass.lpszClassName = "OVERLAY";
RegisterClass(&wndClass);
g_Window = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, "OVERLAY", nullptr,
WS_CHILDWINDOW, rect.left, rect.top, rect.right+1, rect.bottom+1, data->hwnd, nullptr, g_TargetInstance, nullptr);
SetLayeredWindowAttributes(g_Window, RGB(0, 255, 255), 0, LWA_COLORKEY);
ShowWindow(g_Window, SW_SHOW);
UpdateWindow(g_Window);
The 2nd part to this is a I wanted to conditionally block all mouse input to the parent window. I couldn't do this with the transparent portion of the WS_EX_LAYERED window so I tried creating a 2nd transparent STATIC control as a child of the main window but this doesn't block mouse input either.
I'm also sending simulated mouse clicks to the parent window through calls to PostMessage, passing WM_LBUTTONDOWN and WM_LBUTTONUP. How could I block all mouse input to the parent window via a transparent window?
It appears this is not possible to do with a simple transparent window drawn over sibling controls. What I ended up doing was using SetWindowHookEx to add a WH_GETMESSAGE hook into the process from which I use to replace the main window's WndProc function and intercept mouse messages. I tag my simulated mouse messages with a specific value in the wParam argument so the proc will now it was simulated and removes that value, passing it along to the parent window.
If it does not detect my "tag" value in the click message, it will swallow the mouse message and not pass it along to the original WndProc function.
Injected WndProc replacement
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
wParam -= 11141008;
if (wParam != MK_LBUTTON && !g_Paused)
return 0;
break;
case WM_LBUTTONUP:
wParam -= 11141008;
if (wParam != 0 && !g_Paused)
return 0;
break;
case WM_MOUSEHOVER:
case WM_MOUSEMOVE:
if (!g_Paused)
return 0;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Snippet from Windows Hook function
//...
switch (data->message)
{
case (WM_USER + 1):
{
g_Paused = FALSE;
//...
SetWindowSubclass(data->hwnd, WndProc, 1, 0);
break;
}
case (WM_USER + 2):
{
RemoveWindowSubclass(data->hwnd, WndProc, 1);
//...
break;
}
}
//...
The code inside the window hook function is used to subclass the main process window and inject my own WndProc function which in turn processes mouse input the way I want.
This is the code used to "simulate" mouse clicks without physically clicking in the window. Note the added value to wParam to identify this click as simulated and not generated by the user.
void Window::LeftClick(DWORD x, DWORD y, DWORD delayMillis)
{
LPARAM lparam = MAKELPARAM(x, y);
lock_guard<mutex> lock(this->m_ClickMutex);
PostMessage(this->m_Window, WM_LBUTTONDOWN, 11141008 + MK_LBUTTON, lparam);
this_thread::sleep_for(std::chrono::milliseconds(delayMillis));
PostMessage(this->m_Window, WM_LBUTTONUP, 11141008, lparam);
}
Also, just for the person in the comments who was ridiculing my choice of the word simulated and the added criticism for using PostMessage to simulate keyboard input, here is my keyboard input test method which (for my purposes) works flawlessly and very reliably
void GameWindow::KeyPress(UINT vkCode) const
{
UINT scanCode = MapVirtualKey(vkCode, MAPVK_VK_TO_VSC);
LPARAM lparam1 = MAKELPARAM(1, scanCode);
LPARAM lparam2 = MAKELPARAM(1, 0xC000 | scanCode);
PostMessage(this->m_Window, WM_KEYDOWN, vkCode, lparam1);
this_thread::sleep_for(chrono::milliseconds(25));
PostMessage(this->m_Window, WM_KEYUP, vkCode, lparam2);
}
I'm asked to add new feature to an existing program. The program consists of a dialog without title/border. I need couple of things:
When the user simply clicks inside the dialog area, just close it;
Move the dialog when the user mouse-down inside its area and drag
Here's what I found so far:
void MyDialog::onMessageReceived(UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_LBUTTONDOWN:
lastX=LOWORD(lParam);
lastY=HIWORD(lParam);
SendMessage(DlgHandle, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
break;
case WM_LBUTTONUP:
if (LOWORD(lParam)==lastX && HIWORD(lParam)==lastY)
onKillButtonClick();
break;
}}
EDIT:
This function is called in this way:
INT_PTR CALLBACK MyDialog::dialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
_this->onMessageReceived(uMsg, wParam, lParam);
}
Moving the window works very well, but looks like the WM_LBUTTONUP event is lost. I had to click twice to get it fired.
Hope someone can help me...
EDIT:
Using Spy++ I saw that WM_LBTTONUP is fired, but immediately after a new WM_NCLBUTTONDOWN is emitted.
First, I agree with Michael Walz - this is a very confusing behavior: the processing of the mouse up event depends on whether or not it has moved... What if it moved just a little bit? I would much rather dismiss this dialog with a different action - click on the icon, right-click, etc.
However, the correct way to let the user move your captionless window is to process WM_NCHITTEST message and return HTCAPTION:
case WM_NCHITTEST:
SetWindowLong(hDlg, DWL_MSGRESULT, HTCAPTION);
return HTCAPTION;
Unfortunately, Windows then will take over all mouse events, so, as you observed, you would never get WM_LBUTTONUP. You have an option to set a short timer and see if the user started to move your window; cancel it when you get WM_ENTERSIZEMOVE message. If that timer fires - close your window. Yes, it's also awkward, but no more thatn your proposal.
Another way is to handle the move yourself:
static bool bDragging(false), bMoved(false);
static POINT pt1 = {}, pt2 = {};
static RECT r;
switch (message)
{
case WM_LBUTTONDOWN:
GetWindowRect(hDlg, &r);
pt1 = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen(hDlg, &pt1);
bDragging = true;
bMoved = false;
break;
case WM_MOUSEMOVE:
if (bDragging)
{
pt2 = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen(hDlg, &pt2);
if (pt2.x != pt1.x || pt2.y != pt1.y)
{
OffsetRect(&r, pt2.x - pt1.x, pt2.y - pt1.y);
SetWindowPos(hDlg, 0, r.left, r.top, 0, 0, SWP_NOSIZE);
pt1 = pt2;
bMoved = true;
}
}
break;
case WM_LBUTTONUP:
bDragging = false;
if (!bMoved)
PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0);
bMoved = false;
break;
}
return (INT_PTR)FALSE;
I've just created multiple edit boxes (11x11 controls) based on this article:
https://msdn.microsoft.com/en-us/library/windows/desktop/hh298433%28v=vs.85%29.aspx
Well, not exactly same, but I used the code in case WM_CREATE: block to create huge number of controls.
I use this dialog process on the parent window:
INT_PTR CALLBACK StartupDialogProc(HWND dialog, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg){
case WM_INITDIALOG:
Init_Startup(dialog);
return 1;
/*
case EN_CHANGE:
case WM_CTLCOLOREDIT:
{
HDC hdC = (HDC)wParam;
COLORREF crColorBackground = RGB(255,0,0);
if (crColorBackground)
SetBkColor(hdC, crColorBackground);
SetTextColor( hdC, RGB(12,112,212) );
SetBkMode( hdC, TRANSPARENT );
RECT rect;
GetClientRect( (HWND)lParam, &rect );
HBRUSH hBrush = CreateSolidBrush( RGB(209,209,209) );
//FrameRect( hdC, &rect, hBrush );
Rectangle( hdC, (int)rect.left, (int)rect.top, (int)rect.right, (int)rect.bottom );
DeleteObject( hBrush );
LOGBRUSH lb;
lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(249,249,249);
lb.lbHatch = 0;
CreateBrushIndirect(&lb); // LRESULT
// GetStockObject(NULL_BRUSH);
return 1;
}
break;
*/
case WM_DESTROY:
setts.options.page = GetDlgItemInt(dialog, IDC_O_STARTUP_PAGE, NULL, FALSE);
setts.options.recent = GetDlgItemInt(dialog, IDC_O_STARTUP_RECENT, NULL, FALSE);
break;
case WM_CLOSE:
EndDialog(dialog, FALSE);
break;
case WM_COMMAND:
if (wParam == IDOK) {
EndDialog(dialog, TRUE);
return 0;
}
}
return 0;
}
There is few things unclear to me:
1) if I would like to change color of border for all edit controls from id 5001 to id 5121, how to do that? To me, the commented code does not work (when would it be uncommented). It looks like I have this in incorrect place.
2) how correctly create the dialog processes to all the controls? Because there is big number and could be yet few times higher, should I just call a loop from 5001 to id 5121 and call the function:
INT_PTR CALLBACK EditDlgProc(HWND dialog, UINT msg, WPARAM wParam, LPARAM lParam) - that won't work, because every function would need to have different name.
To change the border color of edit control, you have to subclass the edit control and override WM_NCPAINT. That's a little advanced, and you don't really need it. You can just use WS_EX_CLIENTEDGE flag:
CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT" ...
Also make sure project manifest is setup so you get modern window's look.
This would be an error if it had not been commented out:
case EN_CHANGE:
case WM_CTLCOLOREDIT:
Each case should end in break; or return 0;
Moreover, WM_CTLCOLOREDIT should return a brush which was created on heap. It should not return 1. See documentation :
There are also other errors in that section, you should just get rid of that. See this example for painting.
I'm developing an application targeted to a POCKET PC 2003 (Windows CE 4.2) device using C++ and native WINAPI (i.e. no MFC or the like). In it I have a single-line edit control which part of the main window (not a dialog); hence the normal behaviour of Windows when pressing ENTER is to do nothing but beep.
I've subclassed the window procedure for the edit control to override the default behaviour using the following code:
LRESULT CALLBACK Gui::ItemIdInputProc( HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam ) {
switch ( message ) {
case WM_KEYDOWN :
switch ( wParam ) {
case VK_RETURN :
addNewItem();
return 0;
}
}
return CallWindowProc( oldItemIdInputProc_, hwnd, message, wParam, lParam );
}
This causes the equivalent behaviour as pressing the 'OK' button.
Now to the problem at hand: this window procedure does not override the default behaviour of making a beep. I suspect that there must be some other message or messages which are triggered as ENTER is pressed that I fail to capture; I just can't figure out which. I really want to stop the device from beeping as it messes up other sounds that are played in certain circumstances when an item collision occurs, and it is crucial that the user is alerted about that.
Thanks in advance.
After spewing all messages to a log file, I finally managed to figure out which message was causing the beeping - WM_CHAR with wParam set to VK_RETURN. Stopping that message from being forwarded to the edit control stopped the beeping. ^^
The final code now reads:
LRESULT CALLBACK Gui::ItemIdInputProc( HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam ) {
switch ( message ) {
case WM_CHAR :
switch ( wParam ) {
case VK_RETURN :
addNewItem();
return 0;
}
}
return CallWindowProc( oldItemIdInputProc_, hwnd, message, wParam, lParam );
}
Had the same issue but thanks to you, I finally managed to turn the beep off.
// Run the message loop. It will run until GetMessage() returns 0
while(GetMessage (&messages, NULL, 0, 0)) {
if(messages.message == WM_KEYDOWN && messages.wParam == VK_RETURN) {
sendChatMessage("sample text");
continue;
}
// Translate virtual-key messages into character messages
TranslateMessage(&messages);
// Send message to WindowProcedure
DispatchMessage(&messages);
}
I guess the trick was to not let execute those two statements
I had the same problem but with my Rich Edit (using also subclassed callback). This side helped me lot but sadly the solution from gablin didn't work for me. Somehow I couldn't get the VK_RETURN from the WM_CHAR. But from the WM_KEYDOWN message I can:). I also found out that in my case the beep comes only if the rich edit use not the ES_MULTILINE style.
So finaly this is my working solution in the Callback to dissable the beep if return key is pressed. Maybe it can still help someone who has the same problem :)
switch (message){
case (WM_KEYDOWN) : {
switch (wParam) {
case VK_RETURN:
if ((GetWindowLong(this_editbox->getHandle(), GWL_STYLE) & ~ES_MULTILINE)){ //Only dissable return key if the rich edit is a single line rich edit
//Do something you want to do here if return key was pressed for ex. delete text with SetWindowTextA(hRichEdit, ""); after reading
return 0;// stop beep by blocking message
}
}
break;
}
default: break;
}
Try also handling the WM_KEYUP and return 0 for VK_RETURN there as well - Windows non-CE also beeps if you don't handle the key event in both down and up.
In a Windows desktop app, I was also getting annoying beeps when hitting the left arrow key when the insertion point was to the left of the first character, or hitting the right arrow key when the insertion point was positioned after the last character. This code handles the return key as well as the left and right arrow keys to stop the beep.
This is in a Windows desktop app, so I'm not hearing a beep for WM_CHAR + VK_RETURN; you'll have to try this code yourself on CE to see if it works well for you.
bool processKeystroke = true;
if (message == WM_CHAR || message == WM_KEYDOWN || message == WM_KEYUP) {
DWORD start = 0;
DWORD end = 0;
switch (wParam) {
case VK_RETURN:
if ((GetWindowLong(hwnd, GWL_STYLE) & ~ES_MULTILINE)) {
processKeystroke = false;
}
break;
case VK_LEFT:
{
::SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
if (start == 0 && end == 0) {
processKeystroke = false;
}
}
break;
case VK_RIGHT:
{
LPARAM charCount = ::SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
::SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
if (wParam == VK_RIGHT && start == charCount && end == charCount) {
processKeystroke = false;
}
}
break;
}
if (processKeystroke) {
lResult = DefSubclassProc(hwnd, message, wParam, lParam);
}
}
}
In a Windows desktop app, I got the same problem while handling VK_TAB in WM_GETDLGCODE.
so I found the following solution.
SystemParametersInfo(SPI_SETBEEP, FALSE, NULL, 0); // turn of the beep
// do somthing ... //
SystemParametersInfo(SPI_SETBEEP, TRUE, NULL, 0); // turn on the beep