updated listbox control from wine 0.9.2
fixes 36 listbox tests
Modified: trunk/reactos/lib/user32/controls/combo.c
Modified: trunk/reactos/lib/user32/controls/listbox.c
Modified: trunk/reactos/lib/user32/include/user32.h
Modified: trunk/reactos/w32api/include/winuser.h

Modified: trunk/reactos/lib/user32/controls/combo.c
--- trunk/reactos/lib/user32/controls/combo.c	2005-12-04 14:17:13 UTC (rev 19864)
+++ trunk/reactos/lib/user32/controls/combo.c	2005-12-04 14:19:05 UTC (rev 19865)
@@ -548,7 +548,7 @@
 
       /* create listbox popup */
 
-      lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
+      lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
                  (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
 
       if( lphc->dwStyle & CBS_SORT )

Modified: trunk/reactos/lib/user32/controls/listbox.c
--- trunk/reactos/lib/user32/controls/listbox.c	2005-12-04 14:17:13 UTC (rev 19864)
+++ trunk/reactos/lib/user32/controls/listbox.c	2005-12-04 14:19:05 UTC (rev 19865)
@@ -34,7 +34,6 @@
 #define WM_LBTRACKPOINT     0x0131
 #define WS_EX_DRAGDETECT    0x00000002L
 #define WM_BEGINDRAG        0x022C
-#define WM_SYSTIMER         280
 
 UINT STDCALL SetSystemTimer(HWND,UINT_PTR,UINT,TIMERPROC);
 BOOL STDCALL KillSystemTimer(HWND,UINT_PTR);
@@ -73,6 +72,7 @@
 /* Listbox structure */
 typedef struct
 {
+    HWND        self;           /* Our own window handle */
     HWND        owner;          /* Owner window to send notifications to */
     UINT        style;          /* Window style */
     INT         width;          /* Window width */
@@ -90,6 +90,7 @@
     INT         horz_pos;       /* Horizontal position */
     INT         nb_tabs;        /* Number of tabs in array */
     INT        *tabs;           /* Array of tabs */
+    INT         avg_char_width; /* Average width of characters */
     BOOL        caret_on;       /* Is caret on? */
     BOOL        captured;       /* Is mouse captured? */
     BOOL	in_focus;
@@ -107,11 +108,12 @@
 
 
 #define IS_MULTISELECT(descr) \
-    ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
+    ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
+     !((descr)->style & LBS_NOSEL))
 
-#define SEND_NOTIFICATION(hwnd,descr,code) \
+#define SEND_NOTIFICATION(descr,code) \
     (SendMessageW( (descr)->owner, WM_COMMAND, \
-     MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
+     MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
 
 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
 
@@ -127,8 +129,6 @@
 
 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
 
-static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
-static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
 
@@ -167,16 +167,16 @@
 #ifdef __REACTOS__
     L"ComboLBox",          /* name */
     CS_DBLCLKS | CS_SAVEBITS,  /* style */
-    (WNDPROC)ComboLBWndProcW,      /* procW */
-    (WNDPROC)ComboLBWndProcA,      /* procA */
+    (WNDPROC)ListBoxWndProcW,      /* procW */
+    (WNDPROC)ListBoxWndProcA,      /* procA */
     sizeof(LB_DESCR *),   /* extra */
     (LPCWSTR) IDC_ARROW,           /* cursor */
     0                     /* brush */
 #else
     "ComboLBox",          /* name */
     CS_DBLCLKS | CS_SAVEBITS,  /* style */
-    ComboLBWndProcA,      /* procA */
-    ComboLBWndProcW,      /* procW */
+    ListBoxWndProcA,      /* procA */
+    ListBoxWndProcW,      /* procW */
     sizeof(LB_DESCR *),   /* extra */
     IDC_ARROW,            /* cursor */
     0                     /* brush */
@@ -267,7 +267,7 @@
  * Update the scrollbars. Should be called whenever the content
  * of the listbox changes.
  */
-static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
+static void LISTBOX_UpdateScroll( LB_DESCR *descr )
 {
     SCROLLINFO info;
 
@@ -300,11 +300,11 @@
         if (descr->style & LBS_DISABLENOSCROLL)
             info.fMask |= SIF_DISABLENOSCROLL;
         if (descr->style & WS_HSCROLL)
-            SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
+            SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
         info.nMax = 0;
         info.fMask = SIF_RANGE;
         if (descr->style & WS_VSCROLL)
-            SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
+            SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
     }
     else
     {
@@ -316,7 +316,7 @@
         if (descr->style & LBS_DISABLENOSCROLL)
             info.fMask |= SIF_DISABLENOSCROLL;
         if (descr->style & WS_VSCROLL)
-            SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
+            SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
 
         if (descr->horz_extent)
         {
@@ -328,7 +328,7 @@
             if (descr->style & LBS_DISABLENOSCROLL)
                 info.fMask |= SIF_DISABLENOSCROLL;
             if (descr->style & WS_HSCROLL)
-                SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
+                SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
         }
     }
 }
@@ -339,7 +339,7 @@
  *
  * Set the top item of the listbox, scrolling up or down if necessary.
  */
-static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
+static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index,
                                    BOOL scroll )
 {
     INT max = LISTBOX_GetMaxTopIndex( descr );
@@ -351,7 +351,7 @@
     {
         INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
         if (scroll && (abs(diff) < descr->width))
-            ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
+            ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
                               SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
 
         else
@@ -379,14 +379,14 @@
             diff = (descr->top_item - index) * descr->item_height;
 
         if (abs(diff) < descr->height)
-            ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
+            ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
                               SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
         else
             scroll = FALSE;
     }
-    if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
+    if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
     descr->top_item = index;
-    LISTBOX_UpdateScroll( hwnd, descr );
+    LISTBOX_UpdateScroll( descr );
     return LB_OKAY;
 }
 
@@ -397,7 +397,7 @@
  * Update the page size. Should be called when the size of
  * the client area or the item height changes.
  */
-static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
+static void LISTBOX_UpdatePage( LB_DESCR *descr )
 {
     INT page_size;
 
@@ -406,8 +406,8 @@
     if (page_size == descr->page_size) return;
     descr->page_size = page_size;
     if (descr->style & LBS_MULTICOLUMN)
-        InvalidateRect( hwnd, NULL, TRUE );
-    LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
+        InvalidateRect( descr->self, NULL, TRUE );
+    LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
 }
 
 
@@ -417,11 +417,11 @@
  * Update the size of the listbox. Should be called when the size of
  * the client area changes.
  */
-static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
+static void LISTBOX_UpdateSize( LB_DESCR *descr )
 {
     RECT rect;
 
-    GetClientRect( hwnd, &rect );
+    GetClientRect( descr->self, &rect );
     descr->width  = rect.right - rect.left;
     descr->height = rect.bottom - rect.top;
     if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
@@ -429,7 +429,7 @@
         INT remaining;
         RECT rect;
 
-        GetWindowRect( hwnd, &rect );
+        GetWindowRect( descr->self, &rect );
         if(descr->item_height != 0)
             remaining = descr->height % descr->item_height;
         else
@@ -447,21 +447,21 @@
             }
 #endif
             TRACE("[%p]: changing height %d -> %d\n",
-                  hwnd, descr->height, descr->height - remaining );
-            SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
+                  descr->self, descr->height, descr->height - remaining );
+            SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
                             rect.bottom - rect.top - remaining,
                             SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
             return;
         }
     }
