This code however shows how to use the IAutoComplete interface provided by
windows to do this. It provides an in-place suggestion as well as a drop-down mode.
This requires Windows 2000 or Windows ME btw.
The strings need to be provided through an IEnumString interface.
I created an Includefile to create such an interface easily from an array of strings.
Note that this IEnumString implementation is independant of the rest, so if you need an IEnumString
for some other reason and happen to stumble upon this thread, you found your solution
Example code:
Code: Select all
XIncludeFile "IEnumString.pb"
#ACO_NONE = 0 ; No autocompletion.
#ACO_AUTOSUGGEST = $1 ; Enable the autosuggest drop-down list.
#ACO_AUTOAPPEND = $2 ; Enable autoappend.
#ACO_SEARCH = $4 ; Add a search item to the dropdown list of completed strings. If this is item is selected, you should launch a search engine to assist the user.
#ACO_FILTERPREFIXES = $8 ; Don't match common prefixes, such as "www.", "http://", and so on.
#ACO_USETAB = $10 ; Use the TAB key to select an item from the drop-down list. This flag is disabled by default.
#ACO_UPDOWNKEYDROPSLIST = $20 ; Use the UP ARROW and DOWN ARROW keys to display the autosuggest drop-down list.
#ACO_RTLREADING = $40 ; If ACO_RTLREADING is set, the text is read in the opposite direction from the text in the parent window.
; -----------------------------------------------
#Window_0 = 0
#String_0 = 0
Dim Strings.s(17)
Strings(0) = "Else"
Strings(1) = "ElseIf"
Strings(2) = "EnableDebugger"
Strings(3) = "EnableExplicit"
Strings(4) = "End"
Strings(5) = "EndDataSection"
Strings(6) = "EndEnumeration"
Strings(7) = "EndIf"
Strings(8) = "EndImport"
Strings(9) = "EndInterface"
Strings(10) = "EndMacro"
Strings(11) = "EndProcedure"
Strings(12) = "EndSelect"
Strings(13) = "EndStructure"
Strings(14) = "EndStructureUnion"
Strings(15) = "EndWith"
Strings(16) = "Enumeration"
; Initialize the COM environment
CoInitialize_(0)
If OpenWindow(#Window_0, 323, 219, 600, 300, "AutoComplete", #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_ScreenCentered )
If CreateGadgetList(WindowID(#Window_0))
StringGadget(#String_0, 40, 40, 320, 20, "")
SetActiveGadget(#String_0)
; This creates the autocomplete object:
;
If CoCreateInstance_(?CLSID_AutoComplete, 0, 1, ?IID_IAutoComplete2, @AutoComplete.IAutoComplete2) = #S_OK
; Lets change the options for autocomplete a bit:
; See the constant definitions above for the available options.
; There is a dropdown or in-place suggestion mode.
;
AutoComplete\SetOptions(#ACO_AUTOSUGGEST|#ACO_USETAB)
; This creates the IEnumString interface from our string array:
;
Enum.IEnumString = New_EnumString(Strings(), 18)
; This sets the autocomplet with our Enum object.
; If you want more info on the last 2 parameters, read here:
; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/ifaces/iautocomplete/Init.asp
; Note that these must be unicode strings if present!
;
; This also works for Comboboxes, you just need to get the handle to the edit
; control that is contained inside with the GetComboBoxInfo_() function.
;
AutoComplete\Init(GadgetID(#String_0), Enum, 0, 0)
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
; Release our own references to the objects to avoid a memory leak
;
Enum\Release()
AutoComplete\Release()
EndIf
EndIf
EndIf
CoUninitialize_()
DataSection
CLSID_AutoComplete: ; {00BB2763-6A77-11D0-A535-00C04FD7D062}
Data.l $00BB2763
Data.w $6A77, $11D0
Data.b $A5, $35, $00, $C0, $4F, $D7, $D0, $62
IID_IAutoComplete: ; {00BB2762-6A77-11D0-A535-00C04FD7D062}
Data.l $00BB2762
Data.w $6A77, $11D0
Data.b $A5, $35, $00, $C0, $4F, $D7, $D0, $62
IID_IAutoComplete2: ; {EAC04BC0-3791-11D2-BB95-0060977B464C}
Data.l $EAC04BC0
Data.w $3791, $11D2
Data.b $BB, $95, $00, $60, $97, $7B, $46, $4C
EndDataSection
Code: Select all
;
; Generic Implementation of an IEnumString Interface.
; New_EnumString(StringArray(), ItemCount) will create a new IEnumString instance
; with a referencecount of 1. (this means you must call IEnumString\Release()
; to release this reference once you do not need it anymore.)
;
;
;
; To allow for the ::Clone() method without dublicating the buffer,
; the string buffer is kept separate, with its own reference count.
Structure EnumStringBuffer
RefCount.l
Strings.l[0]
; following is the literal string data
EndStructure
; IEnumString data
Structure EnumString
*Vtbl
RefCount.l
StringCount.l
Enumerator.l
*Buffer.EnumStringBuffer
EndStructure
Procedure IEnumString_QueryInterface(*THIS.EnumString, *IID.IID, *Object.LONG)
If *Object = 0
ProcedureReturn #E_INVALIDARG
ElseIf CompareMemory(*IID, ?IID_IUnknown, SizeOf(IID)) Or CompareMemory(*IID, ?IID_IEnumString, SizeOf(IID))
*Object\l = *THIS
*THIS\RefCount + 1
ProcedureReturn #S_OK
Else
*Object\l = 0
ProcedureReturn #E_NOINTERFACE
EndIf
EndProcedure
Procedure IEnumString_AddRef(*THIS.EnumString)
*THIS\RefCount + 1
ProcedureReturn *THIS\RefCount
EndProcedure
Procedure IEnumString_Release(*THIS.EnumString)
*THIS\RefCount - 1
If *THIS\RefCount = 0
*THIS\Buffer\RefCount - 1
If *THIS\Buffer\RefCount = 0
FreeMemory(*THIS\Buffer)
EndIf
FreeMemory(*THIS)
ProcedureReturn 0
Else
ProcedureReturn *THIS\RefCount
EndIf
EndProcedure
Procedure IEnumString_Next(*THIS.EnumString, celt, *rgelt.LONG, *pceltFetched.LONG)
If *THIS\Enumerator + celt <= *THIS\StringCount
count = celt
Else
count = *THIS\StringCount - *THIS\Enumerator
EndIf
For i = 0 To count-1
*rgelt\l = *THIS\Buffer\Strings[*THIS\Enumerator + i]
*rgelt + 4
Next i
*THIS\Enumerator + count
If *pceltFetcted
*pceltFetched\l = count
EndIf
If count = celt
ProcedureReturn #S_OK
Else
ProcedureReturn #S_FALSE
EndIf
EndProcedure
Procedure IEnumString_Skip(*THIS.EnumString, celt)
*THIS\Enumerator + celt
If *THIS\Enumerator <= *THIS\StringCount
ProcedureReturn #S_OK
Else
ProcedureReturn #S_FALSE
EndIf
EndProcedure
Procedure IEnumString_Reset(*THIS.EnumString)
*THIS\Enumerator = 0
ProcedureReturn #S_OK
EndProcedure
Procedure IEnumString_Clone(*THIS.EnumString, *ppenum.LONG)
If *ppenum = 0
ProcedureReturn #E_INVALIDARG
Else
*Clone.EnumString = AllocateMemory(SizeOf(EnumString))
If *Clone
CopyMemory(*THIS, *Clone, SizeOf(EnumString))
*Clone\RefCount = 1
*Clone\Buffer\RefCount + 1
*ppenum\l = *Clone
ProcedureReturn #S_OK
Else
*ppenum\l = 0
ProcedureReturn #E_OUTOFMEMORY
EndIf
EndIf
EndProcedure
Procedure New_EnumString(StringArray$(1), StringCount)
*THIS.EnumString = 0
Size = 4 + StringCount * 4
For i = 0 To StringCount-1
Size + Len(StringArray$(i)) * 2 + 2
Next i
*StringBuffer.EnumStringBuffer = AllocateMemory(Size)
If *StringBuffer
*StringBuffer\RefCount = 1
*Pointer = *StringBuffer + 4 + StringCount * 4
For i = 0 To StringCount - 1
*StringBuffer\Strings[i] = *Pointer
PokeS(*Pointer, StringArray$(i), -1, #PB_Unicode)
*Pointer + Len(StringArray$(i)) * 2 + 2
Next i
*THIS = AllocateMemory(SizeOf(EnumString))
If *THIS
*THIS\Vtbl = ?IEnumStringVtbl
*THIS\RefCount = 1
*THIS\StringCount = StringCount
*THIS\Enumerator = 0
*THIS\Buffer = *StringBuffer
Else
FreeMemory(*StringBuffer)
EndIf
EndIf
ProcedureReturn *THIS
EndProcedure
DataSection
IEnumStringVtbl:
Data.l @IEnumString_QueryInterface()
Data.l @IEnumString_AddRef()
Data.l @IEnumString_Release()
Data.l @IEnumString_Next()
Data.l @IEnumString_Skip()
Data.l @IEnumString_Reset()
Data.l @IEnumString_Clone()
IID_IUnknown: ; {00000000-0000-0000-C000-000000000046}
Data.l $00000000
Data.w $0000, $0000
Data.b $C0, $00, $00, $00, $00, $00, $00, $46
IID_IEnumString: ; {00000101-0000-0000-C000-000000000046}
Data.l $00000101
Data.w $0000, $0000
Data.b $C0, $00, $00, $00, $00, $00, $00, $46
EndDataSection
Oh well, just found a similar example by Justin: http://www.purebasic.fr/english/viewtopic.php?t=15627
Mine could still be usefull though, as the my IEnumString implementation
is more complete, with all methods implemented and all specified scenarios handled.
Its also simpler to use imho.