2025-11-28 00:35:46 +09:00

879 lines
26 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All Rights Reserved.
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <assert.h>
#include <TIPAutoComplete.h>
#include <TIPAutoComplete_i.c>
#include <PenInputPanel.h>
#include <strsafe.h>
#include <new>
#include "Resource.h"
#pragma comment(lib, "comctl32.lib")
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define MAX_STRING 256
#define MAX_ENTRY 1024
#define LISTVIEW_COLUMN_WIDTH 244
const static WCHAR s_wzClassName[] = L"TipAutoCompleteSampleDropdownClass";
const static WCHAR s_wzDropdownTitle[] = L"TIP AutoComplete Sample Dropdown";
const static WCHAR s_wzTipACDialogProp[] = L"CTipACDialog_This";
WCHAR s_rgwzList[MAX_ENTRY][MAX_STRING];
const static PCWSTR s_wzListBackup[] =
{
L"Aphrodite",
L"Apollo",
L"Ares",
L"Artemis",
L"Athena",
L"Demeter",
L"Dionysus",
L"Hades",
L"Hephaestus",
L"Hera",
L"Hermes",
L"Hestia",
L"Poseidon",
L"Zeus"
};
HINSTANCE g_hInstance;
class CTipACDialog : public ITipAutoCompleteProvider
{
public:
CTipACDialog();
static INT_PTR CALLBACK s_DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
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);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// ITipAutoCompleteProvider methods
STDMETHODIMP UpdatePendingText(__RPC__in BSTR bstrPendingText);
STDMETHODIMP Show(BOOL fShow);
private:
long m_nRefCount;
long m_iLineCount;
HWND m_hwndDlg;
HWND m_hwndEdit;
HWND m_hwndListbox;
BOOL m_fSynchronize;
WCHAR m_wzPending[MAX_STRING];
// for !m_fSynchronize mode only
HWND m_hwndDropdown;
HWND m_hwndListView;
WNDPROC m_pOldListViewWndProc;
BOOL m_fInHotTracking;
HRESULT ShowAutoCompleteDropdown(BOOL fShow);
LRESULT EditWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT DropDownWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
void DropDownDrawItem(LPDRAWITEMSTRUCT pdis);
BOOL DropDownNotify(LPNMHDR pnmhdr);
void OnListViewCreate();
LRESULT ListViewWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
ITipAutoCompleteClient *m_pTipACClient;
BOOL OnInitDialog(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void OnCloseDialog(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void OnCommand(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void DisplayMatchedOutput(WCHAR * pszInput);
void InitTabletTip();
void SetTipMode();
void PopulateList();
};
CTipACDialog::CTipACDialog()
: m_nRefCount(0),
m_iLineCount(0),
m_hwndDlg(NULL),
m_hwndEdit(NULL),
m_hwndListbox(NULL),
m_hwndDropdown(NULL),
m_hwndListView(NULL),
m_fSynchronize(FALSE),
m_fInHotTracking(FALSE),
m_pTipACClient(NULL)
{
m_wzPending[0] = NULL;
}
/*static*/ INT_PTR CALLBACK CTipACDialog::s_DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG)
{
SetWindowLongPtr(hWnd, GWLP_USERDATA, lParam);
}
CTipACDialog *pThis = (CTipACDialog *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
try
{
if (pThis)
{
switch (uMsg)
{
case WM_COMMAND:
pThis->OnCommand(hWnd, WM_COMMAND, wParam, lParam);
break;
case WM_INITDIALOG:
pThis->OnInitDialog(hWnd, WM_INITDIALOG, wParam, lParam);
break;
case WM_DESTROY:
pThis->OnCloseDialog(hWnd, WM_DESTROY, wParam, lParam);
break;
}
}
}
catch (...)
{
// Got an unhandled exception, cancel the dialog
EndDialog(hWnd, E_FAIL);
}
return FALSE;
}
/*static*/ LRESULT CALLBACK CTipACDialog::s_EditWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR dwRefData)
{
CTipACDialog *pThis = (CTipACDialog *)dwRefData;
if (pThis)
{
if (pThis->m_hwndEdit == hwnd)
{
return pThis->EditWndProc(uMsg, wParam, lParam);
}
}
assert(pThis && L"CTipACDialog::s_EditWndProc: pThis == NULL");
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/*static*/ LRESULT CALLBACK CTipACDialog::s_DropDownWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CTipACDialog *pThis;
if (WM_NCCREATE == uMsg)
{
pThis = (CTipACDialog *)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
pThis->m_hwndDropdown = hwnd;
}
else
{
pThis = (CTipACDialog *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->DropDownWndProc(uMsg, wParam, lParam);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/*static*/ LRESULT CALLBACK CTipACDialog::s_ListViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CTipACDialog *pThis = (CTipACDialog *)GetProp(hwnd, s_wzTipACDialogProp);
if (pThis)
{
return pThis->ListViewWndProc(uMsg, wParam, lParam);
}
assert(FALSE && L"CTipACDialog::s_ListViewWndProc: pThis == NULL");
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HRESULT CTipACDialog::ShowAutoCompleteDropdown(BOOL fShow)
{
HRESULT hr = E_FAIL;
if (m_pTipACClient && m_hwndDropdown)
{
if (fShow)
{
LRESULT cItems = SendMessage(m_hwndListbox, LB_GETCOUNT, 0, (LPARAM)0);
ListView_SetItemCountEx(m_hwndListView, cItems, 0);
if (0 == cItems)
{
fShow = false;
}
}
if (fShow)
{
BOOL fAllowShow = TRUE;
BOOL fDroppedUp = FALSE;
RECT rcEdit;
GetWindowRect(m_hwndEdit, &rcEdit);
int x = rcEdit.left + 5;
int y = rcEdit.bottom + 200;
int w = rcEdit.right - rcEdit.left;
int h = 200;
hr = m_pTipACClient->RequestShowUI(m_hwndDropdown, &fAllowShow);
assert(SUCCEEDED(hr) && "Failed in call to RequestShowUI");
if (SUCCEEDED(hr) && fAllowShow)
{
RECT rcACList = { x, y, x+w, y+h };
RECT rcModifiedACList = { 0, 0, 0, 0 };
hr = m_pTipACClient->PreferredRects(&rcACList, &rcEdit, &rcModifiedACList, &fDroppedUp);
if (SUCCEEDED(hr))
{
x = rcModifiedACList.left;
y = rcModifiedACList.top;
w = rcModifiedACList.right - rcModifiedACList.left;
h = rcModifiedACList.bottom - rcModifiedACList.top;
SetWindowPos(m_hwndDropdown, HWND_TOP, x, y, w, h, SWP_SHOWWINDOW | SWP_NOACTIVATE);
}
}
}
else // hide
{
ShowWindow(m_hwndDropdown, SW_HIDE);
}
}
return hr;
}
LRESULT CTipACDialog::EditWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SETTEXT:
ShowAutoCompleteDropdown(FALSE);
break;
case WM_SETFOCUS:
break;
case WM_KILLFOCUS:
{
HWND hwndGetFocus = (HWND)wParam;
if (m_hwndEdit != hwndGetFocus)
{
if (m_hwndDropdown && (GetFocus() != m_hwndDropdown))
{
ShowAutoCompleteDropdown(FALSE);
}
}
break;
}
case WM_DESTROY:
{
RemoveWindowSubclass(m_hwndEdit, s_EditWndProc, 0);
if (m_pTipACClient)
{
#ifdef _DEBUG
HRESULT hr = m_pTipACClient->UnadviseProvider(m_hwndEdit, static_cast<ITipAutoCompleteProvider *>(this));
assert(SUCCEEDED(hr) && "Failed in call to UnadviseProvider");
#else // !_DEBUG
m_pTipACClient->UnadviseProvider(m_hwndEdit, static_cast<ITipAutoCompleteProvider *>(this));
#endif // _DEBUG
m_pTipACClient = NULL;
}
break;
}
}
return DefSubclassProc(m_hwndEdit, uMsg, wParam, lParam);
}
LRESULT CTipACDialog::DropDownWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:
OnListViewCreate();
return (NULL != m_hwndListView);
case WM_DESTROY:
{
ShowAutoCompleteDropdown(FALSE);
SetWindowLongPtr(m_hwndDropdown, GWLP_USERDATA, (LONG_PTR)NULL);
HWND hwnd = m_hwndDropdown;
m_hwndDropdown = NULL;
if (NULL != m_hwndListView)
{
DestroyWindow(m_hwndListView);
m_hwndListView = NULL;
}
/// scroll window? Grip window?
// The dropdown incremented this object's ref count
Release();
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
case WM_SIZE:
{
int nWidth = LOWORD(lParam);
int nHeight = HIWORD(lParam);
MoveWindow(m_hwndListView, 0, 0, nWidth, nHeight, TRUE);
break;
}
case WM_MOUSEACTIVATE:
// We don't want mouse clicks to activate us and take focus from the edit box.
return (LRESULT)MA_NOACTIVATE;
case WM_DRAWITEM:
DropDownDrawItem((LPDRAWITEMSTRUCT)lParam);
break;
case WM_NOTIFY:
{
LRESULT lResult = DropDownNotify((LPNMHDR)lParam);
if (lResult)
{
return lResult;
}
break;
}
}
return DefWindowProc(m_hwndDropdown, uMsg, wParam, lParam);
}
void CTipACDialog::DropDownDrawItem(LPDRAWITEMSTRUCT pdis)
{
if (-1 != pdis->itemID)
{
HDC hdc = pdis->hDC;
RECT rc = pdis->rcItem;
BOOL fTextHighlight = pdis->itemState & ODS_SELECTED;
SetBkColor(hdc, GetSysColor(fTextHighlight ? COLOR_HIGHLIGHT : COLOR_WINDOW));
SetTextColor(hdc, GetSysColor(fTextHighlight ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
// Center the string vertically in rc
SIZE sizeText;
WCHAR szText[MAX_STRING];
SendMessage(m_hwndListbox, LB_GETTEXT, pdis->itemID, (LPARAM)szText);
int cch = lstrlen(szText);
GetTextExtentPoint(hdc, szText, cch, &sizeText);
int yMid = (rc.top + rc.bottom) / 2;
int yString = yMid - (sizeText.cy / 2);
int xString = 5;
ExtTextOut(hdc, xString, yString, ETO_OPAQUE | ETO_CLIPPED, &rc, szText, cch, NULL);
}
}
BOOL CTipACDialog::DropDownNotify(LPNMHDR pnmhdr)
{
WCHAR wzBuf[MAX_STRING];
switch (pnmhdr->code)
{
case LVN_GETDISPINFO:
{
assert(pnmhdr->hwndFrom == m_hwndListView);
LV_DISPINFO *pdi = (LV_DISPINFO *)pnmhdr;
if (pdi->item.mask & LVIF_TEXT)
{
SendMessage(m_hwndListbox, LB_GETTEXT, pdi->item.iItem, (LPARAM)pdi->item.pszText);
}
break;
}
case LVN_ITEMCHANGED:
{
const NMLISTVIEW *pnmv = (const NMLISTVIEW *)pnmhdr;
if (!m_fInHotTracking &&
(pnmv->uChanged & LVIF_STATE) && (pnmv->uNewState & (LVIS_FOCUSED | LVIS_SELECTED)))
{
SendMessage(m_hwndListbox, LB_GETTEXT, pnmv->iItem, (LPARAM)wzBuf);
m_wzPending[0] = NULL;
SetWindowText(m_hwndEdit, wzBuf);
int cch = lstrlen(wzBuf);
SendMessage(m_hwndEdit, EM_SETSEL, cch, cch);
}
break;
}
case LVN_ENDSCROLL:
InvalidateRect(m_hwndListView, NULL, TRUE);
break;
case LVN_ITEMACTIVATE:
{
const NMITEMACTIVATE *pnmia = (const NMITEMACTIVATE *)pnmhdr;
SendMessage(m_hwndListbox, LB_GETTEXT, pnmia->iItem, (LPARAM)wzBuf);
m_wzPending[0] = NULL;
// Inform the TIP that the user made a selection.
// This causes the text in the TIP to be discarded.
m_pTipACClient->UserSelection();
SetWindowText(m_hwndEdit, wzBuf);
SendMessage(m_hwndEdit, EM_SETSEL, 0, -1);
ShowAutoCompleteDropdown(FALSE);
break;
}
case LVN_HOTTRACK:
{
const NMLISTVIEW *pnmlv = (const NMLISTVIEW *)pnmhdr;
LVHITTESTINFO lvh;
lvh.pt = pnmlv->ptAction;
int iItem = ListView_HitTest(m_hwndListView, &lvh);
if (-1 != iItem)
{
m_fInHotTracking = TRUE;
ListView_SetItemState(m_hwndListView, iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED | LVIS_CUT | LVIS_DROPHILITED);
SendMessage(m_hwndListView, LVM_ENSUREVISIBLE, iItem, (LPARAM)FALSE);
m_fInHotTracking = FALSE;
}
return TRUE; // we processed this
}
}
return FALSE;
}
void CTipACDialog::OnListViewCreate()
{
DWORD dwFlags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | LVS_REPORT | LVS_NOCOLUMNHEADER | LVS_SINGLESEL | LVS_OWNERDATA | LVS_OWNERDRAWFIXED;
m_hwndListView = CreateWindowEx(0, WC_LISTVIEW, s_wzDropdownTitle, dwFlags, 0, 0, LISTVIEW_COLUMN_WIDTH, 10000, m_hwndDropdown, NULL, g_hInstance, NULL);
if (m_hwndListView)
{
// Subclass the listview window
if (SetProp(m_hwndListView, s_wzTipACDialogProp, this))
{
m_pOldListViewWndProc = (WNDPROC)SetWindowLongPtr(m_hwndListView, GWLP_WNDPROC, (LONG_PTR)&s_ListViewWndProc);
}
ListView_SetExtendedListViewStyle(m_hwndListView, LVS_EX_FULLROWSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TRACKSELECT | LVS_EX_DOUBLEBUFFER);
LV_COLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = LISTVIEW_COLUMN_WIDTH;
ListView_InsertColumn(m_hwndListView, 0, &lvColumn);
}
}
LRESULT CTipACDialog::ListViewWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
CallWindowProc(m_pOldListViewWndProc, m_hwndListView, uMsg, wParam, lParam);
return 0;
case WM_DESTROY:
// Restore old wndproc
RemoveProp(m_hwndListView, s_wzTipACDialogProp);
if (m_pOldListViewWndProc)
{
SetWindowLongPtr(m_hwndListView, GWLP_WNDPROC, (LONG_PTR)m_pOldListViewWndProc);
CallWindowProc(m_pOldListViewWndProc, m_hwndListView, uMsg, wParam, lParam);
m_pOldListViewWndProc = NULL;
}
return 0;
}
return CallWindowProc(m_pOldListViewWndProc, m_hwndListView, uMsg, wParam, lParam);
}
/* *** IUnknown *** */
ULONG CTipACDialog::AddRef()
{
return InterlockedIncrement(&m_nRefCount);
}
ULONG CTipACDialog::Release()
{
ULONG uCount = InterlockedDecrement(&m_nRefCount);
if (0 == uCount)
{
delete this;
}
return uCount;
}
HRESULT CTipACDialog::QueryInterface(REFIID iid, void **ppv)
{
if (!ppv)
{
return E_POINTER;
}
if (IID_IUnknown == iid)
{
*ppv = static_cast<IUnknown *>(this);
}
else if (IID_ITipAutoCompleteProvider == iid)
{
*ppv = static_cast<ITipAutoCompleteProvider *>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
/* *** ITipAutoCompleteProvider *** */
HRESULT CTipACDialog::UpdatePendingText(BSTR bstrPendingText)
{
int cchNewPending = lstrlen(bstrPendingText);
if (m_fSynchronize)
{
// Set the text of m_hwndEdit to bstrPendingText
SetWindowText(m_hwndEdit, bstrPendingText);
SendMessage(m_hwndEdit, EM_SETSEL, cchNewPending, cchNewPending);
}
else
{
if (0 == cchNewPending)
{
m_wzPending[0] = NULL;
}
int const cchEdit = GetWindowTextLength(m_hwndEdit) + 1;
// is there text in the edit already?
if (1 < cchEdit)
{
LRESULT lRes = SendMessage(m_hwndEdit, EM_GETSEL, NULL, NULL);
int ichStart = LOWORD(lRes);
int ichEnd = HIWORD(lRes);
PWSTR pszEdit = new (std::nothrow) WCHAR[cchEdit];
if (pszEdit)
{
GetWindowText(m_hwndEdit, pszEdit, cchEdit);
// copy out of the edit from 0 through ichStart, inclusive
StringCchCopyN(m_wzPending, ARRAYSIZE(m_wzPending), pszEdit, ichStart);
// now append bstrPendingText
StringCchCat(m_wzPending, ARRAYSIZE(m_wzPending), bstrPendingText);
// now append the rest of the edit string
StringCchCat(m_wzPending, ARRAYSIZE(m_wzPending), &pszEdit[ichEnd]);
delete [] pszEdit;
}
}
else if (m_wzPending[0] || (0 < cchNewPending))
{
StringCchCopy(m_wzPending, ARRAYSIZE(m_wzPending), bstrPendingText);
}
DisplayMatchedOutput((0 < m_wzPending[0]) ? m_wzPending : L"");
ShowAutoCompleteDropdown(0 < m_wzPending[0]);
}
return S_OK;
}
HRESULT CTipACDialog::Show(BOOL fShow)
{
// ignore needing to show or hide the AutoComplete list if m_fSynchronize is set
ShowAutoCompleteDropdown(fShow);
return S_OK;
}
BOOL CTipACDialog::OnInitDialog(HWND hwndDlg, UINT, WPARAM, LPARAM)
{
m_hwndDlg = hwndDlg;
m_hwndEdit = GetDlgItem(hwndDlg, IDC_EDIT1);
m_hwndListbox = GetDlgItem(hwndDlg, IDC_LIST1);
SetWindowSubclass(m_hwndEdit, &s_EditWndProc, 0, (DWORD_PTR)this);
CheckDlgButton(hwndDlg, IDC_CHECK1, m_fSynchronize);
InitTabletTip();
PopulateList();
return TRUE;
}
void CTipACDialog::OnCloseDialog(HWND, UINT, WPARAM, LPARAM)
{
if (NULL != m_pTipACClient)
{
m_pTipACClient->Release();
m_pTipACClient = NULL;
}
}
void CTipACDialog::InitTabletTip()
{
assert(!m_pTipACClient);
// Initialize the AutoComplete client
WCHAR wzCLSID[40];
DWORD cb = ARRAYSIZE(wzCLSID) * sizeof(WCHAR);
if (ERROR_SUCCESS == RegGetValue(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoComplete\\Client\\", NULL, RRF_RT_REG_SZ, NULL, (void *)&wzCLSID, &cb))
{
CLSID clsidTipAutoComplete;
if (SUCCEEDED(CLSIDFromString(wzCLSID, &clsidTipAutoComplete)))
{
HRESULT hrTip = CoCreateInstance(clsidTipAutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pTipACClient));
if (SUCCEEDED(hrTip))
{
hrTip = m_pTipACClient->AdviseProvider(m_hwndEdit, static_cast<ITipAutoCompleteProvider *>(this));
assert(SUCCEEDED(hrTip) && "Failed in call to AdviseProvider");
if (FAILED(hrTip))
{
// we failed to talk to the TIP fully, release any reference
m_pTipACClient = NULL;
}
else
{
SetTipMode();
}
}
}
}
}
void CTipACDialog::SetTipMode()
{
if (m_fSynchronize)
{
// Request the TIP to operate in no Insert button mode if Syncronize checkbox is checked
SetProp(m_hwndEdit, MICROSOFT_TIP_NO_INSERT_BUTTON_PROPERTY, (HANDLE)TRUE);
ShowWindow(m_hwndListbox, SW_SHOW);
DestroyWindow(m_hwndDropdown);
m_hwndDropdown = NULL;
}
else
{
// We want the insert button
SetProp(m_hwndEdit, MICROSOFT_TIP_NO_INSERT_BUTTON_PROPERTY, (HANDLE)FALSE);
ShowWindow(m_hwndListbox, SW_HIDE);
if (NULL == m_hwndDropdown) // need to create a dropdown window
{
WNDCLASS wc = {};
wc.lpfnWndProc = s_DropDownWndProc;
wc.cbWndExtra = sizeof(CTipACDialog *);
wc.hInstance = g_hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.style = CS_SAVEBITS | CS_DROPSHADOW;
wc.lpszClassName = s_wzClassName;
RegisterClass(&wc);
DWORD dwExStyle = WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOPARENTNOTIFY;
// The dropdown holds a ref on this object
AddRef();
m_hwndDropdown = CreateWindowEx(dwExStyle, s_wzClassName, s_wzDropdownTitle,
WS_POPUP | WS_BORDER | WS_CLIPCHILDREN, 0, 0, 100, 400,
NULL, NULL, g_hInstance, this);
if (NULL == m_hwndDropdown)
{
Release();
}
}
if (m_hwndDropdown)
{
DisplayMatchedOutput(m_wzPending);
}
}
}
void CTipACDialog::PopulateList()
{
FILE *fp = NULL;
WCHAR szInput[MAX_STRING];
WCHAR *pszInput;
size_t cch;
m_iLineCount = 0;
_wfopen_s(&fp, L"List.txt", L"r");
if (fp)
{
do
{
pszInput = fgetws(szInput, MAX_STRING, fp);
cch = wcslen(szInput);
if (cch && szInput[cch - 1] == 0x0a)
{
szInput[cch - 1] = NULL; // skip the linefeed
cch--;
}
wcsncpy_s(s_rgwzList[m_iLineCount], MAX_STRING, szInput, cch);
m_iLineCount++;
SendMessage(m_hwndListbox, LB_ADDSTRING, 0, (LPARAM)szInput);
} while (pszInput);
}
else
{
for ( ; m_iLineCount < ARRAYSIZE(s_wzListBackup); m_iLineCount++)
{
wcsncpy_s(s_rgwzList[m_iLineCount], MAX_STRING, s_wzListBackup[m_iLineCount], wcslen(s_wzListBackup[m_iLineCount]));
}
}
}
void CTipACDialog::OnCommand(HWND hwnd, UINT, WPARAM wParam, LPARAM)
{
int id = LOWORD(wParam);
WCHAR szSelectedText[MAX_STRING] = {0};
switch (id)
{
case IDCANCEL:
// Explicitly destroy the dropdown window so that it releases its reference to this
DestroyWindow(m_hwndDropdown);
EndDialog(hwnd, S_FALSE);
break;
case IDC_LIST1:
{
LRESULT iSel = SendMessage(m_hwndListbox, LB_GETCURSEL, 0, (LPARAM)0);
if (-1 != iSel)
{
SendMessage(m_hwndListbox, LB_GETTEXT, iSel, (LPARAM)szSelectedText);
SetWindowText(m_hwndEdit, szSelectedText);
}
}
break;
case IDC_EDIT1:
if (EN_UPDATE == HIWORD(wParam))
{
if (!m_fSynchronize && m_wzPending[0])
{
m_wzPending[0] = NULL;
}
else // just match what's in the textbox: m_fSynchronize or nothing pending
{
int const cchInput = GetWindowTextLength(m_hwndEdit) + 1;
PWSTR pszInput = new (std::nothrow) WCHAR[cchInput];
if (pszInput)
{
GetWindowText(m_hwndEdit, pszInput, cchInput);
DisplayMatchedOutput(pszInput);
ShowAutoCompleteDropdown(0 < pszInput[0]);
delete [] pszInput;
}
}
}
break;
case IDC_CHECK1:
m_fSynchronize = IsDlgButtonChecked(hwnd, IDC_CHECK1);
SetTipMode();
break;
}
}
void CTipACDialog::DisplayMatchedOutput(WCHAR * pszInput)
{
int iCount = 0;
int i = 0, j = 0;
BOOL fMatch = FALSE;
// Note that this code is NOT being efficient about filling the list or keeping the list:
// Every time this method is called at all (on every change of the edit box!) we destroy
// the contents of the listbox and then regenerate all of it.
// *****
// This is inefficient list management, and should not be used in shipping code.
// *****
// This method is suitable for this sample's purposes, as the list maintenance isn't important;
// we're showing how to allow the TIP to work with an application's specialized autocomplete list(s).
SendMessage(m_hwndListbox, LB_RESETCONTENT, 0, 0);
for (iCount = 0; iCount < m_iLineCount; iCount++)
{
i = 0;
j = 0;
if (NULL == pszInput[0])
{
fMatch = TRUE;
}
else
{
while (*(pszInput + i) != '\0')
{
fMatch = FALSE;
if (toupper(*(pszInput + i)) == toupper(s_rgwzList[iCount][j]))
{
i++;
j++;
fMatch = TRUE;
}
else
{
break;
}
}
}
if (fMatch)
{
SendMessage(m_hwndListbox, LB_ADDSTRING, 0, (LPARAM)s_rgwzList[iCount]);
}
}
}
STDAPI_(int) wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int)
{
g_hInstance = hInstance;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
CTipACDialog *pTipACDialog = new CTipACDialog();
if (pTipACDialog)
{
// Open the dialog
DialogBoxParam(g_hInstance, (LPCWSTR)IDD_MAIN, NULL, CTipACDialog::s_DialogProc, (LPARAM)pTipACDialog);
}
CoUninitialize();
}
return 0;
}