-    TRACE("[%p]: new size = %d,%d\n", hwnd, descr->width, descr->height );
-    LISTBOX_UpdatePage( hwnd, descr );
-    LISTBOX_UpdateScroll( hwnd, descr );
+    TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
+    LISTBOX_UpdatePage( descr );
+    LISTBOX_UpdateScroll( descr );
 
     /* Invalidate the focused item so it will be repainted correctly */
     if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
     {
-        InvalidateRect( hwnd, &rect, FALSE );
+        InvalidateRect( descr->self, &rect, FALSE );
     }
 }
 
@@ -475,7 +475,12 @@
 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
 {
     /* Index <= 0 is legal even on empty listboxes */
-    if (index && (index >= descr->nb_items)) return -1;
+    if (index && (index >= descr->nb_items))
+    {
+        memset(rect, 0, sizeof(*rect));
+        SetLastError(ERROR_INVALID_INDEX);
+        return LB_ERR;
+    }
     SetRect( rect, 0, 0, descr->width, descr->height );
     if (descr->style & LBS_MULTICOLUMN)
     {
@@ -570,7 +575,7 @@
  *
  * Paint an item.
  */
-static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
+static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc,
                                const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
 {
     LB_ITEMDATA *item = NULL;
@@ -581,7 +586,6 @@
         DRAWITEMSTRUCT dis;
         RECT r;
         HRGN hrgn;
-        UINT id = GetWindowLongA( hwnd, GWL_ID );
 
 	if (!item)
 	{
@@ -596,14 +600,14 @@
         drawing the item, *and* restore the previous region
         after they are done, so a region has better to exist
         else everything ends clipped */
-        GetClientRect(hwnd, &r);
+        GetClientRect(descr->self, &r);
         hrgn = CreateRectRgnIndirect(&r);
         SelectClipRgn( hdc, hrgn);
         DeleteObject( hrgn );
 
         dis.CtlType      = ODT_LISTBOX;
-        dis.CtlID        = id;
-        dis.hwndItem     = hwnd;
+        dis.CtlID        = GetWindowLongPtrW( descr->self, GWLP_ID );
+        dis.hwndItem     = descr->self;
         dis.itemAction   = action;
         dis.hDC          = hdc;
         dis.itemID       = index;
@@ -612,13 +616,13 @@
         if (!ignoreFocus && (descr->focus_item == index) &&
             (descr->caret_on) &&
             (descr->in_focus)) dis.itemState |= ODS_FOCUS;
-        if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
+        if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
         dis.itemData     = item ? item->data : 0;
         dis.rcItem       = *rect;
         TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
-              hwnd, index, item ? debugstr_w(item->str) : "", action,
+              descr->self, index, item ? debugstr_w(item->str) : "", action,
               dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
-        SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
+        SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
     }
     else
     {
@@ -636,7 +640,7 @@
         }
 
         TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
-              hwnd, index, item ? debugstr_w(item->str) : "", action,
+              descr->self, index, item ? debugstr_w(item->str) : "", action,
               rect->left, rect->top, rect->right, rect->bottom );
         if (!item)
             ExtTextOutW( hdc, rect->left + 1, rect->top,
@@ -671,7 +675,7 @@
  *
  * Change the redraw flag.
  */
-static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
+static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
 {
     if (on)
     {
@@ -679,7 +683,7 @@
         descr->style &= ~LBS_NOREDRAW;
         if (descr->style & LBS_DISPLAYCHANGED)
         {     /* page was changed while setredraw false, refresh automatically */
-            InvalidateRect(hwnd, NULL, TRUE);
+            InvalidateRect(descr->self, NULL, TRUE);
             if ((descr->top_item + descr->page_size) > descr->nb_items)
             {      /* reset top of page if less than number of items/page */
                 descr->top_item = descr->nb_items - descr->page_size;
@@ -687,7 +691,7 @@
             }
             descr->style &= ~LBS_DISPLAYCHANGED;
         }
-        LISTBOX_UpdateScroll( hwnd, descr );
+        LISTBOX_UpdateScroll( descr );
     }
     else descr->style |= LBS_NOREDRAW;
 }
@@ -698,7 +702,7 @@
  *
  * Repaint a single item synchronously.
  */
-static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
+static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index,
                                  UINT action )
 {
     HDC hdc;
@@ -707,32 +711,32 @@
     HBRUSH hbrush, oldBrush = 0;
 
     /* Do not repaint the item if the item is not visible */
-    if (!IsWindowVisible(hwnd)) return;
+    if (!IsWindowVisible(descr->self)) return;
     if (descr->style & LBS_NOREDRAW)
     {
        descr->style |= LBS_DISPLAYCHANGED;
        return;
     }
     if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
-    if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
+    if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
     if (descr->font) oldFont = SelectObject( hdc, descr->font );
     hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
-				   (WPARAM)hdc, (LPARAM)hwnd );
+				   (WPARAM)hdc, (LPARAM)descr->self );
     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
-    if (!IsWindowEnabled(hwnd))
+    if (!IsWindowEnabled(descr->self))
         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
     SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
-    LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
+    LISTBOX_PaintItem( descr, hdc, &rect, index, action, FALSE );
     if (oldFont) SelectObject( hdc, oldFont );
     if (oldBrush) SelectObject( hdc, oldBrush );
-    ReleaseDC( hwnd, hdc );
+    ReleaseDC( descr->self, hdc );
 }
 
 
 /***********************************************************************
  *           LISTBOX_InitStorage
  */
