September 21, 2023, 07:05:02 PM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Controlling AutoComplete

Started by sapero, May 29, 2006, 03:34:36 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sapero

May 29, 2006, 03:34:36 AM Last Edit: May 29, 2006, 04:24:42 AM by sapero
Autocompletion expands strings that have been partially entered in an edit control into complete strings. For example, when a user starts to enter a URL in the Address edit control that is embedded in the Internet Explorer toolbar, autocompletion expands the string into one or more complete URLs that are consistent with the existing partial string.

Typically you call the SHAutoComplete api, passing to it handle of the edit control, and some flags.
But you do not have full control over this what is suggested.

To have full control we create a instance of system IAutoComplete interface, and custom implementation of IEnumString interface:
#define IDC_COMBO 1000

#include "shlwapi.inc"
//#include "shldisp.inc" // IAutoComplete; incomplete file. Declarations bellow
#use "sdk\\uuid.lib"


// extracted from shldisp.h
interface IAutoComplete : IUnknown
{
declare virtual Init(
            HWND hwndEdit,
            IUnknown *punkACL,
            LPCOLESTR pwszRegKeyPath,
            LPCOLESTR pwszQuickComplete),HRESULT;

declare virtual Enable(BOOL fEnable),HRESULT;
}

interface IAutoComplete2 : IAutoComplete
{
declare virtual SetOptions(DWORD dwFlag),HRESULT;
declare virtual GetOptions(DWORD *pdwFlag),HRESULT;
}

extern _CLSID_AutoComplete as GUID;
extern _IID_IAutoComplete  as GUID;
extern _IID_IAutoComplete2 as GUID;


#typedef AUTOCOMPLETEOPTIONS int
#define ACO_NONE 0
#define ACO_AUTOSUGGEST 0x1
#define ACO_AUTOAPPEND 0x2
#define ACO_SEARCH 0x4
#define ACO_FILTERPREFIXES 0x8
#define ACO_USETAB 0x10
#define ACO_UPDOWNKEYDROPSLIST 0x20
#define ACO_RTLREADING 0x40


// custom implementation of IEnumString interface

class CEnumString
{
declare  CEnumString();// creates string list
declare _CEnumString();// detroys string list
declare virtual QueryInterface(REFIID *riid, void *ppvObject),HRESULT;
declare virtual AddRef(),ULONG;
declare virtual Release(),ULONG;
declare virtual Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched),HRESULT;
declare virtual Skip(ULONG celt),HRESULT;
declare virtual Reset(),HRESULT;
declare virtual Clone(IEnumString *ppenum),HRESULT;

CPointerList list;
pointer      pos;
ULONG        m_refs;
}



// a ComboBox wrapper with autocomplete feature

class CAutoCompleteComboBox : CComboBox
{
declare  CAutoCompleteComboBox();
declare _CAutoCompleteComboBox();
declare virtual Create(int l,int t,int w,int h,int style,int nID,string title,CWindow *parent),int;
IAutoComplete *pac;
CEnumString es;
}



class CMainWin : CWindow
{
declare virtual OnCreate(),int;
declare virtual OnClose(),int;
CAutoCompleteComboBox combo;
}


CMainWin::OnCreate(),int
{
SetWindowColor(0x88A4B8);
combo.Create(23,33,500,80,AWS_CHILD|AWS_VISIBLE|ACBS_DROPDOWN,IDC_COMBO,"",this);
}


CMainWin::OnClose(),int
{
Destroy();
return 0;
}

global sub main()
{
CMainWin w;

w.Create(0, 0, 600, 400, WS_VISIBLE|AWS_OVERLAPPEDWINDOW, 0, "autocomplete demo", null);

while (w.IsValid()) {wait();}
return;
}




// autocomplete ComboBox methods

CAutoCompleteComboBox::CAutoCompleteComboBox()
{
CoInitialize(null);
pac = null;
CoCreateInstance(_CLSID_AutoComplete, null, CLSCTX_INPROC_SERVER, _IID_IAutoComplete, &pac);
}


CAutoCompleteComboBox::_CAutoCompleteComboBox()
{
if (pac) {
pac->Release();
}
CoUninitialize();
}


