The following is some code I pulled from the PB forums to handle
autocompletion in combo boxes. However it won't let me exit the control.
I've tried TAB and ENTER but find that I must use the mouse to get to the
next control. Can anyone help and tell me what needs to be fixed?
Thanks.
Function LOGIN_CBUSER_CBN_EDITCHANGE (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, _
idComboBox As Dword) As Long
Static fNoUpdate As Long ' used to prevent recursion(-1, 0)
Local lCaretPos As Long ' current position of caret
Local ilList As Long ' index of match in combobox
Local szItem As Asciiz * %MAX_PATH ' scratch pad
If Not fNoUpdate Then
' Note: the following autocompletes the text
' 1. Get the text the user typed
' 2. Try to find a match from the predefined strings
' 3. Save the current position of the caret
' 4. Retrieve the matching text from the combobox
' 5. Set flag to prevent recursion
' 6. Replace the text in the edit control with the matching text
' 7. Select all text from the old position of the caret to the end
' 8. Reset flag
' changed lParam to hWNdControl
' changed lpszItem to VarPtr(szItem)
SendMessage hWndControl, %WM_GETTEXT, %MAX_PATH - 1, ByVal VarPtr(szItem)
ilList = SendMessage(hWndControl, %CB_FINDSTRING, -1, ByVal VarPtr(szItem))
If ilList <> %CB_ERR Then
lCaretPos = LoWrd(SendMessage(hWndControl, %CB_GETEDITSEL, ByVal %Null, ByVal %Null))
SendMessage hWndControl, %CB_GETLBTEXT, ilList, ByVal VarPtr(szItem)
fNoUpdate = -1
SendMessage hWndControl, %WM_SETTEXT, 0, ByVal VarPtr(szItem)
SendMessage hWndControl, %CB_SETEDITSEL, 0, MakLng(lCaretPos, -1)
End If
End If
fNoUpdate = 0
End Function
[/code]
Not sure how much it helps, but in a app I made some time back I had a combo box with port numbers and descriptions and made an autocomplete for it like this:
CONTROL GET TEXT CBHNDL, %IDC_PORT TO portString
SELECT CASE CBCTLMSG
Case %CBN_EDITCHANGE
'You wouldn't need this following IF Block
If Val(portString) > 65535 Or Val(portString) < 1 Then
portString = ""
Control Set Text CbHndl, %IDC_PORT, portString
End If
lengthPortString = Len(portString)
If lengthPortString > previousSize Then
Control Send CbHndl, %IDC_PORT, %CB_SELECTSTRING, -1, VarPtr(portString) To searchResult
If searchResult = %CB_ERR Then Control Set Text CbHndl, %IDC_PORT, portString
Control Send CbHndl, %IDC_PORT, %CB_SETEDITSEL, 0, MakDwd(lengthPortString,-1)
End If
previousSize = Len(portString) ' STATIC DWORD
Of course in FF you'd need to convert the lines into SendMessage's and:
CBCTLMSG= HIWRD(wParam)
And you'd need something to prevent looping back in the callback.
IF preventLoop = 0 THEN
preventLoop = 1
{The code above}
preventLoop = 0
END IF
Here is the converted code in FF:
I made just a blank form with a Combo named Port
Function FORM1_PORT_CBN_EDITCHANGE (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idComboBox As Dword) As Long
Local searchResult As Long
Local lengthComboString As Dword
Local comboString As String
Static previousSize As Dword
Static preventLoop As Dword
If preventLoop = 0 Then
preventLoop = 1
comboString= FF_Control_GetText(HWND_FORM1_PORT)
lengthComboString = Len(comboString)
If lengthComboString > previousSize Then
searchResult= SendMessage(HWND_FORM1_PORT, %CB_SELECTSTRING, -1, StrPtr(comboString))
If searchResult = %CB_ERR Then FF_Control_SetText(HWND_FORM1_PORT, comboString)
End If
SendMessage(HWND_FORM1_PORT, %CB_SETEDITSEL, 0, MakDwd(lengthComboString,-1))
previousSize = lengthComboString
preventLoop = 0
End If
End Function
Function FORM1_WM_CREATE (hWndForm As Dword) As Long
FF_ComboBox_AddString HWND_FORM1_PORT, "7 Echo"
FF_ComboBox_AddString HWND_FORM1_PORT, "9 Discard"
FF_ComboBox_AddString HWND_FORM1_PORT, "11 Active Users"
FF_ComboBox_AddString HWND_FORM1_PORT, "13 Daytime"
FF_ComboBox_AddString HWND_FORM1_PORT, "17 Quote of Day"
FF_ComboBox_AddString HWND_FORM1_PORT, "20 FTP data"
FF_ComboBox_AddString HWND_FORM1_PORT, "21 FTP control"
FF_ComboBox_AddString HWND_FORM1_PORT, "22 SSH"
FF_ComboBox_AddString HWND_FORM1_PORT, "23 Telnet"
FF_ComboBox_AddString HWND_FORM1_PORT, "25 SMTP"
FF_ComboBox_AddString HWND_FORM1_PORT, "37 Time"
FF_ComboBox_AddString HWND_FORM1_PORT, "43 Whois"
FF_ComboBox_AddString HWND_FORM1_PORT, "53 DNS"
FF_ComboBox_AddString HWND_FORM1_PORT, "70 Gopher"
FF_ComboBox_AddString HWND_FORM1_PORT, "79 Finger"
FF_ComboBox_AddString HWND_FORM1_PORT, "80 WWW"
FF_ComboBox_AddString HWND_FORM1_PORT, "110 POP3"
FF_ComboBox_AddString HWND_FORM1_PORT, "119 Happy99"
FF_ComboBox_AddString HWND_FORM1_PORT, "135 epmap"
FF_ComboBox_AddString HWND_FORM1_PORT, "137 Netbios Name"
FF_ComboBox_AddString HWND_FORM1_PORT, "138 Netbios Datagram"
FF_ComboBox_AddString HWND_FORM1_PORT, "139 Netbios Session"
FF_ComboBox_AddString HWND_FORM1_PORT, "445 MS DS LANMAN Messages"
FF_ComboBox_AddString HWND_FORM1_PORT, "1214 KaZaA"
FF_ComboBox_AddString HWND_FORM1_PORT, "1243 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "1433 MS SQL"
FF_ComboBox_AddString HWND_FORM1_PORT, "1434 MS SQL"
FF_ComboBox_AddString HWND_FORM1_PORT, "1719 Elthe Phone"
FF_ComboBox_AddString HWND_FORM1_PORT, "1720 Elthe Phone"
FF_ComboBox_AddString HWND_FORM1_PORT, "5631 PCanywhere Remote"
FF_ComboBox_AddString HWND_FORM1_PORT, "5632 PCanywhere Ping"
FF_ComboBox_AddString HWND_FORM1_PORT, "5634 Old Gnutella"
FF_ComboBox_AddString HWND_FORM1_PORT, "6346 Gnutella"
FF_ComboBox_AddString HWND_FORM1_PORT, "6347 Gnutella 2"
FF_ComboBox_AddString HWND_FORM1_PORT, "6699 Napster"
FF_ComboBox_AddString HWND_FORM1_PORT, "6667 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6711 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6712 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6713 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6776 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "8787 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "8879 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "12345 Amitis.B"
FF_ComboBox_AddString HWND_FORM1_PORT, "16959 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "17300 Kuang2 Virus"
FF_ComboBox_AddString HWND_FORM1_PORT, "53001 Remote Shutdown"
FF_ComboBox_AddString HWND_FORM1_PORT, "54320 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "54321 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "65535 Linux Worm"
End Function
I'm still looking into why the tabs won't work though...
Looks like this isn't just a problem with autocomplete. You are going to need to subclass the edit box "child" of the combo and get the keystroke messages. All but VK_TAB you'd need to forward to the previous proc for the combo and VK_TAB you'd forward to the main window.
Strange ( meaning ... I don't understand ).
I started out with a style of CBS_DropDown so that the list wouldn't
show up unless the user clicked & wanted it. This is seeming to prevent
an exit of the combo box. If I change to CBS_DropDownList then the
first time the list shows and I can exit the field without problem. The next
time into the field, no list shows but the autocompletion is still working
and exits the field okay.
I'll have to check if this is normal for PB or something to do with FF.
Complete code with subclassing:
Global gOldSubClassProc As Dword
Function FORM1_PORT_CBN_EDITCHANGE (ControlIndex As Long, hWndForm As Dword, hWndControl As Dword, idComboBox As Dword) As Long
Local searchResult As Long
Local lengthComboString As Dword
Local comboString As String
Static previousSize As Dword
Static preventLoop As Dword
If preventLoop = 0 Then
preventLoop = 1
comboString= FF_Control_GetText(HWND_FORM1_PORT)
lengthComboString = Len(comboString)
If lengthComboString > previousSize Then
searchResult= SendMessage(HWND_FORM1_PORT, %CB_SELECTSTRING, -1, StrPtr(comboString))
If searchResult = %CB_ERR Then FF_Control_SetText(HWND_FORM1_PORT, comboString)
End If
SendMessage(HWND_FORM1_PORT, %CB_SETEDITSEL, 0, MakDwd(lengthComboString,-1))
previousSize = lengthComboString
preventLoop = 0
End If
End Function
Function CbSubClassProc(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long ' Process our messages in this subclass procedure
If wMsg= %WM_CHAR And wParam= %VK_TAB Then
SendMessage(HWND_FORM1_PORT, wMsg, wParam, lParam)
Function= %TRUE
Else
Function = CallWindowProc(gOldSubClassProc, hWnd, wMsg, wParam, lParam)
End If
End Function
Function FORM1_WM_CREATE (hWndForm As Dword) As Long
gOldSubClassProc = SetWindowLong(GetDlgItem(HWND_FORM1_PORT, 1001), %GWL_WNDPROC, CodePtr(CbSubClassProc))
FF_ComboBox_AddString HWND_FORM1_PORT, "7 Echo"
FF_ComboBox_AddString HWND_FORM1_PORT, "9 Discard"
FF_ComboBox_AddString HWND_FORM1_PORT, "11 Active Users"
FF_ComboBox_AddString HWND_FORM1_PORT, "13 Daytime"
FF_ComboBox_AddString HWND_FORM1_PORT, "17 Quote of Day"
FF_ComboBox_AddString HWND_FORM1_PORT, "20 FTP data"
FF_ComboBox_AddString HWND_FORM1_PORT, "21 FTP control"
FF_ComboBox_AddString HWND_FORM1_PORT, "22 SSH"
FF_ComboBox_AddString HWND_FORM1_PORT, "23 Telnet"
FF_ComboBox_AddString HWND_FORM1_PORT, "25 SMTP"
FF_ComboBox_AddString HWND_FORM1_PORT, "37 Time"
FF_ComboBox_AddString HWND_FORM1_PORT, "43 Whois"
FF_ComboBox_AddString HWND_FORM1_PORT, "53 DNS"
FF_ComboBox_AddString HWND_FORM1_PORT, "70 Gopher"
FF_ComboBox_AddString HWND_FORM1_PORT, "79 Finger"
FF_ComboBox_AddString HWND_FORM1_PORT, "80 WWW"
FF_ComboBox_AddString HWND_FORM1_PORT, "110 POP3"
FF_ComboBox_AddString HWND_FORM1_PORT, "119 Happy99"
FF_ComboBox_AddString HWND_FORM1_PORT, "135 epmap"
FF_ComboBox_AddString HWND_FORM1_PORT, "137 Netbios Name"
FF_ComboBox_AddString HWND_FORM1_PORT, "138 Netbios Datagram"
FF_ComboBox_AddString HWND_FORM1_PORT, "139 Netbios Session"
FF_ComboBox_AddString HWND_FORM1_PORT, "445 MS DS LANMAN Messages"
FF_ComboBox_AddString HWND_FORM1_PORT, "1214 KaZaA"
FF_ComboBox_AddString HWND_FORM1_PORT, "1243 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "1433 MS SQL"
FF_ComboBox_AddString HWND_FORM1_PORT, "1434 MS SQL"
FF_ComboBox_AddString HWND_FORM1_PORT, "1719 Elthe Phone"
FF_ComboBox_AddString HWND_FORM1_PORT, "1720 Elthe Phone"
FF_ComboBox_AddString HWND_FORM1_PORT, "5631 PCanywhere Remote"
FF_ComboBox_AddString HWND_FORM1_PORT, "5632 PCanywhere Ping"
FF_ComboBox_AddString HWND_FORM1_PORT, "5634 Old Gnutella"
FF_ComboBox_AddString HWND_FORM1_PORT, "6346 Gnutella"
FF_ComboBox_AddString HWND_FORM1_PORT, "6347 Gnutella 2"
FF_ComboBox_AddString HWND_FORM1_PORT, "6699 Napster"
FF_ComboBox_AddString HWND_FORM1_PORT, "6667 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6711 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6712 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6713 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "6776 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "8787 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "8879 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "12345 Amitis.B"
FF_ComboBox_AddString HWND_FORM1_PORT, "16959 Sub 7"
FF_ComboBox_AddString HWND_FORM1_PORT, "17300 Kuang2 Virus"
FF_ComboBox_AddString HWND_FORM1_PORT, "53001 Remote Shutdown"
FF_ComboBox_AddString HWND_FORM1_PORT, "54320 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "54321 BackOriface"
FF_ComboBox_AddString HWND_FORM1_PORT, "65535 Linux Worm"
End Function
Function FORM1_WM_DESTROY (hWndForm As Dword) As Long
SetWindowLong(GetDlgItem(HWND_FORM1_PORT, 1001), %GWL_WNDPROC, gOldSubClassProc)
End Function
This is not working because this type of combobox is composed of two controls; and edit control and listbox. I am modifying FireFly's code to subclass the edit control portion as well thus allowing FireFly to handle the keyboard movements.
Hey Paul, I just posted the code you need...hehehe
This only needs done on CBS_DROPDOWN and CBS_SIMPLE style combos too.
This would make a cool new control for FF. Maybe even with an option in the property sheet to flip the list of the combo open or not like IE's url bar does. (CB_SHOWDROPDOWN If searchResult <> %CB_ERR)
Roger,
Thanks. I'll see if I can plug this into my program.
---------------------------
Paul,
Will FF be setting the CB_SETEXTENDEDUI setting for Combo boxes?
I just checked with PB Forms and it was setting the style manually in
code. and it seemed to do the autocompletion without extra code on
my part. but then I found a glitch ( on my system at least ) with the
properties tabs in PB Forms and sent a request to their support. ( The
3rd tab is bleeding over into the 2nd tab. Well, that's their problem )
Will this change be in v1.06 and will it be out soon? No real rush as I'm
still learning this stuff and have a ways to go before I put anything into
production.
Quote from: Roger GarstangHey Paul, I just posted the code you need...hehehe
This only needs done on CBS_DROPDOWN and CBS_SIMPLE style combos too.
Thanks Roger - you're certainly on top of things. :)
I am modifying the generated source code to automatically do the subclassing so the user won't have to.
Quote from: TechSupportThanks Roger - you're certainly on top of things. :)
Yeah, I was thinking Roger was having way to much fun with this. :wink:
You mean you don't have fun with FireFly? Man, I live on this forum just to see what new toys I can play with. FF is awesome!
That, and the one app I used the AutoComplete in also had Radio Buttons I Subclassed and made into status lights...so I had the subclassing code to cut and paste too. That same App also had a %BS_PUSHLIKE button too I used as a toggle...hopefully Paul saw my WishList request to have that and the other non-auto styles for checkboxes/radio buttons available as properties/styles.
I've still got a small problem. If I start typing and it finds a match so far,
it won't let me backspace to clear the whole entry. As far as I'm concerned
( as a user ), I've typed 1 character and it automatically filled in the rest.
But then I change my mind and want to simply backspace over the 1
character I've typed to start over ( say I'm a lousy typist ). It won't let
me and is frustrating. It takes a lot of keystrokes to get back to an empty
field to start typing again and my users won't like it.
I found a routine for VB that so far does what I want but it gets called from
the keypress event. I'm not sure but with the latest FF doing the sub-
classing, will I have access to that routine? In v1.05 I probably could have
done it when I created the sub-classing.
I'm just trying to get the best of all possible solutions. I'm currently
creating versions of roughly the same program in FF & EZGui & Delphi
and VB.
The easiest version for the users will probably be the one used.
Thanks for your help.
That is what the line:
If lengthComboString > previousSize Then
is supposed to do, then it only does it if the character typed makes it larger, backspace makes it <= previousSize so it shouldn't process when backspace is pressed.
Okay, I had missed that line. But had to change it to <> instead of >
to get previoous matches. some of my lists have similar entries.
Wouldn't this have to be another static variable per combo box?
Thanks. Now on to other things ( as soon as the changing of TABS can
be made under program control ).
Yes it is a STATIC variable. It is all in the code. And, you will have it all in the CBN_EDITCHANGE function of each combo, so they will all have their own STATIC variables, etc. You wouldn't want this in say a CUSTOM function of your main form that handles for all combos as they will all be different lengths. <> works, but I had it the other way cause usually when I hit backspace I expect it to cancel out whatever it is doing, like in PB when it changes the case for keywords...backspace puts it back and leaves it the way it was...or in IE when you keep hitting backspace in the URL box it keeps the list fresh, but doesn't autocomplete again until you type a new character.
I just wonder about the status of this, does the subclass code still need to be included?
It seem to work ok, so far, but haven't tested it that much yet. I'm also trying to extend it a bit to actually do something as well in response to the changed selection.
How can I catch that the user hit Enter (or Tab) to "select" the displayed value for use and "move on"?
/Joakim
Good question, I didn't see anything about it in the last update...I'm kinda curious about the other button styles too that aren't AUTO that I mentioned, so toggle buttons would be easier.
As for the extending question, Tab already selects and moves on, Enter would just be another character to process in the subclassed procedure where the tab is...and to make it a little more reusable, you might change it to sending Tab to the parent of the edit control which would be the combo instead of the hard coded handle...and perhaps send the Enter key to the parent of the parent (Combos parent or the Form/Window containing it)...that way it could handle Enter as being the default button push or something.
As for doing something on changing selection, you'd do that in the normal SelChange event for the combo.
Quote from: Roger GarstangAs for the extending question, Tab already selects and moves on, Enter would just be another character to process in the subclassed procedure where the tab is...and to make it a little more reusable, you might change it to sending Tab to the parent of the edit control which would be the combo instead of the hard coded handle...and perhaps send the Enter key to the parent of the parent (Combos parent or the Form/Window containing it)...that way it could handle Enter as being the default button push or something.
Hmm not sure I understood that really, I'm pretty new to PB and FF programing, have had the VB desease for many years... :-)
Quote from: Roger Garstang
As for doing something on changing selection, you'd do that in the normal SelChange event for the combo.
Yes, I understand that but here I'm a bit spoiled by VB, setting the selection in code in VB usually triggers a click event or something to react to, but setting the selection in code with FF (at least with FF_COMBOBOX_SETCURSEL) doesn't seem to trigger code in FUNCTION FORM1_COMBO1_CBN_SELCHANGE or _SELENDOK, only seem to happen when a selection is done in the control, or am I missing something here?
The modification I have done to your original code is this
LOCAL MyData,searchResult AS LONG
Local lengthComboString As Dword
Local comboString As String
Static previousSize As Dword
Static preventLoop As Dword
If preventLoop = 0 Then
preventLoop = 1
comboString= FF_CONTROL_GETTEXT(hWndControl)
lengthComboString = Len(comboString)
If lengthComboString > previousSize Then
searchResult= SENDMESSAGE(hWndControl, %CB_SELECTSTRING, -1, STRPTR(comboString))
IF searchResult = %CB_ERR THEN
FF_CONTROL_SETTEXT(hWndControl, comboString)
ELSE
MyData = FF_COMBOBOX_GETITEMDATA (HWND_FRMATLASVIEW_CBOCOUNTRY, searchResult )
FillListbox FORMAT$(MyData , "000")
END IF
End If
SENDMESSAGE(hWndControl, %CB_SETEDITSEL, 0, MAKDWD(lengthComboString,-1))
previousSize = lengthComboString
preventLoop = 0
End If
I simply pull a stored data Item from the combo and send it to a sub that will fill a list box based on it. That seem to work fine, but when user hits Enter in the combo edit portion I want it to make focus jump to the list box, just that. I'm sure it's an easy one, I just haven't found it yet ;-)
I haven't used the subclassing part btw, and it seem to work fine so far...
/Joakim
Just posted a reply over at PB that made me look for my old code here. It made me wonder if this was added to the new FF. And, my other code for allowing coloring of Combos:
(I had it documented as below, but seem to remember it not working with the combo with the list always visible or something, so it may need tweaked)
Case %WM_CTLCOLOREDIT, %WM_CTLCOLORLISTBOX, %WM_CTLCOLORSTATIC, %WM_CTLCOLORBTN
'Message received from child control about to display.
'Set the Color properties here.
'lParam is the handle of the Control. wParam is the hDC.
FF_hDC = wParam
ff_control = GetProp(lParam, "FF_PTR")
If ff_control = 0 Then
ff_control = GetProp(GetFocus(), "FF_PTR")
If ff_control = 0 Then Exit Select 'let the DefWindowProc handle the coloring
End If
Found a function I didn't know about in another thread that may help this too: GetComboBoxInfo
I cant get it to work, and my english is being forced at max. Is it working? Am i missing something? Are we waiting for a change in Firefly?
I need to add an autocomplete combobox, but when i subclass the combobox i cant get WM_CHAR nor WM_KEYDOWN to work. :(
Added:
Silly mistake, it works now. :), i just have the problem with TAB not working. my subclassing didnt work either... whats the status on this?