-static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
+static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
 {
     LB_ITEMDATA *item;
 
@@ -750,7 +754,7 @@
 
     if (!item)
     {
-        SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
+        SEND_NOTIFICATION( descr, LBN_ERRSPACE );
         return LB_ERRSPACE;
     }
     descr->items = item;
@@ -761,19 +765,25 @@
 /***********************************************************************
  *           LISTBOX_SetTabStops
  */
-static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
+static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count,
                                    LPINT tabs, BOOL short_ints )
 {
-    if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
-    if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
+    INT i;
+
+    if (!(descr->style & LBS_USETABSTOPS))
+    {
+        SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
+        return FALSE;
+    }
+
+    HeapFree( GetProcessHeap(), 0, descr->tabs );
     if (!(descr->nb_tabs = count))
     {
         descr->tabs = NULL;
         return TRUE;
     }
-    /* FIXME: count = 1 */
-    if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
-                                            descr->nb_tabs * sizeof(INT) )))
+    if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
+                                   descr->nb_tabs * sizeof(INT) )))
         return FALSE;
 #ifndef __REACTOS__
     if (short_ints)
@@ -792,7 +802,11 @@
 #else
     memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
 #endif
-    /* FIXME: repaint the window? */
+
+    /* convert into "dialog units"*/
+    for (i = 0; i < descr->nb_tabs; i++)
+        descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
+
     return TRUE;
 }
 
