Page 1 of 1

Simplified ComboBox Autocomplete

Posted: Mon Aug 18, 2003 8:49 pm
by ebs
Code in this post updated to 5.20+
I needed an autocompleting ComboBox and found a tip posted by Saboteur. I simplified his code by using "GetGadgetItemText()". I also made the routine accept the gadget number of the ComboBox to be completed, so it can be used for any number of ComboBoxes.

Code: Select all

;- autocomplete specified combobox
Procedure AutocompleteComboBox(ComboBox.l)
	; get text entered by user
	TextTyped.s = UCase(GetGadgetText(ComboBox))
	LenTextTyped.l = Len(TextTyped)
	; done if no text entered
	If LenTextTyped
		; search combo contents for item that starts with text entered
		MaxItem.l = CountGadgetItems(ComboBox) - 1
		For Item.l = 0 To MaxItem
			If TextTyped = UCase(Left(GetGadgetItemText(ComboBox, Item, 0), LenTextTyped))
				; found matching item
				; set combo state
				SetGadgetState(ComboBox, Item)
				; select added text only
				
				MyPoint.POINT
				hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), MyPoint);5, 5)
				SendMessage_(hComboEdit, #EM_SETSEL, LenTextTyped, -1)
				; exit For loop
				Item = MaxItem
			EndIf
		Next
	EndIf
EndProcedure

If OpenWindow(0, 0, 0, 300, 300, "Autocompletion", #PB_Window_SystemMenu|#PB_Window_TitleBar)
	;       If CreateGadgetList(WindowID(0))
	ComboBoxGadget(0, 10, 10, 200, 100, #PB_ComboBox_Editable)
	AddGadgetItem(0, -1, "Autocomplete")
	AddGadgetItem(0, -1, "Bernard")
	AddGadgetItem(0, -1, "Car")
	AddGadgetItem(0, -1, "Explorer")
	AddGadgetItem(0, -1, "Fantastic")
	AddGadgetItem(0, -1, "General protection error")
	AddGadgetItem(0, -1, "Purebasic")
	AddGadgetItem(0, -1, "Purepower")
	AddGadgetItem(0, -1, "Purevisual")
	AddGadgetItem(0, -1, "Question")
	AddGadgetItem(0, -1, "Zas")
	;       EndIf
EndIf

Repeat
	Event = WaitWindowEvent()
	Select Event
		Case #PB_Event_Gadget
			If EventGadget() = 0
				AutocompleteComboBox(0)
			EndIf
	EndSelect
Until Event = #PB_Event_CloseWindow
End
If you need this capability, please try it and let me know how it works for you.

Eric

Posted: Mon Aug 18, 2003 10:20 pm
by Saboteur
Good update, ebs ;)

Posted: Wed Aug 20, 2003 2:29 am
by aszid
i tested this out and found a little problem, once it has "autocompleted" the line, i can no longer use backspace... that was actually almost the first thing i did when i tried the code.

anyhow, running win2k here, 128mb ram.

Posted: Wed Aug 20, 2003 3:13 pm
by ebs
aszid,

I'm aware of that, and I'm not sure how to handle it the best way.

After autocompletion, backspace does actually work, but the text is autocompleted again immediately, so you don't see it. I'm trying to figure out a straightforward way to delay autocompleting until another character is typed after a backspace, but so far, it's kind of messy. If I come up with something good, I'll post it here.

On another note, sometimes you may not want to autocomplete until a certain number of characters have been entered - I often use 3 characters. You can get this behavior easily by changing the line

Code: Select all

If LenTextTyped
to

Code: Select all

If LenTextTyped >= 3

Autocomplete Works With Backspace

Posted: Sat Aug 23, 2003 10:05 pm
by ebs
Here's a new version of the autocomplete code which works properly when Backspace is pressed:

Code: Select all

#NONE = -1

;- autocomplete specified combobox
Procedure AutocompleteComboBox(ComboBox.l)
  Shared LenTextSave.l
  Shared GadgetSave.l

  ; get text entered by user
  TextTyped.s = UCase(GetGadgetText(ComboBox))
  LenTextTyped.l = Len(TextTyped)
  
  ; skip if same gadget and same length or shorter text (backspace?)
  If ComboBox = GadgetSave And LenTextTyped <= LenTextSave
    LenTextSave = LenTextTyped
  ElseIf LenTextTyped
    GadgetSave = #NONE
    ; search combo contents for item that starts with text entered
    MaxItem.l = CountGadgetItems(ComboBox) - 1
    For Item.l = 0 To MaxItem
      If TextTyped = UCase(Left(GetGadgetItemText(ComboBox, Item, 0), LenTextTyped))
        ; found matching item
        ; set combo state
        SetGadgetState(ComboBox, Item)
        ; select added text only
        hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), 5, 5)
        SendMessage_(hComboEdit, #EM_SETSEL, LenTextTyped, -1)
        ; save gadget number and text length for next pass
        LenTextSave = LenTextTyped
        GadgetSave = ComboBox
        ; exit For loop
        Item = MaxItem
      EndIf
    Next
  EndIf
EndProcedure

If OpenWindow(0, 0, 0, 300, 300, #PB_Window_SystemMenu|#PB_Window_TitleBar, "Autocompletion")
  If CreateGadgetList(WindowID())
    ComboBoxGadget(0, 10, 10, 200, 100, #PB_ComboBox_Editable)
    AddGadgetItem(0, -1, "Autocomplete")
    AddGadgetItem(0, -1, "Bernard")
    AddGadgetItem(0, -1, "Car")
    AddGadgetItem(0, -1, "Explorer")
    AddGadgetItem(0, -1, "Fantastic")
    AddGadgetItem(0, -1, "General protection error")
    AddGadgetItem(0, -1, "Purebasic")
    AddGadgetItem(0, -1, "Purepower")
    AddGadgetItem(0, -1, "Purevisual")
    AddGadgetItem(0, -1, "Question")
    AddGadgetItem(0, -1, "Zas")
  EndIf
EndIf

Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_Gadget
      If EventGadgetID() = 0
        AutocompleteComboBox(0)
      EndIf
  EndSelect
Until Event = #PB_Event_CloseWindow

End
Let me know if it works for you!

Posted: Wed Dec 03, 2003 3:00 am
by Karbon
It works for me when I run it in your example but I'm getting some pretty strange stuff when I included it in my own project.

I copied the function exactly, then changed the

Code: Select all

hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), 5, 5)
to

Code: Select all

hComboEdit.l = ChildWindowFromPoint_(GadgetID(ComboBox), 117, 100)
Which should compensate for my gadget being in a different position.. Now, what I see happening in my project is very much what I expected to happen because you're calling the function every time any event occurs on the gadget, which means it gets called every time a key is pressed.. So if that happens then the TextTyped variable is set to what ever the current text in the gadget is. Now that's all fine and dandy until you get a match on the first character of what you type, then that item is selected which writes the full text of what ever the first match was into the TextTyped variable (because you set the state to that match every time), causing the entire thing to be selected and thus making my life hell!

What I can't figure out for the life of me (and this might be because this is hour 16 today) is - how the hell the example works like it does! I've taken my exact combo box that's dysfunctional in my project, put those items in the one in the example and it works like a charm! I'm using the *exact* function as in the example, less the change I outlined above, and it just doesn't work in my project!

Help!? :-)

