Release 961201

Sat Nov 30 19:21:17 1996  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [configure]
	Re-generated with autoconf 2.11. Let me know if you have
	problems.

	* [controls/listbox.c] [controls/oldlbox.c]
	Listboxes rewritten from scratch. Moved old code still used by
	comboboxes to oldlbox.c

	* [misc/registry.c]
	Use temporary file when saving registry.

	* [windows/dialog.c]
	Implemented Win32 version of DlgDirList() and DlgDirListComboBox().

	* [windows/winproc.c]
	Added translation for listbox Win32 messages.

Sat Nov 30 21:00:00 Alex Korobka <alex@trantor.pharm.sunysb.edu>

	* [controls/widgets.c] [controls/button.c]
	Fixed some incompatibilities with CTL3D DLL.

	* [windows/dialog.c]
	Made dialog windows fit into the desktop.

	* [misc/winsock.c] [misc/winsock_async.c]
	New Winsock engine.

	* [windows/message.c]
	GetMessage() fixes.

	* [windows/queue.c] [windows/hook.c] [windows/win.c]
	SetMessageQueue() fixes.

Fri Nov 29 10:25:12 1996  Slaven Rezic  <eserte@cs.tu-berlin.de>

	* [objects/text.c]
	DrawText16(): Fixed return value.

Tue Nov 26 14:47:09 1996  Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>

	* [files/profile.c] [*/*]
	Added Win32 profile functions, updated to new naming standard.

	* [objects/font.c] [if1632/thunk.c] [include/windows.h]
	Added EnumFonts32*, EnumFontFamiliesEx*, changed prototypes and
	structures.

	* [misc/ole2nls.c] [if1632/thunk.c]
	Added EnumSystemLocales() (winhelp.exe).

	* [misc/registry.c]
	Added Windows 3.1 registry loader supplied by Tor Sjxwall, tor@sn.no

	* [win32/file.c]
	Partially fixed CreateFileMapping(), added UnmapViewOfFile().

Sat Nov 23 23:36:05 1996  Ronan Waide  <waider@waider.ie>

	* [misc/shell.c]
	Fixed some FIXMEs relating to ShellExec() and FindExecutable().

	* [misc/main.c]
	Implemented a few more of the SystemParametersInfo() cases.

Tue Nov 19 01:24:34 1996  Philippe De Muyter  <phdm@info.ucl.ac.be>

	* [include/keyboard.h]
	New file, new macro WINE_VKEY_MAPPINGS (using code taken from event.c).

	* [include/windows.h]
	New [VK_A, VK_Z] and [VK_0, VK9] macros.

	* [misc/keyboard.c]
	Fixes in KeyTable and ToAscii.

	* [objects/font.c]
	FONT_init : Give default value for MSWIN "system" font.
	FONT_MatchFont : Do not try every size of a font family if the
	family does not exist.

	* [windows/event.c]
	lastEventChar hack removed.
	KeyStateTable replaced by InputKeyStateTable (maintained in event.c)
	and QueueKeyStateTable (maintained in message.c).
	EVENT_key : Corrections to the extended bit setting.

	* [windows/message.c] [windows/keyboard.c]
	Implementation of a new QueueKeyStateTable : table of key states
	valid when messages are retrieved by GetMessage or PeekMessage,
	and valid for TranslateMessage.
	TranslateMessage : Convert WM*KEY messages using QueueKeyStateTable
	and ToAscii.
	
Mon Nov 18 16:59:01 1996  Robert Pouliot <krynos@clic.net>

	* [graphics/Makefile.in] [graphics/wing.c]
	  [if1632/wing.spec]
	Some functions for WinG support, mostly empty stubs.

	* [misc/crtdll.c] [if1632/crtdll.spec]
	Many functions added to CRTDLL, mostly calls to Unix C library.
diff --git a/controls/listbox.c b/controls/listbox.c
index a07645d..bad86a7 100644
--- a/controls/listbox.c
+++ b/controls/listbox.c
@@ -1,2269 +1,2393 @@
 /*
  * Listbox controls
- * 
- * Copyright  Martin Ayotte, 1993
- *            Constantine Sapuntzakis, 1995
- * 	      Alex Korobka, 1995, 1996 
- * 
+ *
+ * Copyright 1996 Alexandre Julliard
  */
 
- /*
-  * FIXME: 
-  * - proper scrolling for multicolumn style
-  * - anchor and caret for LBS_EXTENDEDSEL
-  * - proper selection with keyboard
-  * - how to handle (LBS_EXTENDEDSEL | LBS_MULTIPLESEL) style
-  * - support for LBS_NOINTEGRALHEIGHT and LBS_OWNERDRAWVARIABLE styles
-  */
-
-#include <stdio.h>
-#include <stdlib.h>
+#define NO_TRANSITION_TYPES  /* This file is Win32-clean */
 #include <string.h>
-#include <ctype.h>
+#include <stdio.h>
 #include "windows.h"
-#include "win.h"
-#include "gdi.h"
-#include "msdos.h"
-#include "listbox.h"
-#include "dos_fs.h"
 #include "drive.h"
-#include "file.h"
+#include "dos_fs.h"
+#include "msdos.h"
 #include "heap.h"
+#include "spy.h"
+#include "win.h"
 #include "stddebug.h"
 #include "debug.h"
-#include "xmalloc.h"
 
-#define LIST_HEAP_ALLOC(lphl,f,size) \
-    LOCAL_Alloc( lphl->HeapSel, LMEM_FIXED, (size) )
-#define LIST_HEAP_FREE(lphl,handle) \
-    LOCAL_Free( lphl->HeapSel, (handle) )
-#define LIST_HEAP_ADDR(lphl,handle)  \
-    ((handle) ? PTR_SEG_OFF_TO_LIN(lphl->HeapSel, (handle)) : NULL)
+/* Unimplemented yet:
+ * - LBS_NOSEL
+ * - LBS_USETABSTOPS
+ * - Unicode
+ * - Locale handling
+ * - COMBOLBOX
+ */
 
-#define LIST_HEAP_SIZE 0x10000
+/* Items array granularity */
+#define LB_ARRAY_GRANULARITY 16
 
-#define LBMM_EDGE   4    /* distance inside box which is same as moving mouse
-			    outside box, to trigger scrolling of LB */
+/* Scrolling timeout in ms */
+#define LB_SCROLL_TIMEOUT 50
 
-#define MATCH_SUBSTR            2
-#define MATCH_EXACT             1
-#define MATCH_NEAREST           0
+/* Listbox system timer id */
+#define LB_TIMER_ID  2
 
-static void ListBoxInitialize(LPHEADLIST lphl)
+/* Item structure */
+typedef struct
 {
-  lphl->lpFirst        = NULL;
-  lphl->ItemsCount     = 0;
-  lphl->ItemsVisible   = 0;
-  lphl->FirstVisible   = 0;
-  lphl->ColumnsVisible = 1;
-  lphl->ItemsPerColumn = 0;
-  lphl->ItemFocused    = -1;
-  lphl->PrevFocused    = -1;
-}
+    LPSTR     str;       /* Item text */
+    BOOL32    selected;  /* Is item selected? */
+    UINT32    height;    /* Item height (only for OWNERDRAWVARIABLE) */
+    DWORD     data;      /* User data */
+} LB_ITEMDATA;
 
-void CreateListBoxStruct(HWND hwnd, WORD CtlType, LONG styles, HWND parent)
+/* Listbox structure */
+typedef struct
 {
-  LPHEADLIST lphl;
-  HDC32         hdc;
+    HANDLE32      heap;           /* Heap for this listbox */
+    HWND32        owner;          /* Owner window */
+    UINT32        style;          /* Window style */
+    INT32         width;          /* Window width */
+    INT32         height;         /* Window height */
+    LB_ITEMDATA  *items;          /* Array of items */
+    INT32         nb_items;       /* Number of items */
+    INT32         top_item;       /* Top visible item */
+    INT32         selected_item;  /* Selected item */
+    INT32         focus_item;     /* Item that has the focus */
+    INT32         anchor_item;    /* Anchor item for extended selection */
+    INT32         item_height;    /* Default item height */
+    INT32         page_size;      /* Items per listbox page */
+    INT32         column_width;   /* Column width for multi-column listboxes */
+    INT32         horz_extent;    /* Horizontal extent (0 if no hscroll) */
+    INT32         horz_pos;       /* Horizontal position */
+    INT32         nb_tabs;        /* Number of tabs in array */
+    INT32        *tabs;           /* Array of tabs */
+    BOOL32        caret_on;       /* Is caret on? */
+    HFONT32       font;           /* Current font */
+    LCID          locale;         /* Current locale for string comparisons */
+} LB_DESCR;
 
-  lphl = (LPHEADLIST)xmalloc(sizeof(HEADLIST));
-  SetWindowLong32A(hwnd, 0, (LONG)lphl);
-  ListBoxInitialize(lphl);
-  lphl->DrawCtlType    = CtlType;
-  lphl->CtlID          = GetWindowWord(hwnd,GWW_ID);
-  lphl->bRedrawFlag    = TRUE;
-  lphl->iNumStops      = 0;
-  lphl->TabStops       = NULL;
-  lphl->hFont          = GetStockObject32(SYSTEM_FONT);
-  lphl->hSelf          = hwnd;  
-  if (CtlType==ODT_COMBOBOX)              /* use the "faked" style for COMBOLBOX */
-                                          /* LBS_SORT instead CBS_SORT e.g.      */
-    lphl->dwStyle   = MAKELONG(LOWORD(styles),HIWORD(GetWindowLong32A(hwnd,GWL_STYLE)));
-  else
-    lphl->dwStyle   = GetWindowLong32A(hwnd,GWL_STYLE); /* use original style dword */
-  lphl->hParent        = parent;
-  lphl->StdItemHeight  = 15; /* FIXME: should get the font height */
-  lphl->OwnerDrawn     = styles & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE);
-  lphl->HasStrings     = (styles & LBS_HASSTRINGS) || !lphl->OwnerDrawn;
 
-  /* create dummy hdc to set text height */
-  if ((hdc = GetDC32(0)))
-  {
-      TEXTMETRIC16 tm;
-      GetTextMetrics16( hdc, &tm );
-      lphl->StdItemHeight = tm.tmHeight;
-      dprintf_listbox(stddeb,"CreateListBoxStruct:  font height %d\n",
-                      lphl->StdItemHeight);
-      ReleaseDC32( 0, hdc );
-  }
+#define IS_OWNERDRAW(descr) \
+    ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
 
-  if (lphl->OwnerDrawn)
-  {
-    LISTSTRUCT dummyls;
-    
-    lphl->needMeasure = TRUE;
-    dummyls.mis.CtlType    = lphl->DrawCtlType;
-    dummyls.mis.CtlID      = lphl->CtlID;
-    dummyls.mis.itemID     = -1;
-    dummyls.mis.itemWidth  = 0; /* ignored */
-    dummyls.mis.itemData   = 0;
+#define HAS_STRINGS(descr) \
+    (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
 
-    ListBoxAskMeasure(lphl,&dummyls);
-  }
+#define SEND_NOTIFICATION(wnd,descr,code) \
+    (SendMessage32A( (descr)->owner, WM_COMMAND, \
+                     MAKEWPARAM( (wnd)->wIDmenu, (code) ), (wnd)->hwndSelf ))
 
-  lphl->HeapSel = GlobalAlloc16(GMEM_FIXED,LIST_HEAP_SIZE);
-  LocalInit( lphl->HeapSel, 0, LIST_HEAP_SIZE-1);
-}
-
-void DestroyListBoxStruct(LPHEADLIST lphl)
+/* Current timer status */
+typedef enum
 {
-  /* XXX need to free lphl->Heap */
-  GlobalFree16(lphl->HeapSel);
-  free(lphl);
-}
+    LB_TIMER_NONE,
+    LB_TIMER_UP,
+    LB_TIMER_LEFT,
+    LB_TIMER_DOWN,
+    LB_TIMER_RIGHT
+} TIMER_DIRECTION;
 
-static LPHEADLIST ListBoxGetStorageHeader(HWND hwnd)
+static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
+
+
+/***********************************************************************
+ *           LISTBOX_Dump
+ */
+void LISTBOX_Dump( WND *wnd )
 {
-    return (LPHEADLIST)GetWindowLong32A(hwnd,0);
-}
+    INT32 i;
+    LB_ITEMDATA *item;
+    LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
 