@@ -800,34 +814,53 @@
 /***********************************************************************
  *           LISTBOX_GetText
  */
-static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
+static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
 {
-    if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
+    if ((index < 0) || (index >= descr->nb_items))
+    {
+        SetLastError(ERROR_INVALID_INDEX);
+        return LB_ERR;
+    }
     if (HAS_STRINGS(descr))
     {
-        if (!lParam)
-            return strlenW(descr->items[index].str);
+        if (!buffer)
+        {
+            DWORD len = strlenW(descr->items[index].str);
+            if( unicode )
+                return len;
+            return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
+                                        NULL, 0, NULL, NULL );
+        }
 
 	TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
 
         if(unicode)
         {
-            LPWSTR buffer = (LPWSTR)lParam;
             strcpyW( buffer, descr->items[index].str );
             return strlenW(buffer);
         }
         else
         {
-            LPSTR buffer = (LPSTR)lParam;
-            return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
+            return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
         }
     } else {
-        if (lParam)
-            *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
+        if (buffer)
+            *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
         return sizeof(DWORD);
     }
 }
 
+static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
+{
+    INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
+    if (ret == CSTR_LESS_THAN)
+        return -1;
+    if (ret == CSTR_EQUAL)
+        return 0;
+    if (ret == CSTR_GREATER_THAN)
+        return 1;
+    return -1;
+}
 
 /***********************************************************************
  *           LISTBOX_FindStringPos
@@ -835,8 +868,7 @@
  * Find the nearest string located before a given string in sort order.
  * If 'exact' is TRUE, return an error if we don't get an exact match.
  */
