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

lboxctl2.c

Go to the documentation of this file.
00001 /***************************************************************************\ 00002 * 00003 * LBOXCTL2.C - 00004 * 00005 * Copyright (c) 1985 - 1999, Microsoft Corporation 00006 * 00007 * List box handling routines 00008 * 00009 * 18-Dec-1990 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 #define LB_KEYDOWN WM_USER+1 00017 #define NOMODIFIER 0 /* No modifier is down */ 00018 #define SHIFTDOWN 1 /* Shift alone */ 00019 #define CTLDOWN 2 /* Ctl alone */ 00020 #define SHCTLDOWN (SHIFTDOWN + CTLDOWN) /* Ctrl + Shift */ 00021 00022 /* 00023 * Variables for incremental type search support 00024 */ 00025 #define MAX_TYPESEARCH 256 00026 00027 BOOL LBGetDC(PLBIV plb); 00028 void LBReleaseDC(PLBIV plb); 00029 00030 /***************************************************************************\ 00031 * 00032 * LBInvalidateRect() 00033 * 00034 * If the listbox is visible, invalidates a rectangle in the listbox. 00035 * If the listbox is not visible, sets the defer update flag for the listbox 00036 * 00037 \***************************************************************************/ 00038 BOOL xxxLBInvalidateRect(PLBIV plb, LPRECT lprc, BOOL fErase) 00039 { 00040 CheckLock(plb->spwnd); 00041 00042 if (IsLBoxVisible(plb)) { 00043 NtUserInvalidateRect(HWq(plb->spwnd), lprc, fErase); 00044 return(TRUE); 00045 } 00046 00047 if (!plb->fRedraw) 00048 plb->fDeferUpdate = TRUE; 00049 00050 return(FALSE); 00051 } 00052 00053 /***************************************************************************\ 00054 * 00055 * LBGetBrush() 00056 * 00057 * Gets background brush & colors for listbox. 00058 * 00059 \***************************************************************************/ 00060 HBRUSH xxxLBGetBrush(PLBIV plb, HBRUSH *phbrOld) 00061 { 00062 HBRUSH hbr; 00063 HBRUSH hbrOld; 00064 TL tlpwndParent; 00065 00066 CheckLock(plb->spwnd); 00067 00068 SetBkMode(plb->hdc, OPAQUE); 00069 00070 // 00071 // Get brush & colors 00072 // 00073 if ((plb->spwnd->spwndParent == NULL) || 00074 (REBASEPWND(plb->spwnd, spwndParent) == _GetDesktopWindow())) { 00075 ThreadLock(plb->spwndParent, &tlpwndParent); 00076 hbr = GetControlColor(HW(plb->spwndParent), HWq(plb->spwnd), 00077 plb->hdc, WM_CTLCOLORLISTBOX); 00078 ThreadUnlock(&tlpwndParent); 00079 } else 00080 hbr = GetControlBrush(HWq(plb->spwnd), plb->hdc, WM_CTLCOLORLISTBOX); 00081 00082 // 00083 // Select brush into dc 00084 // 00085 if (hbr != NULL) { 00086 hbrOld = SelectObject(plb->hdc, hbr); 00087 if (phbrOld) 00088 *phbrOld = hbrOld; 00089 } 00090 00091 return(hbr); 00092 } 00093 00094 00095 /***************************************************************************\ 00096 * 00097 * LBInitDC() 00098 * 00099 * Initializes dc for listbox 00100 * 00101 \***************************************************************************/ 00102 void LBInitDC(PLBIV plb) 00103 { 00104 RECT rc; 00105 00106 // Set font 00107 if (plb->hFont) 00108 SelectObject(plb->hdc, plb->hFont); 00109 00110 // Set clipping area 00111 _GetClientRect(plb->spwnd, &rc); 00112 IntersectClipRect(plb->hdc, rc.left, rc.top, rc.right, rc.bottom); 00113 00114 OffsetWindowOrgEx(plb->hdc, plb->xOrigin, 0, NULL); 00115 } 00116 00117 00118 /***************************************************************************\ 00119 * LBGetDC 00120 * 00121 * Returns a DC which can be used by a list box even if parentDC is in effect 00122 * 00123 * History: 00124 \***************************************************************************/ 00125 00126 BOOL LBGetDC( 00127 PLBIV plb) 00128 { 00129 if (plb->hdc) 00130 return(FALSE); 00131 00132 plb->hdc = NtUserGetDC(HWq(plb->spwnd)); 00133 00134 LBInitDC(plb); 00135 00136 return TRUE; 00137 } 00138 00139 /***************************************************************************\ 00140 * 00141 * LBTermDC() 00142 * 00143 * Cleans up when done with listbox dc. 00144 * 00145 \***************************************************************************/ 00146 void LBTermDC(PLBIV plb) 00147 { 00148 if (plb->hFont) 00149 SelectObject(plb->hdc, ghFontSys); 00150 } 00151 00152 00153 00154 /***************************************************************************\ 00155 * LBReleaseDC 00156 * 00157 * History: 00158 \***************************************************************************/ 00159 00160 void LBReleaseDC( 00161 PLBIV plb) 00162 { 00163 LBTermDC(plb); 00164 NtUserReleaseDC(HWq(plb->spwnd), plb->hdc); 00165 plb->hdc = NULL; 00166 } 00167 00168 00169 /***************************************************************************\ 00170 * LBGetItemRect 00171 * 00172 * Return the rectangle that the item will be drawn in with respect to the 00173 * listbox window. Returns TRUE if any portion of the item's rectangle 00174 * is visible (ie. in the listbox client rect) else returns FALSE. 00175 * 00176 * History: 00177 \***************************************************************************/ 00178 00179 BOOL LBGetItemRect( 00180 PLBIV plb, 00181 INT sItem, 00182 LPRECT lprc) 00183 { 00184 INT sTmp; 00185 int clientbottom; 00186 00187 /* 00188 * Always allow an item number of 0 so that we can draw the caret which 00189 * indicates the listbox has the focus even though it is empty. 00190 00191 * FreeHand 3.1 passes in -1 as the itemNumber and expects 00192 * a non-null rectangle. So we check for -1 specifically. 00193 * BUGTAG: Fix for Bug #540 --Win95B-- SANKAR -- 2/20/95 -- 00194 */ 00195 00196 if (sItem && (sItem != -1) && ((UINT)sItem >= (UINT)plb->cMac)) 00197 { 00198 SetRectEmpty(lprc); 00199 RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, ""); 00200 return (LB_ERR); 00201 } 00202 00203 _GetClientRect(plb->spwnd, lprc); 00204 00205 if (plb->fMultiColumn) { 00206 00207 /* 00208 * itemHeight * sItem mod number ItemsPerColumn (itemsPerColumn) 00209 */ 00210 lprc->top = plb->cyChar * (sItem % plb->itemsPerColumn); 00211 lprc->bottom = lprc->top + plb->cyChar /*+(plb->OwnerDraw ? 0 : 1)*/; 00212 00213 UserAssert(plb->itemsPerColumn); 00214 00215 if (plb->fRightAlign) { 00216 lprc->right = lprc->right - plb->cxColumn * 00217 ((sItem / plb->itemsPerColumn) - (plb->iTop / plb->itemsPerColumn)); 00218 00219 lprc->left = lprc->right - plb->cxColumn; 00220 } else { 00221 /* 00222 * Remember, this is integer division here... 00223 */ 00224 lprc->left += plb->cxColumn * 00225 ((sItem / plb->itemsPerColumn) - (plb->iTop / plb->itemsPerColumn)); 00226 00227 lprc->right = lprc->left + plb->cxColumn; 00228 } 00229 } else if (plb->OwnerDraw == OWNERDRAWVAR) { 00230 00231 /* 00232 * Var height owner draw 00233 */ 00234 lprc->right += plb->xOrigin; 00235 clientbottom = lprc->bottom; 00236 00237 if (sItem >= plb->iTop) { 00238 for (sTmp = plb->iTop; sTmp < sItem; sTmp++) { 00239 lprc->top = lprc->top + LBGetVariableHeightItemHeight(plb, sTmp); 00240 } 00241 00242 /* 00243 * If item number is 0, it may be we are asking for the rect 00244 * associated with a nonexistant item so that we can draw a caret 00245 * indicating focus on an empty listbox. 00246 */ 00247 lprc->bottom = lprc->top + (sItem < plb->cMac ? LBGetVariableHeightItemHeight(plb, sItem) : plb->cyChar); 00248 return (lprc->top < clientbottom); 00249 } else { 00250 00251 /* 00252 * Item we want the rect of is before plb->iTop. Thus, negative 00253 * offsets for the rect and it is never visible. 00254 */ 00255 for (sTmp = sItem; sTmp < plb->iTop; sTmp++) { 00256 lprc->top = lprc->top - LBGetVariableHeightItemHeight(plb, sTmp); 00257 } 00258 lprc->bottom = lprc->top + LBGetVariableHeightItemHeight(plb, sItem); 00259 return FALSE; 00260 } 00261 } else { 00262 00263 /* 00264 * For fixed height listboxes 00265 */ 00266 if (plb->fRightAlign && !(plb->fMultiColumn || plb->OwnerDraw) && plb->fHorzBar) 00267 lprc->right += plb->xOrigin + (plb->xRightOrigin - plb->xOrigin); 00268 else 00269 lprc->right += plb->xOrigin; 00270 lprc->top = (sItem - plb->iTop) * plb->cyChar; 00271 lprc->bottom = lprc->top + plb->cyChar; 00272 } 00273 00274 return (sItem >= plb->iTop) && 00275 (sItem < (plb->iTop + CItemInWindow(plb, TRUE))); 00276 } 00277 00278 00279 /***************************************************************************\ 00280 * 00281 * LBPrintCallback 00282 * 00283 * Called back from DrawState() 00284 * 00285 \***************************************************************************/ 00286 BOOL CALLBACK LBPrintCallback(HDC hdc, LPWSTR lpstr, PLBIV plb, int cx, int cy) 00287 { 00288 int xStart; 00289 UINT cLen; 00290 RECT rc; 00291 UINT oldAlign; 00292 00293 if (!lpstr) { 00294 return FALSE; 00295 } 00296 00297 if (plb->fMultiColumn) 00298 xStart = 0; 00299 else 00300 xStart = 2; 00301 00302 if (plb->fRightAlign) { 00303 oldAlign = SetTextAlign(hdc, TA_RIGHT | GetTextAlign(hdc)); 00304 xStart = cx - xStart; 00305 } 00306 00307 cLen = wcslen(lpstr); 00308 00309 if (plb->fUseTabStops) { 00310 TabTextOut(hdc, xStart, 0, lpstr, cLen, 00311 (plb->iTabPixelPositions ? plb->iTabPixelPositions[0] : 0), 00312 (plb->iTabPixelPositions ? (LPINT)&plb->iTabPixelPositions[1] : NULL), 00313 plb->fRightAlign ? cx : 0, TRUE, GetTextCharset(plb->hdc)); 00314 } else { 00315 rc.left = 0; 00316 rc.top = 0; 00317 rc.right = cx; 00318 rc.bottom = cy; 00319 00320 if (plb->wMultiple) 00321 ExtTextOut(hdc, xStart, 0, ETO_OPAQUE, &rc, lpstr, cLen, NULL); 00322 else if (plb->fMultiColumn) 00323 ExtTextOut(hdc, xStart, 0, ETO_CLIPPED, &rc, lpstr, cLen, NULL); 00324 else { 00325 ExtTextOut(hdc, xStart, 0, 0, NULL, lpstr, cLen, NULL); 00326 00327 /* 00328 * When the listbox is in the incremental search mode and the item 00329 * is highlighted (so we only draw in the current item), draw the 00330 * caret for search indication. 00331 */ 00332 if ((plb->iTypeSearch != 0) && (plb->OwnerDraw == 0) && 00333 (GetBkColor(hdc) == SYSRGB(HIGHLIGHT))) { 00334 SIZE size; 00335 GetTextExtentPointW(hdc, lpstr, plb->iTypeSearch, &size); 00336 PatBlt(hdc, xStart + size.cx - 1, 1, 1, cy - 2, DSTINVERT); 00337 } 00338 } 00339 } 00340 00341 if (plb->fRightAlign) 00342 SetTextAlign(hdc, oldAlign); 00343 00344 return(TRUE); 00345 } 00346 00347 00348 /***************************************************************************\ 00349 * xxxLBDrawLBItem 00350 * 00351 * History: 00352 \***************************************************************************/ 00353 00354 void xxxLBDrawLBItem( 00355 PLBIV plb, 00356 INT sItem, 00357 LPRECT lprect, 00358 BOOL fHilite, 00359 HBRUSH hbr) 00360 { 00361 LPWSTR lpstr; 00362 DWORD rgbSave; 00363 DWORD rgbBkSave; 00364 UINT uFlags; 00365 HDC hdc = plb->hdc; 00366 UINT oldAlign; 00367 00368 CheckLock(plb->spwnd); 00369 00370 /* 00371 * If the item is selected, then fill with highlight color 00372 */ 00373 if (fHilite) { 00374 FillRect(hdc, lprect, SYSHBR(HIGHLIGHT)); 00375 rgbBkSave = SetBkColor(hdc, SYSRGB(HIGHLIGHT)); 00376 rgbSave = SetTextColor(hdc, SYSRGB(HIGHLIGHTTEXT)); 00377 } else { 00378 00379 /* 00380 * If fUseTabStops, we must fill the background, because later we use 00381 * LBTabTheTextOutForWimps(), which fills the background only partially 00382 * Fix for Bug #1509 -- 01/25/91 -- SANKAR -- 00383 */ 00384 if ((hbr != NULL) && ((sItem == plb->iSelBase) || (plb->fUseTabStops))) { 00385 FillRect(hdc, lprect, hbr); 00386 } 00387 } 00388 00389 uFlags = DST_COMPLEX; 00390 lpstr = GetLpszItem(plb, sItem); 00391 00392 if (TestWF(plb->spwnd, WFDISABLED)) { 00393 if ((COLORREF)SYSRGB(GRAYTEXT) != GetBkColor(hdc)) 00394 SetTextColor(hdc, SYSRGB(GRAYTEXT)); 00395 else 00396 uFlags |= DSS_UNION; 00397 } 00398 00399 if (plb->fRightAlign) 00400 uFlags |= DSS_RIGHT; 00401 00402 if (plb->fRtoLReading) 00403 oldAlign = SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc)); 00404 00405 DrawState(hdc, SYSHBR(WINDOWTEXT), 00406 (DRAWSTATEPROC)LBPrintCallback, 00407 (LPARAM)lpstr, 00408 (WPARAM)plb, 00409 lprect->left, 00410 lprect->top, 00411 lprect->right-lprect->left, 00412 lprect->bottom-lprect->top, 00413 uFlags); 00414 00415 if (plb->fRtoLReading) 00416 SetTextAlign(hdc, oldAlign); 00417 00418 if (fHilite) { 00419 SetTextColor(hdc, rgbSave); 00420 SetBkColor(hdc, rgbBkSave); 00421 } 00422 } 00423 00424 00425 /***************************************************************************\ 00426 * 00427 * LBSetCaret() 00428 * 00429 \***************************************************************************/ 00430 void xxxLBSetCaret(PLBIV plb, BOOL fSetCaret) 00431 { 00432 RECT rc; 00433 BOOL fNewDC; 00434 00435 if (plb->fCaret && ((BOOL) plb->fCaretOn != !!fSetCaret)) { 00436 if (IsLBoxVisible(plb)) { 00437 /* Turn the caret (located at plb->iSelBase) on */ 00438 fNewDC = LBGetDC(plb); 00439 00440 LBGetItemRect(plb, plb->iSelBase, &rc); 00441 00442 if (fNewDC) { 00443 SetBkColor(plb->hdc, SYSRGB(WINDOW)); 00444 SetTextColor(plb->hdc, SYSRGB(WINDOWTEXT)); 00445 } 00446 00447 if (plb->OwnerDraw) { 00448 /* Fill in the drawitem struct */ 00449 UINT itemState = (fSetCaret) ? ODS_FOCUS : 0; 00450 00451 if (IsSelected(plb, plb->iSelBase, HILITEONLY)) 00452 itemState |= ODS_SELECTED; 00453 00454 xxxLBoxDrawItem(plb, plb->iSelBase, ODA_FOCUS, itemState, &rc); 00455 } else if (!TestWF(plb->spwnd, WEFPUIFOCUSHIDDEN)) { 00456 COLORREF crBk = SetBkColor(plb->hdc, SYSRGB(WINDOW)); 00457 COLORREF crText = SetTextColor(plb->hdc, SYSRGB(WINDOWTEXT)); 00458 00459 DrawFocusRect(plb->hdc, &rc); 00460 00461 SetBkColor(plb->hdc, crBk); 00462 SetTextColor(plb->hdc, crText); 00463 } 00464 00465 if (fNewDC) 00466 LBReleaseDC(plb); 00467 } 00468 plb->fCaretOn = !!fSetCaret; 00469 } 00470 } 00471 00472 00473 /***************************************************************************\ 00474 * IsSelected 00475 * 00476 * History: 00477 * 16-Apr-1992 beng The NODATA listbox case 00478 \***************************************************************************/ 00479 00480 BOOL IsSelected( 00481 PLBIV plb, 00482 INT sItem, 00483 UINT wOpFlags) 00484 { 00485 LPBYTE lp; 00486 00487 if ((sItem >= plb->cMac) || (sItem < 0)) { 00488 RIPERR0(ERROR_INVALID_INDEX, RIP_VERBOSE, ""); 00489 // return LB_ERR; 00490 return(FALSE); 00491 } 00492 00493 if (plb->wMultiple == SINGLESEL) { 00494 return (sItem == plb->iSel); 00495 } 00496 00497 lp = plb->rgpch + sItem + 00498 (plb->cMac * (plb->fHasStrings 00499 ? sizeof(LBItem) 00500 : (plb->fHasData 00501 ? sizeof(LBODItem) 00502 : 0))); 00503 sItem = *lp; 00504 00505 if (wOpFlags == HILITEONLY) { 00506 sItem >>= 4; 00507 } else { 00508 sItem &= 0x0F; /* SELONLY */ 00509 } 00510 00511 return sItem; 00512 } 00513 00514 00515 /***************************************************************************\ 00516 * CItemInWindow 00517 * 00518 * Returns the number of items which can fit in a list box. It 00519 * includes the partially visible one at the bottom if fPartial is TRUE. For 00520 * var height ownerdraw, return the number of items visible starting at iTop 00521 * and going to the bottom of the client rect. 00522 * 00523 * History: 00524 \***************************************************************************/ 00525 00526 INT CItemInWindow( 00527 PLBIV plb, 00528 BOOL fPartial) 00529 { 00530 RECT rect; 00531 00532 if (plb->OwnerDraw == OWNERDRAWVAR) { 00533 return CItemInWindowVarOwnerDraw(plb, fPartial); 00534 } 00535 00536 if (plb->fMultiColumn) { 00537 return plb->itemsPerColumn * (plb->numberOfColumns + (fPartial ? 1 : 0)); 00538 } 00539 00540 _GetClientRect(plb->spwnd, &rect); 00541 00542 /* 00543 * fPartial must be considered only if the listbox height is not an 00544 * integral multiple of character height. 00545 * A part of the fix for Bug #3727 -- 01/14/91 -- SANKAR -- 00546 */ 00547 UserAssert(plb->cyChar); 00548 return (INT)((rect.bottom / plb->cyChar) + 00549 ((rect.bottom % plb->cyChar)? (fPartial ? 1 : 0) : 0)); 00550 } 00551 00552 00553 /***************************************************************************\ 00554 * xxxLBoxCtlScroll 00555 * 00556 * Handles vertical scrolling of the listbox 00557 * 00558 * History: 00559 \***************************************************************************/ 00560 00561 void xxxLBoxCtlScroll( 00562 PLBIV plb, 00563 INT cmd, 00564 int yAmt) 00565 { 00566 INT iTopNew; 00567 INT cItemPageScroll; 00568 DWORD dwTime = 0; 00569 00570 CheckLock(plb->spwnd); 00571 00572 if (plb->fMultiColumn) { 00573 00574 /* 00575 * Don't allow vertical scrolling on a multicolumn list box. Needed 00576 * in case app sends WM_VSCROLL messages to the listbox. 00577 */ 00578 return; 00579 } 00580 00581 cItemPageScroll = plb->cItemFullMax; 00582 00583 if (cItemPageScroll > 1) 00584 cItemPageScroll--; 00585 00586 if (plb->cMac) { 00587 iTopNew = plb->iTop; 00588 switch (cmd) { 00589 case SB_LINEUP: 00590 dwTime = yAmt; 00591 iTopNew--; 00592 break; 00593 00594 case SB_LINEDOWN: 00595 dwTime = yAmt; 00596 iTopNew++; 00597 break; 00598 00599 case SB_PAGEUP: 00600 if (plb->OwnerDraw == OWNERDRAWVAR) { 00601 iTopNew = LBPage(plb, plb->iTop, FALSE); 00602 } else { 00603 iTopNew -= cItemPageScroll; 00604 } 00605 break; 00606 00607 case SB_PAGEDOWN: 00608 if (plb->OwnerDraw == OWNERDRAWVAR) { 00609 iTopNew = LBPage(plb, plb->iTop, TRUE); 00610 } else { 00611 iTopNew += cItemPageScroll; 00612 } 00613 break; 00614 00615 case SB_THUMBTRACK: 00616 case SB_THUMBPOSITION: { 00617 00618 /* 00619 * If the listbox contains more than 0xFFFF items 00620 * it means that the scrolbar can return a position 00621 * that cannot fit in a WORD (16 bits), so use 00622 * GetScrollInfo (which is slower) in this case. 00623 */ 00624 if (plb->cMac < 0xFFFF) { 00625 iTopNew = yAmt; 00626 } else { 00627 SCROLLINFO si; 00628 00629 si.cbSize = sizeof(SCROLLINFO); 00630 si.fMask = SIF_TRACKPOS; 00631 00632 GetScrollInfo( HWq(plb->spwnd), SB_VERT, &si); 00633 00634 iTopNew = si.nTrackPos; 00635 } 00636 break; 00637 } 00638 case SB_TOP: 00639 iTopNew = 0; 00640 break; 00641 00642 case SB_BOTTOM: 00643 iTopNew = plb->cMac - 1; 00644 break; 00645 00646 case SB_ENDSCROLL: 00647 plb->fSmoothScroll = TRUE; 00648 xxxLBSetCaret(plb, FALSE); 00649 xxxLBShowHideScrollBars(plb); 00650 xxxLBSetCaret(plb, TRUE); 00651 return; 00652 } 00653 00654 xxxNewITopEx(plb, iTopNew, dwTime); 00655 } 00656 } 00657 00658 /***************************************************************************\ 00659 * LBGetScrollFlags 00660 * 00661 \***************************************************************************/ 00662 00663 DWORD LBGetScrollFlags(PLBIV plb, DWORD dwTime) 00664 { 00665 DWORD dwFlags; 00666 00667 if (GetAppCompatFlags(NULL) & GACF_NOSMOOTHSCROLLING) 00668 goto NoSmoothScrolling; 00669 00670 if (dwTime != 0) { 00671 dwFlags = MAKELONG(SW_SCROLLWINDOW | SW_SMOOTHSCROLL | SW_SCROLLCHILDREN, dwTime); 00672 } else if (TEST_EffectPUSIF(PUSIF_LISTBOXSMOOTHSCROLLING) && plb->fSmoothScroll) { 00673 dwFlags = SW_SCROLLWINDOW | SW_SMOOTHSCROLL | SW_SCROLLCHILDREN; 00674 plb->fSmoothScroll = FALSE; 00675 } else { 00676 NoSmoothScrolling: 00677 dwFlags = SW_SCROLLWINDOW | SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN; 00678 } 00679 00680 return dwFlags; 00681 } 00682 00683 /***************************************************************************\ 00684 * xxxLBoxCtlHScroll 00685 * 00686 * Supports horizontal scrolling of listboxes 00687 * 00688 * History: 00689 \***************************************************************************/ 00690 00691 void xxxLBoxCtlHScroll( 00692 PLBIV plb, 00693 INT cmd, 00694 int xAmt) 00695 { 00696 int newOrigin = plb->xOrigin; 00697 int oldOrigin = plb->xOrigin; 00698 int windowWidth; 00699 RECT rc; 00700 DWORD dwTime = 0; 00701 00702 CheckLock(plb->spwnd); 00703 00704 /* 00705 * Update the window so that we don't run into problems with invalid 00706 * regions during the horizontal scroll. 00707 */ 00708 if (plb->fMultiColumn) { 00709 00710 /* 00711 * Handle multicolumn scrolling in a separate segment 00712 */ 00713 xxxLBoxCtlHScrollMultiColumn(plb, cmd, xAmt); 00714 return; 00715 } 00716 00717 _GetClientRect(plb->spwnd, &rc); 00718 windowWidth = rc.right; 00719 00720 if (plb->cMac) { 00721 00722 switch (cmd) { 00723 case SB_LINEUP: 00724 dwTime = xAmt; 00725 newOrigin -= plb->cxChar; 00726 break; 00727 00728 case SB_LINEDOWN: 00729 dwTime = xAmt; 00730 newOrigin += plb->cxChar; 00731 break; 00732 00733 case SB_PAGEUP: 00734 newOrigin -= (windowWidth / 3) * 2; 00735 break; 00736 00737 case SB_PAGEDOWN: 00738 newOrigin += (windowWidth / 3) * 2; 00739 break; 00740 00741 case SB_THUMBTRACK: 00742 case SB_THUMBPOSITION: 00743 newOrigin = xAmt; 00744 break; 00745 00746 case SB_TOP: 00747 newOrigin = 0; 00748 break; 00749 00750 case SB_BOTTOM: 00751 newOrigin = plb->maxWidth; 00752 break; 00753 00754 case SB_ENDSCROLL: 00755 plb->fSmoothScroll = TRUE; 00756 xxxLBSetCaret(plb, FALSE); 00757 xxxLBShowHideScrollBars(plb); 00758 xxxLBSetCaret(plb, TRUE); 00759 return; 00760 } 00761 00762 xxxLBSetCaret(plb, FALSE); 00763 plb->xOrigin = newOrigin; 00764 plb->xOrigin = xxxSetLBScrollParms(plb, SB_HORZ); 00765 00766 if ((cmd == SB_BOTTOM) && plb->fRightAlign) { 00767 /* 00768 * so we know where to draw from. 00769 */ 00770 plb->xRightOrigin = plb->xOrigin; 00771 } 00772 00773 if(oldOrigin != plb->xOrigin) { 00774 HWND hwnd = HWq(plb->spwnd); 00775 DWORD dwFlags; 00776 00777 dwFlags = LBGetScrollFlags(plb, dwTime); 00778 ScrollWindowEx(hwnd, oldOrigin-plb->xOrigin, 00779 0, NULL, &rc, NULL, NULL, dwFlags); 00780 UpdateWindow(hwnd); 00781 } 00782 00783 xxxLBSetCaret(plb, TRUE); 00784 } else { 00785 // this is a less-than-ideal fix for ImageMind ScreenSaver (Win95 00786 // B#8252) but it works and it doesn't hurt anybody -- JEFFBOG 10/28/94 00787 xxxSetLBScrollParms(plb, SB_HORZ); 00788 } 00789 } 00790 00791 00792 /***************************************************************************\ 00793 * xxxLBoxCtlPaint 00794 * 00795 * History: 00796 \***************************************************************************/ 00797 00798 void xxxLBPaint( 00799 PLBIV plb, 00800 HDC hdc, 00801 LPRECT lprcBounds) 00802 { 00803 INT i; 00804 RECT rect; 00805 RECT scratchRect; 00806 BOOL fHilite; 00807 INT iLastItem; 00808 HBRUSH hbrSave = NULL; 00809 HBRUSH hbrControl; 00810 BOOL fCaretOn; 00811 RECT rcBounds; 00812 HDC hdcSave; 00813 00814 CheckLock(plb->spwnd); 00815 00816 if (lprcBounds == NULL) { 00817 lprcBounds = &rcBounds; 00818 _GetClientRect(plb->spwnd, lprcBounds); 00819 } 00820 00821 hdcSave = plb->hdc; 00822 plb->hdc = hdc; 00823 00824 // Initialize dc. 00825 LBInitDC(plb); 00826 00827 // Turn caret off 00828 if (fCaretOn = plb->fCaretOn) 00829 xxxLBSetCaret(plb, FALSE); 00830 00831 hbrSave = NULL; 00832 hbrControl = xxxLBGetBrush(plb, &hbrSave); 00833 00834 // Get listbox's client 00835 _GetClientRect(plb->spwnd, &rect); 00836 00837 // Adjust width of client rect for scrolled amount 00838 // fix for #140, t-arthb 00839 if (plb->fRightAlign && !(plb->fMultiColumn || plb->OwnerDraw) && plb->fHorzBar) 00840 rect.right += plb->xOrigin + (plb->xRightOrigin - plb->xOrigin); 00841 else 00842 rect.right += plb->xOrigin; 00843 00844 // Get the index of the last item visible on the screen. This is also 00845 // valid for var height ownerdraw. 00846 iLastItem = plb->iTop + CItemInWindow(plb,TRUE); 00847 iLastItem = min(iLastItem, plb->cMac - 1); 00848 00849 // Fill in the background of the listbox if it's an empty listbox 00850 // or if we're doing a control print 00851 if (iLastItem == -1) 00852 FillRect(plb->hdc, &rect, hbrControl); 00853 00854 00855 // Allow AnimateWindow() catch the apps that do not use our DC when 00856 // drawing the list box 00857 SetBoundsRect(plb->hdc, NULL, DCB_RESET | DCB_ENABLE); 00858 00859 for (i = plb->iTop; i <= iLastItem; i++) { 00860 00861 /* 00862 * Note that rect contains the clientrect from when we did the 00863 * _GetClientRect so the width is correct. We just need to adjust 00864 * the top and bottom of the rectangle to the item of interest. 00865 */ 00866 rect.bottom = rect.top + plb->cyChar; 00867 00868 if ((UINT)i < (UINT)plb->cMac) { 00869 00870 /* 00871 * If var height, get the rectangle for the item. 00872 */ 00873 if (plb->OwnerDraw == OWNERDRAWVAR || plb->fMultiColumn) { 00874 LBGetItemRect(plb, i, &rect); 00875 } 00876 00877 if (IntersectRect(&scratchRect, lprcBounds, &rect)) { 00878 fHilite = !plb->fNoSel && IsSelected(plb, i, HILITEONLY); 00879 00880 if (plb->OwnerDraw) { 00881 00882 /* 00883 * Fill in the drawitem struct 00884 */ 00885 xxxLBoxDrawItem(plb, i, ODA_DRAWENTIRE, 00886 (UINT)(fHilite ? ODS_SELECTED : 0), &rect); 00887 } else { 00888 xxxLBDrawLBItem(plb, i, &rect, fHilite, hbrControl); 00889 } 00890 } 00891 } 00892 rect.top = rect.bottom; 00893 } 00894 00895 if (hbrSave != NULL) 00896 SelectObject(hdc, hbrSave); 00897 00898 if (fCaretOn) 00899 xxxLBSetCaret(plb, TRUE); 00900 00901 LBTermDC(plb); 00902 00903 plb->hdc = hdcSave; 00904 } 00905 00906 00907 /***************************************************************************\ 00908 * ISelFromPt 00909 * 00910 * In the loword, returns the closest item number the pt is on. The high 00911 * word is 0 if the point is within bounds of the listbox client rect and is 00912 * 1 if it is outside the bounds. This will allow us to make the invertrect 00913 * disappear if the mouse is outside the listbox yet we can still show the 00914 * outline around the item that would be selected if the mouse is brought back 00915 * in bounds... 00916 * 00917 * History: 00918 \***************************************************************************/ 00919 00920 BOOL ISelFromPt( 00921 PLBIV plb, 00922 POINT pt, 00923 LPDWORD piItem) 00924 { 00925 RECT rect; 00926 int y; 00927 UINT mouseHighWord = 0; 00928 INT sItem; 00929 INT sTmp; 00930 00931 _GetClientRect(plb->spwnd, &rect); 00932 00933 if (pt.y < 0) { 00934 00935 /* 00936 * Mouse is out of bounds above listbox 00937 */ 00938 *piItem = plb->iTop; 00939 return TRUE; 00940 } else if ((y = pt.y) > rect.bottom) { 00941 y = rect.bottom; 00942 mouseHighWord = 1; 00943 } 00944 00945 if (pt.x < 0 || pt.x > rect.right) 00946 mouseHighWord = 1; 00947 00948 /* 00949 * Now just need to check if y mouse coordinate intersects item's rectangle 00950 */ 00951 if (plb->OwnerDraw != OWNERDRAWVAR) { 00952 if (plb->fMultiColumn) { 00953 if (y < plb->itemsPerColumn * plb->cyChar) { 00954 if (plb->fRightAlign) 00955 sItem = plb->iTop + (INT)((y / plb->cyChar) + 00956 ((rect.right - pt.x) / plb->cxColumn) * plb->itemsPerColumn); 00957 else 00958 sItem = plb->iTop + (INT)((y / plb->cyChar) + 00959 (pt.x / plb->cxColumn) * plb->itemsPerColumn); 00960 00961 } else { 00962 00963 /* 00964 * User clicked in blank space at the bottom of a column. 00965 * Just select the last item in the column. 00966 */ 00967 mouseHighWord = 1; 00968 sItem = plb->iTop + (plb->itemsPerColumn - 1) + 00969 (INT)((pt.x / plb->cxColumn) * plb->itemsPerColumn); 00970 } 00971 } else { 00972 sItem = plb->iTop + (INT)(y / plb->cyChar); 00973 } 00974 } else { 00975 00976 /* 00977 * VarHeightOwnerdraw so we gotta do this the hardway... Set the x 00978 * coordinate of the mouse down point to be inside the listbox client 00979 * rectangle since we no longer care about it. This lets us use the 00980 * point in rect calls. 00981 */ 00982 pt.x = 8; 00983 pt.y = y; 00984 for (sTmp = plb->iTop; sTmp < plb->cMac; sTmp++) { 00985 (void)LBGetItemRect(plb, sTmp, &rect); 00986 if (PtInRect(&rect, pt)) { 00987 *piItem = sTmp; 00988 return mouseHighWord; 00989 } 00990 } 00991 00992 /* 00993 * Point was at the empty area at the bottom of a not full listbox 00994 */ 00995 *piItem = plb->cMac - 1; 00996 return mouseHighWord; 00997 } 00998 00999 /* 01000 * Check if user clicked on the blank area at the bottom of a not full list. 01001 * Assumes > 0 items in the listbox. 01002 */ 01003 if (sItem > plb->cMac - 1) { 01004 mouseHighWord = 1; 01005 sItem = plb->cMac - 1; 01006 } 01007 01008 *piItem = sItem; 01009 return mouseHighWord; 01010 } 01011 01012 01013 /***************************************************************************\ 01014 * SetSelected 01015 * 01016 * This is used for button initiated changes of selection state. 01017 * 01018 * fSelected : TRUE if the item is to be set as selected, FALSE otherwise 01019 * 01020 * wOpFlags : HILITEONLY = Modify only the Display state (hi-nibble) 01021 * SELONLY = Modify only the Selection state (lo-nibble) 01022 * HILITEANDSEL = Modify both of them; 01023 * 01024 * History: 01025 * 16-Apr-1992 beng The NODATA listbox case 01026 \***************************************************************************/ 01027 01028 void SetSelected( 01029 PLBIV plb, 01030 INT iSel, 01031 BOOL fSelected, 01032 UINT wOpFlags) 01033 { 01034 LPSTR lp; 01035 BYTE cMask; 01036 BYTE cSelStatus; 01037 01038 if (iSel < 0 || iSel >= plb->cMac) 01039 return; 01040 01041 if (plb->wMultiple == SINGLESEL) { 01042 if (fSelected) 01043 plb->iSel = iSel; 01044 } else { 01045 cSelStatus = (BYTE)fSelected; 01046 switch (wOpFlags) { 01047 case HILITEONLY: 01048 01049 /* 01050 * Mask out lo-nibble 01051 */ 01052 cSelStatus = (BYTE)(cSelStatus << 4); 01053 cMask = 0x0F; 01054 break; 01055 case SELONLY: 01056 01057 /* 01058 * Mask out hi-nibble 01059 */ 01060 cMask = 0xF0; 01061 break; 01062 case HILITEANDSEL: 01063 01064 /* 01065 * Mask the byte fully 01066 */ 01067 cSelStatus |= (cSelStatus << 4); 01068 cMask = 0; 01069 break; 01070 } 01071 lp = (LPSTR)(plb->rgpch) + iSel + 01072 (plb->cMac * (plb->fHasStrings 01073 ? sizeof(LBItem) 01074 : (plb->fHasData ? sizeof(LBODItem) : 0))); 01075 01076 *lp = (*lp & cMask) | cSelStatus; 01077 } 01078 } 01079 01080 01081 /***************************************************************************\ 01082 * LastFullVisible 01083 * 01084 * Returns the last fully visible item in the listbox. This is valid 01085 * for ownerdraw var height and fixed height listboxes. 01086 * 01087 * History: 01088 \***************************************************************************/ 01089 01090 INT LastFullVisible( 01091 PLBIV plb) 01092 { 01093 INT iLastItem; 01094 01095 if (plb->OwnerDraw == OWNERDRAWVAR || plb->fMultiColumn) { 01096 iLastItem = plb->iTop + CItemInWindow(plb, FALSE) - 1; 01097 iLastItem = max(iLastItem, plb->iTop); 01098 } else { 01099 iLastItem = min(plb->iTop + plb->cItemFullMax - 1, plb->cMac - 1); 01100 } 01101 return iLastItem; 01102 } 01103 01104 01105 /***************************************************************************\ 01106 * xxxInvertLBItem 01107 * 01108 * History: 01109 \***************************************************************************/ 01110 01111 void xxxInvertLBItem( 01112 PLBIV plb, 01113 INT i, 01114 BOOL fHilite) /* The new selection state of the item */ 01115 { 01116 RECT rect; 01117 BOOL fCaretOn; 01118 HBRUSH hbrControl; 01119 BOOL fNewDC; 01120 01121 CheckLock(plb->spwnd); 01122 01123 // Skip if item isn't showing. 01124 if (plb->fNoSel || (i < plb->iTop) || (i >= (plb->iTop + CItemInWindow(plb, TRUE)))) 01125 return; 01126 01127 if (IsLBoxVisible(plb)) { 01128 LBGetItemRect(plb, i, &rect); 01129 01130 /* 01131 * Only turn off the caret if it is on. This avoids annoying caret 01132 * flicker when nesting xxxCaretOns and xxxCaretOffs. 01133 */ 01134 if (fCaretOn = plb->fCaretOn) { 01135 xxxLBSetCaret(plb, FALSE); 01136 } 01137 01138 fNewDC = LBGetDC(plb); 01139 01140 hbrControl = xxxLBGetBrush(plb, NULL); 01141 01142 if (!plb->OwnerDraw) { 01143 if (!fHilite) { 01144 FillRect(plb->hdc, &rect, hbrControl); 01145 hbrControl = NULL; 01146 } 01147 01148 xxxLBDrawLBItem(plb, i, &rect, fHilite, hbrControl); 01149 } else { 01150 01151 /* 01152 * We are ownerdraw so fill in the drawitem struct and send off 01153 * to the owner. 01154 */ 01155 xxxLBoxDrawItem(plb, i, ODA_SELECT, 01156 (UINT)(fHilite ? ODS_SELECTED : 0), &rect); 01157 } 01158 01159 if (fNewDC) 01160 LBReleaseDC(plb); 01161 01162 /* 01163 * Turn the caret back on only if it was originally on. 01164 */ 01165 if (fCaretOn) { 01166 xxxLBSetCaret(plb, TRUE); 01167 } 01168 } 01169 } 01170 01171 01172 /***************************************************************************\ 01173 * xxxResetWorld 01174 * 01175 * Resets everyone's selection and hilite state except items in the 01176 * range sStItem to sEndItem (Both inclusive). 01177 * 01178 * History: 01179 \***************************************************************************/ 01180 01181 void xxxResetWorld( 01182 PLBIV plb, 01183 INT iStart, 01184 INT iEnd, 01185 BOOL fSelect) 01186 { 01187 INT i; 01188 INT iLastInWindow; 01189 BOOL fCaretOn; 01190 01191 CheckLock(plb->spwnd); 01192 01193 /* 01194 * If iStart and iEnd are not in correct order we swap them 01195 */ 01196 01197 if (iStart > iEnd) { 01198 i = iStart; 01199 iStart = iEnd; 01200 iEnd = i; 01201 } 01202 01203 if (plb->wMultiple == SINGLESEL) { 01204 if (plb->iSel != -1 && ((plb->iSel < iStart) || (plb->iSel > iEnd))) { 01205 xxxInvertLBItem(plb, plb->iSel, fSelect); 01206 plb->iSel = -1; 01207 } 01208 return; 01209 } 01210 01211 iLastInWindow = plb->iTop + CItemInWindow(plb, TRUE); 01212 01213 if (fCaretOn = plb->fCaretOn) 01214 xxxLBSetCaret(plb, FALSE); 01215 01216 for (i = 0; i < plb->cMac; i++) { 01217 if (i == iStart) 01218 // skip range to be preserved 01219 i = iEnd; 01220 else { 01221 if ((plb->iTop <= i) && (i <= iLastInWindow) && 01222 (fSelect != IsSelected(plb, i, HILITEONLY))) 01223 // Only invert the item if it is visible and present Selection 01224 // state is different from what is required. 01225 xxxInvertLBItem(plb, i, fSelect); 01226 01227 // Set all items outside of preserved range to unselected 01228 SetSelected(plb, i, fSelect, HILITEANDSEL); 01229 } 01230 } 01231 01232 if (fCaretOn) 01233 xxxLBSetCaret(plb, TRUE); 01234 01235 } 01236 01237 01238 /***************************************************************************\ 01239 * xxxNotifyOwner 01240 * 01241 * History: 01242 \***************************************************************************/ 01243 01244 void xxxNotifyOwner( 01245 PLBIV plb, 01246 INT sEvt) 01247 { 01248 TL tlpwndParent; 01249 01250 CheckLock(plb->spwnd); 01251 01252 ThreadLock(plb->spwndParent, &tlpwndParent); 01253 SendMessage(HW(plb->spwndParent), WM_COMMAND, 01254 MAKELONG(PTR_TO_ID(plb->spwnd->spmenu), sEvt), (LPARAM)HWq(plb->spwnd)); 01255 ThreadUnlock(&tlpwndParent); 01256 } 01257 01258 01259 /***************************************************************************\ 01260 * xxxSetISelBase 01261 * 01262 * History: 01263 \***************************************************************************/ 01264 01265 void xxxSetISelBase( 01266 PLBIV plb, 01267 INT sItem) 01268 { 01269 CheckLock(plb->spwnd); 01270 01271 xxxLBSetCaret(plb, FALSE); 01272 plb->iSelBase = sItem; 01273 xxxLBSetCaret(plb, TRUE); 01274 01275 if (FWINABLE()) { 01276 xxxInsureVisible(plb, plb->iSelBase, FALSE); 01277 if (_IsWindowVisible(plb->spwnd)) { 01278 LBEvent(plb, EVENT_OBJECT_FOCUS, sItem); 01279 } 01280 } 01281 } 01282 01283 01284 /***************************************************************************\ 01285 * xxxTrackMouse 01286 * 01287 * History: 01288 \***************************************************************************/ 01289 01290 void xxxTrackMouse( 01291 PLBIV plb, 01292 UINT wMsg, 01293 POINT pt) 01294 { 01295 INT iSelFromPt; 01296 INT iSelTemp; 01297 BOOL mousetemp; 01298 BOOL fMouseInRect; 01299 RECT rcClient; 01300 UINT wModifiers = 0; 01301 BOOL fSelected; 01302 UINT uEvent = 0; 01303 INT trackPtRetn; 01304 HWND hwnd = HWq(plb->spwnd); 01305 TL tlpwndEdit; 01306 TL tlpwndParent; 01307 01308 CheckLock(plb->spwnd); 01309 01310 /* 01311 * Optimization: do nothing if mouse not captured 01312 */ 01313 if ((wMsg != WM_LBUTTONDOWN) && (wMsg != WM_LBUTTONDBLCLK)) { 01314 if (!plb->fCaptured) { 01315 return; 01316 } 01317 /* 01318 * If we are processing a WM_MOUSEMOVE but the mouse has not moved from 01319 * the previous point, then we may be dealing with a mouse "jiggle" sent 01320 * from the kernel (see zzzInvalidateDCCache). If we process this, we will 01321 * snap the listbox selection back to where the mouse cursor is pointing, 01322 * even if the user has not touched the mouse. FritzS: NT5 bug 220722. 01323 * Some apps (like MSMoney98) rely on this, so added the bLastRITWasKeyboard 01324 * check. MCostea #244450 01325 */ 01326 if ((wMsg == WM_MOUSEMOVE) && RtlEqualMemory(&pt, &(plb->ptPrev), sizeof(POINT)) 01327 && gpsi->bLastRITWasKeyboard) { 01328 RIPMSG0(RIP_WARNING, "xxxTrackMouse ignoring WM_MOUSEMOVE with no mouse movement"); 01329 return; 01330 } 01331 } 01332 01333 mousetemp = ISelFromPt(plb, pt, &iSelFromPt); 01334 01335 /* 01336 * If we allow the user to cancel his selection then fMouseInRect is true if 01337 * the mouse is in the listbox client area otherwise it is false. If we 01338 * don't allow the user to cancel his selection, then fMouseInRect will 01339 * always be true. This allows us to implement cancelable selection 01340 * listboxes ie. The selection reverts to the origional one if the user 01341 * releases the mouse outside of the listbox. 01342 */ 01343 fMouseInRect = !mousetemp || !plb->pcbox; 01344 01345 _GetClientRect(plb->spwnd, &rcClient); 01346 01347 switch (wMsg) { 01348 case WM_LBUTTONDBLCLK: 01349 case WM_LBUTTONDOWN: 01350 /* 01351 * We want to divert mouse clicks. If the user clicks outside 01352 * of a dropped down listbox, we want to popup it up, using 01353 * the current selection. 01354 */ 01355 if (plb->fCaptured) { 01356 /* 01357 * If plb->pcbox is NULL, this is a listbox that 01358 * received a WM_LBUTTONDOWN again w/o receiving 01359 * a WM_LBUTTONUP for the previous WM_LBUTTONDOWN 01360 * bug 01361 */ 01362 if (plb->pcbox && mousetemp) { 01363 _ClientToScreen(plb->spwnd, &pt); 01364 01365 if (!PtInRect(&plb->spwnd->rcWindow, pt)) { 01366 /* 01367 * Cancel selection if clicked outside of combo; 01368 * Accept if clicked on combo button or item. 01369 */ 01370 xxxCBHideListBoxWindow(plb->pcbox, TRUE, FALSE); 01371 } else if (!PtInRect(&plb->spwnd->rcClient, pt)) { 01372 /* 01373 * Let it pass through. Save, restore capture in 01374 * case user is clicking on scrollbar. 01375 */ 01376 plb->fCaptured = FALSE; 01377 NtUserReleaseCapture(); 01378 01379 SendMessageWorker(plb->spwnd, WM_NCLBUTTONDOWN, 01380 FindNCHit(plb->spwnd, POINTTOPOINTS(pt)), 01381 MAKELONG(pt.x, pt.y), FALSE); 01382 01383 NtUserSetCapture(hwnd); 01384 plb->fCaptured = TRUE; 01385 } 01386 01387 break; 01388 } 01389 01390 plb->fCaptured = FALSE; 01391 NtUserReleaseCapture(); 01392 } 01393 01394 if (plb->pcbox) { 01395 01396 /* 01397 * If this listbox is in a combo box, set the focus to the combo 01398 * box window so that the edit control/static text is also 01399 * activated 01400 */ 01401 ThreadLock(plb->pcbox->spwndEdit, &tlpwndEdit); 01402 NtUserSetFocus(HWq(plb->pcbox->spwndEdit)); 01403 ThreadUnlock(&tlpwndEdit); 01404 } else { 01405 01406 /* 01407 * Get the focus if the listbox is clicked in and we don't 01408 * already have the focus. If we don't have the focus after 01409 * this, run away... 01410 */ 01411 NtUserSetFocus(hwnd); 01412 if (!plb->fCaret) 01413 return; 01414 } 01415 01416 if (plb->fAddSelMode) { 01417 01418 /* 01419 * If it is in "Add" mode, quit it using shift f8 key... 01420 * However, since we can't send shift key state, we have to turn 01421 * this off directly... 01422 */ 01423 01424 /* 01425 *SendMessage(HW(plb->spwnd),WM_KEYDOWN, (UINT)VK_F8, 0L); 01426 */ 01427 01428 /* 01429 * Switch off the Caret blinking 01430 */ 01431 NtUserKillTimer(hwnd, IDSYS_CARET); 01432 01433 /* 01434 * Make sure the caret does not vanish 01435 */ 01436 xxxLBSetCaret(plb, TRUE); 01437 plb->fAddSelMode = FALSE; 01438 } 01439 01440 if (!plb->cMac) { 01441 01442 /* 01443 * Don't even bother handling the mouse if no items in the 01444 * listbox since the code below assumes >0 items in the 01445 * listbox. We will just get the focus (the statement above) if 01446 * we don't already have it. 01447 */ 01448 break; 01449 } 01450 01451 if (mousetemp) { 01452 01453 /* 01454 * Mouse down occurred in a empty spot. Just ignore it. 01455 */ 01456 break; 01457 } 01458 01459 plb->fDoubleClick = (wMsg == WM_LBUTTONDBLCLK); 01460 01461 if (!plb->fDoubleClick) { 01462 01463 /* 01464 * This hack put in for the shell. Tell the shell where in the 01465 * listbox the user clicked and at what item number. The shell 01466 * can return 0 to continue normal mouse tracking or TRUE to 01467 * abort mouse tracking. 01468 */ 01469 ThreadLock(plb->spwndParent, &tlpwndParent); 01470 trackPtRetn = (INT)SendMessage(HW(plb->spwndParent), WM_LBTRACKPOINT, 01471 (DWORD)iSelFromPt, MAKELONG(pt.x+plb->xOrigin, pt.y)); 01472 ThreadUnlock(&tlpwndParent); 01473 if (trackPtRetn) { 01474 if (trackPtRetn == 2) { 01475 01476 /* 01477 * Ignore double clicks 01478 */ 01479 NtUserCallNoParam(SFI__RESETDBLCLK); 01480 } 01481 return; 01482 } 01483 } 01484 01485 if (plb->pcbox) { 01486 01487 /* 01488 * Save the last selection if this is a combo box. So that it 01489 * can be restored if user decides to cancel the selection by up 01490 * clicking outside the listbox. 01491 */ 01492 plb->iLastSelection = plb->iSel; 01493 } 01494 01495 /* 01496 * Save for timer 01497 */ 01498 plb->ptPrev = pt; 01499 01500 plb->fMouseDown = TRUE; 01501 NtUserSetCapture(hwnd); 01502 plb->fCaptured = TRUE; 01503 01504 if (plb->fDoubleClick) { 01505 01506 /* 01507 * Double click. Fake a button up and exit 01508 */ 01509 xxxTrackMouse(plb, WM_LBUTTONUP, pt); 01510 return; 01511 } 01512 01513 /* 01514 * Set the system timer so that we can autoscroll if the mouse is 01515 * outside the bounds of the listbox rectangle 01516 */ 01517 NtUserSetTimer(hwnd, IDSYS_SCROLL, gpsi->dtScroll, NULL); 01518 01519 01520 01521 /* 01522 * If extended multiselection listbox, are any modifier key pressed? 01523 */ 01524 if (plb->wMultiple == EXTENDEDSEL) { 01525 if (GetKeyState(VK_SHIFT) < 0) 01526 wModifiers = SHIFTDOWN; 01527 if (GetKeyState(VK_CONTROL) < 0) 01528 wModifiers += CTLDOWN; 01529 01530 /* 01531 * Please Note that (SHIFTDOWN + CTLDOWN) == (SHCTLDOWN) 01532 */ 01533 } 01534 01535 01536 switch (wModifiers) { 01537 case NOMODIFIER: 01538 MouseMoveHandler: 01539 if (plb->iSelBase != iSelFromPt) { 01540 xxxLBSetCaret(plb, FALSE); 01541 } 01542 01543 /* 01544 * We only look at the mouse if the point it is pointing to is 01545 * not selected. Since we are not in ExtendedSelMode, anywhere 01546 * the mouse points, we have to set the selection to that item. 01547 * Hence, if the item isn't selected, it means the mouse never 01548 * pointed to it before so we can select it. We ignore already 01549 * selected items so that we avoid flashing the inverted 01550 * selection rectangle. Also, we could get WM_SYSTIMER simulated 01551 * mouse moves which would cause flashing otherwise... 01552 */ 01553 01554 iSelTemp = (fMouseInRect ? iSelFromPt : -1); 01555 01556 /* 01557 * If the LB is either SingleSel or Extended multisel, clear all 01558 * old selections except the new one being made. 01559 */ 01560 if (plb->wMultiple != MULTIPLESEL) { 01561 xxxResetWorld(plb, iSelTemp, iSelTemp, FALSE); 01562 /* 01563 * This will be TRUE if iSelTemp isn't -1 (like below) 01564 * and also if it is but there is a current selection. 01565 */ 01566 if ((iSelTemp == -1) && (plb->iSel != -1)) { 01567 uEvent = EVENT_OBJECT_SELECTIONREMOVE; 01568 } 01569 } 01570 01571 fSelected = IsSelected(plb, iSelTemp, HILITEONLY); 01572 if (iSelTemp != -1) { 01573 01574 /* 01575 * If it is MULTIPLESEL, then toggle; For others, only if 01576 * not selected already, select it. 01577 */ 01578 if (((plb->wMultiple == MULTIPLESEL) && (wMsg != WM_LBUTTONDBLCLK)) || !fSelected) { 01579 SetSelected(plb, iSelTemp, !fSelected, HILITEANDSEL); 01580 01581 /* 01582 * And invert it 01583 */ 01584 xxxInvertLBItem(plb, iSelTemp, !fSelected); 01585 fSelected = !fSelected; /* Set the new state */ 01586 if (plb->wMultiple == MULTIPLESEL) { 01587 uEvent = (fSelected ? EVENT_OBJECT_SELECTIONADD : 01588 EVENT_OBJECT_SELECTIONREMOVE); 01589 } else { 01590 uEvent = EVENT_OBJECT_SELECTION; 01591 } 01592 } 01593 } 01594 01595 /* 01596 * We have to set iSel in case this is a multisel lb. 01597 */ 01598 plb->iSel = iSelTemp; 01599 01600 /* 01601 * Set the new anchor point 01602 */ 01603 plb->iMouseDown = iSelFromPt; 01604 plb->iLastMouseMove = iSelFromPt; 01605 plb->fNewItemState = fSelected; 01606 01607 break; 01608 case SHIFTDOWN: 01609 01610 /* 01611 * This is so that we can handle click and drag for multisel 01612 * listboxes using Shift modifier key . 01613 */ 01614 plb->iLastMouseMove = plb->iSel = iSelFromPt; 01615 01616 01617 01618 /* 01619 * Check if an anchor point already exists 01620 */ 01621 if (plb->iMouseDown == -1) { 01622 plb->iMouseDown = iSelFromPt; 01623 01624 /* 01625 * Reset all the previous selections 01626 */ 01627 xxxResetWorld(plb, plb->iMouseDown, plb->iMouseDown, FALSE); 01628 01629 /* 01630 * Select the current position 01631 */ 01632 SetSelected(plb, plb->iMouseDown, TRUE, HILITEANDSEL); 01633 xxxInvertLBItem(plb, plb->iMouseDown, TRUE); 01634 /* 01635 * We are changing the selction to this item only 01636 */ 01637 uEvent = EVENT_OBJECT_SELECTION; 01638 } else { 01639 01640 /* 01641 * Reset all the previous selections 01642 */ 01643 xxxResetWorld(plb, plb->iMouseDown, plb->iMouseDown, FALSE); 01644 01645 /* 01646 * Select all items from anchor point upto current click pt 01647 */ 01648 xxxAlterHilite(plb, plb->iMouseDown, iSelFromPt, HILITE, HILITEONLY, FALSE); 01649 uEvent = EVENT_OBJECT_SELECTIONWITHIN; 01650 } 01651 plb->fNewItemState = (UINT)TRUE; 01652 break; 01653 01654 case CTLDOWN: 01655 01656 /* 01657 * This is so that we can handle click and drag for multisel 01658 * listboxes using Control modifier key. 01659 */ 01660 01661 /* 01662 * Reset the anchor point to the current point 01663 */ 01664 plb->iMouseDown = plb->iLastMouseMove = plb->iSel = iSelFromPt; 01665 01666 /* 01667 * The state we will be setting items to 01668 */ 01669 plb->fNewItemState = (UINT)!IsSelected(plb, iSelFromPt, (UINT)HILITEONLY); 01670 01671 /* 01672 * Toggle the current point 01673 */ 01674 SetSelected(plb, iSelFromPt, plb->fNewItemState, HILITEANDSEL); 01675 xxxInvertLBItem(plb, iSelFromPt, plb->fNewItemState); 01676 uEvent = (plb->fNewItemState ? EVENT_OBJECT_SELECTIONADD : 01677 EVENT_OBJECT_SELECTIONREMOVE); 01678 break; 01679 01680 case SHCTLDOWN: 01681 01682 /* 01683 * This is so that we can handle click and drag for multisel 01684 * listboxes using Shift and Control modifier keys. 01685 */ 01686 01687 /* 01688 * Preserve all the previous selections 01689 */ 01690 01691 /* 01692 * Deselect only the selection connected with the last 01693 * anchor point; If the last anchor point is associated with a 01694 * de-selection, then do not do it 01695 */ 01696 if (plb->fNewItemState) { 01697 xxxAlterHilite(plb, plb->iMouseDown, plb->iLastMouseMove, FALSE, HILITEANDSEL, FALSE); 01698 } 01699 plb->iLastMouseMove = plb->iSel = iSelFromPt; 01700 01701 /* 01702 * Check if an anchor point already exists 01703 */ 01704 if (plb->iMouseDown == -1) { 01705 01706 /* 01707 * No existing anchor point; Make the current pt as anchor 01708 */ 01709 plb->iMouseDown = iSelFromPt; 01710 } 01711 01712 /* 01713 * If one exists preserve the most recent anchor point 01714 */ 01715 01716 /* 01717 * The state we will be setting items to 01718 */ 01719 plb->fNewItemState = (UINT)IsSelected(plb, plb->iMouseDown, HILITEONLY); 01720 01721 /* 01722 * Select all items from anchor point upto current click pt 01723 */ 01724 xxxAlterHilite(plb, plb->iMouseDown, iSelFromPt, plb->fNewItemState, HILITEONLY, FALSE); 01725 uEvent = EVENT_OBJECT_SELECTIONWITHIN; 01726 break; 01727 } 01728 01729 /* 01730 * Set the new base point (the outline frame caret). We do the check 01731 * first to avoid flashing the caret unnecessarly. 01732 */ 01733 if (plb->iSelBase != iSelFromPt) { 01734 01735 /* 01736 * Since xxxSetISelBase always turns on the caret, we don't need to 01737 * do it here... 01738 */ 01739 xxxSetISelBase(plb, iSelFromPt); 01740 } 01741 01742 /* 01743 * SetISelBase will change the focus and send a focus event. 01744 * Then we send the selection event. 01745 */ 01746 if (FWINABLE() && uEvent) { 01747 LBEvent(plb, uEvent, iSelFromPt); 01748 } 01749 if (wMsg == WM_LBUTTONDOWN && TestWF(plb->spwnd, WEFDRAGOBJECT)) { 01750 if (NtUserDragDetect(hwnd, pt)) { 01751 01752 /* 01753 * User is trying to drag object... 01754 */ 01755 01756 /* 01757 * Fake an up click so that the item is selected... 01758 */ 01759 xxxTrackMouse(plb, WM_LBUTTONUP, pt); 01760 01761 /* 01762 * Notify parent 01763 * #ifndef WIN16 (32-bit Windows), plb->iSelBase gets 01764 * zero-extended to LONG wParam automatically by the compiler. 01765 */ 01766 ThreadLock(plb->spwndParent, &tlpwndParent); 01767 SendMessage(HW(plb->spwndParent), WM_BEGINDRAG, plb->iSelBase, 01768 (LPARAM)hwnd); 01769 ThreadUnlock(&tlpwndParent); 01770 } else { 01771 xxxTrackMouse(plb, WM_LBUTTONUP, pt); 01772 } 01773 return; 01774 } 01775 break; 01776 01777 case WM_MOUSEMOVE: { 01778 int dist; 01779 int iTimer; 01780 01781 /* 01782 * Save for timer. 01783 */ 01784 plb->ptPrev = pt; 01785 /* 01786 * Autoscroll listbox if mouse button is held down and mouse is 01787 * moved outside of the listbox 01788 */ 01789 if (plb->fMouseDown) { 01790 if (plb->fMultiColumn) { 01791 if ((pt.x < 0) || (pt.x >= rcClient.right - 1)) { 01792 /* 01793 * Reset timer interval based on distance from listbox. 01794 * use a longer default interval because each multicolumn 01795 * scrolling increment is larger 01796 */ 01797 dist = pt.x < 0 ? -pt.x : (pt.x - rcClient.right + 1); 01798 iTimer = ((gpsi->dtScroll * 3) / 2) - ((WORD) dist << 4); 01799 01800 if (plb->fRightAlign) 01801 xxxLBoxCtlHScrollMultiColumn(plb, (pt.x < 0 ? SB_LINEDOWN : SB_LINEUP), 0); 01802 else 01803 xxxLBoxCtlHScrollMultiColumn(plb, (pt.x < 0 ? SB_LINEUP : SB_LINEDOWN), 0); 01804 01805 goto SetTimerAndSel; 01806 } 01807 } else if ((pt.y < 0) || (pt.y >= rcClient.bottom - 1)) { 01808 /* 01809 * Reset timer interval based on distance from listbox. 01810 */ 01811 dist = pt.y < 0 ? -pt.y : (pt.y - rcClient.bottom + 1); 01812 iTimer = gpsi->dtScroll - ((WORD) dist << 4); 01813 01814 xxxLBoxCtlScroll(plb, (pt.y < 0 ? SB_LINEUP : SB_LINEDOWN), 0); 01815 SetTimerAndSel: 01816 NtUserSetTimer(hwnd, IDSYS_SCROLL, max(iTimer, 1), NULL); 01817 ISelFromPt(plb, pt, &iSelFromPt); 01818 } 01819 } else { 01820 /* 01821 * Ignore if not in client since we don't autoscroll 01822 */ 01823 if (!PtInRect(&rcClient, pt)) 01824 break; 01825 } 01826 01827 switch (plb->wMultiple) { 01828 case SINGLESEL: 01829 01830 /* 01831 * If it is a single selection or plain multisel list box 01832 */ 01833 goto MouseMoveHandler; 01834 break; 01835 01836 case MULTIPLESEL: 01837 case EXTENDEDSEL: 01838 01839 /* 01840 * Handle mouse movement with extended selection of items 01841 */ 01842 if (plb->iSelBase != iSelFromPt) { 01843 xxxSetISelBase(plb, iSelFromPt); 01844 01845 /* 01846 * If this is an extended Multi sel list box, then 01847 * adjust the display of the range due to the mouse move 01848 */ 01849 if (plb->wMultiple == EXTENDEDSEL) { 01850 xxxLBBlockHilite(plb, iSelFromPt, FALSE); 01851 if (FWINABLE()) 01852 LBEvent(plb, EVENT_OBJECT_SELECTIONWITHIN, iSelFromPt); 01853 } 01854 plb->iLastMouseMove = iSelFromPt; 01855 } 01856 break; 01857 } 01858 break; 01859 } 01860 case WM_LBUTTONUP: 01861 if (plb->fMouseDown) 01862 xxxLBButtonUp(plb, LBUP_RELEASECAPTURE | LBUP_NOTIFY | 01863 (mousetemp ? LBUP_RESETSELECTION : 0) | 01864 (fMouseInRect ? LBUP_SUCCESS : 0)); 01865 } 01866 } 01867 01868 /***************************************************************************\ 01869 * 01870 * LBButtonUp() 01871 * 01872 * Called in response to both WM_CAPTURECHANGED and WM_LBUTTONUP. 01873 * 01874 \***************************************************************************/ 01875 void xxxLBButtonUp(PLBIV plb, UINT uFlags) 01876 { 01877 01878 CheckLock(plb->spwnd); 01879 01880 /* 01881 * If the list box is an Extended listbox, then change the select status 01882 * of all items between the anchor and the last mouse position to the 01883 * newItemState 01884 */ 01885 if (plb->wMultiple == EXTENDEDSEL) 01886 xxxAlterHilite(plb, plb->iMouseDown, plb->iLastMouseMove, 01887 plb->fNewItemState, SELONLY, FALSE); 01888 01889 /* 01890 * This is a combo box and user upclicked outside the listbox 01891 * so we want to restore the original selection. 01892 */ 01893 if (plb->pcbox && (uFlags & LBUP_RESETSELECTION)) { 01894 int iSelOld; 01895 01896 iSelOld = plb->iSel; 01897 01898 if (iSelOld >= 0) 01899 xxxInvertLBItem(plb, plb->iSel, FALSE); 01900 01901 plb->iSel = plb->iLastSelection; 01902 xxxInvertLBItem(plb, plb->iSel, TRUE); 01903 01904 /* 01905 * Note that we always send selection events before we tell the 01906 * app. This is on purpose--the app may turn around and select 01907 * something else when notified. In which case our event would 01908 * be out of order. 01909 */ 01910 if (FWINABLE()) 01911 LBEvent(plb, EVENT_OBJECT_SELECTION, plb->iSel); 01912 01913 /* 01914 * On win-95 and NT4 the check used to be !(uFlags & LBUP_NOTIFY) which 01915 * is a bug because we would notify even when the lb is not LBUP_NOTIFY 01916 */ 01917 if ((uFlags & LBUP_NOTIFY) && plb->fNotify && (iSelOld != plb->iSel)) 01918 xxxNotifyOwner(plb, LBN_SELCHANGE); 01919 } 01920 01921 NtUserKillTimer(HWq(plb->spwnd), IDSYS_SCROLL); 01922 plb->fMouseDown = FALSE; 01923 if (plb->fCaptured) { 01924 plb->fCaptured = FALSE; 01925 if (uFlags & LBUP_RELEASECAPTURE) 01926 NtUserReleaseCapture(); 01927 } 01928 /* 01929 * Don't scroll item as long as any part of it is visible 01930 */ 01931 if (plb->iSelBase < plb->iTop || 01932 plb->iSelBase > plb->iTop + CItemInWindow(plb, TRUE)) 01933 xxxInsureVisible(plb, plb->iSelBase, FALSE); 01934 01935 if (plb->fNotify) { 01936 if (uFlags & LBUP_NOTIFY) { 01937 if (uFlags & LBUP_SUCCESS) { 01938 /* 01939 * ArtMaster needs this SELCHANGE notification now! 01940 */ 01941 if ((plb->fDoubleClick) && !TestWF(plb->spwnd, WFWIN31COMPAT)) 01942 xxxNotifyOwner(plb, LBN_SELCHANGE); 01943 01944 /* 01945 * Notify owner of click or double click on selection 01946 */ 01947 xxxNotifyOwner(plb, (plb->fDoubleClick) ? LBN_DBLCLK : LBN_SELCHANGE); 01948 } else { 01949 /* 01950 * Notify owner that the attempted selection was cancelled. 01951 */ 01952 xxxNotifyOwner(plb, LBN_SELCANCEL); 01953 } 01954 } else if (uFlags & LBUP_SELCHANGE) { 01955 /* 01956 * Did we do some semi-selecting with mouse moves, then hit Enter? 01957 * If so, we need to make sure the app knows that something was 01958 * really truly selected. 01959 */ 01960 UserAssert(TestWF(plb->spwnd, WFWIN40COMPAT)); 01961 if (plb->iLastSelection != plb->iSel) 01962 xxxNotifyOwner(plb, LBN_SELCHANGE); 01963 01964 } 01965 } 01966 01967 } 01968 01969 01970 /***************************************************************************\ 01971 * IncrementISel 01972 * 01973 * History: 01974 \***************************************************************************/ 01975 01976 INT IncrementISel( 01977 PLBIV plb, 01978 INT iSel, 01979 INT sInc) 01980 { 01981 01982 /* 01983 * Assumes cMac > 0, return iSel+sInc in range [0..cmac). 01984 */ 01985 iSel += sInc; 01986 if (iSel < 0) { 01987 return 0; 01988 } else if (iSel >= plb->cMac) { 01989 return plb->cMac - 1; 01990 } 01991 return iSel; 01992 } 01993 01994 01995 /***************************************************************************\ 01996 * NewITop 01997 * 01998 \***************************************************************************/ 01999 02000 void xxxNewITop(PLBIV plb, INT iTopNew) 02001 { 02002 xxxNewITopEx(plb, iTopNew, 0); 02003 } 02004 02005 02006 /***************************************************************************\ 02007 * xxxNewITopEx 02008 * 02009 * History: 02010 \***************************************************************************/ 02011 02012 void xxxNewITopEx( 02013 PLBIV plb, 02014 INT iTopNew, 02015 DWORD dwTime) 02016 { 02017 int iTopOld; 02018 BOOL fCaretOn; 02019 BOOL fMulti = plb->fMultiColumn; 02020 02021 CheckLock(plb->spwnd); 02022 02023 // Always try to turn off caret whether or not redraw is on 02024 if (fCaretOn = plb->fCaretOn) 02025 xxxLBSetCaret(plb, FALSE); 02026 02027 iTopOld = (fMulti) ? (plb->iTop / plb->itemsPerColumn) : plb->iTop; 02028 plb->iTop = iTopNew; 02029 iTopNew = xxxSetLBScrollParms(plb, (fMulti) ? SB_HORZ : SB_VERT); 02030 plb->iTop = (fMulti) ? (iTopNew * plb->itemsPerColumn) : iTopNew; 02031 02032 if (!IsLBoxVisible(plb)) { 02033 return; 02034 } 02035 02036 if (iTopNew != iTopOld) { 02037 int xAmt, yAmt; 02038 RECT rc; 02039 DWORD dwFlags; 02040 02041 _GetClientRect(plb->spwnd, &rc); 02042 02043 if (fMulti) { 02044 yAmt = 0; 02045 if (abs(iTopNew - iTopOld) > plb->numberOfColumns) 02046 // Handle scrolling a large number of columns properly so that 02047 // we don't overflow the size of a rect. 02048 xAmt = 32000; 02049 else { 02050 xAmt = (iTopOld - iTopNew) * plb->cxColumn; 02051 if (plb->fRightAlign) 02052 xAmt = -xAmt; 02053 } 02054 } else { 02055 xAmt = 0; 02056 if (plb->OwnerDraw == OWNERDRAWVAR) { 02057 // 02058 // Have to fake iTopOld for OWNERDRAWVAR listboxes so that 02059 // the scrolling amount calculations work properly. 02060 // 02061 plb->iTop = iTopOld; 02062 yAmt = LBCalcVarITopScrollAmt(plb, iTopOld, iTopNew); 02063 plb->iTop = iTopNew; 02064 } else if (abs(iTopNew - iTopOld) > plb->cItemFullMax) 02065 yAmt = 32000; 02066 else 02067 yAmt = (iTopOld - iTopNew) * plb->cyChar; 02068 } 02069 02070 dwFlags = LBGetScrollFlags(plb, dwTime); 02071 ScrollWindowEx(HWq(plb->spwnd), xAmt, yAmt, NULL, &rc, NULL, 02072 NULL, dwFlags); 02073 UpdateWindow(HWq(plb->spwnd)); 02074 } 02075 02076 // Note that although we turn off the caret regardless of redraw, we 02077 // only turn it on if redraw is true. Slimy thing to fixup many 02078 // caret related bugs... 02079 if (fCaretOn) 02080 // Turn the caret back on only if we turned it off. This avoids 02081 // annoying caret flicker. 02082 xxxLBSetCaret(plb, TRUE); 02083 } 02084 02085 02086 /***************************************************************************\ 02087 * xxxInsureVisible 02088 * 02089 * History: 02090 \***************************************************************************/ 02091 02092 void xxxInsureVisible( 02093 PLBIV plb, 02094 INT iSel, 02095 BOOL fPartial) /* It is ok for the item to be partially visible */ 02096 { 02097 INT sLastVisibleItem; 02098 02099 CheckLock(plb->spwnd); 02100 02101 if (iSel < plb->iTop) { 02102 xxxNewITop(plb, iSel); 02103 } else { 02104 if (fPartial) { 02105 02106 /* 02107 * 1 must be subtracted to get the last visible item 02108 * A part of the fix for Bug #3727 -- 01/14/91 -- SANKAR 02109 */ 02110 sLastVisibleItem = plb->iTop + CItemInWindow(plb, TRUE) - (INT)1; 02111 } else { 02112 sLastVisibleItem = LastFullVisible(plb); 02113 } 02114 02115 if (plb->OwnerDraw != OWNERDRAWVAR) { 02116 if (iSel > sLastVisibleItem) { 02117 if (plb->fMultiColumn) { 02118 xxxNewITop(plb, 02119 ((iSel / plb->itemsPerColumn) - 02120 max(plb->numberOfColumns-1,0)) * plb->itemsPerColumn); 02121 } else { 02122 xxxNewITop(plb, (INT)max(0, iSel - sLastVisibleItem + plb->iTop)); 02123 } 02124 } 02125 } else if (iSel > sLastVisibleItem) 02126 xxxNewITop(plb, LBPage(plb, iSel, FALSE)); 02127 } 02128 } 02129 02130 /***************************************************************************\ 02131 * xxxLBoxCaretBlinker 02132 * 02133 * Timer callback function toggles Caret 02134 * Since it is a callback, it is APIENTRY 02135 * 02136 * History: 02137 \***************************************************************************/ 02138 02139 VOID xxxLBoxCaretBlinker( 02140 HWND hwnd, 02141 UINT wMsg, 02142 UINT_PTR nIDEvent, 02143 DWORD dwTime) 02144 { 02145 PWND pwnd; 02146 PLBIV plb; 02147 02148 /* 02149 * Standard parameters for a timer callback function that aren't used. 02150 * Mentioned here to avoid compiler warnings 02151 */ 02152 UNREFERENCED_PARAMETER(wMsg); 02153 UNREFERENCED_PARAMETER(nIDEvent); 02154 UNREFERENCED_PARAMETER(dwTime); 02155 02156 pwnd = ValidateHwnd(hwnd); 02157 plb = ((PLBWND)pwnd)->pLBIV; 02158 02159 /* 02160 * leave caret on, don't blink it off (prevents rapid blinks?) 02161 */ 02162 if (ISREMOTESESSION() && plb->fCaretOn) { 02163 return; 02164 } 02165 02166 /* 02167 * Check if the Caret is ON, if so, switch it OFF 02168 */ 02169 xxxLBSetCaret(plb, !plb->fCaretOn); 02170 return; 02171 } 02172 02173 02174 /***************************************************************************\ 02175 * xxxLBoxCtlKeyInput 02176 * 02177 * If msg == LB_KEYDOWN, vKey is the number of the item to go to, 02178 * otherwise it is the virtual key. 02179 * 02180 * History: 02181 \***************************************************************************/ 02182 02183 void xxxLBoxCtlKeyInput( 02184 PLBIV plb, 02185 UINT msg, 02186 UINT vKey) 02187 { 02188 INT i; 02189 INT iNewISel; 02190 INT cItemPageScroll; 02191 PCBOX pcbox; 02192 BOOL fDropDownComboBox; 02193 BOOL fExtendedUIComboBoxClosed; 02194 BOOL hScrollBar = TestWF(plb->spwnd, WFHSCROLL); 02195 UINT wModifiers = 0; 02196 BOOL fSelectKey = FALSE; /* assume it is a navigation key */ 02197 UINT uEvent = 0; 02198 HWND hwnd = HWq(plb->spwnd); 02199 TL tlpwndParent; 02200 TL tlpwnd; 02201 02202 CheckLock(plb->spwnd); 02203 02204 pcbox = plb->pcbox; 02205 02206 /* 02207 * Is this a dropdown style combo box/listbox ? 02208 */ 02209 fDropDownComboBox = pcbox && (pcbox->CBoxStyle & SDROPPABLE); 02210 02211 /* 02212 *Is this an extended ui combo box which is closed? 02213 */ 02214 fExtendedUIComboBoxClosed = fDropDownComboBox && pcbox->fExtendedUI && 02215 !pcbox->fLBoxVisible; 02216 02217 if (plb->fMouseDown || (!plb->cMac && vKey != VK_F4)) { 02218 02219 /* 02220 * Ignore keyboard input if we are in the middle of a mouse down deal or 02221 * if there are no items in the listbox. Note that we let F4's go 02222 * through for combo boxes so that the use can pop up and down empty 02223 * combo boxes. 02224 */ 02225 return; 02226 } 02227 02228 /* 02229 * Modifiers are considered only in EXTENDED sel list boxes. 02230 */ 02231 if (plb->wMultiple == EXTENDEDSEL) { 02232 02233 /* 02234 * If multiselection listbox, are any modifiers used ? 02235 */ 02236 if (GetKeyState(VK_SHIFT) < 0) 02237 wModifiers = SHIFTDOWN; 02238 if (GetKeyState(VK_CONTROL) < 0) 02239 wModifiers += CTLDOWN; 02240 02241 /* 02242 * Please Note that (SHIFTDOWN + CTLDOWN) == (SHCTLDOWN) 02243 */ 02244 } 02245 02246 if (msg == LB_KEYDOWN) { 02247 02248 /* 02249 * This is a listbox "go to specified item" message which means we want 02250 * to go to a particular item number (given by vKey) directly. ie. the 02251 * user has typed a character and we want to go to the item which 02252 * starts with that character. 02253 */ 02254 iNewISel = (INT)vKey; 02255 goto TrackKeyDown; 02256 } 02257 02258 cItemPageScroll = plb->cItemFullMax; 02259 02260 if (cItemPageScroll > 1) 02261 cItemPageScroll--; 02262 02263 if (plb->fWantKeyboardInput) { 02264 02265 /* 02266 * Note: msg must not be LB_KEYDOWN here or we'll be in trouble... 02267 */ 02268 ThreadLock(plb->spwndParent, &tlpwndParent); 02269 iNewISel = (INT)SendMessage(HW(plb->spwndParent), WM_VKEYTOITEM, 02270 MAKELONG(vKey, plb->iSelBase), (LPARAM)hwnd); 02271 ThreadUnlock(&tlpwndParent); 02272 02273 if (iNewISel == -2) { 02274 02275 /* 02276 * Don't move the selection... 02277 */ 02278 return; 02279 } 02280 if (iNewISel != -1) { 02281 02282 /* 02283 * Jump directly to the item provided by the app 02284 */ 02285 goto TrackKeyDown; 02286 } 02287 02288 /* 02289 * else do default processing of the character. 02290 */ 02291 } 02292 02293 switch (vKey) { 02294 // LATER IanJa: not language independent!!! 02295 // We could use VkKeyScan() to find out which is the '\' key 02296 // This is VK_OEM_5 '\|' for US English only. 02297 // Germans, Italians etc. have to type CTRL+^ (etc) for this. 02298 // This is documented as File Manager behaviour for 3.0, but apparently 02299 // not for 3.1., although functionality remains. We should still fix it, 02300 // although German (etc?) '\' is generated with AltGr (Ctrl-Alt) (???) 02301 case VERKEY_BACKSLASH: /* '\' character for US English */ 02302 02303 /* 02304 * Check if this is CONTROL-\ ; If so Deselect all items 02305 */ 02306 if ((wModifiers & CTLDOWN) && (plb->wMultiple != SINGLESEL)) { 02307 xxxLBSetCaret(plb, FALSE); 02308 xxxResetWorld(plb, plb->iSelBase, plb->iSelBase, FALSE); 02309 02310 /* 02311 * And select the current item 02312 */ 02313 SetSelected(plb, plb->iSelBase, TRUE, HILITEANDSEL); 02314 xxxInvertLBItem(plb, plb->iSelBase, TRUE); 02315 uEvent = EVENT_OBJECT_SELECTION; 02316 goto CaretOnAndNotify; 02317 } 02318 return; 02319 break; 02320 02321 case VK_DIVIDE: /* NumPad '/' character on enhanced keyboard */ 02322 // LATER IanJa: not language independent!!! 02323 // We could use VkKeyScan() to find out which is the '/' key 02324 // This is VK_OEM_2 '/?' for US English only. 02325 // Germans, Italians etc. have to type CTRL+# (etc) for this. 02326 case VERKEY_SLASH: /* '/' character */ 02327 02328 /* 02329 * Check if this is CONTROL-/ ; If so select all items 02330 */ 02331 if ((wModifiers & CTLDOWN) && (plb->wMultiple != SINGLESEL)) { 02332 xxxLBSetCaret(plb, FALSE); 02333 xxxResetWorld(plb, -1, -1, TRUE); 02334 02335 uEvent = EVENT_OBJECT_SELECTIONWITHIN; 02336 02337 CaretOnAndNotify: 02338 xxxLBSetCaret(plb, TRUE); 02339 if (FWINABLE()) { 02340 LBEvent(plb, uEvent, plb->iSelBase); 02341 } 02342 xxxNotifyOwner(plb, LBN_SELCHANGE); 02343 } 02344 return; 02345 break; 02346 02347 case VK_F8: 02348 02349 /* 02350 * The "Add" mode is possible only in Multiselection listboxes... Get 02351 * into it via SHIFT-F8... (Yes, sometimes these UI people are sillier 02352 * than your "typical dumb user"...) 02353 */ 02354 if (plb->wMultiple != SINGLESEL && wModifiers == SHIFTDOWN) { 02355 02356 /* 02357 * We have to make the caret blink! Do something... 02358 */ 02359 if (plb->fAddSelMode) { 02360 02361 /* 02362 * Switch off the Caret blinking 02363 */ 02364 NtUserKillTimer(hwnd, IDSYS_CARET); 02365 02366 /* 02367 * Make sure the caret does not vanish 02368 */ 02369 xxxLBSetCaret(plb, TRUE); 02370 } else { 02371 02372 /* 02373 * Create a timer to make the caret blink 02374 */ 02375 NtUserSetTimer(hwnd, IDSYS_CARET, gpsi->dtCaretBlink, 02376 xxxLBoxCaretBlinker); 02377 } 02378 02379 /* 02380 * Toggle the Add mode flag 02381 */ 02382 plb->fAddSelMode = (UINT)!plb->fAddSelMode; 02383 } 02384 return; 02385 case VK_SPACE: /* Selection key is space */ 02386 i = 0; 02387 fSelectKey = TRUE; 02388 break; 02389 02390 case VK_PRIOR: 02391 if (fExtendedUIComboBoxClosed) { 02392 02393 /* 02394 * Disable movement keys for TandyT. 02395 */ 02396 return; 02397 } 02398 02399 if (plb->OwnerDraw == OWNERDRAWVAR) { 02400 i = LBPage(plb, plb->iSelBase, FALSE) - plb->iSelBase; 02401 } else { 02402 i = -cItemPageScroll; 02403 } 02404 break; 02405 02406 case VK_NEXT: 02407 if (fExtendedUIComboBoxClosed) { 02408 02409 /* 02410 * Disable movement keys for TandyT. 02411 */ 02412 return; 02413 } 02414 02415 if (plb->OwnerDraw == OWNERDRAWVAR) { 02416 i = LBPage(plb, plb->iSelBase, TRUE) - plb->iSelBase; 02417 } else { 02418 i = cItemPageScroll; 02419 } 02420 break; 02421 02422 case VK_HOME: 02423 if (fExtendedUIComboBoxClosed) { 02424 02425 /* 02426 * Disable movement keys for TandyT. 02427 */ 02428 return; 02429 } 02430 02431 i = (INT_MIN/2)+1; /* A very big negative number */ 02432 break; 02433 02434 case VK_END: 02435 if (fExtendedUIComboBoxClosed) { 02436 02437 /* 02438 * Disable movement keys for TandyT. 02439 */ 02440 return; 02441 } 02442 02443 i = (INT_MAX/2)-1; /* A very big positive number */ 02444 break; 02445 02446 case VK_LEFT: 02447 if (plb->fMultiColumn) { 02448 if (plb->fRightAlign 02449 #ifdef USE_MIRRORING 02450 ^ (!!TestWF(plb->spwnd, WEFLAYOUTRTL)) 02451 02452 #endif 02453 ) 02454 goto ReallyRight; 02455 ReallyLeft: 02456 if (plb->iSelBase / plb->itemsPerColumn == 0) { 02457 i = 0; 02458 } else { 02459 i = -plb->itemsPerColumn; 02460 } 02461 break; 02462 } 02463 02464 if (hScrollBar) { 02465 goto HandleHScrolling; 02466 } else { 02467 02468 /* 02469 * Fall through and handle this as if the up arrow was pressed. 02470 */ 02471 02472 vKey = VK_UP; 02473 } 02474 02475 /* 02476 * Fall through 02477 */ 02478 02479 case VK_UP: 02480 if (fExtendedUIComboBoxClosed) 02481 // Disable movement keys for TandyT. 02482 return; 02483 02484 i = -1; 02485 break; 02486 02487 case VK_RIGHT: 02488 if (plb->fMultiColumn) { 02489 if (plb->fRightAlign 02490 #ifdef USE_MIRRORING 02491 ^ (!!TestWF(plb->spwnd, WEFLAYOUTRTL)) 02492 02493 #endif 02494 ) 02495 goto ReallyLeft; 02496 ReallyRight: 02497 if (plb->iSelBase / plb->itemsPerColumn == plb->cMac / plb->itemsPerColumn) { 02498 i = 0; 02499 } else { 02500 i = plb->itemsPerColumn; 02501 } 02502 break; 02503 } 02504 if (hScrollBar) { 02505 HandleHScrolling: 02506 PostMessage(hwnd, WM_HSCROLL, 02507 (vKey == VK_RIGHT ? SB_LINEDOWN : SB_LINEUP), 0L); 02508 return; 02509 } else { 02510 02511 /* 02512 * Fall through and handle this as if the down arrow was 02513 * pressed. 02514 */ 02515 vKey = VK_DOWN; 02516 } 02517 02518 /* 02519 * Fall through 02520 */ 02521 02522 case VK_DOWN: 02523 if (fExtendedUIComboBoxClosed) { 02524 02525 /* 02526 * If the combo box is closed, down arrow should open it. 02527 */ 02528 if (!pcbox->fLBoxVisible) { 02529 02530 /* 02531 * If the listbox isn't visible, just show it 02532 */ 02533 ThreadLock(pcbox->spwnd, &tlpwnd); 02534 xxxCBShowListBoxWindow(pcbox, TRUE); 02535 ThreadUnlock(&tlpwnd); 02536 } 02537 return; 02538 } 02539 i = 1; 02540 break; 02541 02542 case VK_ESCAPE: 02543 case VK_RETURN: 02544 if (!fDropDownComboBox || !pcbox->fLBoxVisible) 02545 return; 02546 02547 // | If this is a dropped listbox for a combobox and the ENTER | 02548 // | key is pressed, close up the listbox, so FALLTHRU | 02549 // V V 02550 02551 case VK_F4: 02552 if (fDropDownComboBox && !pcbox->fExtendedUI) { 02553 02554 /* 02555 * If we are a dropdown combo box/listbox we want to process 02556 * this key. BUT for TandtT, we don't do anything on VK_F4 if we 02557 * are in extended ui mode. 02558 */ 02559 ThreadLock(pcbox->spwnd, &tlpwnd); 02560 if (!pcbox->fLBoxVisible) { 02561 02562 /* 02563 * If the listbox isn't visible, just show it 02564 */ 02565 xxxCBShowListBoxWindow(pcbox, (vKey != VK_ESCAPE)); 02566 } else { 02567 02568 /* 02569 * Ok, the listbox is visible. So hide the listbox window. 02570 */ 02571 xxxCBHideListBoxWindow(pcbox, TRUE, (vKey != VK_ESCAPE)); 02572 } 02573 ThreadUnlock(&tlpwnd); 02574 } 02575 02576 /* 02577 * Fall through to the return 02578 */ 02579 02580 default: 02581 return; 02582 } 02583 02584 /* 02585 * Find out what the new selection should be 02586 */ 02587 iNewISel = IncrementISel(plb, plb->iSelBase, i); 02588 02589 02590 if (plb->wMultiple == SINGLESEL) { 02591 if (plb->iSel == iNewISel) { 02592 02593 /* 02594 * If we are single selection and the keystroke is moving us to an 02595 * item which is already selected, we don't have to do anything... 02596 */ 02597 return; 02598 } 02599 02600 uEvent = EVENT_OBJECT_SELECTION; 02601 02602 plb->iTypeSearch = 0; 02603 if ((vKey == VK_UP || vKey == VK_DOWN) && 02604 !IsSelected(plb, plb->iSelBase, HILITEONLY)) { 02605 02606 /* 02607 * If the caret is on an unselected item and the user just hits the 02608 * up or down arrow key (ie. with no shift or ctrl modifications), 02609 * then we will just select the item the cursor is at. This is 02610 * needed for proper behavior in combo boxes but do we always want 02611 * to run this code??? Note that this is only used in single 02612 * selection list boxes since it doesn't make sense in the 02613 * multiselection case. Note that an LB_KEYDOWN message must not be 02614 * checked here because the vKey will be an item number not a 02615 * VK_and we will goof. Thus, trackkeydown label is below this to 02616 * fix a bug caused by it being above this... 02617 */ 02618 iNewISel = (plb->iSelBase == -1) ? 0 : plb->iSelBase; 02619 } 02620 } 02621 02622 TrackKeyDown: 02623 02624 xxxSetISelBase(plb, iNewISel); 02625 02626 xxxLBSetCaret(plb, FALSE); 02627 02628 if (wModifiers & SHIFTDOWN) { 02629 // Check if iMouseDown is un-initialised 02630 if (plb->iMouseDown == -1) 02631 plb->iMouseDown = iNewISel; 02632 if (plb->iLastMouseMove == -1) 02633 plb->iLastMouseMove = iNewISel; 02634 02635 // Check if we are in ADD mode 02636 if (plb->fAddSelMode) { 02637 /* Preserve all the pre-existing selections except the 02638 * ones connected with the last anchor point; If the last 02639 * Preserve all the previous selections 02640 */ 02641 /* Deselect only the selection connected with the last 02642 * anchor point; If the last anchor point is associated 02643 * with de-selection, then do not do it 02644 */ 02645 02646 if (!plb->fNewItemState) 02647 plb->iLastMouseMove = plb->iMouseDown; 02648 02649 /* We haven't done anything here because, LBBlockHilite() 02650 * will take care of wiping out the selection between 02651 * Anchor point and iLastMouseMove and select the block 02652 * between anchor point and current cursor location 02653 */ 02654 } else { 02655 /* We are not in ADD mode */ 02656 /* Remove all selections except between the anchor point 02657 * and last mouse move because it will be taken care of in 02658 * LBBlockHilite 02659 */ 02660 xxxResetWorld(plb, plb->iMouseDown, plb->iLastMouseMove, FALSE); 02661 } 02662 02663 uEvent = EVENT_OBJECT_SELECTIONWITHIN; 02664 02665 /* LBBlockHilite takes care to deselect the block between 02666 * the anchor point and iLastMouseMove and select the block 02667 * between the anchor point and the current cursor location 02668 */ 02669 /* Toggle all items to the same selection state as the item 02670 * item at the anchor point) from the anchor point to the 02671 * current cursor location. 02672 */ 02673 plb->fNewItemState = IsSelected(plb, plb->iMouseDown, SELONLY); 02674 xxxLBBlockHilite(plb, iNewISel, TRUE); 02675 02676 plb->iLastMouseMove = iNewISel; 02677 /* Preserve the existing anchor point */ 02678 } else { 02679 /* Check if this is in ADD mode */ 02680 if ((plb->fAddSelMode) || (plb->wMultiple == MULTIPLESEL)) { 02681 /* Preserve all pre-exisiting selections */ 02682 if (fSelectKey) { 02683 /* Toggle the selection state of the current item */ 02684 plb->fNewItemState = !IsSelected(plb, iNewISel, SELONLY); 02685 SetSelected(plb, iNewISel, plb->fNewItemState, HILITEANDSEL); 02686 02687 xxxInvertLBItem(plb, iNewISel, plb->fNewItemState); 02688 02689 /* Set the anchor point at the current location */ 02690 plb->iLastMouseMove = plb->iMouseDown = iNewISel; 02691 uEvent = (plb->fNewItemState ? EVENT_OBJECT_SELECTIONADD : 02692 EVENT_OBJECT_SELECTIONREMOVE); 02693 } 02694 } else { 02695 /* We are NOT in ADD mode */ 02696 /* Remove all existing selections except iNewISel, to 02697 * avoid flickering. 02698 */ 02699 xxxResetWorld(plb, iNewISel, iNewISel, FALSE); 02700 02701 /* Select the current item */ 02702 SetSelected(plb, iNewISel, TRUE, HILITEANDSEL); 02703 xxxInvertLBItem(plb, iNewISel, TRUE); 02704 02705 /* Set the anchor point at the current location */ 02706 plb->iLastMouseMove = plb->iMouseDown = iNewISel; 02707 uEvent = EVENT_OBJECT_SELECTION; 02708 } 02709 } 02710 02711 /* 02712 * Move the cursor to the new location 02713 */ 02714 xxxInsureVisible(plb, iNewISel, FALSE); 02715 xxxLBShowHideScrollBars(plb); 02716 02717 xxxLBSetCaret(plb, TRUE); 02718 02719 if (FWINABLE() && uEvent) { 02720 LBEvent(plb, uEvent, iNewISel); 02721 } 02722 02723 /* 02724 * Should we notify our parent? 02725 */ 02726 if (plb->fNotify) { 02727 if (fDropDownComboBox && pcbox->fLBoxVisible) { 02728 02729 /* 02730 * If we are in a drop down combo box/listbox and the listbox is 02731 * visible, we need to set the fKeyboardSelInListBox bit so that the 02732 * combo box code knows not to hide the listbox since the selchange 02733 * message is caused by the user keyboarding through... 02734 */ 02735 pcbox->fKeyboardSelInListBox = TRUE; 02736 plb->iLastSelection = iNewISel; 02737 } 02738 xxxNotifyOwner(plb, LBN_SELCHANGE); 02739 } 02740 } 02741 02742 02743 /***************************************************************************\ 02744 * Compare 02745 * 02746 * Is lpstr1 equal/prefix/less-than/greater-than lsprst2 (case-insensitive) ? 02747 * 02748 * LATER IanJa: this assume a longer string is never a prefix of a longer one. 02749 * Also assumes that removing 1 or more characters from the end of a string will 02750 * give a string tahs sort before the original. These assumptions are not valid 02751 * for all languages. We nedd better support from NLS. (Consider French 02752 * accents, Spanish c/ch, ligatures, German sharp-s/SS, etc.) 02753 * 02754 * History: 02755 \***************************************************************************/ 02756 02757 INT Compare( 02758 LPCWSTR pwsz1, 02759 LPCWSTR pwsz2, 02760 DWORD dwLocaleId) 02761 { 02762 UINT len1 = wcslen(pwsz1); 02763 UINT len2 = wcslen(pwsz2); 02764 INT result; 02765 02766 /* 02767 * CompareStringW returns: 02768 * 1 = pwsz1 < pwsz2 02769 * 2 = pwsz1 == pwsz2 02770 * 3 = pwsz1 > pwsz2 02771 */ 02772 result = CompareStringW((LCID)dwLocaleId, NORM_IGNORECASE, 02773 pwsz1, min(len1,len2), pwsz2, min(len1, len2)); 02774 02775 if (result == CSTR_LESS_THAN) { 02776 return LT; 02777 } else if (result == CSTR_EQUAL) { 02778 if (len1 == len2) { 02779 return EQ; 02780 } else if (len1 < len2) { 02781 /* 02782 * LATER IanJa: should not assume shorter string is a prefix 02783 * Spanish "c" and "ch", ligatures, German sharp-s/SS etc. 02784 */ 02785 return PREFIX; 02786 } 02787 } 02788 return GT; 02789 } 02790 02791 /***************************************************************************\ 02792 * xxxFindString 02793 * 02794 * Scans for a string in the listbox prefixed by or equal to lpstr. 02795 * For OWNERDRAW listboxes without strings and without the sort style, we 02796 * try to match the long app supplied values. 02797 * 02798 * History: 02799 * 16-Apr-1992 beng The NODATA case 02800 \***************************************************************************/ 02801 02802 INT xxxFindString( 02803 PLBIV plb, 02804 LPWSTR lpstr, 02805 INT sStart, 02806 INT code, 02807 BOOL fWrap) 02808 { 02809 /* 02810 * Search for a prefix match (case-insensitive equal/prefix) 02811 * sStart == -1 means start from beginning, else start looking at sStart+1 02812 * assumes cMac > 0. 02813 */ 02814 INT sInd; /* index of string */ 02815 INT sStop; /* index to stop searching at */ 02816 lpLBItem pRg; 02817 TL tlpwndParent; 02818 INT sortResult; 02819 02820 /* 02821 * Owner-Draw version of pRg 02822 */ 02823 #define pODRg ((lpLBODItem)pRg) 02824 COMPAREITEMSTRUCT cis; 02825 LPWSTR listboxString; 02826 02827 CheckLock(plb->spwnd); 02828 02829 if (plb->fHasStrings && (!lpstr || !*lpstr)) 02830 return LB_ERR; 02831 02832 if (!plb->fHasData) { 02833 RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "FindString called on NODATA lb"); 02834 return LB_ERR; 02835 } 02836 02837 if ((sInd = sStart + 1) >= plb->cMac) 02838 sInd = (fWrap ? 0 : plb->cMac - 1); 02839 02840 sStop = (fWrap ? sInd : 0); 02841 02842 /* 02843 * If at end and no wrap, stop right away 02844 */ 02845 if (((sStart >= plb->cMac - 1) && !fWrap) || (plb->cMac < 1)) { 02846 return LB_ERR; 02847 } 02848 02849 /* 02850 * Apps could pass in an invalid sStart like -2 and we would blow up. 02851 * Win 3.1 would not so we need to fixup sInd to be zero 02852 */ 02853 if (sInd < 0) 02854 sInd = 0; 02855 02856 pRg = (lpLBItem)(plb->rgpch); 02857 02858 do { 02859 if (plb->fHasStrings) { 02860 02861 /* 02862 * Searching for string matches. 02863 */ 02864 listboxString = (LPWSTR)((LPBYTE)plb->hStrings + pRg[sInd].offsz); 02865 02866 if (code == PREFIX && 02867 listboxString && 02868 *lpstr != TEXT('[') && 02869 *listboxString == TEXT('[')) { 02870 02871 /* 02872 * If we are looking for a prefix string and the first items 02873 * in this string are [- then we ignore them. This is so 02874 * that in a directory listbox, the user can goto drives 02875 * by selecting the drive letter. 02876 */ 02877 listboxString++; 02878 if (*listboxString == TEXT('-')) 02879 listboxString++; 02880 } 02881 02882 if (Compare(lpstr, listboxString, plb->dwLocaleId) <= code) { 02883 goto FoundIt; 02884 } 02885 02886 } else { 02887 if (plb->fSort) { 02888 02889 /* 02890 * Send compare item messages to the parent for sorting 02891 */ 02892 cis.CtlType = ODT_LISTBOX; 02893 cis.CtlID = PtrToUlong(plb->spwnd->spmenu); 02894 cis.hwndItem = HWq(plb->spwnd); 02895 cis.itemID1 = (UINT)-1; 02896 cis.itemData1 = (ULONG_PTR)lpstr; 02897 cis.itemID2 = (UINT)sInd; 02898 cis.itemData2 = pODRg[sInd].itemData; 02899 cis.dwLocaleId = plb->dwLocaleId; 02900 02901 ThreadLock(plb->spwndParent, &tlpwndParent); 02902 sortResult = (INT)SendMessage(HW(plb->spwndParent), WM_COMPAREITEM, 02903 cis.CtlID, (LPARAM)&cis); 02904 ThreadUnlock(&tlpwndParent); 02905 02906 02907 if (sortResult == -1) { 02908 sortResult = LT; 02909 } else if (sortResult == 1) { 02910 sortResult = GT; 02911 } else { 02912 sortResult = EQ; 02913 } 02914 02915 if (sortResult <= code) { 02916 goto FoundIt; 02917 } 02918 } else { 02919 02920 /* 02921 * Searching for app supplied long data matches. 02922 */ 02923 if ((ULONG_PTR)lpstr == pODRg[sInd].itemData) 02924 goto FoundIt; 02925 } 02926 } 02927 02928 /* 02929 * Wrap round to beginning of list 02930 */ 02931 if (++sInd == plb->cMac) 02932 sInd = 0; 02933 } while (sInd != sStop); 02934 02935 sInd = -1; 02936 02937 FoundIt: 02938 return sInd; 02939 } 02940 02941 02942 /***************************************************************************\ 02943 * xxxLBoxCtlCharInput 02944 * 02945 * History: 02946 \***************************************************************************/ 02947 02948 void xxxLBoxCtlCharInput( 02949 PLBIV plb, 02950 UINT inputChar, 02951 BOOL fAnsi) 02952 { 02953 INT iSel; 02954 BOOL fControl; 02955 TL tlpwndParent; 02956 02957 CheckLock(plb->spwnd); 02958 02959 if (plb->cMac == 0 || plb->fMouseDown) { 02960 02961 /* 02962 * Get out if we are in the middle of mouse routines or if we have no 02963 * items in the listbox, we just return without doing anything. 02964 */ 02965 return; 02966 } 02967 02968 fControl = (GetKeyState(VK_CONTROL) < 0); 02969 02970 switch (inputChar) { 02971 case VK_ESCAPE: 02972 plb->iTypeSearch = 0; 02973 if (plb->pszTypeSearch) 02974 plb->pszTypeSearch[0] = 0; 02975 break; 02976 02977 case VK_BACK: 02978 if (plb->iTypeSearch) { 02979 plb->pszTypeSearch[plb->iTypeSearch--] = 0; 02980 if (plb->fSort) { 02981 iSel = -1; 02982 goto TypeSearch; 02983 } 02984 } 02985 break; 02986 02987 case VK_SPACE: 02988 if (plb->fAddSelMode || plb->wMultiple == MULTIPLESEL) 02989 break; 02990 /* Otherwise, for single/extended selection listboxes not in add 02991 * selection mode, let the space go thru as a type search character 02992 * FALL THRU 02993 */ 02994 02995 default: 02996 02997 /* 02998 * Move selection to first item beginning with the character the 02999 * user typed. We don't want do this if we are using owner draw. 03000 */ 03001 03002 if (fAnsi && IS_DBCS_ENABLED() && IsDBCSLeadByteEx(THREAD_CODEPAGE(), (BYTE)inputChar)) { 03003 WCHAR wch; 03004 LPWSTR lpwstr = &wch; 03005 03006 inputChar = DbcsCombine(HWq(plb->spwnd), (BYTE)inputChar); 03007 RIPMSG1(RIP_VERBOSE, "xxxLBoxCtlCharInput: combined DBCS. 0x%04x", inputChar); 03008 03009 if (inputChar == 0) { 03010 RIPMSG1(RIP_WARNING, "xxxLBoxCtlCharInput: cannot combine two DBCS. LB=0x%02x", 03011 inputChar); 03012 break; 03013 } 03014 // If it is DBCS, let's ignore the ctrl status. 03015 fControl = FALSE; 03016 03017 // Convert DBCS to UNICODE. 03018 // Note: Leading byte is in the low byte, trailing byte is in high byte. 03019 // Let's assume Little Endian CPUs only, so inputChar can directly be 03020 // input for MBSToWCSEx as an ANSI string. 03021 if (MBToWCSEx(THREAD_CODEPAGE(), (LPCSTR)&inputChar, 2, &lpwstr, 1, FALSE) == 0) { 03022 RIPMSG1(RIP_WARNING, "xxxLBoxCtlCharInput: cannot convert 0x%04x to UNICODE.", 03023 inputChar); 03024 break; 03025 } 03026 inputChar = wch; 03027 } 03028 03029 if (plb->fHasStrings) { 03030 // Incremental Type Search processing 03031 // 03032 // update szTypeSearch string and then move to the first item from 03033 // the current selection whose prefix matches szTypeSearch 03034 // 03035 // the szTypeSearch will continue to grow until a "long enough" 03036 // gap between key entries is encountered -- at which point any 03037 // more searching will start over 03038 03039 /* 03040 * Undo CONTROL-char to char 03041 */ 03042 if (fControl && inputChar < 0x20) 03043 inputChar += 0x40; 03044 03045 if (plb->iTypeSearch == MAX_TYPESEARCH) { 03046 NtUserMessageBeep(0); 03047 break; 03048 } 03049 iSel = -1; 03050 03051 if (plb->pszTypeSearch == NULL) 03052 plb->pszTypeSearch = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(WCHAR) * (MAX_TYPESEARCH + 1)); 03053 03054 if (plb->pszTypeSearch == NULL) { 03055 NtUserMessageBeep(0); 03056 break; 03057 } 03058 03059 plb->pszTypeSearch[plb->iTypeSearch++] = (WCHAR) inputChar; 03060 plb->pszTypeSearch[plb->iTypeSearch] = 0; 03061 03062 TypeSearch: 03063 if (plb->fSort) { 03064 // Set timer to determine when to kill incremental searching 03065 NtUserSetTimer(HWq(plb->spwnd), IDSYS_LBSEARCH, 03066 gpsi->dtLBSearch, NULL); 03067 } else { 03068 // If this is not a sorted listbox, no incremental search. 03069 plb->iTypeSearch = 0; 03070 iSel = plb->iSelBase; 03071 } 03072 03073 03074 /* 03075 * Search for the item beginning with the given character starting 03076 * at iSel+1. We will wrap the search to the beginning of the 03077 * listbox if we don't find the item. If SHIFT is down and we are 03078 * a multiselection lb, then the item's state will be set to 03079 * plb->fNewItemState according to the current mode. 03080 */ 03081 iSel = xxxFindString(plb, plb->pszTypeSearch, iSel, PREFIX, TRUE); 03082 if (iSel == -1) { 03083 // no match found -- check for prefix match 03084 // (i.e. "p" find FIRST item that starts with 'p', 03085 // "pp" find NEXT item that starts with 'p') 03086 if(plb->iTypeSearch) 03087 { 03088 plb->iTypeSearch--; 03089 if ((plb->iTypeSearch == 1) && (plb->pszTypeSearch[0] == plb->pszTypeSearch[1])) 03090 { 03091 plb->pszTypeSearch[1] = 0; 03092 iSel = xxxFindString(plb, plb->pszTypeSearch, plb->iSelBase, PREFIX, TRUE); 03093 } 03094 } 03095 } 03096 // if match is found -- select it 03097 if (iSel != -1) 03098 { 03099 CtlKeyInput: 03100 xxxLBoxCtlKeyInput(plb, LB_KEYDOWN, iSel); 03101 03102 } 03103 } else { 03104 if (plb->spwndParent != NULL) { 03105 ThreadLock(plb->spwndParent, &tlpwndParent); 03106 iSel = (INT)SendMessageWorker(plb->spwndParent, WM_CHARTOITEM, 03107 MAKELONG(inputChar, plb->iSelBase), (LPARAM)HWq(plb->spwnd), fAnsi); 03108 ThreadUnlock(&tlpwndParent); 03109 } else 03110 iSel = -1; 03111 03112 if (iSel != -1 && iSel != -2) 03113 goto CtlKeyInput; 03114 03115 } 03116 break; 03117 } 03118 } 03119 03120 03121 /***************************************************************************\ 03122 * LBoxGetSelItems 03123 * 03124 * effects: For multiselection listboxes, this returns the total number of 03125 * selection items in the listbox if fCountOnly is true. or it fills an array 03126 * (lParam) with the items numbers of the first wParam selected items. 03127 * 03128 * History: 03129 \***************************************************************************/ 03130 03131 int LBoxGetSelItems( 03132 PLBIV plb, 03133 BOOL fCountOnly, 03134 int wParam, 03135 LPINT lParam) 03136 { 03137 int i; 03138 int itemsselected = 0; 03139 03140 if (plb->wMultiple == SINGLESEL) 03141 return LB_ERR; 03142 03143 for (i = 0; i < plb->cMac; i++) { 03144 if (IsSelected(plb, i, SELONLY)) { 03145 if (!fCountOnly) { 03146 if (itemsselected < wParam) 03147 *lParam++ = i; 03148 else { 03149 03150 /* 03151 * That's all the items we can fit in the array. 03152 */ 03153 return itemsselected; 03154 } 03155 } 03156 itemsselected++; 03157 } 03158 } 03159 03160 return itemsselected; 03161 } 03162 03163 03164 /***************************************************************************\ 03165 * xxxLBSetRedraw 03166 * 03167 * Handle WM_SETREDRAW message 03168 * 03169 * History: 03170 \***************************************************************************/ 03171 03172 void xxxLBSetRedraw( 03173 PLBIV plb, 03174 BOOL fRedraw) 03175 { 03176 CheckLock(plb->spwnd); 03177 03178 if (fRedraw) 03179 fRedraw = TRUE; 03180 03181 if (plb->fRedraw != (UINT)fRedraw) { 03182 plb->fRedraw = !!fRedraw; 03183 03184 if (fRedraw) { 03185 xxxLBSetCaret(plb, TRUE); 03186 xxxLBShowHideScrollBars(plb); 03187 03188 if (plb->fDeferUpdate) { 03189 plb->fDeferUpdate = FALSE; 03190 RedrawWindow(HWq(plb->spwnd), NULL, NULL, 03191 RDW_INVALIDATE | RDW_ERASE | 03192 RDW_FRAME | RDW_ALLCHILDREN); 03193 } 03194 } 03195 } 03196 } 03197 03198 /***************************************************************************\ 03199 * xxxLBSelRange 03200 * 03201 * Selects the range of items between i and j, inclusive. 03202 * 03203 * History: 03204 \***************************************************************************/ 03205 03206 void xxxLBSelRange( 03207 PLBIV plb, 03208 int iStart, 03209 int iEnd, 03210 BOOL fnewstate) 03211 { 03212 DWORD temp; 03213 RECT rc; 03214 03215 CheckLock(plb->spwnd); 03216 03217 if (iStart > iEnd) { 03218 temp = iEnd; 03219 iEnd = iStart; 03220 iStart = temp; 03221 } 03222 03223 /* 03224 * We don't want to loop through items that don't exist. 03225 */ 03226 iEnd = min(plb->cMac, iEnd); 03227 iStart = max(iStart, 0); 03228 if (iStart > iEnd) 03229 return; 03230 03231 03232 /* 03233 * iEnd could be equal to MAXINT which is why we test temp and iEnd 03234 * as DWORDs. 03235 */ 03236 for (temp = iStart; temp <= (DWORD)iEnd; temp++) { 03237 03238 if (IsSelected(plb, temp, SELONLY) != fnewstate) { 03239 SetSelected(plb, temp, fnewstate, HILITEANDSEL); 03240 LBGetItemRect(plb, temp, &rc); 03241 03242 xxxLBInvalidateRect(plb, (LPRECT)&rc, FALSE); 03243 } 03244 03245 } 03246 UserAssert(plb->wMultiple); 03247 if (FWINABLE()) { 03248 LBEvent(plb, EVENT_OBJECT_SELECTIONWITHIN, iStart); 03249 } 03250 } 03251 03252 03253 /***************************************************************************\ 03254 * xxxLBSetCurSel 03255 * 03256 * History: 03257 \***************************************************************************/ 03258 03259 int xxxLBSetCurSel( 03260 PLBIV plb, 03261 int iSel) 03262 { 03263 CheckLock(plb->spwnd); 03264 03265 if (!(plb->wMultiple || iSel < -1 || iSel >= plb->cMac)) { 03266 xxxLBSetCaret(plb, FALSE); 03267 if (plb->iSel != -1) { 03268 03269 /* 03270 * This prevents scrolling when iSel == -1 03271 */ 03272 if (iSel != -1) 03273 xxxInsureVisible(plb, iSel, FALSE); 03274 03275 /* 03276 * Turn off old selection 03277 */ 03278 xxxInvertLBItem(plb, plb->iSel, FALSE); 03279 } 03280 03281 if (iSel != -1) { 03282 xxxInsureVisible(plb, iSel, FALSE); 03283 plb->iSelBase = plb->iSel = iSel; 03284 03285 /* 03286 * Highlight new selection 03287 */ 03288 xxxInvertLBItem(plb, plb->iSel, TRUE); 03289 } else { 03290 plb->iSel = -1; 03291 if (plb->cMac) 03292 plb->iSelBase = min(plb->iSelBase, plb->cMac-1); 03293 else 03294 plb->iSelBase = 0; 03295 } 03296 03297 if (FWINABLE()) { 03298 /* 03299 * Send both focus and selection events 03300 */ 03301 if (_IsWindowVisible(plb->spwnd)) { 03302 LBEvent(plb, EVENT_OBJECT_FOCUS, plb->iSelBase); 03303 LBEvent(plb, EVENT_OBJECT_SELECTION, plb->iSel); 03304 } 03305 } 03306 03307 xxxLBSetCaret(plb, TRUE); 03308 return plb->iSel; 03309 } 03310 03311 return LB_ERR; 03312 } 03313 03314 03315 /***************************************************************************\ 03316 * LBSetItemData 03317 * 03318 * Makes the item at index contain the data given. 03319 * 03320 * History: 03321 * 16-Apr-1992 beng The NODATA listbox case 03322 \***************************************************************************/ 03323 03324 int LBSetItemData( 03325 PLBIV plb, 03326 int index, 03327 LONG_PTR data) 03328 { 03329 LPSTR lpItemText; 03330 03331 /* 03332 * v-ronaar: fix bug #25865, don't allow negative indices! 03333 */ 03334 if ((index != -1) && ((UINT) index >= (UINT) plb->cMac)) { 03335 RIPERR1(ERROR_INVALID_INDEX, RIP_WARNING, "LBSetItemData with invalid index %x", index); 03336 return LB_ERR; 03337 } 03338 03339 /* 03340 * No-data listboxes just ignore all LB_SETITEMDATA calls 03341 */ 03342 if (!plb->fHasData) { 03343 return TRUE; 03344 } 03345 03346 lpItemText = (LPSTR)plb->rgpch; 03347 03348 if (index == -1) { 03349 03350 /* 03351 * index == -1 means set the data to all the items 03352 */ 03353 if (plb->fHasStrings) { 03354 for (index = 0; index < plb->cMac; index++) { 03355 03356 ((lpLBItem)lpItemText)->itemData = data; 03357 lpItemText += sizeof(LBItem); 03358 } 03359 } else { 03360 for (index = 0; index < plb->cMac; index++) { 03361 03362 ((lpLBODItem)lpItemText)->itemData = data; 03363 lpItemText += sizeof(LBODItem); 03364 } 03365 } 03366 return TRUE; 03367 } 03368 03369 if (plb->fHasStrings) { 03370 03371 lpItemText = (LPSTR)(lpItemText + (index * sizeof(LBItem))); 03372 ((lpLBItem)lpItemText)->itemData = data; 03373 } else { 03374 03375 lpItemText = (LPSTR)(lpItemText + (index * sizeof(LBODItem))); 03376 ((lpLBODItem)lpItemText)->itemData = data; 03377 } 03378 return TRUE; 03379 } 03380 03381 /***************************************************************************\ 03382 * xxxCheckRedraw 03383 * 03384 * History: 03385 \***************************************************************************/ 03386 03387 void xxxCheckRedraw( 03388 PLBIV plb, 03389 BOOL fConditional, 03390 INT sItem) 03391 { 03392 CheckLock(plb->spwnd); 03393 03394 if (fConditional && plb->cMac && 03395 (sItem > (plb->iTop + CItemInWindow(plb, TRUE)))) 03396 return; 03397 03398 /* 03399 * Don't do anything if the parent is not visible. 03400 */ 03401 xxxLBInvalidateRect(plb, (LPRECT)NULL, TRUE); 03402 } 03403 03404 03405 /***************************************************************************\ 03406 * xxxCaretDestroy 03407 * 03408 * History: 03409 \***************************************************************************/ 03410 03411 void xxxCaretDestroy( 03412 PLBIV plb) 03413 { 03414 CheckLock(plb->spwnd); 03415 03416 /* 03417 * We're losing the focus. Act like up clicks are happening so we release 03418 * capture, set the current selection, notify the parent, etc. 03419 */ 03420 if (plb->fCaptured) 03421 03422 /* 03423 * If we have the capture and we lost the focus, that means we already 03424 * changed the selection and we have to notify also the parent about 03425 * this. So we need to add also the LBUP_SUCCESS flag in this case. 03426 */ 03427 03428 xxxLBButtonUp(plb, LBUP_RELEASECAPTURE | LBUP_NOTIFY | 03429 (plb->fMouseDown ? LBUP_SUCCESS : 0)); 03430 03431 if (plb->fAddSelMode) { 03432 03433 /* 03434 * Switch off the Caret blinking 03435 */ 03436 NtUserKillTimer(HWq(plb->spwnd), IDSYS_CARET); 03437 03438 /* 03439 * Make sure the caret goes away 03440 */ 03441 xxxLBSetCaret(plb, FALSE); 03442 plb->fAddSelMode = FALSE; 03443 } 03444 03445 plb->fCaret = FALSE; 03446 } 03447 03448 03449 /***************************************************************************\ 03450 * xxxLbSetSel 03451 * 03452 * History: 03453 \***************************************************************************/ 03454 03455 LONG xxxLBSetSel( 03456 PLBIV plb, 03457 BOOL fSelect, /* New state to set selection to */ 03458 INT iSel) 03459 { 03460 INT sItem; 03461 RECT rc; 03462 UINT uEvent = 0; 03463 03464 CheckLock(plb->spwnd); 03465 03466 /* 03467 * Bug 17656. WinZip's accelerator key for 'DeSelect All' sends a LB_SETSEL 03468 * message with lparam = 0x0000ffff instead of 0xffffffff(-1). If iSel 03469 * is equal to 0x0000ffff and there are less than 0xffff elements in the 03470 * list we set iSel equal to 0xffffffff. 03471 */ 03472 if ((iSel == (UINT)0xffff) && (iSel >= plb->cMac)) { 03473 iSel = -1; 03474 RIPMSG0(RIP_WARNING, "Sign extending iSel=0xffff to 0xffffffff"); 03475 } 03476 03477 03478 if ((plb->wMultiple == SINGLESEL) || (iSel != -1 && iSel >= plb->cMac)) { 03479 RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, 03480 "xxxLBSetSel:Invalid iSel or SINGLESEL listbox"); 03481 return LB_ERR; 03482 } 03483 03484 xxxLBSetCaret(plb, FALSE); 03485 03486 if (iSel == -1/*(INT)0xffff*/) { 03487 03488 /* 03489 * Set/clear selection from all items if -1 03490 */ 03491 for (sItem = 0; sItem < plb->cMac; sItem++) { 03492 if (IsSelected(plb, sItem, SELONLY) != fSelect) { 03493 SetSelected(plb, sItem, fSelect, HILITEANDSEL); 03494 if (LBGetItemRect(plb, sItem, &rc)) { 03495 xxxLBInvalidateRect(plb, &rc, FALSE); 03496 } 03497 } 03498 } 03499 xxxLBSetCaret(plb, TRUE); 03500 uEvent = EVENT_OBJECT_SELECTIONWITHIN; 03501 } else { 03502 if (fSelect) { 03503 03504 /* 03505 * Check if the item if fully hidden and scroll it into view if it 03506 * is. Note that we don't want to scroll partially visible items 03507 * into full view because this breaks the shell... 03508 */ 03509 xxxInsureVisible(plb, iSel, TRUE); 03510 plb->iSelBase = plb->iSel = iSel; 03511 03512 plb->iMouseDown = plb->iLastMouseMove = iSel; 03513 uEvent = EVENT_OBJECT_FOCUS; 03514 } else { 03515 uEvent = EVENT_OBJECT_SELECTIONREMOVE; 03516 } 03517 SetSelected(plb, iSel, fSelect, HILITEANDSEL); 03518 03519 /* 03520 * Note that we set the caret on bit directly so that we avoid flicker 03521 * when drawing this item. ie. We turn on the caret, redraw the item and 03522 * turn it back on again. 03523 */ 03524 if (!fSelect && plb->iSelBase != iSel) { 03525 xxxLBSetCaret(plb, TRUE); 03526 } else if (plb->fCaret) { 03527 plb->fCaretOn = TRUE; 03528 } 03529 03530 if (LBGetItemRect(plb, iSel, &rc)) { 03531 xxxLBInvalidateRect(plb, &rc, FALSE); 03532 } 03533 } 03534 03535 if (FWINABLE() && _IsWindowVisible(plb->spwnd)) { 03536 if (uEvent == EVENT_OBJECT_FOCUS) { 03537 LBEvent(plb, uEvent, plb->iSelBase); 03538 uEvent = EVENT_OBJECT_SELECTION; 03539 } 03540 LBEvent(plb, uEvent, iSel); 03541 } 03542 03543 return 0; 03544 } 03545 03546 03547 /***************************************************************************\ 03548 * xxxLBoxDrawItem 03549 * 03550 * This fills the draw item struct with some constant data for the given 03551 * item. The caller will only have to modify a small part of this data 03552 * for specific needs. 03553 * 03554 * History: 03555 * 16-Apr-1992 beng The NODATA case 03556 \***************************************************************************/ 03557 03558 void xxxLBoxDrawItem( 03559 PLBIV plb, 03560 INT item, 03561 UINT itemAction, 03562 UINT itemState, 03563 LPRECT lprect) 03564 { 03565 DRAWITEMSTRUCT dis; 03566 TL tlpwndParent; 03567 03568 CheckLock(plb->spwnd); 03569 03570 /* 03571 * Fill the DRAWITEMSTRUCT with the unchanging constants 03572 */ 03573 03574 dis.CtlType = ODT_LISTBOX; 03575 dis.CtlID = PtrToUlong(plb->spwnd->spmenu); 03576 03577 /* 03578 * Use -1 if an invalid item number is being used. This is so that the app 03579 * can detect if it should draw the caret (which indicates the lb has the 03580 * focus) in an empty listbox 03581 */ 03582 dis.itemID = (UINT)(item < plb->cMac ? item : -1); 03583 dis.itemAction = itemAction; 03584 dis.hwndItem = HWq(plb->spwnd); 03585 dis.hDC = plb->hdc; 03586 dis.itemState = itemState | 03587 (UINT)(TestWF(plb->spwnd, WFDISABLED) ? ODS_DISABLED : 0); 03588 03589 if (TestWF(plb->spwnd, WEFPUIFOCUSHIDDEN)) { 03590 dis.itemState |= ODS_NOFOCUSRECT; 03591 } 03592 if (TestWF(plb->spwnd, WEFPUIACCELHIDDEN)) { 03593 dis.itemState |= ODS_NOACCEL; 03594 } 03595 03596 /* 03597 * Set the app supplied data 03598 */ 03599 if (!plb->cMac || !plb->fHasData) { 03600 03601 /* 03602 * If no strings or no items, just use 0 for data. This is so that we 03603 * can display a caret when there are no items in the listbox. 03604 * 03605 * Lazy-eval listboxes of course have no data to pass - only itemID. 03606 */ 03607 dis.itemData = 0L; 03608 } else { 03609 dis.itemData = LBGetItemData(plb, item); 03610 } 03611 03612 CopyRect(&dis.rcItem, lprect); 03613 03614 /* 03615 * Set the window origin to the horizontal scroll position. This is so that 03616 * text can always be drawn at 0,0 and the view region will only start at 03617 * the horizontal scroll offset. We pass this as wParam 03618 */ 03619 /* 03620 * Note: Only pass the itemID in wParam for 3.1 or newer apps. We break 03621 * ccMail otherwise. 03622 */ 03623 03624 ThreadLock(plb->spwndParent, &tlpwndParent); 03625 SendMessage(HW(plb->spwndParent), WM_DRAWITEM, 03626 TestWF(plb->spwndParent, WFWIN31COMPAT) ? dis.CtlID : 0, 03627 (LPARAM)&dis); 03628 ThreadUnlock(&tlpwndParent); 03629 } 03630 03631 03632 /***************************************************************************\ 03633 * xxxLBBlockHilite 03634 * 03635 * In Extended selection mode for multiselection listboxes, when 03636 * mouse is draged to a new position, the range being marked should be 03637 * properly sized(parts of which will be highlighted/dehighlighted). 03638 * NOTE: This routine assumes that iSelFromPt and LasMouseMove are not 03639 * equal because only in that case this needs to be called; 03640 * NOTE: This routine calculates the region whose display attribute is to 03641 * be changed in an optimised way. Instead of de-highlighting the 03642 * the old range completely and highlight the new range, it omits 03643 * the regions that overlap and repaints only the non-pverlapping 03644 * area. 03645 * fKeyBoard = TRUE if this is called for Keyboard interface 03646 * FALSE if called from Mouse interface routines 03647 * 03648 * History: 03649 \***************************************************************************/ 03650 03651 void xxxLBBlockHilite( 03652 PLBIV plb, 03653 INT iSelFromPt, 03654 BOOL fKeyBoard) 03655 { 03656 INT sCurPosOffset; 03657 INT sLastPosOffset; 03658 INT sHiliteOrSel; 03659 BOOL fUseSelStatus; 03660 BOOL DeHiliteStatus; 03661 03662 CheckLock(plb->spwnd); 03663 03664 if (fKeyBoard) { 03665 03666 /* 03667 * Set both Hilite and Selection states 03668 */ 03669 sHiliteOrSel = HILITEANDSEL; 03670 03671 /* 03672 * Do not use the Selection state while de-hiliting 03673 */ 03674 fUseSelStatus = FALSE; 03675 DeHiliteStatus = FALSE; 03676 } else { 03677 03678 /* 03679 * Set/Reset only the Hilite state 03680 */ 03681 sHiliteOrSel = HILITEONLY; 03682 03683 /* 03684 * Use the selection state for de-hilighting 03685 */ 03686 fUseSelStatus = TRUE; 03687 DeHiliteStatus = plb->fNewItemState; 03688 } 03689 03690 03691 03692 /* 03693 * The idea of the routine is to : 03694 * 1. De-hilite the old range (iMouseDown to iLastMouseDown) and 03695 * 2. Hilite the new range (iMouseDwon to iSelFromPt) 03696 */ 03697 03698 /* 03699 * Offset of current mouse position from the anchor point 03700 */ 03701 sCurPosOffset = plb->iMouseDown - iSelFromPt; 03702 03703 /* 03704 * Offset of last mouse position from the anchor point 03705 */ 03706 sLastPosOffset = plb->iMouseDown - plb->iLastMouseMove; 03707 03708 /* 03709 * Check if both current position and last position lie on the same 03710 * side of the anchor point. 03711 */ 03712 if ((sCurPosOffset * sLastPosOffset) >= 0) { 03713 03714 /* 03715 * Yes they are on the same side; So, highlight/dehighlight only 03716 * the difference. 03717 */ 03718 if (abs(sCurPosOffset) > abs(sLastPosOffset)) { 03719 xxxAlterHilite(plb, plb->iLastMouseMove, iSelFromPt, 03720 plb->fNewItemState, sHiliteOrSel, FALSE); 03721 } else { 03722 xxxAlterHilite(plb, iSelFromPt, plb->iLastMouseMove, DeHiliteStatus, 03723 sHiliteOrSel, fUseSelStatus); 03724 } 03725 } else { 03726 xxxAlterHilite(plb, plb->iMouseDown, plb->iLastMouseMove, 03727 DeHiliteStatus, sHiliteOrSel, fUseSelStatus); 03728 xxxAlterHilite(plb, plb->iMouseDown, iSelFromPt, 03729 plb->fNewItemState, sHiliteOrSel, FALSE); 03730 } 03731 } 03732 03733 03734 /***************************************************************************\ 03735 * xxxAlterHilite 03736 * 03737 * Changes the hilite state of (i..j] (ie. excludes i, includes j in case 03738 * you've forgotten this silly notation) to fHilite. It inverts this changes 03739 * the hilite state. 03740 * 03741 * OpFlags: HILITEONLY Only change the display state of the items 03742 * SELONLY Only Change the selection state of the items 03743 * HILITEANDSELECT Do both. 03744 * fHilite: 03745 * HILITE/TRUE 03746 * DEHILITE/FALSE 03747 * fSelStatus: 03748 * if TRUE, use the selection state of the item to hilite/dehilite 03749 * if FALSE, use the fHilite parameter to hilite/dehilite 03750 * 03751 * History: 03752 \***************************************************************************/ 03753 03754 void xxxAlterHilite( 03755 PLBIV plb, 03756 INT i, 03757 INT j, 03758 BOOL fHilite, 03759 INT OpFlags, 03760 BOOL fSelStatus) 03761 { 03762 INT low; 03763 INT high; 03764 INT sLastInWindow; 03765 BOOL fCaretOn; 03766 BOOL fSelected; 03767 03768 CheckLock(plb->spwnd); 03769 03770 sLastInWindow = plb->iTop + CItemInWindow(plb, TRUE); 03771 sLastInWindow = min(sLastInWindow, plb->cMac - 1); 03772 high = max(i, j) + 1; 03773 03774 if (fCaretOn = plb->fCaretOn) { 03775 xxxLBSetCaret(plb, FALSE); 03776 } 03777 03778 for (low = min(i, j); low < high; low++) { 03779 if (low != i) { 03780 if (OpFlags & HILITEONLY) { 03781 if (fSelStatus) { 03782 fSelected = IsSelected(plb, low, SELONLY); 03783 } else { 03784 fSelected = fHilite; 03785 } 03786 if (IsSelected(plb, low, HILITEONLY) != fSelected) { 03787 if (plb->iTop <= low && low <= sLastInWindow) { 03788 03789 /* 03790 * Invert the item only if it is visible 03791 */ 03792 xxxInvertLBItem(plb, low, fSelected); 03793 } 03794 SetSelected(plb, low, fSelected, HILITEONLY); 03795 } 03796 } 03797 03798 if (OpFlags & SELONLY) { 03799 SetSelected(plb, low, fHilite, SELONLY); 03800 } 03801 } 03802 } 03803 03804 if (fCaretOn) { 03805 xxxLBSetCaret(plb, TRUE); 03806 } 03807 }

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