-/* Send notification "code" as part of a WM_COMMAND-message if hwnd
-   has the LBS_NOTIFY style */
-void ListBoxSendNotification(LPHEADLIST lphl, WORD code)
-{
-  if (lphl->dwStyle & LBS_NOTIFY)
-      SendMessage32A( lphl->hParent, WM_COMMAND,
-                      MAKEWPARAM( lphl->CtlID, code), (LPARAM)lphl->hSelf );
-}
-
-
-/* get the maximum value of lphl->FirstVisible */
-int ListMaxFirstVisible(LPHEADLIST lphl)
-{
-    int m = lphl->ItemsCount-lphl->ItemsVisible;
-    return (m < 0) ? 0 : m;
-}
-
-
-void ListBoxUpdateWindow(HWND hwnd, LPHEADLIST lphl, BOOL repaint)
-{
-  if (lphl->dwStyle & WS_VSCROLL)
-    SetScrollRange32(hwnd, SB_VERT, 0, ListMaxFirstVisible(lphl), TRUE);
-  if ((lphl->dwStyle & WS_HSCROLL) && (lphl->ItemsPerColumn != 0))
-    SetScrollRange32(hwnd, SB_HORZ, 1, lphl->ItemsVisible /
-		   lphl->ItemsPerColumn + 1, TRUE);
-
-  if (repaint && lphl->bRedrawFlag) InvalidateRect32( hwnd, NULL, TRUE );
-}
-
-/* Returns: 0 if nothing needs to be changed */
-/*          1 if FirstVisible changed */
-
-int ListBoxScrollToFocus(LPHEADLIST lphl)
-{
-  short       end;
-
-  if (lphl->ItemsCount == 0) return 0;
-  if (lphl->ItemFocused == -1) return 0;
-
-  end = lphl->FirstVisible + lphl->ItemsVisible - 1;
-
-  if (lphl->ItemFocused < lphl->FirstVisible ) {
-    lphl->FirstVisible = lphl->ItemFocused;
-    return 1;
-  } else {
-    if (lphl->ItemFocused > end) {
-      WORD maxFirstVisible = ListMaxFirstVisible(lphl);
-
-      lphl->FirstVisible = lphl->ItemFocused;
-      
-      if (lphl->FirstVisible > maxFirstVisible) {
-	lphl->FirstVisible = maxFirstVisible;
-      }
-      return 1;
+    printf( "Listbox:\n" );
+    printf( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
+            wnd->hwndSelf, (UINT32)descr, descr->heap, descr->nb_items,
+            descr->top_item );
+    for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
+    {
+        printf( "%4d: %-40s %d %08lx %3d\n",
+                i, item->str, item->selected, item->data, item->height );
     }
-  } 
-  return 0;
 }
 
 
-LPLISTSTRUCT ListBoxGetItem(LPHEADLIST lphl, UINT uIndex)
+/***********************************************************************
+ *           LISTBOX_GetCurrentPageSize
+ *
+ * Return the current page size
+ */
+static INT32 LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
 {
-  LPLISTSTRUCT lpls;
-  UINT         Count = 0;
-
-  if (uIndex >= lphl->ItemsCount) return NULL;
-
-  lpls = lphl->lpFirst;
-  while (Count++ < uIndex) lpls = lpls->lpNext;
-  return lpls;
+    INT32 i, height;
+    if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
+    for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
+    {
+        if ((height += descr->items[i].height) > descr->height) break;
+    }
+    if (i == descr->top_item) return 1;
+    else return i - descr->top_item;
 }
 
 
-void ListBoxDrawItem(HWND hwnd, LPHEADLIST lphl, HDC16 hdc, LPLISTSTRUCT lpls, 
-                     RECT16 *rect, WORD itemAction, WORD itemState)
+/***********************************************************************
+ *           LISTBOX_GetMaxTopIndex
+ *
+ * Return the maximum possible index for the top of the listbox.
+ */
+static INT32 LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
 {
-    if (lphl->OwnerDrawn)
+    INT32 max, page;
+
+    if (descr->style & LBS_OWNERDRAWVARIABLE)
+    {
+        page = descr->height;
+        for (max = descr->nb_items - 1; max >= 0; max--)
+            if ((page -= descr->items[max].height) < 0) break;
+        if (max < descr->nb_items - 1) max++;
+    }
+    else if (descr->style & LBS_MULTICOLUMN)
+    {
+        if ((page = descr->width / descr->column_width) < 1) page = 1;
+        max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
+        max = (max - page) * descr->page_size;
+    }
+    else
+    {
+        max = descr->nb_items - descr->page_size;
+    }
+    if (max < 0) max = 0;
+    return max;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_UpdateScroll
+ *
+ * Update the scrollbars. Should be called whenever the content
+ * of the listbox changes.
+ */
+static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
+{
+    SCROLLINFO info;
+
+    if (descr->style & LBS_NOREDRAW) return;
+    info.cbSize = sizeof(info);
+
+    if (descr->style & LBS_MULTICOLUMN)
+    {
+        info.nMin  = 0;
+        info.nMax  = (descr->nb_items - 1) / descr->page_size;
+        info.nPos  = descr->top_item / descr->page_size;
+        info.nPage = descr->width / descr->column_width;
+        if (info.nPage < 1) info.nPage = 1;
+        info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
+        if (descr->style & LBS_DISABLENOSCROLL)
+            info.fMask |= SIF_DISABLENOSCROLL;
+        SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
+        info.nMax = 0;
+        info.fMask = SIF_RANGE;
+        SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
+    }
+    else
+    {
+        info.nMin  = 0;
+        info.nMax  = descr->nb_items - 1;
+        info.nPos  = descr->top_item;
+        info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
+        info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
+        if (descr->style & LBS_DISABLENOSCROLL)
+            info.fMask |= SIF_DISABLENOSCROLL;
+        SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
+
+        if (descr->horz_extent)
+        {
+            info.nMin  = 0;
+            info.nMax  = descr->horz_extent - 1;
+            info.nPos  = descr->horz_pos;
+            info.nPage = descr->width;
+            info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
+            if (descr->style & LBS_DISABLENOSCROLL)
+                info.fMask |= SIF_DISABLENOSCROLL;
+            SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
+        }
+    }
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetTopItem
+ *
+ * Set the top item of the listbox, scrolling up or down if necessary.
+ */
+static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT32 index,
+                                   BOOL32 scroll )
+{
+    INT32 max = LISTBOX_GetMaxTopIndex( wnd, descr );
+    if (index > max) index = max;
+    if (index < 0) index = 0;
+    if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
+    if (descr->top_item == index) return LB_OKAY;
+    if (descr->style & LBS_MULTICOLUMN)
+    {
+        INT32 diff = (descr->top_item - index) / descr->page_size * descr->column_width;
+        if (scroll && (abs(diff) < descr->width))
+            ScrollWindow32( wnd->hwndSelf, diff, 0, NULL, NULL );
+        else
+            scroll = FALSE;
+    }
+    else if (scroll)
+    {
+        INT32 diff;
+        if (descr->style & LBS_OWNERDRAWVARIABLE)
+        {
+            INT32 i;
+            diff = 0;
+            if (index > descr->top_item)
+            {
+                for (i = index - 1; i >= descr->top_item; i--)
+                    diff -= descr->items[i].height;
+            }
+            else
+            {
+                for (i = index; i < descr->top_item; i++)
+                    diff += descr->items[i].height;
+            }
+        }
+        else 
+            diff = (descr->top_item - index) * descr->item_height;
+
+        if (abs(diff) < descr->height)
+            ScrollWindow32( wnd->hwndSelf, 0, diff, NULL, NULL );
+        else
+            scroll = FALSE;
+    }
+    if (!scroll) InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
+    descr->top_item = index;
+    LISTBOX_UpdateScroll( wnd, descr );
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_UpdatePage
+ *
+ * Update the page size. Should be called when the size of
+ * the client area or the item height changes.
+ */
+static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
+{
+    INT32 page_size;
+
+    if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
+    if (page_size == descr->page_size) return;
+    descr->page_size = page_size;
+    if (descr->style & LBS_MULTICOLUMN)
+        InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
+    LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
+}
+
+
+/***********************************************************************
+ *           LISTBOX_UpdateSize
+ *
+ * Update the size of the listbox. Should be called when the size of
+ * the client area changes.
+ */
+static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
+{
+    RECT32 rect;
+
+    GetClientRect32( wnd->hwndSelf, &rect );
+    descr->width  = rect.right - rect.left;
+    descr->height = rect.bottom - rect.top;
+    if (!(descr->style & LBS_NOINTEGRALHEIGHT))
+    {
+        if ((descr->height > descr->item_height) &&
+            (descr->height % descr->item_height))
+        {
+            dprintf_listbox(stddeb, "Listbox %04x: changing height %d -> %d\n",
+                            wnd->hwndSelf, descr->height,
+                            descr->height - descr->height%descr->item_height );
+            SetWindowPos( wnd->hwndSelf, 0, 0, 0,
+                          wnd->rectWindow.right - wnd->rectWindow.left,
+                          wnd->rectWindow.bottom - wnd->rectWindow.top -
+                              (descr->height % descr->item_height),
+                          SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
+            return;
+        }
+    }
+    dprintf_listbox( stddeb, "Listbox %04x: new size = %d,%d\n",
+                     wnd->hwndSelf, descr->width, descr->height );
+    LISTBOX_UpdatePage( wnd, descr );
+    LISTBOX_UpdateScroll( wnd, descr );
+}
+
+
+/***********************************************************************
+ *           LISTBOX_GetItemRect
+ *
+ * Get the rectangle enclosing an item, in listbox client coordinates.
+ * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
+ */
+static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT32 index,
+                                    RECT32 *rect )
+{
+    /* Index <= 0 is legal even on empty listboxes */
+    if (index && (index >= descr->nb_items)) return -1;
+    SetRect32( rect, 0, 0, descr->width, descr->height );
+    if (descr->style & LBS_MULTICOLUMN)
+    {
+        INT32 col = (index / descr->page_size) -
+                        (descr->top_item / descr->page_size);
+        rect->left += col * descr->column_width;
+        rect->right = rect->left + descr->column_width;
+        rect->top += (index % descr->page_size) * descr->item_height;
+        rect->bottom = rect->top + descr->item_height;
+    }
+    else if (descr->style & LBS_OWNERDRAWVARIABLE)
+    {
+        INT32 i;
+        if (index < descr->top_item)
+        {
+            for (i = descr->top_item-1; i >= index; i--)
+                rect->top -= descr->items[i].height;
+        }
+        else
+        {
+            for (i = descr->top_item; i < index; i++)
+                rect->top += descr->items[i].height;
+        }
+        rect->bottom = rect->top + descr->items[index].height;
+        rect->right += descr->horz_pos;
+    }
+    else
+    {
+        rect->top += (index - descr->top_item) * descr->item_height;
+        rect->bottom = rect->top + descr->item_height;
+        rect->right += descr->horz_pos;
+    }
+
+    return ((rect->left < descr->width) && (rect->right > 0) &&
+            (rect->top < descr->height) && (rect->bottom > 0));
+}
+
+
+/***********************************************************************
+ *           LISTBOX_GetItemFromPoint
+ *
+ * Return the item nearest from point (x,y) (in client coordinates).
+ */
+static INT32 LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
+                                       INT32 x, INT32 y )
+{
+    INT32 index = descr->top_item;
+
+    if (!descr->nb_items) return -1;  /* No items */
+    if (descr->style & LBS_OWNERDRAWVARIABLE)
+    {
+        INT32 pos = 0;
+        if (y >= 0)
+        {
+            while (index < descr->nb_items)
+            {
+                if ((pos += descr->items[index].height) > y) break;
+                index++;
+            }
+        }
+        else
+        {
+            while (index > 0)
+            {
+                index--;
+                if ((pos -= descr->items[index].height) <= y) break;
+            }
+        }
+    }
+    else if (descr->style & LBS_MULTICOLUMN)
+    {
+        if (y >= descr->item_height * descr->page_size) return -1;
+        if (y >= 0) index += y / descr->item_height;
+        if (x >= 0) index += (x / descr->column_width) * descr->page_size;
+        else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
+    }
+    else
+    {
+        index += (y / descr->item_height);
+    }
+    if (index < 0) return 0;
+    if (index >= descr->nb_items) return -1;
+    return index;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_PaintItem
+ *
+ * Paint an item.
+ */
+static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC32 hdc,
+                               const RECT32 *rect, INT32 index, UINT32 action )
+{
+    LB_ITEMDATA *item = NULL;
+    if (index < descr->nb_items) item = &descr->items[index];
+
+    if (IS_OWNERDRAW(descr))
     {
         DRAWITEMSTRUCT32 dis;
-
-        dis.CtlID      = lpls->mis.CtlID;
-        dis.CtlType    = lpls->mis.CtlType;
-        dis.itemID     = lpls->mis.itemID;
-        dis.hDC        = hdc;
-        dis.hwndItem   = hwnd;
-        dis.itemData   = lpls->mis.itemData;
-        dis.itemAction = itemAction;
-        dis.itemState  = itemState;
-        CONV_RECT16TO32( rect, &dis.rcItem );
-        SendMessage32A( lphl->hParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis );
-        return;
-    }
-    if (itemAction == ODA_DRAWENTIRE || itemAction == ODA_SELECT) {
-      int 	OldBkMode;
-      DWORD 	dwOldTextColor = 0;
-
-      OldBkMode = SetBkMode(hdc, TRANSPARENT);
-
-      if (itemState != 0) {
-	dwOldTextColor = SetTextColor(hdc, 0x00FFFFFFL);
-	FillRect16(hdc, rect, GetStockObject32(BLACK_BRUSH));
-      }
-
-      if (lphl->dwStyle & LBS_USETABSTOPS) {
-	TabbedTextOut(hdc, rect->left + 5, rect->top + 2, 
-		      (char *)lpls->itemText, strlen((char *)lpls->itemText), 
-		      lphl->iNumStops, lphl->TabStops, 0);
-      } else {
-	TextOut16(hdc, rect->left + 5, rect->top + 2,
-                  (char *)lpls->itemText, strlen((char *)lpls->itemText));
-      }
-
-      if (itemState != 0) {
-	SetTextColor(hdc, dwOldTextColor);
-      }
-      
-      SetBkMode(hdc, OldBkMode);
-    }
-    else DrawFocusRect16(hdc, rect);
-}
-
-
-int ListBoxFindMouse(LPHEADLIST lphl, int X, int Y)
-{
-  LPLISTSTRUCT lpls = lphl->lpFirst;
-  int          i, j;
-  POINT16      point;
-  
-  point.x = X; point.y = Y;
-  if (lphl->ItemsCount == 0) return LB_ERR;
-
-  for(i = 0; i < lphl->FirstVisible; i++) {
-    if (lpls == NULL) return LB_ERR;
-    lpls = lpls->lpNext;
-  }
-  for(j = 0; j < lphl->ItemsVisible; i++, j++) {
-    if (lpls == NULL) return LB_ERR;
-    if (PtInRect16(&lpls->itemRect,point)) {
-      return i;
-    }
-    lpls = lpls->lpNext;
-  }
-  dprintf_listbox(stddeb,"ListBoxFindMouse: not found\n");
-  return LB_ERR;
-}
-
-BOOL32 lbDeleteItemNotify(LPHEADLIST lphl, LPLISTSTRUCT lpls)
-{
-    /* called only for owner drawn listboxes */
-    BOOL32 ret;
-    DELETEITEMSTRUCT16 *delItem = SEGPTR_NEW(DELETEITEMSTRUCT16);
-    if (!delItem) return FALSE;
-
-    delItem->CtlType  = lphl->DrawCtlType;
-    delItem->CtlID    = lphl->CtlID;
-    delItem->itemID   = lpls->mis.itemID;
-    delItem->hwndItem = lphl->hSelf;
-    delItem->itemData = lpls->mis.itemData;
-
-    ret = SendMessage16( lphl->hParent, WM_DELETEITEM, (WPARAM16)lphl->CtlID,
-                         (LPARAM)SEGPTR_GET(delItem) );
-    SEGPTR_FREE(delItem);
-    return ret;
-}
-
-void ListBoxAskMeasure(LPHEADLIST lphl, LPLISTSTRUCT lpls)  
-{
-    MEASUREITEMSTRUCT16 *lpmeasure = SEGPTR_NEW(MEASUREITEMSTRUCT16);
-    if (!lpmeasure) return;
-    *lpmeasure = lpls->mis;
-    lpmeasure->itemHeight = lphl->StdItemHeight;
-    SendMessage16( lphl->hParent, WM_MEASUREITEM, lphl->CtlID,
-                   (LPARAM)SEGPTR_GET(lpmeasure) );
-
-    if (lphl->dwStyle & LBS_OWNERDRAWFIXED)
-    {
-        if (lpmeasure->itemHeight > lphl->StdItemHeight)
-            lphl->StdItemHeight = lpmeasure->itemHeight;
-        lpls->mis.itemHeight = lpmeasure->itemHeight;
-    }
-    SEGPTR_FREE(lpmeasure);
-}
-
-/* -------------------- strings and item data ---------------------- */
-
-LPLISTSTRUCT ListBoxCreateItem(LPHEADLIST lphl, int id)
-{
-  LPLISTSTRUCT lplsnew = (LPLISTSTRUCT)malloc(sizeof(LISTSTRUCT));
-
-  if (lplsnew == NULL) return NULL;
-  
-  lplsnew->itemState      = 0;
-  lplsnew->mis.CtlType    = lphl->DrawCtlType;
-  lplsnew->mis.CtlID      = lphl->CtlID;
-  lplsnew->mis.itemID     = id;
-  lplsnew->mis.itemHeight = lphl->StdItemHeight;
-  lplsnew->mis.itemWidth  = 0; /* ignored */
-  lplsnew->mis.itemData   = 0;
-  SetRectEmpty16( &lplsnew->itemRect );
-
-  return lplsnew;
-}
-
-int ListBoxAskCompare(LPHEADLIST lphl, int startItem, SEGPTR matchData, BOOL exactMatch )
-{
- /*  Do binary search for sorted listboxes. Linked list item storage sort of 
-  *  defeats the purpose ( forces to traverse item list all the time ) but M$ does it this way...
-  *
-  *  MATCH_NEAREST (0) - return position for insertion - for all styles
-  *  MATCH_EXACT   (1) - search for an item, return index or LB_ERR 
-  *  MATCH_SUBSTR  (2) - same as exact match but with strncmp for string comparision
-  */
-
- COMPAREITEMSTRUCT16   *itemCmp;
- LPLISTSTRUCT		currentItem = NULL;
- LPCSTR			matchStr = (lphl->HasStrings)?(LPCSTR)PTR_SEG_TO_LIN(matchData):NULL;
- int                    head, pos = -1, tail, loop = 1;
- short                  b = 0, s_length = 0;
-
- /* check if empty */
-
- if( !lphl->ItemsCount )
-    return (exactMatch)? LB_ERR: 0;
-
- /* set up variables */
-
- if( exactMatch == MATCH_NEAREST )
-     startItem = 0;
- else if( ++startItem ) 
-   {
-     loop = 2;
-     if( startItem >= lphl->ItemsCount ) startItem = lphl->ItemsCount - 1;
-   }
-
- if( exactMatch == MATCH_SUBSTR && lphl->HasStrings )
-   {
-     s_length = strlen( matchStr );
-     if( !s_length ) return 0; 		        /* head of the list - empty string */
-   }
-
- head = startItem; tail = lphl->ItemsCount - 1;
-
- dprintf_listbox(stddeb,"AskCompare: head = %i, tail = %i, data = %08x\n", head, tail, (unsigned)matchData );
-
- if (!(itemCmp = SEGPTR_NEW(COMPAREITEMSTRUCT16))) return 0;
- itemCmp->CtlType        = lphl->DrawCtlType;
- itemCmp->CtlID          = lphl->CtlID;
- itemCmp->hwndItem       = lphl->hSelf;
-
- /* search from startItem */
-
- while ( loop-- )
-  {
-    while( head <= tail )
-     {
-       pos = (tail + head)/2;
-       currentItem = ListBoxGetItem( lphl, pos );
-
-       if( lphl->HasStrings )
-	 {
-           b = ( s_length )? lstrncmpi32A( currentItem->itemText, matchStr, s_length)
-                           : lstrcmpi32A( currentItem->itemText, matchStr);
-	 }
-       else
-         {
-           itemCmp->itemID1      = pos;
-           itemCmp->itemData1    = currentItem->mis.itemData;
-           itemCmp->itemID2      = -1;
-           itemCmp->itemData2    = matchData;
-
-           b = SendMessage16( lphl->hParent, WM_COMPAREITEM,
-                              (WPARAM16)lphl->CtlID,
-                              (LPARAM)SEGPTR_GET(itemCmp) );
-         }
-
-       if( b == 0 )
-       {
-           SEGPTR_FREE(itemCmp);
-           return pos;  /* found exact match */
-       }
-       else
-         if( b < 0 ) head = ++pos;
-         else
-           if( b > 0 ) tail = pos - 1;
-     }
-
-    /* reset to search from the first item */
-    head = 0; tail = startItem - 1;
-  }
-
- dprintf_listbox(stddeb,"\t-> pos = %i\n", pos );
- SEGPTR_FREE(itemCmp);
-
- /* if we got here match is not exact */
-
- if( pos < 0 ) pos = 0;
- else if( pos > lphl->ItemsCount ) pos = lphl->ItemsCount;
-
- return (exactMatch)? LB_ERR: pos;
-}
-
-int ListBoxInsertString(LPHEADLIST lphl, UINT uIndex, LPCSTR newstr)
-{
-  LPLISTSTRUCT *lppls, lplsnew, lpls;
-  HANDLE16 hStr;
-  LPSTR	str;
-  UINT	Count;
-    
-  dprintf_listbox(stddeb,"ListBoxInsertString(%d, %p);\n", uIndex, newstr);
-    
-  if (!newstr) return -1;
-
-  if (uIndex == (UINT)-1)
-    uIndex = lphl->ItemsCount;
-
-  lppls = &lphl->lpFirst;
-  for(Count = 0; Count < uIndex; Count++) {
-    if (*lppls == NULL) return LB_ERR;
-    lppls = (LPLISTSTRUCT *) &(*lppls)->lpNext;
-  }
-    
-  lplsnew = ListBoxCreateItem(lphl, Count);
-  
-  if (lplsnew == NULL) {
-    fprintf(stdnimp,"ListBoxInsertString() out of memory !\n");
-    return LB_ERRSPACE;
-  }
-
-  lplsnew->lpNext = *lppls;
-  *lppls = lplsnew;
-  lphl->ItemsCount++;
-  
-  hStr = 0;
-  if (lphl->HasStrings) {
-    dprintf_listbox(stddeb,"  string: %s\n", newstr);
-    hStr = LIST_HEAP_ALLOC(lphl, LMEM_MOVEABLE, strlen(newstr) + 1);
-    str = (LPSTR)LIST_HEAP_ADDR(lphl, hStr);
-    if (str == NULL) return LB_ERRSPACE;
-    strcpy(str, newstr); 
-    lplsnew->itemText = str;
-    /* I'm not so sure about the next one */
-    lplsnew->mis.itemData = 0;
-  } else {
-    lplsnew->itemText = NULL;
-    lplsnew->mis.itemData = (DWORD)newstr;
-  }
-
-  lplsnew->mis.itemID = uIndex;
-  lplsnew->hData = hStr;
-  
-  /* adjust the itemID field of the following entries */
-  for(lpls = lplsnew->lpNext; lpls != NULL; lpls = lpls->lpNext) {
-      lpls->mis.itemID++;
-  }
- 
-  if (lphl->needMeasure) {
-    ListBoxAskMeasure(lphl, lplsnew);
-  }
-
-  dprintf_listbox(stddeb,"ListBoxInsertString // count=%d\n", lphl->ItemsCount);
-  return uIndex;
-}
-
-
-int ListBoxAddString(LPHEADLIST lphl, SEGPTR itemData)
-{
-    UINT 	pos = (UINT) -1;
-    LPCSTR	newstr = (lphl->HasStrings)?(LPCSTR)PTR_SEG_TO_LIN(itemData):(LPCSTR)itemData;
-
-    if ( lphl->dwStyle & LBS_SORT ) 
-	 pos = ListBoxAskCompare( lphl, -1, itemData, MATCH_NEAREST );
-
-    return ListBoxInsertString(lphl, pos, newstr);
-}
-
-
-int ListBoxGetText(LPHEADLIST lphl, UINT uIndex, LPSTR OutStr)
-{
-  LPLISTSTRUCT lpls;
-
-  if (!OutStr) {
-    dprintf_listbox(stddeb, "ListBoxGetText // OutStr==NULL\n");
-    return 0;
-  }
-  *OutStr = '\0';
-  lpls = ListBoxGetItem (lphl, uIndex);
-  if (lpls == NULL) return LB_ERR;
-
-  if (!lphl->HasStrings) {
-    *((long *)OutStr) = lpls->mis.itemData;
-    return 4;
-  }
-	
-  strcpy(OutStr, lpls->itemText);
-  return strlen(OutStr);
-}
-
-
-DWORD ListBoxGetItemData(LPHEADLIST lphl, UINT uIndex)
-{
-  LPLISTSTRUCT lpls;
-
-  lpls = ListBoxGetItem (lphl, uIndex);
-  if (lpls == NULL) return LB_ERR;
-  return lpls->mis.itemData;
-}
-
-
-int ListBoxSetItemData(LPHEADLIST lphl, UINT uIndex, DWORD ItemData)
-{
-  LPLISTSTRUCT lpls = ListBoxGetItem(lphl, uIndex);
-
-  if (lpls == NULL) return LB_ERR;
-  lpls->mis.itemData = ItemData;
-  return 1;
-}
-
-
-int ListBoxDeleteString(LPHEADLIST lphl, UINT uIndex)
-{
-  LPLISTSTRUCT lpls, lpls2;
-  UINT	Count;
-
-  if (uIndex >= lphl->ItemsCount) return LB_ERR;
-
-  lpls = lphl->lpFirst;
-  if (lpls == NULL) return LB_ERR;
-
-  if (uIndex == 0)
-  {
-    if( lphl->OwnerDrawn )
-        lbDeleteItemNotify( lphl, lpls);
-    lphl->lpFirst = lpls->lpNext;
-  }
-  else 
-  {
-    LPLISTSTRUCT lpls2 = NULL;
-    for(Count = 0; Count < uIndex; Count++) {
-      if (lpls->lpNext == NULL) return LB_ERR;
-
-      lpls2 = lpls;
-      lpls = (LPLISTSTRUCT)lpls->lpNext;
-    }
-    if( lphl->OwnerDrawn )
-	lbDeleteItemNotify( lphl, lpls);
-    lpls2->lpNext = lpls->lpNext;
-  }
-
-  /* adjust the itemID field of the following entries */
-  for(lpls2 = lpls->lpNext; lpls2 != NULL; lpls2 = lpls2->lpNext) {
-      lpls2->mis.itemID--;
-  }
- 
-  lphl->ItemsCount--;
-
-  if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData);
-  free(lpls);
-  
-  return lphl->ItemsCount;
-}
-
-int lbFindString(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr, BOOL match)
-{
-  /*  match is either MATCH_SUBSTR or MATCH_EXACT */
-
-  LPLISTSTRUCT lpls;
-  UINT	       Count;
-  UINT         First      = nFirst + 1;
-  int	       s_length   = 0;
-  LPSTR        lpMatchStr = (LPSTR)MatchStr;
-
-  if (First > lphl->ItemsCount) return LB_ERR;
-
-  if (lphl->dwStyle & LBS_SORT )
-      return ListBoxAskCompare( lphl, nFirst, MatchStr, match );
-
-  if (lphl->HasStrings ) 
-  {
-    lpMatchStr = PTR_SEG_TO_LIN(MatchStr);
-
-    if( match == MATCH_SUBSTR )
-    {
-      s_length = strlen(lpMatchStr);
-      if( !s_length ) return (lphl->ItemsCount)?0:LB_ERR;
-    }
-  }
-
-  lpls = ListBoxGetItem(lphl, First);
-  Count = 0;
-  while(lpls != NULL) 
-  {
-    if (lphl->HasStrings) 
-    {
-      if ( ( s_length )? !lstrncmpi32A(lpls->itemText, lpMatchStr, s_length)
-                       : !lstrcmpi32A(lpls->itemText, lpMatchStr)  ) return Count;
+        dis.CtlType      = ODT_LISTBOX;
+        dis.CtlID        = wnd->wIDmenu;
+        dis.hwndItem     = wnd->hwndSelf;
+        dis.itemAction   = action;
+        dis.hDC          = hdc;
+        dis.itemID       = index;
+        dis.itemState    = 0;
+        if (item && item->selected) dis.itemState |= ODS_SELECTED;
+        if ((descr->focus_item == index) &&
+            (descr->caret_on) &&
+            (GetFocus32() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
+        if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
+        dis.itemData     = item ? item->data : 0;
+        dis.rcItem       = *rect;
+        dprintf_listbox( stddeb, "Listbox %04x: drawitem %d (%s) action=%02x "
+                         "state=%02x rect=%d,%d-%d,%d\n",
+                         wnd->hwndSelf, index, item ? item->str : "", action,
+                         dis.itemState, rect->left, rect->top,
+                         rect->right, rect->bottom );
+        SendMessage32A(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
     }
     else
-      if ( lpls->mis.itemData == (DWORD)lpMatchStr ) return Count;
-
-    lpls = lpls->lpNext;
-    Count++;
-  }
-
-  /* Start over at top */
-  Count = 0;
-  lpls = lphl->lpFirst;
-
-  while (Count < First) 
-  {
-    if (lphl->HasStrings) 
     {
-      if ( ( s_length )? !lstrncmpi32A(lpls->itemText, lpMatchStr, s_length)
-                       : !lstrcmpi32A(lpls->itemText, lpMatchStr)  ) return Count;
+        if (action == ODA_FOCUS)
+        {
+            DrawFocusRect32( hdc, rect );
+            return;
+        }
+        if (item && item->selected)
+        {
+            SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
+            SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
+        }
+        else
+        {
+            SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
+            if (wnd->dwStyle & WS_DISABLED)
+                SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
+            else
+                SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );
+        }
+        dprintf_listbox( stddeb, "Listbox %04x: painting %d (%s) action=%02x"
+                         "rect=%d,%d-%d,%d\n",
+                         wnd->hwndSelf, index, item ? item->str : "", action,
+                         rect->left, rect->top, rect->right, rect->bottom );
+        /* FIXME: check LBS_USETABSTOPS style */
+        if (item)
+            ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
+                           ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
+                           strlen(item->str), NULL );
+        else
+            ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
+                           ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
+        if ((descr->focus_item == index) &&
+            (descr->caret_on) &&
+            (GetFocus32() == wnd->hwndSelf)) DrawFocusRect32( hdc, rect );
     }
-    else
-      if ( lpls->mis.itemData == (DWORD)lpMatchStr ) return Count;
-    
-    lpls = lpls->lpNext;
-    Count++;
-  }
-
-  return LB_ERR;
 }
 
-int ListBoxFindString(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr)
+
+/***********************************************************************
+ *           LISTBOX_SetRedraw
+ *
+ * Change the redraw flag.
+ */
+static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL32 on )
 {
-  return lbFindString(lphl, nFirst, MatchStr, MATCH_SUBSTR );
-}
-
-int ListBoxFindStringExact(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr)
-{
-  return lbFindString(lphl, nFirst, MatchStr, MATCH_EXACT );
-}
-
-int ListBoxResetContent(LPHEADLIST lphl)
-{
-    LPLISTSTRUCT lpls;
-    int i;
-
-    if (lphl->ItemsCount == 0) return 0;
-
-    dprintf_listbox(stddeb, "ListBoxResetContent // ItemCount = %d\n",
-	lphl->ItemsCount);
-
-    for(i = 0; i < lphl->ItemsCount; i++) {
-      lpls = lphl->lpFirst;
-      if (lpls == NULL) return LB_ERR;
-
-      if (lphl->OwnerDrawn) lbDeleteItemNotify(lphl, lpls);
-
-      lphl->lpFirst = lpls->lpNext;
-      if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData);
-      free(lpls);
+    if (on)
+    {
+        if (!(descr->style & LBS_NOREDRAW)) return;
+        descr->style &= ~LBS_NOREDRAW;
+        LISTBOX_UpdateScroll( wnd, descr );
     }
-    ListBoxInitialize(lphl);
+    else descr->style |= LBS_NOREDRAW;
+}
 
+
+/***********************************************************************
+ *           LISTBOX_RepaintItem
+ *
+ * Repaint a single item synchronously.
+ */
+static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT32 index,
+                                 UINT32 action )
+{
+    HDC32 hdc;
+    RECT32 rect;
+
+    if (descr->style & LBS_NOREDRAW) return;
+    if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
+    if ((hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
+    {
+        HFONT32 oldFont = 0;
+        if (descr->font) oldFont = SelectObject32( hdc, descr->font );
+        SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
+        LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
+        if (oldFont) SelectObject32( hdc, oldFont );
+        ReleaseDC32( wnd->hwndSelf, hdc );
+    }
+}
+
+
+/***********************************************************************
+ *           LISTBOX_InitStorage
+ */
+static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT32 nb_items,
+                                    DWORD bytes )
+{
+    LB_ITEMDATA *item;
+
+    nb_items += LB_ARRAY_GRANULARITY - 1;
+    nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
+    if (descr->items)
+        nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
+    if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
+                              nb_items * sizeof(LB_ITEMDATA) )))
+    {
+        SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
+        return LB_ERRSPACE;
+    }
+    descr->items = item;
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetTabStops
+ */
+static BOOL32 LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT32 count,
+                                   LPINT32 tabs, BOOL32 short_ints )
+{
+    if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
+    if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
+    if (!(descr->nb_tabs = count))
+    {
+        descr->tabs = NULL;
+        return TRUE;
+    }
+    /* FIXME: count = 1 */
+    if (!(descr->tabs = (INT32 *)HeapAlloc( descr->heap, 0,
+                                            descr->nb_tabs * sizeof(INT32) )))
+        return FALSE;
+    if (short_ints)
+    {
+        INT32 i;
+        LPINT16 p = (LPINT16)tabs;
+        for (i = 0; i < descr->nb_tabs; i++) descr->tabs[i] = *p++;
+    }
+    else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT32) );
+    /* FIXME: repaint the window? */
     return TRUE;
 }
 
