Source code of Windows XP (NT5)
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.

1824 lines
51 KiB

  1. #include "ctlspriv.h"
  2. #ifdef UNIX
  3. #define EDIT_SELECTALL( hwnd )
  4. #define GetTextExtentPoint GetTextExtentPoint32
  5. #else
  6. #define EDIT_SELECTALL( hwnd ) Edit_SetSel(hwnd, 0, 0); \
  7. Edit_SetSel(hwnd, 0, -1);
  8. #endif
  9. const TCHAR FAR c_szComboBox[] = TEXT("combobox");
  10. const TCHAR FAR c_szComboBoxEx[] = WC_COMBOBOXEX;
  11. #define COMBO_MARGIN 4
  12. #define COMBO_WIDTH g_cxSmIcon
  13. #define COMBO_HEIGHT g_cySmIcon
  14. #define COMBO_BORDER 3
  15. typedef struct {
  16. LPTSTR pszText;
  17. int iImage;
  18. int iSelectedImage;
  19. int iOverlay;
  20. int iIndent;
  21. LPARAM lParam;
  22. } CEITEM, *PCEITEM;
  23. typedef struct {
  24. CONTROLINFO ci;
  25. HWND hwndCombo;
  26. HWND hwndEdit;
  27. DWORD dwExStyle;
  28. HIMAGELIST himl;
  29. HFONT hFont;
  30. int cxIndent;
  31. WPARAM iSel;
  32. CEITEM cei;
  33. BOOL fEditItemSet :1;
  34. BOOL fEditChanged :1;
  35. BOOL fFontCreated :1;
  36. BOOL fInEndEdit :1;
  37. BOOL fInDrop :1;
  38. } COMBOEX, *PCOMBOBOXEX;
  39. void ComboEx_OnWindowPosChanging(PCOMBOBOXEX pce, LPWINDOWPOS pwp);
  40. HFONT ComboEx_GetFont(PCOMBOBOXEX pce);
  41. BOOL ComboEx_OnGetItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem);
  42. int ComboEx_ComputeItemHeight(PCOMBOBOXEX pce, BOOL);
  43. int ComboEx_OnFindStringExact(PCOMBOBOXEX pce, int iStart, LPCTSTR lpsz);
  44. int WINAPI ShellEditWordBreakProc(LPTSTR lpch, int ichCurrent, int cch, int code);
  45. LRESULT CALLBACK ComboSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  46. LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
  47. LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  48. LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
  49. int ComboEx_StrCmp(PCOMBOBOXEX pce, LPCTSTR psz1, LPCTSTR psz2);
  50. #define ComboEx_Editable(pce) (((pce)->ci.style & CBS_DROPDOWNLIST) == CBS_DROPDOWN)
  51. void ComboEx_OnSetFont(PCOMBOBOXEX pce, HFONT hFont, BOOL fRedraw)
  52. {
  53. int iHeight;
  54. HFONT hfontOld = NULL;
  55. if (pce->fFontCreated)
  56. hfontOld = ComboEx_GetFont(pce);
  57. if (!hFont) {
  58. LOGFONT lf;
  59. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
  60. hFont = CreateFontIndirect(&lf);
  61. pce->fFontCreated = TRUE;
  62. } else {
  63. pce->fFontCreated = FALSE;
  64. }
  65. pce->ci.uiCodePage = GetCodePageForFont(hFont);
  66. SendMessage(pce->hwndCombo, WM_SETFONT, (WPARAM)hFont, fRedraw);
  67. if (pce->hwndEdit)
  68. {
  69. SendMessage(pce->hwndEdit, WM_SETFONT, (WPARAM)hFont, fRedraw);
  70. SendMessage(pce->hwndEdit, EM_SETMARGINS, EC_USEFONTINFO, 0L);
  71. }
  72. iHeight = ComboEx_ComputeItemHeight(pce, FALSE);
  73. SendMessage(pce->ci.hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, (LPARAM)iHeight);
  74. SendMessage(pce->hwndCombo, CB_SETITEMHEIGHT, 0, (LPARAM)iHeight);
  75. // do this last so that we don't have a nuked font as we try to create the new one
  76. if (hfontOld)
  77. DeleteObject(hfontOld);
  78. }
  79. void ComboEx_OnDestroy(PCOMBOBOXEX pce)
  80. {
  81. // don't need do destroy hwndCombo.. it will be destroyed along with us.
  82. SendMessage(pce->hwndCombo, CB_RESETCONTENT, 0, 0);
  83. // we may still have string allocated for the item in the edit box so free it
  84. if (pce->cei.pszText)
  85. Str_Set(&(pce->cei.pszText), NULL);
  86. if (pce->fFontCreated) {
  87. DeleteObject(ComboEx_GetFont(pce));
  88. }
  89. if (pce->hwndEdit)
  90. RemoveWindowSubclass(pce->hwndEdit, EditSubclassProc, 0);
  91. if (pce->hwndCombo)
  92. RemoveWindowSubclass(pce->hwndCombo, ComboSubclassProc, 0);
  93. SetWindowPtr(pce->ci.hwnd, 0, 0);
  94. LocalFree(pce);
  95. }
  96. // this gets the client rect without the scrollbar part and the border
  97. void ComboEx_GetComboClientRect(PCOMBOBOXEX pce, LPRECT lprc)
  98. {
  99. GetClientRect(pce->hwndCombo, lprc);
  100. InflateRect(lprc, -g_cxEdge, -g_cyEdge);
  101. lprc->right -= g_cxScrollbar;
  102. }
  103. // returns the edit box (creating it if necessary) or NULL if the combo does
  104. // not have an edit box
  105. HWND ComboEx_GetEditBox(PCOMBOBOXEX pce)
  106. {
  107. HFONT hfont;
  108. DWORD dwStyle;
  109. DWORD dwExStyle = 0;
  110. if (pce->hwndEdit)
  111. return(pce->hwndEdit);
  112. if (!ComboEx_Editable(pce))
  113. return(NULL);
  114. dwStyle = WS_VISIBLE | WS_CLIPSIBLINGS | WS_CHILD | ES_LEFT;
  115. if (pce->ci.style & CBS_AUTOHSCROLL)
  116. dwStyle |= ES_AUTOHSCROLL;
  117. if (pce->ci.style & CBS_OEMCONVERT)
  118. dwStyle |= ES_OEMCONVERT;
  119. #if 0
  120. if (pce->ci.style & CBS_UPPERCASE)
  121. dwStyle |= ES_UPPERCASE;
  122. if (pce->ci.style & CBS_LOWERCASE)
  123. dwStyle |= ES_LOWERCASE;
  124. #endif
  125. dwExStyle = pce->ci.dwExStyle & (WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
  126. pce->hwndEdit = CreateWindowEx(dwExStyle, c_szEdit, c_szNULL, dwStyle, 0, 0, 0, 0,
  127. pce->hwndCombo, IntToPtr_(HMENU, GetDlgCtrlID(pce->ci.hwnd)), HINST_THISDLL, 0);
  128. if (!pce->hwndEdit ||
  129. !SetWindowSubclass(pce->hwndEdit, EditSubclassProc, 0, (DWORD_PTR)pce))
  130. {
  131. return NULL;
  132. }
  133. hfont = ComboEx_GetFont(pce);
  134. if (hfont)
  135. FORWARD_WM_SETFONT(pce->hwndEdit, hfont,
  136. FALSE, SendMessage);
  137. return(pce->hwndEdit);
  138. }
  139. ///
  140. /// the edit box handling...
  141. /*
  142. we want the edit box up on CBN_SETFOCUS and CBN_CLOSEUP
  143. remove it on CBN_DROPDOWN and on CBN_KILLFOCUS
  144. this assumes that CBN_SETFOCUS and CBN_KILLFOCUS will come before and after
  145. CBN_DROPDOWN and CBN_CLOSEUP respectively
  146. */
  147. // Really a BOOL return value
  148. LRESULT ComboEx_EndEdit(PCOMBOBOXEX pce, int iWhy)
  149. {
  150. NMCBEENDEDIT nm;
  151. LRESULT fRet;
  152. if (!ComboEx_GetEditBox(pce))
  153. return(FALSE);
  154. pce->fInEndEdit = TRUE;
  155. GetWindowText(pce->hwndEdit, nm.szText, ARRAYSIZE(nm.szText));
  156. nm.fChanged = pce->fEditChanged;
  157. nm.iWhy = iWhy;
  158. nm.iNewSelection = ComboEx_OnFindStringExact(pce, ComboBox_GetCurSel(pce->hwndCombo) - 1, nm.szText);
  159. fRet = BOOLFROMPTR(CCSendNotify(&pce->ci, CBEN_ENDEDIT, &nm.hdr));
  160. pce->fInEndEdit = FALSE;
  161. if (!fRet)
  162. {
  163. if (nm.iNewSelection != ComboBox_GetCurSel(pce->hwndCombo))
  164. {
  165. if (nm.iNewSelection != -1)
  166. {
  167. SendMessage(pce->ci.hwnd, CB_SETCURSEL, nm.iNewSelection, 0);
  168. }
  169. else
  170. {
  171. //if the selection is -1 and if we do a CB_SETCURSEL on comboboxex then it nukes the text in
  172. //the edit window. Which is not the desired behavior. We need to update the Current Selection in the
  173. //child combobox but leave the text as it is.
  174. SendMessage(pce->hwndCombo, CB_SETCURSEL, nm.iNewSelection,0);
  175. }
  176. }
  177. pce->fEditChanged = FALSE;
  178. }
  179. InvalidateRect(pce->hwndCombo, NULL, FALSE);
  180. return(fRet);
  181. }
  182. void ComboEx_SizeEditBox(PCOMBOBOXEX pce)
  183. {
  184. RECT rc;
  185. int cxIcon = 0, cyIcon = 0;
  186. ComboEx_GetComboClientRect(pce, &rc);
  187. InvalidateRect(pce->hwndCombo, &rc, TRUE); // erase so that the selection highlight is erased
  188. if (pce->himl && !(pce->dwExStyle & CBES_EX_NOEDITIMAGEINDENT))
  189. {
  190. // Make room for icons.
  191. ImageList_GetIconSize(pce->himl, &cxIcon, &cyIcon);
  192. if (cxIcon)
  193. cxIcon += COMBO_MARGIN;
  194. }
  195. // combobox edit field is one border in from the entire combobox client
  196. // rect -- thus add one border to edit control's left side
  197. rc.left += g_cxBorder + cxIcon;
  198. rc.bottom -= g_cyBorder;
  199. rc.top = rc.bottom - ComboEx_ComputeItemHeight(pce, TRUE) - g_cyBorder;
  200. SetWindowPos(pce->hwndEdit, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
  201. SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW);
  202. }
  203. BOOL ComboEx_GetCurSelText(PCOMBOBOXEX pce, LPTSTR pszText, int cchText)
  204. {
  205. COMBOBOXEXITEM cei;
  206. BOOL bRet = TRUE;
  207. cei.mask = CBEIF_TEXT;
  208. cei.pszText = pszText;
  209. cei.cchTextMax = cchText;
  210. cei.iItem = ComboBox_GetCurSel(pce->hwndCombo);
  211. if (cei.iItem == -1 )
  212. {
  213. pszText[0] = 0;
  214. bRet = FALSE;
  215. }
  216. else
  217. {
  218. ComboEx_OnGetItem(pce, &cei);
  219. }
  220. return bRet;
  221. }
  222. void ComboEx_UpdateEditText(PCOMBOBOXEX pce, BOOL fClearOnNoSel)
  223. {
  224. if (!pce->fInEndEdit)
  225. {
  226. TCHAR szText[CBEMAXSTRLEN];
  227. HWND hwndEdit = ComboEx_Editable(pce) ? pce->hwndEdit : pce->hwndCombo;
  228. if (ComboEx_GetCurSelText(pce, szText, ARRAYSIZE(szText)) || fClearOnNoSel) {
  229. SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)szText);
  230. EDIT_SELECTALL( hwndEdit );
  231. }
  232. }
  233. }
  234. BOOL ComboEx_BeginEdit(PCOMBOBOXEX pce)
  235. {
  236. if (!ComboEx_GetEditBox(pce))
  237. return(FALSE);
  238. SetFocus(pce->hwndEdit);
  239. return(TRUE);
  240. }
  241. BOOL ComboSubclass_HandleButton(PCOMBOBOXEX pce, WPARAM wParam, LPARAM lParam)
  242. {
  243. RECT rc;
  244. #ifndef UNIX
  245. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
  246. #else
  247. POINT pt;
  248. pt.x = GET_X_LPARAM(lParam);
  249. pt.y = GET_Y_LPARAM(lParam);
  250. #endif
  251. ComboEx_GetComboClientRect(pce, &rc);
  252. InflateRect(&rc, g_cxEdge, g_cyEdge);
  253. if (PtInRect(&rc, pt)) {
  254. //
  255. // CheckForDragBegin yields, so we must revalidate on the way back.
  256. //
  257. HWND hwndCombo = pce->hwndCombo;
  258. if (CheckForDragBegin(pce->hwndCombo, LOWORD(lParam), HIWORD(lParam)))
  259. {
  260. NMCBEDRAGBEGIN nmcbebd;
  261. LRESULT fRet;
  262. nmcbebd.iItemid = -1;
  263. GetWindowText(pce->hwndEdit, nmcbebd.szText, ARRAYSIZE(nmcbebd.szText));
  264. // BUGBUG - raymondc - why do we ignore the return code?
  265. fRet = CCSendNotify(&pce->ci, CBEN_DRAGBEGIN, &nmcbebd.hdr);
  266. return TRUE;
  267. }
  268. // CheckForDragBegin yields, so revalidate before continuing
  269. else if (IsWindow(hwndCombo)) {
  270. // a click on our border should start edit mode as well
  271. if (ComboEx_Editable(pce)) {
  272. if (!ComboEx_BeginEdit(pce))
  273. SetFocus(pce->hwndCombo);
  274. return TRUE;
  275. }
  276. return FALSE;
  277. }
  278. }
  279. return FALSE;
  280. }
  281. BOOL ComboSubclass_HandleCommand(PCOMBOBOXEX pce, WPARAM wParam, LPARAM lParam)
  282. {
  283. UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
  284. UINT uCmd = GET_WM_COMMAND_CMD(wParam, lParam);
  285. HWND hwnd = GET_WM_COMMAND_HWND(wParam, lParam);
  286. switch (uCmd)
  287. {
  288. case EN_SETFOCUS:
  289. if (!pce->fInDrop)
  290. {
  291. EDIT_SELECTALL( pce->hwndEdit );
  292. CCSendNotify(&pce->ci, CBEN_BEGINEDIT, NULL);
  293. pce->fEditChanged = FALSE;
  294. }
  295. break;
  296. case EN_KILLFOCUS:
  297. {
  298. HWND hwndFocus;
  299. hwndFocus = GetFocus();
  300. if (hwndFocus != pce->hwndCombo)
  301. {
  302. ComboEx_EndEdit(pce, CBENF_KILLFOCUS);
  303. SendMessage(pce->hwndCombo, WM_KILLFOCUS, (WPARAM)hwndFocus, 0);
  304. }
  305. break;
  306. }
  307. case EN_CHANGE:
  308. {
  309. TCHAR szTextOrig[CBEMAXSTRLEN];
  310. TCHAR szTextNow[CBEMAXSTRLEN];
  311. WPARAM iItem;
  312. iItem = ComboBox_GetCurSel(pce->hwndCombo);
  313. if(iItem == -1)
  314. {
  315. if (pce->fEditItemSet && pce->cei.pszText)
  316. {
  317. Str_GetPtr(pce->cei.pszText, szTextOrig, ARRAYSIZE(szTextOrig));
  318. }
  319. else
  320. {
  321. szTextOrig[0] = TEXT('\0');
  322. }
  323. }
  324. else
  325. {
  326. ComboEx_GetCurSelText(pce,szTextOrig, ARRAYSIZE(szTextOrig));
  327. }
  328. #ifndef UNIX
  329. GetWindowText(pce->hwndEdit, szTextNow, ARRAYSIZE(szTextNow));
  330. #else
  331. GetWindowText(pce->hwndEdit, szTextNow, ARRAYSIZE(szTextNow)-1);
  332. #endif
  333. pce->fEditChanged = (ComboEx_StrCmp(pce, szTextOrig, szTextNow) != 0);
  334. SendMessage(pce->ci.hwndParent, WM_COMMAND,
  335. GET_WM_COMMAND_MPS(idCmd, pce->ci.hwnd, CBN_EDITCHANGE));
  336. break;
  337. }
  338. }
  339. return(hwnd == pce->hwndEdit);
  340. }
  341. void EraseWindow(HWND hwnd, HDC hdc, COLORREF clr)
  342. {
  343. RECT rc;
  344. GetClientRect(hwnd, &rc);
  345. FillRectClr(hdc, &rc, clr);
  346. }
  347. LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  348. LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  349. {
  350. PCOMBOBOXEX pce = (PCOMBOBOXEX)dwRefData;
  351. if (uMsg == WM_SETFONT ||
  352. uMsg == WM_WININICHANGE) {
  353. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  354. }
  355. switch(uMsg) {
  356. case WM_ERASEBKGND:
  357. EraseWindow(hwnd, (HDC)wParam, GetSysColor(COLOR_WINDOW));
  358. break;
  359. case WM_DESTROY:
  360. RemoveWindowSubclass(hwnd, EditSubclassProc, 0);
  361. break;
  362. case WM_CHAR:
  363. switch ((TCHAR)wParam) {
  364. case TEXT('\n'):
  365. case TEXT('\r'):
  366. // return... don't go to wndproc because
  367. // the edit control beeps on enter
  368. return 0;
  369. }
  370. break;
  371. case WM_SIZE:
  372. if (GetFocus() != hwnd) {
  373. Edit_SetSel(pce->hwndEdit, 0, 0); // makesure everything is scrolled over first
  374. }
  375. break;
  376. case WM_KEYDOWN:
  377. switch(wParam) {
  378. case VK_RETURN:
  379. if (!ComboEx_EndEdit(pce, CBENF_RETURN))
  380. // we know we have an edit window, so FALSE return means
  381. // app returned FALSE to CBEN_ENDEDIT notification
  382. ComboEx_BeginEdit(pce);
  383. break;
  384. case VK_ESCAPE:
  385. pce->fEditChanged = FALSE;
  386. if (!ComboEx_EndEdit(pce, CBENF_ESCAPE)) {
  387. if(pce->fEditItemSet) {
  388. if(pce->cei.pszText) {
  389. SendMessage(pce->hwndEdit, WM_SETTEXT, (WPARAM)0, (LPARAM)pce->cei.pszText);
  390. EDIT_SELECTALL( pce->hwndEdit );
  391. }
  392. RedrawWindow(pce->hwndCombo, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  393. }else {
  394. ComboEx_BeginEdit(pce);
  395. }
  396. }
  397. break;
  398. // Pass these to the combobox itself to make it work properly...
  399. case VK_HOME:
  400. case VK_END:
  401. if (!pce->fInDrop)
  402. break;
  403. case VK_F4:
  404. case VK_UP:
  405. case VK_DOWN:
  406. case VK_PRIOR:
  407. case VK_NEXT:
  408. if (pce->hwndCombo)
  409. return SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
  410. break;
  411. }
  412. break;
  413. case WM_LBUTTONDOWN:
  414. if (GetFocus() != pce->hwndEdit)
  415. {
  416. SetFocus(pce->hwndEdit);
  417. #ifndef UNIX
  418. // IEUNIX : since we disabled autoselection on first click in address bar,
  419. // we should not eat this message. This allows the dragging to begin with
  420. // the first click.
  421. return(0L); // eat this message
  422. #endif
  423. }
  424. break;
  425. case WM_SYSKEYDOWN:
  426. switch(wParam) {
  427. // Pass these to the combobox itself to make it work properly...
  428. case VK_UP:
  429. case VK_DOWN:
  430. {
  431. LRESULT lR;
  432. if (pce->hwndCombo)
  433. {
  434. lR=SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
  435. #ifdef KEYBOARDCUES
  436. //notify of navigation key usage
  437. CCNotifyNavigationKeyUsage(&(pce->ci), UISF_HIDEFOCUS);
  438. #endif
  439. return lR;
  440. }
  441. }
  442. }
  443. }
  444. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  445. }
  446. LRESULT ComboEx_GetLBText(PCOMBOBOXEX pce, UINT uMsg, WPARAM wParam, LPARAM lParam)
  447. {
  448. COMBOBOXEXITEM cei;
  449. TCHAR szText[CBEMAXSTRLEN];
  450. cei.mask = CBEIF_TEXT;
  451. cei.pszText = szText;
  452. cei.cchTextMax = ARRAYSIZE(szText);
  453. cei.iItem = (INT_PTR)wParam;
  454. if (!ComboEx_OnGetItem(pce, &cei))
  455. return CB_ERR;
  456. if (lParam && uMsg == CB_GETLBTEXT) {
  457. #ifdef UNICODE_WIN9x
  458. if (pce->ci.bUnicode) {
  459. lstrcpy((LPTSTR)lParam, szText);
  460. } else {
  461. WideCharToMultiByte(pce->ci.uiCodePage, 0, szText, -1, (LPSTR)lParam, CBEMAXSTRLEN, NULL,NULL);
  462. }
  463. #else
  464. lstrcpy((LPTSTR)lParam, szText);
  465. #endif
  466. }
  467. return lstrlen(szText);
  468. }
  469. LRESULT CALLBACK ComboSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  470. LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  471. {
  472. PCOMBOBOXEX pce = (PCOMBOBOXEX)dwRefData;
  473. switch (uMsg) {
  474. case WM_ERASEBKGND:
  475. EraseWindow(hwnd, (HDC)wParam, GetSysColor(COLOR_WINDOW));
  476. break;
  477. case CB_GETLBTEXT:
  478. case CB_GETLBTEXTLEN:
  479. return ComboEx_GetLBText(pce, uMsg, wParam, lParam);
  480. case WM_RBUTTONDOWN:
  481. //Fall Thru
  482. case WM_LBUTTONDOWN:
  483. if (ComboSubclass_HandleButton(pce, wParam, lParam)) {
  484. return 0;
  485. }
  486. break;
  487. case WM_COMMAND:
  488. if (ComboSubclass_HandleCommand(pce, wParam, lParam))
  489. return 0;
  490. break;
  491. case WM_DESTROY:
  492. RemoveWindowSubclass(hwnd, ComboSubclassProc, 0);
  493. break;
  494. case WM_SETCURSOR:
  495. if (pce) {
  496. NMMOUSE nm = {0};
  497. nm.dwHitInfo = lParam;
  498. if (CCSendNotify(&pce->ci, NM_SETCURSOR, &nm.hdr))
  499. return 0;
  500. }
  501. break;
  502. }
  503. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  504. }
  505. BOOL ComboEx_OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
  506. {
  507. PCOMBOBOXEX pce;
  508. DWORD dwStyle;
  509. DWORD dwExStyle = 0;
  510. pce = (PCOMBOBOXEX)LocalAlloc(LPTR, sizeof(COMBOEX));
  511. if (!pce)
  512. return FALSE;
  513. SetWindowPtr(hwnd, 0, pce);
  514. // BUGBUG: force off borders off ourself
  515. lpcs->style &= ~(WS_BORDER | WS_VSCROLL | WS_HSCROLL | CBS_UPPERCASE | CBS_LOWERCASE);
  516. SetWindowLong(hwnd, GWL_STYLE, lpcs->style);
  517. CIInitialize(&pce->ci, hwnd, lpcs);
  518. // or in CBS_SIMPLE because we can never allow the sub combo box
  519. // to have just drop down.. it's either all simple or dropdownlist
  520. dwStyle = CBS_OWNERDRAWFIXED | CBS_SIMPLE | CBS_NOINTEGRALHEIGHT | WS_VISIBLE |WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  521. dwStyle |= (lpcs->style & (CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD));
  522. if ((lpcs->style & CBS_DROPDOWNLIST) == CBS_SIMPLE)
  523. dwStyle |= (lpcs->style & (CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_UPPERCASE | CBS_LOWERCASE));
  524. dwExStyle = lpcs->dwExStyle & (WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
  525. pce->hwndCombo = CreateWindowEx(dwExStyle, c_szComboBox, lpcs->lpszName,
  526. dwStyle,
  527. 0, 0, lpcs->cx, lpcs->cy,
  528. hwnd, lpcs->hMenu, lpcs->hInstance, 0);
  529. if (!pce->hwndCombo ||
  530. !SetWindowSubclass(pce->hwndCombo, ComboSubclassProc, 0, (DWORD_PTR)pce) ||
  531. (!ComboEx_GetEditBox(pce) && ComboEx_Editable(pce)))
  532. {
  533. ComboEx_OnDestroy(pce);
  534. return FALSE;
  535. }
  536. ComboEx_OnSetFont(pce, NULL, FALSE);
  537. pce->cxIndent = 10;
  538. pce->iSel = -1;
  539. ComboEx_OnWindowPosChanging(pce, NULL);
  540. return TRUE;
  541. }
  542. HIMAGELIST ComboEx_OnSetImageList(PCOMBOBOXEX pce, HIMAGELIST himl)
  543. {
  544. int iHeight;
  545. HIMAGELIST himlOld = pce->himl;
  546. pce->himl = himl;
  547. iHeight = ComboEx_ComputeItemHeight(pce, FALSE);
  548. SendMessage(pce->ci.hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, iHeight);
  549. SendMessage(pce->hwndCombo, CB_SETITEMHEIGHT, 0, iHeight);
  550. InvalidateRect(pce->hwndCombo, NULL, TRUE);
  551. if (pce->hwndEdit)
  552. ComboEx_SizeEditBox(pce);
  553. return himlOld;
  554. }
  555. void ComboEx_OnDrawItem(PCOMBOBOXEX pce, LPDRAWITEMSTRUCT pdis)
  556. {
  557. HDC hdc = pdis->hDC;
  558. RECT rc = pdis->rcItem;
  559. TCHAR szText[CBEMAXSTRLEN];
  560. int offset = 0;
  561. int xString, yString, xCombo;
  562. int cxIcon = 0, cyIcon = 0;
  563. int iLen;
  564. BOOL fSelected = FALSE;
  565. SIZE sizeText;
  566. COMBOBOXEXITEM cei;
  567. BOOL fNoText = FALSE;
  568. BOOL fEnabled = IsWindowEnabled(pce->hwndCombo);
  569. BOOL fRTLReading = FALSE;
  570. UINT OldTextAlign;
  571. // Setup the dc before we use it.
  572. fRTLReading = GetWindowLong(pdis->hwndItem, GWL_EXSTYLE) & WS_EX_RTLREADING;
  573. if (fRTLReading) {
  574. OldTextAlign = GetTextAlign(hdc);
  575. SetTextAlign(hdc, OldTextAlign|TA_RTLREADING);
  576. }
  577. rc.top += g_cyBorder;
  578. szText[0] = 0;
  579. if (pdis->itemID != -1)
  580. {
  581. cei.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_OVERLAY | CBEIF_SELECTEDIMAGE| CBEIF_INDENT;
  582. cei.pszText = szText;
  583. cei.cchTextMax = ARRAYSIZE(szText);
  584. cei.iItem = pdis->itemID;
  585. ComboEx_OnGetItem(pce, &cei);
  586. if (pce->iSel == (int)pdis->itemID ||
  587. ((pce->iSel == -1) && ((int)pdis->itemID == ComboBox_GetCurSel(pce->hwndCombo))))
  588. fSelected = TRUE;
  589. }
  590. else {
  591. if(pce->fEditItemSet) {
  592. cei.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_OVERLAY | CBEIF_SELECTEDIMAGE| CBEIF_INDENT;
  593. cei.pszText = szText;
  594. cei.cchTextMax = ARRAYSIZE(szText);
  595. cei.iItem = pdis->itemID;
  596. ComboEx_OnGetItem(pce, &cei);
  597. }
  598. }
  599. if (pce->himl && !(pce->dwExStyle & CBES_EX_NOEDITIMAGEINDENT))
  600. {
  601. ImageList_GetIconSize(pce->himl, &cxIcon, &cyIcon);
  602. if (cxIcon)
  603. cxIcon += COMBO_MARGIN;
  604. }
  605. // if we're not drawing the edit box, figure out how far to indent
  606. // over
  607. if (!(pdis->itemState & ODS_COMBOBOXEDIT))
  608. {
  609. offset = (pce->cxIndent * cei.iIndent) + COMBO_BORDER;
  610. }
  611. else
  612. {
  613. if (pce->hwndEdit)
  614. fNoText = TRUE;
  615. if (pce->dwExStyle & CBES_EX_NOEDITIMAGEINDENT)
  616. cxIcon = 0;
  617. }
  618. xCombo = rc.left + offset;
  619. rc.left = xString = xCombo + cxIcon;
  620. iLen = lstrlen(szText);
  621. GetTextExtentPoint(hdc, szText, iLen, &sizeText);
  622. rc.right = rc.left + sizeText.cx;
  623. rc.left--;
  624. rc.right++;
  625. if (pdis->itemAction != ODA_FOCUS)
  626. {
  627. int yMid;
  628. BOOL fTextHighlight = FALSE;;
  629. yMid = (rc.top + rc.bottom) / 2;
  630. // center the string within rc
  631. yString = yMid - (sizeText.cy/2);
  632. if (pdis->itemState & ODS_SELECTED) {
  633. if (!(pdis->itemState & ODS_COMBOBOXEDIT) ||
  634. !ComboEx_Editable(pce)) {
  635. fTextHighlight = TRUE;
  636. }
  637. }
  638. if ( !fEnabled ) {
  639. SetBkColor(hdc, g_clrBtnFace);
  640. SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
  641. } else {
  642. SetBkColor(hdc, GetSysColor(fTextHighlight ?
  643. COLOR_HIGHLIGHT : COLOR_WINDOW));
  644. SetTextColor(hdc, GetSysColor(fTextHighlight ?
  645. COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
  646. }
  647. if ((pdis->itemState & ODS_COMBOBOXEDIT) &&
  648. (rc.right > pdis->rcItem.right))
  649. {
  650. // Need to clip as user does not!
  651. rc.right = pdis->rcItem.right;
  652. }
  653. if (!fNoText) {
  654. ExtTextOut(hdc, xString, yString, ETO_OPAQUE | ETO_CLIPPED, &rc, szText, iLen, NULL);
  655. }
  656. if (pce->himl && (pdis->itemID != -1 || pce->fEditItemSet) &&
  657. !((pce->dwExStyle & (CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT)) &&
  658. (pdis->itemState & ODS_COMBOBOXEDIT))) {
  659. DWORD fTransparent = 0;
  660. if ((pdis->itemState & ODS_COMBOBOXEDIT) && !fEnabled) {
  661. fTransparent = ILD_TRANSPARENT;
  662. }
  663. if (pce->himl && (pdis->itemID != -1 || pce->fEditItemSet) &&
  664. !((pce->dwExStyle & (CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT))))
  665. {
  666. ImageList_Draw(pce->himl,
  667. (fSelected) ? cei.iSelectedImage : cei.iImage,
  668. hdc, xCombo, yMid - (cyIcon/2),
  669. INDEXTOOVERLAYMASK(cei.iOverlay) |
  670. fTransparent |
  671. ((pdis->itemState & ODS_SELECTED) ? (ILD_SELECTED | ILD_FOCUS) : ILD_NORMAL));
  672. }
  673. }
  674. }
  675. if ((pdis->itemAction == ODA_FOCUS ||
  676. (pdis->itemState & ODS_FOCUS))
  677. #ifdef KEYBOARDCUES
  678. && !(CCGetUIState(&(pce->ci)) & UISF_HIDEFOCUS)
  679. #endif
  680. )
  681. {
  682. if (!fNoText) {
  683. DrawFocusRect(hdc, &rc);
  684. }
  685. }
  686. // Restore the text align in the dc.
  687. if (fRTLReading) {
  688. SetTextAlign(hdc, OldTextAlign);
  689. }
  690. }
  691. int ComboEx_ComputeItemHeight(PCOMBOBOXEX pce, BOOL fTextOnly)
  692. {
  693. HDC hdc;
  694. HFONT hfontOld;
  695. int dyDriveItem;
  696. SIZE siz;
  697. hdc = GetDC(NULL);
  698. hfontOld = ComboEx_GetFont(pce);
  699. if (hfontOld)
  700. hfontOld = SelectObject(hdc, hfontOld);
  701. GetTextExtentPoint(hdc, TEXT("W"), 1, &siz);
  702. dyDriveItem = siz.cy;
  703. if (hfontOld)
  704. SelectObject(hdc, hfontOld);
  705. ReleaseDC(NULL, hdc);
  706. if (fTextOnly)
  707. return dyDriveItem;
  708. dyDriveItem += COMBO_BORDER;
  709. // now take into account the icon
  710. if (pce->himl) {
  711. int cxIcon = 0, cyIcon = 0;
  712. ImageList_GetIconSize(pce->himl, &cxIcon, &cyIcon);
  713. if (dyDriveItem < cyIcon)
  714. dyDriveItem = cyIcon;
  715. }
  716. return dyDriveItem;
  717. }
  718. void ComboEx_OnMeasureItem(PCOMBOBOXEX pce, LPMEASUREITEMSTRUCT pmi)
  719. {
  720. pmi->itemHeight = ComboEx_ComputeItemHeight(pce, FALSE);
  721. }
  722. void ComboEx_ISetItem(PCOMBOBOXEX pce, PCEITEM pcei, PCOMBOBOXEXITEM pceItem)
  723. {
  724. if (pceItem->mask & CBEIF_INDENT)
  725. pcei->iIndent = pceItem->iIndent;
  726. if (pceItem->mask & CBEIF_IMAGE)
  727. pcei->iImage = pceItem->iImage;
  728. if (pceItem->mask & CBEIF_SELECTEDIMAGE)
  729. pcei->iSelectedImage = pceItem->iSelectedImage;
  730. if (pceItem->mask & CBEIF_OVERLAY)
  731. pcei->iOverlay = pceItem->iOverlay;
  732. if (pceItem->mask & CBEIF_TEXT) {
  733. Str_Set(&pcei->pszText, pceItem->pszText);
  734. }
  735. if (pceItem->mask & CBEIF_LPARAM) {
  736. pcei->lParam = pceItem->lParam;
  737. }
  738. }
  739. #define ComboEx_GetItemPtr(pce, iItem) \
  740. ((PCEITEM)SendMessage((pce)->hwndCombo, CB_GETITEMDATA, iItem, 0))
  741. #define ComboEx_Count(pce) \
  742. ((int)SendMessage((pce)->hwndCombo, CB_GETCOUNT, 0, 0))
  743. BOOL ComboEx_OnGetItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem)
  744. {
  745. PCEITEM pcei;
  746. NMCOMBOBOXEX nm;
  747. if(pceItem->iItem != -1) {
  748. pcei = ComboEx_GetItemPtr(pce, pceItem->iItem);
  749. }
  750. else {
  751. pcei = &(pce->cei);
  752. }
  753. if ((!pcei) || (pcei == (PCEITEM)-1))
  754. return FALSE;
  755. nm.ceItem.mask = 0;
  756. if (pceItem->mask & CBEIF_TEXT) {
  757. if (pcei->pszText == LPSTR_TEXTCALLBACK) {
  758. nm.ceItem.mask |= CBEIF_TEXT;
  759. } else {
  760. if(pceItem->iItem != -1) {
  761. Str_GetPtr(pcei->pszText, pceItem->pszText, pceItem->cchTextMax);
  762. }else {
  763. SendMessage(pce->hwndEdit, WM_GETTEXT, (WPARAM)pceItem->cchTextMax, (LPARAM)pceItem->pszText);
  764. }
  765. }
  766. }
  767. if (pceItem->mask & CBEIF_IMAGE) {
  768. if (pcei->iImage == I_IMAGECALLBACK) {
  769. nm.ceItem.mask |= CBEIF_IMAGE;
  770. }
  771. pceItem->iImage = pcei->iImage;
  772. }
  773. if (pceItem->mask & CBEIF_SELECTEDIMAGE) {
  774. if (pcei->iSelectedImage == I_IMAGECALLBACK) {
  775. nm.ceItem.mask |= CBEIF_SELECTEDIMAGE;
  776. }
  777. pceItem->iSelectedImage = pcei->iSelectedImage;
  778. }
  779. if (pceItem->mask & CBEIF_OVERLAY) {
  780. if (pcei->iOverlay == I_IMAGECALLBACK) {
  781. nm.ceItem.mask |= CBEIF_OVERLAY;
  782. }
  783. pceItem->iOverlay = pcei->iOverlay;
  784. }
  785. if (pceItem->mask & CBEIF_INDENT) {
  786. if (pcei->iIndent == I_INDENTCALLBACK) {
  787. nm.ceItem.mask |= CBEIF_INDENT;
  788. pceItem->iIndent = 0;
  789. } else {
  790. pceItem->iIndent = pcei->iIndent;
  791. }
  792. }
  793. if (pceItem->mask & CBEIF_LPARAM) {
  794. pceItem->lParam = pcei->lParam;
  795. }
  796. // is there anything to call back for?
  797. if (nm.ceItem.mask) {
  798. UINT uMask = nm.ceItem.mask;
  799. nm.ceItem = *pceItem;
  800. nm.ceItem.lParam = pcei->lParam;
  801. nm.ceItem.mask = uMask;
  802. if ((nm.ceItem.mask & CBEIF_TEXT) &&
  803. nm.ceItem.cchTextMax) {
  804. // null terminate just in case they don't respond
  805. *nm.ceItem.pszText = 0;
  806. }
  807. CCSendNotify(&pce->ci, CBEN_GETDISPINFO, &nm.hdr);
  808. if (nm.ceItem.mask & CBEIF_INDENT)
  809. pceItem->iIndent = nm.ceItem.iIndent;
  810. if (nm.ceItem.mask & CBEIF_IMAGE)
  811. pceItem->iImage = nm.ceItem.iImage;
  812. if (nm.ceItem.mask & CBEIF_SELECTEDIMAGE)
  813. pceItem->iSelectedImage = nm.ceItem.iSelectedImage;
  814. if (nm.ceItem.mask & CBEIF_OVERLAY)
  815. pceItem->iOverlay = nm.ceItem.iOverlay;
  816. if (nm.ceItem.mask & CBEIF_TEXT)
  817. {
  818. if (pceItem->mask & CBEIF_TEXT)
  819. pceItem->pszText = CCReturnDispInfoText(nm.ceItem.pszText, pceItem->pszText, pceItem->cchTextMax);
  820. else
  821. pceItem->pszText = nm.ceItem.pszText;
  822. }
  823. if (nm.ceItem.mask & CBEIF_DI_SETITEM) {
  824. ComboEx_ISetItem(pce, pcei, &nm.ceItem);
  825. }
  826. }
  827. return TRUE;
  828. }
  829. #ifdef UNICODE
  830. BOOL ComboEx_OnGetItemA(PCOMBOBOXEX pce, PCOMBOBOXEXITEMA pceItem)
  831. {
  832. LPWSTR pwszText;
  833. LPSTR pszTextSave;
  834. BOOL fRet;
  835. if (!(pceItem->mask & CBEIF_TEXT)) {
  836. return ComboEx_OnGetItem(pce, (PCOMBOBOXEXITEM)pceItem);
  837. }
  838. pwszText = (LPWSTR)LocalAlloc(LPTR, (pceItem->cchTextMax+1)*sizeof(WCHAR));
  839. if (!pwszText)
  840. return FALSE;
  841. pszTextSave = pceItem->pszText;
  842. ((PCOMBOBOXEXITEM)pceItem)->pszText = pwszText;
  843. fRet = ComboEx_OnGetItem(pce, (PCOMBOBOXEXITEM)pceItem);
  844. pceItem->pszText = pszTextSave;
  845. if (fRet) {
  846. // BUGBUG: WCTMB failes w/ ERROR_INSUFFICIENT_BUFFER whereas the native-A implementation truncates
  847. WideCharToMultiByte(CP_ACP, 0, pwszText, -1,
  848. (LPSTR)pszTextSave, pceItem->cchTextMax, NULL, NULL);
  849. }
  850. LocalFree(pwszText);
  851. return fRet;
  852. }
  853. #endif
  854. BOOL ComboEx_OnSetItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem)
  855. {
  856. if(pceItem->iItem != -1) {
  857. PCEITEM pcei = ComboEx_GetItemPtr(pce, pceItem->iItem);
  858. UINT rdwFlags = 0;
  859. if (pcei == (PCEITEM)-1)
  860. return FALSE;
  861. ComboEx_ISetItem(pce, pcei, pceItem);
  862. if (rdwFlags & (CBEIF_INDENT | CBEIF_IMAGE |CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_OVERLAY)) {
  863. rdwFlags = RDW_ERASE | RDW_INVALIDATE;
  864. }
  865. // BUGBUG: do something better..
  866. if (rdwFlags) {
  867. RedrawWindow(pce->hwndCombo, NULL, NULL, rdwFlags);
  868. }
  869. if (pceItem->iItem == ComboBox_GetCurSel(pce->hwndCombo))
  870. ComboEx_UpdateEditText(pce, FALSE);
  871. // BUGUBG: notify item changed
  872. return TRUE;
  873. } else {
  874. pce->cei.iImage = -1;
  875. pce->cei.iSelectedImage = -1;
  876. ComboEx_ISetItem(pce, &(pce->cei), pceItem);
  877. pce->fEditItemSet = TRUE;
  878. if (!pce->hwndEdit){
  879. Str_Set(&pce->cei.pszText, NULL);
  880. pce->fEditItemSet = FALSE;
  881. return(CB_ERR);
  882. }
  883. if(pce->cei.pszText) {
  884. SendMessage(pce->hwndEdit, WM_SETTEXT, (WPARAM)0, (LPARAM)pce->cei.pszText);
  885. EDIT_SELECTALL( pce->hwndEdit );
  886. }
  887. RedrawWindow(pce->hwndCombo, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  888. return TRUE;
  889. }
  890. }
  891. void ComboEx_HandleDeleteItem(PCOMBOBOXEX pce, LPDELETEITEMSTRUCT pdis)
  892. {
  893. PCEITEM pcei = (PCEITEM)pdis->itemData;
  894. if (pcei) {
  895. NMCOMBOBOXEX nm;
  896. Str_Set(&pcei->pszText, NULL);
  897. nm.ceItem.iItem = pdis->itemID;
  898. nm.ceItem.mask = CBEIF_LPARAM;
  899. nm.ceItem.lParam = pcei->lParam;
  900. CCSendNotify(&pce->ci, CBEN_DELETEITEM, &nm.hdr);
  901. LocalFree(pcei);
  902. }
  903. }
  904. LRESULT ComboEx_OnInsertItem(PCOMBOBOXEX pce, PCOMBOBOXEXITEM pceItem)
  905. {
  906. LRESULT iRet;
  907. PCEITEM pcei = (PCEITEM)LocalAlloc(LPTR, sizeof(CEITEM));
  908. if (!pcei)
  909. return -1;
  910. pcei->iImage = -1;
  911. pcei->iSelectedImage = -1;
  912. //pcei->iOverlay = 0;
  913. //pcei->iIndent = 0;
  914. ComboEx_ISetItem(pce, pcei, pceItem);
  915. iRet = ComboBox_InsertString(pce->hwndCombo, pceItem->iItem, pcei);
  916. if (iRet != -1) {
  917. NMCOMBOBOXEX nm;
  918. nm.ceItem = *pceItem;
  919. CCSendNotify(&pce->ci, CBEN_INSERTITEM, &nm.hdr);
  920. }
  921. return iRet;
  922. }
  923. void ComboEx_OnWindowPosChanging(PCOMBOBOXEX pce, LPWINDOWPOS pwp)
  924. {
  925. RECT rcWindow, rcClient;
  926. RECT rc;
  927. int cxInner;
  928. int cy;
  929. GetWindowRect(pce->ci.hwnd, &rcWindow);
  930. if (pwp) {
  931. // check to see if our size & position aren't actually changing (rebar, for one,
  932. // does lots of DeferWindowPos calls that don't actually change our size or position
  933. // but still generate WM_WINDOWPOSCHANGING msgs). we avoid flicker by bailing here.
  934. RECT rcWp;
  935. SetRect(&rcWp, pwp->x, pwp->y, pwp->x + pwp->cx, pwp->y + pwp->cy);
  936. MapWindowRect(GetParent(pce->ci.hwnd), HWND_DESKTOP, (LPPOINT)&rcWp);
  937. if (EqualRect(&rcWp, &rcWindow)) {
  938. // this is a noop, so bail
  939. return;
  940. }
  941. }
  942. GetClientRect(pce->ci.hwnd, &rcClient);
  943. if (pwp)
  944. cxInner = pwp->cx + RECTWIDTH(rcWindow) - RECTWIDTH(rcClient);
  945. else
  946. cxInner = RECTWIDTH(rcClient);
  947. GetWindowRect(pce->hwndCombo, &rc);
  948. if (cxInner) {
  949. // don't size the inner combo if width is 0; otherwise, the below
  950. // computation will make the comboEX the height of the inner combo
  951. // top + inner combo dropdown instead of JUST the inner combo top
  952. cy = (pwp && ((pce->ci.style & CBS_DROPDOWNLIST) == CBS_SIMPLE)) ? pwp->cy : RECTHEIGHT(rc);
  953. SetWindowPos(pce->hwndCombo, NULL, 0, 0, cxInner, cy,
  954. SWP_NOACTIVATE | (pce->hwndEdit ? SWP_NOREDRAW : 0));
  955. }
  956. GetWindowRect(pce->hwndCombo, &rc);
  957. cy = RECTHEIGHT(rc) + (RECTHEIGHT(rcWindow) - RECTHEIGHT(rcClient));
  958. if (pwp) {
  959. if (cy < pwp->cy || !(pce->dwExStyle & CBES_EX_NOSIZELIMIT)) {
  960. pwp->cy = cy;
  961. }
  962. } else {
  963. if (cy < RECTHEIGHT(rcWindow) || !(pce->dwExStyle & CBES_EX_NOSIZELIMIT)) {
  964. SetWindowPos(pce->ci.hwnd, NULL, 0, 0,
  965. RECTWIDTH(rcWindow),
  966. cy,
  967. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  968. }
  969. }
  970. if (pce->hwndEdit)
  971. {
  972. ComboEx_SizeEditBox(pce);
  973. InvalidateRect(pce->hwndCombo, NULL, TRUE);
  974. }
  975. }
  976. LRESULT ComboEx_HandleCommand(PCOMBOBOXEX pce, WPARAM wParam, LPARAM lParam)
  977. {
  978. LRESULT lres;
  979. UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
  980. UINT uCmd = GET_WM_COMMAND_CMD(wParam, lParam);
  981. if (!pce)
  982. return 0;
  983. if (uCmd == CBN_SELCHANGE)
  984. // update the edit text before forwarding this notification 'cause in
  985. // a normal combobox, the edit control will have already been updated
  986. // upon receipt of this notification
  987. ComboEx_UpdateEditText(pce, FALSE);
  988. lres = SendMessage(pce->ci.hwndParent, WM_COMMAND, GET_WM_COMMAND_MPS(idCmd, pce->ci.hwnd, uCmd));
  989. switch (uCmd) {
  990. case CBN_DROPDOWN:
  991. pce->iSel = ComboBox_GetCurSel(pce->hwndCombo);
  992. ComboEx_EndEdit(pce, CBENF_DROPDOWN);
  993. if (GetFocus() == pce->hwndEdit)
  994. SetFocus(pce->hwndCombo);
  995. pce->fInDrop = TRUE;
  996. break;
  997. case CBN_KILLFOCUS:
  998. ComboEx_EndEdit(pce, CBENF_KILLFOCUS);
  999. break;
  1000. case CBN_CLOSEUP:
  1001. pce->iSel = -1;
  1002. ComboEx_BeginEdit(pce);
  1003. pce->fInDrop = FALSE;
  1004. break;
  1005. case CBN_SETFOCUS:
  1006. ComboEx_BeginEdit(pce);
  1007. break;
  1008. }
  1009. return lres;
  1010. }
  1011. LRESULT ComboEx_OnGetItemData(PCOMBOBOXEX pce, WPARAM i)
  1012. {
  1013. PCEITEM pcei = (PCEITEM)SendMessage(pce->hwndCombo, CB_GETITEMDATA, i, 0);
  1014. if (pcei == NULL || pcei == (PCEITEM)CB_ERR) {
  1015. return CB_ERR;
  1016. }
  1017. return pcei->lParam;
  1018. }
  1019. LRESULT ComboEx_OnSetItemData(PCOMBOBOXEX pce, int i, LPARAM lParam)
  1020. {
  1021. PCEITEM pcei = (PCEITEM)SendMessage(pce->hwndCombo, CB_GETITEMDATA, i, 0);
  1022. if (pcei == NULL || pcei == (PCEITEM)CB_ERR) {
  1023. return CB_ERR;
  1024. }
  1025. pcei->lParam = lParam;
  1026. return 0;
  1027. }
  1028. int ComboEx_OnFindStringExact(PCOMBOBOXEX pce, int iStart, LPCTSTR lpsz)
  1029. {
  1030. int i;
  1031. int iMax = ComboEx_Count(pce);
  1032. TCHAR szText[CBEMAXSTRLEN];
  1033. COMBOBOXEXITEM cei;
  1034. if (iStart < 0)
  1035. iStart = -1;
  1036. cei.mask = CBEIF_TEXT;
  1037. cei.pszText = szText;
  1038. cei.cchTextMax = ARRAYSIZE(szText);
  1039. for (i = iStart + 1 ; i < iMax; i++) {
  1040. cei.iItem = i;
  1041. if (ComboEx_OnGetItem(pce, &cei)) {
  1042. if (!ComboEx_StrCmp(pce, lpsz, szText)) {
  1043. return i;
  1044. }
  1045. }
  1046. }
  1047. for (i = 0; i <= iStart; i++) {
  1048. cei.iItem = i;
  1049. if (ComboEx_OnGetItem(pce, &cei)) {
  1050. if (!ComboEx_StrCmp(pce, lpsz, szText)) {
  1051. return i;
  1052. }
  1053. }
  1054. }
  1055. return CB_ERR;
  1056. }
  1057. int ComboEx_StrCmp(PCOMBOBOXEX pce, LPCTSTR psz1, LPCTSTR psz2)
  1058. {
  1059. if (pce->dwExStyle & CBES_EX_CASESENSITIVE) {
  1060. return lstrcmp(psz1, psz2);
  1061. }
  1062. return lstrcmpi(psz1, psz2);
  1063. }
  1064. DWORD ComboEx_OnSetExStyle(PCOMBOBOXEX pce, DWORD dwExStyle, DWORD dwExMask)
  1065. {
  1066. DWORD dwRet;
  1067. DWORD dwChange;
  1068. if (dwExMask)
  1069. dwExStyle = (pce->dwExStyle & ~ dwExMask) | (dwExStyle & dwExMask);
  1070. dwRet = pce->dwExStyle;
  1071. dwChange = (pce->dwExStyle ^ dwExStyle);
  1072. pce->dwExStyle = dwExStyle;
  1073. if (dwChange & (CBES_EX_NOEDITIMAGE | CBES_EX_NOEDITIMAGEINDENT)) {
  1074. InvalidateRect(pce->ci.hwnd, NULL, TRUE);
  1075. if (pce->hwndEdit)
  1076. {
  1077. ComboEx_SizeEditBox(pce);
  1078. InvalidateRect(pce->hwndEdit, NULL, TRUE);
  1079. }
  1080. }
  1081. if (dwChange & CBES_EX_PATHWORDBREAKPROC)
  1082. SetPathWordBreakProc(pce->hwndEdit, (pce->dwExStyle & CBES_EX_PATHWORDBREAKPROC));
  1083. return dwRet;
  1084. }
  1085. HFONT ComboEx_GetFont(PCOMBOBOXEX pce)
  1086. {
  1087. if (pce->hwndCombo)
  1088. return (HFONT)SendMessage(pce->hwndCombo, WM_GETFONT, 0, 0);
  1089. return NULL;
  1090. }
  1091. LRESULT CALLBACK ComboExWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1092. {
  1093. LRESULT lres = 0;
  1094. PCOMBOBOXEX pce = (PCOMBOBOXEX)GetWindowPtr(hwnd, 0);
  1095. if (!pce) {
  1096. if (uMsg != WM_NCCREATE &&
  1097. uMsg != WM_CREATE)
  1098. goto DoDefault;
  1099. }
  1100. switch (uMsg) {
  1101. HANDLE_MSG(pce, WM_SETFONT, ComboEx_OnSetFont);
  1102. case WM_ENABLE:
  1103. if (pce->hwndCombo)
  1104. EnableWindow(pce->hwndCombo, (BOOL) wParam);
  1105. if (pce->hwndEdit)
  1106. EnableWindow(pce->hwndEdit, (BOOL) wParam);
  1107. break;
  1108. case WM_WININICHANGE:
  1109. InitGlobalMetrics(wParam);
  1110. // only need to re-create this font if we created it in the first place
  1111. // and somebody changed the font (or did a wildcard change)
  1112. //
  1113. // NOTE: Some people broadcast a nonclient metrics change when they
  1114. // change the icon title logfont, so watch for both.
  1115. //
  1116. if (pce && pce->fFontCreated &&
  1117. ((wParam == 0 && lParam == 0) ||
  1118. wParam == SPI_SETICONTITLELOGFONT ||
  1119. wParam == SPI_SETNONCLIENTMETRICS))
  1120. {
  1121. ComboEx_OnSetFont(pce, NULL, TRUE);
  1122. }
  1123. break;
  1124. case WM_SYSCOLORCHANGE:
  1125. InitGlobalColors();
  1126. break;
  1127. case WM_NOTIFYFORMAT:
  1128. return CIHandleNotifyFormat(&pce->ci, lParam);
  1129. break;
  1130. case WM_NCCREATE:
  1131. // strip off the scroll bits
  1132. SetWindowBits(hwnd, GWL_STYLE, WS_BORDER | WS_VSCROLL | WS_HSCROLL, 0);
  1133. goto DoDefault;
  1134. case WM_CREATE:
  1135. CCCreateWindow();
  1136. if (!ComboEx_OnCreate(hwnd, (LPCREATESTRUCT)lParam))
  1137. lres = -1; // OnCreate falied. Fail WM_CREATE
  1138. break;
  1139. case WM_DESTROY:
  1140. ASSERT(pce);
  1141. CCDestroyWindow();
  1142. ComboEx_OnDestroy(pce);
  1143. break;
  1144. case WM_WINDOWPOSCHANGING:
  1145. ComboEx_OnWindowPosChanging(pce, (LPWINDOWPOS)lParam);
  1146. break;
  1147. #if 0
  1148. case WM_SIZE:
  1149. ComboEx_OnSize(pce);
  1150. break;
  1151. #endif
  1152. case WM_DRAWITEM:
  1153. ComboEx_OnDrawItem(pce, (LPDRAWITEMSTRUCT)lParam);
  1154. break;
  1155. case WM_MEASUREITEM:
  1156. ComboEx_OnMeasureItem(pce, (LPMEASUREITEMSTRUCT)lParam);
  1157. break;
  1158. case WM_COMMAND:
  1159. return ComboEx_HandleCommand(pce, wParam, lParam);
  1160. case WM_GETFONT:
  1161. return (LRESULT)ComboEx_GetFont(pce);
  1162. case WM_SETFOCUS:
  1163. if (pce->hwndCombo)
  1164. SetFocus(pce->hwndCombo);
  1165. break;
  1166. case WM_DELETEITEM:
  1167. ComboEx_HandleDeleteItem(pce, (LPDELETEITEMSTRUCT)lParam);
  1168. return TRUE;
  1169. #ifdef KEYBOARDCUES
  1170. case WM_UPDATEUISTATE:
  1171. //not sure need to set bit, will probably not use it, on the other hand this
  1172. // is consistent with remaining of common controls and not very expensive
  1173. CCOnUIState(&(pce->ci), WM_UPDATEUISTATE, wParam, lParam);
  1174. goto DoDefault;
  1175. #endif
  1176. // this is for backcompat only.
  1177. case CBEM_SETEXSTYLE:
  1178. return ComboEx_OnSetExStyle(pce, (DWORD)wParam, 0);
  1179. case CBEM_SETEXTENDEDSTYLE:
  1180. return ComboEx_OnSetExStyle(pce, (DWORD)lParam, (DWORD)wParam);
  1181. case CBEM_GETEXTENDEDSTYLE:
  1182. return pce->dwExStyle;
  1183. case CBEM_GETCOMBOCONTROL:
  1184. return (LRESULT)pce->hwndCombo;
  1185. case CBEM_SETIMAGELIST:
  1186. return (LRESULT)ComboEx_OnSetImageList(pce, (HIMAGELIST)lParam);
  1187. case CBEM_GETIMAGELIST:
  1188. return (LRESULT)pce->himl;
  1189. #ifdef UNICODE
  1190. case CBEM_GETITEMA:
  1191. return ComboEx_OnGetItemA(pce, (PCOMBOBOXEXITEMA)lParam);
  1192. #endif
  1193. case CBEM_GETITEM:
  1194. return ComboEx_OnGetItem(pce, (PCOMBOBOXEXITEM)lParam);
  1195. #ifdef UNICODE
  1196. case CBEM_SETITEMA: {
  1197. LRESULT lResult;
  1198. LPWSTR lpStrings;
  1199. UINT uiCount;
  1200. LPSTR lpAnsiString = (LPSTR) ((PCOMBOBOXEXITEM)lParam)->pszText;
  1201. if ((((PCOMBOBOXEXITEM)lParam)->mask & CBEIF_TEXT) &&
  1202. (((PCOMBOBOXEXITEM)lParam)->pszText != LPSTR_TEXTCALLBACK)) {
  1203. uiCount = lstrlenA(lpAnsiString)+1;
  1204. lpStrings = LocalAlloc(LPTR, (uiCount) * sizeof(TCHAR));
  1205. if (!lpStrings)
  1206. return -1;
  1207. MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lpAnsiString, uiCount,
  1208. lpStrings, uiCount);
  1209. ((PCOMBOBOXEXITEMA)lParam)->pszText = (LPSTR)lpStrings;
  1210. lResult = ComboEx_OnSetItem(pce, (PCOMBOBOXEXITEM)lParam);
  1211. ((PCOMBOBOXEXITEMA)lParam)->pszText = lpAnsiString;
  1212. LocalFree(lpStrings);
  1213. return lResult;
  1214. } else {
  1215. return ComboEx_OnSetItem(pce, (PCOMBOBOXEXITEM)lParam);
  1216. }
  1217. }
  1218. #endif
  1219. case CBEM_SETITEM:
  1220. return ComboEx_OnSetItem(pce, (PCOMBOBOXEXITEM)lParam);
  1221. #ifdef UNICODE
  1222. case CBEM_INSERTITEMA: {
  1223. LRESULT lResult;
  1224. LPWSTR lpStrings;
  1225. UINT uiCount;
  1226. LPSTR lpAnsiString = (LPSTR) ((PCOMBOBOXEXITEM)lParam)->pszText;
  1227. if (!lpAnsiString || lpAnsiString == (LPSTR)LPSTR_TEXTCALLBACK)
  1228. return ComboEx_OnInsertItem(pce, (PCOMBOBOXEXITEM)lParam);
  1229. uiCount = lstrlenA(lpAnsiString)+1;
  1230. lpStrings = LocalAlloc(LPTR, (uiCount) * sizeof(TCHAR));
  1231. if (!lpStrings)
  1232. return -1;
  1233. MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lpAnsiString, uiCount,
  1234. lpStrings, uiCount);
  1235. ((PCOMBOBOXEXITEMA)lParam)->pszText = (LPSTR)lpStrings;
  1236. lResult = ComboEx_OnInsertItem(pce, (PCOMBOBOXEXITEM)lParam);
  1237. ((PCOMBOBOXEXITEMA)lParam)->pszText = lpAnsiString;
  1238. LocalFree(lpStrings);
  1239. return lResult;
  1240. }
  1241. #endif
  1242. case CBEM_INSERTITEM:
  1243. return ComboEx_OnInsertItem(pce, (PCOMBOBOXEXITEM)lParam);
  1244. case CBEM_GETEDITCONTROL:
  1245. return (LRESULT)pce->hwndEdit;
  1246. case CBEM_HASEDITCHANGED:
  1247. return pce->fEditChanged;
  1248. case CB_GETITEMDATA:
  1249. return ComboEx_OnGetItemData(pce, (int)wParam);
  1250. case CB_SETITEMDATA:
  1251. return ComboEx_OnSetItemData(pce, (int)wParam, lParam);
  1252. case CB_LIMITTEXT:
  1253. if (ComboEx_GetEditBox(pce))
  1254. Edit_LimitText(pce->hwndEdit, wParam);
  1255. break;
  1256. case CB_FINDSTRINGEXACT:
  1257. {
  1258. LPCTSTR psz = (LPCTSTR)lParam;
  1259. #ifdef UNICODE_WIN9x
  1260. TCHAR szText[CBEMAXSTRLEN];
  1261. if (!pce->ci.bUnicode) {
  1262. MultiByteToWideChar(pce->ci.uiCodePage, 0, (LPCSTR)lParam, -1, szText, ARRAYSIZE(szText));
  1263. psz = szText;
  1264. }
  1265. #endif
  1266. return ComboEx_OnFindStringExact(pce, (int)wParam, psz);
  1267. }
  1268. case CB_SETITEMHEIGHT:
  1269. lres = SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
  1270. if (wParam == (WPARAM)-1) {
  1271. RECT rcWindow, rcClient;
  1272. int cy;
  1273. GetWindowRect(pce->hwndCombo, &rcWindow);
  1274. cy = RECTHEIGHT(rcWindow);
  1275. GetWindowRect(pce->ci.hwnd, &rcWindow);
  1276. GetClientRect(pce->ci.hwnd, &rcClient);
  1277. cy = cy + (RECTHEIGHT(rcWindow) - RECTHEIGHT(rcClient));
  1278. SetWindowPos(pce->ci.hwnd, NULL, 0, 0,
  1279. RECTWIDTH(rcWindow),
  1280. cy,
  1281. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  1282. }
  1283. break;
  1284. case CB_INSERTSTRING:
  1285. case CB_ADDSTRING:
  1286. case CB_SETEDITSEL:
  1287. case CB_FINDSTRING:
  1288. case CB_DIR:
  1289. // override to do nothing
  1290. break;
  1291. case CB_SETCURSEL:
  1292. case CB_RESETCONTENT:
  1293. case CB_DELETESTRING:
  1294. lres = SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
  1295. ComboEx_UpdateEditText(pce, uMsg == CB_SETCURSEL);
  1296. break;
  1297. case WM_SETTEXT:
  1298. if (!pce->hwndEdit)
  1299. return(CB_ERR);
  1300. #ifdef UNICODE_WIN9x
  1301. // these wm_* messages are always TCHAR
  1302. lres = SendMessageA(pce->hwndEdit, uMsg, wParam, lParam);
  1303. #else
  1304. lres = SendMessage(pce->hwndEdit, uMsg, wParam, lParam);
  1305. #endif
  1306. EDIT_SELECTALL( pce->hwndEdit );
  1307. RedrawWindow(pce->hwndCombo, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  1308. return(lres);
  1309. case WM_CUT:
  1310. case WM_COPY:
  1311. case WM_PASTE:
  1312. case WM_GETTEXT:
  1313. case WM_GETTEXTLENGTH:
  1314. if (!pce->hwndEdit)
  1315. return 0;
  1316. #ifdef UNICODE_WIN9x
  1317. // these wm_* messages are always TCHAR
  1318. return(SendMessageA(pce->hwndEdit, uMsg, wParam, lParam));
  1319. #else
  1320. return(SendMessage(pce->hwndEdit, uMsg, wParam, lParam));
  1321. #endif
  1322. case WM_SETREDRAW:
  1323. if (pce->hwndEdit)
  1324. SendMessage(pce->hwndEdit, uMsg, wParam, lParam);
  1325. break;
  1326. case CB_GETEDITSEL:
  1327. if (pce->hwndEdit)
  1328. return SendMessage(pce->hwndEdit, EM_GETSEL, wParam, lParam);
  1329. // else fall through
  1330. // Handle it being in a dialog...
  1331. // BUGBUG:: May want to handle it differently when edit control has
  1332. // focus...
  1333. case WM_GETDLGCODE:
  1334. case CB_SHOWDROPDOWN:
  1335. case CB_SETEXTENDEDUI:
  1336. case CB_GETEXTENDEDUI:
  1337. case CB_GETDROPPEDSTATE:
  1338. case CB_GETDROPPEDCONTROLRECT:
  1339. case CB_GETCURSEL:
  1340. case CB_GETCOUNT:
  1341. case CB_SELECTSTRING:
  1342. case CB_GETITEMHEIGHT:
  1343. case CB_SETDROPPEDWIDTH:
  1344. return SendMessage(pce->hwndCombo, uMsg, wParam, lParam);
  1345. case CB_GETLBTEXT:
  1346. case CB_GETLBTEXTLEN:
  1347. return ComboEx_GetLBText(pce, uMsg, wParam, lParam);
  1348. default:
  1349. if (CCWndProc(&pce->ci, uMsg, wParam, lParam, &lres))
  1350. return lres;
  1351. DoDefault:
  1352. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1353. }
  1354. return lres;
  1355. }
  1356. BOOL InitComboExClass(HINSTANCE hinst)
  1357. {
  1358. WNDCLASS wc;
  1359. if (!GetClassInfo(hinst, c_szComboBoxEx, &wc)) {
  1360. wc.lpfnWndProc = ComboExWndProc;
  1361. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  1362. wc.hIcon = NULL;
  1363. wc.lpszMenuName = NULL;
  1364. wc.hInstance = hinst;
  1365. wc.lpszClassName = c_szComboBoxEx;
  1366. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
  1367. wc.style = CS_GLOBALCLASS;
  1368. wc.cbWndExtra = sizeof(PCOMBOBOXEX);
  1369. wc.cbClsExtra = 0;
  1370. return RegisterClass(&wc);
  1371. }
  1372. return TRUE;
  1373. }
  1374. //---------------------------------------------------------------------------
  1375. // SetPathWordBreakProc does special break processing for edit controls.
  1376. //
  1377. // The word break proc is called when ctrl-(left or right) arrow is pressed in the
  1378. // edit control. Normal processing provided by USER breaks words at spaces or tabs,
  1379. // but for us it would be nice to break words at slashes, backslashes, & periods too
  1380. // since it may be common to have paths or url's typed in.
  1381. void WINAPI SetPathWordBreakProc(HWND hwndEdit, BOOL fSet)
  1382. {
  1383. #ifndef WINNT
  1384. // There is a bug with how USER handles WH_CALLWNDPROC global hooks in Win95 that
  1385. // causes us to blow up if one is installed and a wordbreakproc is set. Thus,
  1386. // if an app is running that has one of these hooks installed (intellipoint 1.1 etc.) then
  1387. // if we install our wordbreakproc the app will fault when the proc is called. There
  1388. // does not appear to be any way for us to work around it since USER's thunking code
  1389. // trashes the stack so this API is disabled for Win95.
  1390. return;
  1391. #else
  1392. FARPROC lpfnOld;
  1393. // Don't shaft folks who set their own break proc - leave it alone.
  1394. lpfnOld = (FARPROC)SendMessage(hwndEdit, EM_GETWORDBREAKPROC, 0, 0L);
  1395. if (fSet) {
  1396. if (!lpfnOld)
  1397. SendMessage(hwndEdit, EM_SETWORDBREAKPROC, 0, (LPARAM)ShellEditWordBreakProc);
  1398. } else {
  1399. if (lpfnOld == (FARPROC)ShellEditWordBreakProc)
  1400. SendMessage(hwndEdit, EM_SETWORDBREAKPROC, 0, 0L);
  1401. }
  1402. #endif
  1403. }
  1404. #ifdef WINNT
  1405. BOOL IsDelimiter(TCHAR ch)
  1406. {
  1407. return (ch == TEXT(' ') ||
  1408. ch == TEXT('\t') ||
  1409. ch == TEXT('.') ||
  1410. ch == TEXT('/') ||
  1411. ch == TEXT('\\'));
  1412. }
  1413. int WINAPI ShellEditWordBreakProc(LPTSTR lpch, int ichCurrent, int cch, int code)
  1414. {
  1415. LPTSTR lpchT = lpch + ichCurrent;
  1416. int iIndex;
  1417. BOOL fFoundNonDelimiter = FALSE;
  1418. static BOOL fRight = FALSE; // hack due to bug in USER
  1419. switch (code) {
  1420. case WB_ISDELIMITER:
  1421. fRight = TRUE;
  1422. // Simple case - is the current character a delimiter?
  1423. iIndex = (int)IsDelimiter(*lpchT);
  1424. break;
  1425. case WB_LEFT:
  1426. // Move to the left to find the first delimiter. If we are
  1427. // currently at a delimiter, then skip delimiters until we
  1428. // find the first non-delimiter, then start from there.
  1429. //
  1430. // Special case for fRight - if we are currently at a delimiter
  1431. // then just return the current word!
  1432. while ((lpchT = CharPrev(lpch, lpchT)) != lpch) {
  1433. if (IsDelimiter(*lpchT)) {
  1434. if (fRight || fFoundNonDelimiter)
  1435. break;
  1436. } else {
  1437. fFoundNonDelimiter = TRUE;
  1438. fRight = FALSE;
  1439. }
  1440. }
  1441. iIndex = (int) (lpchT - lpch);
  1442. // We are currently pointing at the delimiter, next character
  1443. // is the beginning of the next word.
  1444. if (iIndex > 0 && iIndex < cch)
  1445. iIndex++;
  1446. break;
  1447. case WB_RIGHT:
  1448. fRight = FALSE;
  1449. // If we are not at a delimiter, then skip to the right until
  1450. // we find the first delimiter. If we started at a delimiter, or
  1451. // we have just finished scanning to the first delimiter, then
  1452. // skip all delimiters until we find the first non delimiter.
  1453. //
  1454. // Careful - the string passed in to us may not be NULL terminated!
  1455. fFoundNonDelimiter = !IsDelimiter(*lpchT);
  1456. if (lpchT != (lpch + cch)) {
  1457. while ((lpchT = FastCharNext(lpchT)) != (lpch + cch)) {
  1458. if (IsDelimiter(*lpchT)) {
  1459. fFoundNonDelimiter = FALSE;
  1460. } else {
  1461. if (!fFoundNonDelimiter)
  1462. break;
  1463. }
  1464. }
  1465. }
  1466. // We are currently pointing at the next word.
  1467. iIndex = (int) (lpchT - lpch);
  1468. break;
  1469. }
  1470. return iIndex;
  1471. }
  1472. #endif