Leaked source code of windows server 2003
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.

1245 lines
36 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: lboxctl1.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * List Box Handling Routines
  7. *
  8. * History:
  9. * ??-???-???? ianja Ported from Win 3.0 sources
  10. * 14-Feb-1991 mikeke Added Revalidation code
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. INT xxxLBBinarySearchString(PLBIV plb,LPWSTR lpstr);
  15. /***************************************************************************\
  16. *
  17. * SetLBScrollParms()
  18. *
  19. * Sets the scroll range, page, and position
  20. *
  21. \***************************************************************************/
  22. int xxxSetLBScrollParms(PLBIV plb, int nCtl)
  23. {
  24. int iPos;
  25. int cItems;
  26. UINT iPage;
  27. SCROLLINFO si;
  28. BOOL fNoScroll = FALSE;
  29. PSCROLLPOS psp;
  30. BOOL fCacheInitialized;
  31. int iReturn;
  32. if (nCtl == SB_VERT) {
  33. iPos = plb->iTop;
  34. cItems = plb->cMac;
  35. iPage = plb->cItemFullMax;
  36. if (!plb->fVertBar)
  37. fNoScroll = TRUE;
  38. psp = &plb->VPos;
  39. fCacheInitialized = plb->fVertInitialized;
  40. } else {
  41. if (plb->fMultiColumn) {
  42. iPos = plb->iTop / plb->itemsPerColumn;
  43. cItems = plb->cMac ? ((plb->cMac - 1) / plb->itemsPerColumn) + 1 : 0;
  44. iPage = plb->numberOfColumns;
  45. if (plb->fRightAlign && cItems)
  46. iPos = cItems - iPos - 1;
  47. } else {
  48. iPos = plb->xOrigin;
  49. cItems = plb->maxWidth;
  50. iPage = plb->spwnd->rcClient.right - plb->spwnd->rcClient.left;
  51. }
  52. if (!plb->fHorzBar)
  53. fNoScroll = TRUE;
  54. psp = &plb->HPos;
  55. fCacheInitialized = plb->fHorzInitialized;
  56. }
  57. if (cItems)
  58. cItems--;
  59. if (fNoScroll) {
  60. // Limit page to 0, posMax + 1
  61. iPage = max(min((int)iPage, cItems + 1), 0);
  62. // Limit pos to 0, posMax - (page - 1).
  63. return(max(min(iPos, cItems - ((iPage) ? (int)(iPage - 1) : 0)), 0));
  64. } else {
  65. si.fMask = SIF_ALL;
  66. if (plb->fDisableNoScroll)
  67. si.fMask |= SIF_DISABLENOSCROLL;
  68. /*
  69. * If the scrollbar is already where we want it, do nothing.
  70. */
  71. if (fCacheInitialized) {
  72. if (psp->fMask == si.fMask &&
  73. psp->cItems == cItems && psp->iPage == iPage &&
  74. psp->iPos == iPos)
  75. return psp->iReturn;
  76. } else if (nCtl == SB_VERT) {
  77. plb->fVertInitialized = TRUE;
  78. } else {
  79. plb->fHorzInitialized = TRUE;
  80. }
  81. si.cbSize = sizeof(SCROLLINFO);
  82. si.nMin = 0;
  83. si.nMax = cItems;
  84. si.nPage = iPage;
  85. if (plb->fMultiColumn && plb->fRightAlign)
  86. si.nPos = (iPos+1) > (int)iPage ? iPos - iPage + 1 : 0;
  87. else
  88. si.nPos = iPos;
  89. iReturn = SetScrollInfo(HWq(plb->spwnd), nCtl, &si, plb->fRedraw);
  90. if (plb->fMultiColumn && plb->fRightAlign)
  91. iReturn = cItems - (iReturn + iPage - 1);
  92. /*
  93. * Update the position cache
  94. */
  95. psp->fMask = si.fMask;
  96. psp->cItems = cItems;
  97. psp->iPage = iPage;
  98. psp->iPos = iPos;
  99. psp->iReturn = iReturn;
  100. return iReturn;
  101. }
  102. }
  103. /***************************************************************************\
  104. * xxxLBShowHideScrollBars
  105. *
  106. * History:
  107. \***************************************************************************/
  108. void xxxLBShowHideScrollBars(
  109. PLBIV plb)
  110. {
  111. BOOL fVertDone = FALSE;
  112. BOOL fHorzDone = FALSE;
  113. // Don't do anything if there are no scrollbars or if parents
  114. // are invisible.
  115. if ((!plb->fHorzBar && !plb->fVertBar) || !plb->fRedraw)
  116. return;
  117. //
  118. // Adjust iTop if necessary but DO NOT REDRAW PERIOD. We never did
  119. // in 3.1. There's a potential bug:
  120. // If someone doesn't have redraw off and inserts an item in the
  121. // same position as the caret, we'll tell them to draw before they may
  122. // have called LB_SETITEMDATA for their item. This is because we turn
  123. // the caret off & on inside of NewITop(), even if the item isn't
  124. // changing.
  125. // So we just want to reflect the position/scroll changes.
  126. // CheckRedraw() will _really_ redraw the visual changes later if
  127. // redraw isn't off.
  128. //
  129. if (!plb->fFromInsert) {
  130. xxxNewITop(plb, plb->iTop);
  131. fVertDone = TRUE;
  132. }
  133. if (!plb->fMultiColumn) {
  134. if (!plb->fFromInsert) {
  135. fHorzDone = TRUE;
  136. xxxLBoxCtlHScroll(plb, SB_THUMBPOSITION, plb->xOrigin);
  137. }
  138. if (!fVertDone)
  139. xxxSetLBScrollParms(plb, SB_VERT);
  140. }
  141. if (!fHorzDone)
  142. xxxSetLBScrollParms(plb, SB_HORZ);
  143. }
  144. /***************************************************************************\
  145. * LBGetItemData
  146. *
  147. * returns the long value associated with listbox items. -1 if error
  148. *
  149. * History:
  150. * 16-Apr-1992 beng The NODATA listbox case
  151. \***************************************************************************/
  152. LONG_PTR LBGetItemData(
  153. PLBIV plb,
  154. INT sItem)
  155. {
  156. LONG_PTR buffer;
  157. LPBYTE lpItem;
  158. if (sItem < 0 || sItem >= plb->cMac) {
  159. RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
  160. return LB_ERR;
  161. }
  162. // No-data listboxes always return 0L
  163. //
  164. if (!plb->fHasData) {
  165. return 0L;
  166. }
  167. lpItem = (plb->rgpch +
  168. (sItem * (plb->fHasStrings ? sizeof(LBItem) : sizeof(LBODItem))));
  169. buffer = (plb->fHasStrings ? ((lpLBItem)lpItem)->itemData : ((lpLBODItem)lpItem)->itemData);
  170. return buffer;
  171. }
  172. /***************************************************************************\
  173. * LBGetText
  174. *
  175. * Copies the text associated with index to lpbuffer and returns its length.
  176. * If fLengthOnly, just return the length of the text without doing a copy.
  177. *
  178. * Waring: for size only querries lpbuffer is the count of ANSI characters
  179. *
  180. * Returns count of chars
  181. *
  182. * History:
  183. \***************************************************************************/
  184. INT LBGetText(
  185. PLBIV plb,
  186. BOOL fLengthOnly,
  187. BOOL fAnsi,
  188. INT index,
  189. LPWSTR lpbuffer)
  190. {
  191. LPWSTR lpItemText;
  192. INT cchText;
  193. if (index < 0 || index >= plb->cMac) {
  194. RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
  195. return LB_ERR;
  196. }
  197. if (!plb->fHasStrings && plb->OwnerDraw) {
  198. /*
  199. * Owner draw without strings so we must copy the app supplied DWORD
  200. * value.
  201. */
  202. cchText = sizeof(ULONG_PTR);
  203. if (!fLengthOnly) {
  204. LONG_PTR UNALIGNED *p = (LONG_PTR UNALIGNED *)lpbuffer;
  205. *p = LBGetItemData(plb, index);
  206. }
  207. } else {
  208. lpItemText = GetLpszItem(plb, index);
  209. if (!lpItemText)
  210. return LB_ERR;
  211. /*
  212. * These are strings so we are copying the text and we must include
  213. * the terminating 0 when doing the RtlMoveMemory.
  214. */
  215. cchText = wcslen(lpItemText);
  216. if (fLengthOnly) {
  217. if (fAnsi)
  218. RtlUnicodeToMultiByteSize(&cchText, lpItemText, cchText*sizeof(WCHAR));
  219. } else {
  220. if (fAnsi) {
  221. #ifdef FE_SB // LBGetText()
  222. cchText = WCSToMB(lpItemText, cchText+1, &((LPSTR)lpbuffer), (cchText+1)*sizeof(WORD), FALSE);
  223. /*
  224. * Here.. cchText contains null-terminate char, subtract it... Because, we pass cchText+1 to
  225. * above Unicode->Ansi convertsion to make sure the string is terminated with null.
  226. */
  227. cchText--;
  228. #else
  229. WCSToMB(lpItemText, cchText+1, &((LPSTR)lpbuffer), cchText+1, FALSE);
  230. #endif // FE_SB
  231. } else {
  232. RtlCopyMemory(lpbuffer, lpItemText, (cchText+1)*sizeof(WCHAR));
  233. }
  234. }
  235. }
  236. return cchText;
  237. }
  238. /***************************************************************************\
  239. * GrowMem
  240. *
  241. * History:
  242. * 16-Apr-1992 beng NODATA listboxes
  243. * 23-Jul-1996 jparsons Added numItems parameter for LB_INITSTORAGE support
  244. \***************************************************************************/
  245. BOOL GrowMem(
  246. PLBIV plb,
  247. INT numItems)
  248. {
  249. LONG cb;
  250. HANDLE hMem;
  251. /*
  252. * Allocate memory for pointers to the strings.
  253. */
  254. cb = (plb->cMax + numItems) *
  255. (plb->fHasStrings ? sizeof(LBItem)
  256. : (plb->fHasData ? sizeof(LBODItem)
  257. : 0));
  258. /*
  259. * If multiple selection list box (MULTIPLESEL or EXTENDEDSEL), then
  260. * allocate an extra byte per item to keep track of it's selection state.
  261. */
  262. if (plb->wMultiple != SINGLESEL) {
  263. cb += (plb->cMax + numItems);
  264. }
  265. /*
  266. * Extra bytes for each item so that we can store its height.
  267. */
  268. if (plb->OwnerDraw == OWNERDRAWVAR) {
  269. cb += (plb->cMax + numItems);
  270. }
  271. /*
  272. * Don't allocate more than 2G of memory
  273. */
  274. if (cb > MAXLONG)
  275. return FALSE;
  276. if (plb->rgpch == NULL) {
  277. if ((plb->rgpch = UserLocalAlloc(HEAP_ZERO_MEMORY, (LONG)cb)) == NULL)
  278. return FALSE;
  279. } else {
  280. if ((hMem = UserLocalReAlloc(plb->rgpch, (LONG)cb, HEAP_ZERO_MEMORY)) == NULL)
  281. return FALSE;
  282. plb->rgpch = hMem;
  283. }
  284. plb->cMax += numItems;
  285. return TRUE;
  286. }
  287. /***************************************************************************\
  288. * xxxLBInitStorage
  289. *
  290. * History:
  291. * 23-Jul-1996 jparsons Added support for pre-allocation
  292. \***************************************************************************/
  293. LONG xxxLBInitStorage(PLBIV plb, BOOL fAnsi, INT cItems, INT cb)
  294. {
  295. HANDLE hMem;
  296. INT cbChunk;
  297. /*
  298. * if the app is talking ANSI, then adjust for the worst case in unicode
  299. * where each single ansi byte translates to one 16 bit unicode value
  300. */
  301. if (fAnsi) {
  302. cb *= sizeof(WCHAR) ;
  303. } /* if */
  304. /*
  305. * Fail if either of the parameters look bad.
  306. */
  307. if ((cItems < 0) || (cb < 0)) {
  308. xxxNotifyOwner(plb, LBN_ERRSPACE);
  309. return LB_ERRSPACE;
  310. } /* if */
  311. /*
  312. * try to grow the pointer array (if necessary) accounting for the free space
  313. * already available.
  314. */
  315. cItems -= plb->cMax - plb->cMac ;
  316. if ((cItems > 0) && !GrowMem(plb, cItems)) {
  317. xxxNotifyOwner(plb, LBN_ERRSPACE);
  318. return LB_ERRSPACE;
  319. } /* if */
  320. /*
  321. * now grow the string space if necessary
  322. */
  323. if (plb->fHasStrings) {
  324. if ((cbChunk = (plb->ichAlloc + cb)) > plb->cchStrings) {
  325. /*
  326. * Round up to the nearest 256 byte chunk.
  327. */
  328. cbChunk = (cbChunk & ~0xff) + 0x100;
  329. if (!(hMem = UserLocalReAlloc(plb->hStrings, (LONG)cbChunk, 0))) {
  330. xxxNotifyOwner(plb, LBN_ERRSPACE);
  331. return LB_ERRSPACE;
  332. }
  333. plb->hStrings = hMem;
  334. plb->cchStrings = cbChunk;
  335. } /* if */
  336. } /* if */
  337. /*
  338. * return the number of items that can be stored
  339. */
  340. return plb->cMax ;
  341. }
  342. /***************************************************************************\
  343. * xxxInsertString
  344. *
  345. * Insert an item at a specified position.
  346. *
  347. * History:
  348. * 16-Apr-1992 beng NODATA listboxes
  349. \***************************************************************************/
  350. INT xxxLBInsertItem(
  351. PLBIV plb,
  352. /*
  353. * For owner draw listboxes without LBS_HASSTRINGS style, this is not a
  354. * string but rather a 4 byte value we will store for the app.
  355. */
  356. LPWSTR lpsz,
  357. INT index,
  358. UINT wFlags)
  359. {
  360. MEASUREITEMSTRUCT measureItemStruct;
  361. INT cbString = 0;
  362. INT cbChunk;
  363. PBYTE lp;
  364. PBYTE lpT;
  365. PBYTE lpHeightStart;
  366. LONG cbItem; /* sizeof the Item in rgpch */
  367. HANDLE hMem;
  368. TL tlpwndParent;
  369. CheckLock(plb->spwnd);
  370. if (wFlags & LBI_ADD)
  371. index = (plb->fSort) ? xxxLBBinarySearchString(plb, lpsz) : -1;
  372. if (!plb->rgpch) {
  373. if (index != 0 && index != -1) {
  374. RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
  375. return LB_ERR;
  376. }
  377. plb->iSel = -1;
  378. plb->iSelBase = 0;
  379. plb->cMax = 0;
  380. plb->cMac = 0;
  381. plb->iTop = 0;
  382. plb->rgpch = UserLocalAlloc(HEAP_ZERO_MEMORY, 0L);
  383. if (!plb->rgpch)
  384. return LB_ERR;
  385. }
  386. if (index == -1) {
  387. index = plb->cMac;
  388. }
  389. if (index > plb->cMac || plb->cMac >= MAXLONG) {
  390. RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
  391. return LB_ERR;
  392. }
  393. if (plb->fHasStrings) {
  394. /*
  395. * we must store the string in the hStrings memory block.
  396. */
  397. cbString = (wcslen(lpsz) + 1)*sizeof(WCHAR); /* include 0 terminator */
  398. if ((cbChunk = (plb->ichAlloc + cbString)) > plb->cchStrings) {
  399. /*
  400. * Round up to the nearest 256 byte chunk.
  401. */
  402. cbChunk = (cbChunk & ~0xff) + 0x100;
  403. if (!(hMem = UserLocalReAlloc(plb->hStrings, (LONG)cbChunk,
  404. 0))) {
  405. xxxNotifyOwner(plb, LBN_ERRSPACE);
  406. return LB_ERRSPACE;
  407. }
  408. plb->hStrings = hMem;
  409. plb->cchStrings = cbChunk;
  410. }
  411. /*
  412. * Note difference between Win 95 code with placement of new string
  413. */
  414. if (wFlags & UPPERCASE)
  415. CharUpperBuffW((LPWSTR)lpsz, cbString / sizeof(WCHAR));
  416. else if (wFlags & LOWERCASE)
  417. CharLowerBuffW((LPWSTR)lpsz, cbString / sizeof(WCHAR));
  418. lp = (PBYTE)(plb->hStrings);
  419. RtlMoveMemory(lp + plb->ichAlloc, lpsz, cbString);
  420. }
  421. /*
  422. * Now expand the pointer array.
  423. */
  424. if (plb->cMac >= plb->cMax) {
  425. if (!GrowMem(plb, CITEMSALLOC)) {
  426. xxxNotifyOwner(plb, LBN_ERRSPACE);
  427. return LB_ERRSPACE;
  428. }
  429. }
  430. lpHeightStart = lpT = lp = plb->rgpch;
  431. /*
  432. * Now calculate how much room we must make for the string pointer (lpsz).
  433. * If we are ownerdraw without LBS_HASSTRINGS, then a single DWORD
  434. * (LBODItem.itemData) stored for each item, but if we have strings with
  435. * each item then a LONG string offset (LBItem.offsz) is also stored.
  436. */
  437. cbItem = (plb->fHasStrings ? sizeof(LBItem)
  438. : (plb->fHasData ? sizeof(LBODItem):0));
  439. cbChunk = (plb->cMac - index) * cbItem;
  440. if (plb->wMultiple != SINGLESEL) {
  441. /*
  442. * Extra bytes were allocated for selection flag for each item
  443. */
  444. cbChunk += plb->cMac;
  445. }
  446. if (plb->OwnerDraw == OWNERDRAWVAR) {
  447. /*
  448. * Extra bytes were allocated for each item's height
  449. */
  450. cbChunk += plb->cMac;
  451. }
  452. /*
  453. * First, make room for the 2 byte pointer to the string or the 4 byte app
  454. * supplied value.
  455. */
  456. lpT += (index * cbItem);
  457. RtlMoveMemory(lpT + cbItem, lpT, cbChunk);
  458. if (!plb->fHasStrings && plb->OwnerDraw) {
  459. if (plb->fHasData) {
  460. /*
  461. * Ownerdraw so just save the DWORD value
  462. */
  463. lpLBODItem p = (lpLBODItem)lpT;
  464. p->itemData = (ULONG_PTR)lpsz;
  465. }
  466. } else {
  467. lpLBItem p = ((lpLBItem)lpT);
  468. /*
  469. * Save the start of the string. Let the item data field be 0
  470. */
  471. p->offsz = (LONG)(plb->ichAlloc);
  472. p->itemData = 0;
  473. plb->ichAlloc += cbString;
  474. }
  475. /*
  476. * Now if Multiple Selection lbox, we have to insert a selection status
  477. * byte. If var height ownerdraw, then we also have to move up the height
  478. * bytes.
  479. */
  480. if (plb->wMultiple != SINGLESEL) {
  481. lpT = lp + ((plb->cMac + 1) * cbItem) + index;
  482. RtlMoveMemory(lpT + 1, lpT, plb->cMac - index +
  483. (plb->OwnerDraw == OWNERDRAWVAR ? plb->cMac : 0));
  484. *lpT = 0; /* fSelected = FALSE */
  485. }
  486. /*
  487. * Increment count of items in the listbox now before we send a message to
  488. * the app.
  489. */
  490. plb->cMac++;
  491. /*
  492. * If varheight ownerdraw, we much insert an extra byte for the item's
  493. * height.
  494. */
  495. if (plb->OwnerDraw == OWNERDRAWVAR) {
  496. /*
  497. * Variable height owner draw so we need to get the height of each item.
  498. */
  499. lpHeightStart += (plb->cMac * cbItem) + index +
  500. (plb->wMultiple ? plb->cMac : 0);
  501. RtlMoveMemory(lpHeightStart + 1, lpHeightStart, plb->cMac - 1 - index);
  502. /*
  503. * Query for item height only if we are var height owner draw.
  504. */
  505. measureItemStruct.CtlType = ODT_LISTBOX;
  506. measureItemStruct.CtlID = PtrToUlong(plb->spwnd->spmenu);
  507. measureItemStruct.itemID = index;
  508. /*
  509. * System font height is default height
  510. */
  511. measureItemStruct.itemHeight = (UINT)gpsi->cySysFontChar;
  512. measureItemStruct.itemData = (ULONG_PTR)lpsz;
  513. /*
  514. * If "has strings" then add the special thunk bit so the client data
  515. * will be thunked to a client side address. LB_DIR sends a string
  516. * even if the listbox is not HASSTRINGS so we need to special
  517. * thunk this case. HP Dashboard for windows send LB_DIR to a non
  518. * HASSTRINGS listbox needs the server string converted to client.
  519. * WOW needs to know about this situation as well so we mark the
  520. * previously uninitialized itemWidth as FLAT.
  521. */
  522. if (plb->fHasStrings || (wFlags & MSGFLAG_SPECIAL_THUNK)) {
  523. measureItemStruct.itemWidth = MIFLAG_FLAT;
  524. }
  525. ThreadLock(plb->spwndParent, &tlpwndParent);
  526. SendMessage(HW(plb->spwndParent),
  527. WM_MEASUREITEM,
  528. measureItemStruct.CtlID,
  529. (LPARAM)&measureItemStruct);
  530. ThreadUnlock(&tlpwndParent);
  531. *lpHeightStart = (BYTE)measureItemStruct.itemHeight;
  532. }
  533. /*
  534. * If the item was inserted above the current selection then move
  535. * the selection down one as well.
  536. */
  537. if ((plb->wMultiple == SINGLESEL) && (plb->iSel >= index))
  538. plb->iSel++;
  539. if (plb->OwnerDraw == OWNERDRAWVAR)
  540. LBSetCItemFullMax(plb);
  541. /*
  542. * Check if scroll bars need to be shown/hidden
  543. */
  544. plb->fFromInsert = TRUE;
  545. xxxLBShowHideScrollBars(plb);
  546. if (plb->fHorzBar && plb->fRightAlign && !(plb->fMultiColumn || plb->OwnerDraw)) {
  547. /*
  548. * origin to right
  549. */
  550. xxxLBoxCtlHScroll(plb, SB_BOTTOM, 0);
  551. }
  552. plb->fFromInsert = FALSE;
  553. xxxCheckRedraw(plb, TRUE, index);
  554. LBEvent(plb, EVENT_OBJECT_CREATE, index);
  555. return index;
  556. }
  557. /***************************************************************************\
  558. * LBlstrcmpi
  559. *
  560. * This is a version of lstrcmpi() specifically used for listboxes
  561. * This gives more weight to '[' characters than alpha-numerics;
  562. * The US version of lstrcmpi() and lstrcmp() are similar as far as
  563. * non-alphanumerals are concerned; All non-alphanumerals get sorted
  564. * before alphanumerals; This means that subdirectory strings that start
  565. * with '[' will get sorted before; But we don't want that; So, this
  566. * function takes care of it;
  567. *
  568. * History:
  569. \***************************************************************************/
  570. INT LBlstrcmpi(
  571. LPWSTR lpStr1,
  572. LPWSTR lpStr2,
  573. DWORD dwLocaleId)
  574. {
  575. /*
  576. * NOTE: This function is written so as to reduce the number of calls
  577. * made to the costly IsCharAlphaNumeric() function because that might
  578. * load a language module; It 'traps' the most frequently occurring cases
  579. * like both strings starting with '[' or both strings NOT starting with '['
  580. * first and only in abosolutely necessary cases calls IsCharAlphaNumeric();
  581. */
  582. if (*lpStr1 == TEXT('[')) {
  583. if (*lpStr2 == TEXT('[')) {
  584. goto LBL_End;
  585. }
  586. if (IsCharAlphaNumeric(*lpStr2)) {
  587. return 1;
  588. }
  589. }
  590. if ((*lpStr2 == TEXT('[')) && IsCharAlphaNumeric(*lpStr1)) {
  591. return -1;
  592. }
  593. LBL_End:
  594. if ((GetClientInfo()->dwTIFlags & TIF_16BIT) &&
  595. dwLocaleId == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)) {
  596. /*
  597. * This is how Windows95 does, bug #4199
  598. */
  599. return (*pfnWowIlstrcmp)(lpStr1, lpStr2);
  600. }
  601. return (INT)CompareStringW((LCID)dwLocaleId, NORM_IGNORECASE,
  602. lpStr1, -1, lpStr2, -1 ) - 2;
  603. }
  604. /***************************************************************************\
  605. * xxxLBBinarySearchString
  606. *
  607. * Does a binary search of the items in the SORTED listbox to find
  608. * out where this item should be inserted. Handles both HasStrings and item
  609. * long WM_COMPAREITEM cases.
  610. *
  611. * History:
  612. * 27 April 1992 GregoryW
  613. * Modified to support sorting based on current list box locale.
  614. \***************************************************************************/
  615. INT xxxLBBinarySearchString(
  616. PLBIV plb,
  617. LPWSTR lpstr)
  618. {
  619. BYTE *FAR *lprgpch;
  620. INT sortResult;
  621. COMPAREITEMSTRUCT cis;
  622. LPWSTR pszLBBase;
  623. LPWSTR pszLB;
  624. INT itemhigh;
  625. INT itemnew = 0;
  626. INT itemlow = 0;
  627. TL tlpwndParent;
  628. CheckLock(plb->spwnd);
  629. if (!plb->cMac)
  630. return 0;
  631. lprgpch = (BYTE *FAR *)(plb->rgpch);
  632. if (plb->fHasStrings) {
  633. pszLBBase = plb->hStrings;
  634. }
  635. itemhigh = plb->cMac - 1;
  636. while (itemlow <= itemhigh) {
  637. itemnew = (itemhigh + itemlow) / 2;
  638. if (plb->fHasStrings) {
  639. /*
  640. * Searching for string matches.
  641. */
  642. pszLB = (LPWSTR)((LPSTR)pszLBBase + ((lpLBItem)lprgpch)[itemnew].offsz);
  643. sortResult = LBlstrcmpi(pszLB, lpstr, plb->dwLocaleId);
  644. } else {
  645. /*
  646. * Send compare item messages to the parent for sorting
  647. */
  648. cis.CtlType = ODT_LISTBOX;
  649. cis.CtlID = PtrToUlong(plb->spwnd->spmenu);
  650. cis.hwndItem = HWq(plb->spwnd);
  651. cis.itemID1 = itemnew;
  652. cis.itemData1 = ((lpLBODItem)lprgpch)[itemnew].itemData;
  653. cis.itemID2 = (UINT)-1;
  654. cis.itemData2 = (ULONG_PTR)lpstr;
  655. cis.dwLocaleId = plb->dwLocaleId;
  656. ThreadLock(plb->spwndParent, &tlpwndParent);
  657. sortResult = (INT)SendMessage(HW(plb->spwndParent), WM_COMPAREITEM,
  658. cis.CtlID, (LPARAM)&cis);
  659. ThreadUnlock(&tlpwndParent);
  660. }
  661. if (sortResult < 0) {
  662. itemlow = itemnew + 1;
  663. } else if (sortResult > 0) {
  664. itemhigh = itemnew - 1;
  665. } else {
  666. itemlow = itemnew;
  667. goto FoundIt;
  668. }
  669. }
  670. FoundIt:
  671. return max(0, itemlow);
  672. }
  673. /***************************************************************************\
  674. * xxxLBResetContent
  675. *
  676. * History:
  677. \***************************************************************************/
  678. BOOL xxxLBResetContent(
  679. PLBIV plb)
  680. {
  681. if (!plb->cMac)
  682. return TRUE;
  683. xxxLBoxDoDeleteItems(plb);
  684. if (plb->rgpch != NULL) {
  685. UserLocalFree(plb->rgpch);
  686. plb->rgpch = NULL;
  687. }
  688. if (plb->hStrings != NULL) {
  689. UserLocalFree(plb->hStrings);
  690. plb->hStrings = NULL;
  691. }
  692. InitHStrings(plb);
  693. if (TestWF(plb->spwnd, WFWIN31COMPAT))
  694. xxxCheckRedraw(plb, FALSE, 0);
  695. else if (IsVisible(plb->spwnd))
  696. NtUserInvalidateRect(HWq(plb->spwnd), NULL, TRUE);
  697. plb->iSelBase = 0;
  698. plb->iTop = 0;
  699. plb->cMac = 0;
  700. plb->cMax = 0;
  701. plb->xOrigin = 0;
  702. plb->iLastSelection = 0;
  703. plb->iSel = -1;
  704. xxxLBShowHideScrollBars(plb);
  705. return TRUE;
  706. }
  707. /***************************************************************************\
  708. * xxxLBoxCtlDelete
  709. *
  710. * History:
  711. * 16-Apr-1992 beng NODATA listboxes
  712. \***************************************************************************/
  713. INT xxxLBoxCtlDelete(
  714. PLBIV plb,
  715. INT sItem) /* Item number to delete */
  716. {
  717. LONG cb;
  718. LPBYTE lp;
  719. LPBYTE lpT;
  720. RECT rc;
  721. int cbItem; /* size of Item in rgpch */
  722. LPWSTR lpString;
  723. PBYTE pbStrings;
  724. INT cbStringLen;
  725. LPBYTE itemNumbers;
  726. INT sTmp;
  727. TL tlpwnd;
  728. CheckLock(plb->spwnd);
  729. if (sItem < 0 || sItem >= plb->cMac) {
  730. RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, "");
  731. return LB_ERR;
  732. }
  733. LBEvent(plb, EVENT_OBJECT_DESTROY, sItem);
  734. if (plb->cMac == 1) {
  735. /*
  736. * When the item count is 0, we send a resetcontent message so that we
  737. * can reclaim our string space this way.
  738. */
  739. SendMessageWorker(plb->spwnd, LB_RESETCONTENT, 0, 0, FALSE);
  740. goto FinishUpDelete;
  741. }
  742. /*
  743. * Get the rectangle associated with the last item in the listbox. If it is
  744. * visible, we need to invalidate it. When we delete an item, everything
  745. * scrolls up to replace the item deleted so we must make sure we erase the
  746. * old image of the last item in the listbox.
  747. */
  748. if (LBGetItemRect(plb, (INT)(plb->cMac - 1), &rc)) {
  749. xxxLBInvalidateRect(plb, &rc, TRUE);
  750. }
  751. // 3.1 and earlier used to only send WM_DELETEITEMs if it was an ownerdraw
  752. // listbox. 4.0 and above will send WM_DELETEITEMs for every item that has
  753. // nonzero item data.
  754. if (TestWF(plb->spwnd, WFWIN40COMPAT) || (plb->OwnerDraw && plb->fHasData)) {
  755. xxxLBoxDeleteItem(plb, sItem);
  756. }
  757. plb->cMac--;
  758. cbItem = (plb->fHasStrings ? sizeof(LBItem)
  759. : (plb->fHasData ? sizeof(LBODItem): 0));
  760. cb = ((plb->cMac - sItem) * cbItem);
  761. /*
  762. * Byte for the selection status of the item.
  763. */
  764. if (plb->wMultiple != SINGLESEL) {
  765. cb += (plb->cMac + 1);
  766. }
  767. if (plb->OwnerDraw == OWNERDRAWVAR) {
  768. /*
  769. * One byte for the height of the item.
  770. */
  771. cb += (plb->cMac + 1);
  772. }
  773. /*
  774. * Might be nodata and singlesel, for instance.
  775. * but what out for the case where cItem == cMac (and cb == 0).
  776. */
  777. if ((cb != 0) || plb->fHasStrings) {
  778. lp = plb->rgpch;
  779. lpT = (lp + (sItem * cbItem));
  780. if (plb->fHasStrings) {
  781. /*
  782. * If we has strings with each item, then we want to compact the string
  783. * heap so that we can recover the space occupied by the string of the
  784. * deleted item.
  785. */
  786. /*
  787. * Get the string which we will be deleting
  788. */
  789. pbStrings = (PBYTE)(plb->hStrings);
  790. lpString = (LPTSTR)(pbStrings + ((lpLBItem)lpT)->offsz);
  791. cbStringLen = (wcslen(lpString) + 1) * sizeof(WCHAR); /* include null terminator */
  792. /*
  793. * Now compact the string array
  794. */
  795. plb->ichAlloc = plb->ichAlloc - cbStringLen;
  796. RtlMoveMemory(lpString, (PBYTE)lpString + cbStringLen,
  797. plb->ichAlloc + (pbStrings - (LPBYTE)lpString));
  798. /*
  799. * We have to update the string pointers in plb->rgpch since all the
  800. * string after the deleted string have been moved down stringLength
  801. * bytes. Note that we have to explicitly check all items in the list
  802. * box if the string was allocated after the deleted item since the
  803. * LB_SORT style allows a lower item number to have a string allocated
  804. * at the end of the string heap for example.
  805. */
  806. itemNumbers = lp;
  807. for (sTmp = 0; sTmp <= plb->cMac; sTmp++) {
  808. lpLBItem p =(lpLBItem)itemNumbers;
  809. if ( (LPTSTR)(p->offsz + pbStrings) > lpString ) {
  810. p->offsz -= cbStringLen;
  811. }
  812. p++;
  813. itemNumbers=(LPBYTE)p;
  814. }
  815. }
  816. /*
  817. * Now compact the pointers to the strings (or the long app supplied values
  818. * if ownerdraw without strings).
  819. */
  820. RtlMoveMemory(lpT, lpT + cbItem, cb);
  821. /*
  822. * Compress the multiselection bytes
  823. */
  824. if (plb->wMultiple != SINGLESEL) {
  825. lpT = (lp + (plb->cMac * cbItem) + sItem);
  826. RtlMoveMemory(lpT, lpT + 1, plb->cMac - sItem +
  827. (plb->OwnerDraw == OWNERDRAWVAR ? plb->cMac + 1 : 0));
  828. }
  829. if (plb->OwnerDraw == OWNERDRAWVAR) {
  830. /*
  831. * Compress the height bytes
  832. */
  833. lpT = (lp + (plb->cMac * cbItem) + (plb->wMultiple ? plb->cMac : 0)
  834. + sItem);
  835. RtlMoveMemory(lpT, lpT + 1, plb->cMac - sItem);
  836. }
  837. }
  838. if (plb->wMultiple == SINGLESEL) {
  839. if (plb->iSel == sItem) {
  840. plb->iSel = -1;
  841. if (plb->pcbox != NULL) {
  842. ThreadLock(plb->pcbox->spwnd, &tlpwnd);
  843. xxxCBInternalUpdateEditWindow(plb->pcbox, NULL);
  844. ThreadUnlock(&tlpwnd);
  845. }
  846. } else if (plb->iSel > sItem)
  847. plb->iSel--;
  848. }
  849. if ((plb->iMouseDown != -1) && (sItem <= plb->iMouseDown))
  850. plb->iMouseDown = -1;
  851. if (plb->iSelBase && sItem == plb->iSelBase)
  852. plb->iSelBase--;
  853. if (plb->cMac) {
  854. plb->iSelBase = min(plb->iSelBase, plb->cMac - 1);
  855. } else {
  856. plb->iSelBase = 0;
  857. }
  858. if ((plb->wMultiple == EXTENDEDSEL) && (plb->iSel == -1))
  859. plb->iSel = plb->iSelBase;
  860. if (plb->OwnerDraw == OWNERDRAWVAR)
  861. LBSetCItemFullMax(plb);
  862. /*
  863. * We always set a new iTop. The iTop won't change if it doesn't need to
  864. * but it will change if: 1. The iTop was deleted or 2. We need to change
  865. * the iTop so that we fill the listbox.
  866. */
  867. xxxInsureVisible(plb, plb->iTop, FALSE);
  868. FinishUpDelete:
  869. /*
  870. * Check if scroll bars need to be shown/hidden
  871. */
  872. plb->fFromInsert = TRUE;
  873. xxxLBShowHideScrollBars(plb);
  874. plb->fFromInsert = FALSE;
  875. xxxCheckRedraw(plb, TRUE, sItem);
  876. xxxInsureVisible(plb, plb->iSelBase, FALSE);
  877. return plb->cMac;
  878. }
  879. /***************************************************************************\
  880. * xxxLBoxDeleteItem
  881. *
  882. * Sends a WM_DELETEITEM message to the owner of an ownerdraw listbox
  883. *
  884. * History:
  885. \***************************************************************************/
  886. void xxxLBoxDeleteItem(
  887. PLBIV plb,
  888. INT sItem)
  889. {
  890. DELETEITEMSTRUCT dis;
  891. TL tlpwndParent;
  892. CheckLock(plb->spwnd);
  893. if (plb->spwnd == NULL)
  894. return;
  895. /*
  896. * Bug 262122 - joejo
  897. * No need to send message if no data!
  898. */
  899. if (!plb->fHasData) {
  900. return;
  901. }
  902. /*
  903. * Fill the DELETEITEMSTRUCT
  904. */
  905. dis.CtlType = ODT_LISTBOX;
  906. dis.CtlID = PtrToUlong(plb->spwnd->spmenu);
  907. dis.itemID = sItem;
  908. dis.hwndItem = HWq(plb->spwnd);
  909. /*
  910. * Bug 262122 - joejo
  911. * Fixed in 93 so that ItemData was passed. For some reason, not
  912. * merged in.
  913. */
  914. dis.itemData = LBGetItemData(plb, sItem);
  915. if (plb->spwndParent != NULL) {
  916. ThreadLock(plb->spwndParent, &tlpwndParent);
  917. SendMessage(HWq(plb->spwndParent), WM_DELETEITEM, dis.CtlID,
  918. (LPARAM)&dis);
  919. ThreadUnlock(&tlpwndParent);
  920. }
  921. }
  922. /**************************************************************************\
  923. * xxxLBSetCount
  924. *
  925. * Sets the number of items in a lazy-eval (fNoData) listbox.
  926. *
  927. * Calling SetCount scorches any existing selection state. To preserve
  928. * selection state, call Insert/DeleteItem instead.
  929. *
  930. * History
  931. * 16-Apr-1992 beng Created
  932. \**************************************************************************/
  933. INT xxxLBSetCount(
  934. PLBIV plb,
  935. INT cItems)
  936. {
  937. UINT cbRequired;
  938. BOOL fRedraw;
  939. CheckLock(plb->spwnd);
  940. /*
  941. * SetCount is only valid on lazy-eval ("nodata") listboxes.
  942. * All other lboxen must add their items one at a time, although
  943. * they may SetCount(0) via RESETCONTENT.
  944. */
  945. if (plb->fHasStrings || plb->fHasData) {
  946. RIPERR0(ERROR_SETCOUNT_ON_BAD_LB, RIP_VERBOSE, "");
  947. return LB_ERR;
  948. }
  949. if (cItems == 0) {
  950. SendMessageWorker(plb->spwnd, LB_RESETCONTENT, 0, 0, FALSE);
  951. return 0;
  952. }
  953. // If redraw isn't turned off, turn it off now
  954. if (fRedraw = plb->fRedraw)
  955. xxxLBSetRedraw(plb, FALSE);
  956. cbRequired = LBCalcAllocNeeded(plb, cItems);
  957. /*
  958. * Reset selection and position
  959. */
  960. plb->iSelBase = 0;
  961. plb->iTop = 0;
  962. plb->cMax = 0;
  963. plb->xOrigin = 0;
  964. plb->iLastSelection = 0;
  965. plb->iSel = -1;
  966. if (cbRequired != 0) { // Only if record instance data required
  967. /*
  968. * If listbox was previously empty, prepare for the
  969. * realloc-based alloc strategy ahead.
  970. */
  971. if (plb->rgpch == NULL) {
  972. plb->rgpch = UserLocalAlloc(HEAP_ZERO_MEMORY, 0L);
  973. plb->cMax = 0;
  974. if (plb->rgpch == NULL) {
  975. xxxNotifyOwner(plb, LBN_ERRSPACE);
  976. return LB_ERRSPACE;
  977. }
  978. }
  979. /*
  980. * rgpch might not have enough room for the new record instance
  981. * data, so check and realloc as necessary.
  982. */
  983. if (cItems >= plb->cMax) {
  984. INT cMaxNew;
  985. UINT cbNew;
  986. HANDLE hmemNew;
  987. /*
  988. * Since GrowMem presumes a one-item-at-a-time add schema,
  989. * SetCount can't use it. Too bad.
  990. */
  991. cMaxNew = cItems+CITEMSALLOC;
  992. cbNew = LBCalcAllocNeeded(plb, cMaxNew);
  993. hmemNew = UserLocalReAlloc(plb->rgpch, cbNew, HEAP_ZERO_MEMORY);
  994. if (hmemNew == NULL) {
  995. xxxNotifyOwner(plb, LBN_ERRSPACE);
  996. return LB_ERRSPACE;
  997. }
  998. plb->rgpch = hmemNew;
  999. plb->cMax = cMaxNew;
  1000. }
  1001. /*
  1002. * Reset the item instance data (multisel annotations)
  1003. */
  1004. RtlZeroMemory(plb->rgpch, cbRequired);
  1005. }
  1006. plb->cMac = cItems;
  1007. // Turn redraw back on
  1008. if (fRedraw)
  1009. xxxLBSetRedraw(plb, TRUE);
  1010. xxxLBInvalidateRect(plb, NULL, TRUE);
  1011. // Not In Chicago -- FritzS
  1012. // NtUserSetScrollPos(plb->spwnd, SB_HORZ, 0, plb->fRedraw);
  1013. // NtUserSetScrollPos(plb->spwnd, SB_VERT, 0, plb->fRedraw);
  1014. xxxLBShowHideScrollBars(plb); // takes care of fRedraw
  1015. return 0;
  1016. }
  1017. /**************************************************************************\
  1018. * LBCalcAllocNeeded
  1019. *
  1020. * Calculate the number of bytes needed in rgpch to accommodate a given
  1021. * number of items.
  1022. *
  1023. * History
  1024. * 16-Apr-1992 beng Created
  1025. \**************************************************************************/
  1026. UINT LBCalcAllocNeeded(
  1027. PLBIV plb,
  1028. INT cItems)
  1029. {
  1030. UINT cb;
  1031. /*
  1032. * Allocate memory for pointers to the strings.
  1033. */
  1034. cb = cItems * (plb->fHasStrings ? sizeof(LBItem)
  1035. : (plb->fHasData ? sizeof(LBODItem)
  1036. : 0));
  1037. /*
  1038. * If multiple selection list box (MULTIPLESEL or EXTENDEDSEL), then
  1039. * allocate an extra byte per item to keep track of it's selection state.
  1040. */
  1041. if (plb->wMultiple != SINGLESEL) {
  1042. cb += cItems;
  1043. }
  1044. /*
  1045. * Extra bytes for each item so that we can store its height.
  1046. */
  1047. if (plb->OwnerDraw == OWNERDRAWVAR) {
  1048. cb += cItems;
  1049. }
  1050. return cb;
  1051. }