PDA

View Full Version : Keyboard Input & CallBacks



marcuslee
04-12-2009, 07:09
With Callbacks now the main way of handling window messages, what is the best way to capture keypresses in the main loop? For example, I would like a certain keyboard shortcut to be avaliable to the user at anytime during program execution. It seems to me that any of the GETKEYSTATE commands wouldn't work with a callback. Or, do they?


Mark

marcuslee
05-12-2009, 20:32
I found this thread (http://community.thinbasic.com/index.php?topic=2671.0) which seems to talk about what I asked.

I tried to implement it with the Flash Card program I am trying to put together. I am on a computer that I haven't worked on for several days, so the script I use here is old. I have a much more up-to-date version on my other computer in the basement (which, I don't use to get online). But, I thought that if I could get the feature to work here, I could then take it downstairs.

%WM_KEYDOWN didn't seem to work with adding the controls in the callback, so I added the controls in the main loop. I got the KEYDOWN feature to work, but the font did not change for the labels, only for the textboxes.

Also, the KEYDOWN only works at the beginning. If I press one of the buttons, the KEYDOWN stops working.

Here's my code:



'------------------------------------------------------------------------------
' Bible Flash Card Input Program ... by Mark Lee
'------------------------------------------------------------------------------

Uses "UI"
Uses "File"
Uses "Crypto"
Uses "Console"

Declare Function IsDialogMessage Lib "USER32.DLL" Alias "IsDialogMessageA" (ByVal hDlg As DWord, lpMsg As tagMSG) As Long
Declare Function WIN_GetMessage Lib "USER32.DLL" Alias "GetMessageA" (lpMsg As tagMSG, ByVal hWnd As DWord, ByVal uMsgFilterMin As DWord, ByVal uMsgFilterMax As DWord) As Long
Declare Function TranslateMessage Lib "USER32.DLL" Alias "TranslateMessage" (lpMsg As tagMSG) As Long
Declare Function DispatchMessage Lib "USER32.DLL" Alias "DispatchMessageA" (lpMsg As tagMSG) As Long
'---Constant declarations
begin const
%ID_Button_01 = %IDOK
%ID_Button_02
%ID_Button_03
%ID_Button_04
%ID_Button_05
%ID_Button_06
%ID_Label_01
%ID_Label_02
%ID_Label_03
%ID_Label_04
%ID_Label_05
%ID_Textbox_01
%ID_Textbox_02
%ID_Textbox_03
End Const

Type FlashCard
Front As ASCIIZ * 300
Back As ASCIIZ * 300
End Type

'Global Declarations
Dim hFont1 As DWord Resource = Font_Create("Arial Black", 12)
Dim hFont2 As DWord Resource = Font_Create("Arial Black", 16)
Dim hFont3 As DWord Resource = Font_Create("Arial Black", 14)
Dim LabelText As String = "Input Program for Bible Flash Cards"
Dim FileName As String = APP_SourcePath & "Cards.DAT"
Dim File As DWord = FILE_Open (FileName, "random" , SIZEOF (FlashCard))
Dim hDlg As DWord
Dim CardNumber As Integer = 1
Dim CardNumberString As String = "Card Number: " & Str$(CardNumber)
Dim SingleCard As FlashCard
Dim Encrypted As FlashCard
Dim password As String = "password"
Dim Result As Long


'------------------------------------------------------------------------------
' Program start point
'------------------------------------------------------------------------------
FUNCTION TBMain() as long
'---Create a new dialog
Dialog NEW PIXELS, 0, "Bible Flash Cards", -1, -1, 650, 450, _
%WS_DLGFRAME | _
%DS_CENTER | _
%WS_CAPTION | _
%WS_SYSMENU | _
%WS_OVERLAPPEDWINDOW, %WS_EX_STATICEDGE To hDlg

'---Set window minimum size
Dialog SET MINSIZE hDlg, 650, 450
Dialog SHOW MODELESS hDlg, Call cbDialog

'---Add controls
Control ADD BUTTON, hDlg, %ID_Button_01, "Save Card" , 100, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP | %BS_DEFAULT, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_02, "Previous" , 200, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_03, "Next" , 300, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_04, "Clear" , 400, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_05, "Empty Card", 500, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_06, "OK", 300, 380, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton

Control ADD LABEL, hDlg, %ID_Label_01, LabelText, 100, 40, 500, 50 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_02, "Front", 0, 150, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_03, "Back", 0, 260, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_04, CardNumberString,0, 10, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_05, "GOTO Card Number:", 100, 380, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel

Control ADD TEXTBOX, hDlg, %ID_Textbox_01, "", 100, 110, 500, 100,%ES_MULTILINE | %WS_BORDER,,Call cbTextbox
Control ADD TEXTBOX, hDlg, %ID_Textbox_02, "", 100, 220, 500, 100,%ES_MULTILINE | %WS_BORDER,,Call cbTextbox
Control ADD TEXTBOX, hDlg, %ID_Textbox_03, "", 210, 380, 80, 35,%ES_MULTILINE | %WS_BORDER | %ES_NUMBER | %ES_CENTER,,Call cbTextbox
' Setting the font for labels and textboxes
Control SEND hDlg, %ID_Label_01, %WM_SETFONT, hFont2, 0
Control SEND hDlg, %ID_Label_02, %WM_SETFONT, hFont3, 0
Control SEND hDlg, %ID_Label_03, %WM_SETFONT, hFont3, 0
Control SEND hDlg, %ID_Textbox_01, %WM_SETFONT, hFont1, 0
Control SEND hDlg, %ID_Textbox_02, %WM_SETFONT, hFont1, 0
Control SEND hDlg, %ID_Textbox_03, %WM_SETFONT, hFont1, 0
'Show the first flash card
Call ShowCard() To Result