-/* --------------------- selection ------------------------- */
 
-int ListBoxSetCurSel(LPHEADLIST lphl, WORD wIndex)
+/***********************************************************************
+ *           LISTBOX_GetText
+ */
+static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT32 index,
+                                LPSTR buffer )
 {
-  LPLISTSTRUCT lpls;
-
-  /* use ListBoxSetSel instead */
-  if (lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) ) return 0;
-
-  /* unselect previous item */
-  if (lphl->ItemFocused != -1) {
-    lphl->PrevFocused = lphl->ItemFocused;
-    lpls = ListBoxGetItem(lphl, lphl->ItemFocused);
-    if (lpls == 0) return LB_ERR;
-    lpls->itemState = 0;
-  }
-
-  if ((wIndex != (UINT)-1) && (wIndex < lphl->ItemsCount))
-  {
-    lphl->ItemFocused = wIndex;
-    lpls = ListBoxGetItem(lphl, wIndex);
-    if (lpls == 0) return LB_ERR;
-    lpls->itemState = ODS_SELECTED | ODS_FOCUS;
-
-    return 0;
-  }
-
-  return LB_ERR;
-}
-
-
-int ListBoxSetSel(LPHEADLIST lphl, WORD wIndex, WORD state)
-{
-  LPLISTSTRUCT  lpls;
-  int           n = 0;
-
-  if (!(lphl->dwStyle &  (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
-        return LB_ERR;
-
-  if (wIndex == (UINT)-1) {
-    for (lpls = lphl->lpFirst; lpls != NULL; lpls = lpls->lpNext) {
-      if( lpls->itemState & ODS_SELECTED) n++;
-      lpls->itemState = state? lpls->itemState |  ODS_SELECTED
-                             : lpls->itemState & ~ODS_SELECTED;
+    if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
+    if (HAS_STRINGS(descr))
+    {
+        lstrcpy32A( buffer, descr->items[index].str );
+        return strlen(buffer);
     }
-    return n;
-  }
-
-  if (wIndex >= lphl->ItemsCount) return LB_ERR;
-
-  lpls = ListBoxGetItem(lphl, wIndex);
-  lpls->itemState = state? lpls->itemState |  ODS_SELECTED
-                         : lpls->itemState & ~ODS_SELECTED;
-
-  return 0;
+    else
+    {
+        memcpy( buffer, &descr->items[index].data, sizeof(DWORD) );
+        return sizeof(DWORD);
+    }
 }
 
 
-int ListBoxGetSel(LPHEADLIST lphl, WORD wIndex)
+/***********************************************************************
+ *           LISTBOX_FindStringPos
+ *
+ * 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 INT32 LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
+                                    BOOL32 exact )
 {
-  LPLISTSTRUCT lpls = ListBoxGetItem(lphl, wIndex);
+    INT32 index, min, max, res = -1;
 
-  if (lpls == NULL) return LB_ERR;
-  return lpls->itemState & ODS_SELECTED;
+    if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
+    min = 0;
+    max = descr->nb_items;
+    while (min != max)
+    {
+        index = (min + max) / 2;
+        if (HAS_STRINGS(descr))
+            res = lstrcmpi32A( descr->items[index].str, str );
+        else
+        {
+            COMPAREITEMSTRUCT32 cis;
+            cis.CtlType    = ODT_LISTBOX;
+            cis.CtlID      = wnd->wIDmenu;
+            cis.hwndItem   = wnd->hwndSelf;
+            cis.itemID1    = index;
+            cis.itemData1  = descr->items[index].data;
+            cis.itemID2    = -1;
+            cis.itemData2  = (DWORD)str;
+            cis.dwLocaleId = descr->locale;
+            res = SendMessage32A( descr->owner, WM_COMPAREITEM,
+                                  wnd->wIDmenu, (LPARAM)&cis );
+        }
+        if (!res) return index;
+        if (res > 0) max = index;
+        else min = index + 1;
+    }
+    return exact ? -1 : max;
 }
 
-/* ------------------------- dir listing ------------------------ */
 
-LONG ListBoxDirectory(LPHEADLIST lphl, UINT attrib, LPCSTR filespec)
+/***********************************************************************
+ *           LISTBOX_FindFileStrPos
+ *
+ * Find the nearest string located before a given string in directory
+ * sort order (i.e. first files, then directories, then drives).
+ */
+static INT32 LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
 {
-    char 	mask[13];
-    char*	temp = NULL;
-    const char*	ptr;
-    int 	skip, count;
-    LONG 	ret;
-    DOS_DIRENT 	entry;
+    INT32 min, max, res = -1;
+
+    if (!HAS_STRINGS(descr))
+        return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
+    min = 0;
+    max = descr->nb_items;
+    while (min != max)
+    {
+        INT32 index = (min + max) / 2;
+        const char *p = descr->items[index].str;
+        if (*p == '[')  /* drive or directory */
+        {
+            if (*str != '[') res = -1;
+            else if (p[1] == '-')  /* drive */
+            {
+                if (str[1] == '-') res = str[2] - p[2];
+                else res = -1;
+            }
+            else  /* directory */
+            {
+                if (str[1] == '-') res = 1;
+                else res = lstrcmpi32A( str, p );
+            }
+        }
+        else  /* filename */
+        {
+            if (*str == '[') res = 1;
+            else res = lstrcmpi32A( str, p );
+        }
+        if (!res) return index;
+        if (res < 0) max = index;
+        else min = index + 1;
+    }
+    return max;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_FindString
+ *
+ * Find the item beginning with a given string.
+ */
+static INT32 LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT32 start,
+                                 LPCSTR str, BOOL32 exact )
+{
+    INT32 i;
+    LB_ITEMDATA *item;
+
+    if (start >= descr->nb_items) start = -1;
+    item = descr->items + start + 1;
+    if (HAS_STRINGS(descr))
+    {
+        if (exact)
+        {
+            for (i = start + 1; i < descr->nb_items; i++, item++)
+                if (!lstrcmpi32A( str, item->str )) return i;
+            for (i = 0, item = descr->items; i <= start; i++, item++)
+                if (!lstrcmpi32A( str, item->str )) return i;
+        }
+        else
+        {
+ /* Special case for drives and directories: ignore prefix */
+#define CHECK_DRIVE(item) \
+    if ((item)->str[0] == '[') \
+    { \
+        if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
+        if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
+        return i; \
+    }
+
+            INT32 len = strlen(str);
+            for (i = start + 1; i < descr->nb_items; i++, item++)
+            {
+               if (!lstrncmpi32A( str, item->str, len )) return i;
+               CHECK_DRIVE(item);
+            }
+            for (i = 0, item = descr->items; i <= start; i++, item++)
+            {
+               if (!lstrncmpi32A( str, item->str, len )) return i;
+               CHECK_DRIVE(item);
+            }
+#undef CHECK_DRIVE
+        }
+    }
+    else
+    {
+        if (exact && (descr->style & LBS_SORT))
+            /* If sorted, use a WM_COMPAREITEM binary search */
+            return LISTBOX_FindStringPos( wnd, 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;
+        for (i = 0, item = descr->items; i <= start; i++, item++)
+            if (item->data == (DWORD)str) return i;
+    }
+    return LB_ERR;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_GetSelCount
+ */
+static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
+{
+    INT32 i, count;
+    LB_ITEMDATA *item = descr->items;
+
+    if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
+    for (i = count = 0; i < descr->nb_items; i++, item++)
+        if (item->selected) count++;
+    return count;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_GetSelItems16
+ */
+static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
+                                      LPINT16 array )
+{
+    INT32 i, count;
+    LB_ITEMDATA *item = descr->items;
+
+    if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
+    for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
+        if (item->selected) array[count++] = (INT16)i;
+    return count;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_GetSelItems32
+ */
+static LRESULT LISTBOX_GetSelItems32( WND *wnd, LB_DESCR *descr, INT32 max,
+                                      LPINT32 array )
+{
+    INT32 i, count;
+    LB_ITEMDATA *item = descr->items;
+
+    if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
+    for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
+        if (item->selected) array[count++] = i;
+    return count;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_Paint
+ */
+static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC32 hdc )
+{
+    INT32 i, col_pos = descr->page_size - 1;
+    RECT32 rect;
+    HFONT32 oldFont = 0;
+
+    SetRect32( &rect, 0, 0, descr->width, descr->height );
+    if (descr->style & LBS_NOREDRAW) return 0;
+    if (descr->style & LBS_MULTICOLUMN)
+        rect.right = rect.left + descr->column_width;
+    else if (descr->horz_pos)
+    {
+        SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
+        rect.right += descr->horz_pos;
+    }
+
+    if (descr->font) oldFont = SelectObject32( hdc, descr->font );
+
+    if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
+        (GetFocus32() == wnd->hwndSelf))
+    {
+        /* Special case for empty listbox: paint focus rect */
+        rect.bottom = rect.top + descr->item_height;
+        LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
+                           ODA_DRAWENTIRE );
+        rect.top = rect.bottom;
+    }
+
+    for (i = descr->top_item; i < descr->nb_items; i++)
+    {
+        if (!(descr->style & LBS_OWNERDRAWVARIABLE))
+            rect.bottom = rect.top + descr->item_height;
+        else
+            rect.bottom = rect.top + descr->items[i].height;
+
+        LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
+        rect.top = rect.bottom;
+
+        if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
+        {
+            if (!IS_OWNERDRAW(descr))
+            {
+                /* Clear the bottom of the column */
+                SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
+                if (rect.top < descr->height)
+                {
+                    rect.bottom = descr->height;
+                    ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
+                                   &rect, NULL, 0, NULL );
+                }
+            }
+
+            /* Go to the next column */
+            rect.left += descr->column_width;
+            rect.right += descr->column_width;
+            rect.top = 0;
+            col_pos = descr->page_size - 1;
+        }
+        else
+        {
+            col_pos--;
+            if (rect.top >= descr->height) break;
+        }
+    }
+
+    if (!IS_OWNERDRAW(descr))
+    {
+        /* Clear the remainder of the client area */
+        SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
+        if (rect.top < descr->height)
+        {
+            rect.bottom = descr->height;
+            ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
+                           &rect, NULL, 0, NULL );
+        }
+        if (rect.right < descr->width)
+        {
+            rect.left   = rect.right;
+            rect.right  = descr->width;
+            rect.top    = 0;
+            rect.bottom = descr->height;
+            ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
+                           &rect, NULL, 0, NULL );
+        }
+    }
+    if (oldFont) SelectObject32( hdc, oldFont );
+    return 0;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_InvalidateItems
+ *
+ * Invalidate all items from a given item. If the specified item is not
+ * visible, nothing happens.
+ */
+static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT32 index )
+{
+    RECT32 rect;
+
+    if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
+    {
+        rect.bottom = descr->height;
+        InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
+        if (descr->style & LBS_MULTICOLUMN)
+        {
+            /* Repaint the other columns */
+            rect.left  = rect.right;
+            rect.right = descr->width;
+            rect.top   = 0;
+            InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
+        }
+    }
+}
+
+
+/***********************************************************************
+ *           LISTBOX_GetItemHeight
+ */
+static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index )
+{
+    if (descr->style & LBS_OWNERDRAWVARIABLE)
+    {
+        if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
+        return descr->items[index].height;
+    }
+    else return descr->item_height;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetItemHeight
+ */
+static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index,
+                                      UINT32 height )
+{
+    if (!height) height = 1;
+
+    if (descr->style & LBS_OWNERDRAWVARIABLE)
+    {
+        if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
+        dprintf_listbox( stddeb, "Listbox %04x: item %d height = %d\n",
+                         wnd->hwndSelf, index, height );
+        descr->items[index].height = height;
+        LISTBOX_UpdateScroll( wnd, descr );
+        LISTBOX_InvalidateItems( wnd, descr, index );
+    }
+    else if (height != descr->item_height)
+    {
+        dprintf_listbox( stddeb, "Listbox %04x: new height = %d\n",
+                         wnd->hwndSelf, height );
+        descr->item_height = height;
+        LISTBOX_UpdatePage( wnd, descr );
+        LISTBOX_UpdateScroll( wnd, descr );
+        InvalidateRect32( wnd->hwndSelf, 0, TRUE );
+    }
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetHorizontalPos
+ */
+static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT32 pos )
+{
+    INT32 diff;
+
+    if (pos > descr->horz_extent - descr->width)
+        pos = descr->horz_extent - descr->width;
+    if (pos < 0) pos = 0;
+    if (!(diff = descr->horz_pos - pos)) return;
+    dprintf_listbox( stddeb, "Listbox %04x: new horz pos = %d\n",
+                     wnd->hwndSelf, pos );
+    descr->horz_pos = pos;
+    LISTBOX_UpdateScroll( wnd, descr );
+    if (abs(diff) < descr->width)
+        ScrollWindow32( wnd->hwndSelf, diff, 0, NULL, NULL );
+    else
+        InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetHorizontalExtent
+ */
+static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
+                                            UINT32 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;
+    dprintf_listbox( stddeb, "Listbox %04x: new horz extent = %d\n",
+                     wnd->hwndSelf, extent );
+    descr->horz_extent = extent;
+    if (descr->horz_pos > extent - descr->width)
+        LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
+    else
+        LISTBOX_UpdateScroll( wnd, descr );
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetColumnWidth
+ */
+static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT32 width)
+{
+    width += 2;  /* For left and right margin */
+    if (width == descr->column_width) return LB_OKAY;
+    dprintf_listbox( stddeb, "Listbox %04x: new column width = %d\n",
+                     wnd->hwndSelf, width );
+    descr->column_width = width;
+    LISTBOX_UpdatePage( wnd, descr );
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetFont
+ *
+ * Returns the item height.
+ */
+static INT32 LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT32 font )
+{
+    HDC32 hdc;
+    HFONT32 oldFont = 0;
+    TEXTMETRIC32A tm;
+
+    descr->font = font;
+
+    if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
+    {
+        fprintf( stderr, "LISTBOX_SetFont: unable to get DC\n" );
+        return 16;
+    }
+    if (font) oldFont = SelectObject32( hdc, font );
+    GetTextMetrics32A( hdc, &tm );
+    if (oldFont) SelectObject32( hdc, oldFont );
+    ReleaseDC32( wnd->hwndSelf, hdc );
+    if (!IS_OWNERDRAW(descr))
+        LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight + 2 );
+    return tm.tmHeight + 2;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_MakeItemVisible
+ *
+ * Make sure that a given item is partially or fully visible.
+ */
+static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT32 index,
+                                     BOOL32 fully )
+{
+    INT32 top;
+
+    if (index <= descr->top_item) top = index;
+    else if (descr->style & LBS_MULTICOLUMN)
+    {
+        INT32 cols = descr->width;
+        if (!fully) cols += descr->column_width - 1;
+        if (cols >= descr->column_width) cols /= descr->column_width;
+        else cols = 1;
+        if (index < descr->top_item + (descr->page_size * cols)) return;
+        top = index - descr->page_size * (cols - 1);
+    }
+    else if (descr->style & LBS_OWNERDRAWVARIABLE)
+    {
+        INT32 height = fully ? descr->items[index].height : 1;
+        for (top = index; top > descr->top_item; top--)
+            if ((height += descr->items[top-1].height) > descr->height) break;
+    }
+    else
+    {
+        if (index < descr->top_item + descr->page_size) return;
+        if (!fully && (index == descr->top_item + descr->page_size) &&
+            (descr->height > (descr->page_size * descr->item_height))) return;
+        top = index - descr->page_size + 1;
+    }
+    LISTBOX_SetTopItem( wnd, descr, top, TRUE );
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SelectItemRange
+ *
+ * Select a range of items. Should only be used on a MULTIPLESEL listbox.
+ */
+static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT32 first,
+                                        INT32 last, BOOL32 on )
+{
+    INT32 i;
+
+    /* A few sanity checks */
+
+    if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
+    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 */
+    {
+        for (i = first; i <= last; i++)
+        {
+            if (descr->items[i].selected) continue;
+            descr->items[i].selected = TRUE;
+            LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
+        }
+    }
+    else  /* Turn selection off */
+    {
+        for (i = first; i <= last; i++)
+        {
+            if (!descr->items[i].selected) continue;
+            descr->items[i].selected = FALSE;
+            LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
+        }
+    }
+    if (descr->style & LBS_NOTIFY)
+        SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetCaretIndex
+ */
+static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT32 index,
+                                      BOOL32 fully_visible )
+{
+    INT32 oldfocus = descr->focus_item;
+
+    if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
+    if (index == oldfocus) return LB_OKAY;
+    descr->focus_item = index;
+    if ((oldfocus != -1) && descr->caret_on && (GetFocus32() == wnd->hwndSelf))
+        LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
+    if (index != -1)
+    {
+        LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
+        if (descr->caret_on && (GetFocus32() == wnd->hwndSelf))
+            LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
+    }
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetSelection
+ */
+static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT32 index,
+                                     BOOL32 on, BOOL32 send_notify )
+{
+    if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
+    if (descr->style & LBS_MULTIPLESEL)
+    {
+        if (index == -1)  /* Select all items */
+            return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
+        else  /* Only one item */
+            return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
+    }
+    else
+    {
+        INT32 oldsel = descr->selected_item;
+        if (index == oldsel) return LB_OKAY;
+        if (oldsel != -1) descr->items[oldsel].selected = FALSE;
+        if (index != -1) descr->items[index].selected = TRUE;
+        descr->selected_item = index;
+/* FIXME    if (index != -1) LISTBOX_MakeItemVisible( wnd, descr, index );*/
+        if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
+        if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
+        if (send_notify) SEND_NOTIFICATION( wnd, descr,
+                               (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
+    }
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_MoveCaret
+ *
+ * Change the caret position and extend the selection to the new caret.
+ */
+static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT32 index,
+                               BOOL32 fully_visible )
+{
+    LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
+    if (descr->style & LBS_EXTENDEDSEL)
+    {
+        if (descr->anchor_item != -1)
+        {
+            INT32 first = MIN( descr->focus_item, descr->anchor_item );
+            INT32 last  = MAX( descr->focus_item, descr->anchor_item );
+            if (first > 0)
+                LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
+            LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
+            LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
+        }
+    }
+    else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
+    {
+        /* Set selection to new caret item */
+        LISTBOX_SetSelection( wnd, descr, index, TRUE,
+                              (descr->style & LBS_NOTIFY) != 0 );
+    }
+}
+
+
+/***********************************************************************
+ *           LISTBOX_InsertItem
+ */
+static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT32 index,
+                                   LPSTR str, DWORD data )
+{
+    LB_ITEMDATA *item;
+    INT32 max_items;
+
+    if (index == -1) index = descr->nb_items;
+    else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
+    if (!descr->items) max_items = 0;
+    else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
+    if (descr->nb_items == max_items)
+    {
+        /* We need to grow the array */
+        max_items += LB_ARRAY_GRANULARITY;
+        if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
+                                  max_items * sizeof(LB_ITEMDATA) )))
+        {
+            SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
+            return LB_ERRSPACE;
+        }
+        descr->items = item;
+    }
+
+    /* Insert the item structure */
+
+    item = &descr->items[index];
+    if (index < descr->nb_items)
+        RtlMoveMemory( item + 1, item,
+                       (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
+    item->str      = str;
+    item->data     = data;
+    item->height   = 0;
+    item->selected = FALSE;
+    descr->nb_items++;
+
+    /* Get item height */
+
+    if (descr->style & LBS_OWNERDRAWVARIABLE)
+    {
+        MEASUREITEMSTRUCT32 mis;
+        mis.CtlType    = ODT_LISTBOX;
+        mis.CtlID      = wnd->wIDmenu;
+        mis.itemID     = index;
+        mis.itemData   = descr->items[index].data;
+        mis.itemHeight = descr->item_height;
+        SendMessage32A( descr->owner, WM_MEASUREITEM, wnd->wIDmenu,
+                        (LPARAM)&mis );
+        item->height = mis.itemHeight ? mis.itemHeight : 1;
+        dprintf_listbox( stddeb, "Listbox %04x: measure item %d (%s) = %d\n",
+                         wnd->hwndSelf, index, str ? str : "", item->height );
+    }
+
+    /* Repaint the items */
+
+    LISTBOX_UpdateScroll( wnd, descr );
+    LISTBOX_InvalidateItems( wnd, descr, index );
+
+    /* Move selection and focused item */
+
+    if (index <= descr->selected_item) descr->selected_item++;
+    if (index <= descr->focus_item)
+    {
+        descr->focus_item++;
+        LISTBOX_MoveCaret( wnd, descr, descr->focus_item - 1, FALSE );
+    }
+
+    /* If listbox was empty, set focus to the first item */
+
+    if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_InsertString
+ */
+static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT32 index,
+                                     LPCSTR str )
+{
+    LPSTR new_str = NULL;
+    DWORD data = 0;
+    LRESULT ret;
+
+    if (HAS_STRINGS(descr))
+    {
+        if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
+        {
+            SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
+            return LB_ERRSPACE;
+        }
+    }
+    else data = (DWORD)str;
+
+    if (index == -1) index = descr->nb_items;
+    if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
+    {
+        if (new_str) HeapFree( descr->heap, 0, new_str );
+        return ret;
+    }
+
+    dprintf_listbox( stddeb, "Listbox %04x: added item %d '%s'\n",
+                     wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
+    return index;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_DeleteItem
+ *
+ * Delete the content of an item. 'index' must be a valid index.
+ */
+static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT32 index )
+{
+    /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
+     *       while Win95 sends it for all items with user data.
+     *       It's probably better to send it too often than not
+     *       often enough, so this is what we do here.
+     */
+    if (IS_OWNERDRAW(descr) || descr->items[index].data)
+    {
+        DELETEITEMSTRUCT32 dis;
+        dis.CtlType  = ODT_LISTBOX;
+        dis.CtlID    = wnd->wIDmenu;
+        dis.itemID   = index;
+        dis.hwndItem = wnd->hwndSelf;
+        dis.itemData = descr->items[index].data;
+        SendMessage32A( wnd->hwndSelf, WM_DELETEITEM, wnd->wIDmenu,
+                        (LPARAM)&dis );
+    }
+    if (HAS_STRINGS(descr) && descr->items[index].str)
+        HeapFree( descr->heap, 0, descr->items[index].str );
+}
+
+
+/***********************************************************************
+ *           LISTBOX_RemoveItem
+ *
+ * Remove an item from the listbox and delete its content.
+ */
+static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT32 index )
+{
+    LB_ITEMDATA *item;
+    INT32 max_items;
+
+    if (index == -1) index = descr->nb_items - 1;
+    else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
+    LISTBOX_DeleteItem( wnd, descr, index );
+
+    /* Remove the item */
+
+    item = &descr->items[index];
+    if (index < descr->nb_items-1)
+        RtlMoveMemory( item, item + 1,
+                       (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
+    descr->nb_items--;
+    if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
+
+    /* Shrink the item array if possible */
+
+    max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
+    if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
+    {
+        max_items -= LB_ARRAY_GRANULARITY;
+        item = HeapReAlloc( descr->heap, 0, descr->items,
+                            max_items * sizeof(LB_ITEMDATA) );
+        if (item) descr->items = item;
+    }
+
+    /* Repaint the items */
+
+    LISTBOX_UpdateScroll( wnd, descr );
+    LISTBOX_InvalidateItems( wnd, descr, index );
+
+    /* Move selection and focused item */
+
+    if (index <= descr->selected_item) descr->selected_item--;
+    if (index <= descr->focus_item)
+    {
+        descr->focus_item--;
+        LISTBOX_MoveCaret( wnd, descr, descr->focus_item + 1, FALSE );
+    }
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_ResetContent
+ */
+static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
+{
+    INT32 i;
+
+    for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
+    if (descr->items) HeapFree( descr->heap, 0, descr->items );
+    descr->nb_items      = 0;
+    descr->top_item      = 0;
+    descr->selected_item = -1;
+    descr->focus_item    = 0;
+    descr->anchor_item   = -1;
+    descr->items         = NULL;
+    LISTBOX_UpdateScroll( wnd, descr );
+    InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
+}
+
+
+/***********************************************************************
+ *           LISTBOX_SetCount
+ */
+static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT32 count )
+{
+    LRESULT ret;
+
+    if (HAS_STRINGS(descr)) return LB_ERR;
+    /* FIXME: this is far from optimal... */
+    if (count > descr->nb_items)
+    {
+        while (count > descr->nb_items)
+            if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
+                return ret;
+    }
+    else if (count < descr->nb_items)
+    {
+        while (count < descr->nb_items)
+            if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
+                return ret;
+    }
+    return LB_OKAY;
+}
+
+
+/***********************************************************************
+ *           LISTBOX_Directory
+ */
+LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT32 attrib,
+                           LPCSTR filespec, BOOL32 long_names )
+{
+    char mask[13];
+    const char *ptr;
     char *path, *p;
+    int count, skip, pos;
+    LRESULT ret;
+    DOS_DIRENT entry;
 
-    dprintf_listbox(stddeb, "ListBoxDirectory: '%s' %04x\n", filespec, attrib);
+    /* FIXME: should use FindFirstFile/FindNextFile */
+
     if (!filespec) return LB_ERR;
     if (!(ptr = DOSFS_GetUnixFileName( filespec, FALSE ))) return LB_ERR;
-    path = xstrdup(ptr);
+    path = HEAP_strdupA( SystemHeap, 0, ptr );
     p = strrchr( path, '/' );
     *p++ = '\0';
-    if (!(ptr = DOSFS_ToDosFCBFormat( p )) || 
-        !(temp = SEGPTR_ALLOC( sizeof(char) * 16 )) )
+    if (!(ptr = DOSFS_ToDosFCBFormat( p )))
     {
-        free( path );
+        HeapFree( SystemHeap, 0, path );
         return LB_ERR;
     }
-
     strcpy( mask, ptr );
 
-    dprintf_listbox(stddeb, "ListBoxDirectory: path=%s mask=%s\n", path, mask);
-
-    skip = ret = 0;
+    skip = 0;
+    ret = LB_OKAY;
     attrib &= ~FA_LABEL;
     while ((count = DOSFS_FindNext( path, mask, NULL, 0,
                                     attrib, skip, &entry )) > 0)
     {
+        char buffer[260];
         skip += count;
         if (entry.attr & FA_DIRECTORY)
         {
-            if ((attrib & DDL_DIRECTORY) && strcmp(entry.name, ".          "))
-            {
-                sprintf(temp, "[%s]", DOSFS_ToDosDTAFormat( entry.name ) );
-                AnsiLower( temp );
-                if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break;
-            }
+            if (!(attrib & DDL_DIRECTORY) || !strcmp(entry.name,".          "))
+                continue;
+            if (long_names) sprintf( buffer, "[%s]", entry.unixname );
+            else sprintf( buffer, "[%s]", DOSFS_ToDosDTAFormat( entry.name ) );
         }
         else  /* not a directory */
         {
-            if (!(attrib & DDL_EXCLUSIVE) ||
-                ((attrib & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE)) ==
+            if ((attrib & DDL_EXCLUSIVE) &&
+                ((attrib & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE)) !=
                  (entry.attr & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE))))
-            {
-                strcpy( temp, DOSFS_ToDosDTAFormat( entry.name ) );
-                AnsiLower( temp );
-                if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break;
-            }
+                continue;
+            if (long_names) strcpy( buffer, entry.unixname );
+            else strcpy( buffer, DOSFS_ToDosDTAFormat( entry.name ) );
         }
-
-        dprintf_listbox(stddeb,"\tn - %i, file '%s'\n", count, temp); 
+        if (!long_names) AnsiLower( buffer );
+        pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
+        if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
+            break;
     }
-    if (attrib & DDL_DRIVES)
+
+    if ((ret == LB_OKAY) && (attrib & DDL_DRIVES))
     {
-        int x;
-	DWORD oldstyle = lphl->dwStyle;
-	    
-	lphl->dwStyle &= ~LBS_SORT;
-        strcpy( temp, "[-a-]" );
-        for (x = 0; x < MAX_DOS_DRIVES; x++, temp[2]++)
+        char buffer[] = "[-a-]";
+        int drive;
+        for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
         {
-            if (DRIVE_IsValid(x))
-                if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break;
+            if (!DRIVE_IsValid(drive)) continue;
+            if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
+                break;
         }
-	lphl->dwStyle = oldstyle;
     }
 
-    free( path );
-    SEGPTR_FREE( temp );
-
+    HeapFree( SystemHeap, 0, path );
     return ret;
 }
 
-/* ------------------------- dimensions ------------------------- */
 
-int ListBoxGetItemRect(LPHEADLIST lphl, WORD wIndex, LPRECT16 lprect)
+/***********************************************************************
+ *           LISTBOX_HandleVScroll
+ */
+static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
+                                      WPARAM32 wParam, LPARAM lParam )
 {
-  LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wIndex);
+    SCROLLINFO info;
 