Posted: Wed Dec 03, 2003 4:01 pm
by Karbon
All fixed up - just me being a cluebie..

Posted: Wed Dec 03, 2003 4:21 pm
by ebs
Mitch,

I didn't even get a chance to reply before you figured it out! :D

You went back to using

Code: Select all

...ChildWindowFromPoint_(GadgetID(ComboBox), 5, 5)
Right?

Eric

Posted: Wed Dec 03, 2003 4:33 pm
by Karbon
Actually, no, if I do that then none of the text at all gets selected.. Aren't those the x/y coors of the combo?

Code: Select all

ComboBoxGadget(#Gadget_invoice_cmb_customer,65,75,234,100,#PB_ComboBox_Editable)
... that's my combo box..

All the text is being selected on the first match.. I'm scratching my head a bit on this one :-)

Posted: Wed Dec 03, 2003 4:49 pm
by Karbon
Grrrrrrrrrr.. I changed it back to 5,5 (after reading the docs!).. Still getting nothing at all selected in the combo though.. Still bug hunting!

Posted: Wed Dec 03, 2003 5:11 pm
by ebs
Mitch,

If you like, post your code and I'll see if I can figure it out.

Eric

Posted: Wed Dec 03, 2003 6:11 pm
by Karbon
The thing is - I'm using the exact code from the example and callling the procedure in the exact way the example does. My only guess is that something in the callback is messing with that combobox as the only place that gadget is mentioned is in the event handler (when the gadget id gets passed to the autocomplete procedure) and when the gadget is created and populated.

Crazy!

Posted: Wed Dec 03, 2003 7:35 pm
by Karbon
So what was happening was that SetGadgetState() was actually triggering the event on the combo. I'm still totally confused as to why it happens in my project and not in the example but it happens just the same.. I commented out the callback to make sure it wasn't something in there fussing with the combobox and got the same broken result..

If I do this in my event handling loop it works like a charm.. :

Code: Select all

      Case #PB_Event_Gadget
        
        Select EventGadgetID()
          
          Case #Gadget_invoice_cmb_customer
            
            If EventType() = 5
              
             AutocompleteComboBox(#Gadget_invoice_cmb_customer)
          
            EndIf
The EventType() that appears to come from SetGadgetState() is type 6, but I'm unable to find anything that might tell me what that means :-)