Tab and Esc keys sometimes don't work
Moderators: Hacker, petermad, Stefan2, white
- ghisler(Author)
- Site Admin
- Posts: 50390
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
I will check that, but it's probably not easy - I would probably have to subclass the edit control for that. Maybe I have to postpone it to a later release...
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com
Hmmm, every TControl in Delphi 5 has public WindowProc property, which allows replacing a window proc easily at runtime. However, there is also a solution for Delphi 2:
Regards!
Code: Select all
function EditWndProc(Handle : HWND; Msg : UINT; wParam : WPARAM; lParam : LPARAM) : LRESULT; stdcall;
begin
with TEdit(FindControl(Handle)) do
begin
{Test}
if Msg = WM_KEYDOWN then
if wParam = VK_RETURN then
Application.MessageBox('Enter pressed','Test',MB_OK);
if Msg = WM_DESTROY then
SetWindowLong(Handle,GWL_WNDPROC,Tag);
Result:=CallWindowProc(Pointer(Tag),Handle,Msg,wParam,lParam);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
with Edit1 do
begin
Tag:=GetWindowLong(Handle,GWL_WNDPROC);
SetWindowLong(Handle,GWL_WNDPROC,Integer(@EditWndProc));
end;
end;
Now, in rc2, it's almost ideal :) There is a small problem with a "ding" sound when pressing Enter or Esc. I suppose that the reason is edit control - TEdit always plays "ding" sound when pressing Enter or Esc. The solution is to handle WM_CHAR message:
P.S. Maybe you could also add a support for Tab key - when no message box is existing, Tab key exits editing mode and jumps to second panel immediately.
Regards!
Code: Select all
function EditWndProc(Handle : HWND; Msg : UINT; wParam : WPARAM; lParam : LPARAM) : LRESULT; stdcall;
begin
with TEdit(FindControl(Handle)) do
begin
{Test}
if Msg = WM_KEYDOWN then
if wParam = VK_RETURN then
Text:='Enter pressed';
{Disable "ding" sound}
if Msg = WM_CHAR then
if AnsiChar(wParam) in [#9,#13,#27] then
wParam:=0;
if Msg = WM_DESTROY then
SetWindowLong(Handle,GWL_WNDPROC,Tag);
Result:=CallWindowProc(Pointer(Tag),Handle,Msg,wParam,lParam);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
with Edit1 do
begin
Tag:=GetWindowLong(Handle,GWL_WNDPROC);
SetWindowLong(Handle,GWL_WNDPROC,Integer(@EditWndProc));
end;
end;
Regards!
- ghisler(Author)
- Site Admin
- Posts: 50390
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
That's why I wrote in the history "Unfortunately the beep cannot be avoided". I will try your workaround.
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com
- ghisler(Author)
- Site Admin
- Posts: 50390
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
I tried your workaround, but it didn't work.
Moving thread to "will not be changed".
Moving thread to "will not be changed".
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com
I thought that your TInEdit control is derived from TEdit (which omits calling the original window procedure when char is #0), but it is probably derived directly from Windows EDIT class and it calls original window procedure even for #0 char.
So the working solution is:
We are not changing wParam to #0, but we set Result to 0 (= WM_CHAR message handled) and omit calling original window procedure.
I checked (with a debugger) the code of the original EDIT window procedure. It beeps for chars with code <= 32 (space), except for some chars like Ctrl+C or backspace - these chars must be passed to the original window procedure.
Regards!
So the working solution is:
Code: Select all
function EditWndProc(Handle : HWND; Msg : UINT; wParam : WPARAM; lParam : LPARAM) : LRESULT; stdcall;
begin
with TEdit(FindControl(Handle)) do
begin
{Disable "ding" sound}
if Msg = WM_CHAR then
{3 = Ctrl+C (Copy), 8 = VK_BACK, 22 = Ctrl+V (Paste), 24 = Ctrl+X (Cut),
26 = Ctrl+Z (Undo), 30 = unicode separator, 31 = unicode separator}
{24 = Ctrl+X dings when no text is selected and this behavior should
be preserved, so we don't exit for Ctrl+X}
if Char(wParam) in [#0..#2,#4..#7,#9..#21,#23,#25,#27..#29] then
begin
Result:=0;
Exit;
end;
if Msg = WM_DESTROY then
SetWindowLong(Handle,GWL_WNDPROC,Tag);
Result:=CallWindowProc(Pointer(Tag),Handle,Msg,wParam,lParam);
end;
end;
I checked (with a debugger) the code of the original EDIT window procedure. It beeps for chars with code <= 32 (space), except for some chars like Ctrl+C or backspace - these chars must be passed to the original window procedure.
Regards!
- ghisler(Author)
- Site Admin
- Posts: 50390
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
Thanks, I will try your code. Hopefully this will not break input of Chinese or other double byte charsets...
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com
Important remark. I used Char, but it's mapped to AnsiChar (not WideChar) in Delphi 2. So another small fix - I added checking for > 255 characters and changed Char to AnsiChar to be sure to check for 1-byte value in all Delphi versions:
The code above is based on the EDIT message handling in user32.dll (checked with a debugger - there is even 'EditWndProc' export in user32.dll), so I should work properly - DefWindowProc only dings and exits in these cases (for single-line edit controls and when edit control is not a part of the combobox).
Code: Select all
function EditWndProc(Handle : HWND; Msg : UINT; wParam : WPARAM; lParam : LPARAM) : LRESULT; stdcall;
begin
with TEdit(FindControl(Handle)) do
begin
{Disable "ding" sound}
if Msg = WM_CHAR then
{3 = Ctrl+C (Copy), 8 = VK_BACK, 22 = Ctrl+V (Paste), 24 = Ctrl+X (Cut),
26 = Ctrl+Z (Undo), 30 = unicode separator, 31 = unicode separator}
{24 = Ctrl+X dings when no text is selected and this behavior should
be preserved, so we don't exit for Ctrl+X}
if (Word(wParam) < 256) and (AnsiChar(wParam) in [#0..#2,#4..#7,#9..#21,#23,#25,#27..#29]) then
begin
Result:=0;
Exit;
end;
if Msg = WM_DESTROY then
SetWindowLong(Handle,GWL_WNDPROC,Tag);
Result:=CallWindowProc(Pointer(Tag),Handle,Msg,wParam,lParam);
end;
end;
- ghisler(Author)
- Site Admin
- Posts: 50390
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
The problem is only with the 64-bit version, which is using Lazarus, not Delphi 2. I'm just using
which seems to work just fine.
Code: Select all
if WideChar(wParam) in [#0..#2,#4..#7,#9..#21,#23,#25,#27..#29] then
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com
Checking if WideChar belongs to set of chars is not safe, at least in Delphi (I didn't make any tests with Lazarus). With Delphi < 2009 this construction can't even be compiled. With newer Delphi versions it can be compiled, but with warning. I created the following small test and tested it with Delphi XE2:
Test1 sets caption to 'ok', but Test2 sets it to 'error'. We get the following warning message for Test2: "WideChar reduced to byte char in set expressions. Consider using 'CharInSet' function in 'SysUtils' unit". And - as it can be seen in assembler - for some reason Test2 compares C not with #161, but with #711.
And the suggested CharInSet function is:
So "if (Word(wParam) < 256) and (AnsiChar(wParam) in [...]) then" construction seems to be more safe and more universal than "if WideChar(wParam) in [...] then".
Some references:
W1050: WideChar reduced to byte char in set expressions (Delphi)
Delphi, how to avoid unicode warning message in D2009, D2010
Code: Select all
procedure TForm1.Button1Click(Sender: TObject);
var
C : AnsiChar;
begin
C:=#161;
if C in [#161] then
Caption:='ok'
else
Caption:='error';
end;
procedure TForm1.Button2Click(Sender: TObject);
var
C : WideChar;
begin
C:=#161;
if C in [#161] then
Caption:='ok'
else
Caption:='error';
end;
And the suggested CharInSet function is:
Code: Select all
function CharInSet(C: WideChar; const CharSet: TSysCharSet): Boolean;
begin
Result := (C < #$0100) and (AnsiChar(C) in CharSet);
end;
Some references:
W1050: WideChar reduced to byte char in set expressions (Delphi)
Delphi, how to avoid unicode warning message in D2009, D2010
- ghisler(Author)
- Site Admin
- Posts: 50390
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
I didn't encounter any problems with Lazarus, but I will still chage it to
Code: Select all
if (wparam<255) and (char(wParam) in [#0..#2,#4..#7,#9..#21,#23,#25,#27..#29]) then
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com
- ghisler(Author)
- Site Admin
- Posts: 50390
- Joined: 2003-02-04, 09:46 UTC
- Location: Switzerland
- Contact:
Not in Lazarus...
Please try with RC4 whether it works as intended or not.
Please try with RC4 whether it works as intended or not.
Author of Total Commander
https://www.ghisler.com
https://www.ghisler.com