-  dprintf_listbox(stddeb,"ListBox LB_GETITEMRECT %i %p", wIndex,lpls);
-  if (lpls == NULL)
-  {
-    if (lphl->dwStyle & LBS_OWNERDRAWVARIABLE)
-      return LB_ERR;
-    else 
+    if (descr->style & LBS_MULTICOLUMN) return 0;
+    switch(LOWORD(wParam))
     {
-     GetClientRect16(lphl->hSelf,lprect);
-     lprect->bottom=lphl->StdItemHeight;
-     if (lprect->right<0) lprect->right=0;
+    case SB_LINEUP:
+        LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
+        break;
+    case SB_LINEDOWN:
+        LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
+        break;
+    case SB_PAGEUP:
+        LISTBOX_SetTopItem( wnd, descr, descr->top_item -
+                            LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
+        break;
+    case SB_PAGEDOWN:
+        LISTBOX_SetTopItem( wnd, descr, descr->top_item +
+                            LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
+        break;
+    case SB_THUMBPOSITION:
+        LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
+        break;
+    case SB_THUMBTRACK:
+        info.cbSize = sizeof(info);
+        info.fMask = SIF_TRACKPOS;
+        GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
+        LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
+        break;
+    case SB_TOP:
+        LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
+        break;
+    case SB_BOTTOM:
+        LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
+        break;
     }
-  }
-  else
-   *lprect = lpls->itemRect;
-  dprintf_listbox(stddeb," = %d,%d  %d,%d\n", lprect->left,lprect->top,
-                                              lprect->right,lprect->bottom);
-  return 0;
-}
-
-
-int ListBoxSetItemHeight(LPHEADLIST lphl, WORD wIndex, long height)
-{
-  LPLISTSTRUCT lpls;
-
-  if (!(lphl->dwStyle & LBS_OWNERDRAWVARIABLE)) {
-    lphl->StdItemHeight = (short)height;
     return 0;
-  }
-  
-  lpls = ListBoxGetItem(lphl, wIndex);
-  if (lpls == NULL) return LB_ERR;
-  
-  lpls->mis.itemHeight = height;
-  return 0;
-}
-
-/* -------------------------- string search ------------------------ */  
-
-int ListBoxFindNextMatch(LPHEADLIST lphl, WORD wChar)
-{
-  LPLISTSTRUCT lpls;
-  UINT	       count,first;
-
-  if ((char)wChar < ' ') return LB_ERR;
-  if (!lphl->HasStrings) return LB_ERR;
-
-  lpls = lphl->lpFirst;
-  
-  for (count = 0; lpls != NULL; lpls = lpls->lpNext, count++) {
-    if (tolower(*lpls->itemText) == tolower((char)wChar)) break;
-  }
-  if (lpls == NULL) return LB_ERR;
-  first = count;
-  for(; lpls != NULL; lpls = lpls->lpNext, count++) {
-    if (*lpls->itemText != (char)wChar) 
-      break;
-    if ((short) count > lphl->ItemFocused)
-      return count;
-  }
-  return first;
-}
-
-/***********************************************************************
- *           LBCreate
- */
-static LONG LBCreate(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST   lphl;
-  LONG	       dwStyle = GetWindowLong32A(hwnd,GWL_STYLE);
-  RECT16 rect;
-
-  CreateListBoxStruct(hwnd, ODT_LISTBOX, dwStyle, GetParent16(hwnd));
-  lphl = ListBoxGetStorageHeader(hwnd);
-  dprintf_listbox(stddeb,"ListBox created: lphl = %p dwStyle = %04x:%04x\n", 
-			  lphl, HIWORD(dwStyle), LOWORD(dwStyle));
-
-  GetClientRect16(hwnd,&rect);
-  lphl->ColumnsWidth = rect.right - rect.left;
-
-  if (dwStyle & WS_VSCROLL) 
-    SetScrollRange32(hwnd, SB_VERT, 0, ListMaxFirstVisible(lphl), TRUE);
-  if (dwStyle & WS_HSCROLL) 
-    SetScrollRange32(hwnd, SB_HORZ, 1, 1, TRUE);
-
-  return 0;
 }
 
 
 /***********************************************************************
- *           LBDestroy
+ *           LISTBOX_HandleHScroll
  */
-static LONG LBDestroy(HWND hwnd, WORD wParam, LONG lParam)
+static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
+                                      WPARAM32 wParam, LPARAM lParam )
 {
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
+    SCROLLINFO info;
+    INT32 page;
 
-  ListBoxResetContent(lphl);
-
-  DestroyListBoxStruct(lphl);
-  dprintf_listbox(stddeb,"ListBox destroyed: lphl = %p\n",lphl);
-  return 0;
-}
-
-
-/***********************************************************************
- *           LBNCCalcSize
- */
-static LONG LBNCCalcSize(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LONG	     ret = DefWindowProc16(hwnd, WM_NCCALCSIZE, wParam, lParam);
-
-  return (GetWindowLong32A(hwnd,GWL_STYLE) & LBS_MULTICOLUMN)? WVR_VREDRAW : ret;
-}
-
-
-/***********************************************************************
- *           LBVScroll
- */
-static LONG LBVScroll(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  int  y;
-
-  dprintf_listbox(stddeb,"ListBox WM_VSCROLL w=%04X l=%08lX !\n",
-		  wParam, lParam);
-  y = lphl->FirstVisible;
-
-  switch(wParam) {
-  case SB_LINEUP:
-    if (lphl->FirstVisible > 0)
-      lphl->FirstVisible--;
-    break;
-
-  case SB_LINEDOWN:
-    lphl->FirstVisible++;
-    break;
-
-  case SB_PAGEUP:
-    if (lphl->FirstVisible > lphl->ItemsVisible) {
-      lphl->FirstVisible -= lphl->ItemsVisible;
-    } else {
-      lphl->FirstVisible = 0;
+    if (descr->style & LBS_MULTICOLUMN)
+    {
+        switch(LOWORD(wParam))
+        {
+        case SB_LINELEFT:
+            LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
+                                TRUE );
+            break;
+        case SB_LINERIGHT:
+            LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
+                                TRUE );
+            break;
+        case SB_PAGELEFT:
+            page = descr->width / descr->column_width;
+            if (page < 1) page = 1;
+            LISTBOX_SetTopItem( wnd, descr,
+                             descr->top_item - page * descr->page_size, TRUE );
+            break;
+        case SB_PAGERIGHT:
+            page = descr->width / descr->column_width;
+            if (page < 1) page = 1;
+            LISTBOX_SetTopItem( wnd, descr,
+                             descr->top_item + page * descr->page_size, TRUE );
+            break;
+        case SB_THUMBPOSITION:
+            LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
+                                TRUE );
+            break;
+        case SB_THUMBTRACK:
+            info.cbSize = sizeof(info);
+            info.fMask  = SIF_TRACKPOS;
+            GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
+            LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
+                                TRUE );
+            break;
+        case SB_LEFT:
+            LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
+            break;
+        case SB_RIGHT:
+            LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
+            break;
+        }
     }
