News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Interacting with the keyboard with edit window

Started by allynm, January 04, 2010, 07:21:58 PM

Previous topic - Next topic

allynm

Hi everyone,

I am trying to write a WINDOWS app (not console!) that allows the user to enter sequentially a set of floats.  The window I  am using is an edit window.  I am struggling with how to make the interactiive data entry work.  What I would like to do is have the user go to a menu and select "data entry".  Then the edit window opens.  The user enters a float, hits "enter", the old entry disappears (gets cleared) and then the user enters another float.  So far I have gotten the menu up and running, and I open up an edit window successfully.  What I don't know how to do is to get the "enter key" logic to work.

I'm happy to send the code along if that would be of help.

I've looked thru the MASM32 examples and haven't found one to use as a model.  There have been some related Forum threads, but I haven't found one that explains how to use the "enter" key to make the interactivity work.

Any advide on this cold, cold day in Pennsylvania would be greatly appreciated.

Regards,

Mark Allyn

jj2007

Mark,
You need to subclass your edit control, and catch the WM_KEYUP message. Here is a snippet.
Hope this helps,
Jochen


; CASE WM_CREATE ; place this part in your WndProc
; invoke CreateWindowEx, ....
invoke SetWindowLong, hEdit, ; use just behind the line
GWL_WNDPROC, SubEdit ; where you create your control
mov opEdit, eax ; the old pointer, see below

SubEdit PROTO:DWORD,:DWORD,:DWORD,:DWORD ; move this line to proto section
opEdit dd ? ; move this line to data? section

SubEdit proc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
SWITCH uMsg

CASE WM_KEYDOWN
; return 0

CASE WM_CHAR
; return 0

CASE WM_KEYUP
.if wParam==VK_RETURN
... get the text, clear the control
.endif

; return 0

DEFAULT
; xor eax, eax

ENDSW
  invoke CallWindowProc, opEdit, hwnd, uMsg, wParam, lParam
  ret
SubEdit endp

allynm

Hi JJ-

I'm such a newcomer to asm/Masm that I was and am completely unfamiliar with subclassing technique.  I have seen it referred to, but didn't make the connection between it and my problem.  I knew that I was having trouble in the WndProc, and I could see that I needed somehow to use VK_RETURN, but there ended my thinking.

This is a great help.  I will try it out shortly. 

Regards,
Mark

allynm

Hi again JJ-

I have inserted your snippet into my program.  Because it doesn't function as expected, I have a further question. 

Does it matter if the edit control is created as a result of the user pressing a menuitem rather than as a result of receiving a WM_CREATE message.  The answer to this would be very helpful since I have several questions tied up in this question.

But for the moment, it would be most helpful if you could clarify just this point.

Thanks again,
Mark

WryBugz

    .if uMsg == WM_KEYUP

      .if wParam == VK_F1
      .endif
      .if wParam == 27             ; Escape exit proggy
        invoke PostQuitMessage,NULL
        return 0
      .endif
      .if wParam == 13                 ; enter
        invoke SendMessage,hEdit,WM_GETTEXT,sizeof buffer,pBuf
        invoke FpuAtoFL, ADDR gvar, NULL, DEST_FPU
        return 0
      .endif
    .endif

    invoke CallWindowProc,lpfnScriptProc,hCtl,uMsg,wParam,lParam

    ret


Same thing another wording. You use the SendMessage Call to get your ascii float. Then you can use the FPU Lib conversions to make it a number. Then you can play with it. The FPU conversion functions are under the QEditor help file listings.
How you create the edit control does not matter. You just have to make the call to SetWindowLong so that you can process the messages from the control.

If you look at the richedit example, it does all this.

jj2007

Quote from: allynm on January 04, 2010, 09:25:21 PM
Does it matter if the edit control is created as a result of the user pressing a menuitem

Mark,
This sound like trouble. WryBugz is correct, it should not make a difference. Normally, an edit control is created at program start, in the WM_CREATE handler. Afterwards, you can show/hide it with ShowWindow. Your wording sounds, ehm, non-standard :wink

Could you post your code please? If it has confidential elements, strip them or PM me.

Slugsnack

Actually there is a better way to process the return button ^_^

http://support.microsoft.com/kb/102589