CAutoCompleteComboBox::Create(int l,int t,int w,int h,int style,int nID,string title,CWindow *parent),int
{
BOOL ok = CComboBox!!Create(l,t,w,h,style,nID,title,parent);
if ok
{
// fill the autocomplete list from ini file, registry or static strings
es.list.Add( L"Array of size celt (or larger) of the string values");
es.list.Add( L"Creates another enumerator that contains the same enumeration state");
es.list.Add( L"If you set the ACO_SEARCH flag in the dwFlag parameter");
es.list.Add( L"Number of string values being requested");
es.list.Add( L"Pointer to the number of elements actually supplied in rgelt");
es.list.Add( L"Retrieves the next celt items in the enumeration sequence");
es.list.Add( L"Skips over the next specified number of elements in the enumeration sequence");
es.list.Add( L"This method supports the standard return values E_INVALIDARG");
es.list.Add( L"You might not want to release these interfaces if you need to access them later");

if (pac)
{
// get handle to combo's edit control (cbi.hwndItem)
COMBOBOXINFO cbi;
cbi.cbSize = sizeof(COMBOBOXINFO);
GetComboBoxInfo(m_hwnd, cbi);

// initilize the IAutoComplete interface by passing
// edit handle and IEnumString interface (CEnumString es)
pac->Init(cbi.hwndItem, es, null,null);

// set options to display autosuggest window
IAutoComplete2 *pac2;

if (!pac->QueryInterface(_IID_IAutoComplete2, &pac2))
{
    pac2->SetOptions(ACO_AUTOSUGGEST);
    pac2->Release();
}
// enable autocomplete
pac->Enable(true);
}
}
return ok;
}





// string enumerator methods
// only these strings are suggested while typing text in combo

CEnumString::CEnumString()
{
m_refs = 1;
list.Create();
pos = 1; // uninitialized
}


CEnumString::_CEnumString()
{
list.RemoveAll(false);
}


CEnumString::QueryInterface(REFIID *riid,/*out*/void *ppvObject),HRESULT
{
// if (*riid == _IID_IEnumString)
if (!memcmp(riid, _IID_IEnumString, sizeof(GUID)) || !memcmp(riid, _IID_IUnknown, sizeof(GUID)))
{
*(pointer)ppvObject = this;
AddRef();
return S_OK;
}
*(pointer)ppvObject = null;
return E_NOINTERFACE;
}

CEnumString::AddRef(),ULONG
{
m_refs++;
return m_refs;
}

CEnumString::Release(),ULONG
{
m_refs--;
if (!m_refs) {delete this; return 0;}
return m_refs;
}
// Retrieves the next celt items in the enumeration sequence.
// If there are fewer than the requested number of elements left in the sequence,
// it retrieves the remaining elements.
// The number of elements actually retrieved is returned through pceltFetched
// (unless the caller passed in NULL for that parameter).
CEnumString::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched),HRESULT
{
if (!celt) return S_FALSE;
*pceltFetched = 0;
// list position pointer initialized?
if (pos == 1) pos = list.GetFirst();

while (celt!=0 && pos!=0)
{
string *data = list.GetData(pos);
*(pointer)rgelt = data;
rgelt += 4;
*pceltFetched++;

celt--;
pos = list.GetNext(pos);
}
// return S_OK if the number of elements supplied is celt; S_FALSE otherwise.
// return celt ? S_FALSE : S_OK; <- not implemented
return IIF(celt, S_FALSE, S_OK);
// IIF( BOOL, ifTrue, ifFalse )
// if celt return S_FALSE else return S_OK;
}


// Skips over the next specified number of elements in the enumeration sequence
CEnumString::Skip(ULONG celt),HRESULT
{
// list position pointer initialized?
if (pos == 1) pos = list.GetFirst();

while (celt && pos)
{
celt--;
pos = list.GetNext(pos);
}
// return S_OK if the number of elements skipped is celt; otherwise, S_FALSE
return IIF(celt, S_FALSE, S_OK);
}


// Resets the enumeration sequence to the beginning
CEnumString::Reset(),HRESULT
{
pos = list.GetFirst();
return S_OK;
}

// Creates another enumerator that contains the same enumeration state
// as the current one. Using this function, a client can record
// a particular point in the enumeration sequence and then return to that point
// at a later time. The new enumerator supports the same interface as
// the original one
CEnumString::Clone(IEnumString *ppenum),HRESULT
{
CEnumString *pdup = new(CEnumString, 1);
*(pointer)ppenum = pdup;
if (pdup)
{
// copy string list
pointer oldpos = list.GetFirst();
pointer newpos = 0;
while (oldpos)
{
pdup->list.Add(list.GetData(oldpos));
if (!newpos) newpos = pdup->list.GetFirst() else newpos = pdup->list.GetNext(newpos);
if (oldpos == pos) pdup->pos = newpos;
oldpos = list.GetNext(oldpos);
}
return S_OK;
}
// This method supports the standard return values E_INVALIDARG,
// E_OUTOFMEMORY, and E_UNEXPECTED
return E_OUTOFMEMORY;
}