test: lboxctl2.c Source File

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 }