/* Copyright 1996 Microsoft */ #ifndef _AUTOCOMP_HPP_ #define _AUTOCOMP_HPP_ #include "accdel.h" // TODO List: // 1. Convert AutoComplete to be a Free Threaded object and move it and all it's // lists into MultiApartment model. // 2. Get thread out of ThreadPool API in shlwapi.dll instead of creating thead our // selves. // 3. See if SHWaitForSendMessageThread() will cause a lock if bg thread is in SendMessage(). // If so, make sure the bg loop's hard loop doesn't call SendMessage() in this case without // first looking for QUIT message. // WARNING On Usage: // This object is marked Apartment model and this abuses COM. These are rules // that need to be followed to prevent bugs. This object will be used within three scopes. // The first scope is the caller doing: 1a) CoInitialize(), 1b) CoCreateInstance(). // 1c) p->Init(), 1d) p->Release(), 1e) CoUninitialize(). // The second scope is the object doing: 2a) Subclass();AddRef(), 2b) WM_DESTROY;Release(); // 1c) p->Init(), 1d) p->Release(), 1e) CoUninitialize(). // The third scope is the background thread doing: 3a) (in thread proc) CoInitialize(), // 3b) CoUninitialize(). // This object requires that 1E come after 2B and that should be the only requirement // for the use of this object. // // PRIVATE // #define AC_LIST_GROWTH_CONST 50 const WCHAR CH_WILDCARD = L'\1'; // indicates a wildcard search // // Debug Flags // #define AC_WARNING TF_WARNING + TF_AUTOCOMPLETE #define AC_ERROR TF_ERROR + TF_AUTOCOMPLETE #define AC_GENERAL TF_GENERAL + TF_AUTOCOMPLETE #define AC_FUNC TF_FUNC + TF_AUTOCOMPLETE // Enable test regkey #define ALLOW_ALWAYS_DROP_UP // // WndProc messages to the dropdown window // enum { AM_BUTTONCLICK = WM_APP + 400, AM_UPDATESCROLLPOS, AM_DESTROY }; // // Flags passed from background thread when the search is completed enum { SRCH_LIMITREACHED = 0x01, // out of memory or we reached our limit SRCH_USESORTINDEX = 0x02, // use sort index to order results }; #define ACO_UNINITIALIZED 0x80000000 // if autocomplete options have not been initialized // // PUBLIC // HRESULT SHUseDefaultAutoComplete(HWND hwndEdit, IBrowserService * pbs, IN OPTIONAL IAutoComplete2 ** ppac, OUT OPTIONAL IShellService ** ppssACLISF, OUT OPTIONAL BOOL fUseCMDMRU); // Forward references class CAutoComplete; class CACString* CreateACString(LPCWSTR pszStr); //+------------------------------------------------------------------------- // CACString - Autocomplete string shared by foreground & background threads //-------------------------------------------------------------------------- class CACString { public: ULONG AddRef(); ULONG Release(); ULONG GetSortIndex() { return m_ulSortIndex; } void SetSortIndex(ULONG ulIndex) { m_ulSortIndex = ulIndex; } LPCWSTR GetStr() const { return m_sz; } LPCWSTR GetStrToCompare() const { return m_sz + m_iIgnore; } int GetLength() const { return m_cChars; } int GetLengthToCompare() const { return m_cChars - m_iIgnore; } const WCHAR& operator [] (int nIndex) const { return m_sz[nIndex]; } operator LPCWSTR() { return m_sz; } BOOL HasPrefix() { return m_iIgnore; } BOOL PrefixLength() { return m_iIgnore; } // Note, the following compare functions ignore the prefix of the CACString int CompareSortingIndex(CACString& r); int StrCmpI(LPCWSTR psz) { return ::StrCmpI(m_sz + m_iIgnore, psz); } int StrCmpI(CACString& r) { return ::StrCmpI(m_sz + m_iIgnore, r.m_sz + r.m_iIgnore); } int StrCmpNI(LPCWSTR psz, int cch) { return ::StrCmpNI(m_sz + m_iIgnore, psz, cch); } protected: friend CACString* CreateACString(LPCWSTR pszStr, int iIgnore, ULONG ulSortIndex); // Prevent creation on stack CACString(); LONG m_cRef; // reference count int m_cChars; // length of string (excluding null) int m_iIgnore; // # prefix characters to ignore when comparing strings ULONG m_ulSortIndex; // can be used instead of default alphabetical sorting WCHAR m_sz[1]; // first character of the string }; //+------------------------------------------------------------------------- // CACThread - Autocomplete thread that runs in the background //-------------------------------------------------------------------------- class CACThread : public IUnknown { public: // *** IUnknown *** virtual STDMETHODIMP_(ULONG) AddRef(); virtual STDMETHODIMP_(ULONG) Release(); virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); CACThread(CAutoComplete& rAutoComp); virtual ~CACThread(); BOOL Init(IEnumString* pes, IACList* pacl); void GotFocus(); void LostFocus(); BOOL HasFocus() { return m_fWorkItemQueued != 0; } BOOL StartSearch(LPCWSTR pszSearch, DWORD dwOptions); void StopSearch(); void SyncShutDownBGThread(); BOOL IsDisabled() { return m_fDisabled; } // Helper functions static BOOL MatchesSpecialPrefix(LPCWSTR pszSearch); static int GetSpecialPrefixLen(LPCWSTR psz); protected: LONG m_cRef; CAutoComplete* m_pAutoComp; // portion of autocomplete that runs on main thread LONG m_fWorkItemQueued; // if request made to shlwapi thread pool LONG m_idThread; HANDLE m_hCreateEvent; // thread startup syncronizatrion BOOL m_fDisabled:1; // is autocomplete disabled? LPWSTR m_pszSearch; // String we are currently searching for HDPA m_hdpa_list; // list of completions DWORD m_dwSearchStatus;// see SRCH_* flags IEnumString* m_pes; // Used internally for real AutoComplete functionality. IEnumACString* m_peac; // Used with IEnumString to get sort index IACList* m_pacl; // Additional methods for autocomplete lists (optional). void _SendAsyncShutDownMsg(BOOL fFinalShutDown); void _FreeThreadData(); HRESULT _ThreadLoop(); HRESULT _Next(LPWSTR szUrl, ULONG cchUrl, ULONG* pulSortIndex); HRESULT _ProcessMessage(MSG * pMsg, DWORD * pdwTimeout, BOOL * pfStayAlive); void _Search(LPWSTR pszSearch, DWORD dwOptions); BOOL _AddToList(LPTSTR pszUrl, int cchMatch, ULONG ulSortIndex); void _DoExpand(LPCWSTR pszSearch); static DWORD WINAPI _ThreadProc(LPVOID lpv); static int CALLBACK _DpaCompare(LPVOID p1, LPVOID p2, LPARAM lParam); }; //+------------------------------------------------------------------------- // CAutoComplete - Main autocomplete class that runs on the main UI thread //-------------------------------------------------------------------------- class CAutoComplete : public IAutoComplete2 , public IAutoCompleteDropDown , public IEnumString , public CDelegateAccessibleImpl { public: ////////////////////////////////////////////////////// // Public Interfaces ////////////////////////////////////////////////////// // *** IUnknown *** virtual STDMETHODIMP_(ULONG) AddRef(); virtual STDMETHODIMP_(ULONG) Release(); virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); // *** IEnumString *** virtual STDMETHODIMP Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched); virtual STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; } virtual STDMETHODIMP Reset(); virtual STDMETHODIMP Clone(IEnumString **ppenum) { return E_NOTIMPL; } // *** IAutoComplete *** virtual STDMETHODIMP Init(HWND hwnd, IUnknown *punkACL, LPCOLESTR pwszRegKeyPath, LPCOLESTR pwszQuickCompleteString); virtual STDMETHODIMP Enable(BOOL fEnable); // *** IAutoComplete2 *** virtual STDMETHODIMP SetOptions(DWORD dwFlag); virtual STDMETHODIMP GetOptions(DWORD* pdwFlag); // *** IAutoCompleteDropDown *** virtual STDMETHODIMP GetDropDownStatus(DWORD *pdwFlags, LPWSTR *ppwszString); virtual STDMETHODIMP ResetEnumerator(); // *** IAccessible *** STDMETHODIMP get_accName(VARIANT varChild, BSTR *pszName); protected: // Methods called by the background thread friend CACThread; void SearchComplete(HDPA hdpa, DWORD dwSearchStatus) { PostMessage(m_hwndEdit, m_uMsgSearchComplete, dwSearchStatus, (LPARAM)hdpa); } BOOL IsEnabled(); // Constructor / Destructor (protected so we can't create on stack) CAutoComplete(); virtual ~CAutoComplete(); // Instance creator friend HRESULT CAutoComplete_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi); BOOL _Init(); // Private variables LONG m_cRef; CACThread* m_pThread; // background autocomplete thread TCHAR m_szQuickComplete[MAX_PATH]; TCHAR m_szRegKeyPath[MAX_PATH]; DWORD m_dwFlags; HWND m_hwndEdit; HWND m_hwndCombo; // if m_hwndEdit is part of a combobox HFONT m_hfontListView; LPTSTR m_pszCurrent; int m_iCurrent; DWORD m_dwLastSearchFlags; WNDPROC m_pOldListViewWndProc; IEnumString * m_pes; // Used internally for real AutoComplete functionality. IACList * m_pacl; // Additional methods for autocomplete lists (optional). HDPA m_hdpa; // sorted completions list HDPA m_hdpaSortIndex; // matches from m_hdpa ordered by sort index LPWSTR m_pszLastSearch; // string last sent for completion int m_iFirstMatch; // first match in list (-1 if no matches) int m_iLastMatch; // last match in list (-1 if no matches) int m_iAppended; // item completed in the edit box BITBOOL m_fEditControlUnicode:1; // if the edit control is unicode. BITBOOL m_fNeedNewList:1; // last search was truncated BITBOOL m_fDropDownResized:1; // user has resized drop down BITBOOL m_fAppended:1; // if something currently appended BITBOOL m_fSearchForAdded:1; // if last item in dpa is "Search for <>" BITBOOL m_fSearchFor:1; // if "Search for <>" is to be displayed BITBOOL m_fImeCandidateOpen:1; // if the IME's candidate window is visible DWORD m_dwOptions; // autocomplete options (ACO_*) EDITWORDBREAKPROC m_oldEditWordBreakProc; // original word break proc for m_hwndEdit // Member variables for drop-down auto-suggest window HWND m_hwndDropDown; // Shows completions in drop-down window HWND m_hwndList; // Shows completions in drop-down window HWND m_hwndScroll; // scrollbar HWND m_hwndGrip; // gripper for resizing the dropdown int m_nStatusHeight; // height of status in drop-down int m_nDropWidth; // width of drop-down window int m_nDropHeight; // height of drop-down window int m_cxGripper; // width/height of gripper BITBOOL m_fDroppedUp:1; // if dropdown is over top the edit box #ifdef ALLOW_ALWAYS_DROP_UP BITBOOL m_fAlwaysDropUp:1; // TEST regkey to always drop up #endif BITBOOL m_fSettingText:1; // if setting the edit text BITBOOL m_fInHotTracking:1; // if new selection is due to hot-tracking // Member Variables used for external IEnumString IEnumString * m_pesExtern; // Used internally for real AutoComplete functionality. LPTSTR m_szEnumString; // Registered messages sent to edit window UINT m_uMsgSearchComplete; UINT m_uMsgItemActivate; static HHOOK s_hhookMouse; // windows hook installed when drop-down visible static HWND s_hwndDropDown; // dropdown currently visible static BOOL s_fNoActivate; // keep topmost-window from losing activation void _OnSearchComplete(HDPA hdpa, DWORD dwSearchStatus); BOOL _GetItem(int iIndex, LPWSTR pswText, int cchMax, BOOL fDisplayName); void _UpdateCompletion(LPCWSTR pszTyped, int iChanged, BOOL fAppend); void _HideDropDown(); void _ShowDropDown(); void _PositionDropDown(); void _SeeWhatsEnabled(); BOOL _IsAutoSuggestEnabled() { return m_dwOptions & ACO_AUTOSUGGEST; } BOOL _IsRTLReadingEnabled() { return m_dwOptions & ACO_RTLREADING; } BOOL _IsAutoAppendEnabled() { return (m_dwOptions & ACO_AUTOAPPEND) || (m_dwOptions & ACO_UNINITIALIZED); } BOOL _IsComboboxDropped() { return (m_hwndCombo && ComboBox_GetDroppedState(m_hwndCombo)); } void _UpdateGrip(); void _UpdateScrollbar(); static BOOL _IsWhack(TCHAR ch); static BOOL _IsBreakChar(WCHAR wch); BOOL _WantToAppendResults(); int _JumpToNextBreak(int iLoc, DWORD dwFlags); BOOL _CursorMovement(WPARAM wParam); void _RemoveCompletion(); void _GetEditText(); void _SetEditText(LPCWSTR psz); void _UpdateText(int iStartSel, int iEndSel, LPCTSTR pszCurrent, LPCTSTR pszNew); BOOL _OnKeyDown(WPARAM wParam); LRESULT _OnChar(WPARAM wParam, LPARAM lParam); void _StartCompletion(BOOL fAppend, BOOL fEvenIfEmpty = FALSE); BOOL _StartSearch(LPCWSTR pszSearch); void _StopSearch(); BOOL _ResetSearch(); void _GotFocus(); LPTSTR _QuickEnter(); BOOL _AppendNext(BOOL fAppendToWhack); BOOL _AppendPrevious(BOOL fAppendToWhack); void _Append(CACString& rStr, BOOL fAppendToWhack); BOOL _SetQuickCompleteStrings(LPCOLESTR pcszRegKeyPath, LPCOLESTR pcszQuickCompleteString); void _SubClassParent(HWND hwnd); void _UnSubClassParent(HWND hwnd); LRESULT _DropDownWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT _EditWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT _ListViewWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); void _DropDownDrawItem(LPDRAWITEMSTRUCT pdis); BOOL _DropDownNotify(LPNMHDR pnmhdr); static int _DPADestroyCallback(LPVOID p, LPVOID d); static void _FreeDPAPtrs(HDPA hdpa); static int CALLBACK _DPACompareSortIndex(LPVOID p1, LPVOID p2, LPARAM lParam); static int CALLBACK EditWordBreakProcW(LPWSTR lpch, int ichCurrent, int cch, int code); static LRESULT CALLBACK s_EditWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); static LRESULT CALLBACK s_DropDownWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK s_ListViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK s_ParentWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); static LRESULT CALLBACK s_GripperWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData); static LRESULT CALLBACK s_MouseHook(int nCode, WPARAM wParam, LPARAM lParam); }; #endif