Process WM_COMMAND when the low word of wParam is IDOK. Then you can use GetFocus() to get the current active control if you wish.

allynm

#7
Hi JJ and Wrybugz and others,

Here is the code.  Nothing is proprietary about it.  You will note that I have incorporated JJ's subclassing method.  The code "works" but I am still concerned about creating the edit window off of the menu item rather than in a switch statement with WM_CREATE.


include \masm32\include\masm32rt.inc

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
SubEdit PROTO :DWORD, :DWORD, :DWORD, :DWORD

.data
ClassName db "SimpleWinClass",0
AppName  db "Statistics Calculator",0
EditClassName db "edit",0
DataName db "Data InPut",0
MenuName db "Menu_0",0
Data_string db "You selected DATA menu item",0
Compute_string db "You selected COMPUTE menu item",0
Goodbye_string db "See you again, bye",0
GotIt_string db "Got the text", 0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndEdit HWND ?
buffer db 8 dup(?)
opEdit DWORD ?

.const
IDM_DATA equ 1
IDM_COMPUTE equ 2
IDM_EXIT equ 3
EditID equ 4

.code
start:
invoke GetModuleHandle, NULL
mov    hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov   wc.cbSize,SIZEOF WNDCLASSEX
mov   wc.style, CS_HREDRAW or CS_VREDRAW
mov   wc.lpfnWndProc, OFFSET WndProc
mov   wc.cbClsExtra,NULL
mov   wc.cbWndExtra,NULL
push  hInst
pop   wc.hInstance
mov   wc.hbrBackground,COLOR_WINDOW+1
mov   wc.lpszMenuName,OFFSET MenuName
mov   wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov   wc.hIcon,eax
mov   wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov   wc.hCursor,eax
invoke RegisterClassEx, addr wc
INVOKE CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,300,200,NULL,NULL,\
           hInst,NULL
mov   hwnd,eax
INVOKE ShowWindow, hwnd,SW_SHOWNORMAL
INVOKE UpdateWindow, hwnd
.WHILE TRUE
                INVOKE GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
INVOKE TranslateMessage, ADDR msg
                INVOKE DispatchMessage, ADDR msg
.ENDW
mov     eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

.IF uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_CREATE

.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam == 0
.IF ax==IDM_DATA
invoke MessageBox,NULL,ADDR Data_string,OFFSET AppName,MB_OK
invoke CreateWindowEx, WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL,\
50, 35, 200, 25, hWnd, EditID, hInstance, NULL
mov hwndEdit, eax

invoke SetWindowLong, hwndEdit, ; use just behind the line
GWL_WNDPROC, SubEdit ; where you create your control
mov opEdit, eax ; the old pointer, see below



invoke SetFocus, hwndEdit


.ELSEIF ax==IDM_COMPUTE
invoke MessageBox, NULL,ADDR Compute_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_EXIT
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
invoke PostQuitMessage, NULL

.ELSE
invoke DestroyWindow,hWnd
.ENDIF
   .ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor    eax,eax
ret
WndProc endp

SubEdit proc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
SWITCH uMsg

CASE WM_KEYDOWN
; return 0

CASE WM_CHAR
; return 0

CASE WM_KEYUP
.if wParam==VK_RETURN
invoke GetWindowText, hwndEdit, ADDR buffer, 8 ;... get the text, clear the control
invoke SetWindowText, hwndEdit, NULL
invoke MessageBox, NULL, ADDR buffer, ADDR GotIt_string, MB_OK  ;line 181
.endif

; return 0

DEFAULT
; xor eax, eax

ENDSW
  invoke CallWindowProc, opEdit, hwnd, uMsg, wParam, lParam
  ret
SubEdit endp
end start


Mark, I just added the CODE tags so your post is easier to read.

jj2007

Mark,
It see a white window with no menu - should there be something in the WM_CREATE handler? Snippet below.

mov hMenu, rv(CreateMenu) ; create the main menu
mov hM1, rv(CreateMenu) ; plus two
mov hM2, rv(CreateMenu) ; sub menus
invoke AppendMenu,hMenu, MF_POPUP, hM1,chr$("&File")
invoke AppendMenu, hM1, MF_STRING, IdMenuNew, chr$("&New",9,"Ctrl+N")
invoke AppendMenu, hM1, MF_STRING, IdMenuSave, chr$("&Save",9,"Ctrl+S")
invoke AppendMenu,hMenu, MF_POPUP, hM2,chr$("&Edit")
invoke AppendMenu, hM2, MF_STRING, IdMenuCopy, chr$("&Copy",9,"Ctrl+C")
invoke SetMenu, hWnd, hMenu ; attach menu to main window