Local uMsg As tagMsg
While Win_GetMessage(uMsg, %NULL, 0, 0)
If IsFalse (IsDialogMessage(hDlg, uMsg) ) Then
TranslateMessage uMsg
DispatchMessage uMsg
End If
Wend
End Function

'------------------------------------------------------------------------------
' Callback procedure for main window
'------------------------------------------------------------------------------
callback FUNCTION cbDialog() as long


select case cbmsg

Case %WM_CREATE

Case %WM_SETFOCUS


Case %WM_KEYDOWN
'---
PrintL "%WM_KEYDOWN"
Select Case CBWPARAM
Case %VK_F1
PrintL "You pressed F1 key", CBCTL, CBWPARAM, Bin$(CBLPARAM, 32)
Case %VK_RETURN
PrintL "You pressed RETURN key", CBCTL, CBWPARAM, Bin$(CBLPARAM, 32)
Case Else
PrintL "You pressed ... some key", CBCTL, CBWPARAM, Bin$(CBLPARAM, 32)
End Select



'Case %WM_COMMAND
'Case %WM_SIZE '---The WM_SIZE message is sent to a window after its size has changed.
'Case %WM_SIZING '---The WM_SIZING message is sent to a window that the user is resizing.
'Case %WM_MOVE
'Case %WM_DESTROY
end select

END FUNCTION

'------------------------------------------------------------------------------
' Callback procedure for button control
'------------------------------------------------------------------------------
callback function cbButton() as long

if cbmsg = %WM_COMMAND then

select case CBCTLMSG
case %BN_CLICKED
select case CBCTL
case %ID_Button_01
Call InputCard() To Result
case %ID_Button_02
Call PreviousCard() To Result
Case %ID_Button_03
Call NextCard() To Result
Case %ID_Button_04
Control SET TEXT hdlg, %ID_Textbox_01, ""
Control SET TEXT hdlg, %ID_Textbox_02, ""
Case %ID_Button_05
Call EmptyCard() To Result
Case %ID_Button_06
Call GotoCard() To Result

End Select
end select

Function = %TRUE

end if
End Function





CallBack Function cbLabel() As Long
Function = %TRUE
End Function




CallBack Function cbTextbox() As Long
Function = %TRUE
End Function

Function InputCard() As Long
'Contents of current card are put into the Cards.Dat file
Local x, y As String

Control GET TEXT hDlg, %ID_Textbox_01 To x
Control GET TEXT hDlg, %ID_Textbox_02 To y
'MsgBox 0, x

Encrypted.Front = iCrypto_String2ASCII(x)
Encrypted.Back = iCrypto_String2ASCII(y)

'x = iCrypto_DecryptRC4(Encrypted.Front, password)
'MsgBox 0, x

FILE_PutR (File, CardNumber, Encrypted)
'FILE_GetR (File, CardNumber, SingleCard)

'x = iCrypto_DecryptRC4(SingleCard.Front, password)
'MsgBox 0, x

Function = %TRUE

End Function

Function NextCard() As Long
'Goes to the next card

CardNumber = CardNumber + 1
Call ShowCard() To Result

Function = %TRUE
End Function

Function PreviousCard() As Long

If CardNumber = 1 Then MsgBox 0, "This is the first card."
If CardNumber > 1 Then CardNumber = CardNumber - 1


Call ShowCard() To Result

Function = %TRUE
End Function

Function ShowCard() As Long
'Shows the card, card number set elsewhere

Control SET TEXT hdlg, %ID_Label_04, "Card Number: " & Str$(CardNumber)

FILE_GetR (File, CardNumber, Encrypted)
SingleCard.Front = iCrypto_ASCII2String(Encrypted.Front)
SingleCard.Back = iCrypto_ASCII2String(Encrypted.Back)
Control SET TEXT hdlg, %ID_Textbox_01, Trim$(SingleCard.Front)
Control SET TEXT hdlg, %ID_Textbox_02, Trim$(SingleCard.Back)

Function=%TRUE
End Function

Function EmptyCard() As Long
'Brings up the first empty flash card

CardNumber = 1
Do
FILE_GetR (File, CardNumber, Encrypted)
If Encrypted.Front = "" Then
Exit Do
Else
CardNumber = CardNumber + 1
End If
Loop

Call ShowCard() To Result

Function=%TRUE
End Function

Function GotoCard() As Long
'Allows user to choose which card to view
Local x As String
Control GET TEXT hDlg, %ID_Textbox_03 To x
If x <> "" Then
CardNumber = x
Call ShowCard() To Result
Else
MsgBox 0, "Card Number doesn't seem to be valid."
End If
Function=%TRUE
End Function




Does anyone have any ideas?



Mark :violent:

Petr Schreiber
05-12-2009, 21:05
Hi Mark,

