[Windows] AutoComplete for StringGadgets

Share your advanced PureBasic knowledge/code with the community.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8425
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: [Windows] AutoComplete for StringGadgets

Post by netmaestro »

Might have happened onto something...
MSDN wrote: You can use CLSID_ACLMulti if you want AutoComplete to use multiple lists

Code: Select all

DataSection
  CLSID_ACLMulti:
  Data.l $00BB2765
  Data.w $6A77, $11D0
  Data.b $A5, $35, $00, $C0, $4F, $D7, $D0, $62
EndDataSection
Use in place of CLSID_AutoComplete. Since you're still up for working with this, you might give this CLSID a try.
BERESHEIT
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: [Windows] AutoComplete for StringGadgets

Post by rsts »

Not sure that solves the updating problem. I haven't been able to get it to work yet, but then, what did you expect :)

The release/rebuild the list approach seems to be unreliable, at least in my testing.

According to this source code, it appears there are ways to add and remove items as part of iautocomplete. Although how to access them remains a mystery to me.

http://www.codeproject.com/kb/wtl/Custo ... e_wtl.aspx

If you decide to cook one of your own, and can understand c++, this might provide a start. Once again, way beyond me. I'll soon be looking for some other alternative. Maybe I can get xombie to update his for lists again :)

http://www.codeproject.com/KB/combobox/ ... plete.aspx

cheers
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: [Windows] AutoComplete for StringGadgets

Post by rsts »

Mr maestro

Any chance this could be contributing to my unexplained crashes? (which often occur at the procedureReturn)

http://www.purebasic.fr/english/viewtop ... +procedure

cheers
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8425
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: [Windows] AutoComplete for StringGadgets

Post by netmaestro »

I doubt it as that fails every time, and that's not what's happening to you. Imho, the IAutoComplete2 object isn't releasing all its resources when the \release() method is called and that's causing a crash on the next run.
BERESHEIT
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: [Windows] AutoComplete for StringGadgets

Post by rsts »

Thanks again.

Between perusing codeproject and msdn, I realize I'm in way over my head. I'm moving back to xombies routine, which appears it may satisfy my basic requirements, at least for now. I've spent way too much time on this and it seems I'm no closer to a real solution than I was at the start.

If you decide to roll your own, I'll be happy to lend whatever assistance I can. If you make any progress on this one, please let me know that as well.

Thanks once again for all your assistance.

cheers
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8425
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: [Windows] AutoComplete for StringGadgets

Post by netmaestro »

Here is a solution half-done. That is, the object is working and fully functional, it's just the display presentation isn't done. Results just go to the debug window. Size your debug window narrow and long and pretend it's a menu, then have a play with this and see if you can get it to fail:

Code: Select all

;==================================================================
;
; Library:           AutoComplete v. 0.3 alpha
; Author:            Lloyd Gallant (netmaestro)
; Date:              July 21, 2010
; Target OS:         Microsoft Windows All
; Target Compiler:   PureBasic 4.50 and later
; license:           Free, unrestricted, no warranty whatsoever
;                    credit appreciated but not required
;
;==================================================================

UseSQLiteDatabase()

#DATABASE_OK = ""

Structure AutoCompleteObject
  *vTable
  database.i
  subclass_oldproc.i
  gadget.i
  dynamic.b
EndStructure

Interface iAutoCompleteObject
  Attach    ( gadget, *strings, size )
  AddString ( gadget, *string )
  Release   ()
EndInterface

Procedure AddString(*this.AutoCompleteObject, gadget, *string )
  text$ = PeekS(*string)
  sql$ =  "SELECT promptstring FROM Autocomplete where (promptstring = '"+text$+"' and gadget = '" + Str(gadget) + "')"
  DatabaseQuery(*this\database, sql$)
  recordcount = 0
  While NextDatabaseRow(*this\database)
    recordcount+1
  Wend
  FinishDatabaseQuery(*this\database)
  If recordcount = 0
    sql$ = "insert into Autocomplete (gadget, promptstring) values(" + Str(gadget) + "," + "'"+ PeekS(*string) + "')"
    DatabaseUpdate(*this\database, sql$)
  EndIf
  ProcedureReturn 1
EndProcedure