-static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
-                                    BOOL exact )
+static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
 {
     INT index, min, max, res = -1;
 
@@ -847,15 +879,15 @@
     {
         index = (min + max) / 2;
         if (HAS_STRINGS(descr))
-            res = lstrcmpiW( str, descr->items[index].str);
+            res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
         else
         {
             COMPAREITEMSTRUCT cis;
-            UINT id = GetWindowLongA( hwnd, GWL_ID );
+            UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
 
             cis.CtlType    = ODT_LISTBOX;
             cis.CtlID      = id;
-            cis.hwndItem   = hwnd;
+            cis.hwndItem   = descr->self;
             /* note that some application (MetaStock) expects the second item
              * to be in the listbox */
             cis.itemID1    = -1;
@@ -872,19 +904,18 @@
     return exact ? -1 : max;
 }
 
-
 /***********************************************************************
  *           LISTBOX_FindFileStrPos
  *
  * Find the nearest string located before a given string in directory
  * sort order (i.e. first files, then directories, then drives).
  */
-static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
+static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
 {
     INT min, max, res = -1;
 
     if (!HAS_STRINGS(descr))
-        return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
+        return LISTBOX_FindStringPos( descr, str, FALSE );
     min = 0;
     max = descr->nb_items;
     while (min != max)
@@ -902,13 +933,13 @@
             else  /* directory */
             {
                 if (str[1] == '-') res = 1;
-                else res = lstrcmpiW( str, p );
+                else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
             }
         }
         else  /* filename */
         {
             if (*str == '[') res = 1;
-            else res = lstrcmpiW( str, p );
+            else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
         }
         if (!res) return index;
         if (res < 0) max = index;
@@ -923,8 +954,7 @@
  *
  * Find the item beginning with a given string.
  */
-static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
-                                 LPCWSTR str, BOOL exact )
+static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
 {
     INT i;
     LB_ITEMDATA *item;
@@ -937,9 +967,9 @@
         if (exact)
         {
             for (i = start + 1; i < descr->nb_items; i++, item++)
-                if (!lstrcmpiW( str, item->str )) return i;
+                if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
             for (i = 0, item = descr->items; i <= start; i++, item++)
-                if (!lstrcmpiW( str, item->str )) return i;
+                if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
         }
         else
         {
@@ -970,13 +1000,13 @@
     {
         if (exact && (descr->style & LBS_SORT))
             /* If sorted, use a WM_COMPAREITEM binary search */
-            return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
+            return LISTBOX_FindStringPos( descr, str, TRUE );
 
         /* Otherwise use a linear search */
         for (i = start + 1; i < descr->nb_items; i++, item++)
-            if (item->data == (DWORD)str) return i;
+            if (item->data == (ULONG_PTR)str) return i;
         for (i = 0, item = descr->items; i <= start; i++, item++)
-            if (item->data == (DWORD)str) return i;
+            if (item->data == (ULONG_PTR)str) return i;
     }
     return LB_ERR;
 }
@@ -990,7 +1020,9 @@
     INT i, count;
     LB_ITEMDATA *item = descr->items;
 
-    if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
+    if (!(descr->style & LBS_MULTIPLESEL) ||
+        (descr->style & LBS_NOSEL))
+      return LB_ERR;
     for (i = count = 0; i < descr->nb_items; i++, item++)
         if (item->selected) count++;
     return count;
@@ -1032,7 +1064,7 @@
 /***********************************************************************
  *           LISTBOX_Paint
  */
-static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
+static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
 {
     INT i, col_pos = descr->page_size - 1;
     RECT rect;
@@ -1053,16 +1085,18 @@
 
     if (descr->font) oldFont = SelectObject( hdc, descr->font );
     hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
-			   	   (WPARAM)hdc, (LPARAM)hwnd );
+			   	   (WPARAM)hdc, (LPARAM)descr->self );
     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
-    if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
+    if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
 
     if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
         (descr->in_focus))
     {
         /* Special case for empty listbox: paint focus rect */
         rect.bottom = rect.top + descr->item_height;
-        LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
+        ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
+                     &rect, NULL, 0, NULL );
+        LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item,
                            ODA_FOCUS, FALSE );
         rect.top = rect.bottom;
     }
@@ -1085,7 +1119,7 @@
 	    focusRect.top = rect.top;
 	    focusRect.bottom = rect.bottom;
         }
-        LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
+        LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
         rect.top = rect.bottom;
 
         if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
@@ -1115,8 +1149,9 @@
     }
 
     /* Paint the focus item now */
-    if (focusRect.top != focusRect.bottom && descr->caret_on)
-        LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
+    if (focusRect.top != focusRect.bottom &&
+        descr->caret_on && descr->in_focus)
+        LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
 
     if (!IS_OWNERDRAW(descr))
     {
@@ -1149,7 +1184,7 @@
  * Invalidate all items from a given item. If the specified item is not
  * visible, nothing happens.
  */
-static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
+static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
 {
     RECT rect;
 
@@ -1161,24 +1196,24 @@
             return;
         }
         rect.bottom = descr->height;
-        InvalidateRect( hwnd, &rect, TRUE );
+        InvalidateRect( descr->self, &rect, TRUE );
         if (descr->style & LBS_MULTICOLUMN)
         {
             /* Repaint the other columns */
             rect.left  = rect.right;
             rect.right = descr->width;
             rect.top   = 0;
-            InvalidateRect( hwnd, &rect, TRUE );
+            InvalidateRect( descr->self, &rect, TRUE );
         }
     }
 }
 
