It is currently Sun Feb 23, 2020 9:17 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 36 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Autocompleting Combobox and Strings
PostPosted: Mon Dec 19, 2005 8:43 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Jul 01, 2004 2:51 am
Posts: 898
Location: Tacoma, WA
Here is an autocompletion routine for both the string gadget and combobox gadget. There are some slight bloats in variables since my own internal version has a few more things to it that are specific to my project. Feel free to remove what you like from this in order to lighten it up. The post text stuff could probably be safely removed, for example.

Anyway, string gadgets use string arrays for their completion. So do something like this...

Code:
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"


And then set the autocompleting for the string gadget by simply doing...

Code:
ac_SetAutocomplete(#StringTest, @Array01())


...that. You can have different string arrays tied to different string gadgets or the same string array tied to different string gadgets. Whatever you like. Setting autocomplete for a combobox is done with the same procedure by calling...

Code:
ac_SetAutocomplete(#ComboTest, 0)


...that. It will use the combobox's internal strings to autocomplete.

You can remove autocompletion during the program by calling

Code:
ac_RemoveAutocomplete(#comboTest)


Or remove all autocompletion (for example, at the end of your program) by calling

Code:
ac_DestroyAutocomplete()


Let me know if y'all want some other functionality built in to handle some other behavior. Or modify yourself :)

Here's my test form - saved as "Main.pb"

Code:
; 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


And here's the autocomplete routines. I have this saved as "Autocomplete.pb" for my example. Yes, I know, big shock that it's not called "xComplete.pb", right? :D

Code:
;
;- 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


There it is. Let me know if you spot any bugs or need something added to it. Have fun ^_^ Now I'm off to make more code examples that refuse to work for gnozal! :D


Top
 Profile  
Reply with quote  
 Post subject: Re: Autocompleting Combobox and Strings
PostPosted: Mon Dec 19, 2005 8:57 pm 
Offline
Addict
Addict

Joined: Wed Aug 24, 2005 8:39 am
Posts: 2736
Location: Southwest OH - USA
Xombie wrote:
There it is. Let me know if you spot any bugs or need something added to it. Have fun ^_^ Now I'm off to make more code examples that refuse to work for gnozal! :D


Excellent code plus a sense of humor. What a contributor. :lol:

Thanks for another nice one.

cheers


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Dec 19, 2005 9:15 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10577
Location: Beyond the pale...
Xombie, you're a coding machine! :lol:

I'll tuck this one away - could be useful for a future app.

Thanks for sharing.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Dec 20, 2005 3:55 am 
Offline
Addict
Addict
User avatar

Joined: Thu Jul 01, 2004 2:51 am
Posts: 898
Location: Tacoma, WA
Nah. Think of me like the military. I'll occasionally let out bits of my main project code for other people :) Also, things like this autocomplete take hardly any time. A lot of my code is reusable so I can just copy bits from other parts and put it together. So when I need something for my main project, I will put something together and think about whether it's alright to share.

Yay for Purebasic :)


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Dec 20, 2005 6:44 pm 
Offline
Addict
Addict

Joined: Wed Aug 24, 2005 8:39 am
Posts: 2736
Location: Southwest OH - USA
Xombie wrote:

Yay for Purebasic :)


Yes, yay for Purebasic, and a bigger Yay for the the people who so generously share their time and code to the benefit of all of us.

I think you know who they are :wink:

cheers


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 13, 2006 11:02 am 
Offline
Addict
Addict
User avatar

Joined: Fri Feb 25, 2005 1:01 am
Posts: 806
Location: France > Rennes
Your code does'nt run with PB4. Some help ?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 13, 2006 4:27 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Jul 01, 2004 2:51 am
Posts: 898
Location: Tacoma, WA
My apologies for not putting this into a zip file but I don't have access to my ftp server at the moment.

Save as 'Main.pb'
Code:
; 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, "Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
   ;
   If CreateGadgetList(WindowID(#WindowMain))
      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 EventGadget() = #ButtonTest
            ;
            If EventType() = #PB_EventType_LeftClick
               ;
               ;
            EndIf
            ;
         EndIf
         ;
      EndIf
      ;
   Until DoQuit = #True

EndIf

ac_DestroyAutocomplete()
; Remove all autocompleting items.
End


Save as 'Autocomplete.pb'
Code:
;
;- Coded by Xombie 12/19/2005, updated 7/13/2006
;
;- 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
Global 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.l 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


See if that works for you. I also fixed a huge (but very simple) bug that was fixed in private versions but not here :)


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jul 14, 2006 10:07 am 
Offline
Addict
Addict
User avatar

Joined: Fri Feb 25, 2005 1:01 am
Posts: 806
Location: France > Rennes
It's cool. thank you.

have you tested in an editor gadget ?

That runs but just for the first word. Any solution for running that when we want or with a comboboxgadget like in the IDE of Purebasic ?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Jul 19, 2006 11:46 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Jul 01, 2004 2:51 am
Posts: 898
Location: Tacoma, WA
I guess I don't know exactly what you're asking. Could you explain again, please?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 20, 2006 8:05 am 
Offline
Addict
Addict
User avatar

Joined: Wed Apr 30, 2003 8:15 am
Posts: 999
Location: Germany
@Xombie: please try the word 'jumped'. It will only find 'Jack' ....


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jul 20, 2006 4:18 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Jul 01, 2004 2:51 am
Posts: 898
Location: Tacoma, WA
Well, that depends on where you're doing it. The combobox doesn't have the word 'Jack' added and the only 'j' word is 'jumped' so if you start typing 'Jack' there, it will only find 'jumped'.

There are three separate completion tests. The combobox and each of the stringgadgets have their own lists. The first string gadget is the one that contains the word 'Jack' - but no 'jumped'. You can do a 'AddGadgetItem(#ComboTest, -1, "Jack")' with the rest of them to test whether it will find 'Jack' or 'jumped'.

Hope that helps ^_^


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Aug 08, 2006 8:35 am 
Offline
Addict
Addict
User avatar

Joined: Fri Feb 25, 2005 1:01 am
Posts: 806
Location: France > Rennes
Sorry Xombie, I was in holidays.

In fact, i want to add to my lib LibEditorPlus, the autocompletion functionnality. So i want to create the same autocompletion system which is in PB IDE. Have you some Idea ?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Aug 08, 2006 7:54 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Jul 01, 2004 2:51 am
Posts: 898
Location: Tacoma, WA
You mean autocompleting individual words with a popup word selector as you type? Or what? Maybe if you gave me an example?


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Wed Aug 09, 2006 7:52 am 
Offline
Addict
Addict
User avatar

Joined: Fri Feb 25, 2005 1:01 am
Posts: 806
Location: France > Rennes
You mean autocompleting individual words with a popup word selector as you type? => It's exactly that !


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Aug 10, 2006 6:39 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Jul 14, 2006 8:53 pm
Posts: 672
Location: Malta
The routine is fantastic and I tried to use it
as typical of me I stumbled onto a bug immidiately
I've taken the liberty to slightly modify your test example to illustrate the bug

basically once destroyed the list never works again
combogadget and and list are create on the fly using button create and destroyed with the kill button

I'm still rather green with programming with windows so I have no clue as how to fix it


; By Xombie - 12/12/2005
; Slightly modified by kinglestat 10/August/2006

Enumeration ; Window List
#WindowMain
EndEnumeration
Enumeration ; Menu List
#MenuMain
EndEnumeration
Enumeration ; Control List
#ButtonKill
#ButtonCreate
#StringTest
#StringTest2
#ComboTest
EndEnumeration
;- Global Variables
Dim Array01.s(10)
Dim array02.s(20)
;- Includes
XIncludeFile "Autocomplete.pb"
;- Main Program
DoQuit.b

Procedure FillCombo ()

ComboBoxGadget(#ComboTest, GadgetX(#ButtonCreate), GadgetY(#ButtonCreate) + GadgetHeight(#ButtonCreate) + 1, 100, 200, #PB_ComboBox_Editable )

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")

EndProcedure


;
If OpenWindow(#WindowMain, 100, 300, 300, 200, "Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
;
If CreateGadgetList(WindowID(#WindowMain))
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 ( #ButtonKill, GadgetX(#StringTest2), GadgetY(#StringTest2) + GadgetHeight(#StringTest2) + 1, 100, 20, "Kill" )
ButtonGadget ( #ButtonCreate, GadgetX(#StringTest2) + 120, GadgetY(#StringTest2) + GadgetHeight(#StringTest2) + 1, 100, 20, "Create" )

EndIf
;
;{ Create autocomplete lists
;
;/ Create items for the combobox.

;/ 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 EventGadget() = #ButtonKill

If EventType() = #PB_EventType_LeftClick

ac_RemoveAutocomplete( #ComboTest )
FreeGadget ( #ComboTest )

EndIf

ElseIf EventGadget() = #ButtonCreate

If EventType() = #PB_EventType_LeftClick

;ac_SetAutocomplete(#StringTest, @Array01())
;ac_SetAutocomplete(#StringTest2, @array02())
FillCombo ()
;ComboBoxGadget ( #ComboTest, GadgetX(#StringTest2), GadgetY(#StringTest2) + GadgetHeight(#StringTest2) + 1, 100, 200, #PB_ComboBox_Editable ) ;
ac_SetAutocomplete(#ComboTest, 0)

EndIf
;
EndIf
;
EndIf
;
Until DoQuit = #True

EndIf

ac_DestroyAutocomplete()
; Remove all autocompleting items.
End

cheers

KingLestat


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 36 posts ]  Go to page 1, 2, 3  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye