Autocomplete for Comboboxgadget - Simple version

Share your advanced PureBasic knowledge/code with the community.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Autocomplete for Comboboxgadget - Simple version

Post by mesozorn »

I have seen other autocomplete routines posted which are very good indeed. This is mine which is very short and very simple:

Code: Select all

Structure comboboxinfo
  cbSize.l
  rcItem.RECT
  rcButton.RECT
  stateButton.l
  hwndCombo.l
  hwndEdit.l
  hwndList.l
EndStructure

cbinfo.comboboxinfo


OpenWindow(0, 0, 0, 270, 140, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ComboBoxGadget(0, 10, 10, 250, 21, #PB_ComboBox_Editable)
  AddGadgetItem(0, -1, "Adams")
  AddGadgetItem(0, -1, "Franklin")
  AddGadgetItem(0, -1, "Jefferson")
  AddGadgetItem(0, -1, "Walters")
  AddGadgetItem(0, -1, "Washington")
 SetActiveGadget(0)


Repeat
  W=WaitWindowEvent()
      
      
  If W=#WM_KEYDOWN And  GetActiveGadget()=0
    acg=GetActiveGadget()
    cbinfo\cbsize=SizeOf(comboboxinfo)
    GetComboBoxInfo_(GadgetID(acg),@cbinfo)
    tb=cbinfo.comboboxinfo\hwndedit
    SendMessage_(tb, #EM_GETSEL, @spos.l, @epos.l) 
      
    matchesfound=0  
      For x=0 To CountGadgetItems(acg)-1
        If LCase(Left(GetGadgetText(acg),spos)+LCase(Chr(EventwParam())))=LCase(Left(GetGadgetItemText(acg,x),spos+1)) And epos=Len(GetGadgetText(acg))
          matchesfound+1:match=x
          If matchesfound>1:Break:EndIf
        EndIf
      Next x
      
      If matchesfound=1  
          ks=GetKeyState_(#VK_SHIFT)
          If ks<2:addchar$=LCase(Chr(EventwParam())):Else:addchar$=UCase(Chr(EventwParam())):EndIf
          SetGadgetText(acg,Left(GetGadgetText(acg),spos)+addchar$+Mid(GetGadgetItemText(acg,match),spos+2))
          SendMessage_(tb, #EM_SETSEL, spos+1, epos+999) 
          PeekMessage_(@msg.MSG, GadgetID(acg), #WM_KEYFIRST, #WM_KEYLAST, #PM_REMOVE)
      EndIf
      
  EndIf
          
    
Until W = #PB_Event_CloseWindow
It is of course very easy to enhance this so as to be able to add autocomplete functionality to any comboboxgadget created on the fly, without having to go back and edit this source to include each new gadget. I have not added these extra lines to the example above only because it is a fairly obvious enhancement.
Last edited by mesozorn on Tue Nov 10, 2009 8:52 pm, edited 1 time in total.
akj
Enthusiast
Enthusiast
Posts: 668
Joined: Mon Jun 09, 2003 10:08 pm
Location: Nottingham

Re: Autocomplete for Comboboxgadget - Simple version

Post by akj »

@mesozorn:

There is a bug in your routine in that hWnd is never assigned a value, but is used in the line

Code: Select all

PeekMessage_(@msg, hWnd, #WM_KEYFIRST, #WM_KEYLAST, #PM_REMOVE)
Anthony Jordan
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Autocomplete for Comboboxgadget - Simple version

Post by mesozorn »

akj wrote:@mesozorn:

There is a bug in your routine in that hWnd is never assigned a value, but is used in the line

Code: Select all

PeekMessage_(@msg, hWnd, #WM_KEYFIRST, #WM_KEYLAST, #PM_REMOVE)
Thanks for noticing. It still seems to work correctly even if hwnd is left as 0 regardless of anything else, but just to be safe I have now edited it to be:

Code: Select all

PeekMessage_(@msg.MSG, GadgetID(acg), #WM_KEYFIRST, #WM_KEYLAST, #PM_REMOVE)
Amnesty
User
User
Posts: 54
Joined: Wed Jul 04, 2007 4:34 pm
Location: Germany

Re: Autocomplete for Comboboxgadget - Simple version

Post by Amnesty »

Its amazing simple, thank you for this.

How can I use this with numbers, I am thinking about customer IDs. (for example 11208736)
When I am using only numbers in Comboboxgadget, autocomplete is not working.

Sorry, it works with numbers but not with NumPad.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Autocomplete for Comboboxgadget - Simple version

Post by mesozorn »

This modified version works for NumPad, but there are still a bunch of other non-standard characters which will trip it up as well. I will post a further modified version later which will handle all types of keys. The original version is mainly for regular letters and numbers.

Code: Select all

Structure comboboxinfo
  cbSize.l
  rcItem.RECT
  rcButton.RECT
  stateButton.l
  hwndCombo.l
  hwndEdit.l
  hwndList.l
EndStructure

cbinfo.comboboxinfo


OpenWindow(0, 0, 0, 270, 140, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ComboBoxGadget(0, 10, 10, 250, 21, #PB_ComboBox_Editable)
  AddGadgetItem(0, -1, "12345")
  AddGadgetItem(0, -1, "23468")
  AddGadgetItem(0, -1, "48347")
  AddGadgetItem(0, -1, "58237")
  AddGadgetItem(0, -1, "58772")
SetActiveGadget(0)


Repeat
  W=WaitWindowEvent()
     
     
  If W=#WM_KEYDOWN And  GetActiveGadget()=0
    acg=GetActiveGadget()
    cbinfo\cbsize=SizeOf(comboboxinfo)
    GetComboBoxInfo_(GadgetID(acg),@cbinfo)
    tb=cbinfo.comboboxinfo\hwndedit
    SendMessage_(tb, #EM_GETSEL, @spos.l, @epos.l)
    
        
    matchesfound=0 
      For x=0 To CountGadgetItems(acg)-1
        If (LCase(Left(GetGadgetText(acg),spos)+LCase(Chr(EventwParam())))=LCase(Left(GetGadgetItemText(acg,x),spos+1)) And epos=Len(GetGadgetText(acg))) Or (EventwParam()>=96 And EventwParam()<=105 And LCase(Left(GetGadgetText(acg),spos)+LCase(Chr(EventwParam()-48)))=LCase(Left(GetGadgetItemText(acg,x),spos+1)) And epos=Len(GetGadgetText(acg)))

          matchesfound+1:match=x
          If matchesfound>1:Break:EndIf
        EndIf
      Next x
     
      If matchesfound=1 
          If EventwParam()>=96 And EventwParam()<=105:ep=EventwParam()-48:Else:ep=EventwParam():EndIf
          ks=GetKeyState_(#VK_SHIFT)
          If ks<2:addchar$=LCase(Chr(ep)):Else:addchar$=UCase(Chr(ep)):EndIf
          SetGadgetText(acg,Left(GetGadgetText(acg),spos)+addchar$+Mid(GetGadgetItemText(acg,match),spos+2))
          SendMessage_(tb, #EM_SETSEL, spos+1, epos+999)
          PeekMessage_(@msg.MSG, GadgetID(acg), #WM_KEYFIRST, #WM_KEYLAST, #PM_REMOVE)
      EndIf
     
  EndIf
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Autocomplete for Comboboxgadget - Simple version

Post by mesozorn »

Okay, the new version below works for all keys and characters I believe. I switched to using the subclassing approach which makes it even more APISH than before, but I was already using plenty of API calls in the original version, so not that much difference I suppose. Just note two things:

1) It is the textbox portion of the ComboBoxGadget which gets subclassed, and not the entire ComboBoxGadget itself. You can use a simple procedure or macro to do this in one clean step, but I've left the long version in for illustration.

2) This is my first attempt ever at subclassing, so it may be buggy.

Code: Select all

Structure comboboxinfo  
  cbSize.l
  rcItem.RECT
  rcButton.RECT
  stateButton.l
  hwndCombo.l
  hwndEdit.l
  hwndList.l
EndStructure
Global cbinfo.comboboxinfo
Global oldcomboProc


Procedure.i ComboAutoComplete(hWnd, uMsg, wParam, lParam)
  Protected result
  Select uMsg
    Case #WM_CHAR
    acg=GetActiveGadget() ;GetDlgCtrlID_(hwnd)
    SendMessage_(hwnd, #EM_GETSEL, @spos.l, @epos.l)

    matchesfound=0 
      For x=0 To CountGadgetItems(acg)-1
        If LCase(Left(GetGadgetText(acg),spos)+LCase(Chr(wParam)))=LCase(Left(GetGadgetItemText(acg,x),spos+1)) And epos=Len(GetGadgetText(acg))
          matchesfound+1:match=x
          If matchesfound>1:Break:EndIf
        EndIf
      Next x
     
      If matchesfound=1 
          ks=GetKeyState_(#VK_SHIFT)
          If ks<2:addchar$=LCase(Chr(wparam)):Else:addchar$=UCase(Chr(wparam)):EndIf
          SetGadgetText(acg,Left(GetGadgetText(acg),spos)+addchar$+Mid(GetGadgetItemText(acg,match),spos+2))
          SendMessage_(hwnd, #EM_SETSEL, spos+1, epos+999)
          result=0
      Else
        result = CallWindowProc_(oldcomboproc, hWnd, uMsg, wParam, lParam)
      EndIf
      
    Default
      result = CallWindowProc_(oldcomboproc, hWnd, uMsg, wParam, lParam)
  EndSelect
  ProcedureReturn result
EndProcedure


OpenWindow(0, 0, 0, 270, 140, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ComboBoxGadget(0, 10, 10, 250, 21, #PB_ComboBox_Editable)
  cbinfo\cbsize=SizeOf(comboboxinfo)
  GetComboBoxInfo_(GadgetID(0),@cbinfo)
  tb=cbinfo.comboboxinfo\hwndedit
  oldcomboproc = SetWindowLong_(tb, #GWL_WNDPROC, @ComboAutoComplete())
  AddGadgetItem(0, -1, "12345")
  AddGadgetItem(0, -1, "23/468")
  AddGadgetItem(0, -1, "483~47")
  AddGadgetItem(0, -1, "58237")
  AddGadgetItem(0, -1, "58.772")
SetActiveGadget(0)


Repeat
  W=WaitWindowEvent()
Until W = #PB_Event_CloseWindow
ThoPie
User
User
Posts: 47
Joined: Sat Aug 22, 2009 6:49 pm

Re: Autocomplete for Comboboxgadget - Simple version

Post by ThoPie »

Unfortunately, this great ComboBox-Autocompletion works not with PureBasic 4.5x. Please can anybody adapt this for the usage with version 4.5.
Thanks a lot.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Autocomplete for Comboboxgadget - Simple version

Post by srod »

GetComboBoxInfo_() is not reliable for the new combos. I have replaced this and the example seems to work ok.

Code: Select all

Global oldcomboProc


Procedure.i ComboAutoComplete(hWnd, uMsg, wParam, lParam)
  Protected result
  Select uMsg
    Case #WM_CHAR
    acg=GetActiveGadget() ;GetDlgCtrlID_(hwnd)
    SendMessage_(hwnd, #EM_GETSEL, @spos.l, @epos.l)

    matchesfound=0 
      For x=0 To CountGadgetItems(acg)-1
        If LCase(Left(GetGadgetText(acg),spos)+LCase(Chr(wParam)))=LCase(Left(GetGadgetItemText(acg,x),spos+1)) And epos=Len(GetGadgetText(acg))
          matchesfound+1:match=x
          If matchesfound>1:Break:EndIf
        EndIf
      Next x
     
      If matchesfound=1 
          ks=GetKeyState_(#VK_SHIFT)
          If ks<2:addchar$=LCase(Chr(wparam)):Else:addchar$=UCase(Chr(wparam)):EndIf
          SetGadgetText(acg,Left(GetGadgetText(acg),spos)+addchar$+Mid(GetGadgetItemText(acg,match),spos+2))
          SendMessage_(hwnd, #EM_SETSEL, spos+1, epos+999)
          result=0
      Else
        result = CallWindowProc_(oldcomboproc, hWnd, uMsg, wParam, lParam)
      EndIf
      
    Default
      result = CallWindowProc_(oldcomboproc, hWnd, uMsg, wParam, lParam)
  EndSelect
  ProcedureReturn result
EndProcedure


OpenWindow(0, 0, 0, 270, 140, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  hWnd = ComboBoxGadget(0, 10, 10, 250, 21, #PB_ComboBox_Editable)
  hWnd = GetWindow_(hWnd, #GW_CHILD)
  hWndEdit = FindWindowEx_(hWnd, 0, "EDIT", 0)
  oldcomboproc = SetWindowLong_(hWndEdit, #GWL_WNDPROC, @ComboAutoComplete())
  AddGadgetItem(0, -1, "12345")
  AddGadgetItem(0, -1, "23/468")
  AddGadgetItem(0, -1, "483~47")
  AddGadgetItem(0, -1, "58237")
  AddGadgetItem(0, -1, "58.772")
SetActiveGadget(0)


Repeat
  W=WaitWindowEvent()
Until W = #PB_Event_CloseWindow
Last edited by srod on Mon Apr 12, 2010 5:47 pm, edited 1 time in total.
I may look like a mule, but I'm not a complete ass.
ThoPie
User
User
Posts: 47
Joined: Sat Aug 22, 2009 6:49 pm

Re: Autocomplete for Comboboxgadget - Simple version

Post by ThoPie »

Wow! Thanks. :lol:
USCode
Addict
Addict
Posts: 924
Joined: Wed Mar 24, 2004 11:04 pm
Location: Seattle

Re: Autocomplete for Comboboxgadget - Simple version

Post by USCode »

How about a cross-platform version??? Possible?
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Autocomplete for Comboboxgadget - Simple version

Post by mesozorn »

srod wrote:GetComboBoxInfo_() is not reliable for the new combos. I have replaced this and the example seems to work ok.
Out of curiosity, why is it that this API command will no longer work on a ComboBoxGadget under PB 4.5? GetComboBoxInfo_() is a standard Windows command, so shouldn't it always work on a standard Windows combo control?
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: Autocomplete for Comboboxgadget - Simple version

Post by ts-soft »

ComboBox is changed to ComboBoxEx to support images!
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: Autocomplete for Comboboxgadget - Simple version

Post by mesozorn »

ts-soft wrote:ComboBox is changed to ComboBoxEx to support images!
Ah I see, in which case is there going to be any support for either of:

SendMessage_(hWnd,#CBEM_GETCOMBOCONTROL,0,0)
SendMessage_(hWnd,#CBEM_GETEDITCONTROL,0,0)

? Right now neither of those API constants appear to be recognized..? They would be helpful in either extracting the standard ComboBox portion of the ComboBoxEx, or getting the edit control handle directly. Is there any way of finding out what those numerical constants would be and declaring them oneself, even if they are not yet included in PB by default? Thanks...
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4790
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Autocomplete for Comboboxgadget - Simple version

Post by Fangbeast »

Code: Select all

Global oldcomboProc

Procedure.i ComboAutoComplete(hWnd, uMsg, wParam, lParam)
  Protected result
  Select uMsg
    Case #WM_CHAR
    acg=GetActiveGadget() ;GetDlgCtrlID_(hwnd)
    SendMessage_(hwnd, #EM_GETSEL, @spos.l, @epos.l)
    matchesfound=0 
      For x=0 To CountGadgetItems(acg)-1
        If LCase(Left(GetGadgetText(acg),spos)+LCase(Chr(wParam)))=LCase(Left(GetGadgetItemText(acg,x),spos+1)) And epos=Len(GetGadgetText(acg))
          matchesfound+1:match=x
          If matchesfound>1:Break:EndIf
        EndIf
      Next x   
      If matchesfound=1 
          ks=GetKeyState_(#VK_SHIFT)
          If ks<2:addchar$=LCase(Chr(wparam)):Else:addchar$=UCase(Chr(wparam)):EndIf     SetGadgetText(acg,Left(GetGadgetText(acg),spos)+addchar$+Mid(GetGadgetItemText(acg,match),spos+2))
          SendMessage_(hwnd, #EM_SETSEL, spos+1, epos+999)
          result=0
      Else
        result = CallWindowProc_(oldcomboproc, hWnd, uMsg, wParam, lParam)
      EndIf 
    Default
      result = CallWindowProc_(oldcomboproc, hWnd, uMsg, wParam, lParam)
  EndSelect
  ProcedureReturn result
EndProcedure

OpenWindow(0, 0, 0, 270, 140, "ComboBoxGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  hWnd = ComboBoxGadget(0, 10, 10, 250, 21, #PB_ComboBox_Editable)
  hWnd = GetWindow_(hWnd, #GW_CHILD)
  hWndEdit = FindWindowEx_(hWnd, 0, "EDIT", 0)
  oldcomboproc = SetWindowLong_(hWndEdit, #GWL_WNDPROC, @ComboAutoComplete())
  AddGadgetItem(0, -1, "12345")
  AddGadgetItem(0, -1, "23/468")
  AddGadgetItem(0, -1, "483~47")
  AddGadgetItem(0, -1, "58237")
  AddGadgetItem(0, -1, "58.772")
SetActiveGadget(0)
Repeat
  W=WaitWindowEvent()
Until W = #PB_Event_CloseWindow
I'm afraid to touch working code that I don't understand so I am wondering how this can be extended to cover multiple comboboxes as oldcomboproc is only for a single combo box as I understand it??
Amateur Radio/VK3HAF, (D-STAR/DMR and more), Arduino, ESP32, Coding, Crochet
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Autocomplete for Comboboxgadget - Simple version

Post by netmaestro »

oldcomboproc is only for a single combo box as I understand it??
'oldcomboproc' is going to be the same for all comboboxes you subclass as it's the default combobox procedure. You can safely use this with multiple combo boxes.
BERESHEIT
Post Reply