you are doing good by trapping the WM_KeyDown message.
The problem is in other fact - the focus.

Once you are in textbox for example, it traps all keypresses for writing text to.

For Windows GUI program, I would consider the following:
- use WM_HELP to catch F1, works reliably
- do not try to catch some other keys, for example ENTER is trapped by buttons with %IDOK and ESCAPE for those with %IDCANCEL
- the way you handle message pump, you can use GetAsyncKeyState too:


CallBack Function cbDialog() As Long

If (GetAsyncKeyState(%VK_X) And 1) Then
PrintL "X Pressed"
End If



Petr

marcuslee
06-12-2009, 08:09
you are doing good by trapping the WM_KeyDown message.
The problem is in other fact - the focus.


That makes perfect sense.



the way you handle message pump, you can use GetAsyncKeyState too:


CallBack Function cbDialog() As Long

If (GetAsyncKeyState(%VK_X) And 1) Then
PrintL "X Pressed"
End If


I tried using WIN_SetFocus() to give the focus back to hdlg, but KEYDOWN didn't work. Is there a way to allow controls to work but after the script is done executing stuff for the control, focus and thus keypresses can be given back to the main window?

Also, can you think of a reason why the "Control SEND hDlg, %ID_Label_01, %WM_SETFONT, hFont3, 0" type lines didn't work when the same line for the textboxes did work?


Mark

Petr Schreiber
06-12-2009, 11:22
Hi Mark,

I am not able to answer the focus question currentely. Must think about it.

Regarding the fonts, I would recommend to force redraw:


Control SEND hDlg, %ID_Label_01, %WM_SETFONT, hFont2, %TRUE
Control SEND hDlg, %ID_Label_02, %WM_SETFONT, hFont3, %TRUE
Control SEND hDlg, %ID_Label_03, %WM_SETFONT, hFont3, %TRUE
Control SEND hDlg, %ID_Textbox_01, %WM_SETFONT, hFont1, %TRUE
Control SEND hDlg, %ID_Textbox_02, %WM_SETFONT, hFont1, %TRUE
Control SEND hDlg, %ID_Textbox_03, %WM_SETFONT, hFont1, %TRUE


When you set WM_SETFONT to google, it should take you to the MSDN website topic on it. It is very good reference for messages and generally Windows programming.

They say there that the last parameter of message is there "If this parameter is TRUE, the control redraws itself.".
I guess LABELs are just static controls, so they need this kind of push.


Petr

marcuslee
06-12-2009, 16:34
I am not able to answer the focus question currentely. Must think about it.


Good! That means I have challenged both you and me.

I don't know if this will help, but doing some msgbox debugging, I discovered that the value of hDlg is the same throughout but the result of win_setfocus does not match.
Here:


FUNCTION TBMain() as long
'---Create a new dialog
Dialog NEW PIXELS, 0, "Bible Flash Cards", -1, -1, 650, 450, _
%WS_DLGFRAME | _
%DS_CENTER | _
%WS_CAPTION | _
%WS_SYSMENU | _
%WS_OVERLAPPEDWINDOW, %WS_EX_STATICEDGE To hDlg

'---Set window minimum size
Dialog SET MINSIZE hDlg, 650, 450
Dialog SHOW MODELESS hDlg, Call cbDialog
MsgBox 0, hDlg

And, Here:


select case CBCTL
case %ID_Button_01
Call InputCard() To Result
Result = WIN_SetFocus(hDlg)
MsgBox 0, hDlg & Chr$(13) & Result


Also, I looked on MSDN, and I found the WIN_SetFocus() notification and the SetFocus function. thinBasic doesn't seem to have both. The description in the help file of Win_SetFocus seems to match the SetFocus function:



The SetFocus function sets the keyboard focus to the specified window. The window must be attached to the calling thread's message queue.


From the description on MSDN, I don't understand Win_SetFocus. Why do you need to send a message to a window telling it that it has focus when you have already used the Set focus function?



The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus.





When you set WM_SETFONT to google, it should take you to the MSDN website topic on it. It is very good reference for messages and generally Windows programming.

They say there that the last parameter of message is there "If this parameter is TRUE, the control redraws itself.".
I guess LABELs are just static controls, so they need this kind of push.


I'm on MSDN all the time. I don't know if I looked at this article or not. Thank you for pointing it out. For me MSDN is difficult to understand sometimes because the examples are in C or I guess C++, which I have a hard time following. There are times that you need to do several things to accomplish a task ... then ... MSDN is daunting for me! :read:

Mark

marcuslee
06-12-2009, 16:52
OK, I goofed. MSDN was talking about WM_SETFOCUS not WIN_SETFOCUS. Maybe I need my eyes examined! ;--)

Since it is a message notification, I thought about using DIALOG SEND. But, it doesn't seem to work. Here's what I have tried:



case %ID_Button_01
Call InputCard() To Result
WIN_SetFocus(hDlg)
Dialog SEND hDlg, %WM_SETFOCUS,%ID_Button_01 ,0, To result
MsgBox 0, hDlg & Chr$(13) & Result


I get an error message. Oh well, at least I thought I was on to something. :unguee:


Mark

Petr Schreiber
06-12-2009, 16:59
Hi Mark,