-    break;
-
-  case SB_PAGEDOWN:
-    lphl->FirstVisible += lphl->ItemsVisible;
-    break;
-
-  case SB_THUMBTRACK:
-    lphl->FirstVisible = LOWORD(lParam);
-    break;
-  }
-
-  if (lphl->FirstVisible > ListMaxFirstVisible(lphl))
-    lphl->FirstVisible = ListMaxFirstVisible(lphl);
-
-  if (y != lphl->FirstVisible) {
-    SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-    InvalidateRect32( hwnd, NULL, TRUE );
-  }
-  return 0;
+    else if (descr->horz_extent)
+    {
+        switch(LOWORD(wParam))
+        {
+        case SB_LINELEFT:
+            LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
+            break;
+        case SB_LINERIGHT:
+            LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
+            break;
+        case SB_PAGELEFT:
+            LISTBOX_SetHorizontalPos( wnd, descr,
+                                      descr->horz_pos - descr->width );
+            break;
+        case SB_PAGERIGHT:
+            LISTBOX_SetHorizontalPos( wnd, descr,
+                                      descr->horz_pos + descr->width );
+            break;
+        case SB_THUMBPOSITION:
+            LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
+            break;
+        case SB_THUMBTRACK:
+            info.cbSize = sizeof(info);
+            info.fMask = SIF_TRACKPOS;
+            GetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info );
+            LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
+            break;
+        case SB_LEFT:
+            LISTBOX_SetHorizontalPos( wnd, descr, 0 );
+            break;
+        case SB_RIGHT:
+            LISTBOX_SetHorizontalPos( wnd, descr,
+                                      descr->horz_extent - descr->width );
+            break;
+        }
+    }
+    return 0;
 }
 
+
 /***********************************************************************
- *           LBHScroll
+ *           LISTBOX_HandleLButtonDown
  */
-static LONG LBHScroll(HWND hwnd, WORD wParam, LONG lParam)
+static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
+                                          WPARAM32 wParam, INT32 x, INT32 y )
 {
-  LPHEADLIST lphl;
-  int        y;
-
-  dprintf_listbox(stddeb,"ListBox WM_HSCROLL w=%04X l=%08lX !\n",
-		  wParam, lParam);
-  lphl = ListBoxGetStorageHeader(hwnd);
-  y = lphl->FirstVisible;
-  switch(wParam) {
-  case SB_LINEUP:
-    if (lphl->FirstVisible > lphl->ItemsPerColumn) {
-      lphl->FirstVisible -= lphl->ItemsPerColumn;
-    } else {
-      lphl->FirstVisible = 0;
+    INT32 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
+    dprintf_listbox( stddeb, "Listbox %04x: lbuttondown %d,%d item %d\n",
+                     wnd->hwndSelf, x, y, index );
+    if (!descr->caret_on && (GetFocus32() == wnd->hwndSelf)) return 0;
+    if (index != -1)
+    {
+        if (descr->style & LBS_EXTENDEDSEL)
+        {
+            if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
+            if (wParam & MK_CONTROL)
+            {
+                LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
+                LISTBOX_SetSelection( wnd, descr, index,
+                                      !descr->items[index].selected,
+                                      (descr->style & LBS_NOTIFY) != 0 );
+            }
+            else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
+        }
+        else
+        {
+            LISTBOX_MoveCaret( wnd, descr, index, FALSE );
+            LISTBOX_SetSelection( wnd, descr, index,
+                                  (!(descr->style & LBS_MULTIPLESEL) || 
+                                   !descr->items[index].selected),
+                                  (descr->style & LBS_NOTIFY) != 0 );
+        }
     }
-    break;
-  case SB_LINEDOWN:
-    lphl->FirstVisible += lphl->ItemsPerColumn;
-    break;
-  case SB_PAGEUP:
-    if (lphl->ItemsPerColumn != 0) {
-      int lbsub = lphl->ItemsVisible / lphl->ItemsPerColumn * lphl->ItemsPerColumn;
-      if (lphl->FirstVisible > lbsub) {
-	lphl->FirstVisible -= lbsub;
-      } else {
-	lphl->FirstVisible = 0;
-      }
+    SetFocus32( wnd->hwndSelf );
+    SetCapture32( wnd->hwndSelf );
+    if (index != -1)
+    {
+        if (descr->style & LBS_NOTIFY)
+            SendMessage32A( descr->owner, WM_LBTRACKPOINT, index,
+                            MAKELPARAM( x, y ) );
+        if (wnd->dwExStyle & WS_EX_DRAGDETECT)
+        {
+            POINT16 pt = { x, y };
+            if (DragDetect( wnd->hwndSelf, pt ))
+                SendMessage32A( descr->owner, WM_BEGINDRAG, 0, 0 );
+        }
     }
-    break;
-  case SB_PAGEDOWN:
-    if (lphl->ItemsPerColumn != 0)
-      lphl->FirstVisible += lphl->ItemsVisible /
-	lphl->ItemsPerColumn * lphl->ItemsPerColumn;
-    break;
-  case SB_THUMBTRACK:
-    lphl->FirstVisible = lphl->ItemsPerColumn * LOWORD(lParam);
-    break;
-  } 
-  if (lphl->FirstVisible > ListMaxFirstVisible(lphl))
-    lphl->FirstVisible = ListMaxFirstVisible(lphl);
-
-  if (lphl->ItemsPerColumn != 0) {
-    lphl->FirstVisible = lphl->FirstVisible /
-      lphl->ItemsPerColumn * lphl->ItemsPerColumn + 1;
-    if (y != lphl->FirstVisible) {
-      SetScrollPos32(hwnd, SB_HORZ, lphl->FirstVisible / 
-		   lphl->ItemsPerColumn + 1, TRUE);
-      InvalidateRect32( hwnd, NULL, TRUE );
-    }
-  }
-  return 0;
+    return 0;
 }
 
-/***********************************************************************
- *           LBLButtonDown
- */
-static LONG LBLButtonDown(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  WORD       wRet;
-  int        y,n;
-  RECT16     rectsel;
-
-  SetFocus32(hwnd);
-  SetCapture32(hwnd);
-
-  lphl->PrevFocused = lphl->ItemFocused;
-
-  y = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam));
-
-  if (y == -1) return 0;
-
-  if (lphl->dwStyle & LBS_NOTIFY && y!= LB_ERR )
-     if( SendMessage16(lphl->hParent, WM_LBTRACKPOINT, y, lParam) )
-         return 0;
-
-
-  switch( lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) )
-   {
-        case LBS_MULTIPLESEL:
-                lphl->ItemFocused = y;
-                wRet = ListBoxGetSel(lphl, y);
-                ListBoxSetSel(lphl, y, !wRet);
-                break;
-        case LBS_EXTENDEDSEL:
-                /* should handle extended mode here and in kbd handler 
-                 */ 
-
-                if ( lphl->PrevFocused != y && y!= LB_ERR)
-                 {
-                   LPLISTSTRUCT lpls = ListBoxGetItem( lphl, lphl->ItemFocused = y );
-                   n = ListBoxSetSel(lphl,-1,FALSE);
-
-                   lpls->itemState = ODS_FOCUS | ODS_SELECTED;
-
-                   if( n > 1 && n != LB_ERR )
-                     InvalidateRect32( hwnd,NULL,TRUE );
-                 }
-                else
-                       return 0;
-
-                break;
-        case 0:
-                if( y!=lphl->ItemFocused )
-                  ListBoxSetCurSel(lphl, y);
-                else
-                  return 0;
-                break;
-        default:
-                fprintf(stdnimp,"Listbox: LBS_MULTIPLESEL and LBS_EXTENDEDSEL are on!\n");
-                return 0;
-   }
-
- /* invalidate changed items */
- if( lphl->dwStyle & LBS_MULTIPLESEL || y!=lphl->PrevFocused )
-   {
-     ListBoxGetItemRect(lphl, y, &rectsel);
-     InvalidateRect16( hwnd, &rectsel, TRUE );
-   }
- if( lphl->PrevFocused!=-1 && y!=lphl->PrevFocused ) 
-   {
-     ListBoxGetItemRect(lphl, lphl->PrevFocused, &rectsel);
-     InvalidateRect16( hwnd, &rectsel, TRUE );
-   }
-
-  if (GetWindowLong32A(lphl->hSelf,GWL_EXSTYLE) & WS_EX_DRAGDETECT)
-     if( DragDetect(lphl->hSelf,MAKEPOINT16(lParam)) )
-         SendMessage16(lphl->hParent, WM_BEGINDRAG,0,0L);
-  return 0;
-}
 
 /***********************************************************************
- *           LBLButtonUp
- */
-static LONG LBLButtonUp(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  if (GetCapture32() == hwnd) ReleaseCapture();
-
-  if (lphl->PrevFocused != lphl->ItemFocused)
-    ListBoxSendNotification(lphl, LBN_SELCHANGE);
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBRButtonUp
- */
-static LONG LBRButtonUp(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  SendMessage16(lphl->hParent, WM_COMMAND, GetWindowWord(hwnd,GWW_ID),
-		MAKELONG(hwnd, LBN_DBLCLK));
-  return 0;
-}
-
-/***********************************************************************
- *           LBMouseMove
- */
-static LONG LBMouseMove(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  int  y,redraw_prev = 0;
-  int  iRet;
-  RECT16 rect, rectsel;   /* XXX Broken */
-
-  dprintf_listbox(stddeb,"LBMouseMove %d %d\n",SLOWORD(lParam),SHIWORD(lParam));
-  if ((wParam & MK_LBUTTON) != 0) {
-    y = SHIWORD(lParam);
-    if (y < LBMM_EDGE) {
-      if (lphl->FirstVisible > 0) {
-	lphl->FirstVisible--;
-	SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-	InvalidateRect32( hwnd, NULL, TRUE );
-	return 0;
-      }
-    }
-    GetClientRect16(hwnd, &rect);
-    if (y >= (rect.bottom-LBMM_EDGE)) {
-      if (lphl->FirstVisible < ListMaxFirstVisible(lphl)) {
-	lphl->FirstVisible++;
-	SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-	InvalidateRect32( hwnd, NULL, TRUE );
-	return 0;
-      }
-      }
-    if ((y > 0) && (y < (rect.bottom - LBMM_EDGE))) {
-      if ((y < rectsel.top) || (y > rectsel.bottom)) {
-        iRet = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam));
-        if (iRet == lphl->ItemFocused || iRet == -1)  {
-	  return 0;
-	}
-	if (lphl->dwStyle & LBS_MULTIPLESEL) {
-          lphl->ItemFocused = iRet;
-	  ListBoxSendNotification(lphl, LBN_SELCHANGE);
-        } else if ( lphl->dwStyle & LBS_EXTENDEDSEL )
-                  {
-                     /* Fixme: extended selection mode */
-                     ListBoxSetSel( lphl, lphl->ItemFocused, 0);
-                     lphl->PrevFocused = lphl->ItemFocused;
-                     lphl->ItemFocused = iRet;
-                     ListBoxSetSel( lphl, iRet, TRUE);
-                     redraw_prev = 1;
-                  }
-               else
-                  {
-                     ListBoxSetCurSel(lphl, (WORD)iRet);
-                     redraw_prev = 1; 
-                  }
-        if( lphl->PrevFocused!=-1 && redraw_prev )
-          {
-            ListBoxGetItemRect(lphl, lphl->PrevFocused, &rectsel);
-            InvalidateRect16( hwnd, &rectsel, TRUE );
-          }
-        ListBoxGetItemRect(lphl, iRet, &rectsel);
-	InvalidateRect16( hwnd, &rectsel, TRUE );
-      }
-    }
-  }
-
-  return 0;
-  }
-
-/***********************************************************************
- *           LBKeyDown
+ *           LISTBOX_HandleTimer
  *
- * Doesn't yet handle properly VK_SHIFT with LB_EXTENDEDSEL
+ * Handle scrolling upon a timer event.
+ * Return TRUE if scrolling should continue.
  */