-static void LISTBOX_InvalidateItemRect( HWND hwnd, LB_DESCR *descr, INT index )
+static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
 {
     RECT rect;
 
     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
-        InvalidateRect( hwnd, &rect, TRUE );
+        InvalidateRect( descr->self, &rect, TRUE );
 }
 
 /***********************************************************************
@@ -1188,7 +1223,11 @@
 {
     if (descr->style & LBS_OWNERDRAWVARIABLE)
     {
-        if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
+        if ((index < 0) || (index >= descr->nb_items))
+        {
+            SetLastError(ERROR_INVALID_INDEX);
+            return LB_ERR;
+        }
         return descr->items[index].height;
     }
     else return descr->item_height;
@@ -1198,28 +1237,32 @@
 /***********************************************************************
  *           LISTBOX_SetItemHeight
  */
-static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
+static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index,
                                       INT height, BOOL repaint )
 {
     if (!height) height = 1;
 
     if (descr->style & LBS_OWNERDRAWVARIABLE)
     {
-        if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
-        TRACE("[%p]: item %d height = %d\n", hwnd, index, height );
+        if ((index < 0) || (index >= descr->nb_items))
+        {
+            SetLastError(ERROR_INVALID_INDEX);
+            return LB_ERR;
+        }
+        TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
         descr->items[index].height = height;
-        LISTBOX_UpdateScroll( hwnd, descr );
+        LISTBOX_UpdateScroll( descr );
 	if (repaint)
-	    LISTBOX_InvalidateItems( hwnd, descr, index );
+	    LISTBOX_InvalidateItems( descr, index );
     }
     else if (height != descr->item_height)
     {
-        TRACE("[%p]: new height = %d\n", hwnd, height );
+        TRACE("[%p]: new height = %d\n", descr->self, height );
         descr->item_height = height;
-        LISTBOX_UpdatePage( hwnd, descr );
-        LISTBOX_UpdateScroll( hwnd, descr );
+        LISTBOX_UpdatePage( descr );
+        LISTBOX_UpdateScroll( descr );
 	if (repaint)
-	    InvalidateRect( hwnd, 0, TRUE );
+	    InvalidateRect( descr->self, 0, TRUE );
     }
     return LB_OKAY;
 }
@@ -1228,7 +1271,7 @@
 /***********************************************************************
  *           LISTBOX_SetHorizontalPos
  */
-static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
+static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
 {
     INT diff;
 
@@ -1236,39 +1279,39 @@
         pos = descr->horz_extent - descr->width;
     if (pos < 0) pos = 0;
     if (!(diff = descr->horz_pos - pos)) return;
-    TRACE("[%p]: new horz pos = %d\n", hwnd, pos );
+    TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
     descr->horz_pos = pos;
-    LISTBOX_UpdateScroll( hwnd, descr );
+    LISTBOX_UpdateScroll( descr );
     if (abs(diff) < descr->width)
     {
         RECT rect;
         /* Invalidate the focused item so it will be repainted correctly */
         if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
-            InvalidateRect( hwnd, &rect, TRUE );
-        ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
+            InvalidateRect( descr->self, &rect, TRUE );
+        ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
                           SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
     }
     else
-        InvalidateRect( hwnd, NULL, TRUE );
+        InvalidateRect( descr->self, NULL, TRUE );
 }
 
 
 /***********************************************************************
  *           LISTBOX_SetHorizontalExtent
  */
-static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
+static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr,
                                             INT extent )
 {
     if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
         return LB_OKAY;
     if (extent <= 0) extent = 1;
     if (extent == descr->horz_extent) return LB_OKAY;
-    TRACE("[%p]: new horz extent = %d\n", hwnd, extent );
+    TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
     descr->horz_extent = extent;
     if (descr->horz_pos > extent - descr->width)
-        LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
+        LISTBOX_SetHorizontalPos( descr, extent - descr->width );
     else
-        LISTBOX_UpdateScroll( hwnd, descr );
+        LISTBOX_UpdateScroll( descr );
     return LB_OKAY;
 }
 
@@ -1276,12 +1319,12 @@
 /***********************************************************************
  *           LISTBOX_SetColumnWidth
  */
-static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
+static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
 {
     if (width == descr->column_width) return LB_OKAY;
-    TRACE("[%p]: new column width = %d\n", hwnd, width );
+    TRACE("[%p]: new column width = %d\n", descr->self, width );
     descr->column_width = width;
-    LISTBOX_UpdatePage( hwnd, descr );
+    LISTBOX_UpdatePage( descr );
     return LB_OKAY;
 }
 
@@ -1291,26 +1334,29 @@
  *
  * Returns the item height.
  */