Procedure _nmAC_StringProc(hwnd, msg, wparam, lparam)
  *this.AutoCompleteObject = GetProp_(hwnd, "acdata")
  oldproc = *this\subclass_oldproc
  gadget = *this\gadget
  
  Select msg
    Case #WM_NCDESTROY
      RemoveProp_(hwnd, "oldproc")
      
    Case #WM_KEYUP
      If GetFocus_() = hwnd
        ClearDebugOutput()
        text$ = GetGadgetText(*this\gadget)
        If text$ <> ""
          sql$ =  "SELECT promptstring FROM Autocomplete where (promptstring like '"+text$+"%' and gadget = '" + Str(gadget) + "')"
          DatabaseQuery(*this\database, sql$)
          While NextDatabaseRow(*this\database)
            Debug GetDatabaseString(*this\database,0)
          Wend
          FinishDatabaseQuery(*this\database)
        EndIf
      EndIf
      
    Case #WM_KILLFOCUS
      If *this\dynamic
        text$ = GetGadgetText(gadget)
        Addstring(*this, gadget, @text$)
      EndIf
      
    Case #WM_SETFOCUS
      SendMessage_(hwnd, #EM_SETSEL, 0, -1)
    
  EndSelect   
  ProcedureReturn CallWindowProc_(oldproc, hwnd, msg, wparam, lparam)
EndProcedure

Procedure Attach(*this.AutoCompleteObject, gadget, *strings, size )
  Protected fail_status = #False
  *ptr = *strings
  For i=1 To size
    sql$ = "insert into Autocomplete (gadget, promptstring) values(" + Str(gadget) + "," + "'"+ PeekS(PeekL(*ptr)) + "')"
    If DatabaseUpdate(*this\database, sql$)
      *ptr+SizeOf(integer)
    Else
      fail_status = #True
      Break
    EndIf
  Next
  If IsGadget(gadget) And GadgetType(gadget) = #PB_GadgetType_String And fail_status = #False
    *this\gadget = gadget
    *this\subclass_oldproc = SetWindowLongPtr_(GadgetID(gadget), #GWL_WNDPROC, @_nmAC_StringProc())
    SetProp_(GadgetID(gadget), "acdata", *this )
  Else
    fail_status = #True
  EndIf
  
  If fail_status = #False 
    ProcedureReturn #True
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure NewObject_Autocomplete(dynamic=0)
  *newobject.AutoCompleteObject = AllocateMemory(SizeOf(AutoCompleteObject))
  With *newobject
    \vTable = ?AutoComplete_Methods
    \dynamic = dynamic
  EndWith
  dbOpenResult = OpenDatabase(#PB_Any, ":memory:", "", "")
  If dbOpenResult
    DatabaseUpdate(dbOpenResult, "CREATE TABLE AutoComplete (gadget VARCHAR, promptstring VARCHAR(255));")       
    If DatabaseError() = #DATABASE_OK
      *newobject\database = dbOpenResult
      ProcedureReturn *newobject
    Else
      ProcedureReturn 0
    EndIf
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure Release(*this.AutoCompleteObject)
  If *this\subclass_oldproc
    SetWindowLongPtr_(GadgetID(*this\gadget),#GWL_WNDPROC, *this\subclass_oldproc)
  EndIf
  If IsDatabase(*this\database)
    CloseDatabase(*this\database)
  EndIf
  FreeMemory(*this)
EndProcedure

DataSection
  AutoComplete_Methods:
  Data.l @Attach(), @AddString(), @Release()
EndDataSection

;=================================================================
;                    END OF INCLUDE CODESECTION
;=================================================================

; Test prog

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"


OpenWindow(0,0,0,320,240,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
StringGadget(6, 10,10, 300,20,"")
StringGadget(7, 10,50, 300,20,"")
SetActiveGadget(6)


prompts1.iAutoCompleteObject = NewObject_Autocomplete(1) ; Dynamic additions on
prompts1\Attach(6, @strings(), 17 )

prompts2.iAutoCompleteObject = NewObject_Autocomplete() ; Dynamic additions off - list doesn't change
prompts2\Attach(7, @strings(), 17 )

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

prompts1\Release()
prompts2\Release()

BERESHEIT
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: [Windows] AutoComplete for StringGadgets

Post by rsts »

Wow, what a novel approach. Brilliant!

At least some of us don't get lost in the forest looking for trees.

This looks very promising. Plus, I know little bit about SQLite, so can understand bits of what's going on.

My hat's of to you, Mr maestro.
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Re: [Windows] AutoComplete for StringGadgets

Post by Xombie »

Huh. I haven't messed with my autocomplete stuff for a long time. If I need to change anything, let me know.
Marco2007
Enthusiast
Enthusiast
Posts: 638
Joined: Tue Jun 12, 2007 10:30 am
Location: not there...

Re: [Windows] AutoComplete for StringGadgets

Post by Marco2007 »

Hi,

I got a problem and I hope someone could help me, please.

Freak`s code runs fine on almost every WinOS, but on some Win7-64bit machines it chrashes when I use ExplorerGadget or Webgadget?!
This is really freaking me out :-)

If I just put one line to the code (ExplorerGadget or WebGadget), the app is freezing, when trying to type something into the StringGadget :-(
No error or something :-(

Has anyone an idea, what`s the reason for this?

** Edit: WebGadget runs fine, when not using it like an Explorer (dir instead or url)...

Many, many Thanks!
PureBasic for Windows
Peyman
Enthusiast
Enthusiast
Posts: 203
Joined: Mon Dec 24, 2007 4:15 pm
Location: Iran

Fixed AutoComplete for StringGadgets in X64 Compiler

Post by Peyman »

i changed some lines so now its Compatible with x64 PureBasic compiler.

AutoComplete.pbi

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.)
;
;
;
EnableExplicit

; 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.Integer)
  If *Object = 0
    ProcedureReturn #E_INVALIDARG
  ElseIf CompareMemory(*IID, ?IID_IUnknown, SizeOf(IID)) Or  CompareMemory(*IID, ?IID_IEnumString, SizeOf(IID))
    *Object\i = *THIS
    *THIS\RefCount + 1
    ProcedureReturn #S_OK
  Else
    *Object\i = 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.Integer, *pceltFetched.Integer)
  Protected count.i, i.i
  
  If *THIS\Enumerator + celt <= *THIS\StringCount
    count = celt
  Else
    count = *THIS\StringCount - *THIS\Enumerator
  EndIf
 
  For i = 0 To count-1
    *rgelt\i = *THIS\Buffer\Strings[*THIS\Enumerator + i]
    *rgelt + 4
  Next i
 
  *THIS\Enumerator + count
 
  If *pceltFetched
    *pceltFetched\i = 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.Integer)
  Protected *Clone.EnumString
  
  If *ppenum = 0
    ProcedureReturn #E_INVALIDARG
  Else   
    *Clone = AllocateMemory(SizeOf(EnumString))
    If *Clone
      CopyMemory(*THIS, *Clone, SizeOf(EnumString))
      *Clone\RefCount = 1
      *Clone\Buffer\RefCount + 1
      *ppenum\i = *Clone
      ProcedureReturn #S_OK
    Else
      *ppenum\i = 0
      ProcedureReturn #E_OUTOFMEMORY
    EndIf 
  EndIf
EndProcedure


Procedure New_EnumString(Array StringArray.s(1))
  Protected *THIS.EnumString = 0, *StringBuffer.EnumStringBuffer
  Protected StringCount.i, Size.i, i.i
  Protected *Pointer
  
  StringCount = ArraySize(StringArray())

  Size = 4 + StringCount * 4
  For i = 0 To StringCount - 1
    Size + Len(StringArray(i)) * 2 + 2
  Next i

  *StringBuffer = 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.i @IEnumString_QueryInterface()
    Data.i @IEnumString_AddRef()
    Data.i @IEnumString_Release()
    Data.i @IEnumString_Next()
    Data.i @IEnumString_Skip()
    Data.i @IEnumString_Reset()
    Data.i @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
    
  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


#CLSCTX_INPROC_SERVER     = $1
#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.

main.pb

Code: Select all

IncludeFile "AutoComplete.pbi"

; -----------------------------------------------

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

Define *AutoComplete.IAutoComplete2, *Enum.IEnumString

If OpenWindow(#Window_0, 323, 219, 600, 300, "AutoComplete",  #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_ScreenCentered )
    StringGadget(#String_0, 40, 40, 320, 20, "")
    SetActiveGadget(#String_0)
   
    ; This creates the autocomplete object:
    ;
    If CoCreateInstance_(?CLSID_AutoComplete, #Null, 1, ?IID_IAutoComplete2, @*AutoComplete) = #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 = New_EnumString(Strings())
     
      ; 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
Sorry for my bad english.
LiK137
Enthusiast
Enthusiast
Posts: 279
Joined: Wed Jun 23, 2010 5:13 pm

Re: [Windows] AutoComplete for StringGadgets

Post by LiK137 »

Autocomplete with using sql select is clear. But how to attach structured or multidimentional array like
1: MyArray(id, string)
2: MyArray\id and MyArray\string
to editable comboboxgadget to autofilter gadget contents.
Post Reply