-static LONG LBKeyDown(HWND hwnd, WORD wParam, LONG lParam)
+static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
+                                    INT32 index, TIMER_DIRECTION dir )
 {
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  WORD       newFocused = 0xFFFF;
-  RECT16     rect;
-
-  ListBoxGetItemRect(lphl,lphl->ItemFocused,&rect);
-  switch(wParam) 
+    switch(dir)
     {
-	case VK_HOME:
-	case VK_END:
-	case VK_LEFT:
-	case VK_RIGHT:
-	case VK_UP:
-	case VK_DOWN:
-	case VK_PRIOR:
-	case VK_NEXT:
-	     if ( lphl->dwStyle & LBS_WANTKEYBOARDINPUT )
-	        {
-		  newFocused = (WORD)(INT)SendMessage16(lphl->hParent,WM_VKEYTOITEM,
-                                                        wParam,MAKELPARAM(lphl->ItemFocused,hwnd));
-	          if ( newFocused == 0xFFFE ) return 0L;
-                }
-	     if ( newFocused == 0xFFFF ) 
-		{
-		  newFocused = lphl->ItemFocused;
-
-		  /* nested switch */
-		  switch(wParam)
-		    {
-                        case VK_HOME:
-                          newFocused = 0;
-                          break;
-                        case VK_END:
-                          newFocused = lphl->ItemsCount - 1;
-                          break;
-                        case VK_LEFT:
-  			  if (lphl->dwStyle & LBS_MULTICOLUMN) {
-                            if (newFocused >= lphl->ItemsPerColumn) {
-                                newFocused -= lphl->ItemsPerColumn;
-                            } else {
-                                newFocused = 0;
-                            }
-                          }
-                          break;
-                        case VK_UP:
-                          if (newFocused > 0) newFocused--;
-                          break;
-                        case VK_RIGHT:
-                          if (lphl->dwStyle & LBS_MULTICOLUMN) 
-                             newFocused += lphl->ItemsPerColumn;
-                          break;
-                        case VK_DOWN:
-                          newFocused++;
-                          break;
-                        case VK_PRIOR:
-                          if (newFocused > lphl->ItemsVisible)
-                              newFocused -= lphl->ItemsVisible;
-			    else  newFocused = 0;
-                            break;
-                        case VK_NEXT:
-                          newFocused += lphl->ItemsVisible;
-                          break;
-                        default:
-                          return 0;
-                    }
-		  /* end of nested switch */
-		}
-	     break;   
-	case VK_SPACE:
-             if (lphl->dwStyle & LBS_MULTIPLESEL)
-                {
-                 WORD wRet = ListBoxGetSel(lphl, lphl->ItemFocused);
-                 ListBoxSetSel(lphl, lphl->ItemFocused, !wRet);
-                 }
-             return 0;
-
-        /* chars are handled in LBChar */
-	default:
-	     return 0;
+    case LB_TIMER_UP:
+        if (descr->top_item) index = descr->top_item - 1;
+        else index = 0;
+        break;
+    case LB_TIMER_LEFT:
+        if (descr->top_item) index -= descr->page_size;
+        break;
+    case LB_TIMER_DOWN:
+        index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
+        if (index >= descr->nb_items) index = descr->nb_items - 1;
+        break;
+    case LB_TIMER_RIGHT:
+        if (index + descr->page_size < descr->nb_items)
+            index += descr->page_size;
+        break;
+    case LB_TIMER_NONE:
+        break;
     }
+    if (index == descr->focus_item) return FALSE;
+    LISTBOX_MoveCaret( wnd, descr, index, FALSE );
+    return TRUE;
+}
 
-  /* at this point newFocused is set up */
 
-  if (newFocused >= lphl->ItemsCount)
-    newFocused = lphl->ItemsCount - 1;
-  
-  if (!(lphl->dwStyle & LBS_MULTIPLESEL)) 
-     {
-        ListBoxSetCurSel(lphl, newFocused);
-	ListBoxSendNotification(lphl, LBN_SELCHANGE);
-     }
-
-  lphl->ItemFocused = newFocused;
-
-  if( ListBoxScrollToFocus(lphl) || (lphl->dwStyle & 
-                          (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) )
-  InvalidateRect32( hwnd, NULL, TRUE );
-  else
+/***********************************************************************
+ *           LISTBOX_HandleSystemTimer
+ *
+ * WM_SYSTIMER handler.
+ */
+static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
+{
+    if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
     {
-	InvalidateRect16( hwnd, &rect, TRUE );
-	if( newFocused < 0x8000 )
-          {
-	   ListBoxGetItemRect(lphl, newFocused, &rect);
-	   InvalidateRect16( hwnd, &rect, TRUE );
-          }
+        KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
+        LISTBOX_Timer = LB_TIMER_NONE;
     }
-
-  SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBChar
- */
-static LONG LBChar(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  WORD       newFocused = 0xFFFF;
-
-  if ( (lphl->dwStyle & LBS_WANTKEYBOARDINPUT) && !(lphl->HasStrings))
-       {
-        newFocused = (WORD)(INT)SendMessage16(lphl->hParent,WM_CHARTOITEM,
-                                              wParam,MAKELPARAM(lphl->ItemFocused,hwnd));
-        if ( newFocused == 0xFFFE ) return 0L;
-       }
-
-  if (newFocused == 0xFFFF ) 
-  newFocused = ListBoxFindNextMatch(lphl, wParam);
-
-  if (newFocused == (WORD)LB_ERR) return 0;
-
-  if (newFocused >= lphl->ItemsCount)
-    newFocused = lphl->ItemsCount - 1;
-
-  if (!(lphl->dwStyle & LBS_MULTIPLESEL)) 
-     {
-    ListBoxSetCurSel(lphl, newFocused);
-	ListBoxSendNotification(lphl, LBN_SELCHANGE);
-  }
-
-  lphl->ItemFocused = newFocused;
-  ListBoxScrollToFocus(lphl);
-  SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-
-  InvalidateRect32( hwnd, NULL, TRUE );
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBSetRedraw
- */
-static LONG LBSetRedraw(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST  lphl = ListBoxGetStorageHeader(hwnd);
-
-  dprintf_listbox(stddeb,"ListBox WM_SETREDRAW hWnd=%04x w=%04x !\n",
-		  hwnd, wParam);
-  lphl->bRedrawFlag = wParam;
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBSetFont
- */
-static LONG LBSetFont(HWND hwnd, WPARAM16 wParam, LPARAM lParam)
-{
-  LPHEADLIST  lphl = ListBoxGetStorageHeader(hwnd);
-  HDC32 hdc;
-
-  if (wParam == 0)
-    lphl->hFont = GetStockObject32(SYSTEM_FONT);
-  else
-    lphl->hFont = (HFONT16)wParam;
-
-  /* a new font means possible new text height */
-  /* does this mean the height of each entry must be separately changed? */
-  /* or are we guaranteed to get a LBSetFont before the first insert/add? */
-  if ((hdc = GetDC32(0)))
-  {
-      TEXTMETRIC16 tm;
-      GetTextMetrics16( hdc, &tm );
-      lphl->StdItemHeight = tm.tmHeight;
-      dprintf_listbox(stddeb,"LBSetFont:  new font %d with height %d\n",
-                      lphl->hFont, lphl->StdItemHeight);
-      ReleaseDC32( 0, hdc );
-  }
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBPaint
- */
-static LONG LBPaint(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST   lphl = ListBoxGetStorageHeader(hwnd);
-  LPLISTSTRUCT lpls;
-  PAINTSTRUCT16 ps;
-  HBRUSH16 hBrush;
-  HFONT32 hOldFont;
-  HDC16 hdc    = BeginPaint16( hwnd, &ps );
-  DC    *dc    = (DC *)GDI_GetObjPtr(hdc, DC_MAGIC);
-  RECT16  rect, paintRect, scratchRect;
-  int   i, top, height, maxwidth, ipc;
-
-  top = 0;
-
-  if (!IsWindowVisible(hwnd) || !lphl->bRedrawFlag) {
-    EndPaint16(hwnd, &ps);
     return 0;
-  }
-
-  GetRgnBox16(dc->w.hGCClipRgn,&paintRect);
-  GetClientRect16(hwnd, &rect);
-  IntersectRect16(&paintRect,&rect,&paintRect);
-
-  hOldFont = SelectObject32(hdc, lphl->hFont);
-
-  hBrush = (HBRUSH16)SendMessage32A( lphl->hParent, WM_CTLCOLORLISTBOX,
-                                     (WPARAM32)hdc, (LPARAM)hwnd);
-  if (hBrush == 0) hBrush = GetStockObject32(WHITE_BRUSH);
-
-  FillRect16(hdc, &rect, hBrush);
-
-  maxwidth = rect.right;
-  if (lphl->dwStyle & LBS_MULTICOLUMN) {
-    rect.right = lphl->ColumnsWidth;
-  }
-  lpls = lphl->lpFirst;
-
-  lphl->ItemsVisible = 0;
-  lphl->ItemsPerColumn = ipc = 0;
-
-  for(i = 0; i < lphl->ItemsCount; i++) {
-    if (lpls == NULL) break;
-
-    if (i >= lphl->FirstVisible) {
-      height = lpls->mis.itemHeight;
-
-      if (top > (rect.bottom-height+1)) {
-	if (lphl->dwStyle & LBS_MULTICOLUMN) {
-	  lphl->ItemsPerColumn = MAX(lphl->ItemsPerColumn, ipc);
-	  ipc = 0;
-	  top = 0;
-	  rect.left += lphl->ColumnsWidth;
-	  rect.right += lphl->ColumnsWidth;
-	  if (rect.left > maxwidth) break;
-	} else {
-	  break;
-	}
-      }
-
-      lpls->itemRect.top    = top;
-      lpls->itemRect.bottom = top + height;
-      lpls->itemRect.left   = rect.left;
-      lpls->itemRect.right  = rect.right;
-
-      if( IntersectRect16(&scratchRect,&paintRect,&lpls->itemRect) )
-       {
-        dprintf_listbox(stddeb,"LBPaint: drawing item: %d %d %d %d %d\n",
-                        rect.left,top,rect.right,top+height,lpls->itemState);
-
-        if (lphl->OwnerDrawn && (lphl->ItemFocused == i) && GetFocus32() == hwnd)
-           {
-             ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_FOCUS, 
-                                                      lpls->itemState & ~ODS_FOCUS);
-             ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE, 
-                                                      lpls->itemState & ~ODS_FOCUS);
-             ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_FOCUS, lpls->itemState);
-           }
-        else
-            ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE,
-                                                     lpls->itemState);
-       }
-
-      top += height;
-      lphl->ItemsVisible++;
-      ipc++;
-    }
-
-    lpls = lpls->lpNext;
-  }
-  ListBoxUpdateWindow(hwnd,lphl,FALSE);
-  SelectObject32(hdc,hOldFont);
-  EndPaint16( hwnd, &ps );
-  return 0;
 }
 
+
 /***********************************************************************
- *           LBSetFocus
+ *           LISTBOX_HandleMouseMove
+ *
+ * WM_MOUSEMOVE handler.
  */
-static LONG LBSetFocus(HWND hwnd, WORD wParam, LONG lParam)
+static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
+                                     INT32 x, INT32 y )
 {
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
+    INT32 index;
+    TIMER_DIRECTION dir;
 
-  dprintf_listbox(stddeb,"ListBox WM_SETFOCUS for %04x\n",hwnd);
-  if(!(lphl->dwStyle & LBS_MULTIPLESEL) )
-       if( lphl->ItemsCount && lphl->ItemFocused != -1)
-         {
-           HDC32        hDC = GetDC32(hwnd);
-           HFONT32 hOldFont = SelectObject32(hDC, lphl->hFont);
-           LPLISTSTRUCT lpls;
-
-           lpls = ListBoxGetItem(lphl,lphl->ItemFocused);
-           lpls->itemState |= ODS_FOCUS;
-
-           ListBoxDrawItem(hwnd,lphl,hDC,lpls,&lpls->itemRect, ODA_FOCUS, lpls->itemState);
-           SelectObject32(hDC, hOldFont);
-           ReleaseDC32(hwnd,hDC);
-         }
-
-  ListBoxSendNotification(lphl, LBN_SETFOCUS);
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBKillFocus
- */
-static LONG LBKillFocus(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  dprintf_listbox(stddeb,"ListBox WM_KILLFOCUS for %04x\n",hwnd);
-  if (!(lphl->dwStyle & LBS_MULTIPLESEL))
-     {
-       if( lphl->ItemsCount )
-           if( lphl->ItemFocused != -1 )
-             {
-              HDC32        hDC = GetDC32(hwnd);
-              HFONT32 hOldFont = SelectObject32(hDC, lphl->hFont);
-              LPLISTSTRUCT lpls;
-
-              lpls = ListBoxGetItem(lphl,lphl->ItemFocused);
-              lpls->itemState &= ~ODS_FOCUS;
-
-              ListBoxDrawItem(hwnd,lphl,hDC,lpls,&lpls->itemRect, ODA_FOCUS, lpls->itemState);
-              SelectObject32(hDC, hOldFont);
-              ReleaseDC32(hwnd,hDC);
-             }
-           else
-             dprintf_listbox(stddeb,"LBKillFocus: no focused item!\n");
-     }
-  else
-     InvalidateRect32( hwnd, NULL, TRUE );
-
-  ListBoxSendNotification(lphl, LBN_KILLFOCUS);
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBResetContent
- */
-static LONG LBResetContent(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  dprintf_listbox(stddeb,"ListBox LB_RESETCONTENT !\n");
-  ListBoxResetContent(lphl);
-  ListBoxUpdateWindow(hwnd, lphl, TRUE);
-  return 0;
-}
-
-/***********************************************************************
- *           LBDir
- */
-static LONG LBDir(HWND hwnd, WORD wParam, LONG lParam)
-{
-    LONG ret;
-    LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-    dprintf_listbox(stddeb,"ListBox LB_DIR !\n");
-
-    ret = ListBoxDirectory(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam));
-    ListBoxUpdateWindow(hwnd, lphl, TRUE);
-    return ret;
-}
-
-/***********************************************************************
- *           LBAddString
- */
-static LONG LBAddString(HWND hwnd, WORD wParam, LONG lParam)
-{
-  WORD  wRet;
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  wRet = ListBoxAddString(lphl, (SEGPTR)lParam);
-
-  ListBoxUpdateWindow(hwnd,lphl,TRUE);
-  return wRet;
-}
-
-/***********************************************************************
- *           LBGetText
- */
-static LONG LBGetText(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LONG   wRet;
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  dprintf_listbox(stddeb, "LB_GETTEXT  wParam=%d\n",wParam);
-  wRet = ListBoxGetText(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam));
-
-  return wRet;
-}
-
-/***********************************************************************
- *           LBInsertString
- */
-static LONG LBInsertString(HWND hwnd, WORD wParam, LONG lParam)
-{
-  WORD  wRet;
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  if (lphl->HasStrings)
-    wRet = ListBoxInsertString(lphl, wParam, (LPCSTR)PTR_SEG_TO_LIN(lParam));
-  else
-    wRet = ListBoxInsertString(lphl, wParam, (LPCSTR)lParam);
-
-  ListBoxUpdateWindow(hwnd,lphl,TRUE);
-  return wRet;
-}
-
-/***********************************************************************
- *           LBDeleteString
- */
-static LONG LBDeleteString(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  LONG lRet = ListBoxDeleteString(lphl,wParam);
-  
-  ListBoxUpdateWindow(hwnd,lphl,TRUE);
-  return lRet;
-}
-
-/***********************************************************************
- *           LBFindString
- */
-static LONG LBFindString(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  return lbFindString(lphl, wParam, (SEGPTR)lParam, MATCH_SUBSTR);
-}
-
-/***********************************************************************
- *           LBFindStringExact
- */
-static LONG LBFindStringExact(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  return lbFindString(lphl, wParam, (SEGPTR)lParam, MATCH_EXACT);
-}
-
-/***********************************************************************
- *           LBGetCaretIndex
- */
-static LONG LBGetCaretIndex(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  return lphl->ItemFocused;
-}
-
-/***********************************************************************
- *           LBGetCount
- */
-static LONG LBGetCount(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST  lphl;
-
-  lphl = ListBoxGetStorageHeader(hwnd);
-  return lphl->ItemsCount;
-}
-
-/***********************************************************************
- *           LBGetCurSel
- */
-static LONG LBGetCurSel(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST  lphl;
-
-  lphl = ListBoxGetStorageHeader(hwnd);
-  dprintf_listbox(stddeb,"ListBox LB_GETCURSEL %i !\n", 
-		  lphl->ItemFocused);
-  return lphl->ItemFocused;
-}
-
-/***********************************************************************
- *           LBGetHorizontalExtent
- */
-static LONG LBGetHorizontalExtent(HWND hwnd, WORD wParam, LONG lParam)
-{    
-  return 0;
-}
-
-/***********************************************************************
- *           LBGetItemHeight
- */
-static LONG LBGetItemHeight(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  LPLISTSTRUCT lpls = ListBoxGetItem (lphl, wParam);
-  
-  if (lpls == NULL) return LB_ERR;
-  return lpls->mis.itemHeight;
-}
-
-/***********************************************************************
- *           LBGetItemRect
- */
-static LONG LBGetItemRect(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  return ListBoxGetItemRect(lphl, wParam, PTR_SEG_TO_LIN(lParam));
-}
-
-/***********************************************************************
- *           LBGetSel
- */
-static LONG LBGetSel(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  int        iSel = ListBoxGetSel(lphl, wParam);
-
-  dprintf_listbox(stdnimp,"LBGetSel: item %u - %i\n",wParam,iSel);
-
-  return (iSel)? 1 : 0;
-}
-
-/***********************************************************************
- *           LBGetSelCount
- */
-static LONG LBGetSelCount(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  LPLISTSTRUCT lpls;
-  int          cnt = 0;
-  int          items = 0;
-
-  if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
-	 return LB_ERR;
-
-  for( lpls = lphl->lpFirst;
-       lpls;
-       lpls = lpls->lpNext )
-	{
-	   items++;
-           if (lpls->itemState ) 
-               cnt++;
-  }
-
-  return cnt;
-}
-
-/***********************************************************************
- *           LBGetSelItems
- */
-static LONG LBGetSelItems(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST  lphl = ListBoxGetStorageHeader(hwnd);
-  LPLISTSTRUCT lpls;
-  int cnt, idx;
-  int *lpItems = PTR_SEG_TO_LIN(lParam);
-
-  if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
-        return LB_ERR;
-
-  if (wParam == 0) return 0;
-
-  lpls = lphl->lpFirst;
-  cnt = 0; idx = 0;
-
-  while (lpls != NULL) {
-    if (lpls->itemState > 0) lpItems[cnt++] = idx;
-
-    if (cnt == wParam) break;
-    idx++;
-    lpls = lpls->lpNext;
-  }
-
-  return cnt;
-}
-
-/***********************************************************************
- *           LBGetTextLen
- */
-static LONG LBGetTextLen(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST   lphl = ListBoxGetStorageHeader(hwnd);
-  LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wParam);
-
-  if (lpls == NULL || !lphl->HasStrings) return LB_ERR;
-  return strlen(lpls->itemText);
-}
-
-/***********************************************************************
- *           LBGetDlgCode
- */
-static LONG LBGetDlgCode(HWND hwnd, WORD wParam, LONG lParam)
-{
-  return DLGC_WANTARROWS | DLGC_WANTCHARS;
-}
-
-/***********************************************************************
- *           LBGetTopIndex
- */
-static LONG LBGetTopIndex(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  return lphl->FirstVisible;
-}
-
-
-/***********************************************************************
- *           LBSelectString
- */
-static LONG LBSelectString(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  INT  iRet;
-
-  iRet = lbFindString(lphl, wParam, (SEGPTR)lParam, MATCH_SUBSTR);
-
-  if( iRet != LB_ERR)
+    if (descr->style & LBS_MULTICOLUMN)
     {
-      if( lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) )
-         ListBoxSetSel(lphl,iRet,TRUE);
-      else
-         ListBoxSetCurSel(lphl,iRet);
+        if (y < 0) y = 0;
+        else if (y >= descr->item_height * descr->page_size)
+            y = descr->item_height * descr->page_size - 1;
 
-      lphl->ItemFocused = iRet;
-      InvalidateRect32( hwnd, 0, TRUE );
+        if (x < 0)
+        {
+            dir = LB_TIMER_LEFT;
+            x = 0;
+        }
+        else if (x >= descr->width)
+        {
+            dir = LB_TIMER_RIGHT;
+            x = descr->width - 1;
+        }
+        else dir = LB_TIMER_NONE;  /* inside */
     }