-static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
+static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
 {
     HDC hdc;
     HFONT oldFont = 0;
-    TEXTMETRICW tm;
+    const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    SIZE sz;
 
     descr->font = font;
 
-    if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
+    if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
     {
         ERR("unable to get DC.\n" );
         return 16;
     }
     if (font) oldFont = SelectObject( hdc, font );
-    GetTextMetricsW( hdc, &tm );
+    GetTextExtentPointA( hdc, alphabet, 52, &sz);
     if (oldFont) SelectObject( hdc, oldFont );
-    ReleaseDC( hwnd, hdc );
+    ReleaseDC( descr->self, hdc );
+
+    descr->avg_char_width = (sz.cx / 26 + 1) / 2;
     if (!IS_OWNERDRAW(descr))
-        LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
-    return tm.tmHeight ;
+        LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
+    return sz.cy;
 }
 
 
@@ -1319,7 +1365,7 @@
  *
  * Make sure that a given item is partially or fully visible.
  */
-static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
+static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index,
                                      BOOL fully )
 {
     INT top;
@@ -1347,7 +1393,7 @@
             (descr->height > (descr->page_size * descr->item_height))) return;
         top = index - descr->page_size + 1;
     }
-    LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
+    LISTBOX_SetTopItem( descr, top, TRUE );
 }
 
 /***********************************************************************
@@ -1357,7 +1403,7 @@
  *   index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
  *
  */
-static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
+static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index,
                                       BOOL fully_visible )
 {
     INT oldfocus = descr->focus_item;
@@ -1367,11 +1413,11 @@
     if (index == oldfocus) return LB_OKAY;
     descr->focus_item = index;
     if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
-        LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
+        LISTBOX_RepaintItem( descr, oldfocus, ODA_FOCUS );
 
-    LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
+    LISTBOX_MakeItemVisible( descr, index, fully_visible );
     if (descr->caret_on && (descr->in_focus))
-        LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
+        LISTBOX_RepaintItem( descr, index, ODA_FOCUS );
 
     return LB_OKAY;
 }
@@ -1382,7 +1428,7 @@
  *
  * Select a range of items. Should only be used on a MULTIPLESEL listbox.
  */
-static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
+static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
                                         INT last, BOOL on )
 {
     INT i;
@@ -1395,8 +1441,6 @@
     if (last == -1) last = descr->nb_items - 1;
     if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
     if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
-    /* selected_item reflects last selected/unselected item on multiple sel */
-    descr->selected_item = last;
 
     if (on)  /* Turn selection on */
     {
@@ -1404,9 +1448,8 @@
         {
             if (descr->items[i].selected) continue;
             descr->items[i].selected = TRUE;
-            LISTBOX_InvalidateItemRect(hwnd, descr, i);
+            LISTBOX_InvalidateItemRect(descr, i);
         }
-        LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
     }
     else  /* Turn selection off */
     {
@@ -1414,7 +1457,7 @@
         {
             if (!descr->items[i].selected) continue;
             descr->items[i].selected = FALSE;
-            LISTBOX_InvalidateItemRect(hwnd, descr, i);
+            LISTBOX_InvalidateItemRect(descr, i);
         }
     }
     return LB_OKAY;
@@ -1423,19 +1466,23 @@
 /***********************************************************************
  *           LISTBOX_SetSelection
  */
-static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
+static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
                                      BOOL on, BOOL send_notify )
 {
     TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
 
-    if (descr->style & LBS_NOSEL) return LB_ERR;
+    if (descr->style & LBS_NOSEL)
+    {
+        descr->selected_item = index;
+        return LB_ERR;
+    }
     if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
     if (descr->style & LBS_MULTIPLESEL)
     {
         if (index == -1)  /* Select all items */
-            return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
+            return LISTBOX_SelectItemRange( descr, 0, -1, on );
         else  /* Only one item */
-            return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
+            return LISTBOX_SelectItemRange( descr, index, index, on );
     }
     else
     {
@@ -1444,9 +1491,9 @@
         if (oldsel != -1) descr->items[oldsel].selected = FALSE;
         if (index != -1) descr->items[index].selected = TRUE;
         descr->selected_item = index;
-        if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
-        if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
-        if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
+        if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
+        if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
+        if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
                                (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
 	else
 	    if( descr->lphc ) /* set selection change flag for parent combo */
@@ -1461,7 +1508,7 @@
  *
  * Change the caret position and extend the selection to the new caret.
[truncated at 1000 lines; 1662 more skipped]