Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1460 lines
40 KiB

#include <ctlspriv.h>
const TCHAR FAR c_szComboBox[] = TEXT("combobox");
const TCHAR FAR c_szComboBoxEx[] = WC_COMBOBOXEX;
#define COMBO_MARGIN 4
#define COMBO_WIDTH g_cxSmIcon
#define COMBO_HEIGHT g_cySmIcon
#define COMBO_BORDER 3
typedef struct {
CONTROLINFO ci;
HWND hwndCombo;
HWND hwndEdit;
DWORD dwExStyle;
HIMAGELIST himl;
HFONT hFont;
int cxIndent;
int cyFull;
int iSel;
BOOL fEditChanged :1;
BOOL fFontCreated :1;
BOOL fInEndEdit :1;
BOOL fInDrop :1;
} COMBOEX, *PCOMBOBOXEX;
typedef struct {
LPTSTR pszText;
int iImage;
int iSelectedImage;
int iOverlay;
int iIndent;
LPARAM lParam;
} CEITEM, *PCEITEM;
void ComboEx_OnSize(PCOMBOBOXEX pce);
HFONT ComboEx_GetFont(PCOMBOBOXEX pce);
BOOL ComboEx_OnGetItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem);
int ComboEx_ComputeItemHeight(PCOMBOBOXEX pce, BOOL);
LRESULT ComboEx_OnFindStringExact(PCOMBOBOXEX pce, int iStart, LPCTSTR lpsz);
int WINAPI ShellEditWordBreakProc(LPTSTR lpch, int ichCurrent, int cch, int code);
LRESULT CALLBACK ComboSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT uIdSubclass, DWORD dwRefData);
LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT uIdSubclass, DWORD dwRefData);
#define ComboEx_Editable(pce) (((pce)->ci.style & CBS_DROPDOWNLIST) == CBS_DROPDOWN)
void ComboEx_DeleteFont(PCOMBOBOXEX pce)
{
if (pce->fFontCreated) {
DeleteObject(ComboEx_GetFont(pce));
}
}
void ComboEx_OnSetFont(PCOMBOBOXEX pce, HFONT hFont, BOOL fRedraw)
{
int iHeight;
ComboEx_DeleteFont(pce);
if (!hFont) {
LOGFONT lf;
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
hFont = CreateFontIndirect(&lf);
pce->fFontCreated = TRUE;
} else {
pce->fFontCreated = FALSE;
}
pce->ci.uiCodePage = GetCodePageForFont(hFont);
SendMessage(pce->hwndCombo, WM_SETFONT, (WPARAM)hFont, fRedraw);
if (pce->hwndEdit)
{
SendMessage(pce->hwndEdit, WM_SETFONT, (WPARAM)hFont, fRedraw);
SendMessage(pce->hwndEdit, EM_SETMARGINS, EC_USEFONTINFO, 0L);
}
iHeight = ComboEx_ComputeItemHeight(pce, FALSE);
SendMessage(pce->hwndCombo, CB_SETITEMHEIGHT, (WPARAM)-1, (LPARAM)iHeight);
SendMessage(pce->hwndCombo, CB_SETITEMHEIGHT, 0, (LPARAM)iHeight);
}
void ComboEx_OnDestroy(PCOMBOBOXEX pce)
{
// don't need do destroy hwndCombo.. it will be destroyed along with us.
SendMessage(pce->hwndCombo, CB_RESETCONTENT, 0, 0);
ComboEx_DeleteFont(pce);
if (pce->hwndEdit)
RemoveWindowSubclass(pce->hwndEdit, EditSubclassProc, 0);
if (pce->hwndCombo)
RemoveWindowSubclass(pce->hwndCombo, ComboSubclassProc, 0);
SetWindowInt(pce->ci.hwnd, 0, (LONG)0);
LocalFree(pce);
}
// this gets the client rect without the scrollbar part and the border
void ComboEx_GetComboClientRect(PCOMBOBOXEX pce, LPRECT lprc)
{
GetClientRect(pce->hwndCombo, lprc);
InflateRect(lprc, -g_cxEdge, -g_cyEdge);
lprc->right -= g_cxScrollbar;
}
// returns the edit box (creating it if necessary) or NULL if the combo does
// not have an edit box
HWND ComboEx_GetEditBox(PCOMBOBOXEX pce)
{
HFONT hfont;
DWORD dwStyle;
if (pce->hwndEdit)
return(pce->hwndEdit);
if (!ComboEx_Editable(pce))
return(NULL);
dwStyle = WS_VISIBLE | WS_CLIPSIBLINGS | WS_CHILD | ES_LEFT;
if (pce->ci.style & CBS_AUTOHSCROLL)
dwStyle |= ES_AUTOHSCROLL;
if (pce->ci.style & CBS_OEMCONVERT)
dwStyle |= ES_OEMCONVERT;
#if 0
if (pce->ci.style & CBS_UPPERCASE)
dwStyle |= ES_UPPERCASE;
if (pce->ci.style & CBS_LOWERCASE)
dwStyle |= ES_LOWERCASE;
#endif
pce->hwndEdit = CreateWindowEx(0, c_szEdit, c_szNULL, dwStyle, 0, 0, 0, 0,
pce->hwndCombo, (HMENU)GetDlgCtrlID(pce->ci.hwnd), HINST_THISDLL, 0);
if (!pce->hwndEdit ||
!SetWindowSubclass(pce->hwndEdit, EditSubclassProc, 0, (DWORD)pce))
{
return NULL;
}
hfont = ComboEx_GetFont(pce);
if (hfont)
FORWARD_WM_SETFONT(pce->hwndEdit, hfont,
FALSE, SendMessage);
return(pce->hwndEdit);
}
///
/// the edit box handling...
/*
we want the edit box up on CBN_SETFOCUS and CBN_CLOSEUP
remove it on CBN_DROPDOWN and on CBN_KILLFOCUS
this assumes that CBN_SETFOCUS and CBN_KILLFOCUS will come before and after
CBN_DROPDOWN and CBN_CLOSEUP respectively
*/
BOOL ComboEx_EndEdit(PCOMBOBOXEX pce, int iWhy)
{
NMCBEENDEDIT nm;
BOOL fRet;
if (!ComboEx_GetEditBox(pce))
return(FALSE);
pce->fInEndEdit = TRUE;
GetWindowText(pce->hwndEdit, nm.szText, ARRAYSIZE(nm.szText));
nm.fChanged = pce->fEditChanged;
nm.iWhy = iWhy;
if (nm.fChanged)
nm.iNewSelection = ComboEx_OnFindStringExact(pce, SendMessage(pce->hwndCombo, CB_GETCURSEL, 0, 0) - 1, nm.szText);
else
nm.iNewSelection = SendMessage(pce->hwndCombo, CB_GETCURSEL, 0, 0);
fRet = CCSendNotify(&pce->ci, CBEN_ENDEDIT, &nm.hdr);
pce->fInEndEdit = FALSE;
if (!fRet) {
pce->fEditChanged = FALSE;
if (nm.iNewSelection != SendMessage(pce->hwndCombo, CB_GETCURSEL, 0, 0))
SendMessage(pce->ci.hwnd, CB_SETCURSEL, nm.iNewSelection, 0);
}
InvalidateRect(pce->hwndCombo, NULL, FALSE);
return(fRet);
}
void ComboEx_SizeEditBox(PCOMBOBOXEX pce)
{
RECT rc;
int cxIcon = 0, cyIcon = 0;
ComboEx_GetComboClientRect(pce, &rc);
InvalidateRect(pce->hwndCombo, &rc, TRUE); // erase so that the selection highlight is erased
if (pce->himl)
{
ImageList_GetIconSize(pce->himl, &cxIcon, &cyIcon);
if (cxIcon)
cxIcon += COMBO_MARGIN;
}
// combobox edit field is one border in from the entire combobox client
// rect -- thus add one border to edit control's left side
rc.left += g_cxBorder + cxIcon;
rc.bottom -= g_cyBorder;
rc.top = rc.bottom - ComboEx_ComputeItemHeight(pce, TRUE) - g_cyBorder;
SetWindowPos(pce->hwndEdit, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW);
}
BOOL ComboEx_GetCurSelText(PCOMBOBOXEX pce, LPTSTR pszText, int cchText)
{
COMBOBOXEXITEM cei;
cei.mask = CBEIF_TEXT;
cei.pszText = pszText;
cei.cchTextMax = cchText;
cei.iItem = SendMessage(pce->hwndCombo, CB_GETCURSEL, 0, 0);
if (cei.iItem == -1 ) {
pszText[0] = 0;
return(FALSE);
} else {
ComboEx_OnGetItem(pce, &cei);
return(TRUE);
}
}
void ComboEx_UpdateEditText(PCOMBOBOXEX pce)
{
if (pce->hwndEdit && !pce->fInEndEdit)
{
TCHAR szText[CBEMAXSTRLEN];
if (ComboEx_GetCurSelText(pce, szText, ARRAYSIZE(szText)))
{
SendMessage(pce->hwndEdit, WM_SETTEXT, 0, (LPARAM)szText);
Edit_SetSel(pce->hwndEdit, 0, 0); // makesure everything is scrolled over first
Edit_SetSel(pce->hwndEdit, 0, -1); // select everything
}
}
}
BOOL ComboEx_BeginEdit(PCOMBOBOXEX pce)
{
if (!ComboEx_GetEditBox(pce))
return(FALSE);
SetFocus(pce->hwndEdit);
return(TRUE);
}
BOOL ComboSubclass_HandleButton(PCOMBOBOXEX pce, WPARAM wParam, LPARAM lParam)
{
if (ComboEx_Editable(pce)) {
RECT rc;
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
ComboEx_GetComboClientRect(pce, &rc);
// a click on our border should start as well
InflateRect(&rc, g_cxEdge, g_cyEdge);
if (PtInRect(&rc, pt))
{
if (!ComboEx_BeginEdit(pce))
SetFocus(pce->hwndCombo);
return TRUE;
}
}
return FALSE;
}
BOOL ComboSubclass_HandleCommand(PCOMBOBOXEX pce, WPARAM wParam, LPARAM lParam)
{
UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
UINT uCmd = GET_WM_COMMAND_CMD(wParam, lParam);
HWND hwnd = GET_WM_COMMAND_HWND(wParam, lParam);
switch (uCmd)
{
case EN_SETFOCUS:
if (!pce->fInDrop)
{
Edit_SetSel(pce->hwndEdit, 0, 0); // makesure everything is scrolled over first
Edit_SetSel(pce->hwndEdit, 0, -1); // select everything
CCSendNotify(&pce->ci, CBEN_BEGINEDIT, NULL);
pce->fEditChanged = FALSE;
}
break;
case EN_KILLFOCUS:
{
HWND hwndFocus;
hwndFocus = GetFocus();
if (hwndFocus != pce->hwndCombo)
{
ComboEx_EndEdit(pce, CBENF_KILLFOCUS);
SendMessage(pce->hwndCombo, WM_KILLFOCUS, (WPARAM)hwndFocus, 0);
}
break;
}
case EN_CHANGE:
{
TCHAR szTextOrig[CBEMAXSTRLEN];
TCHAR szTextNow[CBEMAXSTRLEN];
ComboEx_GetCurSelText(pce, szTextOrig, ARRAYSIZE(szTextOrig));
GetWindowText(pce->hwndEdit, szTextNow, ARRAYSIZE(szTextNow));
pce->fEditChanged = (lstrcmp(szTextOrig, szTextNow) != 0);
SendMessage(pce->ci.hwndParent, WM_COMMAND,
GET_WM_COMMAND_MPS(idCmd, pce->ci.hwnd, CBN_EDITCHANGE));
break;
}
}
return(hwnd == pce->hwndEdit);
}
LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT uIdSubclass, DWORD dwRefData)
{
PCOMBOBOXEX pce = (PCOMBOBOXEX)dwRefData;
switch(uMsg) {
case WM_DESTROY:
RemoveWindowSubclass(hwnd, EditSubclassProc, 0);
break;
case WM_CHAR:
switch ((TCHAR)wParam) {
case TEXT('\n'):
case TEXT('\r'):
// return... don't go to wndproc because
// the edit control beeps on enter
return 0;
}
break;
case WM_KEYDOWN:
switch(wParam) {
case VK_RETURN:
if (!ComboEx_EndEdit(pce, CBENF_RETURN))
// we know we have an edit window, so FALSE return means
// app returned FALSE to CBEN_ENDEDIT notification
ComboEx_BeginEdit(pce);
break;
case VK_ESCAPE:
pce->fEditChanged = FALSE;
if (!ComboEx_EndEdit(pce, CBENF_ESCAPE))
// we know we have an edit window, so FALSE return means
// app returned FALSE to CBEN_ENDEDIT notification
ComboEx_BeginEdit(pce);
break;
// Pass these to the combobox itself to make it work properly...
case VK_HOME:
case VK_END:
if (!pce->fInDrop)
break;
case VK_F4:
case VK_UP:
case VK_DOWN:
case VK_PRIOR:
case VK_NEXT:
if (pce->hwndCombo)
return SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
break;
}
break;
case WM_LBUTTONDOWN:
if (GetFocus() != pce->hwndEdit)
{
SetFocus(pce->hwndEdit);
return(0L); // eat this message
}
break;
case WM_SYSKEYDOWN:
switch(wParam) {
// Pass these to the combobox itself to make it work properly...
case VK_UP:
case VK_DOWN:
if (pce->hwndCombo)
return SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK ComboSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT uIdSubclass, DWORD dwRefData)
{
PCOMBOBOXEX pce = (PCOMBOBOXEX)dwRefData;
switch (uMsg) {
case CB_GETLBTEXT:
case CB_GETLBTEXTLEN:
{
COMBOBOXEXITEM cei;
TCHAR szText[CBEMAXSTRLEN];
cei.mask = CBEIF_TEXT;
cei.pszText = szText;
cei.cchTextMax = ARRAYSIZE(szText);
cei.iItem = wParam;
if (!ComboEx_OnGetItem(pce, &cei))
return CB_ERR;
if (lParam && uMsg == CB_GETLBTEXT)
lstrcpy((LPTSTR)lParam, szText);
return lstrlen(szText);
}
case WM_LBUTTONDOWN:
if (ComboSubclass_HandleButton(pce, wParam, lParam)) {
return 0;
}
break;
case WM_COMMAND:
if (ComboSubclass_HandleCommand(pce, wParam, lParam))
return 0;
break;
case WM_DESTROY:
RemoveWindowSubclass(hwnd, ComboSubclassProc, 0);
break;
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
BOOL ComboEx_OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
PCOMBOBOXEX pce;
DWORD dwStyle;
pce = (PCOMBOBOXEX)LocalAlloc(LPTR, sizeof(COMBOEX));
if (!pce)
return FALSE;
SetWindowInt(hwnd, 0, (LONG)pce);
// BUGBUG: force off borders off ourself
lpcs->style &= ~(WS_BORDER | WS_VSCROLL | WS_HSCROLL | CBS_UPPERCASE | CBS_LOWERCASE);
SetWindowLong(hwnd, GWL_STYLE, lpcs->style);
CIInitialize(&pce->ci, hwnd, lpcs);
// or in CBS_SIMPLE because we can never allow the sub combo box
// to have just drop down.. it's either all simple or dropdownlist
dwStyle = CBS_OWNERDRAWFIXED | CBS_SIMPLE | CBS_NOINTEGRALHEIGHT | WS_VISIBLE |WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
dwStyle |= (lpcs->style & (CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD));
pce->cyFull = lpcs->cy;
pce->hwndCombo = CreateWindowEx(0, c_szComboBox, lpcs->lpszName,
dwStyle,
0, 0, lpcs->cx, lpcs->cy,
hwnd, lpcs->hMenu, lpcs->hInstance, 0);
if (!pce->hwndCombo ||
!SetWindowSubclass(pce->hwndCombo, ComboSubclassProc, 0, (DWORD)pce) ||
(!ComboEx_GetEditBox(pce) && ComboEx_Editable(pce)))
{
ComboEx_OnDestroy(pce);
return FALSE;
}
ComboEx_OnSetFont(pce, NULL, FALSE);
pce->cxIndent = 10;
pce->iSel = -1;
ComboEx_OnSize(pce);
return TRUE;
}
HIMAGELIST ComboEx_OnSetImageList(PCOMBOBOXEX pce, HIMAGELIST himl)
{
int iHeight;
HIMAGELIST himlOld = pce->himl;
pce->himl = himl;
iHeight = ComboEx_ComputeItemHeight(pce, FALSE);
SendMessage(pce->hwndCombo, CB_SETITEMHEIGHT, (WPARAM)-1, iHeight);
SendMessage(pce->hwndCombo, CB_SETITEMHEIGHT, 0, iHeight);
InvalidateRect(pce->hwndCombo, NULL, TRUE);
if (pce->hwndEdit)
ComboEx_SizeEditBox(pce);
return himlOld;
}
void ComboEx_OnDrawItem(PCOMBOBOXEX pce, LPDRAWITEMSTRUCT pdis)
{
HDC hdc = pdis->hDC;
RECT rc = pdis->rcItem;
TCHAR szText[CBEMAXSTRLEN];
int offset = 0;
int xString, yString, xCombo;
int cxIcon = 0, cyIcon = 0;
int iLen;
BOOL fSelected = FALSE;
SIZE sizeText;
COMBOBOXEXITEM cei;
BOOL fNoText = FALSE;
rc.top += g_cyBorder;
if (pdis->itemID != -1)
{
cei.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_OVERLAY | CBEIF_SELECTEDIMAGE| CBEIF_INDENT;
cei.pszText = szText;
cei.cchTextMax = ARRAYSIZE(szText);
cei.iItem = pdis->itemID;
ComboEx_OnGetItem(pce, &cei);
if (pce->iSel == (int)pdis->itemID ||
((pce->iSel == -1) && ((int)pdis->itemID == (int)SendMessage(pce->hwndCombo, CB_GETCURSEL, 0, 0))))
fSelected = TRUE;
}
if (pce->himl)
{
ImageList_GetIconSize(pce->himl, &cxIcon, &cyIcon);
if (cxIcon)
cxIcon += COMBO_MARGIN;
}
// if we're not drawing the edit box, figure out how far to indent
// over
if (!(pdis->itemState & ODS_COMBOBOXEDIT))
{
offset = (pce->cxIndent * cei.iIndent) + COMBO_BORDER;
}
else
{
if (pce->hwndEdit)
fNoText = TRUE;
if (pce->dwExStyle & CBES_EX_NOEDITIMAGEINDENT)
cxIcon = 0;
}
xCombo = rc.left + offset;
rc.left = xString = xCombo + cxIcon;
iLen = lstrlen(szText);
GetTextExtentPoint(hdc, szText, iLen, &sizeText);
rc.right = rc.left + sizeText.cx;
rc.left--;
rc.right++;
if (pdis->itemAction != ODA_FOCUS)
{
int yMid;
BOOL fTextHighlight = FALSE;;
yMid = (rc.top + rc.bottom) / 2;
// center the string within rc
yString = yMid - (sizeText.cy/2);
if (pdis->itemState & ODS_SELECTED) {
if (!(pdis->itemState & ODS_COMBOBOXEDIT) ||
!ComboEx_Editable(pce)) {
fTextHighlight = TRUE;
}
}
SetBkColor(hdc, GetSysColor(fTextHighlight ?
COLOR_HIGHLIGHT : COLOR_WINDOW));
SetTextColor(hdc, GetSysColor(fTextHighlight ?
COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
if ((pdis->itemState & ODS_COMBOBOXEDIT) &&
(rc.right > pdis->rcItem.right))
{
// Need to clip as user does not!
rc.right = pdis->rcItem.right;
}
if (!fNoText) {
ExtTextOut(hdc, xString, yString, ETO_OPAQUE | ETO_CLIPPED, &rc, szText, iLen, NULL);
}
if (pce->himl && (pdis->itemID >= 0) &&
!((pce->dwExStyle & (CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT)) &&
(pdis->itemState & ODS_COMBOBOXEDIT)))
{
ImageList_Draw(pce->himl,
(fSelected) ? cei.iSelectedImage : cei.iImage,
hdc, xCombo, yMid - (cyIcon/2),
INDEXTOOVERLAYMASK(cei.iOverlay) |
((pdis->itemState & ODS_SELECTED) ? (ILD_SELECTED | ILD_FOCUS) : ILD_NORMAL));
}
}
if (pdis->itemAction == ODA_FOCUS ||
(pdis->itemState & ODS_FOCUS))
{
if (!fNoText) {
DrawFocusRect(hdc, &rc);
}
}
}
int ComboEx_ComputeItemHeight(PCOMBOBOXEX pce, BOOL fTextOnly)
{
HDC hdc;
HFONT hfontOld;
int dyDriveItem;
SIZE siz;
hdc = GetDC(NULL);
hfontOld = ComboEx_GetFont(pce);
if (hfontOld)
hfontOld = SelectObject(hdc, hfontOld);
GetTextExtentPoint(hdc, TEXT("W"), 1, &siz);
dyDriveItem = siz.cy;
if (hfontOld)
SelectObject(hdc, hfontOld);
ReleaseDC(NULL, hdc);
if (fTextOnly)
return dyDriveItem;
// now take into account the icon
if (pce->himl) {
int cxIcon = 0, cyIcon = 0;
ImageList_GetIconSize(pce->himl, &cxIcon, &cyIcon);
if (dyDriveItem < cyIcon)
dyDriveItem = cyIcon;
}
dyDriveItem += COMBO_BORDER;
return dyDriveItem;
}
void ComboEx_OnMeasureItem(PCOMBOBOXEX pce, LPMEASUREITEMSTRUCT pmi)
{
pmi->itemHeight = ComboEx_ComputeItemHeight(pce, FALSE);
}
void ComboEx_ISetItem(PCOMBOBOXEX pce, PCEITEM pcei, PCOMBOBOXEXITEM pceItem)
{
if (pceItem->mask & CBEIF_INDENT)
pcei->iIndent = pceItem->iIndent;
if (pceItem->mask & CBEIF_IMAGE)
pcei->iImage = pceItem->iImage;
if (pceItem->mask & CBEIF_SELECTEDIMAGE)
pcei->iSelectedImage = pceItem->iSelectedImage;
if (pceItem->mask & CBEIF_OVERLAY)
pcei->iOverlay = pceItem->iOverlay;
if (pceItem->mask & CBEIF_TEXT) {
if (pcei->pszText == LPSTR_TEXTCALLBACK)
pcei->pszText = NULL;
Str_Set(&pcei->pszText, pceItem->pszText);
}
if (pceItem->mask & CBEIF_LPARAM) {
pcei->lParam = pceItem->lParam;
}
}
#define ComboEx_GetItemPtr(pce, iItem) \
((PCEITEM)SendMessage((pce)->hwndCombo, CB_GETITEMDATA, iItem, 0))
#define ComboEx_Count(pce) \
((int)SendMessage((pce)->hwndCombo, CB_GETCOUNT, 0, 0))
BOOL ComboEx_OnGetItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem)
{
PCEITEM pcei = ComboEx_GetItemPtr(pce, pceItem->iItem);
NMCOMBOBOXEX nm;
if (pcei == (PCEITEM)-1)
return FALSE;
nm.ceItem.mask = 0;
if (pceItem->mask & CBEIF_TEXT) {
if (pcei->pszText == LPSTR_TEXTCALLBACK) {
nm.ceItem.mask |= CBEIF_TEXT;
} else {
Str_GetPtr(pcei->pszText, pceItem->pszText, pceItem->cchTextMax);
}
}
if (pceItem->mask & CBEIF_IMAGE) {
if (pcei->iImage == I_IMAGECALLBACK) {
nm.ceItem.mask |= CBEIF_IMAGE;
}
pceItem->iImage = pcei->iImage;
}
if (pceItem->mask & CBEIF_SELECTEDIMAGE) {
if (pcei->iSelectedImage == I_IMAGECALLBACK) {
nm.ceItem.mask |= CBEIF_SELECTEDIMAGE;
}
pceItem->iSelectedImage = pcei->iSelectedImage;
}
if (pceItem->mask & CBEIF_OVERLAY) {
if (pcei->iOverlay == I_IMAGECALLBACK) {
nm.ceItem.mask |= CBEIF_OVERLAY;
}
pceItem->iOverlay = pcei->iOverlay;
}
if (pceItem->mask & CBEIF_INDENT) {
if (pcei->iIndent == I_INDENTCALLBACK) {
nm.ceItem.mask |= CBEIF_INDENT;
pceItem->iIndent = 0;
} else {
pceItem->iIndent = pcei->iIndent;
}
}
if (pceItem->mask & CBEIF_LPARAM) {
pceItem->lParam = pcei->lParam;
}
// is there anything to call back for?
if (nm.ceItem.mask) {
UINT uMask = nm.ceItem.mask;
nm.ceItem = *pceItem;
nm.ceItem.lParam = pcei->lParam;
nm.ceItem.mask = uMask;
if ((nm.ceItem.mask & CBEIF_TEXT) &&
nm.ceItem.cchTextMax) {
// null terminate just in case they don't respond
*nm.ceItem.pszText = 0;
}
CCSendNotify(&pce->ci, CBEN_GETDISPINFO, &nm.hdr);
if (nm.ceItem.mask & CBEIF_INDENT)
pceItem->iIndent = nm.ceItem.iIndent;
if (nm.ceItem.mask & CBEIF_IMAGE)
pceItem->iImage = nm.ceItem.iImage;
if (nm.ceItem.mask & CBEIF_SELECTEDIMAGE)
pceItem->iSelectedImage = nm.ceItem.iSelectedImage;
if (nm.ceItem.mask & CBEIF_OVERLAY)
pceItem->iOverlay = nm.ceItem.iOverlay;
if (nm.ceItem.mask & CBEIF_TEXT)
pceItem->pszText = nm.ceItem.pszText;
if (nm.ceItem.mask & CBEIF_DI_SETITEM) {
ComboEx_ISetItem(pce, pcei, &nm.ceItem);
}
}
return TRUE;
}
#ifdef UNICODE
BOOL ComboEx_OnGetItemA(PCOMBOBOXEX pce, PCOMBOBOXEXITEMA pceItem)
{
LPWSTR pwszText;
LPSTR pszTextSave;
BOOL fRet;
if (!(pceItem->mask & CBEIF_TEXT)) {
return ComboEx_OnGetItem(pce, (PCOMBOBOXEXITEM)pceItem);
}
pwszText = (LPWSTR)GlobalAlloc(GPTR, (pceItem->cchTextMax+1)*sizeof(WCHAR));
if (!pwszText)
return FALSE;
pszTextSave = pceItem->pszText;
((PCOMBOBOXEXITEM)pceItem)->pszText = pwszText;
fRet = ComboEx_OnGetItem(pce, (PCOMBOBOXEXITEM)pceItem);
pceItem->pszText = pszTextSave;
if (fRet) {
WideCharToMultiByte(CP_ACP, 0, pwszText, -1,
(LPSTR)pszTextSave, pceItem->cchTextMax, NULL, NULL);
}
GlobalFree(pwszText);
return fRet;
}
#endif
BOOL ComboEx_OnSetItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem)
{
PCEITEM pcei = ComboEx_GetItemPtr(pce, pceItem->iItem);
UINT rdwFlags = 0;
if (pcei == (PCEITEM)-1)
return FALSE;
ComboEx_ISetItem(pce, pcei, pceItem);
if (rdwFlags & (CBEIF_INDENT | CBEIF_IMAGE |CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_OVERLAY)) {
rdwFlags = RDW_ERASE | RDW_INVALIDATE;
}
// BUGBUG: do something better..
if (rdwFlags) {
RedrawWindow(pce->hwndCombo, NULL, NULL, rdwFlags);
}
ComboEx_UpdateEditText(pce);
// BUGUBG: notify item changed
return TRUE;
}
void ComboEx_HandleDeleteItem(PCOMBOBOXEX pce, LPDELETEITEMSTRUCT pdis)
{
PCEITEM pcei = (PCEITEM)pdis->itemData;
if (pcei) {
NMCOMBOBOXEX nm;
if (pcei->pszText && pcei->pszText != LPSTR_TEXTCALLBACK) {
Str_Set(&pcei->pszText, NULL);
}
nm.ceItem.iItem = pdis->itemID;
nm.ceItem.mask = CBEIF_LPARAM;
nm.ceItem.lParam = pcei->lParam;
CCSendNotify(&pce->ci, CBEN_DELETEITEM, &nm.hdr);
LocalFree(pcei);
}
}
int ComboEx_OnInsertItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem)
{
int iRet;
PCEITEM pcei = (PCEITEM)LocalAlloc(LPTR, sizeof(CEITEM));
if (!pcei)
return -1;
pcei->iImage = -1;
pcei->iSelectedImage = -1;
//pcei->iOverlay = 0;
//pcei->iIndent = 0;
ComboEx_ISetItem(pce, pcei, pceItem);
iRet = SendMessage(pce->hwndCombo, CB_INSERTSTRING, pceItem->iItem, (LPARAM)pcei);
if (iRet != -1) {
NMCOMBOBOXEX nm;
nm.ceItem = *pceItem;
CCSendNotify(&pce->ci, CBEN_INSERTITEM, &nm.hdr);
}
return iRet;
}
void ComboEx_OnSize(PCOMBOBOXEX pce)
{
if (pce) {
RECT rcWindow, rcClient;
RECT rc;
int cxInner;
GetWindowRect(pce->ci.hwnd, &rcWindow);
GetClientRect(pce->ci.hwnd, &rcClient);
cxInner = RECTWIDTH(rcClient);
if (cxInner)
// don't size the inner combo if width is 0; otherwise, the below
// computation will make the comboEX the height of the inner combo
// top + inner combo dropdown instead of JUST the inner combo top
SetWindowPos(pce->hwndCombo, NULL, 0, 0, cxInner, pce->cyFull,
SWP_NOACTIVATE | (pce->hwndEdit ? SWP_NOREDRAW : 0));
GetWindowRect(pce->hwndCombo, &rc);
SetWindowPos(pce->ci.hwnd, NULL, 0, 0,
RECTWIDTH(rcWindow),
RECTHEIGHT(rc) + (RECTHEIGHT(rcWindow) - RECTHEIGHT(rcClient)),
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
if (pce->hwndEdit)
{
ComboEx_SizeEditBox(pce);
InvalidateRect(pce->hwndCombo, NULL, TRUE);
}
}
}
LRESULT ComboEx_HandleCommand(PCOMBOBOXEX pce, WPARAM wParam, LPARAM lParam)
{
LRESULT lres;
UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
UINT uCmd = GET_WM_COMMAND_CMD(wParam, lParam);
if (!pce)
return 0;
if (uCmd == CBN_SELCHANGE)
// update the edit text before forwarding this notification 'cause in
// a normal combobox, the edit control will have already been updated
// upon receipt of this notification
ComboEx_UpdateEditText(pce);
lres = SendMessage(pce->ci.hwndParent, WM_COMMAND, GET_WM_COMMAND_MPS(idCmd, pce->ci.hwnd, uCmd));
switch (uCmd) {
case CBN_DROPDOWN:
pce->iSel = SendMessage(pce->hwndCombo, CB_GETCURSEL, 0, 0);
ComboEx_EndEdit(pce, CBENF_DROPDOWN);
pce->fInDrop = TRUE;
break;
case CBN_KILLFOCUS:
ComboEx_EndEdit(pce, CBENF_KILLFOCUS);
break;
case CBN_CLOSEUP:
pce->iSel = -1;
ComboEx_BeginEdit(pce);
pce->fInDrop = FALSE;
break;
case CBN_SETFOCUS:
ComboEx_BeginEdit(pce);
break;
}
return lres;
}
LRESULT ComboEx_OnGetItemData(PCOMBOBOXEX pce, int i)
{
PCEITEM pcei = (PCEITEM)SendMessage(pce->hwndCombo, CB_GETITEMDATA, i, 0);
if (pcei == NULL || pcei == (PCEITEM)CB_ERR) {
return CB_ERR;
}
return pcei->lParam;
}
LRESULT ComboEx_OnSetItemData(PCOMBOBOXEX pce, int i, DWORD lParam)
{
PCEITEM pcei = (PCEITEM)SendMessage(pce->hwndCombo, CB_GETITEMDATA, i, 0);
if (pcei == NULL || pcei == (PCEITEM)CB_ERR) {
return CB_ERR;
}
pcei->lParam = (LPARAM)lParam;
return 0;
}
LRESULT ComboEx_OnFindStringExact(PCOMBOBOXEX pce, int iStart, LPCTSTR lpsz)
{
int i;
int iMax = ComboEx_Count(pce);
TCHAR szText[CBEMAXSTRLEN];
COMBOBOXEXITEM cei;
if (iStart < 0)
iStart = -1;
cei.mask = CBEIF_TEXT;
cei.pszText = szText;
cei.cchTextMax = ARRAYSIZE(szText);
for (i = iStart + 1 ; i < iMax; i++) {
cei.iItem = i;
if (ComboEx_OnGetItem(pce, &cei)) {
if (!lstrcmpi(lpsz, szText)) {
return i;
}
}
}
for (i = 0; i <= iStart; i++) {
cei.iItem = i;
if (ComboEx_OnGetItem(pce, &cei)) {
if (!lstrcmpi(lpsz, szText)) {
return i;
}
}
}
return CB_ERR;
}
DWORD ComboEx_OnSetExStyle(PCOMBOBOXEX pce, DWORD dwExStyle)
{
DWORD dwRet = pce->dwExStyle;
DWORD dwChange = (pce->dwExStyle ^ dwExStyle);
pce->dwExStyle = dwExStyle;
if (dwChange & (CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT)) {
InvalidateRect(pce->ci.hwnd, NULL, TRUE);
if (pce->hwndEdit)
{
ComboEx_SizeEditBox(pce);
InvalidateRect(pce->hwndEdit, NULL, TRUE);
}
}
if (dwChange & CBES_EX_PATHWORDBREAKPROC)
SetPathWordBreakProc(pce->hwndEdit, (pce->dwExStyle & CBES_EX_PATHWORDBREAKPROC));
return dwRet;
}
HFONT ComboEx_GetFont(PCOMBOBOXEX pce)
{
if (pce->hwndCombo)
return (HFONT)SendMessage(pce->hwndCombo, WM_GETFONT, 0, 0);
return NULL;
}
LRESULT CALLBACK ComboExWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lres = 0;
PCOMBOBOXEX pce = (PCOMBOBOXEX)GetWindowInt(hwnd, 0);
switch (uMsg) {
HANDLE_MSG(pce, WM_SETFONT, ComboEx_OnSetFont);
case WM_ENABLE:
if (pce->hwndCombo)
EnableWindow(pce->hwndCombo, (BOOL) wParam);
if (pce->hwndEdit)
EnableWindow(pce->hwndEdit, (BOOL) wParam);
break;
case WM_WININICHANGE:
InitGlobalMetrics(wParam);
if (pce)
ComboEx_OnSetFont(pce, NULL, TRUE);
break;
case WM_SYSCOLORCHANGE:
InitGlobalColors();
break;
case WM_NCCREATE:
// strip off the scroll bits
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~(WS_BORDER | WS_VSCROLL | WS_HSCROLL));
goto DoDefault;
case WM_CREATE:
return ComboEx_OnCreate(hwnd, (LPCREATESTRUCT)lParam);
case WM_DESTROY:
ComboEx_OnDestroy(pce);
break;
#if 0
case WM_WINDOWPOSCHANGING:
if (pce)
{
LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
RECT rc;
HWND hwndParent;
if (!(lpwp->flags & SWP_NOMOVE) || !(lpwp->flags & SWP_NOSIZE))
{
GetWindowRect(hwnd, &rc);
/*
if (!(lpwp->flags & SWP_NOSIZE))
{
if (((rc.right - rc.left) != lpwp->cx) ||
((rc.bottom - rc.top) != lpwp->cy))
{
lpwp->flags |= SWP_NOREDRAW;
goto DoDefault;
}
}
*/
if (!(lpwp->flags & SWP_NOMOVE) && (hwndParent = GetParent(hwnd)))
{
MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rc, 1);
if ((rc.left != lpwp->x) || (rc.top != lpwp->y))
lpwp->flags |= SWP_NOREDRAW;
}
}
}
goto DoDefault;
#endif
case WM_SIZE:
ComboEx_OnSize(pce);
break;
case WM_DRAWITEM:
ComboEx_OnDrawItem(pce, (LPDRAWITEMSTRUCT)lParam);
break;
case WM_MEASUREITEM:
ComboEx_OnMeasureItem(pce, (LPMEASUREITEMSTRUCT)lParam);
break;
case WM_COMMAND:
return ComboEx_HandleCommand(pce, wParam, lParam);
case WM_GETFONT:
return (LRESULT)ComboEx_GetFont(pce);
case WM_SETFOCUS:
if (pce && pce->hwndCombo)
SetFocus(pce->hwndCombo);
break;
case WM_DELETEITEM:
ComboEx_HandleDeleteItem(pce, (LPDELETEITEMSTRUCT)lParam);
return TRUE;
case CBEM_SETEXSTYLE:
return ComboEx_OnSetExStyle(pce, wParam);
case CBEM_GETEXSTYLE:
return pce->dwExStyle;
case CBEM_GETCOMBOCONTROL:
return (LRESULT)pce->hwndCombo;
case CBEM_SETIMAGELIST:
return (LRESULT)ComboEx_OnSetImageList(pce, (HIMAGELIST)lParam);
case CBEM_GETIMAGELIST:
return (LRESULT)pce->himl;
#ifdef UNICODE
case CBEM_GETITEMA:
return ComboEx_OnGetItemA(pce, (PCOMBOBOXEXITEMA)lParam);
#endif
case CBEM_GETITEM:
return ComboEx_OnGetItem(pce, (PCOMBOBOXEXITEM)lParam);
#ifdef UNICODE
case CBEM_SETITEMA: {
LRESULT lResult;
LPWSTR lpStrings;
UINT uiCount;
LPSTR lpAnsiString = (LPSTR) ((PCOMBOBOXEXITEM)lParam)->pszText;
if ((((PCOMBOBOXEXITEM)lParam)->mask & CBEIF_TEXT) &&
(((PCOMBOBOXEXITEM)lParam)->pszText != LPSTR_TEXTCALLBACK)) {
uiCount = lstrlenA(lpAnsiString)+1;
lpStrings = GlobalAlloc (GPTR, (uiCount) * sizeof(TCHAR));
if (!lpStrings)
return -1;
MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lpAnsiString, uiCount,
lpStrings, uiCount);
((PCOMBOBOXEXITEMA)lParam)->pszText = (LPSTR)lpStrings;
lResult = ComboEx_OnSetItem(pce, (PCOMBOBOXEXITEM)lParam);
((PCOMBOBOXEXITEMA)lParam)->pszText = lpAnsiString;
GlobalFree(lpStrings);
return lResult;
} else {
return ComboEx_OnSetItem(pce, (PCOMBOBOXEXITEM)lParam);
}
}
#endif
case CBEM_SETITEM:
return ComboEx_OnSetItem(pce, (PCOMBOBOXEXITEM)lParam);
#ifdef UNICODE
case CBEM_INSERTITEMA: {
LRESULT lResult;
LPWSTR lpStrings;
UINT uiCount;
LPSTR lpAnsiString = (LPSTR) ((PCOMBOBOXEXITEM)lParam)->pszText;
if (!lpAnsiString)
return ComboEx_OnInsertItem(pce, (PCOMBOBOXEXITEM)lParam);
uiCount = lstrlenA(lpAnsiString)+1;
lpStrings = GlobalAlloc (GPTR, (uiCount) * sizeof(TCHAR));
if (!lpStrings)
return -1;
MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lpAnsiString, uiCount,
lpStrings, uiCount);
((PCOMBOBOXEXITEMA)lParam)->pszText = (LPSTR)lpStrings;
lResult = ComboEx_OnInsertItem(pce, (PCOMBOBOXEXITEM)lParam);
((PCOMBOBOXEXITEMA)lParam)->pszText = lpAnsiString;
GlobalFree(lpStrings);
return lResult;
}
#endif
case CBEM_INSERTITEM:
return ComboEx_OnInsertItem(pce, (PCOMBOBOXEXITEM)lParam);
case CBEM_GETEDITCONTROL:
return (LRESULT)pce->hwndEdit;
case CBEM_HASEDITCHANGED:
return pce->fEditChanged;
case CB_GETITEMDATA:
return ComboEx_OnGetItemData(pce, wParam);
case CB_SETITEMDATA:
return ComboEx_OnSetItemData(pce, wParam, lParam);
case CB_LIMITTEXT:
if (ComboEx_GetEditBox(pce))
Edit_LimitText(pce->hwndEdit, wParam);
break;
case CB_FINDSTRINGEXACT:
return ComboEx_OnFindStringExact(pce, wParam, (LPCTSTR)lParam);
case CB_SETITEMHEIGHT:
case CB_INSERTSTRING:
case CB_ADDSTRING:
case CB_SETEDITSEL:
case CB_FINDSTRING:
case CB_DIR:
// override to do nothing
break;
case CB_SETCURSEL:
case CB_RESETCONTENT:
case CB_DELETESTRING:
lres = SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
ComboEx_UpdateEditText(pce);
break;
case WM_SETTEXT:
if (!pce->hwndEdit)
return(CB_ERR);
lres = SendMessage(pce->hwndEdit, uMsg, wParam, lParam);
RedrawWindow(pce->hwndCombo, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
return(lres);
case WM_CUT:
case WM_COPY:
case WM_PASTE:
case WM_GETTEXT:
if (!pce->hwndEdit)
return(CB_ERR);
return(SendMessage(pce->hwndEdit, uMsg, wParam, lParam));
case WM_SETREDRAW:
if (pce->hwndEdit)
SendMessage(pce->hwndEdit, uMsg, wParam, lParam);
break;
// Handle it being in a dialog...
// BUGBUG:: May want to handle it differently when edit control has
// focus...
case WM_GETDLGCODE:
case CB_SHOWDROPDOWN:
case CB_SETEXTENDEDUI:
case CB_GETEXTENDEDUI:
case CB_GETDROPPEDSTATE:
case CB_GETDROPPEDCONTROLRECT:
case CB_GETCURSEL:
case CB_GETCOUNT:
case CB_SELECTSTRING:
case CB_GETLBTEXT:
case CB_GETLBTEXTLEN:
case CB_GETITEMHEIGHT:
case CB_SETDROPPEDWIDTH:
return SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
DoDefault:
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return lres;
}
BOOL InitComboExClass(HINSTANCE hinst)
{
WNDCLASS wc;
if (!GetClassInfo(hinst, c_szComboBoxEx, &wc)) {
wc.lpfnWndProc = ComboExWndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = NULL;
wc.lpszMenuName = NULL;
wc.hInstance = hinst;
wc.lpszClassName = c_szComboBoxEx;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
wc.style = CS_GLOBALCLASS;
wc.cbWndExtra = sizeof(PCOMBOBOXEX);
wc.cbClsExtra = 0;
return RegisterClass(&wc);
}
return TRUE;
}
//---------------------------------------------------------------------------
// SetPathWordBreakProc does special break processing for edit controls.
//
// The word break proc is called when ctrl-(left or right) arrow is pressed in the
// edit control. Normal processing provided by USER breaks words at spaces or tabs,
// but for us it would be nice to break words at slashes, backslashes, & periods too
// since it may be common to have paths or url's typed in.
void WINAPI SetPathWordBreakProc(HWND hwndEdit, BOOL fSet)
{
#ifndef WINNT
// There is a bug with how USER handles WH_CALLWNDPROC global hooks in Win95 that
// causes us to blow up if one is installed and a wordbreakproc is set. Thus,
// if an app is running that has one of these hooks installed (intellipoint 1.1 etc.) then
// if we install our wordbreakproc the app will fault when the proc is called. There
// does not appear to be any way for us to work around it since USER's thunking code
// trashes the stack so this API is disabled for Win95.
return;
#else
FARPROC lpfnOld;
// Don't shaft folks who set their own break proc - leave it alone.
lpfnOld = (FARPROC)SendMessage(hwndEdit, EM_GETWORDBREAKPROC, 0, 0L);
if (fSet) {
if (!lpfnOld)
SendMessage(hwndEdit, EM_SETWORDBREAKPROC, 0, (LPARAM)ShellEditWordBreakProc);
} else {
if (lpfnOld == (FARPROC)ShellEditWordBreakProc)
SendMessage(hwndEdit, EM_SETWORDBREAKPROC, 0, 0L);
}
#endif
}
#ifdef WINNT
BOOL IsDelimiter(TCHAR ch)
{
return (ch == TEXT(' ') ||
ch == TEXT('\t') ||
ch == TEXT('.') ||
ch == TEXT('/') ||
ch == TEXT('\\'));
}
int WINAPI ShellEditWordBreakProc(LPTSTR lpch, int ichCurrent, int cch, int code)
{
LPTSTR lpchT = lpch + ichCurrent;
int iIndex;
BOOL fFoundNonDelimiter = FALSE;
static BOOL fRight = FALSE; // hack due to bug in USER
switch (code) {
case WB_ISDELIMITER:
fRight = TRUE;
// Simple case - is the current character a delimiter?
iIndex = (int)IsDelimiter(*lpchT);
break;
case WB_LEFT:
// Move to the left to find the first delimiter. If we are
// currently at a delimiter, then skip delimiters until we
// find the first non-delimiter, then start from there.
//
// Special case for fRight - if we are currently at a delimiter
// then just return the current word!
while ((lpchT = CharPrev(lpch, lpchT)) != lpch) {
if (IsDelimiter(*lpchT)) {
if (fRight || fFoundNonDelimiter)
break;
} else {
fFoundNonDelimiter = TRUE;
fRight = FALSE;
}
}
iIndex = lpchT - lpch;
// We are currently pointing at the delimiter, next character
// is the beginning of the next word.
if (iIndex > 0 && iIndex < cch)
iIndex++;
break;
case WB_RIGHT:
fRight = FALSE;
// If we are not at a delimiter, then skip to the right until
// we find the first delimiter. If we started at a delimiter, or
// we have just finished scanning to the first delimiter, then
// skip all delimiters until we find the first non delimiter.
//
// Careful - the string passed in to us may not be NULL terminated!
fFoundNonDelimiter = !IsDelimiter(*lpchT);
if (lpchT != (lpch + cch)) {
while ((lpchT = CharNext(lpchT)) != (lpch + cch)) {
if (IsDelimiter(*lpchT)) {
fFoundNonDelimiter = FALSE;
} else {
if (!fFoundNonDelimiter)
break;
}
}
}
// We are currently pointing at the next word.
iIndex = lpchT - lpch;
break;
}
return iIndex;
}
#endif