-  return iRet;
+    else
+    {
+        if (y < 0) dir = LB_TIMER_UP;  /* above */
+        else if (y >= descr->height) dir = LB_TIMER_DOWN;  /* below */
+        else dir = LB_TIMER_NONE;  /* inside */
+    }
+
+    index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
+    if (index == -1) index = descr->focus_item;
+    if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
+
+    /* Start/stop the system timer */
+
+    if (dir != LB_TIMER_NONE)
+        SetSystemTimer32( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
+    else if (LISTBOX_Timer != LB_TIMER_NONE)
+        KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
+    LISTBOX_Timer = dir;
 }
 
+
 /***********************************************************************
- *           LBSelItemRange
+ *           LISTBOX_HandleKeyDown
  */
-static LONG LBSelItemRange(HWND hwnd, WORD wParam, LONG lParam)
+static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr,
+                                      WPARAM32 wParam )
 {
-  LPHEADLIST   lphl = ListBoxGetStorageHeader(hwnd);
-  LPLISTSTRUCT lpls;
-  WORD         cnt;
-  WORD         first = LOWORD(lParam);
-  WORD         last = HIWORD(lParam);
-  BOOL         select = wParam;
-
-  if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)  )) 
-        return LB_ERR;
-
-  if (first >= lphl->ItemsCount ||
-      last >= lphl->ItemsCount) return LB_ERR;
-
-  lpls = lphl->lpFirst;
-  cnt = 0;
-
-  while (lpls != NULL) {
-    if (cnt++ >= first)
-      lpls->itemState = select ? lpls->itemState | ODS_SELECTED : 0;
-
-    if (cnt > last)
-      break;
-
-    lpls = lpls->lpNext;
-  }
-
-  return 0;
+    INT32 caret = -1;
+    if (descr->style & LBS_WANTKEYBOARDINPUT)
+    {
+        caret = SendMessage32A( descr->owner, WM_VKEYTOITEM,
+                                MAKEWPARAM(LOWORD(wParam), descr->focus_item),
+                                wnd->hwndSelf );
+        if (caret == -2) return 0;
+    }
+    if (caret == -1) switch(wParam)
+    {
+    case VK_LEFT:
+        if (descr->style & LBS_MULTICOLUMN)
+        {
+            if (descr->focus_item >= descr->page_size)
+                caret = descr->focus_item - descr->page_size;
+            break;
+        }
+        /* fall through */
+    case VK_UP:
+        caret = descr->focus_item - 1;
+        if (caret < 0) caret = 0;
+        break;
+    case VK_RIGHT:
+        if (descr->style & LBS_MULTICOLUMN)
+        {
+            if (descr->focus_item + descr->page_size < descr->nb_items)
+                caret = descr->focus_item + descr->page_size;
+            break;
+        }
+        /* fall through */
+    case VK_DOWN:
+        caret = descr->focus_item + 1;
+        if (caret >= descr->nb_items) caret = descr->nb_items - 1;
+        break;
+    case VK_PRIOR:
+        if (descr->style & LBS_MULTICOLUMN)
+        {
+            INT32 page = descr->width / descr->column_width;
+            if (page < 1) page = 1;
+            caret = descr->focus_item - (page * descr->page_size) + 1;
+        }
+        else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
+        if (caret < 0) caret = 0;
+        break;
+    case VK_NEXT:
+        if (descr->style & LBS_MULTICOLUMN)
+        {
+            INT32 page = descr->width / descr->column_width;
+            if (page < 1) page = 1;
+            caret = descr->focus_item + (page * descr->page_size) - 1;
+        }
+        else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
+        if (caret >= descr->nb_items) caret = descr->nb_items - 1;
+        break;
+    case VK_HOME:
+        caret = 0;
+        break;
+    case VK_END:
+        caret = descr->nb_items - 1;
+        break;
+    case VK_SPACE:
+        if (descr->style & LBS_EXTENDEDSEL)
+        {
+            if (!(GetKeyState(VK_SHIFT) & 0x8000))
+                descr->anchor_item = descr->focus_item;
+            LISTBOX_MoveCaret( wnd, descr, descr->focus_item, TRUE );
+        }
+        else if (descr->style & LBS_MULTIPLESEL)
+            LISTBOX_SetSelection( wnd, descr, descr->focus_item,
+                                  !descr->items[descr->focus_item].selected,
+                                  (descr->style & LBS_NOTIFY) != 0 );
+        break;
+    }
+    if (caret >= 0)
+    {
+        if ((descr->style & LBS_EXTENDEDSEL) &&
+            !(GetKeyState( VK_SHIFT ) & 0x8000))
+            descr->anchor_item = caret;
+        LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
+    }
+    return 0;
 }
 
+
 /***********************************************************************
- *           LBSetCaretIndex
+ *           LISTBOX_HandleChar
  */
-static LONG LBSetCaretIndex(HWND hwnd, WORD wParam, LONG lParam)
+static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
+                                   WPARAM32 wParam )
 {
-  LPHEADLIST   lphl = ListBoxGetStorageHeader(hwnd);
-  int          i;
+    INT32 index;
+    char str[2] = { wParam & 0xff, '\0' };
 
-  if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) )) return 0;
-
-  dprintf_listbox(stddeb,"LBSetCaretIndex: hwnd %04x n=%i\n",hwnd,wParam);  
-
-  if (wParam >= lphl->ItemsCount) return LB_ERR;
-
-  lphl->ItemFocused = wParam;
-  i = ListBoxScrollToFocus (lphl);
-
-  SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-  if(i)
-    InvalidateRect32( hwnd, NULL, TRUE );
- 
-  return 1;
+    index = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE );
+    if (index != LB_ERR) LISTBOX_MoveCaret( wnd, descr, index, TRUE );
+    return 0;
 }
 
-/***********************************************************************
- *           LBSetColumnWidth
- */
-static LONG LBSetColumnWidth(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  lphl->ColumnsWidth = wParam;
-  InvalidateRect32( hwnd, NULL, TRUE );
-  return 0;
-}
 
 /***********************************************************************
- *           LBSetHorizontalExtent
+ *           LISTBOX_Create
  */
-static LONG LBSetHorizontalExtent(HWND hwnd, WORD wParam, LONG lParam)
+static BOOL32 LISTBOX_Create( WND *wnd )
 {
-  return 0;
-}
+    LB_DESCR *descr;
+    MEASUREITEMSTRUCT32 mis;
+    RECT32 rect;
 
-/***********************************************************************
- *           LBGetItemData
- */
-static LONG LBGetItemData(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  dprintf_listbox(stddeb, "LB_GETITEMDATA wParam=%x\n", wParam);
-  return ListBoxGetItemData(lphl, wParam);
-}
+    if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
+        return FALSE;
+    if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
+    {
+        HeapFree( GetProcessHeap(), 0, descr );
+        return FALSE;
+    }
+    GetClientRect32( wnd->hwndSelf, &rect );
+    descr->owner         = GetParent32( wnd->hwndSelf );
+    descr->style         = wnd->dwStyle;
+    descr->width         = rect.right - rect.left;
+    descr->height        = rect.bottom - rect.top;
+    descr->items         = NULL;
+    descr->nb_items      = 0;
+    descr->top_item      = 0;
+    descr->selected_item = -1;
+    descr->focus_item    = 0;
+    descr->anchor_item   = -1;
+    descr->item_height   = 1;
+    descr->page_size     = 1;
+    descr->column_width  = 150;
+    descr->horz_extent   = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
+    descr->horz_pos      = 0;
+    descr->nb_tabs       = 0;
+    descr->tabs          = NULL;
+    descr->caret_on      = TRUE;
+    descr->font          = 0;
+    descr->locale        = 0;  /* FIXME */
 
-/***********************************************************************
- *           LBSetItemData
- */
-static LONG LBSetItemData(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  dprintf_listbox(stddeb, "LB_SETITEMDATA  wParam=%x  lParam=%lx\n", wParam, lParam);
-  return ListBoxSetItemData(lphl, wParam, lParam);
-}
+    *(LB_DESCR **)wnd->wExtra = descr;
 
-/***********************************************************************
- *           LBSetTabStops
- */
-static LONG LBSetTabStops(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST  lphl;
+    if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
+    if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
+    if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
+    descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
 
-  lphl = ListBoxGetStorageHeader(hwnd);
+    if (descr->style & LBS_OWNERDRAWFIXED)
+    {
+        mis.CtlType    = ODT_LISTBOX;
+        mis.CtlID      = wnd->wIDmenu;
+        mis.itemID     = 0;
+        mis.itemWidth  = 0;
+        mis.itemData   = 0;
+        mis.itemHeight = descr->item_height;
+        SendMessage32A( descr->owner, WM_MEASUREITEM, wnd->wIDmenu,
+                        (LPARAM)&mis );
+        descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
+    }
 
-  if (lphl->TabStops != NULL) {
-    lphl->iNumStops = 0;
-    free (lphl->TabStops);
-  }
-
-  lphl->TabStops = malloc (wParam * sizeof (short));
-  if (lphl->TabStops) {
-    lphl->iNumStops = wParam;
-    memcpy (lphl->TabStops, PTR_SEG_TO_LIN(lParam), wParam * sizeof (short));
     return TRUE;
-  }
-
-  return FALSE;
 }
 
+
 /***********************************************************************
- *           LBSetCurSel
+ *           LISTBOX_Destroy
  */