maybe set focus works to activate window as a such, if you have focus of some control inside, it understand it like window is focused. WM_SetFocus is used to switch between windows.

I think this fight will never be won - user can anytime jump to controls using TAB keys.
I think keypress based interaction for dialog with buttons and other controls is something not very standard in Windows world.

So here we get to the core of the problem :) - for what do you need to read the keypresses?
I think the GetAsyncKeyState will do its job here, as it is focus resistant.


Petr

marcuslee
07-12-2009, 00:01
So here we get to the core of the problem :) - for what do you need to read the keypresses?
I think the GetAsyncKeyState will do its job here, as it is focus resistant.


It is more of a curiosity than a need. If it were easy and fool proof ... or at least fool proof, I would use it anytime I had a major program (most of my programming creations are for myself, anyway). Keypresses would provide a quick way to perform common functions, which would other wise be done by buttons or menus (which I seldom use). Keyboarding, IMHO, is almost always faster than clicking with a mouse. :comp4:

I tried your GetAsyncKeyState example from before, and at first I was really happy because it worked even after I clicked a button. But, then, I noticed that I couldn't pick a key that was likely to be used in a textbox, so I switched to GetMultiAsyncKeyState. After that, I noticed that the GetAsyncKeyState & GetMultiAsyncKeyState functions were firing too quickly. Even if I was trying to hit the buttons quickly, I would still end up on card 16 or worse yet, card 346 or some high number.

Is there a way for the IF statement that has the GetAsyncKeyState or GetMultiAsyncKeyState functions to fire only once when the user presses the button(s)?

I tried using the SLEEP command at the end of the IF THEN block, but that didn't work as expected either.


Mark

Michael Clease
07-12-2009, 00:39
Mark have you tried reseting the keystate? I dont know if this will make any difference but its worth a try.

Use GetAsyncKeyState with vKey equal to -1 to reset keys flags

Mike

marcuslee
07-12-2009, 07:08
Mark have you tried reseting the keystate? I dont know if this will make any difference but its worth a try.

Use GetAsyncKeyState with vKey equal to -1 to reset keys flags


I tried the following.



CallBack Function cbDialog() As Long

If (GetMultiAsyncKeyState(%VK_CONTROL, %VK_N) And 1) Then
Call NextCard() To Result
GetMultiAsyncKeyState(-1)
End If


Don't know if this is what you had in mind. It didn't seem to make a difference. The script still executed numerous times. Did you mean to put it somewhere else?


Mark

Petr Schreiber
07-12-2009, 14:53
Hi Mark,

I think for this functionality, you can make it even easier:


'------------------------------------------------------------------------------
' Bible Flash Card Input Program ... by Mark Lee
'------------------------------------------------------------------------------

Uses "UI"
Uses "File"
Uses "Crypto"
Uses "Console"

Declare Function IsDialogMessage Lib "USER32.DLL" Alias "IsDialogMessageA" (ByVal hDlg As DWord, lpMsg As tagMSG) As Long
Declare Function WIN_GetMessage Lib "USER32.DLL" Alias "GetMessageA" (lpMsg As tagMSG, ByVal hWnd As DWord, ByVal uMsgFilterMin As DWord, ByVal uMsgFilterMax As DWord) As Long
Declare Function TranslateMessage Lib "USER32.DLL" Alias "TranslateMessage" (lpMsg As tagMSG) As Long
Declare Function DispatchMessage Lib "USER32.DLL" Alias "DispatchMessageA" (lpMsg As tagMSG) As Long
'---Constant declarations
Begin Const
%ID_Button_01 = %IDOK
%ID_Button_02
%ID_Button_03
%ID_Button_04
%ID_Button_05
%ID_Button_06
%ID_Label_01
%ID_Label_02
%ID_Label_03
%ID_Label_04
%ID_Label_05
%ID_Textbox_01
%ID_Textbox_02
%ID_Textbox_03
End Const

Type FlashCard
Front As ASCIIZ * 300
Back As ASCIIZ * 300
End Type

'Global Declarations
Dim hFont1 As DWord Resource = Font_Create("Arial Black", 12)
Dim hFont2 As DWord Resource = Font_Create("Arial Black", 16)
Dim hFont3 As DWord Resource = Font_Create("Arial Black", 14)
Dim LabelText As String = "Input Program for Bible Flash Cards"
Dim FileName As String = APP_SourcePath & "Cards.DAT"
Dim File As DWord = FILE_Open (FileName, "random" , SIZEOF (FlashCard))
Dim hDlg As DWord
Dim CardNumber As Integer = 1
Dim CardNumberString As String = "Card Number: " & Str$(CardNumber)
Dim SingleCard As FlashCard
Dim Encrypted As FlashCard
Dim password As String = "password"
Dim Result As Long


'------------------------------------------------------------------------------
' Program start point
'------------------------------------------------------------------------------
Function TBMain() As Long
'---Create a new dialog
Dialog NEW PIXELS, 0, "Bible Flash Cards", -1, -1, 650, 450, _
%WS_DLGFRAME | _
%DS_CENTER | _
%WS_CAPTION | _
%WS_SYSMENU | _
%WS_OVERLAPPEDWINDOW, %WS_EX_STATICEDGE To hDlg

'---Set window minimum size
Dialog SET MINSIZE hDlg, 650, 450


