ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

listbox.c
Go to the documentation of this file.
00001 /*
00002  * Listbox controls
00003  *
00004  * Copyright 1996 Alexandre Julliard
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  *
00020  * NOTES
00021  *
00022  * This code was audited for completeness against the documented features
00023  * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
00024  * 
00025  * Unless otherwise noted, we believe this code to be complete, as per
00026  * the specification mentioned above.
00027  * If you discover missing features, or bugs, please note them below.
00028  *
00029  * TODO:
00030  *    - GetListBoxInfo()
00031  *    - LB_GETLISTBOXINFO
00032  *    - LBS_NODATA
00033  */
00034 
00035 #include <user32.h>
00036 
00037 #include <wine/debug.h>
00038 
00039 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
00040 
00041 /* Items array granularity */
00042 #define LB_ARRAY_GRANULARITY 16
00043 
00044 /* Scrolling timeout in ms */
00045 #define LB_SCROLL_TIMEOUT 50
00046 
00047 /* Listbox system timer id */
00048 #define LB_TIMER_ID  2
00049 
00050 /* flag listbox changed while setredraw false - internal style */
00051 #define LBS_DISPLAYCHANGED 0x80000000
00052 
00053 /* Item structure */
00054 typedef struct
00055 {
00056     LPWSTR    str;       /* Item text */
00057     BOOL      selected;  /* Is item selected? */
00058     UINT      height;    /* Item height (only for OWNERDRAWVARIABLE) */
00059     ULONG_PTR data;      /* User data */
00060 } LB_ITEMDATA;
00061 
00062 /* Listbox structure */
00063 typedef struct
00064 {
00065     HWND        self;           /* Our own window handle */
00066     HWND        owner;          /* Owner window to send notifications to */
00067     UINT        style;          /* Window style */
00068     INT         width;          /* Window width */
00069     INT         height;         /* Window height */
00070     LB_ITEMDATA  *items;        /* Array of items */
00071     INT         nb_items;       /* Number of items */
00072     INT         top_item;       /* Top visible item */
00073     INT         selected_item;  /* Selected item */
00074     INT         focus_item;     /* Item that has the focus */
00075     INT         anchor_item;    /* Anchor item for extended selection */
00076     INT         item_height;    /* Default item height */
00077     INT         page_size;      /* Items per listbox page */
00078     INT         column_width;   /* Column width for multi-column listboxes */
00079     INT         horz_extent;    /* Horizontal extent (0 if no hscroll) */
00080     INT         horz_pos;       /* Horizontal position */
00081     INT         nb_tabs;        /* Number of tabs in array */
00082     INT        *tabs;           /* Array of tabs */
00083     INT         avg_char_width; /* Average width of characters */
00084     BOOL        caret_on;       /* Is caret on? */
00085     BOOL        captured;       /* Is mouse captured? */
00086     BOOL    in_focus;
00087     HFONT       font;           /* Current font */
00088     LCID          locale;       /* Current locale for string comparisons */
00089     LPHEADCOMBO   lphc;     /* ComboLBox */
00090     LONG        UIState;        // REACTOS
00091 } LB_DESCR;
00092 
00093 
00094 #define IS_OWNERDRAW(descr) \
00095     ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
00096 
00097 #define HAS_STRINGS(descr) \
00098     (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
00099 
00100 
00101 #define IS_MULTISELECT(descr) \
00102     ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
00103      !((descr)->style & LBS_NOSEL))
00104 
00105 #define SEND_NOTIFICATION(descr,code) \
00106     (SendMessageW( (descr)->owner, WM_COMMAND, \
00107      MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
00108 
00109 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
00110 
00111 /* Current timer status */
00112 typedef enum
00113 {
00114     LB_TIMER_NONE,
00115     LB_TIMER_UP,
00116     LB_TIMER_LEFT,
00117     LB_TIMER_DOWN,
00118     LB_TIMER_RIGHT
00119 } TIMER_DIRECTION;
00120 
00121 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
00122 
00123 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
00124 
00125 /*********************************************************************
00126  * listbox class descriptor
00127  */
00128 static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0};
00129 const struct builtin_class_descr LISTBOX_builtin_class =
00130 {
00131     listboxW,             /* name */
00132     CS_DBLCLKS /*| CS_PARENTDC*/,  /* style */
00133     ListBoxWndProcA,      /* procA */
00134     ListBoxWndProcW,      /* procW */
00135     sizeof(LB_DESCR *),   /* extra */
00136     IDC_ARROW,            /* cursor */
00137     0                     /* brush */
00138 };
00139 
00140 
00141 /*********************************************************************
00142  * combolbox class descriptor
00143  */
00144 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
00145 const struct builtin_class_descr COMBOLBOX_builtin_class =
00146 {
00147     combolboxW,           /* name */
00148     CS_DBLCLKS | CS_SAVEBITS,  /* style */
00149     ListBoxWndProcA,      /* procA */
00150     ListBoxWndProcW,      /* procW */
00151     sizeof(LB_DESCR *),   /* extra */
00152     IDC_ARROW,            /* cursor */
00153     0                     /* brush */
00154 };
00155 
00156 
00157 /***********************************************************************
00158  *           LISTBOX_GetCurrentPageSize
00159  *
00160  * Return the current page size
00161  */
00162 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
00163 {
00164     INT i, height;
00165     if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
00166     for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
00167     {
00168         if ((height += descr->items[i].height) > descr->height) break;
00169     }
00170     if (i == descr->top_item) return 1;
00171     else return i - descr->top_item;
00172 }
00173 
00174 
00175 /***********************************************************************
00176  *           LISTBOX_GetMaxTopIndex
00177  *
00178  * Return the maximum possible index for the top of the listbox.
00179  */
00180 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
00181 {
00182     INT max, page;
00183 
00184     if (descr->style & LBS_OWNERDRAWVARIABLE)
00185     {
00186         page = descr->height;
00187         for (max = descr->nb_items - 1; max >= 0; max--)
00188             if ((page -= descr->items[max].height) < 0) break;
00189         if (max < descr->nb_items - 1) max++;
00190     }
00191     else if (descr->style & LBS_MULTICOLUMN)
00192     {
00193         if ((page = descr->width / descr->column_width) < 1) page = 1;
00194         max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
00195         max = (max - page) * descr->page_size;
00196     }
00197     else
00198     {
00199         max = descr->nb_items - descr->page_size;
00200     }
00201     if (max < 0) max = 0;
00202     return max;
00203 }
00204 
00205 
00206 /***********************************************************************
00207  *           LISTBOX_UpdateScroll
00208  *
00209  * Update the scrollbars. Should be called whenever the content
00210  * of the listbox changes.
00211  */
00212 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
00213 {
00214     SCROLLINFO info;
00215 
00216     /* Check the listbox scroll bar flags individually before we call
00217        SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
00218        no WS_VSCROLL, we end up with an uninitialized, visible horizontal
00219        scroll bar when we do not need one.
00220     if (!(descr->style & WS_VSCROLL)) return;
00221     */
00222 
00223     /*   It is important that we check descr->style, and not wnd->dwStyle,
00224        for WS_VSCROLL, as the former is exactly the one passed in
00225        argument to CreateWindow.
00226          In Windows (and from now on in Wine :) a listbox created
00227        with such a style (no WS_SCROLL) does not update
00228        the scrollbar with listbox-related data, thus letting
00229        the programmer use it for his/her own purposes. */
00230 
00231     if (descr->style & LBS_NOREDRAW) return;
00232     info.cbSize = sizeof(info);
00233 
00234     if (descr->style & LBS_MULTICOLUMN)
00235     {
00236         info.nMin  = 0;
00237         info.nMax  = (descr->nb_items - 1) / descr->page_size;
00238         info.nPos  = descr->top_item / descr->page_size;
00239         info.nPage = descr->width / descr->column_width;
00240         if (info.nPage < 1) info.nPage = 1;
00241         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
00242         if (descr->style & LBS_DISABLENOSCROLL)
00243             info.fMask |= SIF_DISABLENOSCROLL;
00244         if (descr->style & WS_HSCROLL)
00245             SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
00246         info.nMax = 0;
00247         info.fMask = SIF_RANGE;
00248         if (descr->style & WS_VSCROLL)
00249             SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
00250     }
00251     else
00252     {
00253         info.nMin  = 0;
00254         info.nMax  = descr->nb_items - 1;
00255         info.nPos  = descr->top_item;
00256         info.nPage = LISTBOX_GetCurrentPageSize( descr );
00257         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
00258         if (descr->style & LBS_DISABLENOSCROLL)
00259             info.fMask |= SIF_DISABLENOSCROLL;
00260         if (descr->style & WS_VSCROLL)
00261             SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
00262 
00263         if (descr->horz_extent)
00264         {
00265             info.nMin  = 0;
00266             info.nMax  = descr->horz_extent - 1;
00267             info.nPos  = descr->horz_pos;
00268             info.nPage = descr->width;
00269             info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
00270             if (descr->style & LBS_DISABLENOSCROLL)
00271                 info.fMask |= SIF_DISABLENOSCROLL;
00272             if (descr->style & WS_HSCROLL)
00273                 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
00274         }
00275     }
00276 }
00277 
00278 
00279 /***********************************************************************
00280  *           LISTBOX_SetTopItem
00281  *
00282  * Set the top item of the listbox, scrolling up or down if necessary.
00283  */
00284 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
00285 {
00286     INT max = LISTBOX_GetMaxTopIndex( descr );
00287 
00288     TRACE("setting top item %d, scroll %d\n", index, scroll);
00289 
00290     if (index > max) index = max;
00291     if (index < 0) index = 0;
00292     if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
00293     if (descr->top_item == index) return LB_OKAY;
00294     if (descr->style & LBS_MULTICOLUMN)
00295     {
00296         INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
00297         if (scroll && (abs(diff) < descr->width))
00298             ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
00299                               SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
00300 
00301         else
00302             scroll = FALSE;
00303     }
00304     else if (scroll)
00305     {
00306         INT diff;
00307         if (descr->style & LBS_OWNERDRAWVARIABLE)
00308         {
00309             INT i;
00310             diff = 0;
00311             if (index > descr->top_item)
00312             {
00313                 for (i = index - 1; i >= descr->top_item; i--)
00314                     diff -= descr->items[i].height;
00315             }
00316             else
00317             {
00318                 for (i = index; i < descr->top_item; i++)
00319                     diff += descr->items[i].height;
00320             }
00321         }
00322         else
00323             diff = (descr->top_item - index) * descr->item_height;
00324 
00325         if (abs(diff) < descr->height)
00326             ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
00327                             SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
00328         else
00329             scroll = FALSE;
00330     }
00331     if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
00332     descr->top_item = index;
00333     LISTBOX_UpdateScroll( descr );
00334     return LB_OKAY;
00335 }
00336 
00337 
00338 /***********************************************************************
00339  *           LISTBOX_UpdatePage
00340  *
00341  * Update the page size. Should be called when the size of
00342  * the client area or the item height changes.
00343  */
00344 static void LISTBOX_UpdatePage( LB_DESCR *descr )
00345 {
00346     INT page_size;
00347 
00348     if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
00349                        page_size = 1;
00350     if (page_size == descr->page_size) return;
00351     descr->page_size = page_size;
00352     if (descr->style & LBS_MULTICOLUMN)
00353         InvalidateRect( descr->self, NULL, TRUE );
00354     LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
00355 }
00356 
00357 
00358 /***********************************************************************
00359  *           LISTBOX_UpdateSize
00360  *
00361  * Update the size of the listbox. Should be called when the size of
00362  * the client area changes.
00363  */
00364 static void LISTBOX_UpdateSize( LB_DESCR *descr )
00365 {
00366     RECT rect;
00367 
00368     GetClientRect( descr->self, &rect );
00369     descr->width  = rect.right - rect.left;
00370     descr->height = rect.bottom - rect.top;
00371     if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
00372     {
00373         INT remaining;
00374         RECT rect;
00375 
00376         GetWindowRect( descr->self, &rect );
00377         if(descr->item_height != 0)
00378             remaining = descr->height % descr->item_height;
00379         else
00380             remaining = 0;
00381         if ((descr->height > descr->item_height) && remaining)
00382         {
00383             TRACE("[%p]: changing height %d -> %d\n",
00384                   descr->self, descr->height, descr->height - remaining );
00385             SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
00386                           rect.bottom - rect.top - remaining,
00387                           SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
00388             return;
00389         }
00390     }
00391     TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
00392     LISTBOX_UpdatePage( descr );
00393     LISTBOX_UpdateScroll( descr );
00394 
00395     /* Invalidate the focused item so it will be repainted correctly */
00396     if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
00397     {
00398         InvalidateRect( descr->self, &rect, FALSE );
00399     }
00400 }
00401 
00402 
00403 /***********************************************************************
00404  *           LISTBOX_GetItemRect
00405  *
00406  * Get the rectangle enclosing an item, in listbox client coordinates.
00407  * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
00408  */
00409 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect )
00410 {
00411     /* Index <= 0 is legal even on empty listboxes */
00412     if (index && (index >= descr->nb_items))
00413     {
00414         memset(rect, 0, sizeof(*rect));
00415         SetLastError(ERROR_INVALID_INDEX);
00416         return LB_ERR;
00417     }
00418     SetRect( rect, 0, 0, descr->width, descr->height );
00419     if (descr->style & LBS_MULTICOLUMN)
00420     {
00421         INT col = (index / descr->page_size) -
00422                         (descr->top_item / descr->page_size);
00423         rect->left += col * descr->column_width;
00424         rect->right = rect->left + descr->column_width;
00425         rect->top += (index % descr->page_size) * descr->item_height;
00426         rect->bottom = rect->top + descr->item_height;
00427     }
00428     else if (descr->style & LBS_OWNERDRAWVARIABLE)
00429     {
00430         INT i;
00431         rect->right += descr->horz_pos;
00432         if ((index >= 0) && (index < descr->nb_items))
00433         {
00434             if (index < descr->top_item)
00435             {
00436                 for (i = descr->top_item-1; i >= index; i--)
00437                     rect->top -= descr->items[i].height;
00438             }
00439             else
00440             {
00441                 for (i = descr->top_item; i < index; i++)
00442                     rect->top += descr->items[i].height;
00443             }
00444             rect->bottom = rect->top + descr->items[index].height;
00445 
00446         }
00447     }
00448     else
00449     {
00450         rect->top += (index - descr->top_item) * descr->item_height;
00451         rect->bottom = rect->top + descr->item_height;
00452         rect->right += descr->horz_pos;
00453     }
00454 
00455     TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect));
00456 
00457     return ((rect->left < descr->width) && (rect->right > 0) &&
00458             (rect->top < descr->height) && (rect->bottom > 0));
00459 }
00460 
00461 
00462 /***********************************************************************
00463  *           LISTBOX_GetItemFromPoint
00464  *
00465  * Return the item nearest from point (x,y) (in client coordinates).
00466  */
00467 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
00468 {
00469     INT index = descr->top_item;
00470 
00471     if (!descr->nb_items) return -1;  /* No items */
00472     if (descr->style & LBS_OWNERDRAWVARIABLE)
00473     {
00474         INT pos = 0;
00475         if (y >= 0)
00476         {
00477             while (index < descr->nb_items)
00478             {
00479                 if ((pos += descr->items[index].height) > y) break;
00480                 index++;
00481             }
00482         }
00483         else
00484         {
00485             while (index > 0)
00486             {
00487                 index--;
00488                 if ((pos -= descr->items[index].height) <= y) break;
00489             }
00490         }
00491     }
00492     else if (descr->style & LBS_MULTICOLUMN)
00493     {
00494         if (y >= descr->item_height * descr->page_size) return -1;
00495         if (y >= 0) index += y / descr->item_height;
00496         if (x >= 0) index += (x / descr->column_width) * descr->page_size;
00497         else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
00498     }
00499     else
00500     {
00501         index += (y / descr->item_height);
00502     }
00503     if (index < 0) return 0;
00504     if (index >= descr->nb_items) return -1;
00505     return index;
00506 }
00507 
00508 
00509 /***********************************************************************
00510  *           LISTBOX_PaintItem
00511  *
00512  * Paint an item.
00513  */
00514 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, 
00515                    INT index, UINT action, BOOL ignoreFocus )
00516 {
00517     LB_ITEMDATA *item = NULL;
00518     if (index < descr->nb_items) item = &descr->items[index];
00519 
00520     if (IS_OWNERDRAW(descr))
00521     {
00522         DRAWITEMSTRUCT dis;
00523         RECT r;
00524         HRGN hrgn;
00525 
00526     if (!item)
00527     {
00528         if (action == ODA_FOCUS)
00529             { // REACTOS
00530                if (!(descr->UIState & UISF_HIDEFOCUS))
00531                    DrawFocusRect( hdc, rect );
00532             } //
00533         else
00534             ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
00535         return;
00536     }
00537 
00538         /* some programs mess with the clipping region when
00539         drawing the item, *and* restore the previous region
00540         after they are done, so a region has better to exist
00541         else everything ends clipped */
00542         GetClientRect(descr->self, &r);
00543         hrgn = set_control_clipping( hdc, &r );
00544 
00545         dis.CtlType      = ODT_LISTBOX;
00546         dis.CtlID        = GetWindowLongPtrW( descr->self, GWLP_ID );
00547         dis.hwndItem     = descr->self;
00548         dis.itemAction   = action;
00549         dis.hDC          = hdc;
00550         dis.itemID       = index;
00551         dis.itemState    = 0;
00552         if (item->selected) dis.itemState |= ODS_SELECTED;
00553         if (!ignoreFocus && (descr->focus_item == index) &&
00554             (descr->caret_on) &&
00555             (descr->in_focus)) dis.itemState |= ODS_FOCUS;
00556         if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
00557         dis.itemData     = item->data;
00558         dis.rcItem       = *rect;
00559         TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
00560               descr->self, index, item ? debugstr_w(item->str) : "", action,
00561               dis.itemState, wine_dbgstr_rect(rect) );
00562         SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
00563         SelectClipRgn( hdc, hrgn );
00564         if (hrgn) DeleteObject( hrgn );
00565     }
00566     else
00567     {
00568         COLORREF oldText = 0, oldBk = 0;
00569 
00570         if (action == ODA_FOCUS)
00571         {
00572             if (!(descr->UIState & UISF_HIDEFOCUS)) // REACTOS
00573                 DrawFocusRect( hdc, rect );
00574             return;
00575         }
00576         if (item && item->selected)
00577         {
00578             oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
00579             oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
00580         }
00581 
00582         TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
00583               descr->self, index, item ? debugstr_w(item->str) : "", action,
00584               wine_dbgstr_rect(rect) );
00585         if (!item)
00586             ExtTextOutW( hdc, rect->left + 1, rect->top,
00587                            ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
00588         else if (!(descr->style & LBS_USETABSTOPS))
00589             ExtTextOutW( hdc, rect->left + 1, rect->top,
00590                          ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
00591                          strlenW(item->str), NULL );
00592         else
00593     {
00594         /* Output empty string to paint background in the full width. */
00595             ExtTextOutW( hdc, rect->left + 1, rect->top,
00596                          ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
00597             TabbedTextOutW( hdc, rect->left + 1 , rect->top,
00598                             item->str, strlenW(item->str),
00599                             descr->nb_tabs, descr->tabs, 0);
00600     }
00601         if (item && item->selected)
00602         {
00603             SetBkColor( hdc, oldBk );
00604             SetTextColor( hdc, oldText );
00605         }
00606         if (!ignoreFocus && (descr->focus_item == index) &&
00607             (descr->caret_on) &&
00608             (descr->in_focus) &&
00609             !(descr->UIState & UISF_HIDEFOCUS)) DrawFocusRect( hdc, rect );
00610     }
00611 }
00612 
00613 
00614 /***********************************************************************
00615  *           LISTBOX_SetRedraw
00616  *
00617  * Change the redraw flag.
00618  */
00619 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
00620 {
00621     if (on)
00622     {
00623         if (!(descr->style & LBS_NOREDRAW)) return;
00624         descr->style &= ~LBS_NOREDRAW;
00625         if (descr->style & LBS_DISPLAYCHANGED)
00626         {     /* page was changed while setredraw false, refresh automatically */
00627             InvalidateRect(descr->self, NULL, TRUE);
00628             if ((descr->top_item + descr->page_size) > descr->nb_items)
00629             {      /* reset top of page if less than number of items/page */
00630                 descr->top_item = descr->nb_items - descr->page_size;
00631                 if (descr->top_item < 0) descr->top_item = 0;
00632             }
00633             descr->style &= ~LBS_DISPLAYCHANGED;
00634         }
00635         LISTBOX_UpdateScroll( descr );
00636     }
00637     else descr->style |= LBS_NOREDRAW;
00638 }
00639 
00640 
00641 /***********************************************************************
00642  *           LISTBOX_RepaintItem
00643  *
00644  * Repaint a single item synchronously.
00645  */
00646 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
00647 {
00648     HDC hdc;
00649     RECT rect;
00650     HFONT oldFont = 0;
00651     HBRUSH hbrush, oldBrush = 0;
00652 
00653     /* Do not repaint the item if the item is not visible */
00654     if (!IsWindowVisible(descr->self)) return;
00655     if (descr->style & LBS_NOREDRAW)
00656     {
00657         descr->style |= LBS_DISPLAYCHANGED;
00658         return;
00659     }
00660     if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
00661     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
00662     if (descr->font) oldFont = SelectObject( hdc, descr->font );
00663     hbrush = GetControlColor( descr->owner, descr->self, hdc, WM_CTLCOLORLISTBOX);
00664     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
00665     if (!IsWindowEnabled(descr->self))
00666         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
00667     SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
00668     LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE );
00669     if (oldFont) SelectObject( hdc, oldFont );
00670     if (oldBrush) SelectObject( hdc, oldBrush );
00671     ReleaseDC( descr->self, hdc );
00672 }
00673 
00674 
00675 /***********************************************************************
00676  *           LISTBOX_DrawFocusRect
00677  */
00678 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
00679 {
00680     HDC hdc;
00681     RECT rect;
00682     HFONT oldFont = 0;
00683 
00684     /* Do not repaint the item if the item is not visible */
00685     if (!IsWindowVisible(descr->self)) return;
00686 
00687     if (descr->focus_item == -1) return;
00688     if (!descr->caret_on || !descr->in_focus) return;
00689 
00690     if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return;
00691     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
00692     if (descr->font) oldFont = SelectObject( hdc, descr->font );
00693     if (!IsWindowEnabled(descr->self))
00694         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
00695     SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
00696     LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, on ? FALSE : TRUE );
00697     if (oldFont) SelectObject( hdc, oldFont );
00698     ReleaseDC( descr->self, hdc );
00699 }
00700 
00701 
00702 /***********************************************************************
00703  *           LISTBOX_InitStorage
00704  */
00705 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
00706 {
00707     LB_ITEMDATA *item;
00708 
00709     nb_items += LB_ARRAY_GRANULARITY - 1;
00710     nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
00711     if (descr->items) {
00712         nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
00713     item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
00714                               nb_items * sizeof(LB_ITEMDATA));
00715     }
00716     else {
00717     item = HeapAlloc( GetProcessHeap(), 0,
00718                               nb_items * sizeof(LB_ITEMDATA));
00719     }
00720 
00721     if (!item)
00722     {
00723         SEND_NOTIFICATION( descr, LBN_ERRSPACE );
00724         return LB_ERRSPACE;
00725     }
00726     descr->items = item;
00727     return LB_OKAY;
00728 }
00729 
00730 
00731 /***********************************************************************
00732  *           LISTBOX_SetTabStops
00733  */
00734 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs )
00735 {
00736     INT i;
00737 
00738     if (!(descr->style & LBS_USETABSTOPS))
00739     {
00740         SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
00741         return FALSE;
00742     }
00743 
00744     HeapFree( GetProcessHeap(), 0, descr->tabs );
00745     if (!(descr->nb_tabs = count))
00746     {
00747         descr->tabs = NULL;
00748         return TRUE;
00749     }
00750     if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
00751                                             descr->nb_tabs * sizeof(INT) )))
00752         return FALSE;
00753     memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) ); 
00754 
00755     /* convert into "dialog units"*/
00756     for (i = 0; i < descr->nb_tabs; i++)
00757         descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
00758 
00759     return TRUE;
00760 }
00761 
00762 
00763 /***********************************************************************
00764  *           LISTBOX_GetText
00765  */
00766 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
00767 {
00768     DWORD len;
00769 
00770     if ((index < 0) || (index >= descr->nb_items))
00771     {
00772         SetLastError(ERROR_INVALID_INDEX);
00773         return LB_ERR;
00774     }
00775     if (HAS_STRINGS(descr))
00776     {
00777         if (!buffer)
00778         {
00779             len = strlenW(descr->items[index].str);
00780             if( unicode )
00781                 return len;
00782             return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
00783                                         NULL, 0, NULL, NULL );
00784         }
00785 
00786     TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
00787 
00788         _SEH2_TRY  /* hide a Delphi bug that passes a read-only buffer */
00789         {
00790             if(unicode)
00791             {
00792                 strcpyW( buffer, descr->items[index].str );
00793                 len = strlenW(buffer);
00794             }
00795             else
00796             {
00797                 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
00798                                           (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
00799             }
00800         }
00801         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
00802         {
00803             WARN( "got an invalid buffer (Delphi bug?)\n" );
00804             SetLastError( ERROR_INVALID_PARAMETER );
00805             len = LB_ERR;
00806         }
00807         _SEH2_END
00808     } else {
00809         if (buffer)
00810             *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
00811         len = sizeof(DWORD);
00812     }
00813     return len;
00814 }
00815 
00816 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
00817 {
00818     INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
00819     if (ret == CSTR_LESS_THAN)
00820         return -1;
00821     if (ret == CSTR_EQUAL)
00822         return 0;
00823     if (ret == CSTR_GREATER_THAN)
00824         return 1;
00825     return -1;
00826 }
00827 
00828 /***********************************************************************
00829  *           LISTBOX_FindStringPos
00830  *
00831  * Find the nearest string located before a given string in sort order.
00832  * If 'exact' is TRUE, return an error if we don't get an exact match.
00833  */
00834 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
00835 {
00836     INT index, min, max, res = -1;
00837 
00838     if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
00839     min = 0;
00840     max = descr->nb_items;
00841     while (min != max)
00842     {
00843         index = (min + max) / 2;
00844         if (HAS_STRINGS(descr))
00845             res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
00846         else
00847         {
00848             COMPAREITEMSTRUCT cis;
00849             UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
00850 
00851             cis.CtlType    = ODT_LISTBOX;
00852             cis.CtlID      = id;
00853             cis.hwndItem   = descr->self;
00854             /* note that some application (MetaStock) expects the second item
00855              * to be in the listbox */
00856             cis.itemID1    = -1;
00857             cis.itemData1  = (ULONG_PTR)str;
00858             cis.itemID2    = index;
00859             cis.itemData2  = descr->items[index].data;
00860             cis.dwLocaleId = descr->locale;
00861             res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
00862         }
00863         if (!res) return index;
00864         if (res < 0) max = index;
00865         else min = index + 1;
00866     }
00867     return exact ? -1 : max;
00868 }
00869 
00870 
00871 /***********************************************************************
00872  *           LISTBOX_FindFileStrPos
00873  *
00874  * Find the nearest string located before a given string in directory
00875  * sort order (i.e. first files, then directories, then drives).
00876  */
00877 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
00878 {
00879     INT min, max, res = -1;
00880 
00881     if (!HAS_STRINGS(descr))
00882         return LISTBOX_FindStringPos( descr, str, FALSE );
00883     min = 0;
00884     max = descr->nb_items;
00885     while (min != max)
00886     {
00887         INT index = (min + max) / 2;
00888         LPCWSTR p = descr->items[index].str;
00889         if (*p == '[')  /* drive or directory */
00890         {
00891             if (*str != '[') res = -1;
00892             else if (p[1] == '-')  /* drive */
00893             {
00894                 if (str[1] == '-') res = str[2] - p[2];
00895                 else res = -1;
00896             }
00897             else  /* directory */
00898             {
00899                 if (str[1] == '-') res = 1;
00900                 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
00901             }
00902         }
00903         else  /* filename */
00904         {
00905             if (*str == '[') res = 1;
00906             else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
00907         }
00908         if (!res) return index;
00909         if (res < 0) max = index;
00910         else min = index + 1;
00911     }
00912     return max;
00913 }
00914 
00915 
00916 /***********************************************************************
00917  *           LISTBOX_FindString
00918  *
00919  * Find the item beginning with a given string.
00920  */
00921 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
00922 {
00923     INT i;
00924     LB_ITEMDATA *item;
00925 
00926     if (start >= descr->nb_items) start = -1;
00927     item = descr->items + start + 1;
00928     if (HAS_STRINGS(descr))
00929     {
00930         if (!str || ! str[0] ) return LB_ERR;
00931         if (exact)
00932         {
00933             for (i = start + 1; i < descr->nb_items; i++, item++)
00934                 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
00935             for (i = 0, item = descr->items; i <= start; i++, item++)
00936                 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
00937         }
00938         else
00939         {
00940  /* Special case for drives and directories: ignore prefix */
00941 #define CHECK_DRIVE(item) \
00942     if ((item)->str[0] == '[') \
00943     { \
00944         if (!strncmpiW( str, (item)->str+1, len )) return i; \
00945         if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
00946         return i; \
00947     }
00948 
00949             INT len = strlenW(str);
00950             for (i = start + 1; i < descr->nb_items; i++, item++)
00951             {
00952                if (!strncmpiW( str, item->str, len )) return i;
00953                CHECK_DRIVE(item);
00954             }
00955             for (i = 0, item = descr->items; i <= start; i++, item++)
00956             {
00957                if (!strncmpiW( str, item->str, len )) return i;
00958                CHECK_DRIVE(item);
00959             }
00960 #undef CHECK_DRIVE
00961         }
00962     }
00963     else
00964     {
00965         if (exact && (descr->style & LBS_SORT))
00966             /* If sorted, use a WM_COMPAREITEM binary search */
00967             return LISTBOX_FindStringPos( descr, str, TRUE );
00968 
00969         /* Otherwise use a linear search */
00970         for (i = start + 1; i < descr->nb_items; i++, item++)
00971             if (item->data == (ULONG_PTR)str) return i;
00972         for (i = 0, item = descr->items; i <= start; i++, item++)
00973             if (item->data == (ULONG_PTR)str) return i;
00974     }
00975     return LB_ERR;
00976 }
00977 
00978 
00979 /***********************************************************************
00980  *           LISTBOX_GetSelCount
00981  */
00982 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
00983 {
00984     INT i, count;
00985     const LB_ITEMDATA *item = descr->items;
00986 
00987     if (!(descr->style & LBS_MULTIPLESEL) ||
00988         (descr->style & LBS_NOSEL))
00989       return LB_ERR;
00990     for (i = count = 0; i < descr->nb_items; i++, item++)
00991         if (item->selected) count++;
00992     return count;
00993 }
00994 
00995 
00996 /***********************************************************************
00997  *           LISTBOX_GetSelItems
00998  */
00999 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
01000 {
01001     INT i, count;
01002     const LB_ITEMDATA *item = descr->items;
01003 
01004     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
01005     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
01006         if (item->selected) array[count++] = i;
01007     return count;
01008 }
01009 
01010 
01011 /***********************************************************************
01012  *           LISTBOX_Paint
01013  */
01014 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
01015 {
01016     INT i, col_pos = descr->page_size - 1;
01017     RECT rect;
01018     RECT focusRect = {-1, -1, -1, -1};
01019     HFONT oldFont = 0;
01020     HBRUSH hbrush, oldBrush = 0;
01021 
01022     if (descr->style & LBS_NOREDRAW) return 0;
01023 
01024     SetRect( &rect, 0, 0, descr->width, descr->height );
01025     if (descr->style & LBS_MULTICOLUMN)
01026         rect.right = rect.left + descr->column_width;
01027     else if (descr->horz_pos)
01028     {
01029         SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
01030         rect.right += descr->horz_pos;
01031     }
01032 
01033     if (descr->font) oldFont = SelectObject( hdc, descr->font );
01034     hbrush = GetControlColor( descr->owner, descr->self, hdc, WM_CTLCOLORLISTBOX);
01035     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
01036     if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
01037 
01038     if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
01039         (descr->in_focus))
01040     {
01041         /* Special case for empty listbox: paint focus rect */
01042         rect.bottom = rect.top + descr->item_height;
01043         ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
01044                      &rect, NULL, 0, NULL );
01045         LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
01046         rect.top = rect.bottom;
01047     }
01048 
01049     /* Paint all the item, regarding the selection
01050        Focus state will be painted after  */
01051 
01052     for (i = descr->top_item; i < descr->nb_items; i++)
01053     {
01054         if (!(descr->style & LBS_OWNERDRAWVARIABLE))
01055             rect.bottom = rect.top + descr->item_height;
01056         else
01057             rect.bottom = rect.top + descr->items[i].height;
01058 
01059         if (i == descr->focus_item)
01060         {
01061         /* keep the focus rect, to paint the focus item after */
01062         focusRect.left = rect.left;
01063         focusRect.right = rect.right;
01064         focusRect.top = rect.top;
01065         focusRect.bottom = rect.bottom;
01066         }
01067         LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
01068         rect.top = rect.bottom;
01069 
01070         if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
01071         {
01072             if (!IS_OWNERDRAW(descr))
01073             {
01074                 /* Clear the bottom of the column */
01075                 if (rect.top < descr->height)
01076                 {
01077                     rect.bottom = descr->height;
01078                     ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
01079                                    &rect, NULL, 0, NULL );
01080                 }
01081             }
01082 
01083             /* Go to the next column */
01084             rect.left += descr->column_width;
01085             rect.right += descr->column_width;
01086             rect.top = 0;
01087             col_pos = descr->page_size - 1;
01088         }
01089         else
01090         {
01091             col_pos--;
01092             if (rect.top >= descr->height) break;
01093         }
01094     }
01095 
01096     /* Paint the focus item now */
01097     if (focusRect.top != focusRect.bottom &&
01098         descr->caret_on && descr->in_focus)
01099         LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
01100 
01101     if (!IS_OWNERDRAW(descr))
01102     {
01103         /* Clear the remainder of the client area */
01104         if (rect.top < descr->height)
01105         {
01106             rect.bottom = descr->height;
01107             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
01108                            &rect, NULL, 0, NULL );
01109         }
01110         if (rect.right < descr->width)
01111         {
01112             rect.left   = rect.right;
01113             rect.right  = descr->width;
01114             rect.top    = 0;
01115             rect.bottom = descr->height;
01116             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
01117                            &rect, NULL, 0, NULL );
01118         }
01119     }
01120     if (oldFont) SelectObject( hdc, oldFont );
01121     if (oldBrush) SelectObject( hdc, oldBrush );
01122     return 0;
01123 }
01124 
01125 
01126 /***********************************************************************
01127  *           LISTBOX_InvalidateItems
01128  *
01129  * Invalidate all items from a given item. If the specified item is not
01130  * visible, nothing happens.
01131  */
01132 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
01133 {
01134     RECT rect;
01135 
01136     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
01137     {
01138         if (descr->style & LBS_NOREDRAW)
01139         {
01140             descr->style |= LBS_DISPLAYCHANGED;
01141             return;
01142         }
01143         rect.bottom = descr->height;
01144         InvalidateRect( descr->self, &rect, TRUE );
01145         if (descr->style & LBS_MULTICOLUMN)
01146         {
01147             /* Repaint the other columns */
01148             rect.left  = rect.right;
01149             rect.right = descr->width;
01150             rect.top   = 0;
01151             InvalidateRect( descr->self, &rect, TRUE );
01152         }
01153     }
01154 }
01155 
01156 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
01157 {
01158     RECT rect;
01159 
01160     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
01161         InvalidateRect( descr->self, &rect, TRUE );
01162 }
01163 
01164 /***********************************************************************
01165  *           LISTBOX_GetItemHeight
01166  */
01167 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
01168 {
01169     if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
01170     {
01171         if ((index < 0) || (index >= descr->nb_items))
01172         {
01173             SetLastError(ERROR_INVALID_INDEX);
01174             return LB_ERR;
01175         }
01176         return descr->items[index].height;
01177     }
01178     else return descr->item_height;
01179 }
01180 
01181 
01182 /***********************************************************************
01183  *           LISTBOX_SetItemHeight
01184  */
01185 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
01186 {
01187     if (height > MAXBYTE)
01188         return -1;
01189 
01190     if (!height) height = 1;
01191 
01192     if (descr->style & LBS_OWNERDRAWVARIABLE)
01193     {
01194         if ((index < 0) || (index >= descr->nb_items))
01195         {
01196             SetLastError(ERROR_INVALID_INDEX);
01197             return LB_ERR;
01198         }
01199         TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
01200         descr->items[index].height = height;
01201         LISTBOX_UpdateScroll( descr );
01202     if (repaint)
01203         LISTBOX_InvalidateItems( descr, index );
01204     }
01205     else if (height != descr->item_height)
01206     {
01207         TRACE("[%p]: new height = %d\n", descr->self, height );
01208         descr->item_height = height;
01209         LISTBOX_UpdatePage( descr );
01210         LISTBOX_UpdateScroll( descr );
01211     if (repaint)
01212         InvalidateRect( descr->self, 0, TRUE );
01213     }
01214     return LB_OKAY;
01215 }
01216 
01217 
01218 /***********************************************************************
01219  *           LISTBOX_SetHorizontalPos
01220  */
01221 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
01222 {
01223     INT diff;
01224 
01225     if (pos > descr->horz_extent - descr->width)
01226         pos = descr->horz_extent - descr->width;
01227     if (pos < 0) pos = 0;
01228     if (!(diff = descr->horz_pos - pos)) return;
01229     TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
01230     descr->horz_pos = pos;
01231     LISTBOX_UpdateScroll( descr );
01232     if (abs(diff) < descr->width)
01233     {
01234         RECT rect;
01235         /* Invalidate the focused item so it will be repainted correctly */
01236         if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
01237             InvalidateRect( descr->self, &rect, TRUE );
01238         ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
01239                           SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
01240     }
01241     else
01242         InvalidateRect( descr->self, NULL, TRUE );
01243 }
01244 
01245 
01246 /***********************************************************************
01247  *           LISTBOX_SetHorizontalExtent
01248  */
01249 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
01250 {
01251     if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
01252         return LB_OKAY;
01253     if (extent <= 0) extent = 1;
01254     if (extent == descr->horz_extent) return LB_OKAY;
01255     TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
01256     descr->horz_extent = extent;
01257     if (descr->horz_pos > extent - descr->width)
01258         LISTBOX_SetHorizontalPos( descr, extent - descr->width );
01259     else
01260         LISTBOX_UpdateScroll( descr );
01261     return LB_OKAY;
01262 }
01263 
01264 
01265 /***********************************************************************
01266  *           LISTBOX_SetColumnWidth
01267  */
01268 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
01269 {
01270     if (width == descr->column_width) return LB_OKAY;
01271     TRACE("[%p]: new column width = %d\n", descr->self, width );
01272     descr->column_width = width;
01273     LISTBOX_UpdatePage( descr );
01274     return LB_OKAY;
01275 }
01276 
01277 
01278 /***********************************************************************
01279  *           LISTBOX_SetFont
01280  *
01281  * Returns the item height.
01282  */
01283 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
01284 {
01285     HDC hdc;
01286     HFONT oldFont = 0;
01287     const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
01288     SIZE sz;
01289 
01290     descr->font = font;
01291 
01292     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
01293     {
01294         ERR("unable to get DC.\n" );
01295         return 16;
01296     }
01297     if (font) oldFont = SelectObject( hdc, font );
01298     GetTextExtentPointA( hdc, alphabet, 52, &sz);
01299     if (oldFont) SelectObject( hdc, oldFont );
01300     ReleaseDC( descr->self, hdc );
01301 
01302     descr->avg_char_width = (sz.cx / 26 + 1) / 2;
01303     if (!IS_OWNERDRAW(descr))
01304         LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
01305     return sz.cy;
01306 }
01307 
01308 
01309 /***********************************************************************
01310  *           LISTBOX_MakeItemVisible
01311  *
01312  * Make sure that a given item is partially or fully visible.
01313  */
01314 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
01315 {
01316     INT top;
01317 
01318     TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);
01319 
01320     if (index <= descr->top_item) top = index;
01321     else if (descr->style & LBS_MULTICOLUMN)
01322     {
01323         INT cols = descr->width;
01324         if (!fully) cols += descr->column_width - 1;
01325         if (cols >= descr->column_width) cols /= descr->column_width;
01326         else cols = 1;
01327         if (index < descr->top_item + (descr->page_size * cols)) return;
01328         top = index - descr->page_size * (cols - 1);
01329     }
01330     else if (descr->style & LBS_OWNERDRAWVARIABLE)
01331     {
01332         INT height = fully ? descr->items[index].height : 1;
01333         for (top = index; top > descr->top_item; top--)
01334             if ((height += descr->items[top-1].height) > descr->height) break;
01335     }
01336     else
01337     {
01338         if (index < descr->top_item + descr->page_size) return;
01339         if (!fully && (index == descr->top_item + descr->page_size) &&
01340             (descr->height > (descr->page_size * descr->item_height))) return;
01341         top = index - descr->page_size + 1;
01342     }
01343     LISTBOX_SetTopItem( descr, top, TRUE );
01344 }
01345 
01346 /***********************************************************************
01347  *           LISTBOX_SetCaretIndex
01348  *
01349  * NOTES
01350  *   index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
01351  *
01352  */
01353 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
01354 {
01355     INT oldfocus = descr->focus_item;
01356 
01357     TRACE("old focus %d, index %d\n", oldfocus, index);
01358 
01359     if (descr->style & LBS_NOSEL) return LB_ERR;
01360     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
01361     if (index == oldfocus) return LB_OKAY;
01362 
01363     LISTBOX_DrawFocusRect( descr, FALSE );
01364     descr->focus_item = index;
01365 
01366     LISTBOX_MakeItemVisible( descr, index, fully_visible );
01367     LISTBOX_DrawFocusRect( descr, TRUE );
01368 
01369     return LB_OKAY;
01370 }
01371 
01372 
01373 /***********************************************************************
01374  *           LISTBOX_SelectItemRange
01375  *
01376  * Select a range of items. Should only be used on a MULTIPLESEL listbox.
01377  */
01378 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
01379                                         INT last, BOOL on )
01380 {
01381     INT i;
01382 
01383     /* A few sanity checks */
01384 
01385     if (descr->style & LBS_NOSEL) return LB_ERR;
01386     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
01387 
01388     if (!descr->nb_items) return LB_OKAY;
01389 
01390     if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
01391     if (first < 0) first = 0;
01392     if (last < first) return LB_OKAY;
01393 
01394     if (on)  /* Turn selection on */
01395     {
01396         for (i = first; i <= last; i++)
01397         {
01398             if (descr->items[i].selected) continue;
01399             descr->items[i].selected = TRUE;
01400             LISTBOX_InvalidateItemRect(descr, i);
01401         }
01402     }
01403     else  /* Turn selection off */
01404     {
01405         for (i = first; i <= last; i++)
01406         {
01407             if (!descr->items[i].selected) continue;
01408             descr->items[i].selected = FALSE;
01409             LISTBOX_InvalidateItemRect(descr, i);
01410         }
01411     }
01412     return LB_OKAY;
01413 }
01414 
01415 /***********************************************************************
01416  *           LISTBOX_SetSelection
01417  */
01418 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
01419                                      BOOL on, BOOL send_notify )
01420 {
01421     TRACE( "cur_sel=%d index=%d notify=%s\n",
01422            descr->selected_item, index, send_notify ? "YES" : "NO" );
01423 
01424     if (descr->style & LBS_NOSEL)
01425     {
01426         descr->selected_item = index;
01427         return LB_ERR;
01428     }
01429     if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
01430     if (descr->style & LBS_MULTIPLESEL)
01431     {
01432         if (index == -1)  /* Select all items */
01433             return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
01434         else  /* Only one item */
01435             return LISTBOX_SelectItemRange( descr, index, index, on );
01436     }
01437     else
01438     {
01439         INT oldsel = descr->selected_item;
01440         if (index == oldsel) return LB_OKAY;
01441         if (oldsel != -1) descr->items[oldsel].selected = FALSE;
01442         if (index != -1) descr->items[index].selected = TRUE;
01443         if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
01444         descr->selected_item = index;
01445         if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
01446         if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
01447                                (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
01448     else
01449         if( descr->lphc ) /* set selection change flag for parent combo */
01450         descr->lphc->wState |= CBF_SELCHANGE;
01451     }
01452     return LB_OKAY;
01453 }
01454 
01455 
01456 /***********************************************************************
01457  *           LISTBOX_MoveCaret
01458  *
01459  * Change the caret position and extend the selection to the new caret.
01460  */
01461 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
01462 {
01463     TRACE("old focus %d, index %d\n", descr->focus_item, index);
01464 
01465     if ((index <  0) || (index >= descr->nb_items))
01466         return;
01467 
01468     /* Important, repaint needs to be done in this order if
01469        you want to mimic Windows behavior:
01470        1. Remove the focus and paint the item
01471        2. Remove the selection and paint the item(s)
01472        3. Set the selection and repaint the item(s)
01473        4. Set the focus to 'index' and repaint the item */
01474 
01475     /* 1. remove the focus and repaint the item */
01476     LISTBOX_DrawFocusRect( descr, FALSE );
01477 
01478     /* 2. then turn off the previous selection */
01479     /* 3. repaint the new selected item */
01480     if (descr->style & LBS_EXTENDEDSEL)
01481     {
01482         if (descr->anchor_item != -1)
01483         {
01484             INT first = min( index, descr->anchor_item );
01485             INT last  = max( index, descr->anchor_item );
01486             if (first > 0)
01487                 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
01488             LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
01489             LISTBOX_SelectItemRange( descr, first, last, TRUE );
01490         }
01491     }
01492     else if (!(descr->style & LBS_MULTIPLESEL))
01493     {
01494         /* Set selection to new caret item */
01495         LISTBOX_SetSelection( descr, index, TRUE, FALSE );
01496     }
01497 
01498     /* 4. repaint the new item with the focus */
01499     descr->focus_item = index;
01500     LISTBOX_MakeItemVisible( descr, index, fully_visible );
01501     LISTBOX_DrawFocusRect( descr, TRUE );
01502 }
01503 
01504 
01505 /***********************************************************************
01506  *           LISTBOX_InsertItem
01507  */
01508 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
01509                                    LPWSTR str, ULONG_PTR data )
01510 {
01511     LB_ITEMDATA *item;
01512     INT max_items;
01513     INT oldfocus = descr->focus_item;
01514 
01515     if (index == -1) index = descr->nb_items;
01516     else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
01517     if (!descr->items) max_items = 0;
01518     else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
01519     if (descr->nb_items == max_items)
01520     {
01521         /* We need to grow the array */
01522         max_items += LB_ARRAY_GRANULARITY;
01523     if (descr->items)
01524             item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
01525                                   max_items * sizeof(LB_ITEMDATA) );
01526     else
01527         item = HeapAlloc( GetProcessHeap(), 0,
01528                                   max_items * sizeof(LB_ITEMDATA) );
01529         if (!item)
01530         {
01531             SEND_NOTIFICATION( descr, LBN_ERRSPACE );
01532             return LB_ERRSPACE;
01533         }
01534         descr->items = item;
01535     }
01536 
01537     /* Insert the item structure */
01538 
01539     item = &descr->items[index];
01540     if (index < descr->nb_items)
01541         RtlMoveMemory( item + 1, item,
01542                        (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
01543     item->str      = str;
01544     item->data     = data;
01545     item->height   = 0;
01546     item->selected = FALSE;
01547     descr->nb_items++;
01548 
01549     /* Get item height */
01550 
01551     if (descr->style & LBS_OWNERDRAWVARIABLE)
01552     {
01553         MEASUREITEMSTRUCT mis;
01554         UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
01555 
01556         mis.CtlType    = ODT_LISTBOX;
01557         mis.CtlID      = id;
01558         mis.itemID     = index;
01559         mis.itemData   = descr->items[index].data;
01560         mis.itemHeight = descr->item_height;
01561         SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
01562         item->height = mis.itemHeight ? mis.itemHeight : 1;
01563         TRACE("[%p]: measure item %d (%s) = %d\n",
01564               descr->self, index, str ? debugstr_w(str) : "", item->height );
01565     }
01566 
01567     /* Repaint the items */
01568 
01569     LISTBOX_UpdateScroll( descr );
01570     LISTBOX_InvalidateItems( descr, index );
01571 
01572     /* Move selection and focused item */
01573     /* If listbox was empty, set focus to the first item */
01574     if (descr->nb_items == 1)
01575          LISTBOX_SetCaretIndex( descr, 0, FALSE );
01576     /* single select don't change selection index in win31 */
01577     else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
01578     {
01579         descr->selected_item++;
01580         LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
01581     }
01582     else
01583     {
01584         if (index <= descr->selected_item)
01585         {
01586             descr->selected_item++;
01587             descr->focus_item = oldfocus; /* focus not changed */
01588         }
01589     }
01590     return LB_OKAY;
01591 }
01592 
01593 
01594 /***********************************************************************
01595  *           LISTBOX_InsertString
01596  */
01597 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
01598 {
01599     LPWSTR new_str = NULL;
01600     ULONG_PTR data = 0;
01601     LRESULT ret;
01602 
01603     if (HAS_STRINGS(descr))
01604     {
01605         static const WCHAR empty_stringW[] = { 0 };
01606         if (!str) str = empty_stringW;
01607         if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
01608         {
01609             SEND_NOTIFICATION( descr, LBN_ERRSPACE );
01610             return LB_ERRSPACE;
01611         }
01612         strcpyW(new_str, str);
01613     }
01614     else data = (ULONG_PTR)str;
01615 
01616     if (index == -1) index = descr->nb_items;
01617     if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
01618     {
01619         HeapFree( GetProcessHeap(), 0, new_str );
01620         return ret;
01621     }
01622 
01623     TRACE("[%p]: added item %d %s\n",
01624           descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
01625     return index;
01626 }
01627 
01628 
01629 /***********************************************************************
01630  *           LISTBOX_DeleteItem
01631  *
01632  * Delete the content of an item. 'index' must be a valid index.
01633  */
01634 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
01635 {
01636     /* save the item data before it gets freed by LB_RESETCONTENT */
01637     ULONG_PTR item_data = descr->items[index].data;
01638     LPWSTR item_str = descr->items[index].str;
01639 
01640     if (!descr->nb_items)
01641         SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
01642 
01643     /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
01644      *       while Win95 sends it for all items with user data.
01645      *       It's probably better to send it too often than not
01646      *       often enough, so this is what we do here.
01647      */
01648     if (IS_OWNERDRAW(descr) || item_data)
01649     {
01650         DELETEITEMSTRUCT dis;
01651         UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
01652 
01653         dis.CtlType  = ODT_LISTBOX;
01654         dis.CtlID    = id;
01655         dis.itemID   = index;
01656         dis.hwndItem = descr->self;
01657         dis.itemData = item_data;
01658         SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
01659     }
01660     if (HAS_STRINGS(descr))
01661         HeapFree( GetProcessHeap(), 0, item_str );
01662 }
01663 
01664 
01665 /***********************************************************************
01666  *           LISTBOX_RemoveItem
01667  *
01668  * Remove an item from the listbox and delete its content.
01669  */
01670 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
01671 {
01672     LB_ITEMDATA *item;
01673     INT max_items;
01674 
01675     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
01676 
01677     /* We need to invalidate the original rect instead of the updated one. */
01678     LISTBOX_InvalidateItems( descr, index );
01679 
01680     descr->nb_items--;
01681     LISTBOX_DeleteItem( descr, index );
01682 
01683     if (!descr->nb_items) return LB_OKAY;
01684 
01685     /* Remove the item */
01686 
01687     item = &descr->items[index];
01688     if (index < descr->nb_items)
01689         RtlMoveMemory( item, item + 1,
01690                        (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
01691     if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
01692 
01693     /* Shrink the item array if possible */
01694 
01695     max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
01696     if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
01697     {
01698         max_items -= LB_ARRAY_GRANULARITY;
01699         item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
01700                             max_items * sizeof(LB_ITEMDATA) );
01701         if (item) descr->items = item;
01702     }
01703     /* Repaint the items */
01704 
01705     LISTBOX_UpdateScroll( descr );
01706     /* if we removed the scrollbar, reset the top of the list
01707       (correct for owner-drawn ???) */
01708     if (descr->nb_items == descr->page_size)
01709         LISTBOX_SetTopItem( descr, 0, TRUE );
01710 
01711     /* Move selection and focused item */
01712     if (!IS_MULTISELECT(descr))
01713     {
01714         if (index == descr->selected_item)
01715             descr->selected_item = -1;
01716         else if (index < descr->selected_item)
01717         {
01718             descr->selected_item--;
01719             if (ISWIN31) /* win 31 do not change the selected item number */
01720                LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
01721         }
01722     }
01723 
01724     if (descr->focus_item >= descr->nb_items)
01725     {
01726           descr->focus_item = descr->nb_items - 1;
01727           if (descr->focus_item < 0) descr->focus_item = 0;
01728     }
01729     return LB_OKAY;
01730 }
01731 
01732 
01733 /***********************************************************************
01734  *           LISTBOX_ResetContent
01735  */
01736 static void LISTBOX_ResetContent( LB_DESCR *descr )
01737 {
01738     INT i;
01739 
01740     for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
01741     HeapFree( GetProcessHeap(), 0, descr->items );
01742     descr->nb_items      = 0;
01743     descr->top_item      = 0;
01744     descr->selected_item = -1;
01745     descr->focus_item    = 0;
01746     descr->anchor_item   = -1;
01747     descr->items         = NULL;
01748 }
01749 
01750 
01751 /***********************************************************************
01752  *           LISTBOX_SetCount
01753  */
01754 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
01755 {
01756     LRESULT ret;
01757 
01758     if (HAS_STRINGS(descr))
01759     {
01760         SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
01761         return LB_ERR;
01762     }
01763 
01764     /* FIXME: this is far from optimal... */
01765     if (count > descr->nb_items)
01766     {
01767         while (count > descr->nb_items)
01768             if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
01769                 return ret;
01770     }
01771     else if (count < descr->nb_items)
01772     {
01773         while (count < descr->nb_items)
01774             if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
01775                 return ret;
01776     }
01777     return LB_OKAY;
01778 }
01779 
01780 
01781 /***********************************************************************
01782  *           LISTBOX_Directory
01783  */
01784 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
01785                                   LPCWSTR filespec, BOOL long_names )
01786 {
01787     HANDLE handle;
01788     LRESULT ret = LB_OKAY;
01789     WIN32_FIND_DATAW entry;
01790     int pos;
01791     LRESULT maxinsert = LB_ERR;
01792 
01793     /* don't scan directory if we just want drives exclusively */
01794     if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
01795         /* scan directory */
01796         if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
01797         {
01798          int le = GetLastError();
01799             if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
01800         }
01801         else
01802         {
01803             do
01804             {
01805                 WCHAR buffer[270];
01806                 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
01807                 {
01808                     static const WCHAR bracketW[]  = { ']',0 };
01809                     static const WCHAR dotW[] = { '.',0 };
01810                     if (!(attrib & DDL_DIRECTORY) ||
01811                         !strcmpW( entry.cFileName, dotW )) continue;
01812                     buffer[0] = '[';
01813                     if (!long_names && entry.cAlternateFileName[0])
01814                         strcpyW( buffer + 1, entry.cAlternateFileName );
01815                     else
01816                         strcpyW( buffer + 1, entry.cFileName );
01817                     strcatW(buffer, bracketW);
01818                 }
01819                 else  /* not a directory */
01820                 {
01821 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
01822                  FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
01823 
01824                     if ((attrib & DDL_EXCLUSIVE) &&
01825                         ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
01826                         continue;
01827 #undef ATTRIBS
01828                     if (!long_names && entry.cAlternateFileName[0])
01829                         strcpyW( buffer, entry.cAlternateFileName );
01830                     else
01831                         strcpyW( buffer, entry.cFileName );
01832                 }
01833                 if (!long_names) CharLowerW( buffer );
01834                 pos = LISTBOX_FindFileStrPos( descr, buffer );
01835                 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
01836                     break;
01837                 if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
01838             } while (FindNextFileW( handle, &entry ));
01839             FindClose( handle );
01840         }
01841     }
01842     if (ret >= 0)
01843     {
01844         ret = maxinsert;
01845 
01846         /* scan drives */
01847         if (attrib & DDL_DRIVES)
01848         {
01849             WCHAR buffer[] = {'[','-','a','-',']',0};
01850             WCHAR root[] = {'A',':','\\',0};
01851             int drive;
01852             for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
01853             {
01854                 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
01855                 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
01856                     break;
01857             }
01858         }
01859     }
01860     return ret;
01861 }
01862 
01863 
01864 /***********************************************************************
01865  *           LISTBOX_HandleVScroll
01866  */
01867 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
01868 {
01869     SCROLLINFO info;
01870 
01871     if (descr->style & LBS_MULTICOLUMN) return 0;
01872     switch(scrollReq)
01873     {
01874     case SB_LINEUP:
01875         LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
01876         break;
01877     case SB_LINEDOWN:
01878         LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
01879         break;
01880     case SB_PAGEUP:
01881         LISTBOX_SetTopItem( descr, descr->top_item -
01882                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
01883         break;
01884     case SB_PAGEDOWN:
01885         LISTBOX_SetTopItem( descr, descr->top_item +
01886                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
01887         break;
01888     case SB_THUMBPOSITION:
01889         LISTBOX_SetTopItem( descr, pos, TRUE );
01890         break;
01891     case SB_THUMBTRACK:
01892         info.cbSize = sizeof(info);
01893         info.fMask = SIF_TRACKPOS;
01894         GetScrollInfo( descr->self, SB_VERT, &info );
01895         LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
01896         break;
01897     case SB_TOP:
01898         LISTBOX_SetTopItem( descr, 0, TRUE );
01899         break;
01900     case SB_BOTTOM:
01901         LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
01902         break;
01903     }
01904     return 0;
01905 }
01906 
01907 
01908 /***********************************************************************
01909  *           LISTBOX_HandleHScroll
01910  */
01911 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
01912 {
01913     SCROLLINFO info;
01914     INT page;
01915 
01916     if (descr->style & LBS_MULTICOLUMN)
01917     {
01918         switch(scrollReq)
01919         {
01920         case SB_LINELEFT:
01921             LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
01922                                 TRUE );
01923             break;
01924         case SB_LINERIGHT:
01925             LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
01926                                 TRUE );
01927             break;
01928         case SB_PAGELEFT:
01929             page = descr->width / descr->column_width;
01930             if (page < 1) page = 1;
01931             LISTBOX_SetTopItem( descr,
01932                              descr->top_item - page * descr->page_size, TRUE );
01933             break;
01934         case SB_PAGERIGHT:
01935             page = descr->width / descr->column_width;
01936             if (page < 1) page = 1;
01937             LISTBOX_SetTopItem( descr,
01938                              descr->top_item + page * descr->page_size, TRUE );
01939             break;
01940         case SB_THUMBPOSITION:
01941             LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
01942             break;
01943         case SB_THUMBTRACK:
01944             info.cbSize = sizeof(info);
01945             info.fMask  = SIF_TRACKPOS;
01946             GetScrollInfo( descr->self, SB_VERT, &info );
01947             LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
01948                                 TRUE );
01949             break;
01950         case SB_LEFT:
01951             LISTBOX_SetTopItem( descr, 0, TRUE );
01952             break;
01953         case SB_RIGHT:
01954             LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
01955             break;
01956         }
01957     }
01958     else if (descr->horz_extent)
01959     {
01960         switch(scrollReq)
01961         {
01962         case SB_LINELEFT:
01963             LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
01964             break;
01965         case SB_LINERIGHT:
01966             LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
01967             break;
01968         case SB_PAGELEFT:
01969             LISTBOX_SetHorizontalPos( descr,
01970                                       descr->horz_pos - descr->width );
01971             break;
01972         case SB_PAGERIGHT:
01973             LISTBOX_SetHorizontalPos( descr,
01974                                       descr->horz_pos + descr->width );
01975             break;
01976         case SB_THUMBPOSITION:
01977             LISTBOX_SetHorizontalPos( descr, pos );
01978             break;
01979         case SB_THUMBTRACK:
01980             info.cbSize = sizeof(info);
01981             info.fMask = SIF_TRACKPOS;
01982             GetScrollInfo( descr->self, SB_HORZ, &info );
01983             LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
01984             break;
01985         case SB_LEFT:
01986             LISTBOX_SetHorizontalPos( descr, 0 );
01987             break;
01988         case SB_RIGHT:
01989             LISTBOX_SetHorizontalPos( descr,
01990                                       descr->horz_extent - descr->width );
01991             break;
01992         }
01993     }
01994     return 0;
01995 }
01996 
01997 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
01998 {
01999     short gcWheelDelta = 0;
02000     UINT pulScrollLines = 3;
02001 
02002     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
02003 
02004     gcWheelDelta -= delta;
02005 
02006     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
02007     {
02008         int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
02009         cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
02010         LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
02011     }
02012     return 0;
02013 }
02014 
02015 /***********************************************************************
02016  *           LISTBOX_HandleLButtonDown
02017  */
02018 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
02019 {
02020     INT index = LISTBOX_GetItemFromPoint( descr, x, y );
02021 
02022     TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
02023           descr->self, x, y, index, descr->focus_item);
02024 
02025     if (!descr->caret_on && (descr->in_focus)) return 0;
02026 
02027     if (!descr->in_focus)
02028     {
02029         if( !descr->lphc ) SetFocus( descr->self );
02030         else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
02031     }
02032 
02033     if (index == -1) return 0;
02034 
02035     if (!descr->lphc)
02036     {
02037         if (descr->style & LBS_NOTIFY )
02038             SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
02039                             MAKELPARAM( x, y ) );
02040     }
02041 
02042     descr->captured = TRUE;
02043     SetCapture( descr->self );
02044 
02045     if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
02046     {
02047         /* we should perhaps make sure that all items are deselected
02048            FIXME: needed for !LBS_EXTENDEDSEL, too ?
02049            if (!(keys & (MK_SHIFT|MK_CONTROL)))
02050            LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
02051         */
02052 
02053         if (!(keys & MK_SHIFT)) descr->anchor_item = index;
02054         if (keys & MK_CONTROL)
02055         {
02056             LISTBOX_SetCaretIndex( descr, index, FALSE );
02057             LISTBOX_SetSelection( descr, index,
02058                                   !descr->items[index].selected,
02059                                   (descr->style & LBS_NOTIFY) != 0);
02060         }
02061         else
02062         {
02063             LISTBOX_MoveCaret( descr, index, FALSE );
02064 
02065             if (descr->style & LBS_EXTENDEDSEL)
02066             {
02067                 LISTBOX_SetSelection( descr, index,
02068                                descr->items[index].selected,
02069                               (descr->style & LBS_NOTIFY) != 0 );
02070             }
02071             else
02072             {
02073                 LISTBOX_SetSelection( descr, index,
02074                                !descr->items[index].selected,
02075                               (descr->style & LBS_NOTIFY) != 0 );
02076             }
02077         }
02078     }
02079     else
02080     {
02081         descr->anchor_item = index;
02082         LISTBOX_MoveCaret( descr, index, FALSE );
02083         LISTBOX_SetSelection( descr, index,
02084                               TRUE, (descr->style & LBS_NOTIFY) != 0 );
02085     }
02086 
02087     if (!descr->lphc)
02088     {   //  See rev 40864 use Ptr for 64 bit.
02089         if (GetWindowLongPtrW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
02090         {
02091             POINT pt;
02092 
02093         pt.x = x;
02094         pt.y = y;
02095 
02096             if (DragDetect( descr->self, pt ))
02097                 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
02098         }
02099     }
02100     return 0;
02101 }
02102 
02103 
02104 /*************************************************************************
02105  * LISTBOX_HandleLButtonDownCombo [Internal]
02106  *
02107  * Process LButtonDown message for the ComboListBox
02108  *
02109  * PARAMS
02110  *     pWnd       [I] The windows internal structure
02111  *     pDescr     [I] The ListBox internal structure
02112  *     keys       [I] Key Flag (WM_LBUTTONDOWN doc for more info)
02113  *     x          [I] X Mouse Coordinate
02114  *     y          [I] Y Mouse Coordinate
02115  *
02116  * RETURNS
02117  *     0 since we are processing the WM_LBUTTONDOWN Message
02118  *
02119  * NOTES
02120  *  This function is only to be used when a ListBox is a ComboListBox
02121  */
02122 
02123 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
02124 {
02125     RECT clientRect, screenRect;
02126     POINT mousePos;
02127 
02128     mousePos.x = x;
02129     mousePos.y = y;
02130 
02131     GetClientRect(descr->self, &clientRect);
02132 
02133     if(PtInRect(&clientRect, mousePos))
02134     {
02135        /* MousePos is in client, resume normal processing */
02136         if (msg == WM_LBUTTONDOWN)
02137         {
02138            descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
02139            return LISTBOX_HandleLButtonDown( descr, keys, x, y);
02140         }
02141         else if (descr->style & LBS_NOTIFY)
02142             SEND_NOTIFICATION( descr, LBN_DBLCLK );
02143     }
02144     else
02145     {
02146         POINT screenMousePos;
02147         HWND hWndOldCapture;
02148 
02149         /* Check the Non-Client Area */
02150         screenMousePos = mousePos;
02151         hWndOldCapture = GetCapture();
02152         ReleaseCapture();
02153         GetWindowRect(descr->self, &screenRect);
02154         ClientToScreen(descr->self, &screenMousePos);
02155 
02156         if(!PtInRect(&screenRect, screenMousePos))
02157         {
02158             LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
02159             LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
02160             COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
02161         }
02162         else
02163         {
02164             /* Check to see the NC is a scrollbar */
02165             INT nHitTestType=0;
02166             LONG style = GetWindowLongPtrW( descr->self, GWL_STYLE );
02167             /* Check Vertical scroll bar */
02168             if (style & WS_VSCROLL)
02169             {
02170                 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
02171                 if (PtInRect( &clientRect, mousePos ))
02172                     nHitTestType = HTVSCROLL;
02173             }
02174               /* Check horizontal scroll bar */
02175             if (style & WS_HSCROLL)
02176             {
02177                 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
02178                 if (PtInRect( &clientRect, mousePos ))
02179                     nHitTestType = HTHSCROLL;
02180             }
02181             /* Windows sends this message when a scrollbar is clicked
02182              */
02183 
02184             if(nHitTestType != 0)
02185             {
02186                 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
02187                              MAKELONG(screenMousePos.x, screenMousePos.y));
02188             }
02189             /* Resume the Capture after scrolling is complete
02190              */
02191             if(hWndOldCapture != 0)
02192                 SetCapture(hWndOldCapture);
02193         }
02194     }
02195     return 0;
02196 }
02197 
02198 /***********************************************************************
02199  *           LISTBOX_HandleLButtonUp
02200  */
02201 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
02202 {
02203     if (LISTBOX_Timer != LB_TIMER_NONE)
02204         KillSystemTimer( descr->self, LB_TIMER_ID );
02205     LISTBOX_Timer = LB_TIMER_NONE;
02206     if (descr->captured)
02207     {
02208         descr->captured = FALSE;
02209         if (GetCapture() == descr->self) ReleaseCapture();
02210         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
02211             SEND_NOTIFICATION( descr, LBN_SELCHANGE );
02212     }
02213     return 0;
02214 }
02215 
02216 
02217 /***********************************************************************
02218  *           LISTBOX_HandleTimer
02219  *
02220  * Handle scrolling upon a timer event.
02221  * Return TRUE if scrolling should continue.
02222  */
02223 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
02224 {
02225     switch(dir)
02226     {
02227     case LB_TIMER_UP:
02228         if (descr->top_item) index = descr->top_item - 1;
02229         else index = 0;
02230         break;
02231     case LB_TIMER_LEFT:
02232         if (descr->top_item) index -= descr->page_size;
02233         break;
02234     case LB_TIMER_DOWN:
02235         index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
02236         if (index == descr->focus_item) index++;
02237         if (index >= descr->nb_items) index = descr->nb_items - 1;
02238         break;
02239     case LB_TIMER_RIGHT:
02240         if (index + descr->page_size < descr->nb_items)
02241             index += descr->page_size;
02242         break;
02243     case LB_TIMER_NONE:
02244         break;
02245     }
02246     if (index == descr->focus_item) return FALSE;
02247     LISTBOX_MoveCaret( descr, index, FALSE );
02248     return TRUE;
02249 }
02250 
02251 
02252 /***********************************************************************
02253  *           LISTBOX_HandleSystemTimer
02254  *
02255  * WM_SYSTIMER handler.
02256  */
02257 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
02258 {
02259     if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
02260     {
02261         KillSystemTimer( descr->self, LB_TIMER_ID );
02262         LISTBOX_Timer = LB_TIMER_NONE;
02263     }
02264     return 0;
02265 }
02266 
02267 
02268 /***********************************************************************
02269  *           LISTBOX_HandleMouseMove
02270  *
02271  * WM_MOUSEMOVE handler.
02272  */
02273 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
02274                                      INT x, INT y )
02275 {
02276     INT index;
02277     TIMER_DIRECTION dir = LB_TIMER_NONE;
02278 
02279     if (!descr->captured) return;
02280 
02281     if (descr->style & LBS_MULTICOLUMN)
02282     {
02283         if (y < 0) y = 0;
02284         else if (y >= descr->item_height * descr->page_size)
02285             y = descr->item_height * descr->page_size - 1;
02286 
02287         if (x < 0)
02288         {
02289             dir = LB_TIMER_LEFT;
02290             x = 0;
02291         }
02292         else if (x >= descr->width)
02293         {
02294             dir = LB_TIMER_RIGHT;
02295             x = descr->width - 1;
02296         }
02297     }
02298     else
02299     {
02300         if (y < 0) dir = LB_TIMER_UP;  /* above */
02301         else if (y >= descr->height) dir = LB_TIMER_DOWN;  /* below */
02302     }
02303 
02304     index = LISTBOX_GetItemFromPoint( descr, x, y );
02305     if (index == -1) index = descr->focus_item;
02306     if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
02307 
02308     /* Start/stop the system timer */
02309 
02310     if (dir != LB_TIMER_NONE)
02311         SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
02312     else if (LISTBOX_Timer != LB_TIMER_NONE)
02313         KillSystemTimer( descr->self, LB_TIMER_ID );
02314     LISTBOX_Timer = dir;
02315 }
02316 
02317 
02318 /***********************************************************************
02319  *           LISTBOX_HandleKeyDown
02320  */
02321 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
02322 {
02323     INT caret = -1;
02324     BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
02325     if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
02326         bForceSelection = FALSE; /* only for single select list */
02327 
02328     if (descr->style & LBS_WANTKEYBOARDINPUT)
02329     {
02330         caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
02331                                 MAKEWPARAM(LOWORD(key), descr->focus_item),
02332                                 (LPARAM)descr->self );
02333         if (caret == -2) return 0;
02334     }
02335     if (caret == -1) switch(key)
02336     {
02337     case VK_LEFT:
02338         if (descr->style & LBS_MULTICOLUMN)
02339         {
02340             bForceSelection = FALSE;
02341             if (descr->focus_item >= descr->page_size)
02342                 caret = descr->focus_item - descr->page_size;
02343             break;
02344         }
02345         /* fall through */
02346     case VK_UP:
02347         caret = descr->focus_item - 1;
02348         if (caret < 0) caret = 0;
02349         break;
02350     case VK_RIGHT:
02351         if (descr->style & LBS_MULTICOLUMN)
02352         {
02353             bForceSelection = FALSE;
02354             if (descr->focus_item + descr->page_size < descr->nb_items)
02355                 caret = descr->focus_item + descr->page_size;
02356             break;
02357         }
02358         /* fall through */
02359     case VK_DOWN:
02360         caret = descr->focus_item + 1;
02361         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
02362         break;
02363 
02364     case VK_PRIOR:
02365         if (descr->style & LBS_MULTICOLUMN)
02366         {
02367             INT page = descr->width / descr->column_width;
02368             if (page < 1) page = 1;
02369             caret = descr->focus_item - (page * descr->page_size) + 1;
02370         }
02371         else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
02372         if (caret < 0) caret = 0;
02373         break;
02374     case VK_NEXT:
02375         if (descr->style & LBS_MULTICOLUMN)
02376         {
02377             INT page = descr->width / descr->column_width;
02378             if (page < 1) page = 1;
02379             caret = descr->focus_item + (page * descr->page_size) - 1;
02380         }
02381         else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
02382         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
02383         break;
02384     case VK_HOME:
02385         caret = 0;
02386         break;
02387     case VK_END:
02388         caret = descr->nb_items - 1;
02389         break;
02390     case VK_SPACE:
02391         if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
02392         else if (descr->style & LBS_MULTIPLESEL)
02393         {
02394             LISTBOX_SetSelection( descr, descr->focus_item,
02395                                   !descr->items[descr->focus_item].selected,
02396                                   (descr->style & LBS_NOTIFY) != 0 );
02397         }
02398         break;
02399     default:
02400         bForceSelection = FALSE;
02401     }
02402     if (bForceSelection) /* focused item is used instead of key */
02403         caret = descr->focus_item;
02404     if (caret >= 0)
02405     {
02406         if (((descr->style & LBS_EXTENDEDSEL) &&
02407             !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
02408             !IS_MULTISELECT(descr))
02409             descr->anchor_item = caret;
02410         LISTBOX_MoveCaret( descr, caret, TRUE );
02411 
02412         if (descr->style & LBS_MULTIPLESEL)
02413             descr->selected_item = caret;
02414         else
02415             LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
02416         if (descr->style & LBS_NOTIFY)
02417         {
02418             if (descr->lphc && IsWindowVisible( descr->self ))
02419             {
02420                 /* make sure that combo parent doesn't hide us */
02421                 descr->lphc->wState |= CBF_NOROLLUP;
02422             }
02423             if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
02424         }
02425     }
02426     return 0;
02427 }
02428 
02429 
02430 /***********************************************************************
02431  *           LISTBOX_HandleChar
02432  */
02433 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
02434 {
02435     INT caret = -1;
02436     WCHAR str[2];
02437 
02438     str[0] = charW;
02439     str[1] = '\0';
02440 
02441     if (descr->style & LBS_WANTKEYBOARDINPUT)
02442     {
02443         caret = SendMessageW( descr->owner, WM_CHARTOITEM,
02444                                 MAKEWPARAM(charW, descr->focus_item),
02445                                 (LPARAM)descr->self );
02446         if (caret == -2) return 0;
02447     }
02448     if (caret == -1)
02449         caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
02450     if (caret != -1)
02451     {
02452         if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
02453            LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
02454         LISTBOX_MoveCaret( descr, caret, TRUE );
02455         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
02456             SEND_NOTIFICATION( descr, LBN_SELCHANGE );
02457     }
02458     return 0;
02459 }
02460 
02461 /* ReactOS Retrieve the UI state for the control */
02462 static BOOL LISTBOX_update_uistate(LB_DESCR *descr)
02463 {
02464     LONG prev_flags;
02465 
02466     prev_flags = descr->UIState;
02467     descr->UIState = DefWindowProcW(descr->self, WM_QUERYUISTATE, 0, 0);
02468     return prev_flags != descr->UIState;
02469 }
02470 
02471 
02472 /***********************************************************************
02473  *           LISTBOX_Create
02474  */
02475 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
02476 {
02477     LB_DESCR *descr;
02478     MEASUREITEMSTRUCT mis;
02479     RECT rect;
02480 
02481     if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
02482         return FALSE;
02483 
02484     GetClientRect( hwnd, &rect );
02485     descr->self          = hwnd;
02486     descr->owner         = GetParent( descr->self );
02487     descr->style         = GetWindowLongPtrW( descr->self, GWL_STYLE );
02488     descr->width         = rect.right - rect.left;
02489     descr->height        = rect.bottom - rect.top;
02490     descr->items         = NULL;
02491     descr->nb_items      = 0;
02492     descr->top_item      = 0;
02493     descr->selected_item = -1;
02494     descr->focus_item    = 0;
02495     descr->anchor_item   = -1;
02496     descr->item_height   = 1;
02497     descr->page_size     = 1;
02498     descr->column_width  = 150;
02499     descr->horz_extent   = (descr->style & WS_HSCROLL) ? 1 : 0;
02500     descr->horz_pos      = 0;
02501     descr->nb_tabs       = 0;
02502     descr->tabs          = NULL;
02503     descr->caret_on      = lphc ? FALSE : TRUE;
02504     if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
02505     descr->in_focus      = FALSE;
02506     descr->captured      = FALSE;
02507     descr->font          = 0;
02508     descr->locale        = GetUserDefaultLCID();
02509     descr->lphc      = lphc;
02510 
02511     if( lphc )
02512     {
02513         TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
02514         descr->owner = lphc->self;
02515     }
02516 
02517     SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
02518 
02519     LISTBOX_update_uistate(descr); // ReactOS
02520 
02521 /*    if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
02522  */
02523     if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
02524     if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
02525     if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
02526     descr->item_height = LISTBOX_SetFont( descr, 0 );
02527 
02528     if (descr->style & LBS_OWNERDRAWFIXED)
02529     {
02530     if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
02531     {
02532         /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
02533       descr->item_height = lphc->fixedOwnerDrawHeight;
02534     }
02535     else
02536     {
02537             UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
02538             mis.CtlType    = ODT_LISTBOX;
02539             mis.CtlID      = id;
02540             mis.itemID     = -1;
02541             mis.itemWidth  =  0;
02542             mis.itemData   =  0;
02543             mis.itemHeight = descr->item_height;
02544             SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
02545             descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
02546     }
02547     }
02548 
02549     TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
02550     return TRUE;
02551 }
02552 
02553 
02554 /***********************************************************************
02555  *           LISTBOX_Destroy
02556  */
02557 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
02558 {
02559     LISTBOX_ResetContent( descr );
02560     SetWindowLongPtrW( descr->self, 0, 0 );
02561     HeapFree( GetProcessHeap(), 0, descr );
02562     return TRUE;
02563 }
02564 
02565 
02566 /***********************************************************************
02567  *           ListBoxWndProc_common
02568  */
02569 LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
02570                                    WPARAM wParam, LPARAM lParam, BOOL unicode )
02571 {
02572     LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
02573     LPHEADCOMBO lphc = 0;
02574     LRESULT ret;
02575 #ifdef __REACTOS__
02576     PWND pWnd;
02577 
02578     pWnd = ValidateHwnd(hwnd);
02579     if (pWnd)
02580     {
02581        if (!pWnd->fnid)
02582        {
02583           NtUserSetWindowFNID(hwnd, FNID_LISTBOX); // Could be FNID_COMBOLBOX by class.
02584        }
02585        else
02586        {
02587           if (pWnd->fnid != FNID_LISTBOX)
02588           {
02589              ERR("Wrong window class for listbox! fnId 0x%x\n",pWnd->fnid);
02590              return 0;
02591           }
02592        }
02593     }    
02594 #endif    
02595 
02596     if (!descr)
02597     {
02598         if (!IsWindow(hwnd)) return 0;
02599 
02600         if (msg == WM_CREATE)
02601         {
02602         CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
02603             if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
02604             if (!LISTBOX_Create( hwnd, lphc )) return -1;
02605             TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
02606             return 0;
02607         }
02608         /* Ignore all other messages before we get a WM_CREATE */
02609         return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
02610                          DefWindowProcA( hwnd, msg, wParam, lParam );
02611     }
02612     if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
02613 
02614     TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
02615           descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
02616 
02617     switch(msg)
02618     {
02619     case LB_RESETCONTENT:
02620         LISTBOX_ResetContent( descr );
02621         LISTBOX_UpdateScroll( descr );
02622         InvalidateRect( descr->self, NULL, TRUE );
02623         return 0;
02624 
02625     case LB_ADDSTRING:
02626     case LB_ADDSTRING_LOWER:
02627     case LB_ADDSTRING_UPPER:
02628     {
02629         INT ret;
02630         LPWSTR textW;
02631         if(unicode || !HAS_STRINGS(descr))
02632             textW = (LPWSTR)lParam;
02633         else
02634         {
02635             LPSTR textA = (LPSTR)lParam;
02636             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
02637             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
02638                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
02639             else
02640                 return LB_ERRSPACE;
02641         }
02642         /* in the unicode the version, the string is really overwritten
02643            during the converting case */
02644         if (msg == LB_ADDSTRING_LOWER)
02645             strlwrW(textW);
02646         else if (msg == LB_ADDSTRING_UPPER)
02647             struprW(textW);
02648         wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
02649         ret = LISTBOX_InsertString( descr, wParam, textW );
02650         if (!unicode && HAS_STRINGS(descr))
02651             HeapFree(GetProcessHeap(), 0, textW);
02652         return ret;
02653     }
02654 
02655     case LB_INSERTSTRING:
02656     case LB_INSERTSTRING_UPPER:
02657     case LB_INSERTSTRING_LOWER:
02658     {
02659         INT ret;
02660         LPWSTR textW;
02661         if(unicode || !HAS_STRINGS(descr))
02662             textW = (LPWSTR)lParam;
02663         else
02664         {
02665             LPSTR textA = (LPSTR)lParam;
02666             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
02667             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
02668                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
02669             else
02670                 return LB_ERRSPACE;
02671         }
02672         /* in the unicode the version, the string is really overwritten
02673            during the converting case */
02674         if (msg == LB_INSERTSTRING_LOWER)
02675             strlwrW(textW);
02676         else if (msg == LB_INSERTSTRING_UPPER)
02677             struprW(textW);
02678         ret = LISTBOX_InsertString( descr, wParam, textW );
02679         if(!unicode && HAS_STRINGS(descr))
02680             HeapFree(GetProcessHeap(), 0, textW);
02681         return ret;
02682     }
02683 
02684     case LB_ADDFILE:
02685     {
02686         INT ret;
02687         LPWSTR textW;
02688         if(unicode || !HAS_STRINGS(descr))
02689             textW = (LPWSTR)lParam;
02690         else
02691         {
02692             LPSTR textA = (LPSTR)lParam;
02693             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
02694             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
02695                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
02696             else
02697                 return LB_ERRSPACE;
02698         }
02699         wParam = LISTBOX_FindFileStrPos( descr, textW );
02700         ret = LISTBOX_InsertString( descr, wParam, textW );
02701         if(!unicode && HAS_STRINGS(descr))
02702             HeapFree(GetProcessHeap(), 0, textW);
02703         return ret;
02704     }
02705 
02706     case LB_DELETESTRING:
02707         if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
02708             return descr->nb_items;
02709         else
02710         {
02711             SetLastError(ERROR_INVALID_INDEX);
02712             return LB_ERR;
02713         }
02714 
02715     case LB_GETITEMDATA:
02716         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
02717         {
02718             SetLastError(ERROR_INVALID_INDEX);
02719             return LB_ERR;
02720         }
02721         return descr->items[wParam].data;
02722 
02723     case LB_SETITEMDATA:
02724         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
02725         {
02726             SetLastError(ERROR_INVALID_INDEX);
02727             return LB_ERR;
02728         }
02729         descr->items[wParam].data = lParam;
02730         /* undocumented: returns TRUE, not LB_OKAY (0) */
02731         return TRUE;
02732 
02733     case LB_GETCOUNT:
02734         return descr->nb_items;
02735 
02736     case LB_GETTEXT:
02737         return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
02738 
02739     case LB_GETTEXTLEN:
02740         if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
02741         {
02742             SetLastError(ERROR_INVALID_INDEX);
02743             return LB_ERR;
02744         }
02745         if (!HAS_STRINGS(descr)) return sizeof(DWORD);
02746         if (unicode) return strlenW( descr->items[wParam].str );
02747         return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
02748                                     strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
02749 
02750     case LB_GETCURSEL:
02751         if (descr->nb_items == 0)
02752             return LB_ERR;
02753         if (!IS_MULTISELECT(descr))
02754             return descr->selected_item;
02755         if (descr->selected_item != -1)
02756             return descr->selected_item;
02757         return descr->focus_item;
02758         /* otherwise, if the user tries to move the selection with the    */
02759         /* arrow keys, we will give the application something to choke on */
02760     case LB_GETTOPINDEX:
02761         return descr->top_item;
02762 
02763     case LB_GETITEMHEIGHT:
02764         return LISTBOX_GetItemHeight( descr, wParam );
02765 
02766     case LB_SETITEMHEIGHT:
02767         return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
02768 
02769     case LB_ITEMFROMPOINT:
02770         {
02771             POINT pt;
02772             RECT rect;
02773             int index;
02774             BOOL hit = TRUE;
02775 
02776             /* The hiword of the return value is not a client area
02777                hittest as suggested by MSDN, but rather a hittest on
02778                the returned listbox item. */
02779 
02780             if(descr->nb_items == 0)
02781                 return 0x1ffff;      /* win9x returns 0x10000, we copy winnt */
02782 
02783             pt.x = (short)LOWORD(lParam);
02784             pt.y = (short)HIWORD(lParam);
02785 
02786             SetRect(&rect, 0, 0, descr->width, descr->height);
02787 
02788             if(!PtInRect(&rect, pt))
02789             {
02790                 pt.x = min(pt.x, rect.right - 1);
02791                 pt.x = max(pt.x, 0);
02792                 pt.y = min(pt.y, rect.bottom - 1);
02793                 pt.y = max(pt.y, 0);
02794                 hit = FALSE;
02795             }
02796 
02797             index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
02798 
02799             if(index == -1)
02800             {
02801                 index = descr->nb_items - 1;
02802                 hit = FALSE;
02803             }
02804             return MAKELONG(index, hit ? 0 : 1);
02805         }
02806 
02807     case LB_SETCARETINDEX:
02808         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
02809         if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
02810             return LB_ERR;
02811         else if (ISWIN31)
02812              return wParam;
02813         else
02814              return LB_OKAY;
02815 
02816     case LB_GETCARETINDEX:
02817         return descr->focus_item;
02818 
02819     case LB_SETTOPINDEX:
02820         return LISTBOX_SetTopItem( descr, wParam, TRUE );
02821 
02822     case LB_SETCOLUMNWIDTH:
02823         return LISTBOX_SetColumnWidth( descr, wParam );
02824 
02825     case LB_GETITEMRECT:
02826         return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
02827 
02828     case LB_FINDSTRING:
02829     {
02830         INT ret;
02831         LPWSTR textW;
02832         if(unicode || !HAS_STRINGS(descr))
02833             textW = (LPWSTR)lParam;
02834         else
02835         {
02836             LPSTR textA = (LPSTR)lParam;
02837             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
02838             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
02839                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
02840         }
02841         ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
02842         if(!unicode && HAS_STRINGS(descr))
02843             HeapFree(GetProcessHeap(), 0, textW);
02844         return ret;
02845     }
02846 
02847     case LB_FINDSTRINGEXACT:
02848     {
02849         INT ret;
02850         LPWSTR textW;
02851         if(unicode || !HAS_STRINGS(descr))
02852             textW = (LPWSTR)lParam;
02853         else
02854         {
02855             LPSTR textA = (LPSTR)lParam;
02856             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
02857             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
02858                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
02859         }
02860         ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
02861         if(!unicode && HAS_STRINGS(descr))
02862             HeapFree(GetProcessHeap(), 0, textW);
02863         return ret;
02864     }
02865 
02866     case LB_SELECTSTRING:
02867     {
02868         INT index;
02869         LPWSTR textW;
02870 
02871     if(HAS_STRINGS(descr))
02872         TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
02873                              debugstr_a((LPSTR)lParam));
02874         if(unicode || !HAS_STRINGS(descr))
02875             textW = (LPWSTR)lParam;
02876         else
02877         {
02878             LPSTR textA = (LPSTR)lParam;
02879             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
02880             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
02881                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
02882         }
02883         index = LISTBOX_FindString( descr, wParam, textW, FALSE );
02884         if(!unicode && HAS_STRINGS(descr))
02885             HeapFree(GetProcessHeap(), 0, textW);
02886         if (index != LB_ERR)
02887     {
02888             LISTBOX_MoveCaret( descr, index, TRUE );
02889             LISTBOX_SetSelection( descr, index, TRUE, FALSE );
02890     }
02891         return index;
02892     }
02893 
02894     case LB_GETSEL:
02895         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
02896             return LB_ERR;
02897         return descr->items[wParam].selected;
02898 
02899     case LB_SETSEL:
02900         return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
02901 
02902     case LB_SETCURSEL:
02903         if (IS_MULTISELECT(descr)) return LB_ERR;
02904         LISTBOX_SetCaretIndex( descr, wParam, FALSE );
02905         ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
02906     if (ret != LB_ERR) ret = descr->selected_item;
02907     return ret;
02908 
02909     case LB_GETSELCOUNT:
02910         return LISTBOX_GetSelCount( descr );
02911 
02912     case LB_GETSELITEMS:
02913         return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
02914 
02915     case LB_SELITEMRANGE:
02916         if (LOWORD(lParam) <= HIWORD(lParam))
02917             return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
02918                                             HIWORD(lParam), wParam );
02919         else
02920             return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
02921                                             LOWORD(lParam), wParam );
02922 
02923     case LB_SELITEMRANGEEX:
02924         if ((INT)lParam >= (INT)wParam)
02925             return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
02926         else
02927             return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
02928 
02929     case LB_GETHORIZONTALEXTENT:
02930         return descr->horz_extent;
02931 
02932     case LB_SETHORIZONTALEXTENT:
02933         return LISTBOX_SetHorizontalExtent( descr, wParam );
02934 
02935     case LB_GETANCHORINDEX:
02936         return descr->anchor_item;
02937 
02938     case LB_SETANCHORINDEX:
02939         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
02940         {
02941             SetLastError(ERROR_INVALID_INDEX);
02942             return LB_ERR;
02943         }
02944         descr->anchor_item = (INT)wParam;
02945         return LB_OKAY;
02946 
02947     case LB_DIR:
02948     {
02949         INT ret;
02950         LPWSTR textW;
02951         if(unicode)
02952             textW = (LPWSTR)lParam;
02953         else
02954         {
02955             LPSTR textA = (LPSTR)lParam;
02956             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
02957             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
02958                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
02959         }
02960         ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
02961         if(!unicode)
02962             HeapFree(GetProcessHeap(), 0, textW);
02963         return ret;
02964     }
02965 
02966     case LB_GETLOCALE:
02967         return descr->locale;
02968 
02969     case LB_SETLOCALE:
02970     {
02971         LCID ret;
02972         if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
02973             return LB_ERR;
02974         ret = descr->locale;
02975         descr->locale = (LCID)wParam;
02976         return ret;
02977     }
02978 
02979     case LB_INITSTORAGE:
02980         return LISTBOX_InitStorage( descr, wParam );
02981 
02982     case LB_SETCOUNT:
02983         return LISTBOX_SetCount( descr, (INT)wParam );
02984 
02985     case LB_SETTABSTOPS:
02986         return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam );
02987 
02988     case LB_CARETON:
02989         if (descr->caret_on)
02990             return LB_OKAY;
02991         descr->caret_on = TRUE;
02992         if ((descr->focus_item != -1) && (descr->in_focus))
02993             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
02994         return LB_OKAY;
02995 
02996     case LB_CARETOFF:
02997         if (!descr->caret_on)
02998             return LB_OKAY;
02999         descr->caret_on = FALSE;
03000         if ((descr->focus_item != -1) && (descr->in_focus))
03001             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
03002         return LB_OKAY;
03003 
03004     case LB_GETLISTBOXINFO:
03005         FIXME("LB_GETLISTBOXINFO: stub!\n");
03006         return 0;
03007 
03008     case WM_DESTROY:
03009         return LISTBOX_Destroy( descr );
03010 
03011     case WM_ENABLE:
03012         InvalidateRect( descr->self, NULL, TRUE );
03013         return 0;
03014 
03015     case WM_SETREDRAW:
03016         LISTBOX_SetRedraw( descr, wParam != 0 );
03017         return 0;
03018 
03019     case WM_GETDLGCODE:
03020         return DLGC_WANTARROWS | DLGC_WANTCHARS;
03021 
03022     case WM_PRINTCLIENT:
03023     case WM_PAINT:
03024         {
03025             PAINTSTRUCT ps;
03026             HDC hdc = ( wParam ) ? ((HDC)wParam) :  BeginPaint( descr->self, &ps );
03027             ret = LISTBOX_Paint( descr, hdc );
03028             if( !wParam ) EndPaint( descr->self, &ps );
03029         }
03030         return ret;
03031     case WM_SIZE:
03032         LISTBOX_UpdateSize( descr );
03033         return 0;
03034     case WM_GETFONT:
03035         return (LRESULT)descr->font;
03036     case WM_SETFONT:
03037         LISTBOX_SetFont( descr, (HFONT)wParam );
03038         if (lParam) InvalidateRect( descr->self, 0, TRUE );
03039         return 0;
03040     case WM_SETFOCUS:
03041         descr->in_focus = TRUE;
03042         descr->caret_on = TRUE;
03043         if (descr->focus_item != -1)
03044             LISTBOX_DrawFocusRect( descr, TRUE );
03045         SEND_NOTIFICATION( descr, LBN_SETFOCUS );
03046         return 0;
03047     case WM_KILLFOCUS:
03048         descr->in_focus = FALSE;
03049         if ((descr->focus_item != -1) && descr->caret_on)
03050             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
03051         SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
03052         return 0;
03053     case WM_HSCROLL:
03054         return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
03055     case WM_VSCROLL:
03056         return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
03057     case WM_MOUSEWHEEL:
03058         if (wParam & (MK_SHIFT | MK_CONTROL))
03059             return DefWindowProcW( descr->self, msg, wParam, lParam );
03060         return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
03061     case WM_LBUTTONDOWN:
03062     if (lphc)
03063             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
03064                                                   (INT16)LOWORD(lParam),
03065                                                   (INT16)HIWORD(lParam) );
03066         return LISTBOX_HandleLButtonDown( descr, wParam,
03067                                           (INT16)LOWORD(lParam),
03068                                           (INT16)HIWORD(lParam) );
03069     case WM_LBUTTONDBLCLK:
03070     if (lphc)
03071             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
03072                                                   (INT16)LOWORD(lParam),
03073                                                   (INT16)HIWORD(lParam) );
03074         if (descr->style & LBS_NOTIFY)
03075             SEND_NOTIFICATION( descr, LBN_DBLCLK );
03076         return 0;
03077     case WM_MOUSEMOVE:
03078         if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
03079         {
03080             BOOL    captured = descr->captured;
03081             POINT   mousePos;
03082             RECT    clientRect;
03083 
03084             mousePos.x = (INT16)LOWORD(lParam);
03085             mousePos.y = (INT16)HIWORD(lParam);
03086 
03087             /*
03088              * If we are in a dropdown combobox, we simulate that
03089              * the mouse is captured to show the tracking of the item.
03090              */
03091             if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
03092                 descr->captured = TRUE;
03093 
03094             LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
03095 
03096             descr->captured = captured;
03097         } 
03098         else if (GetCapture() == descr->self)
03099         {
03100             LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
03101                                      (INT16)HIWORD(lParam) );
03102         }
03103         return 0;
03104     case WM_LBUTTONUP:
03105     if (lphc)
03106     {
03107             POINT mousePos;
03108             RECT  clientRect;
03109 
03110             /*
03111              * If the mouse button "up" is not in the listbox,
03112              * we make sure there is no selection by re-selecting the
03113              * item that was selected when the listbox was made visible.
03114              */
03115             mousePos.x = (INT16)LOWORD(lParam);
03116             mousePos.y = (INT16)HIWORD(lParam);
03117 
03118             GetClientRect(descr->self, &clientRect);
03119 
03120             /*
03121              * When the user clicks outside the combobox and the focus
03122              * is lost, the owning combobox will send a fake buttonup with
03123              * 0xFFFFFFF as the mouse location, we must also revert the
03124              * selection to the original selection.
03125              */
03126             if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
03127                 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
03128         }
03129         return LISTBOX_HandleLButtonUp( descr );
03130     case WM_KEYDOWN:
03131         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
03132         {
03133             /* for some reason Windows makes it possible to
03134              * show/hide ComboLBox by sending it WM_KEYDOWNs */
03135 
03136             if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
03137                 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
03138                   && (wParam == VK_DOWN || wParam == VK_UP)) )
03139             {
03140                 COMBO_FlipListbox( lphc, FALSE, FALSE );
03141                 return 0;
03142             }
03143         }
03144         return LISTBOX_HandleKeyDown( descr, wParam );
03145     case WM_CHAR:
03146     {
03147         WCHAR charW;
03148         if(unicode)
03149             charW = (WCHAR)wParam;
03150         else
03151         {
03152             CHAR charA = (CHAR)wParam;
03153             MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
03154         }
03155         return LISTBOX_HandleChar( descr, charW );
03156     }
03157     case WM_SYSTIMER:
03158         return LISTBOX_HandleSystemTimer( descr );
03159     case WM_ERASEBKGND:
03160         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
03161         {
03162             RECT rect;
03163             HBRUSH hbrush = GetControlColor( descr->owner, descr->self, (HDC)wParam, WM_CTLCOLORLISTBOX);
03164         TRACE("hbrush = %p\n", hbrush);
03165         if(!hbrush)
03166         hbrush = GetSysColorBrush(COLOR_WINDOW);
03167         if(hbrush)
03168         {
03169         GetClientRect(descr->self, &rect);
03170         FillRect((HDC)wParam, &rect, hbrush);
03171         }
03172         }
03173         return 1;
03174     case WM_DROPFILES:
03175         if( lphc ) return 0;
03176         return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
03177                          SendMessageA( descr->owner, msg, wParam, lParam );
03178 
03179     case WM_NCDESTROY:
03180         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
03181             lphc->hWndLBox = 0;
03182 #ifdef __REACTOS__
03183         NtUserSetWindowFNID(hwnd, FNID_DESTROY);
03184 #endif
03185         break;
03186 
03187     case WM_NCACTIVATE:
03188         if (lphc) return 0;
03189     break;
03190 // ReactOS
03191     case WM_UPDATEUISTATE:
03192         if (unicode)
03193             DefWindowProcW(descr->self, msg, wParam, lParam);
03194         else
03195             DefWindowProcA(descr->self, msg, wParam, lParam);
03196 
03197         if (LISTBOX_update_uistate(descr))
03198         {
03199            /* redraw text */
03200            if (descr->focus_item != -1)
03201                LISTBOX_DrawFocusRect( descr, descr->in_focus );
03202         }
03203         break;
03204 //
03205     default:
03206         if ((msg >= WM_USER) && (msg < 0xc000))
03207             WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
03208                  hwnd, msg, wParam, lParam );
03209     }
03210 
03211     return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
03212                      DefWindowProcA( hwnd, msg, wParam, lParam );
03213 }
03214 
03215 /***********************************************************************
03216  *           ListBoxWndProcA
03217  */
03218 LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
03219 {
03220     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
03221 }
03222 
03223 /***********************************************************************
03224  *           ListBoxWndProcW
03225  */
03226 LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
03227 {
03228     return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
03229 }

Generated on Sun May 27 2012 04:38:41 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.