-static LONG LBSetCurSel(HWND hwnd, WORD wParam, LONG lParam)
+static BOOL32 LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
 {
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  WORD  wRet;
-
-  dprintf_listbox(stddeb,"ListBox LB_SETCURSEL wParam=%x !\n", 
-		  wParam);
-
-  wRet = ListBoxSetCurSel(lphl, wParam);
-
-  SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-  InvalidateRect32( hwnd, NULL, TRUE );
-
-  return wRet;
-}
-
-/***********************************************************************
- *           LBSetSel
- */
-static LONG LBSetSel(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  RECT16 rect;
-  int iRet;
-
-  dprintf_listbox(stddeb,"ListBox LB_SETSEL wParam=%x lParam=%lX !\n", wParam, lParam);
-
-  iRet = ListBoxSetSel(lphl, LOWORD(lParam), wParam);
-
-  if( iRet > 1 ) InvalidateRect32( hwnd, NULL, TRUE );
-  else if( iRet != LB_ERR )
-      {
-        if( lphl->dwStyle & LBS_EXTENDEDSEL &&
-            lphl->ItemFocused != LOWORD(lParam) )
-          {
-            ListBoxGetItemRect(lphl, lphl->ItemFocused , &rect);
-            InvalidateRect16( hwnd, &rect, TRUE );
-            lphl->ItemFocused = LOWORD(lParam);
-          }
-        ListBoxGetItemRect(lphl,LOWORD(lParam),&rect);
-        InvalidateRect16( hwnd, &rect, TRUE );
-      }
-
-  return (iRet == (WORD)LB_ERR)? LB_ERR: 0;
-}
-
-/***********************************************************************
- *           LBSetTopIndex
- */
-static LONG LBSetTopIndex(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-  dprintf_listbox(stddeb,"ListBox LB_SETTOPINDEX wParam=%x !\n",
-		  wParam);
-  lphl->FirstVisible = wParam;
-  SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
-
-  InvalidateRect32( hwnd, NULL, TRUE );
-
-  return 0;
-}
-
-/***********************************************************************
- *           LBSetItemHeight
- */
-static LONG LBSetItemHeight(HWND hwnd, WORD wParam, LONG lParam)
-{
-  LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-  WORD wRet;
-  
-  dprintf_listbox(stddeb,"ListBox LB_SETITEMHEIGHT wParam=%x lParam=%lX !\n", wParam, lParam);
-  wRet = ListBoxSetItemHeight(lphl, wParam, lParam);
-  InvalidateRect32( hwnd, NULL, TRUE );
-  return wRet;
-}
-
-/***********************************************************************
- *	     LBPassToParent
- */
-static LRESULT LBPassToParent(HWND hwnd, UINT message, WPARAM16 wParam, LPARAM lParam)
-{
-  WND* ptrWnd = WIN_FindWndPtr(hwnd);  
-
-  if( ptrWnd )
-      if( /* !(ptrWnd->dwExStyle & WS_EX_NOPARENTNOTIFY) && */ 
-          ptrWnd->parent ) 
-          return SendMessage16(ptrWnd->parent->hwndSelf,message,wParam,lParam);
-  return 0;
-}
-
-/***********************************************************************
- *           ListBoxWndProc 
- */
-LRESULT ListBoxWndProc(HWND hwnd, UINT message, WPARAM16 wParam, LPARAM lParam)
-{ 
-    switch (message) {
-     case WM_CREATE: return LBCreate(hwnd, wParam, lParam);
-     case WM_DESTROY: return LBDestroy(hwnd, wParam, lParam);
-     case WM_GETDLGCODE: return LBGetDlgCode(hwnd, wParam, lParam);
-     case WM_VSCROLL: return LBVScroll(hwnd, wParam, lParam);
-     case WM_HSCROLL: return LBHScroll(hwnd, wParam, lParam);
-     case WM_LBUTTONDOWN: return LBLButtonDown(hwnd, wParam, lParam);
-     case WM_LBUTTONUP: return LBLButtonUp(hwnd, wParam, lParam);
-     case WM_RBUTTONUP: return LBRButtonUp(hwnd, wParam, lParam);
-     case WM_LBUTTONDBLCLK: return LBRButtonUp(hwnd, wParam, lParam);
-     case WM_MOUSEMOVE: return LBMouseMove(hwnd, wParam, lParam);
-     case WM_KEYDOWN: return LBKeyDown(hwnd, wParam, lParam);
-     case WM_CHAR: return LBChar(hwnd, wParam, lParam);
-     case WM_SETFONT: return LBSetFont(hwnd, wParam, lParam);
-     case WM_SETREDRAW: return LBSetRedraw(hwnd, wParam, lParam);
-     case WM_PAINT: return LBPaint(hwnd, wParam, lParam);
-     case WM_SETFOCUS: return LBSetFocus(hwnd, wParam, lParam);
-     case WM_KILLFOCUS: return LBKillFocus(hwnd, wParam, lParam);
-     case WM_NCCALCSIZE: return LBNCCalcSize(hwnd, wParam, lParam);
-     case LB_RESETCONTENT16: return LBResetContent(hwnd, wParam, lParam);
-     case LB_DIR16: return LBDir(hwnd, wParam, lParam);
-     case LB_ADDSTRING16: return LBAddString(hwnd, wParam, lParam);
-     case LB_INSERTSTRING16: return LBInsertString(hwnd, wParam, lParam);
-     case LB_DELETESTRING16: return LBDeleteString(hwnd, wParam, lParam);
-     case LB_FINDSTRING16: return LBFindString(hwnd, wParam, lParam);
-     case LB_FINDSTRINGEXACT16: return LBFindStringExact(hwnd, wParam, lParam);
-     case LB_GETCARETINDEX16: return LBGetCaretIndex(hwnd, wParam, lParam);
-     case LB_GETCOUNT16: return LBGetCount(hwnd, wParam, lParam);
-     case LB_GETCURSEL16: return LBGetCurSel(hwnd, wParam, lParam);
-     case LB_GETHORIZONTALEXTENT16: return LBGetHorizontalExtent(hwnd, wParam, lParam);
-     case LB_GETITEMDATA16: return LBGetItemData(hwnd, wParam, lParam);
-     case LB_GETITEMHEIGHT16: return LBGetItemHeight(hwnd, wParam, lParam);
-     case LB_GETITEMRECT16: return LBGetItemRect(hwnd, wParam, lParam);
-     case LB_GETSEL16: return LBGetSel(hwnd, wParam, lParam);
-     case LB_GETSELCOUNT16: return LBGetSelCount(hwnd, wParam, lParam);
-     case LB_GETSELITEMS16: return LBGetSelItems(hwnd, wParam, lParam);
-     case LB_GETTEXT16: return LBGetText(hwnd, wParam, lParam);
-     case LB_GETTEXTLEN16: return LBGetTextLen(hwnd, wParam, lParam);
-     case LB_GETTOPINDEX16: return LBGetTopIndex(hwnd, wParam, lParam);
-     case LB_SELECTSTRING16: return LBSelectString(hwnd, wParam, lParam);
-     case LB_SELITEMRANGE16: return LBSelItemRange(hwnd, wParam, lParam);
-     case LB_SETCARETINDEX16: return LBSetCaretIndex(hwnd, wParam, lParam);
-     case LB_SETCOLUMNWIDTH16: return LBSetColumnWidth(hwnd, wParam, lParam);
-     case LB_SETHORIZONTALEXTENT16: return LBSetHorizontalExtent(hwnd, wParam, lParam);
-     case LB_SETITEMDATA16: return LBSetItemData(hwnd, wParam, lParam);
-     case LB_SETTABSTOPS16: return LBSetTabStops(hwnd, wParam, lParam);
-     case LB_SETCURSEL16: return LBSetCurSel(hwnd, wParam, lParam);
-     case LB_SETSEL16: return LBSetSel(hwnd, wParam, lParam);
-     case LB_SETTOPINDEX16: return LBSetTopIndex(hwnd, wParam, lParam);
-     case LB_SETITEMHEIGHT16: return LBSetItemHeight(hwnd, wParam, lParam);
-
-     case WM_DROPFILES: return LBPassToParent(hwnd, message, wParam, lParam);
-
-     /* these will have to be implemented for proper LBS_EXTENDEDSEL -
-      *
-      * anchor item is an item that with caret (focused) item defines a 
-      * range of currently selected items when listbox is in the extended 
-      * selection mode.
-      */
-     case LB_SETANCHORINDEX16: return LB_SETANCHORINDEX16; /* that's what Windows returns */
-     case LB_GETANCHORINDEX16: return 0;
-
-	case WM_DROPOBJECT:
-	case WM_QUERYDROPOBJECT:
-	case WM_DRAGSELECT:
-	case WM_DRAGMOVE:
-	        {
-		 LPDRAGINFO lpDragInfo = (LPDRAGINFO) PTR_SEG_TO_LIN((SEGPTR)lParam);
-		 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
-
-		 lpDragInfo->l = ListBoxFindMouse(lphl,lpDragInfo->pt.x,
-						       lpDragInfo->pt.y);
-	        
-		 return LBPassToParent(hwnd, message, wParam, lParam);
-		}
-    }
-    
-    return DefWindowProc16(hwnd, message, wParam, lParam);
-}
-
-
-/**********************************************************************
- *	    DlgDirList    (USER.100)
- */
-INT DlgDirList( HWND hDlg, SEGPTR spec, INT idLBox, INT idStatic, UINT attrib )
-{
-    char *filespec = (char *)PTR_SEG_TO_LIN( spec );
-    int drive;
-    HWND hwnd;
-
-#define SENDMSG(msg,wparam,lparam) \
-    ((attrib & DDL_POSTMSGS) ? PostMessage( hwnd, msg, wparam, lparam ) \
-                             : SendMessage16( hwnd, msg, wparam, lparam ))
-
-    dprintf_listbox( stddeb, "DlgDirList: %04x '%s' %d %d %04x\n",
-                     hDlg, filespec ? filespec : "NULL",
-                     idLBox, idStatic, attrib );
-
-    if (filespec && filespec[0] && (filespec[1] == ':'))
-    {
-        drive = toupper( filespec[0] ) - 'A';
-        filespec += 2;
-        if (!DRIVE_SetCurrentDrive( drive )) return FALSE;
-    }
-    else drive = DRIVE_GetCurrentDrive();
-
-    if (idLBox && ((hwnd = GetDlgItem( hDlg, idLBox )) != 0))
-    {
-        char mask[20];
-        
-        if (!filespec || !filespec[0]) strcpy( mask, "*.*" );
-        else
-        {
-            /* If the path exists and is a directory, chdir to it */
-            if (DRIVE_Chdir( drive, filespec )) strcpy( mask, "*.*" );
-            else
-            {
-                char *p, *p2;
-                p = filespec;
-                if ((p2 = strrchr( p, '\\' ))) p = p2 + 1;
-                if ((p2 = strrchr( p, '/' ))) p = p2 + 1;
-                lstrcpyn32A( mask, p, sizeof(mask) );
-                if (p != filespec)
-                {
-                    p[-1] = '\0';
-                    if (!DRIVE_Chdir( drive, filespec )) return FALSE;
-                }
-            }
-        }
-        
-        strcpy( (char *)PTR_SEG_TO_LIN(spec), mask );
-
-        dprintf_listbox(stddeb, "ListBoxDirectory: path=%c:\\%s mask=%s\n",
-                        'A' + drive, DRIVE_GetDosCwd(drive), mask);
-        
-        SENDMSG( LB_RESETCONTENT16, 0, 0 );
-        if ((attrib & DDL_DIRECTORY) && !(attrib & DDL_EXCLUSIVE))
-        {
-            char *temp;
-            if (SENDMSG( LB_DIR16, attrib & ~(DDL_DIRECTORY | DDL_DRIVES),
-                         (LPARAM)spec ) == LB_ERR) return FALSE;
-            if (!(temp = SEGPTR_ALLOC( 4*sizeof(char) ))) return FALSE;
-            strcpy( temp, "*.*" );
-            /* FIXME: this won't work with PostMessage(), as temp will */
-            /* have been freed by the time we do a DispatchMessage().  */
-            if (SENDMSG( LB_DIR16, (attrib & (DDL_DIRECTORY | DDL_DRIVES)) | DDL_EXCLUSIVE,
-                         (LPARAM)SEGPTR_GET(temp) ) == LB_ERR)
-            {
-                SEGPTR_FREE(temp);
-                return FALSE;
-            }
-            SEGPTR_FREE(temp);
-        }
-        else
-        {
-            if (SENDMSG( LB_DIR16, attrib, (LPARAM)spec) == LB_ERR) return FALSE;
-        }
-    }
-
-    if (idStatic && ((hwnd = GetDlgItem( hDlg, idStatic )) != 0))
-    {
-        char temp[512];
-        int drive = DRIVE_GetCurrentDrive();
-        strcpy( temp, "A:\\" );
-        temp[0] += drive;
-        lstrcpyn32A( temp + 3, DRIVE_GetDosCwd(drive), sizeof(temp)-3 );
-        AnsiLower( temp );
-        /* Can't use PostMessage() here, because the string is on the stack */
-        SetDlgItemText32A( hDlg, idStatic, temp );
-    }
+    LISTBOX_ResetContent( wnd, descr );
+    HeapDestroy( descr->heap );
+    HeapFree( GetProcessHeap(), 0, descr );
+    *(LB_DESCR **)wnd->wExtra = NULL;
     return TRUE;
-#undef SENDMSG
+}
+
+
+/***********************************************************************
+ *           ListBoxWndProc
+ */
+LRESULT ListBoxWndProc(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
+{
+    LRESULT ret;
+    LB_DESCR *descr;
+    WND *wnd = WIN_FindWndPtr( hwnd );
+
+    if (!wnd) return 0;
+    if (!(descr = *(LB_DESCR **)wnd->wExtra))
+    {
+        if (msg == WM_CREATE)
+        {
+            if (!LISTBOX_Create( wnd )) return -1;
+            dprintf_listbox( stddeb, "Listbox: creating wnd=%04x descr=%p\n",
+                             hwnd, *(LB_DESCR **)wnd->wExtra );
+            return 0;
+        }
+        /* Ignore all other messages before we get a WM_CREATE */
+        return DefWindowProc32A( hwnd, msg, wParam, lParam );
+    }
+
+    dprintf_listbox( stddeb, "Listbox %04x: msg %s wp %08x lp %08lx\n",
+                     wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
+    switch(msg)
+    {
+    case LB_RESETCONTENT16:
+    case LB_RESETCONTENT32:
+        LISTBOX_ResetContent( wnd, descr );
+        return 0;
+
+    case LB_ADDSTRING16:
+        if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
+        /* fall through */
+    case LB_ADDSTRING32:
+        wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
+        return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
+
+    case LB_INSERTSTRING16:
+        if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
+        wParam = (INT32)(INT16)wParam;
+        /* fall through */
+    case LB_INSERTSTRING32:
+        return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
+
+    case LB_ADDFILE16:
+        if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
+        /* fall through */
+    case LB_ADDFILE32:
+        wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
+        return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
+
+    case LB_DELETESTRING16:
+    case LB_DELETESTRING32:
+        return LISTBOX_RemoveItem( wnd, descr, wParam );
+
+    case LB_GETITEMDATA16:
+    case LB_GETITEMDATA32:
+        if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
+            return LB_ERR;
+        return descr->items[wParam].data;
+
+    case LB_SETITEMDATA16:
+    case LB_SETITEMDATA32:
+        if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
+            return LB_ERR;
+        descr->items[wParam].data = (DWORD)lParam;
+        return LB_OKAY;
+
+    case LB_GETCOUNT16:
+    case LB_GETCOUNT32:
+        return descr->nb_items;
+
+    case LB_GETTEXT16:
+        lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
+        /* fall through */
+    case LB_GETTEXT32:
+        return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
+
+    case LB_GETTEXTLEN16:
+        /* fall through */
+    case LB_GETTEXTLEN32:
+        if (wParam >= descr->nb_items) return LB_ERR;
+        return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
+                                   : sizeof(DWORD));
+
+    case LB_GETCURSEL16:
+    case LB_GETCURSEL32:
+        return descr->selected_item;
+
+    case LB_GETTOPINDEX16:
+    case LB_GETTOPINDEX32:
+        return descr->top_item;
+
+    case LB_GETITEMHEIGHT16:
+    case LB_GETITEMHEIGHT32:
+        return LISTBOX_GetItemHeight( wnd, descr, wParam );
+
+    case LB_SETITEMHEIGHT16:
+        lParam = LOWORD(lParam);
+        /* fall through */
+    case LB_SETITEMHEIGHT32:
+        return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
+
+    case LB_ITEMFROMPOINT32:
+        {
+            POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
+            RECT32 rect = { 0, 0, descr->width, descr->height };
+            return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
+                             PtInRect32( &rect, pt ) );
+        }
+
+    case LB_SETCARETINDEX16:
+    case LB_SETCARETINDEX32:
+        return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
+
+    case LB_GETCARETINDEX16:
+    case LB_GETCARETINDEX32:
+        return descr->focus_item;
+
+    case LB_SETTOPINDEX16:
+    case LB_SETTOPINDEX32:
+        return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
+
+    case LB_SETCOLUMNWIDTH16:
+    case LB_SETCOLUMNWIDTH32:
+        return LISTBOX_SetColumnWidth( wnd, descr, wParam );
+
+    case LB_GETITEMRECT16:
+        {
+            RECT32 rect;
+            ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
+            CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
+        }
+        return ret;
+
+    case LB_GETITEMRECT32:
+        return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT32 *)lParam );
+
+    case LB_FINDSTRING16:
+        wParam = (INT32)(INT16)wParam;
+        if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
+        /* fall through */
+    case LB_FINDSTRING32:
+        return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
+
+    case LB_FINDSTRINGEXACT16:
+        wParam = (INT32)(INT16)wParam;
+        if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
+        /* fall through */
+    case LB_FINDSTRINGEXACT32:
+        return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
+
+    case LB_SELECTSTRING16:
+        wParam = (INT32)(INT16)wParam;
+        if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
+        /* fall through */
+    case LB_SELECTSTRING32:
+        {
+            INT32 index = LISTBOX_FindString( wnd, descr, wParam,
+                                              (LPCSTR)lParam, FALSE );
+            if (index == LB_ERR) return LB_ERR;
+            LISTBOX_SetSelection( wnd, descr, index, TRUE,
+                                  (descr->style & LBS_NOTIFY) != 0 );
+            return index;
+        }
+
+    case LB_GETSEL16:
+        wParam = (INT32)(INT16)wParam;
+        /* fall through */
+    case LB_GETSEL32:
+        if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
+            return LB_ERR;
+        return descr->items[wParam].selected;
+
+    case LB_SETSEL16:
+        lParam = (INT32)(INT16)lParam;
+        /* fall through */
+    case LB_SETSEL32:
+        return LISTBOX_SetSelection( wnd, descr, lParam, wParam,
+                                     (descr->style & LBS_NOTIFY) != 0 );
+
+    case LB_SETCURSEL16:
+        wParam = (INT32)(INT16)wParam;
+        /* fall through */
+    case LB_SETCURSEL32:
+        return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
+
+    case LB_GETSELCOUNT16:
+    case LB_GETSELCOUNT32:
+        return LISTBOX_GetSelCount( wnd, descr );
+
+    case LB_GETSELITEMS16:
+        return LISTBOX_GetSelItems16( wnd, descr, wParam,
+                                      (LPINT16)PTR_SEG_TO_LIN(lParam) );
+
+    case LB_GETSELITEMS32:
+        return LISTBOX_GetSelItems32( wnd, descr, wParam, (LPINT32)lParam );
+
+    case LB_SELITEMRANGE16:
+    case LB_SELITEMRANGE32:
+        return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
+                                        HIWORD(lParam), wParam );
+
+    case LB_SELITEMRANGEEX16:
+    case LB_SELITEMRANGEEX32:
+        if ((INT32)lParam >= (INT32)wParam)
+            return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
+        else
+            return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
+
+    case LB_GETHORIZONTALEXTENT16:
+    case LB_GETHORIZONTALEXTENT32:
+        return descr->horz_extent;
+
+    case LB_SETHORIZONTALEXTENT16:
+    case LB_SETHORIZONTALEXTENT32:
+        return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
+
+    case LB_GETANCHORINDEX16:
+    case LB_GETANCHORINDEX32:
+        return descr->anchor_item;
+
+    case LB_SETANCHORINDEX16:
+        wParam = (INT32)(INT16)wParam;
+        /* fall through */
+    case LB_SETANCHORINDEX32:
+        if (((INT32)wParam < -1) || ((INT32)wParam >= descr->nb_items))
+            return LB_ERR;
+        descr->anchor_item = (INT32)wParam;
+        return LB_OKAY;
+
+    case LB_DIR16:
+        return LISTBOX_Directory( wnd, descr, wParam,
+                                  (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
+
+    case LB_DIR32:
+        return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
+
+    case LB_GETLOCALE32:
+        return descr->locale;
+
+    case LB_SETLOCALE32:
+        descr->locale = (LCID)wParam;  /* FIXME: should check for valid lcid */
+        return LB_OKAY;
+
+    case LB_INITSTORAGE32:
+        return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
+
+    case LB_SETCOUNT32:
+        return LISTBOX_SetCount( wnd, descr, (INT32)wParam );
+
+    case LB_SETTABSTOPS16:
+        return LISTBOX_SetTabStops( wnd, descr, (INT32)(INT16)wParam,
+                                    (LPINT32)PTR_SEG_TO_LIN(lParam), TRUE );
+
+    case LB_SETTABSTOPS32:
+        return LISTBOX_SetTabStops( wnd, descr, wParam,
+                                    (LPINT32)lParam, FALSE );
+
+    case LB_CARETON16:
+    case LB_CARETON32:
+        if (descr->caret_on) return LB_OKAY;
+        descr->caret_on = TRUE;
+        if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
+            LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
+        return LB_OKAY;
+
+    case LB_CARETOFF16:
+    case LB_CARETOFF32:
+        if (!descr->caret_on) return LB_OKAY;
+        descr->caret_on = FALSE;
+        if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
+            LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
+        return LB_OKAY;
+
+    case WM_DESTROY:
+        return LISTBOX_Destroy( wnd, descr );
+
+    case WM_ENABLE:
+        InvalidateRect32( hwnd, NULL, TRUE );
+        return 0;
+
+    case WM_SETREDRAW:
+        LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
+        return 0;
+
+    case WM_GETDLGCODE:
+        return DLGC_WANTARROWS | DLGC_WANTCHARS;
+
+    case WM_PAINT:
+        {
+            PAINTSTRUCT32 ps;
+            HDC32 hdc = BeginPaint32( hwnd, &ps );
+            ret = LISTBOX_Paint( wnd, descr, hdc );
+            EndPaint32( hwnd, &ps );
+        }
+        return ret;
+
+    case WM_SIZE:
+        LISTBOX_UpdateSize( wnd, descr );
+        return 0;
+
+    case WM_GETFONT:
+        return descr->font;
+
+    case WM_SETFONT:
+        LISTBOX_SetFont( wnd, descr, (HFONT32)wParam );
+        if (lParam) InvalidateRect32( wnd->hwndSelf, 0, TRUE );
+        return 0;
+
+    case WM_SETFOCUS:
+        descr->caret_on = TRUE;
+        if (descr->focus_item != -1)
+            LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
+        SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
+        return 0;
+
+    case WM_KILLFOCUS:
+        if ((descr->focus_item != -1) && descr->caret_on)
+            LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
+        SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
+        return 0;
+
+    case WM_HSCROLL:
+        return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
+
+    case WM_VSCROLL:
+        return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
+
+    case WM_LBUTTONDOWN:
+        return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
+                                          (INT16)LOWORD(lParam),
+                                          (INT16)HIWORD(lParam) );
+
+    case WM_LBUTTONDBLCLK:
+        if (descr->style & LBS_NOTIFY)
+            SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
+        return 0;
+
+    case WM_MOUSEMOVE:
+        if (GetCapture32() == hwnd)
+            LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
+                                     (INT16)HIWORD(lParam) );
+        return 0;
+
+    case WM_LBUTTONUP:
+        if (LISTBOX_Timer != LB_TIMER_NONE)
+            KillSystemTimer32( hwnd, LB_TIMER_ID );
+        LISTBOX_Timer = LB_TIMER_NONE;
+        if (GetCapture32() == hwnd) ReleaseCapture();
+        return 0;
+
+    case WM_KEYDOWN:
+        return LISTBOX_HandleKeyDown( wnd, descr, wParam );
+
+    case WM_CHAR:
+        return LISTBOX_HandleChar( wnd, descr, wParam );
+
+    case WM_SYSTIMER:
+        return LISTBOX_HandleSystemTimer( wnd, descr );
+
+    case WM_ERASEBKGND:
+        if (IS_OWNERDRAW(descr))
+        {
+            RECT32 rect = { 0, 0, descr->width, descr->height };
+            HBRUSH32 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
+                                              wParam, (LPARAM)wnd->hwndSelf );
+            if (hbrush) FillRect32( (HDC32)wParam, &rect, hbrush );
+        }
+        return 1;
+
+    case WM_DROPFILES:
+        return SendMessage32A( descr->owner, msg, wParam, lParam );
+
+    case WM_DROPOBJECT:
+    case WM_QUERYDROPOBJECT:
+    case WM_DRAGSELECT:
+    case WM_DRAGMOVE:
+    {
+        LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
+        dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
+                                                dragInfo->pt.y );
+        return SendMessage32A( descr->owner, msg, wParam, lParam );
+    }
+
+    default:
+        if ((msg >= WM_USER) && (msg < 0xc000))
+            fprintf(stderr,"Listbox %04x: unknown msg %04x wp %08x lp %08lx\n",
+                    hwnd, msg, wParam, lParam );
+        return DefWindowProc32A( hwnd, msg, wParam, lParam );
+    }
+    return 0;
 }