'---Add controls
Control ADD BUTTON, hDlg, %ID_Button_01, "&Save Card" , 100, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP | %BS_DEFAULT, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_02, "&Previous" , 200, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_03, "&Next" , 300, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_04, "&Clear" , 400, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_05, "&Empty Card", 500, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton
Control ADD BUTTON, hDlg, %ID_Button_06, "&OK", 300, 380, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton

Control ADD LABEL, hDlg, %ID_Label_01, LabelText, 100, 40, 500, 50 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_02, "Front", 0, 150, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_03, "Back", 0, 260, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_04, CardNumberString,0, 10, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel
Control ADD LABEL, hDlg, %ID_Label_05, "GOTO Card Number:", 100, 380, 80, 100 ,%SS_NOTIFY | %SS_CENTER , , Call cbLabel

Control ADD TEXTBOX, hDlg, %ID_Textbox_01, "", 100, 110, 500, 100,%ES_MULTILINE | %WS_BORDER,,Call cbTextbox
Control ADD TEXTBOX, hDlg, %ID_Textbox_02, "", 100, 220, 500, 100,%ES_MULTILINE | %WS_BORDER,,Call cbTextbox
Control ADD TEXTBOX, hDlg, %ID_Textbox_03, "", 210, 380, 80, 35,%ES_MULTILINE | %WS_BORDER | %ES_NUMBER | %ES_CENTER,,Call cbTextbox
' Setting the font for labels and textbox es

printl hFont1, hFont2, hFont3
Control SEND hDlg, %ID_Label_01, %WM_SETFONT, hFont2, %TRUE
Control SEND hDlg, %ID_Label_02, %WM_SETFONT, hFont3, %TRUE
Control SEND hDlg, %ID_Label_03, %WM_SETFONT, hFont3, %TRUE
Control SEND hDlg, %ID_Textbox_01, %WM_SETFONT, hFont1, %TRUE
Control SEND hDlg, %ID_Textbox_02, %WM_SETFONT, hFont1, %TRUE
Control SEND hDlg, %ID_Textbox_03, %WM_SETFONT, hFont1, %TRUE
'Show the first flash card
Call ShowCard() To Result

Dialog SHOW MODAL hDlg, Call cbDialog
End Function

'------------------------------------------------------------------------------
' Callback procedure for main window
'------------------------------------------------------------------------------

CallBack Function cbDialog() As Long


Select Case CBMSG


Case %WM_KEYDOWN
'---
PrintL "%WM_KEYDOWN"
Select Case CBWPARAM
Case %VK_F1
PrintL "You pressed F1 key", CBCTL, CBWPARAM, Bin$(CBLPARAM, 32)
Case %VK_RETURN
PrintL "You pressed RETURN key", CBCTL, CBWPARAM, Bin$(CBLPARAM, 32)
Case Else
PrintL "You pressed ... some key", CBCTL, CBWPARAM, Bin$(CBLPARAM, 32)
End Select



Case %WM_COMMAND
PrintL CBCTL
'Case %WM_SIZE '---The WM_SIZE message is sent to a window after its size has changed.
'Case %WM_SIZING '---The WM_SIZING message is sent to a window that the user is resizing.
'Case %WM_MOVE
'Case %WM_DESTROY
End Select

End Function

'------------------------------------------------------------------------------
' Callback procedure for button control
'------------------------------------------------------------------------------
CallBack Function cbButton() As Long

If CBMSG = %WM_COMMAND Then

Select Case CBCTLMSG
Case %BN_CLICKED
Select Case CBCTL
Case %ID_Button_01
Call InputCard() To Result
WIN_SetFocus(hDlg)
Dialog SEND hDlg, %WM_SETFOCUS,%ID_Button_01,0 To result
MsgBox 0, hDlg & Chr$(13) & Result


Case %ID_Button_02
Call PreviousCard() To Result
Case %ID_Button_03
Call NextCard() To Result
Case %ID_Button_04
Control SET TEXT hdlg, %ID_Textbox_01, ""
Control SET TEXT hdlg, %ID_Textbox_02, ""
Case %ID_Button_05
Call EmptyCard() To Result
Case %ID_Button_06
Call GotoCard() To Result

End Select
End Select

Function = %TRUE

End If
End Function





CallBack Function cbLabel() As Long
Function = %TRUE
End Function




CallBack Function cbTextbox() As Long
Function = %TRUE
End Function

Function InputCard() As Long
'Contents of current card are put into the Cards.Dat file
Local x, y As String

Control GET TEXT hDlg, %ID_Textbox_01 To x
Control GET TEXT hDlg, %ID_Textbox_02 To y
'MsgBox 0, x

Encrypted.Front = iCrypto_String2ASCII(x)
Encrypted.Back = iCrypto_String2ASCII(y)

'x = iCrypto_DecryptRC4(Encrypted.Front, password)
'MsgBox 0, x

FILE_PutR (File, CardNumber, Encrypted)
'FILE_GetR (File, CardNumber, SingleCard)

'x = iCrypto_DecryptRC4(SingleCard.Front, password)
'MsgBox 0, x

Function = %TRUE

End Function

Function NextCard() As Long
'Goes to the next card

CardNumber = CardNumber + 1
Call ShowCard() To Result

Function = %TRUE
End Function

