Anyway, string gadgets use string arrays for their completion. So do something like this...
Code: Select all
Dim Array01.s(10)
Array01(0) = "Jack"
Array01(1) = "and"
Array01(2) = "Jill"
Array01(3) = "went"
Array01(4) = "up"
Array01(5) = "the"
Array01(6) = "hill"
Array01(7) = "to"
Array01(8) = "fetch"
Array01(9) = "a"
Array01(10) = "pail"
Code: Select all
ac_SetAutocomplete(#StringTest, @Array01())
Code: Select all
ac_SetAutocomplete(#ComboTest, 0)
You can remove autocompletion during the program by calling
Code: Select all
ac_RemoveAutocomplete(#comboTest)
Code: Select all
ac_DestroyAutocomplete()
Here's my test form - saved as "Main.pb"
Code: Select all
; By Xombie - 12/12/2005
;
Enumeration ; Window List
#WindowMain
EndEnumeration
Enumeration ; Menu List
#MenuMain
EndEnumeration
Enumeration ; Control List
#ButtonTest
#StringTest
#StringTest2
#ComboTest
EndEnumeration
;- Global Variables
Dim Array01.s(10)
Dim array02.s(20)
;- Includes
XIncludeFile "Autocomplete.pb"
;- Main Program
DoQuit.b
;
If OpenWindow(#WindowMain, 100, 300, 300, 200, #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget, "Test")
;
If CreateGadgetList(WindowID())
AdvancedGadgetEvents(#True)
StringGadget(#StringTest, 0, 0, 100, 20, "")
StringGadget(#StringTest2, 0, 21, 100, 20, "")
ComboBoxGadget(#ComboTest, GadgetX(#StringTest2), GadgetY(#StringTest2) + GadgetHeight(#StringTest2) + 1, 100, 200, #PB_ComboBox_Editable)
ButtonGadget(#ButtonTest, GadgetX(#ComboTest), GadgetY(#ComboTest) + GadgetHeight(#ComboTest) + 1, 100, 20, "Test")
EndIf
;
;{ Create autocomplete lists
;
;/ Create items for the combobox.
AddGadgetItem(#ComboTest, -1, "One")
AddGadgetItem(#ComboTest, -1, "Two")
AddGadgetItem(#ComboTest, -1, "Three")
AddGadgetItem(#ComboTest, -1, "Four")
AddGadgetItem(#ComboTest, -1, "Five")
AddGadgetItem(#ComboTest, -1, "Six")
AddGadgetItem(#ComboTest, -1, "Seven")
AddGadgetItem(#ComboTest, -1, "Eight")
AddGadgetItem(#ComboTest, -1, "Nine")
AddGadgetItem(#ComboTest, -1, "Ten")
AddGadgetItem(#ComboTest, -1, "The")
AddGadgetItem(#ComboTest, -1, "quick")
AddGadgetItem(#ComboTest, -1, "brown")
AddGadgetItem(#ComboTest, -1, "fox")
AddGadgetItem(#ComboTest, -1, "jumped")
AddGadgetItem(#ComboTest, -1, "over")
AddGadgetItem(#ComboTest, -1, "the")
AddGadgetItem(#ComboTest, -1, "two")
AddGadgetItem(#ComboTest, -1, "lazy")
AddGadgetItem(#ComboTest, -1, "dogs")
;/ Create items for the first string
Array01(0) = "Jack"
Array01(1) = "and"
Array01(2) = "Jill"
Array01(3) = "went"
Array01(4) = "up"
Array01(5) = "the"
Array01(6) = "hill"
Array01(7) = "to"
Array01(8) = "fetch"
Array01(9) = "a"
Array01(10) = "pail"
;/ Create items for the second string
array02(0) = "Jack"
array02(1) = "Sprat"
array02(2) = "could"
array02(3) = "eat"
array02(4) = "no"
array02(5) = "fat"
array02(6) = "and"
array02(7) = "his"
array02(8) = "wife"
array02(9) = "could"
array02(10) = "eat"
array02(11) = "no"
array02(12) = "lean"
array02(13) = "but"
array02(14) = "between"
array02(15) = "the"
array02(16) = "both"
array02(17) = "of"
array02(18) = "them"
array02(19) = "they"
array02(20) = "licked"
;}
;
ac_SetAutocomplete(#StringTest, @Array01())
;
ac_SetAutocomplete(#StringTest2, @array02())
;
ac_SetAutocomplete(#ComboTest, 0)
;
Repeat
;
EventID.l = WaitWindowEvent()
;
If EventID = #PB_Event_CloseWindow ; If the user has pressed on the close button
;
DoQuit = #True
;
ElseIf EventID = #PB_Event_Gadget
;
If EventGadgetID() = #ButtonTest
;
If EventType() = #PB_EventType_LeftClick
;
;
EndIf
;
EndIf
;
EndIf
;
Until DoQuit = #True
EndIf
ac_DestroyAutocomplete()
; Remove all autocompleting items.
End
Code: Select all
;
;- Coded by Xombie 12/19/2005
;
;- Enumeration
Enumeration ; Control Type Enumeration
#ac_String
#ac_Combo
EndEnumeration
;- Structures
Structure s_ACStringStructure
FakeString.s[0]
EndStructure
Structure s_AutoComplete
;
Gadget.l
Handle.l
Parent.l
CallBack.l
; The old callback handle.
ArrayAddress.l
; Only valid for edit controls, this will be the address of the first element in an array of strings.
EndStructure
;- Global Variables
NewList _ac_Main.s_AutoComplete()
;- Helper Functions
Procedure.b ac_GetGadgetType(Handle.l)
; Return the gadget type based on the classname used in CreateWindowExW_()
HoldString.s
; This will store the length of the wide character string, in characters.
lCount.l
; Used to store the number of character copied.
HoldString = Space(255)
; Allocate size for our string.
GetClassName_(Handle, @HoldString, 255)
; Call our function to retrieve the classname.
If HoldString = "Edit"
ProcedureReturn #ac_String
ElseIf HoldString = "ComboBox"
ProcedureReturn #ac_Combo
EndIf
;
EndProcedure
Procedure.b ac_GadgetExists(Gadget.l)
;
ResetList(_ac_Main())
While NextElement(_ac_Main())
If _ac_Main()\Gadget = Gadget : ProcedureReturn #True : EndIf
Wend
;
ProcedureReturn #False
;
EndProcedure
Procedure.b ac_GetOldCallback(Handle.l)
;
ResetList(_ac_Main())
While NextElement(_ac_Main())
If _ac_Main()\Handle = Handle : ProcedureReturn _ac_Main()\CallBack : EndIf
Wend
;
ProcedureReturn -1
;
EndProcedure
;- Callback Functions
Procedure.l ac_HandleEditEvents(HandleWindow.l, Message.l, wParam.l, lParam.l)
; Custom callback for edit controls.
lResult.l
;
CallBack.l
;
Gadget.l
;
ArrayAddress.l
;
iLoop.l
;
HoldTotal.l
;
HoldLength.l
;
HoldCombinedLength.l
;
HoldStart.l
;
HoldEnd.l
;
HoldSelection.l
;
HoldString.s
;
PreText.s
;
PostText.s
;
CombinedText.s
;
MatchText.s
;
*Position.s_ACStringStructure
; This will be a pointer to the strings in the array.
CallBack = ac_GetOldCallback(HandleWindow)
; Return the old callback procedure address.
If CallBack = -1 : ProcedureReturn : EndIf
; This should never happen as this callback is only set for autocomplete items.
ResetList(_ac_Main())
While NextElement(_ac_Main())
If _ac_Main()\Handle = HandleWindow : ArrayAddress = _ac_Main()\ArrayAddress : Gadget = _ac_Main()\Gadget : Break : EndIf
Wend
;
HoldTotal = PeekL(ArrayAddress - 8)
; Return the number of items in the array.
If HoldTotal = 0 : ProcedureReturn : EndIf
; No need to complete if 0 items.
*Position = ArrayAddress
; Point to the first element in the array.
If Message = #WM_CHAR
;
HoldString = GetGadgetText(Gadget)
; Store the current text.
HoldLength = Len(HoldString)
;
SendMessage_(HandleWindow, #EM_GETSEL, @HoldStart, @HoldEnd)
; Store the start and end selection values.
PreText = Mid(HoldString, 1, HoldStart)
; The text before the selection.
PostText = Mid(HoldString, HoldEnd + 1, HoldLength - HoldEnd)
; The text after the selection.
CombinedText = LCase(PreText + PostText + Chr(wParam))
;
HoldCombinedLength = Len(CombinedText)
;
For iLoop = 0 To HoldTotal - 1
; Loop through all items in the array.
MatchText = PeekS(*Position\FakeString[iLoop])
; Store the text at index iLoop. This little trick of getting the items in a array by addresses is thanks (again) to freak ( http://forums.purebasic.com/english/viewtopic.php?t=15366 )
If LCase(Left(MatchText, HoldCombinedLength)) = CombinedText
; Found a matching item in the combobox.
SetGadgetText(Gadget, MatchText)
;
SendMessage_(HandleWindow, #EM_SETSEL, HoldCombinedLength, -1)
;
wParam = 0
;
Break
; Exit the loop.
EndIf
;
Next iLoop
;
If wParam <> 0 : lResult = CallWindowProc_(CallBack, HandleWindow, Message, wParam, lParam) : EndIf
;
Else
;
lResult = CallWindowProc_(CallBack, HandleWindow, Message, wParam, lParam)
;
EndIf
;
ProcedureReturn lResult
;
EndProcedure
Procedure.l ac_HandleComboEvents(HandleWindow.l, Message.l, wParam.l, lParam.l)
; Custom callback for combobox controls.
lResult.l
;
CallBack.l
;
Gadget.l
;
Parent.l
;
iLoop.l
;
HoldTotal.l
;
HoldLength.l
;
HoldCombinedLength.l
;
HoldStart.l
;
HoldEnd.l
;
HoldSelection.l
;
HoldString.s
;
PreText.s
;
PostText.s
;
CombinedText.s
;
MatchText.s
;
CallBack = ac_GetOldCallback(HandleWindow)
; Return the old callback procedure address.
If CallBack = -1 : ProcedureReturn : EndIf
; This should never happen as this callback is only set for autocomplete items.
ResetList(_ac_Main())
While NextElement(_ac_Main())
If _ac_Main()\Handle = HandleWindow : Parent = _ac_Main()\Parent : Gadget = _ac_Main()\Gadget : Break : EndIf
Wend
; Retrieve the handle to the combobox and the gadget id.
If Message = #WM_CHAR
;
HoldTotal = SendMessage_(Parent, #CB_GETCOUNT, 0, 0)
; Return the number of items in the combobox.
If HoldTotal
; Only need to check for autocompletion if items exist in the combobox.
HoldString = GetGadgetText(Gadget)
; Store the current combobox text.
HoldLength = Len(HoldString)
;
SendMessage_(Parent, #CB_GETEDITSEL, @HoldStart, @HoldEnd)
; Store the start and end selection values.
PreText = Mid(HoldString, 1, HoldStart)
; The text before the selection.
PostText = Mid(HoldString, HoldEnd + 1, HoldLength - HoldEnd)
; The text after the selection.
CombinedText = LCase(PreText + PostText + Chr(wParam))
;
HoldCombinedLength = Len(CombinedText)
;
For iLoop = 0 To HoldTotal - 1
; Loop through all items in the combo box.
MatchText = GetGadgetItemText(Gadget, iLoop, 0)
; Store the text at index iLoop.
If LCase(Left(MatchText, HoldCombinedLength)) = CombinedText
; Found a matching item in the combobox.
SetGadgetText(Gadget, MatchText)
;
HoldSelection = HoldCombinedLength | -1 << 16
; Convert the start and end selection into an lParam. The start position is the combined length of the pre
; and post text. The end is set to -1 to select the rest of the text.
SendMessage_(Parent, #CB_SETEDITSEL, 0, HoldSelection)
;
wParam = 0
;
Break
; Exit the loop.
EndIf
;
Next iLoop
;
If wParam <> 0 : lResult = CallWindowProc_(CallBack, HandleWindow, Message, wParam, lParam) : EndIf
;
Else
;
lResult = CallWindowProc_(CallBack, HandleWindow, Message, wParam, lParam)
;
EndIf
;
Else
;
lResult = CallWindowProc_(CallBack, HandleWindow, Message, wParam, lParam)
;
EndIf
;
ProcedureReturn lResult
;
EndProcedure
;- Main Functions
Procedure ac_SetAutocomplete(Gadget.l, EditGadgetArrayAddress.l)
; EditGadgetArrayAddress is exactly that. The address to the first element of a plain string array. This will be used
; for the autocompletion list. Feel free to modify this to handle linked lists or whatever. It will be ignored for combobox.
Type.b
;
Handle.l
;
If ac_GadgetExists(Gadget) : ProcedureReturn : EndIf
; Check if the gadget already exists in the autocomplete list.
Handle = GadgetID(Gadget)
;
Type = ac_GetGadgetType(Handle)
;
If Type = #ac_String
; String type.
If EditGadgetArrayAddress = 0 : ProcedureReturn : EndIf
; An edit control must be associated with an array address.
AddElement(_ac_Main())
;
_ac_Main()\Gadget = Gadget
_ac_Main()\Handle = Handle
; The handle to the edit control.
_ac_Main()\Parent = 0
; An edit control is not like a combobox. Does not have a parent container.
_ac_Main()\CallBack = SetWindowLong_(Handle, #GWL_WNDPROC, @ac_HandleEditEvents())
; The callback will be set for the edit control, not the combobox.
_ac_Main()\ArrayAddress = EditGadgetArrayAddress
;
ElseIf Type = #ac_Combo
; Combobox type
AddElement(_ac_Main())
;
_ac_Main()\Gadget = Gadget
_ac_Main()\Handle = GetWindow_(Handle, #GW_CHILD)
; This is the handle to the edit control within the combobox. I think I got this little tidbit from fr34k but don't quote me on that.
_ac_Main()\Parent = Handle
; This will be the handle to the combobox control.
_ac_Main()\CallBack = SetWindowLong_(_ac_Main()\Handle, #GWL_WNDPROC, @ac_HandleComboEvents())
; The callback will be set for the edit control, not the combobox.
_ac_Main()\ArrayAddress = 0
;
Else
; Not a combo or string type. Currently, no other controls are supported.
ProcedureReturn
;
EndIf
;
EndProcedure
Procedure ac_RemoveAutocomplete(Gadget.l)
;
Handle.l
;
If ac_GadgetExists(Gadget) : ProcedureReturn : EndIf
; Check if the gadget already exists in the autocomplete list.
Handle = GadgetID(Gadget)
;
ResetList(_ac_Main())
While NextElement(_ac_Main())
If _ac_Main()\Gadget = Gadget : SetWindowLong_(Handle, #GWL_WNDPROC, _ac_Main()\CallBack) : Break : EndIf
Wend
;
EndProcedure
Procedure ac_DestroyAutocomplete()
; Remove all autocomplete items.
If CountList(_ac_Main()) = 0 : ProcedureReturn : EndIf
; No need to destroy if no items in the list.
ResetList(_ac_Main())
While NextElement(_ac_Main())
SetWindowLong_(Handle, #GWL_WNDPROC, _ac_Main()\CallBack)
Wend
;
EndProcedure