Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

lboxctl1.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: lboxctl1.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * List Box Handling Routines 00007 * 00008 * History: 00009 * ??-???-???? ianja Ported from Win 3.0 sources 00010 * 14-Feb-1991 mikeke Added Revalidation code 00011 \***************************************************************************/ 00012 00013 #include "precomp.h" 00014 #pragma hdrstop 00015 00016 INT xxxLBBinarySearchString(PLBIV plb,LPWSTR lpstr); 00017 00018 /***************************************************************************\ 00019 * 00020 * SetLBScrollParms() 00021 * 00022 * Sets the scroll range, page, and position 00023 * 00024 \***************************************************************************/ 00025 00026 int xxxSetLBScrollParms(PLBIV plb, int nCtl) 00027 { 00028 int iPos; 00029 int cItems; 00030 UINT iPage; 00031 SCROLLINFO si; 00032 BOOL fNoScroll = FALSE; 00033 PSCROLLPOS psp; 00034 BOOL fCacheInitialized; 00035 int iReturn; 00036 00037 if (nCtl == SB_VERT) { 00038 iPos = plb->iTop; 00039 cItems = plb->cMac; 00040 iPage = plb->cItemFullMax; 00041 if (!plb->fVertBar) 00042 fNoScroll = TRUE; 00043 psp = &plb->VPos; 00044 fCacheInitialized = plb->fVertInitialized; 00045 } else { 00046 if (plb->fMultiColumn) { 00047 iPos = plb->iTop / plb->itemsPerColumn; 00048 cItems = plb->cMac ? ((plb->cMac - 1) / plb->itemsPerColumn) + 1 : 0; 00049 iPage = plb->numberOfColumns; 00050 if (plb->fRightAlign && cItems) 00051 iPos = cItems - iPos - 1; 00052 } else { 00053 iPos = plb->xOrigin; 00054 cItems = plb->maxWidth; 00055 iPage = plb->spwnd->rcClient.right - plb->spwnd->rcClient.left; 00056 } 00057 00058 if (!plb->fHorzBar) 00059 fNoScroll = TRUE; 00060 psp = &plb->HPos; 00061 fCacheInitialized = plb->fHorzInitialized; 00062 } 00063 00064 if (cItems) 00065 cItems--; 00066 00067 if (fNoScroll) { 00068 // Limit page to 0, posMax + 1 00069 iPage = max(min((int)iPage, cItems + 1), 0); 00070 00071 // Limit pos to 0, posMax - (page - 1). 00072 return(max(min(iPos, cItems - ((iPage) ? (int)(iPage - 1) : 0)), 0)); 00073 } else { 00074 si.fMask = SIF_ALL; 00075 if (plb->fDisableNoScroll) 00076 si.fMask |= SIF_DISABLENOSCROLL; 00077 00078 /* 00079 * If the scrollbar is already where we want it, do nothing. 00080 */ 00081 if (fCacheInitialized) { 00082 if (psp->fMask == si.fMask && 00083 psp->cItems == cItems && psp->iPage == iPage && 00084 psp->iPos == iPos) 00085 return psp->iReturn; 00086 } else if (nCtl == SB_VERT) { 00087 plb->fVertInitialized = TRUE; 00088 } else { 00089 plb->fHorzInitialized = TRUE; 00090 } 00091 00092 si.cbSize = sizeof(SCROLLINFO); 00093 si.nMin = 0; 00094 si.nMax = cItems; 00095 si.nPage = iPage; 00096 00097 if (plb->fMultiColumn && plb->fRightAlign) 00098 si.nPos = (iPos+1) > (int)iPage ? iPos - iPage + 1 : 0; 00099 else 00100 si.nPos = iPos; 00101 00102 iReturn = NtUserSetScrollInfo(HWq(plb->spwnd), nCtl, &si, plb->fRedraw); 00103 if (plb->fMultiColumn && plb->fRightAlign) 00104 iReturn = cItems - (iReturn + iPage - 1); 00105 00106 /* 00107 * Update the position cache 00108 */ 00109 psp->fMask = si.fMask; 00110 psp->cItems = cItems; 00111 psp->iPage = iPage; 00112 psp->iPos = iPos; 00113 psp->iReturn = iReturn; 00114 00115 return iReturn; 00116 } 00117 } 00118 00119 /***************************************************************************\ 00120 * xxxLBShowHideScrollBars 00121 * 00122 * History: 00123 \***************************************************************************/ 00124 00125 void xxxLBShowHideScrollBars( 00126 PLBIV plb) 00127 { 00128 BOOL fVertDone = FALSE; 00129 BOOL fHorzDone = FALSE; 00130 00131 // Don't do anything if there are no scrollbars or if parents 00132 // are invisible. 00133 if ((!plb->fHorzBar && !plb->fVertBar) || !plb->fRedraw) 00134 return; 00135 00136 // 00137 // Adjust iTop if necessary but DO NOT REDRAW PERIOD. We never did 00138 // in 3.1. There's a potential bug: 00139 // If someone doesn't have redraw off and inserts an item in the 00140 // same position as the caret, we'll tell them to draw before they may 00141 // have called LB_SETITEMDATA for their item. This is because we turn 00142 // the caret off & on inside of NewITop(), even if the item isn't 00143 // changing. 00144 // So we just want to reflect the position/scroll changes. 00145 // CheckRedraw() will _really_ redraw the visual changes later if 00146 // redraw isn't off. 00147 // 00148 00149 if (!plb->fFromInsert) { 00150 xxxNewITop(plb, plb->iTop); 00151 fVertDone = TRUE; 00152 } 00153 00154 if (!plb->fMultiColumn) { 00155 if (!plb->fFromInsert) { 00156 fHorzDone = TRUE; 00157 xxxLBoxCtlHScroll(plb, SB_THUMBPOSITION, plb->xOrigin); 00158 } 00159 00160 if (!fVertDone) 00161 xxxSetLBScrollParms(plb, SB_VERT); 00162 } 00163 if (!fHorzDone) 00164 xxxSetLBScrollParms(plb, SB_HORZ); 00165 } 00166 00167 /***************************************************************************\ 00168 * LBGetItemData 00169 * 00170 * returns the long value associated with listbox items. -1 if error 00171 * 00172 * History: 00173 * 16-Apr-1992 beng The NODATA listbox case 00174 \***************************************************************************/ 00175 00176 LONG_PTR LBGetItemData( 00177 PLBIV plb, 00178 INT sItem) 00179 { 00180 LONG_PTR buffer; 00181 LPBYTE lpItem; 00182 00183 if (sItem < 0 || sItem >= plb->cMac) { 00184 RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, ""); 00185 return LB_ERR; 00186 } 00187 00188 // No-data listboxes always return 0L 00189 // 00190 if (!plb->fHasData) { 00191 return 0L; 00192 } 00193 00194 lpItem = (plb->rgpch + 00195 (sItem * (plb->fHasStrings ? sizeof(LBItem) : sizeof(LBODItem)))); 00196 buffer = (plb->fHasStrings ? ((lpLBItem)lpItem)->itemData : ((lpLBODItem)lpItem)->itemData); 00197 return buffer; 00198 } 00199 00200 00201 /***************************************************************************\ 00202 * LBGetText 00203 * 00204 * Copies the text associated with index to lpbuffer and returns its length. 00205 * If fLengthOnly, just return the length of the text without doing a copy. 00206 * 00207 * Waring: for size only querries lpbuffer is the count of ANSI characters 00208 * 00209 * Returns count of chars 00210 * 00211 * History: 00212 \***************************************************************************/ 00213 00214 INT LBGetText( 00215 PLBIV plb, 00216 BOOL fLengthOnly, 00217 BOOL fAnsi, 00218 INT index, 00219 LPWSTR lpbuffer) 00220 { 00221 LPWSTR lpItemText; 00222 INT cchText; 00223 00224 if (index < 0 || index >= plb->cMac) { 00225 RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, ""); 00226 return LB_ERR; 00227 } 00228 00229 if (!plb->fHasStrings && plb->OwnerDraw) { 00230 00231 /* 00232 * Owner draw without strings so we must copy the app supplied DWORD 00233 * value. 00234 */ 00235 cchText = sizeof(ULONG_PTR); 00236 00237 if (!fLengthOnly) { 00238 LONG_PTR UNALIGNED *p = (LONG_PTR UNALIGNED *)lpbuffer; 00239 *p = LBGetItemData(plb, index); 00240 } 00241 } else { 00242 lpItemText = GetLpszItem(plb, index); 00243 if (!lpItemText) 00244 return LB_ERR; 00245 00246 /* 00247 * These are strings so we are copying the text and we must include 00248 * the terminating 0 when doing the RtlMoveMemory. 00249 */ 00250 cchText = wcslen(lpItemText); 00251 00252 if (fLengthOnly) { 00253 if (fAnsi) 00254 RtlUnicodeToMultiByteSize(&cchText, lpItemText, cchText*sizeof(WCHAR)); 00255 } else { 00256 if (fAnsi) { 00257 #ifdef FE_SB // LBGetText() 00258 cchText = WCSToMB(lpItemText, cchText+1, &((LPSTR)lpbuffer), (cchText+1)*sizeof(WORD), FALSE); 00259 /* 00260 * Here.. cchText contains null-terminate char, subtract it... Because, we pass cchText+1 to 00261 * above Unicode->Ansi convertsion to make sure the string is terminated with null. 00262 */ 00263 cchText--; 00264 #else 00265 WCSToMB(lpItemText, cchText+1, &((LPSTR)lpbuffer), cchText+1, FALSE); 00266 #endif // FE_SB 00267 } else { 00268 RtlCopyMemory(lpbuffer, lpItemText, (cchText+1)*sizeof(WCHAR)); 00269 } 00270 } 00271 00272 } 00273 00274 return cchText; 00275 } 00276 00277 /***************************************************************************\ 00278 * GrowMem 00279 * 00280 * History: 00281 * 16-Apr-1992 beng NODATA listboxes 00282 * 23-Jul-1996 jparsons Added numItems parameter for LB_INITSTORAGE support 00283 \***************************************************************************/ 00284 00285 BOOL GrowMem( 00286 PLBIV plb, 00287 INT numItems) 00288 00289 { 00290 LONG cb; 00291 HANDLE hMem; 00292 00293 /* 00294 * Allocate memory for pointers to the strings. 00295 */ 00296 cb = (plb->cMax + numItems) * 00297 (plb->fHasStrings ? sizeof(LBItem) 00298 : (plb->fHasData ? sizeof(LBODItem) 00299 : 0)); 00300 00301 /* 00302 * If multiple selection list box (MULTIPLESEL or EXTENDEDSEL), then 00303 * allocate an extra byte per item to keep track of it's selection state. 00304 */ 00305 if (plb->wMultiple != SINGLESEL) { 00306 cb += (plb->cMax + numItems); 00307 } 00308 00309 /* 00310 * Extra bytes for each item so that we can store its height. 00311 */ 00312 if (plb->OwnerDraw == OWNERDRAWVAR) { 00313 cb += (plb->cMax + numItems); 00314 } 00315 00316 /* 00317 * Don't allocate more than 2G of memory 00318 */ 00319 if (cb > MAXLONG) 00320 return FALSE; 00321 00322 if (plb->rgpch == NULL) { 00323 if ((plb->rgpch = UserLocalAlloc(HEAP_ZERO_MEMORY, (LONG)cb)) == NULL) 00324 return FALSE; 00325 } else { 00326 if ((hMem = UserLocalReAlloc(plb->rgpch, (LONG)cb, HEAP_ZERO_MEMORY)) == NULL) 00327 return FALSE; 00328 plb->rgpch = hMem; 00329 } 00330 00331 plb->cMax += numItems; 00332 00333 return TRUE; 00334 } 00335 00336 /***************************************************************************\ 00337 * xxxLBInitStorage 00338 * 00339 * History: 00340 * 23-Jul-1996 jparsons Added support for pre-allocation 00341 \***************************************************************************/ 00342 LONG xxxLBInitStorage(PLBIV plb, BOOL fAnsi, INT cItems, INT cb) 00343 { 00344 HANDLE hMem; 00345 INT cbChunk; 00346 00347 /* 00348 * if the app is talking ANSI, then adjust for the worst case in unicode 00349 * where each single ansi byte translates to one 16 bit unicode value 00350 */ 00351 if (fAnsi) { 00352 cb *= sizeof(WCHAR) ; 00353 } /* if */ 00354 00355 /* 00356 * Fail if either of the parameters look bad. 00357 */ 00358 if ((cItems < 0) || (cb < 0)) { 00359 xxxNotifyOwner(plb, LBN_ERRSPACE); 00360 return LB_ERRSPACE; 00361 } /* if */ 00362 00363 /* 00364 * try to grow the pointer array (if necessary) accounting for the free space 00365 * already available. 00366 */ 00367 cItems -= plb->cMax - plb->cMac ; 00368 if ((cItems > 0) && !GrowMem(plb, cItems)) { 00369 xxxNotifyOwner(plb, LBN_ERRSPACE); 00370 return LB_ERRSPACE; 00371 } /* if */ 00372 00373 /* 00374 * now grow the string space if necessary 00375 */ 00376 if (plb->fHasStrings) { 00377 if ((cbChunk = (plb->ichAlloc + cb)) > plb->cchStrings) { 00378 00379 /* 00380 * Round up to the nearest 256 byte chunk. 00381 */ 00382 cbChunk = (cbChunk & ~0xff) + 0x100; 00383 if (!(hMem = UserLocalReAlloc(plb->hStrings, (LONG)cbChunk, 0))) { 00384 xxxNotifyOwner(plb, LBN_ERRSPACE); 00385 return LB_ERRSPACE; 00386 } 00387 plb->hStrings = hMem; 00388 plb->cchStrings = cbChunk; 00389 } /* if */ 00390 } /* if */ 00391 00392 /* 00393 * return the number of items that can be stored 00394 */ 00395 return plb->cMax ; 00396 } 00397 00398 /***************************************************************************\ 00399 * xxxInsertString 00400 * 00401 * Insert an item at a specified position. 00402 * 00403 * History: 00404 * 16-Apr-1992 beng NODATA listboxes 00405 \***************************************************************************/ 00406 00407 INT xxxLBInsertItem( 00408 PLBIV plb, 00409 00410 /* 00411 * For owner draw listboxes without LBS_HASSTRINGS style, this is not a 00412 * string but rather a 4 byte value we will store for the app. 00413 */ 00414 LPWSTR lpsz, 00415 INT index, 00416 UINT wFlags) 00417 { 00418 MEASUREITEMSTRUCT measureItemStruct; 00419 INT cbString; 00420 INT cbChunk; 00421 PBYTE lp; 00422 PBYTE lpT; 00423 PBYTE lpHeightStart; 00424 LONG cbItem; /* sizeof the Item in rgpch */ 00425 HANDLE hMem; 00426 TL tlpwndParent; 00427 00428 CheckLock(plb->spwnd); 00429 00430 if (wFlags & LBI_ADD) 00431 index = (plb->fSort) ? xxxLBBinarySearchString(plb, lpsz) : -1; 00432 00433 if (!plb->rgpch) { 00434 if (index != 0 && index != -1) { 00435 RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, ""); 00436 return LB_ERR; 00437 } 00438 00439 plb->iSel = -1; 00440 plb->iSelBase = 0; 00441 plb->cMax = 0; 00442 plb->cMac = 0; 00443 plb->iTop = 0; 00444 plb->rgpch = UserLocalAlloc(HEAP_ZERO_MEMORY, 0L); 00445 if (!plb->rgpch) 00446 return LB_ERR; 00447 } 00448 00449 if (index == -1) { 00450 index = plb->cMac; 00451 } 00452 00453 if (index > plb->cMac || plb->cMac >= MAXLONG) { 00454 RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, ""); 00455 return LB_ERR; 00456 } 00457 00458 if (plb->fHasStrings) { 00459 00460 /* 00461 * we must store the string in the hStrings memory block. 00462 */ 00463 cbString = (wcslen(lpsz) + 1)*sizeof(WCHAR); /* include 0 terminator */ 00464 00465 if ((cbChunk = (plb->ichAlloc + cbString)) > plb->cchStrings) { 00466 00467 /* 00468 * Round up to the nearest 256 byte chunk. 00469 */ 00470 cbChunk = (cbChunk & ~0xff) + 0x100; 00471 if (!(hMem = UserLocalReAlloc(plb->hStrings, (LONG)cbChunk, 00472 0))) { 00473 xxxNotifyOwner(plb, LBN_ERRSPACE); 00474 return LB_ERRSPACE; 00475 } 00476 plb->hStrings = hMem; 00477 00478 plb->cchStrings = cbChunk; 00479 } 00480 00481 /* 00482 * Note difference between Win 95 code with placement of new string 00483 */ 00484 if (wFlags & UPPERCASE) 00485 CharUpperBuffW((LPWSTR)lpsz, cbString / sizeof(WCHAR)); 00486 else if (wFlags & LOWERCASE) 00487 CharLowerBuffW((LPWSTR)lpsz, cbString / sizeof(WCHAR)); 00488 00489 lp = (PBYTE)(plb->hStrings); 00490 RtlMoveMemory(lp + plb->ichAlloc, lpsz, cbString); 00491 } 00492 00493 /* 00494 * Now expand the pointer array. 00495 */ 00496 if (plb->cMac >= plb->cMax) { 00497 if (!GrowMem(plb, CITEMSALLOC)) { 00498 xxxNotifyOwner(plb, LBN_ERRSPACE); 00499 return LB_ERRSPACE; 00500 } 00501 } 00502 00503 lpHeightStart = lpT = lp = plb->rgpch; 00504 00505 /* 00506 * Now calculate how much room we must make for the string pointer (lpsz). 00507 * If we are ownerdraw without LBS_HASSTRINGS, then a single DWORD 00508 * (LBODItem.itemData) stored for each item, but if we have strings with 00509 * each item then a LONG string offset (LBItem.offsz) is also stored. 00510 */ 00511 cbItem = (plb->fHasStrings ? sizeof(LBItem) 00512 : (plb->fHasData ? sizeof(LBODItem):0)); 00513 cbChunk = (plb->cMac - index) * cbItem; 00514 00515 if (plb->wMultiple != SINGLESEL) { 00516 00517 /* 00518 * Extra bytes were allocated for selection flag for each item 00519 */ 00520 cbChunk += plb->cMac; 00521 } 00522 00523 if (plb->OwnerDraw == OWNERDRAWVAR) { 00524 00525 /* 00526 * Extra bytes were allocated for each item's height 00527 */ 00528 cbChunk += plb->cMac; 00529 } 00530 00531 /* 00532 * First, make room for the 2 byte pointer to the string or the 4 byte app 00533 * supplied value. 00534 */ 00535 lpT += (index * cbItem); 00536 RtlMoveMemory(lpT + cbItem, lpT, cbChunk); 00537 if (!plb->fHasStrings && plb->OwnerDraw) { 00538 if (plb->fHasData) { 00539 /* 00540 * Ownerdraw so just save the DWORD value 00541 */ 00542 lpLBODItem p = (lpLBODItem)lpT; 00543 p->itemData = (ULONG_PTR)lpsz; 00544 } 00545 } else { 00546 lpLBItem p = ((lpLBItem)lpT); 00547 00548 /* 00549 * Save the start of the string. Let the item data field be 0 00550 */ 00551 p->offsz = (LONG)(plb->ichAlloc); 00552 p->itemData = 0; 00553 plb->ichAlloc += cbString; 00554 } 00555 00556 /* 00557 * Now if Multiple Selection lbox, we have to insert a selection status 00558 * byte. If var height ownerdraw, then we also have to move up the height 00559 * bytes. 00560 */ 00561 if (plb->wMultiple != SINGLESEL) { 00562 lpT = lp + ((plb->cMac + 1) * cbItem) + index; 00563 RtlMoveMemory(lpT + 1, lpT, plb->cMac - index + 00564 (plb->OwnerDraw == OWNERDRAWVAR ? plb->cMac : 0)); 00565 *lpT = 0; /* fSelected = FALSE */ 00566 } 00567 00568 /* 00569 * Increment count of items in the listbox now before we send a message to 00570 * the app. 00571 */ 00572 plb->cMac++; 00573 00574 /* 00575 * If varheight ownerdraw, we much insert an extra byte for the item's 00576 * height. 00577 */ 00578 if (plb->OwnerDraw == OWNERDRAWVAR) { 00579 00580 /* 00581 * Variable height owner draw so we need to get the height of each item. 00582 */ 00583 lpHeightStart += (plb->cMac * cbItem) + index + 00584 (plb->wMultiple ? plb->cMac : 0); 00585 00586 RtlMoveMemory(lpHeightStart + 1, lpHeightStart, plb->cMac - 1 - index); 00587 00588 /* 00589 * Query for item height only if we are var height owner draw. 00590 */ 00591 measureItemStruct.CtlType = ODT_LISTBOX; 00592 measureItemStruct.CtlID = PtrToUlong(plb->spwnd->spmenu); 00593 measureItemStruct.itemID = index; 00594 00595 /* 00596 * System font height is default height 00597 */ 00598 measureItemStruct.itemHeight = (UINT)gpsi->cySysFontChar; 00599 measureItemStruct.itemData = (ULONG_PTR)lpsz; 00600 00601 /* 00602 * If "has strings" then add the special thunk bit so the client data 00603 * will be thunked to a client side address. LB_DIR sends a string 00604 * even if the listbox is not HASSTRINGS so we need to special 00605 * thunk this case. HP Dashboard for windows send LB_DIR to a non 00606 * HASSTRINGS listbox needs the server string converted to client. 00607 * WOW needs to know about this situation as well so we mark the 00608 * previously uninitialized itemWidth as FLAT. 00609 */ 00610 if (plb->fHasStrings || (wFlags & MSGFLAG_SPECIAL_THUNK)) { 00611 measureItemStruct.itemWidth = MIFLAG_FLAT; 00612 } 00613 00614 ThreadLock(plb->spwndParent, &tlpwndParent); 00615 SendMessage(HW(plb->spwndParent), 00616 WM_MEASUREITEM, 00617 measureItemStruct.CtlID, 00618 (LPARAM)&measureItemStruct); 00619 ThreadUnlock(&tlpwndParent); 00620 *lpHeightStart = (BYTE)measureItemStruct.itemHeight; 00621 } 00622 00623 00624 /* 00625 * If the item was inserted above the current selection then move 00626 * the selection down one as well. 00627 */ 00628 if ((plb->wMultiple == SINGLESEL) && (plb->iSel >= index)) 00629 plb->iSel++; 00630 00631 if (plb->OwnerDraw == OWNERDRAWVAR) 00632 LBSetCItemFullMax(plb); 00633 00634 /* 00635 * Check if scroll bars need to be shown/hidden 00636 */ 00637 plb->fFromInsert = TRUE; 00638 xxxLBShowHideScrollBars(plb); 00639 if (plb->fHorzBar && plb->fRightAlign && !(plb->fMultiColumn || plb->OwnerDraw)) { 00640 /* 00641 * origin to right 00642 */ 00643 xxxLBoxCtlHScroll(plb, SB_BOTTOM, 0); 00644 } 00645 plb->fFromInsert = FALSE; 00646 00647 xxxCheckRedraw(plb, TRUE, index); 00648 00649 if (FWINABLE()) { 00650 LBEvent(plb, EVENT_OBJECT_CREATE, index); 00651 } 00652 00653 return index; 00654 } 00655 00656 00657 /***************************************************************************\ 00658 * LBlstrcmpi 00659 * 00660 * This is a version of lstrcmpi() specifically used for listboxes 00661 * This gives more weight to '[' characters than alpha-numerics; 00662 * The US version of lstrcmpi() and lstrcmp() are similar as far as 00663 * non-alphanumerals are concerned; All non-alphanumerals get sorted 00664 * before alphanumerals; This means that subdirectory strings that start 00665 * with '[' will get sorted before; But we don't want that; So, this 00666 * function takes care of it; 00667 * 00668 * History: 00669 \***************************************************************************/ 00670 00671 INT LBlstrcmpi( 00672 LPWSTR lpStr1, 00673 LPWSTR lpStr2, 00674 DWORD dwLocaleId) 00675 { 00676 00677 /* 00678 * NOTE: This function is written so as to reduce the number of calls 00679 * made to the costly IsCharAlphaNumeric() function because that might 00680 * load a language module; It 'traps' the most frequently occurring cases 00681 * like both strings starting with '[' or both strings NOT starting with '[' 00682 * first and only in abosolutely necessary cases calls IsCharAlphaNumeric(); 00683 */ 00684 if (*lpStr1 == TEXT('[')) { 00685 if (*lpStr2 == TEXT('[')) { 00686 goto LBL_End; 00687 } 00688 if (IsCharAlphaNumeric(*lpStr2)) { 00689 return 1; 00690 } 00691 } 00692 00693 if ((*lpStr2 == TEXT('[')) && IsCharAlphaNumeric(*lpStr1)) { 00694 return -1; 00695 } 00696 00697 LBL_End: 00698 if ((GetClientInfo()->dwTIFlags & TIF_16BIT) && 00699 dwLocaleId == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)) { 00700 /* 00701 * This is how Windows95 does, bug #4199 00702 */ 00703 return (*pfnWowIlstrcmp)(lpStr1, lpStr2); 00704 } 00705 return (INT)CompareStringW((LCID)dwLocaleId, NORM_IGNORECASE, 00706 lpStr1, -1, lpStr2, -1 ) - 2; 00707 } 00708 00709 00710 /***************************************************************************\ 00711 * xxxLBBinarySearchString 00712 * 00713 * Does a binary search of the items in the SORTED listbox to find 00714 * out where this item should be inserted. Handles both HasStrings and item 00715 * long WM_COMPAREITEM cases. 00716 * 00717 * History: 00718 * 27 April 1992 GregoryW 00719 * Modified to support sorting based on current list box locale. 00720 \***************************************************************************/ 00721 00722 INT xxxLBBinarySearchString( 00723 PLBIV plb, 00724 LPWSTR lpstr) 00725 { 00726 BYTE *FAR *lprgpch; 00727 INT sortResult; 00728 COMPAREITEMSTRUCT cis; 00729 LPWSTR pszLBBase; 00730 LPWSTR pszLB; 00731 INT itemhigh; 00732 INT itemnew = 0; 00733 INT itemlow = 0; 00734 TL tlpwndParent; 00735 00736 CheckLock(plb->spwnd); 00737 00738 if (!plb->cMac) 00739 return 0; 00740 00741 lprgpch = (BYTE *FAR *)(plb->rgpch); 00742 if (plb->fHasStrings) { 00743 pszLBBase = plb->hStrings; 00744 } 00745 00746 itemhigh = plb->cMac - 1; 00747 while (itemlow <= itemhigh) { 00748 itemnew = (itemhigh + itemlow) / 2; 00749 00750 if (plb->fHasStrings) { 00751 00752 /* 00753 * Searching for string matches. 00754 */ 00755 pszLB = (LPWSTR)((LPSTR)pszLBBase + ((lpLBItem)lprgpch)[itemnew].offsz); 00756 sortResult = LBlstrcmpi(pszLB, lpstr, plb->dwLocaleId); 00757 } else { 00758 00759 /* 00760 * Send compare item messages to the parent for sorting 00761 */ 00762 cis.CtlType = ODT_LISTBOX; 00763 cis.CtlID = PtrToUlong(plb->spwnd->spmenu); 00764 cis.hwndItem = HWq(plb->spwnd); 00765 cis.itemID1 = itemnew; 00766 cis.itemData1 = ((lpLBODItem)lprgpch)[itemnew].itemData; 00767 cis.itemID2 = (UINT)-1; 00768 cis.itemData2 = (ULONG_PTR)lpstr; 00769 cis.dwLocaleId = plb->dwLocaleId; 00770 ThreadLock(plb->spwndParent, &tlpwndParent); 00771 sortResult = (INT)SendMessage(HW(plb->spwndParent), WM_COMPAREITEM, 00772 cis.CtlID, (LPARAM)&cis); 00773 ThreadUnlock(&tlpwndParent); 00774 } 00775 00776 if (sortResult < 0) { 00777 itemlow = itemnew + 1; 00778 } else if (sortResult > 0) { 00779 itemhigh = itemnew - 1; 00780 } else { 00781 itemlow = itemnew; 00782 goto FoundIt; 00783 } 00784 } 00785 00786 FoundIt: 00787 00788 return max(0, itemlow); 00789 } 00790 00791 /***************************************************************************\ 00792 * xxxLBResetContent 00793 * 00794 * History: 00795 \***************************************************************************/ 00796 00797 BOOL xxxLBResetContent( 00798 PLBIV plb) 00799 { 00800 if (!plb->cMac) 00801 return TRUE; 00802 00803 xxxLBoxDoDeleteItems(plb); 00804 00805 if (plb->rgpch != NULL) { 00806 UserLocalFree(plb->rgpch); 00807 plb->rgpch = NULL; 00808 } 00809 00810 if (plb->hStrings != NULL) { 00811 UserLocalFree(plb->hStrings); 00812 plb->hStrings = NULL; 00813 } 00814 00815 InitHStrings(plb); 00816 00817 if (TestWF(plb->spwnd, WFWIN31COMPAT)) 00818 xxxCheckRedraw(plb, FALSE, 0); 00819 else if (IsVisible(plb->spwnd)) 00820 NtUserInvalidateRect(HWq(plb->spwnd), NULL, TRUE); 00821 00822 plb->iSelBase = 0; 00823 plb->iTop = 0; 00824 plb->cMac = 0; 00825 plb->cMax = 0; 00826 plb->xOrigin = 0; 00827 plb->iLastSelection = 0; 00828 plb->iSel = -1; 00829 00830 xxxLBShowHideScrollBars(plb); 00831 return TRUE; 00832 } 00833 00834 00835 /***************************************************************************\ 00836 * xxxLBoxCtlDelete 00837 * 00838 * History: 00839 * 16-Apr-1992 beng NODATA listboxes 00840 \***************************************************************************/ 00841 00842 INT xxxLBoxCtlDelete( 00843 PLBIV plb, 00844 INT sItem) /* Item number to delete */ 00845 { 00846 LONG cb; 00847 LPBYTE lp; 00848 LPBYTE lpT; 00849 RECT rc; 00850 int cbItem; /* size of Item in rgpch */ 00851 LPWSTR lpString; 00852 PBYTE pbStrings; 00853 INT cbStringLen; 00854 LPBYTE itemNumbers; 00855 INT sTmp; 00856 TL tlpwnd; 00857 00858 CheckLock(plb->spwnd); 00859 00860 if (sItem < 0 || sItem >= plb->cMac) { 00861 RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, ""); 00862 return LB_ERR; 00863 } 00864 00865 if (FWINABLE()) { 00866 LBEvent(plb, EVENT_OBJECT_DESTROY, sItem); 00867 } 00868 00869 if (plb->cMac == 1) { 00870 00871 /* 00872 * When the item count is 0, we send a resetcontent message so that we 00873 * can reclaim our string space this way. 00874 */ 00875 SendMessageWorker(plb->spwnd, LB_RESETCONTENT, 0, 0, FALSE); 00876 goto FinishUpDelete; 00877 } 00878 00879 /* 00880 * Get the rectangle associated with the last item in the listbox. If it is 00881 * visible, we need to invalidate it. When we delete an item, everything 00882 * scrolls up to replace the item deleted so we must make sure we erase the 00883 * old image of the last item in the listbox. 00884 */ 00885 if (LBGetItemRect(plb, (INT)(plb->cMac - 1), &rc)) { 00886 xxxLBInvalidateRect(plb, &rc, TRUE); 00887 } 00888 00889 // 3.1 and earlier used to only send WM_DELETEITEMs if it was an ownerdraw 00890 // listbox. 4.0 and above will send WM_DELETEITEMs for every item that has 00891 // nonzero item data. 00892 if (TestWF(plb->spwnd, WFWIN40COMPAT) || (plb->OwnerDraw && plb->fHasData)) { 00893 xxxLBoxDeleteItem(plb, sItem); 00894 } 00895 00896 plb->cMac--; 00897 00898 cbItem = (plb->fHasStrings ? sizeof(LBItem) 00899 : (plb->fHasData ? sizeof(LBODItem): 0)); 00900 cb = ((plb->cMac - sItem) * cbItem); 00901 00902 /* 00903 * Byte for the selection status of the item. 00904 */ 00905 if (plb->wMultiple != SINGLESEL) { 00906 cb += (plb->cMac + 1); 00907 } 00908 00909 if (plb->OwnerDraw == OWNERDRAWVAR) { 00910 00911 /* 00912 * One byte for the height of the item. 00913 */ 00914 cb += (plb->cMac + 1); 00915 } 00916 00917 /* 00918 * Might be nodata and singlesel, for instance. 00919 * but what out for the case where cItem == cMac (and cb == 0). 00920 */ 00921 if ((cb != 0) || plb->fHasStrings) { 00922 lp = plb->rgpch; 00923 00924 lpT = (lp + (sItem * cbItem)); 00925 00926 if (plb->fHasStrings) { 00927 /* 00928 * If we has strings with each item, then we want to compact the string 00929 * heap so that we can recover the space occupied by the string of the 00930 * deleted item. 00931 */ 00932 /* 00933 * Get the string which we will be deleting 00934 */ 00935 pbStrings = (PBYTE)(plb->hStrings); 00936 lpString = (LPTSTR)(pbStrings + ((lpLBItem)lpT)->offsz); 00937 cbStringLen = (wcslen(lpString) + 1) * sizeof(WCHAR); /* include null terminator */ 00938 00939 /* 00940 * Now compact the string array 00941 */ 00942 plb->ichAlloc = plb->ichAlloc - cbStringLen; 00943 00944 RtlMoveMemory(lpString, (PBYTE)lpString + cbStringLen, 00945 plb->ichAlloc + (pbStrings - (LPBYTE)lpString)); 00946 00947 /* 00948 * We have to update the string pointers in plb->rgpch since all the 00949 * string after the deleted string have been moved down stringLength 00950 * bytes. Note that we have to explicitly check all items in the list 00951 * box if the string was allocated after the deleted item since the 00952 * LB_SORT style allows a lower item number to have a string allocated 00953 * at the end of the string heap for example. 00954 */ 00955 itemNumbers = lp; 00956 for (sTmp = 0; sTmp <= plb->cMac; sTmp++) { 00957 lpLBItem p =(lpLBItem)itemNumbers; 00958 if ( (LPTSTR)(p->offsz + pbStrings) > lpString ) { 00959 p->offsz -= cbStringLen; 00960 } 00961 p++; 00962 itemNumbers=(LPBYTE)p; 00963 } 00964 } 00965 00966 /* 00967 * Now compact the pointers to the strings (or the long app supplied values 00968 * if ownerdraw without strings). 00969 */ 00970 RtlMoveMemory(lpT, lpT + cbItem, cb); 00971 00972 /* 00973 * Compress the multiselection bytes 00974 */ 00975 if (plb->wMultiple != SINGLESEL) { 00976 lpT = (lp + (plb->cMac * cbItem) + sItem); 00977 RtlMoveMemory(lpT, lpT + 1, plb->cMac - sItem + 00978 (plb->OwnerDraw == OWNERDRAWVAR ? plb->cMac + 1 : 0)); 00979 } 00980 00981 if (plb->OwnerDraw == OWNERDRAWVAR) { 00982 /* 00983 * Compress the height bytes 00984 */ 00985 lpT = (lp + (plb->cMac * cbItem) + (plb->wMultiple ? plb->cMac : 0) 00986 + sItem); 00987 RtlMoveMemory(lpT, lpT + 1, plb->cMac - sItem); 00988 } 00989 00990 } 00991 00992 if (plb->wMultiple == SINGLESEL) { 00993 if (plb->iSel == sItem) { 00994 plb->iSel = -1; 00995 00996 if (plb->pcbox != NULL) { 00997 ThreadLock(plb->pcbox->spwnd, &tlpwnd); 00998 xxxCBInternalUpdateEditWindow(plb->pcbox, NULL); 00999 ThreadUnlock(&tlpwnd); 01000 } 01001 } else if (plb->iSel > sItem) 01002 plb->iSel--; 01003 } 01004 01005 if ((plb->iMouseDown != -1) && (sItem <= plb->iMouseDown)) 01006 plb->iMouseDown = -1; 01007 01008 if (plb->iSelBase && sItem == plb->iSelBase) 01009 plb->iSelBase--; 01010 01011 if (plb->cMac) { 01012 plb->iSelBase = min(plb->iSelBase, plb->cMac - 1); 01013 } else { 01014 plb->iSelBase = 0; 01015 } 01016 01017 if ((plb->wMultiple == EXTENDEDSEL) && (plb->iSel == -1)) 01018 plb->iSel = plb->iSelBase; 01019 01020 if (plb->OwnerDraw == OWNERDRAWVAR) 01021 LBSetCItemFullMax(plb); 01022 01023 /* 01024 * We always set a new iTop. The iTop won't change if it doesn't need to 01025 * but it will change if: 1. The iTop was deleted or 2. We need to change 01026 * the iTop so that we fill the listbox. 01027 */ 01028 xxxInsureVisible(plb, plb->iTop, FALSE); 01029 01030 FinishUpDelete: 01031 01032 /* 01033 * Check if scroll bars need to be shown/hidden 01034 */ 01035 plb->fFromInsert = TRUE; 01036 xxxLBShowHideScrollBars(plb); 01037 plb->fFromInsert = FALSE; 01038 01039 xxxCheckRedraw(plb, TRUE, sItem); 01040 xxxInsureVisible(plb, plb->iSelBase, FALSE); 01041 01042 return plb->cMac; 01043 } 01044 01045 /***************************************************************************\ 01046 * xxxLBoxDeleteItem 01047 * 01048 * Sends a WM_DELETEITEM message to the owner of an ownerdraw listbox 01049 * 01050 * History: 01051 \***************************************************************************/ 01052 01053 void xxxLBoxDeleteItem( 01054 PLBIV plb, 01055 INT sItem) 01056 { 01057 DELETEITEMSTRUCT dis; 01058 TL tlpwndParent; 01059 01060 CheckLock(plb->spwnd); 01061 if (plb->spwnd == NULL) 01062 return; 01063 01064 /* 01065 * Bug 262122 - joejo 01066 * No need to send message if no data! 01067 */ 01068 if (!plb->fHasData) { 01069 return; 01070 } 01071 01072 /* 01073 * Fill the DELETEITEMSTRUCT 01074 */ 01075 dis.CtlType = ODT_LISTBOX; 01076 dis.CtlID = PtrToUlong(plb->spwnd->spmenu); 01077 dis.itemID = sItem; 01078 dis.hwndItem = HWq(plb->spwnd); 01079 01080 /* 01081 * Bug 262122 - joejo 01082 * Fixed in 93 so that ItemData was passed. For some reason, not 01083 * merged in. 01084 */ 01085 dis.itemData = LBGetItemData(plb, sItem); 01086 01087 if (plb->spwndParent != NULL) { 01088 ThreadLock(plb->spwndParent, &tlpwndParent); 01089 SendMessage(HWq(plb->spwndParent), WM_DELETEITEM, dis.CtlID, 01090 (LPARAM)&dis); 01091 ThreadUnlock(&tlpwndParent); 01092 } 01093 } 01094 01095 /**************************************************************************\ 01096 * xxxLBSetCount 01097 * 01098 * Sets the number of items in a lazy-eval (fNoData) listbox. 01099 * 01100 * Calling SetCount scorches any existing selection state. To preserve 01101 * selection state, call Insert/DeleteItem instead. 01102 * 01103 * History 01104 * 16-Apr-1992 beng Created 01105 \**************************************************************************/ 01106 01107 INT xxxLBSetCount( 01108 PLBIV plb, 01109 INT cItems) 01110 { 01111 UINT cbRequired; 01112 BOOL fRedraw; 01113 01114 CheckLock(plb->spwnd); 01115 01116 /* 01117 * SetCount is only valid on lazy-eval ("nodata") listboxes. 01118 * All other lboxen must add their items one at a time, although 01119 * they may SetCount(0) via RESETCONTENT. 01120 */ 01121 if (plb->fHasStrings || plb->fHasData) { 01122 RIPERR0(ERROR_SETCOUNT_ON_BAD_LB, RIP_VERBOSE, ""); 01123 return LB_ERR; 01124 } 01125 01126 if (cItems == 0) { 01127 SendMessageWorker(plb->spwnd, LB_RESETCONTENT, 0, 0, FALSE); 01128 return 0; 01129 } 01130 01131 // If redraw isn't turned off, turn it off now 01132 if (fRedraw = plb->fRedraw) 01133 xxxLBSetRedraw(plb, FALSE); 01134 01135 cbRequired = LBCalcAllocNeeded(plb, cItems); 01136 01137 /* 01138 * Reset selection and position 01139 */ 01140 plb->iSelBase = 0; 01141 plb->iTop = 0; 01142 plb->cMax = 0; 01143 plb->xOrigin = 0; 01144 plb->iLastSelection = 0; 01145 plb->iSel = -1; 01146 01147 if (cbRequired != 0) { // Only if record instance data required 01148 01149 /* 01150 * If listbox was previously empty, prepare for the 01151 * realloc-based alloc strategy ahead. 01152 */ 01153 if (plb->rgpch == NULL) { 01154 plb->rgpch = UserLocalAlloc(HEAP_ZERO_MEMORY, 0L); 01155 plb->cMax = 0; 01156 01157 if (plb->rgpch == NULL) { 01158 xxxNotifyOwner(plb, LBN_ERRSPACE); 01159 return LB_ERRSPACE; 01160 } 01161 } 01162 01163 /* 01164 * rgpch might not have enough room for the new record instance 01165 * data, so check and realloc as necessary. 01166 */ 01167 if (cItems >= plb->cMax) { 01168 INT cMaxNew; 01169 UINT cbNew; 01170 HANDLE hmemNew; 01171 01172 /* 01173 * Since GrowMem presumes a one-item-at-a-time add schema, 01174 * SetCount can't use it. Too bad. 01175 */ 01176 cMaxNew = cItems+CITEMSALLOC; 01177 cbNew = LBCalcAllocNeeded(plb, cMaxNew); 01178 hmemNew = UserLocalReAlloc(plb->rgpch, cbNew, HEAP_ZERO_MEMORY); 01179 01180 if (hmemNew == NULL) { 01181 xxxNotifyOwner(plb, LBN_ERRSPACE); 01182 return LB_ERRSPACE; 01183 } 01184 01185 plb->rgpch = hmemNew; 01186 plb->cMax = cMaxNew; 01187 } 01188 01189 /* 01190 * Reset the item instance data (multisel annotations) 01191 */ 01192 RtlZeroMemory(plb->rgpch, cbRequired); 01193 } 01194 01195 plb->cMac = cItems; 01196 01197 // Turn redraw back on 01198 if (fRedraw) 01199 xxxLBSetRedraw(plb, TRUE); 01200 01201 xxxLBInvalidateRect(plb, NULL, TRUE); 01202 // Not In Chicago -- FritzS 01203 // NtUserSetScrollPos(plb->spwnd, SB_HORZ, 0, plb->fRedraw); 01204 // NtUserSetScrollPos(plb->spwnd, SB_VERT, 0, plb->fRedraw); 01205 xxxLBShowHideScrollBars(plb); // takes care of fRedraw 01206 01207 return 0; 01208 } 01209 01210 /**************************************************************************\ 01211 * LBCalcAllocNeeded 01212 * 01213 * Calculate the number of bytes needed in rgpch to accommodate a given 01214 * number of items. 01215 * 01216 * History 01217 * 16-Apr-1992 beng Created 01218 \**************************************************************************/ 01219 01220 UINT LBCalcAllocNeeded( 01221 PLBIV plb, 01222 INT cItems) 01223 { 01224 UINT cb; 01225 01226 /* 01227 * Allocate memory for pointers to the strings. 01228 */ 01229 cb = cItems * (plb->fHasStrings ? sizeof(LBItem) 01230 : (plb->fHasData ? sizeof(LBODItem) 01231 : 0)); 01232 01233 /* 01234 * If multiple selection list box (MULTIPLESEL or EXTENDEDSEL), then 01235 * allocate an extra byte per item to keep track of it's selection state. 01236 */ 01237 if (plb->wMultiple != SINGLESEL) { 01238 cb += cItems; 01239 } 01240 01241 /* 01242 * Extra bytes for each item so that we can store its height. 01243 */ 01244 if (plb->OwnerDraw == OWNERDRAWVAR) { 01245 cb += cItems; 01246 } 01247 01248 return cb; 01249 }

Generated on Sat May 15 19:40:36 2004 for test by doxygen 1.3.7