Function PreviousCard() As Long

If CardNumber = 1 Then MsgBox 0, "This is the first card."
If CardNumber > 1 Then CardNumber = CardNumber - 1


Call ShowCard() To Result

Function = %TRUE
End Function

Function ShowCard() As Long
'Shows the card, card number set elsewhere

Control SET TEXT hdlg, %ID_Label_04, "Card Number: " & Str$(CardNumber)

FILE_GetR (File, CardNumber, Encrypted)
SingleCard.Front = iCrypto_ASCII2String(Encrypted.Front)
SingleCard.Back = iCrypto_ASCII2String(Encrypted.Back)
Control SET TEXT hdlg, %ID_Textbox_01, Trim$(SingleCard.Front)
Control SET TEXT hdlg, %ID_Textbox_02, Trim$(SingleCard.Back)

Function=%TRUE
End Function

Function EmptyCard() As Long
'Brings up the first empty flash card

CardNumber = 1
Do
FILE_GetR (File, CardNumber, Encrypted)
If Encrypted.Front = "" Then
Exit Do
Else
CardNumber = CardNumber + 1
End If
Loop

Call ShowCard() To Result

Function=%TRUE
End Function

Function GotoCard() As Long
'Allows user to choose which card to view
Local x As String
Control GET TEXT hDlg, %ID_Textbox_03 To x
If x <> "" Then
CardNumber = x
Call ShowCard() To Result
Else
MsgBox 0, "Card Number doesn't seem to be valid."
End If
Function=%TRUE
End Function


When you create button for "Previous" like this:


Control ADD BUTTON, hDlg, %ID_Button_02, "&Previous" , 200, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton


... with that extra "&" before "P" in "Previous", you basically tell Windows you can press the button using ALT+P.
This way you can also switch back to modal dialog, which takes much less CPU time in ThinBASIC.

marcuslee
07-12-2009, 17:10
When you create button for "Previous" like this:


Control ADD BUTTON, hDlg, %ID_Button_02, "&Previous" , 200, 340, 100, 35, %BS_NOTIFY | %WS_TABSTOP, Call cbButton


... with that extra "&" before "P" in "Previous", you basically tell Windows you can press the button using ALT+P.
This way you can also switch back to modal dialog, which takes much less CPU time in ThinBASIC.


That is most excellent! Especially since the keypresses are really only for me, not the average user. Thank you very much. And, a much simpler solution as well. I like to KISS it. (Keep It Simple, Stupid!)

:eusaclap: :eusaclap: :eusaclap:


Mark

catventure
10-01-2010, 16:18
It's an interesting thread. I'd long hoped that there could be some sort of simple to use command to clear the text input buffer of all keypresses...

For instance in my UI adventure game I sometimes want to pause the program for a number of seconds (I can use SLEEP or a dialog timer)
During the paused time however the user might conceivably press some keys; which I need to somehow make the program ignore/erase...
However keypresses don't seem to be fully cleared even if I use

getwindowkeystate (hwnd, -1)
or
getasynckeystate (-1)

I guess what I'm looking for is some way of turning off keyboard input and key memory until the pause is over and then turn it back on again.

Regards,
catventure

catventure
12-01-2010, 19:10
While searching about disabling keyboard temporarily so that keypresses are not recognised I am wondering if the below VB code that I found might do the trick, if it could be converted for thinbasic...



VB Code:
Option Explicit

Private Declare Function BlockInput Lib "user32" (ByVal fBlock As Long) As Long
'Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Sub Form_Activate()
DoEvents
'block the mouse and keyboard input
BlockInput True
'wait 10 seconds before unblocking it
Sleep 10000 ' for 10 seconds
'unblock the mouse and keyboard input
BlockInput False
Unload Me
End Sub


Not sure how to do

catventure.

Petr Schreiber
12-01-2010, 19:43
Hi,

of course you can use this function in ThinBASIC too, as TB can access DLLs.
Just add the following:


DECLARE FUNCTION BlockInput LIB "USER32.DLL" ALIAS "BlockInput" ( BYVAL fBlockIt AS LONG ) AS LONG


But I am not sure if it will be of any help for KeyState, read here:
http://msdn.microsoft.com/en-us/library/ms646290%28VS.85%29.aspx

I do not remember much how TAB was done, could you show brief code listing where you experience the trouble?


Petr

catventure
12-01-2010, 20:57
Hi Petr,

Thanks. I have cut the code down a bit. It simply pauses the adventure game for (in this case) ten secs...
I could have used sleep 10000

This code is outside of dialog callback process

Problem is that any keys pressed during the pause phase are in the queue and will show up in my input textbox after the pause has finished. Worse still, if <return> or <enter> pressed during the pause then TAB thinks the player has typed in a "blank" input command and when the pause is over will execute it and print in the richedit something like "You need to type something in..."



numvalue = 10
PauseBase = Timer
Do
PauseElap = Timer
If PauseElap - PauseBase > numvalue Then
Exit Do
End If
Loop
EXIT FUNCTION


Otherwise of course it works OK

Petr Schreiber
12-01-2010, 21:36
I just tested the BlockInput and it seems to block both classic and AsyncKey for me here.
Do you use callback or the old deprecated (while/wend+peekMessage) functions?

The following example seems to block everything ok:


Uses "UI"

Declare Function BlockInput Lib "USER32.DLL" Alias "BlockInput" ( ByVal fBlockIt As Long ) As Long

' -- ID numbers of controls
Begin Const
%bClose = %WM_USER + 1
%bSetEditFocus
%eEdit
%chUseBlocking
End Const

' -- Create dialog here
Function TBMAIN()
Local hDlg As DWord

Dialog New 0, "Blocking test",-1,-1, 160, 120, _
%WS_POPUP Or %WS_VISIBLE Or %WS_CAPTION Or %WS_SYSMENU Or %WS_MINIMIZEBOX To hDlg

' -- Place controls here
Control Add Button, hDlg, %bClose, "Close", 95, 100, 60, 14, Call cbCloseButton
Control Add Button, hDlg, %bSetEditFocus, "Click me, and type on keyboard, try pushing N", 5, 40, 60, 60, %BS_MULTILINE Call cbEditButton
Control Add Textbox, hDlg, %eEdit, "", 5, 5, 100, 14
Control Add Checkbox, hDlg, %chUseBlocking, "Use blocking", 5, 25, 100, 14

Dialog Show Modal hDlg, Call cbDialog

End Function

' -- Callback for dialog
CallBack Function cbDialog()

' -- Test for messages
Select Case CBMSG

Case %WM_INITDIALOG
' -- Put code to be executed after dialog creation here

Case %WM_COMMAND
' -- You can handle controls here

'SELECT CASE CBCTL
'
' CASE ...
' IF CBCTLMSG = ... THEN
'
' EndIf
'
'END SELECT


Case %WM_CLOSE
' -- Put code to be executed before dialog end here

End Select

End Function

' -- Callback for close button
CallBack Function cbCloseButton()

If CBMSG = %WM_COMMAND Then
If CBCTLMSG = %BN_CLICKED Then
' -- Closes the dialog
Dialog End CBHNDL
EndIf
EndIf

End Function

CallBack Function cbEditButton()
Local blockIt As Long
If CBMSG = %WM_COMMAND Then
If CBCTLMSG = %BN_CLICKED Then
Sleep 100
Control Set Text CBHNDL, %eEdit, ""
Control Get Check CBHNDL, %chUseBlocking To blockIt
Control Set Text CBHNDL, %bSetEditFocus, "Start typing random letters"

' -- Begin blocking
If blockIt Then BlockInput(TRUE)

Control Set Focus CBHNDL, %eEdit

Sleep 5000

Control Set Text CBHNDL, %bSetEditFocus, "Stop typing"

Sleep 2000

' -- End blocking
If blockIt Then
BlockInput(FALSE)
GetAsyncKeyState(-1)
EndIf

If GetAsyncKeyState(%VK_N) Then MsgBox CBHNDL, "You pressed N, I saw you!"

If blockIt Then
If Control_GetText(CBHNDL, %eEdit) = "" Then
MsgBox CBHNDL, "Blocking was successful!"
Else
MsgBox CBHNDL, "Blocking FAILED"
EndIf
EndIf

Control Set Text CBHNDL, %bSetEditFocus, "Click me, and type on keyboard, try pushing N"

EndIf
EndIf

End Function


So try to surround the function call where you do pausing:


BlockInput(TRUE)

' -- Call function here

BlockInput(FALSE)
GetAsyncKeyState(-1)



Petr

catventure
12-01-2010, 21:47
Hi Petr,

What a great little example! Very neat! And it worked for me to I am very pleased to say. Thank you so much. I will examine more in depth the code now. Yes I am using callbacks and not the old way. This could be just the job.

Catventure(Phil)

Petr Schreiber
13-01-2010, 10:45
Thanks Phil,

I hope it will work for you, let me know!


Petr

ErosOlmi
13-01-2010, 12:17
Two changes from this recent posts will see the light in next thinBasic beta release:

1. WIN_BlockInput will be added as native UI command[/list]


Begin Const
...
End Const
will be changed in

Begin Const [, InitialValue]
...
End Const

So it will be possible to have something like:

Begin Const, %WM_USER
%bClose
%bSetEditFocus
%eEdit
%chUseBlocking
End Const

or maybe also something like:

Begin ControlsID
%bClose
%bSetEditFocus
%eEdit
%chUseBlocking
End Const

Thanks
Eros

catventure
14-01-2010, 13:45
@Eros,

Those improvements are welcomed.