donkey

Personally I find the WM_KEYUP/KEYDOWN/CHAR messages too limited, especially when applied to edit controls. Since the edit control processes many keys before the subclass procedure is called there are some like the ESC key that you will never see using those messages. That's mainly because the keys are processed through the message handler and if they are in the range allowed then they are passed on to the WndProc, otherwise they are handled internally. So if you want to use the escape key to abort an edit then your pretty much stuck. A better alternative is a very seldom used message, the WM_GETDLGCODE notification. Because it is essentially asking your procedure if you would like to process a key press it will send everything, including the escape key...

.WM_GETDLGCODE
cmp D[uMsg],WM_GETDLGCODE
jne >.NEXTMSG
cmp D[wParam],VK_ESCAPE
jne >>.EXIT
// process escape key here
jmp >>.EXIT


try it sometime, you may like it :)

<EDIT>

Another small preference when I'm subclassing, I don't like global variables, I find them messy. To store the old window proc address I almost always use the GWL_USERDATA field of the window...

invoke SetWindowLong,[hEdit],GWL_WNDPROC,offset EditSubClass
invoke SetWindowLong,[hEdit],GWL_USERDATA,eax


and in the subclass procedure:

.EXIT
invoke GetWindowLong,[hwnd],GWL_USERDATA
invoke CallWindowProc,eax,[hwnd],[uMsg],[wParam],[lParam]
RET


But this is just a personal preference, and it does use up a useful place to store data.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

jj2007

Quote from: donkey on January 05, 2010, 08:24:09 AM
Personally I find the WM_KEYUP/KEYDOWN/CHAR messages too limited, especially when applied to edit controls. Since the edit control processes many keys before the subclass procedure is called there are some like the ESC key that you will never see using those messages.

Strange, for me it works under Windows XP. Which version are you using? Which other keys are not being caught?

See test attached. Of course, if you activate VK_ESCAPE in MyAccels, the edit control will not get it. But that is by design.

donkey

Hi jj2007,

Are you saying that your WinXP will send an ESC key via WM_CHAR or WM_KEYDOWN in a dialog application ??? I'll have to double check that one. Ofcourse if you use a window message pump without IsDialogMessage it'll probably send it but then you lose all sorts of features like tabbing between controls.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

hutch--

One of the things I have learnt to do with edit controls, specifically rich edit controls is do the key processing in the main message loop, there were endless problems handling key strokes in a rich edit subclass.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

donkey

Quote from: hutch-- on January 05, 2010, 09:50:11 AM
One of the things I have learnt to do with edit controls, specifically rich edit controls is do the key processing in the main message loop, there were endless problems handling key strokes in a rich edit subclass.

Hi Hutch,

I wholeheartedly agree with that but there are times when you really need the control to be as independent as possible from the main loop. For example if you plan to reuse the control in several projects, it can be a pain to set up the main loop every time you need to merge it. Or if you want to add a specific functionality to it, like limiting the allowed characters, for example to numeric only but with the decimal point.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

jj2007

Quote from: donkey on January 05, 2010, 09:42:29 AM
Hi jj2007,

Are you saying that your WinXP will send an ESC key via WM_CHAR or WM_KEYDOWN in a dialog application ???

No, of course not. Mark's code is a standard windows app, not a dialog. The same applies to my attachment, which clearly demonstrates that the Escape key can be handled in the subclassing procedure.

Quote from: hutch-- on January 05, 2010, 09:50:11 AM
One of the things I have learnt to do with edit controls, specifically rich edit controls is do the key processing in the main message loop, there were endless problems handling key strokes in a rich edit subclass.

Hutch,
My RichMasm edit subclass procedure is 1783 bytes long and handles all kinds of messages, including automatic indenting when pressing Return and expanding ism to invoke SendMessage, etc - never encountered any serious problems there, in contrast to other places where RichEd.dll bugs would be a good reason to sue Microsoft...