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.

1416 lines
36 KiB

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