@Petr,
I could not get the blockinput function to work on TAB... :(
I discovered from this site that there is a problem in Vista and 7 with blockinput:
http://www.winautomation.com/forum/block-input

When I ran Thinbasic with "run administrator permission" and then ran TAB it worked- absolutely no problems blocking keypresses great!

So it seems that there is an issue here - but how to resolve? Wait for Microsoft fix?

The odd thing which I don't get is that your example DOES work without the "Run As Administrator" option...

Regards,
catventure.

Petr Schreiber
14-01-2010, 14:12
Oh,

now that is really odd.

For now I would recommend to add to TAB help file:
"If you are user of Windows Vista or 7, please run the application as administrator"

There are ways how to force this in "manifest" of EXE file, but I would not recommend it.
I also presume users won't be happy to run as admin. So...

...then there is another possible workaround:



CONTROL DISABLE hDlg, ControlID '... on the input textbox and other selected controls
' do what you need (SLEEP n)
CONTROL ENABLE hDlg, ControlID '... to bring control back to life.
GetAsyncKeyState(-1)


Petr

catventure
14-01-2010, 17:40
CONTROL DISABLE hDlg, ControlID '... on the input textbox and other selected controls
' do what you need (SLEEP n)
CONTROL ENABLE hDlg, ControlID '... to bring control back to life.
GetAsyncKeyState(-1)


Petr


I've already tried that. It doesn't do what I want. The keypresses are still retained in memory and are printed in the input textbox when focus is returned after the pause.
I'll think I will use blockinput anyway because the pause still works as before and at least it will block unwanted keypresses on some machines. As you pointed out I can ask Vista/7 users to run with admin rights...

catventure.

Petr Schreiber
14-01-2010, 18:16
I am sorry to hear that,

it seems to work on XPs:



Uses "UI"

Declare Function BlockInput Lib "USER32.DLL" Alias "BlockInput" ( ByVal fBlockIt As Long ) As Long

' -- ID numbers of controls
Begin Const
%bClose = %WM_USER + 1
%bSetEditFocus
%eEdit
%chUseBlocking
End Const

' -- Create dialog here
Function TBMAIN()
Local hDlg As DWord

Dialog New 0, "Blocking test",-1,-1, 160, 120, _
%WS_POPUP Or %WS_VISIBLE Or %WS_CAPTION Or %WS_SYSMENU Or %WS_MINIMIZEBOX To hDlg

' -- Place controls here
Control Add Button, hDlg, %bClose, "Close", 95, 100, 60, 14, Call cbCloseButton
Control Add Button, hDlg, %bSetEditFocus, "Click me, and type on keyboard, try pushing N", 5, 40, 60, 60, %BS_MULTILINE Call cbEditButton
Control Add Textbox, hDlg, %eEdit, "", 5, 5, 100, 14
Control Add Checkbox, hDlg, %chUseBlocking, "Use blocking", 5, 25, 100, 14

Dialog Show Modal hDlg, Call cbDialog

End Function

' -- Callback for dialog
CallBack Function cbDialog()

' -- Test for messages
Select Case CBMSG

Case %WM_INITDIALOG
' -- Put code to be executed after dialog creation here

Case %WM_COMMAND
' -- You can handle controls here

'SELECT CASE CBCTL
'
' CASE ...
' IF CBCTLMSG = ... THEN
'
' EndIf
'
'END SELECT


Case %WM_CLOSE
' -- Put code to be executed before dialog end here

End Select

End Function

' -- Callback for close button
CallBack Function cbCloseButton()

If CBMSG = %WM_COMMAND Then
If CBCTLMSG = %BN_CLICKED Then
' -- Closes the dialog
Dialog End CBHNDL
EndIf
EndIf

End Function

CallBack Function cbEditButton()
Local blockIt As Long
If CBMSG = %WM_COMMAND Then
If CBCTLMSG = %BN_CLICKED Then
Sleep 100
Control Set Text CBHNDL, %eEdit, ""
Control Get Check CBHNDL, %chUseBlocking To blockIt
Control Set Text CBHNDL, %bSetEditFocus, "Start typing random letters"

' -- Begin blocking
Control Set Focus CBHNDL, %eEdit
If blockIt Then
Control Disable CBHNDL, %eEdit
Control Redraw CBHNDL, %eEdit
End If

Sleep 5000

Control Set Text CBHNDL, %bSetEditFocus, "Stop typing"

Sleep 2000

' -- End blocking
If blockIt Then
Control Enable CBHNDL, %eEdit
Control Redraw CBHNDL, %eEdit
GetAsyncKeyState(-1)
EndIf

If GetAsyncKeyState(%VK_N) Then MsgBox CBHNDL, "You pressed N, I saw you!"

If blockIt Then
If Control_GetText(CBHNDL, %eEdit) = "" Then
MsgBox CBHNDL, "Blocking was successful!"
Else
MsgBox CBHNDL, "Blocking FAILED"
EndIf
EndIf

Control Set Text CBHNDL, %bSetEditFocus, "Click me, and type on keyboard, try pushing N"

EndIf
EndIf

End Function

catventure
14-01-2010, 20:59
Hi Petr,

That code you posted *seems* to run OK and work on mine - but it will not handle enter/return key problem from activating...
When I tried plugging in the enable/disable stuff into TAB it seems to have no effect and as I said the keypresses always show up in the textbox afterward. That's not too bad - I could delete those; but is the enter key which if pressed during the pause that causes main problem...
Thanks anyway.

catventure.

Petr Schreiber
14-01-2010, 21:21
Hi Phil,

do you handle the ENTER key using the magical %IDOK trick?
If yes, then simply try to disable/enable or kill/control add again, it should do the job.


Petr

catventure
15-01-2010, 17:26
do you handle the ENTER key using the magical %IDOK trick?
If yes, then simply try to disable/enable or kill/control add again, it should do the job.



Hi Petr,

Yes enter/return handled by %IDOK in main dialog adventure window callback.
However don't see how I could turn this off without closing window. Even if I use "dialog disable" it doesn't help flush the keyboard buffer or block the keypress. The 'blockinput' function currently is the best way.

catventure.