[PATCH 1/2] comctl32: Introduce ComboBox control

Nikolay Sivov nsivov at codeweavers.com
Tue Feb 13 01:56:36 CST 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/comctl32/Makefile.in  |    1 +
 dlls/comctl32/combo.c      | 2117 ++++++++++++++++++++++++++++++++++++++++++++
 dlls/comctl32/comctl32.h   |    1 +
 dlls/comctl32/commctrl.c   |    2 +
 dlls/comctl32/tests/misc.c |    1 +
 dlls/user32/class.c        |    1 -
 6 files changed, 2122 insertions(+), 1 deletion(-)
 create mode 100644 dlls/comctl32/combo.c

diff --git a/dlls/comctl32/Makefile.in b/dlls/comctl32/Makefile.in
index 03dd808427..080f78c3fe 100644
--- a/dlls/comctl32/Makefile.in
+++ b/dlls/comctl32/Makefile.in
@@ -7,6 +7,7 @@ DELAYIMPORTS = winmm uxtheme
 C_SRCS = \
 	animate.c \
 	button.c \
+	combo.c \
 	comboex.c \
 	comctl32undoc.c \
 	commctrl.c \
diff --git a/dlls/comctl32/combo.c b/dlls/comctl32/combo.c
new file mode 100644
index 0000000000..b478bfca6c
--- /dev/null
+++ b/dlls/comctl32/combo.c
@@ -0,0 +1,2117 @@
+/*
+ * Combo controls
+ *
+ * Copyright 1997 Alex Korobka
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * TODO:
+ *   - ComboBox_[GS]etMinVisible()
+ *   - CB_GETMINVISIBLE, CB_SETMINVISIBLE
+ *   - CB_SETTOPINDEX
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+#define OEMRESOURCE
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "commctrl.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+#include "comctl32.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(combo);
+
+  /* bits in the dwKeyData */
+#define KEYDATA_ALT             0x2000
+#define KEYDATA_PREVSTATE       0x4000
+
+/*
+ * Additional combo box definitions
+ */
+
+#define CB_NOTIFY( lphc, code ) \
+    (SendMessageW((lphc)->owner, WM_COMMAND, \
+                  MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
+
+#define CB_DISABLED( lphc )   (!IsWindowEnabled((lphc)->self))
+#define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
+#define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
+#define CB_HWND( lphc )       ((lphc)->self)
+#define CB_GETTYPE( lphc )    ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
+
+#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
+
+/*
+ * Drawing globals
+ */
+static HBITMAP 	hComboBmp = 0;
+static UINT	CBitHeight, CBitWidth;
+
+/*
+ * Look and feel dependent "constants"
+ */
+
+#define COMBO_YBORDERGAP         5
+#define COMBO_XBORDERSIZE()      2
+#define COMBO_YBORDERSIZE()      2
+#define COMBO_EDITBUTTONSPACE()  0
+#define EDIT_CONTROL_PADDING()   1
+
+#define CBF_DROPPED             0x0001
+#define CBF_BUTTONDOWN          0x0002
+#define CBF_NOROLLUP            0x0004
+#define CBF_MEASUREITEM         0x0008
+#define CBF_FOCUSED             0x0010
+#define CBF_CAPTURE             0x0020
+#define CBF_EDIT                0x0040
+#define CBF_NORESIZE            0x0080
+#define CBF_NOTIFY              0x0100
+#define CBF_NOREDRAW            0x0200
+#define CBF_SELCHANGE           0x0400
+#define CBF_NOEDITNOTIFY        0x1000
+#define CBF_NOLBSELECT          0x2000  /* do not change current selection */
+#define CBF_BEENFOCUSED         0x4000  /* has it ever had focus           */
+#define CBF_EUI                 0x8000
+
+#define ID_CB_LISTBOX           1000
+#define ID_CB_EDIT              1001
+
+typedef struct
+{
+   HWND           self;
+   HWND           owner;
+   UINT           dwStyle;
+   HWND           hWndEdit;
+   HWND           hWndLBox;
+   UINT           wState;
+   HFONT          hFont;
+   RECT           textRect;
+   RECT           buttonRect;
+   RECT           droppedRect;
+   INT            droppedIndex;
+   INT            fixedOwnerDrawHeight;
+   INT            droppedWidth;   /* last two are not used unless set */
+   INT            editHeight;     /* explicitly */
+} HEADCOMBO, *LPHEADCOMBO;
+
+/***********************************************************************
+ *           COMBO_Init
+ *
+ * Load combo button bitmap.
+ */
+static BOOL COMBO_Init(void)
+{
+  HDC		hDC;
+
+  if( hComboBmp ) return TRUE;
+  if( (hDC = CreateCompatibleDC(0)) )
+  {
+    BOOL	bRet = FALSE;
+    if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
+    {
+      BITMAP      bm;
+      HBITMAP     hPrevB;
+      RECT        r;
+
+      GetObjectW( hComboBmp, sizeof(bm), &bm );
+      CBitHeight = bm.bmHeight;
+      CBitWidth  = bm.bmWidth;
+
+      TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
+
+      hPrevB = SelectObject( hDC, hComboBmp);
+      SetRect( &r, 0, 0, CBitWidth, CBitHeight );
+      InvertRect( hDC, &r );
+      SelectObject( hDC, hPrevB );
+      bRet = TRUE;
+    }
+    DeleteDC( hDC );
+    return bRet;
+  }
+  return FALSE;
+}
+
+/***********************************************************************
+ *           COMBO_NCCreate
+ */
+static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
+{
+    LPHEADCOMBO lphc;
+
+    if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
+    {
+        lphc->self = hwnd;
+        SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
+
+       /* some braindead apps do try to use scrollbar/border flags */
+
+	lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
+        SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
+
+	/*
+	 * We also have to remove the client edge style to make sure
+	 * we don't end-up with a non client area.
+	 */
+        SetWindowLongW( hwnd, GWL_EXSTYLE,
+                        GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
+
+	if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
+              lphc->dwStyle |= CBS_HASSTRINGS;
+	if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
+	      lphc->wState |= CBF_NOTIFY;
+
+        TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
+        return TRUE;
+    }
+    return FALSE;
+}
+
+/***********************************************************************
+ *           COMBO_NCDestroy
+ */
+static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
+{
+
+   if( lphc )
+   {
+       TRACE("[%p]: freeing storage\n", lphc->self);
+
+       if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
+   	   DestroyWindow( lphc->hWndLBox );
+
+       SetWindowLongPtrW( lphc->self, 0, 0 );
+       HeapFree( GetProcessHeap(), 0, lphc );
+   }
+   return 0;
+}
+
+/***********************************************************************
+ *           CBGetTextAreaHeight
+ *
+ * This method will calculate the height of the text area of the
+ * combobox.
+ * The height of the text area is set in two ways.
+ * It can be set explicitly through a combobox message or through a
+ * WM_MEASUREITEM callback.
+ * If this is not the case, the height is set to font height + 4px
+ * This height was determined through experimentation.
+ * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
+ */
+static INT CBGetTextAreaHeight(
+  HWND        hwnd,
+  LPHEADCOMBO lphc)
+{
+  INT iTextItemHeight;
+
+  if( lphc->editHeight ) /* explicitly set height */
+  {
+    iTextItemHeight = lphc->editHeight;
+  }
+  else
+  {
+    TEXTMETRICW tm;
+    HDC         hDC       = GetDC(hwnd);
+    HFONT       hPrevFont = 0;
+    INT         baseUnitY;
+
+    if (lphc->hFont)
+      hPrevFont = SelectObject( hDC, lphc->hFont );
+
+    GetTextMetricsW(hDC, &tm);
+
+    baseUnitY = tm.tmHeight;
+
+    if( hPrevFont )
+      SelectObject( hDC, hPrevFont );
+
+    ReleaseDC(hwnd, hDC);
+
+    iTextItemHeight = baseUnitY + 4;
+  }
+
+  /*
+   * Check the ownerdraw case if we haven't asked the parent the size
+   * of the item yet.
+   */
+  if ( CB_OWNERDRAWN(lphc) &&
+       (lphc->wState & CBF_MEASUREITEM) )
+  {
+    MEASUREITEMSTRUCT measureItem;
+    RECT              clientRect;
+    INT               originalItemHeight = iTextItemHeight;
+    UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
+
+    /*
+     * We use the client rect for the width of the item.
+     */
+    GetClientRect(hwnd, &clientRect);
+
+    lphc->wState &= ~CBF_MEASUREITEM;
+
+    /*
+     * Send a first one to measure the size of the text area
+     */
+    measureItem.CtlType    = ODT_COMBOBOX;
+    measureItem.CtlID      = id;
+    measureItem.itemID     = -1;
+    measureItem.itemWidth  = clientRect.right;
+    measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
+    measureItem.itemData   = 0;
+    SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
+    iTextItemHeight = 6 + measureItem.itemHeight;
+
+    /*
+     * Send a second one in the case of a fixed ownerdraw list to calculate the
+     * size of the list items. (we basically do this on behalf of the listbox)
+     */
+    if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
+    {
+      measureItem.CtlType    = ODT_COMBOBOX;
+      measureItem.CtlID      = id;
+      measureItem.itemID     = 0;
+      measureItem.itemWidth  = clientRect.right;
+      measureItem.itemHeight = originalItemHeight;
+      measureItem.itemData   = 0;
+      SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
+      lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
+    }
+
+    /*
+     * Keep the size for the next time
+     */
+    lphc->editHeight = iTextItemHeight;
+  }
+
+  return iTextItemHeight;
+}
+
+/***********************************************************************
+ *           CBForceDummyResize
+ *
+ * The dummy resize is used for listboxes that have a popup to trigger
+ * a re-arranging of the contents of the combobox and the recalculation
+ * of the size of the "real" control window.
+ */
+static void CBForceDummyResize(
+  LPHEADCOMBO lphc)
+{
+  RECT windowRect;
+  int newComboHeight;
+
+  newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
+
+  GetWindowRect(lphc->self, &windowRect);
+
+  /*
+   * We have to be careful, resizing a combobox also has the meaning that the
+   * dropped rect will be resized. In this case, we want to trigger a resize
+   * to recalculate layout but we don't want to change the dropped rectangle
+   * So, we pass the height of text area of control as the height.
+   * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
+   * message.
+   */
+  SetWindowPos( lphc->self,
+		NULL,
+		0, 0,
+		windowRect.right  - windowRect.left,
+		newComboHeight,
+		SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
+}
+
+/***********************************************************************
+ *           CBCalcPlacement
+ *
+ * Set up component coordinates given valid lphc->RectCombo.
+ */
+static void CBCalcPlacement(
+  HWND        hwnd,
+  LPHEADCOMBO lphc,
+  LPRECT      lprEdit,
+  LPRECT      lprButton,
+  LPRECT      lprLB)
+{
+  /*
+   * Again, start with the client rectangle.
+   */
+  GetClientRect(hwnd, lprEdit);
+
+  /*
+   * Remove the borders
+   */
+  InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
+
+  /*
+   * Chop off the bottom part to fit with the height of the text area.
+   */
+  lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
+
+  /*
+   * The button starts the same vertical position as the text area.
+   */
+  CopyRect(lprButton, lprEdit);
+
+  /*
+   * If the combobox is "simple" there is no button.
+   */
+  if( CB_GETTYPE(lphc) == CBS_SIMPLE )
+    lprButton->left = lprButton->right = lprButton->bottom = 0;
+  else
+  {
+    /*
+     * Let's assume the combobox button is the same width as the
+     * scrollbar button.
+     * size the button horizontally and cut-off the text area.
+     */
+    lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
+    lprEdit->right  = lprButton->left;
+  }
+
+  /*
+   * In the case of a dropdown, there is an additional spacing between the
+   * text area and the button.
+   */
+  if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+  {
+    lprEdit->right -= COMBO_EDITBUTTONSPACE();
+  }
+
+  /*
+   * If we have an edit control, we space it away from the borders slightly.
+   */
+  if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
+  {
+    InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
+  }
+
+  /*
+   * Adjust the size of the listbox popup.
+   */
+  if( CB_GETTYPE(lphc) == CBS_SIMPLE )
+  {
+    /*
+     * Use the client rectangle to initialize the listbox rectangle
+     */
+    GetClientRect(hwnd, lprLB);
+
+    /*
+     * Then, chop-off the top part.
+     */
+    lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
+  }
+  else
+  {
+    /*
+     * Make sure the dropped width is as large as the combobox itself.
+     */
+    if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
+    {
+      lprLB->right  = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
+
+      /*
+       * In the case of a dropdown, the popup listbox is offset to the right.
+       * so, we want to make sure it's flush with the right side of the
+       * combobox
+       */
+      if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+	lprLB->right -= COMBO_EDITBUTTONSPACE();
+    }
+    else
+       lprLB->right = lprLB->left + lphc->droppedWidth;
+  }
+
+  /* don't allow negative window width */
+  if (lprEdit->right < lprEdit->left)
+    lprEdit->right = lprEdit->left;
+
+  TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
+
+  TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
+
+  TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
+}
+
+/***********************************************************************
+ *           CBGetDroppedControlRect
+ */
+static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
+{
+    /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
+     of the combo box and the lower right corner of the listbox */
+
+    GetWindowRect(lphc->self, lpRect);
+
+    lpRect->right =  lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
+    lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
+
+}
+
+/***********************************************************************
+ *           COMBO_Create
+ */
+static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style )
+{
+  static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
+  static const WCHAR editName[] = {'E','d','i','t',0};
+
+  if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
+  if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
+
+  lphc->owner = hwndParent;
+
+  /*
+   * The item height and dropped width are not set when the control
+   * is created.
+   */
+  lphc->droppedWidth = lphc->editHeight = 0;
+
+  /*
+   * The first time we go through, we want to measure the ownerdraw item
+   */
+  lphc->wState |= CBF_MEASUREITEM;
+
+  /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
+
+  if( lphc->owner || !(style & WS_VISIBLE) )
+  {
+      UINT lbeStyle   = 0;
+      UINT lbeExStyle = 0;
+
+      /*
+       * Initialize the dropped rect to the size of the client area of the
+       * control and then, force all the areas of the combobox to be
+       * recalculated.
+       */
+      GetClientRect( hwnd, &lphc->droppedRect );
+      CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
+
+      /*
+       * Adjust the position of the popup listbox if it's necessary
+       */
+      if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
+      {
+	lphc->droppedRect.top   = lphc->textRect.bottom + COMBO_YBORDERSIZE();
+
+	/*
+	 * If it's a dropdown, the listbox is offset
+	 */
+	if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+	  lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
+
+        if (lphc->droppedRect.bottom < lphc->droppedRect.top)
+            lphc->droppedRect.bottom = lphc->droppedRect.top;
+        if (lphc->droppedRect.right < lphc->droppedRect.left)
+            lphc->droppedRect.right = lphc->droppedRect.left;
+        MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
+      }
+
+      /* create listbox popup */
+
+      lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
+                 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
+
+      if( lphc->dwStyle & CBS_SORT )
+	lbeStyle |= LBS_SORT;
+      if( lphc->dwStyle & CBS_HASSTRINGS )
+	lbeStyle |= LBS_HASSTRINGS;
+      if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
+	lbeStyle |= LBS_NOINTEGRALHEIGHT;
+      if( lphc->dwStyle & CBS_DISABLENOSCROLL )
+	lbeStyle |= LBS_DISABLENOSCROLL;
+
+      if( CB_GETTYPE(lphc) == CBS_SIMPLE ) 	/* child listbox */
+      {
+	lbeStyle |= WS_VISIBLE;
+
+	/*
+	 * In win 95 look n feel, the listbox in the simple combobox has
+	 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
+	 */
+	lbeStyle   &= ~WS_BORDER;
+	lbeExStyle |= WS_EX_CLIENTEDGE;
+      }
+      else
+      {
+        lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
+      }
+
+      lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
+              lphc->droppedRect.left, lphc->droppedRect.top, lphc->droppedRect.right - lphc->droppedRect.left,
+              lphc->droppedRect.bottom - lphc->droppedRect.top, hwnd, (HMENU)ID_CB_LISTBOX,
+              (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
+      if( lphc->hWndLBox )
+      {
+	  BOOL	bEdit = TRUE;
+	  lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
+
+	  if( lphc->wState & CBF_EDIT )
+	  {
+	      if( lphc->dwStyle & CBS_OEMCONVERT )
+		  lbeStyle |= ES_OEMCONVERT;
+	      if( lphc->dwStyle & CBS_AUTOHSCROLL )
+		  lbeStyle |= ES_AUTOHSCROLL;
+	      if( lphc->dwStyle & CBS_LOWERCASE )
+		  lbeStyle |= ES_LOWERCASE;
+	      else if( lphc->dwStyle & CBS_UPPERCASE )
+		  lbeStyle |= ES_UPPERCASE;
+
+              if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
+
+              lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
+                                               lphc->textRect.left, lphc->textRect.top,
+                                               lphc->textRect.right - lphc->textRect.left,
+                                               lphc->textRect.bottom - lphc->textRect.top,
+                                               hwnd, (HMENU)ID_CB_EDIT,
+                                               (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
+	      if( !lphc->hWndEdit )
+		bEdit = FALSE;
+	  }
+
+          if( bEdit )
+	  {
+	    if( CB_GETTYPE(lphc) != CBS_SIMPLE )
+	    {
+              /* Now do the trick with parent */
+	      SetParent(lphc->hWndLBox, HWND_DESKTOP);
+              /*
+               * If the combo is a dropdown, we must resize the control
+	       * to fit only the text area and button. To do this,
+	       * we send a dummy resize and the WM_WINDOWPOSCHANGING message
+	       * will take care of setting the height for us.
+               */
+	      CBForceDummyResize(lphc);
+	    }
+
+	    TRACE("init done\n");
+	    return 0;
+	  }
+	  ERR("edit control failure.\n");
+      } else ERR("listbox failure.\n");
+  } else ERR("no owner for visible combo.\n");
+
+  /* CreateWindow() will send WM_NCDESTROY to cleanup */
+
+  return -1;
+}
+
+/***********************************************************************
+ *           CBPaintButton
+ *
+ * Paint combo button (normal, pressed, and disabled states).
+ */
+static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
+{
+    UINT buttonState = DFCS_SCROLLCOMBOBOX;
+
+    if( lphc->wState & CBF_NOREDRAW )
+      return;
+
+
+    if (lphc->wState & CBF_BUTTONDOWN)
+	buttonState |= DFCS_PUSHED;
+
+    if (CB_DISABLED(lphc))
+	buttonState |= DFCS_INACTIVE;
+
+    DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
+}
+
+/***********************************************************************
+ *           COMBO_PrepareColors
+ *
+ * This method will sent the appropriate WM_CTLCOLOR message to
+ * prepare and setup the colors for the combo's DC.
+ *
+ * It also returns the brush to use for the background.
+ */
+static HBRUSH COMBO_PrepareColors(
+        LPHEADCOMBO lphc,
+        HDC         hDC)
+{
+    HBRUSH  hBkgBrush;
+
+    /*
+     * Get the background brush for this control.
+     */
+    if (CB_DISABLED(lphc))
+    {
+        hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
+                (WPARAM)hDC, (LPARAM)lphc->self );
+
+        /*
+         * We have to change the text color since WM_CTLCOLORSTATIC will
+         * set it to the "enabled" color. This is the same behavior as the
+         * edit control
+         */
+        SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
+    }
+    else
+    {
+        /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
+        hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
+                (WPARAM)hDC, (LPARAM)lphc->self );
+    }
+
+    /*
+     * Catch errors.
+     */
+    if( !hBkgBrush )
+        hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
+
+    return hBkgBrush;
+}
+
+/***********************************************************************
+ *           CBPaintText
+ *
+ * Paint CBS_DROPDOWNLIST text field / update edit control contents.
+ */
+static void CBPaintText(
+  LPHEADCOMBO lphc,
+  HDC         hdc_paint)
+{
+   RECT rectEdit = lphc->textRect;
+   INT	id, size = 0;
+   LPWSTR pText = NULL;
+
+   TRACE("\n");
+
+   /* follow Windows combobox that sends a bunch of text
+    * inquiries to its listbox while processing WM_PAINT. */
+
+   if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
+   {
+        size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
+	if (size == LB_ERR)
+	  FIXME("LB_ERR probably not handled yet\n");
+        if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
+	{
+            /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
+           size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
+	    pText[size] = '\0';	/* just in case */
+	} else return;
+   }
+
+   if( lphc->wState & CBF_EDIT )
+   {
+        static const WCHAR empty_stringW[] = { 0 };
+	if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
+	if( lphc->wState & CBF_FOCUSED )
+           SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
+   }
+   else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
+   {
+     /* paint text field ourselves */
+     HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
+     UINT itemState = ODS_COMBOBOXEDIT;
+     HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
+     HBRUSH hPrevBrush, hBkgBrush;
+
+     /*
+      * Give ourselves some space.
+      */
+     InflateRect( &rectEdit, -1, -1 );
+
+     hBkgBrush = COMBO_PrepareColors( lphc, hdc );
+     hPrevBrush = SelectObject( hdc, hBkgBrush );
+     FillRect( hdc, &rectEdit, hBkgBrush );
+
+     if( CB_OWNERDRAWN(lphc) )
+     {
+       DRAWITEMSTRUCT dis;
+       HRGN           clipRegion;
+       UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
+
+       /* setup state for DRAWITEM message. Owner will highlight */
+       if ( (lphc->wState & CBF_FOCUSED) &&
+	    !(lphc->wState & CBF_DROPPED) )
+	   itemState |= ODS_SELECTED | ODS_FOCUS;
+
+       if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
+
+       dis.CtlType	= ODT_COMBOBOX;
+       dis.CtlID	= ctlid;
+       dis.hwndItem	= lphc->self;
+       dis.itemAction	= ODA_DRAWENTIRE;
+       dis.itemID	= id;
+       dis.itemState	= itemState;
+       dis.hDC		= hdc;
+       dis.rcItem	= rectEdit;
+       dis.itemData     = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
+
+       /*
+	* Clip the DC and have the parent draw the item.
+	*/
+       clipRegion = set_control_clipping( hdc, &rectEdit );
+
+       SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
+
+       SelectClipRgn( hdc, clipRegion );
+       if (clipRegion) DeleteObject( clipRegion );
+     }
+     else
+     {
+       static const WCHAR empty_stringW[] = { 0 };
+
+       if ( (lphc->wState & CBF_FOCUSED) &&
+	    !(lphc->wState & CBF_DROPPED) ) {
+
+	   /* highlight */
+	   FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
+	   SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
+	   SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
+       }
+
+       ExtTextOutW( hdc,
+		    rectEdit.left + 1,
+		    rectEdit.top + 1,
+		    ETO_OPAQUE | ETO_CLIPPED,
+		    &rectEdit,
+		    pText ? pText : empty_stringW , size, NULL );
+
+       if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
+	 DrawFocusRect( hdc, &rectEdit );
+     }
+
+     if( hPrevFont )
+       SelectObject(hdc, hPrevFont );
+
+    if( hPrevBrush )
+        SelectObject( hdc, hPrevBrush );
+
+     if( !hdc_paint )
+       ReleaseDC( lphc->self, hdc );
+   }
+   HeapFree( GetProcessHeap(), 0, pText );
+}
+
+/***********************************************************************
+ *           CBPaintBorder
+ */
+static void CBPaintBorder(
+  HWND            hwnd,
+  const HEADCOMBO *lphc,
+  HDC             hdc)
+{
+  RECT clientRect;
+
+  if (CB_GETTYPE(lphc) != CBS_SIMPLE)
+  {
+    GetClientRect(hwnd, &clientRect);
+  }
+  else
+  {
+    clientRect = lphc->textRect;
+
+    InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
+    InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
+  }
+
+  DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
+}
+
+/***********************************************************************
+ *           COMBO_Paint
+ */
+static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
+{
+  PAINTSTRUCT ps;
+  HDC 	hDC;
+
+  hDC = (hParamDC) ? hParamDC
+		   : BeginPaint( lphc->self, &ps);
+
+  TRACE("hdc=%p\n", hDC);
+
+  if( hDC && !(lphc->wState & CBF_NOREDRAW) )
+  {
+      HBRUSH	hPrevBrush, hBkgBrush;
+
+      /*
+       * Retrieve the background brush and select it in the
+       * DC.
+       */
+      hBkgBrush = COMBO_PrepareColors(lphc, hDC);
+
+      hPrevBrush = SelectObject( hDC, hBkgBrush );
+      if (!(lphc->wState & CBF_EDIT))
+        FillRect(hDC, &lphc->textRect, hBkgBrush);
+
+      /*
+       * In non 3.1 look, there is a sunken border on the combobox
+       */
+      CBPaintBorder(lphc->self, lphc, hDC);
+
+      if( !IsRectEmpty(&lphc->buttonRect) )
+      {
+	CBPaintButton(lphc, hDC, lphc->buttonRect);
+      }
+
+      /* paint the edit control padding area */
+      if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
+      {
+          RECT rPadEdit = lphc->textRect;
+
+          InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
+
+          FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
+      }
+
+      if( !(lphc->wState & CBF_EDIT) )
+	CBPaintText( lphc, hDC );
+
+      if( hPrevBrush )
+	SelectObject( hDC, hPrevBrush );
+  }
+
+  if( !hParamDC )
+    EndPaint(lphc->self, &ps);
+
+  return 0;
+}
+
+/***********************************************************************
+ *           CBUpdateLBox
+ *
+ * Select listbox entry according to the contents of the edit control.
+ */
+static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
+{
+   INT	length, idx;
+   LPWSTR pText = NULL;
+
+   idx = LB_ERR;
+   length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
+
+   if( length > 0 )
+       pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
+
+   TRACE("\t edit text length %i\n", length );
+
+   if( pText )
+   {
+       GetWindowTextW( lphc->hWndEdit, pText, length + 1);
+       idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
+       HeapFree( GetProcessHeap(), 0, pText );
+   }
+
+   SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
+
+   /* probably superfluous but Windows sends this too */
+   SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
+   SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
+
+   return idx;
+}
+
+/***********************************************************************
+ *           CBUpdateEdit
+ *
+ * Copy a listbox entry to the edit control.
+ */
+static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
+{
+   INT	length;
+   LPWSTR pText = NULL;
+   static const WCHAR empty_stringW[] = { 0 };
+
+   TRACE("\t %i\n", index );
+
+   if( index >= 0 ) /* got an entry */
+   {
+       length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
+       if( length != LB_ERR)
+       {
+	   if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
+	   {
+               SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
+	   }
+       }
+   }
+
+   if( CB_HASSTRINGS(lphc) )
+   {
+      lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
+      SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
+      lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
+   }
+
+   if( lphc->wState & CBF_FOCUSED )
+      SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
+
+   HeapFree( GetProcessHeap(), 0, pText );
+}
+
+/***********************************************************************
+ *           CBDropDown
+ *
+ * Show listbox popup.
+ */
+static void CBDropDown( LPHEADCOMBO lphc )
+{
+    HMONITOR monitor;
+    MONITORINFO mon_info;
+   RECT rect,r;
+   int nItems;
+   int nDroppedHeight;
+
+   TRACE("[%p]: drop down\n", lphc->self);
+
+   CB_NOTIFY( lphc, CBN_DROPDOWN );
+
+   /* set selection */
+
+   lphc->wState |= CBF_DROPPED;
+   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+   {
+       lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
+
+       /* Update edit only if item is in the list */
+       if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
+	 CBUpdateEdit( lphc, lphc->droppedIndex );
+   }
+   else
+   {
+       lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
+
+       SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
+                    lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
+       SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
+   }
+
+   /* now set popup position */
+   GetWindowRect( lphc->self, &rect );
+
+   /*
+    * If it's a dropdown, the listbox is offset
+    */
+   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+     rect.left += COMBO_EDITBUTTONSPACE();
+
+  /* if the dropped height is greater than the total height of the dropped
+     items list, then force the drop down list height to be the total height
+     of the items in the dropped list */
+
+  /* And Remove any extra space (Best Fit) */
+   nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
+  /* if listbox length has been set directly by its handle */
+   GetWindowRect(lphc->hWndLBox, &r);
+   if (nDroppedHeight < r.bottom - r.top)
+       nDroppedHeight = r.bottom - r.top;
+   nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
+
+   if (nItems > 0)
+   {
+      int nHeight;
+      int nIHeight;
+
+      nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
+
+      nHeight = nIHeight*nItems;
+
+      if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
+         nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
+
+      if (nDroppedHeight < nHeight)
+      {
+            if (nItems < 5)
+                nDroppedHeight = (nItems+1)*nIHeight;
+            else if (nDroppedHeight < 6*nIHeight)
+                nDroppedHeight = 6*nIHeight;
+      }
+   }
+
+   r.left = rect.left;
+   r.top = rect.bottom;
+   r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
+   r.bottom = r.top + nDroppedHeight;
+
+   /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
+   monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
+   mon_info.cbSize = sizeof(mon_info);
+   GetMonitorInfoW( monitor, &mon_info );
+
+   if (r.bottom > mon_info.rcWork.bottom)
+   {
+       r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
+       r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
+   }
+
+   SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
+                 SWP_NOACTIVATE | SWP_SHOWWINDOW );
+
+
+   if( !(lphc->wState & CBF_NOREDRAW) )
+     RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
+			   RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
+
+   EnableWindow( lphc->hWndLBox, TRUE );
+   if (GetCapture() != lphc->self)
+      SetCapture(lphc->hWndLBox);
+}
+
+/***********************************************************************
+ *           CBRollUp
+ *
+ * Hide listbox popup.
+ */
+static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
+{
+   HWND	hWnd = lphc->self;
+
+   TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
+	 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
+
+   CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
+
+   if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
+   {
+
+       if( lphc->wState & CBF_DROPPED )
+       {
+	   RECT	rect;
+
+	   lphc->wState &= ~CBF_DROPPED;
+	   ShowWindow( lphc->hWndLBox, SW_HIDE );
+
+           if(GetCapture() == lphc->hWndLBox)
+           {
+               ReleaseCapture();
+           }
+
+	   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+	   {
+	       rect = lphc->buttonRect;
+	   }
+	   else
+           {
+	       if( bButton )
+	       {
+		 UnionRect( &rect,
+			    &lphc->buttonRect,
+			    &lphc->textRect);
+	       }
+	       else
+		 rect = lphc->textRect;
+
+	       bButton = TRUE;
+	   }
+
+	   if( bButton && !(lphc->wState & CBF_NOREDRAW) )
+	       RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
+			       RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
+	   CB_NOTIFY( lphc, CBN_CLOSEUP );
+       }
+   }
+}
+
+/***********************************************************************
+ *           COMBO_FlipListbox
+ *
+ * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
+ */
+BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
+{
+   if( lphc->wState & CBF_DROPPED )
+   {
+       CBRollUp( lphc, ok, bRedrawButton );
+       return FALSE;
+   }
+
+   CBDropDown( lphc );
+   return TRUE;
+}
+
+/***********************************************************************
+ *           CBRepaintButton
+ */
+static void CBRepaintButton( LPHEADCOMBO lphc )
+   {
+  InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
+  UpdateWindow(lphc->self);
+}
+
+/***********************************************************************
+ *           COMBO_SetFocus
+ */
+static void COMBO_SetFocus( LPHEADCOMBO lphc )
+{
+   if( !(lphc->wState & CBF_FOCUSED) )
+   {
+       if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
+           SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
+
+       /* This is wrong. Message sequences seem to indicate that this
+          is set *after* the notify. */
+       /* lphc->wState |= CBF_FOCUSED;  */
+
+       if( !(lphc->wState & CBF_EDIT) )
+	 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
+
+       CB_NOTIFY( lphc, CBN_SETFOCUS );
+       lphc->wState |= CBF_FOCUSED;
+   }
+}
+
+/***********************************************************************
+ *           COMBO_KillFocus
+ */
+static void COMBO_KillFocus( LPHEADCOMBO lphc )
+{
+   HWND	hWnd = lphc->self;
+
+   if( lphc->wState & CBF_FOCUSED )
+   {
+       CBRollUp( lphc, FALSE, TRUE );
+       if( IsWindow( hWnd ) )
+       {
+           if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
+               SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
+
+ 	   lphc->wState &= ~CBF_FOCUSED;
+
+           /* redraw text */
+	   if( !(lphc->wState & CBF_EDIT) )
+	     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
+
+           CB_NOTIFY( lphc, CBN_KILLFOCUS );
+       }
+   }
+}
+
+/***********************************************************************
+ *           COMBO_Command
+ */
+static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
+{
+   if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
+   {
+       /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
+
+       switch( HIWORD(wParam) >> 8 )
+       {
+	   case (EN_SETFOCUS >> 8):
+
+               TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
+
+		COMBO_SetFocus( lphc );
+	        break;
+
+	   case (EN_KILLFOCUS >> 8):
+
+               TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
+
+		/* NOTE: it seems that Windows' edit control sends an
+		 * undocumented message WM_USER + 0x1B instead of this
+		 * notification (only when it happens to be a part of
+		 * the combo). ?? - AK.
+		 */
+
+		COMBO_KillFocus( lphc );
+		break;
+
+
+	   case (EN_CHANGE >> 8):
+	       /*
+	        * In some circumstances (when the selection of the combobox
+		* is changed for example) we don't want the EN_CHANGE notification
+		* to be forwarded to the parent of the combobox. This code
+		* checks a flag that is set in these occasions and ignores the
+		* notification.
+	        */
+		if (lphc->wState & CBF_NOLBSELECT)
+		{
+		  lphc->wState &= ~CBF_NOLBSELECT;
+		}
+		else
+		{
+		  CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
+		}
+
+	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
+		  CB_NOTIFY( lphc, CBN_EDITCHANGE );
+		break;
+
+	   case (EN_UPDATE >> 8):
+	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
+		  CB_NOTIFY( lphc, CBN_EDITUPDATE );
+		break;
+
+	   case (EN_ERRSPACE >> 8):
+		CB_NOTIFY( lphc, CBN_ERRSPACE );
+       }
+   }
+   else if( lphc->hWndLBox == hWnd )
+   {
+       switch( (short)HIWORD(wParam) )
+       {
+	   case LBN_ERRSPACE:
+		CB_NOTIFY( lphc, CBN_ERRSPACE );
+		break;
+
+	   case LBN_DBLCLK:
+		CB_NOTIFY( lphc, CBN_DBLCLK );
+		break;
+
+	   case LBN_SELCHANGE:
+	   case LBN_SELCANCEL:
+
+                TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
+
+                /* do not roll up if selection is being tracked
+                 * by arrow keys in the dropdown listbox */
+                if (!(lphc->wState & CBF_NOROLLUP))
+                {
+                    CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
+                }
+                else lphc->wState &= ~CBF_NOROLLUP;
+
+		CB_NOTIFY( lphc, CBN_SELCHANGE );
+
+		if( HIWORD(wParam) == LBN_SELCHANGE)
+		{
+		   if( lphc->wState & CBF_EDIT )
+		       lphc->wState |= CBF_NOLBSELECT;
+		   CBPaintText( lphc, NULL );
+		}
+                break;
+
+	   case LBN_SETFOCUS:
+	   case LBN_KILLFOCUS:
+		/* nothing to do here since ComboLBox always resets the focus to its
+		 * combo/edit counterpart */
+		 break;
+       }
+   }
+   return 0;
+}
+
+/***********************************************************************
+ *           COMBO_ItemOp
+ *
+ * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
+ */
+static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
+{
+   HWND hWnd = lphc->self;
+   UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
+
+   TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
+
+   switch( msg )
+   {
+   case WM_DELETEITEM:
+       {
+           DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
+           lpIS->CtlType  = ODT_COMBOBOX;
+           lpIS->CtlID    = id;
+           lpIS->hwndItem = hWnd;
+           break;
+       }
+   case WM_DRAWITEM:
+       {
+           DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
+           lpIS->CtlType  = ODT_COMBOBOX;
+           lpIS->CtlID    = id;
+           lpIS->hwndItem = hWnd;
+           break;
+       }
+   case WM_COMPAREITEM:
+       {
+           COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
+           lpIS->CtlType  = ODT_COMBOBOX;
+           lpIS->CtlID    = id;
+           lpIS->hwndItem = hWnd;
+           break;
+       }
+   case WM_MEASUREITEM:
+       {
+           MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
+           lpIS->CtlType  = ODT_COMBOBOX;
+           lpIS->CtlID    = id;
+           break;
+       }
+   }
+   return SendMessageW(lphc->owner, msg, id, lParam);
+}
+
+
+/***********************************************************************
+ *           COMBO_GetTextW
+ */
+static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
+{
+    INT length;
+
+    if( lphc->wState & CBF_EDIT )
+        return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
+
+    /* get it from the listbox */
+
+    if (!count || !buf) return 0;
+    if( lphc->hWndLBox )
+    {
+        INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
+        if (idx == LB_ERR) goto error;
+        length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
+        if (length == LB_ERR) goto error;
+
+        /* 'length' is without the terminating character */
+        if (length >= count)
+        {
+            LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
+            if (!lpBuffer) goto error;
+            length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
+
+            /* truncate if buffer is too short */
+            if (length != LB_ERR)
+            {
+                lstrcpynW( buf, lpBuffer, count );
+                length = count;
+            }
+            HeapFree( GetProcessHeap(), 0, lpBuffer );
+        }
+        else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
+
+        if (length == LB_ERR) return 0;
+        return length;
+    }
+
+ error:  /* error - truncate string, return zero */
+    buf[0] = 0;
+    return 0;
+}
+
+/***********************************************************************
+ *           CBResetPos
+ *
+ * This function sets window positions according to the updated
+ * component placement struct.
+ */
+static void CBResetPos(
+  LPHEADCOMBO lphc,
+  const RECT  *rectEdit,
+  const RECT  *rectLB,
+  BOOL        bRedraw)
+{
+   BOOL	bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
+
+   /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
+    * sizing messages */
+
+   if( lphc->wState & CBF_EDIT )
+     SetWindowPos( lphc->hWndEdit, 0,
+		   rectEdit->left, rectEdit->top,
+		   rectEdit->right - rectEdit->left,
+		   rectEdit->bottom - rectEdit->top,
+                       SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
+
+   SetWindowPos( lphc->hWndLBox, 0,
+		 rectLB->left, rectLB->top,
+                 rectLB->right - rectLB->left,
+		 rectLB->bottom - rectLB->top,
+		   SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
+
+   if( bDrop )
+   {
+       if( lphc->wState & CBF_DROPPED )
+       {
+           lphc->wState &= ~CBF_DROPPED;
+           ShowWindow( lphc->hWndLBox, SW_HIDE );
+       }
+
+       if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
+           RedrawWindow( lphc->self, NULL, 0,
+                           RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
+   }
+}
+
+
+/***********************************************************************
+ *           COMBO_Size
+ */
+static void COMBO_Size( LPHEADCOMBO lphc )
+{
+  /*
+   * Those controls are always the same height. So we have to make sure
+   * they are not resized to another value.
+   */
+  if( CB_GETTYPE(lphc) != CBS_SIMPLE )
+  {
+    int newComboHeight, curComboHeight, curComboWidth;
+    RECT rc;
+
+    GetWindowRect(lphc->self, &rc);
+    curComboHeight = rc.bottom - rc.top;
+    curComboWidth = rc.right - rc.left;
+    newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE();
+
+    /*
+     * Resizing a combobox has another side effect, it resizes the dropped
+     * rectangle as well. However, it does it only if the new height for the
+     * combobox is more than the height it should have. In other words,
+     * if the application resizing the combobox only had the intention to resize
+     * the actual control, for example, to do the layout of a dialog that is
+     * resized, the height of the dropdown is not changed.
+     */
+    if( curComboHeight > newComboHeight )
+    {
+      TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
+            curComboHeight, newComboHeight, lphc->droppedRect.bottom,
+            lphc->droppedRect.top);
+      lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
+    }
+    /*
+     * Restore original height
+     */
+    if( curComboHeight != newComboHeight )
+      SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
+            SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW);
+  }
+
+  CBCalcPlacement(lphc->self,
+		  lphc,
+		  &lphc->textRect,
+		  &lphc->buttonRect,
+		  &lphc->droppedRect);
+
+  CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
+}
+
+
+/***********************************************************************
+ *           COMBO_Font
+ */
+static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
+{
+  /*
+   * Set the font
+   */
+  lphc->hFont = hFont;
+
+  /*
+   * Propagate to owned windows.
+   */
+  if( lphc->wState & CBF_EDIT )
+      SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
+  SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
+
+  /*
+   * Redo the layout of the control.
+   */
+  if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
+  {
+    CBCalcPlacement(lphc->self,
+		    lphc,
+		    &lphc->textRect,
+		    &lphc->buttonRect,
+		    &lphc->droppedRect);
+
+    CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
+  }
+  else
+  {
+    CBForceDummyResize(lphc);
+  }
+}
+
+
+/***********************************************************************
+ *           COMBO_SetItemHeight
+ */
+static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
+{
+   LRESULT	lRet = CB_ERR;
+
+   if( index == -1 ) /* set text field height */
+   {
+       if( height < 32768 )
+       {
+           lphc->editHeight = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
+
+	 /*
+	  * Redo the layout of the control.
+	  */
+	 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
+	 {
+	   CBCalcPlacement(lphc->self,
+			   lphc,
+			   &lphc->textRect,
+			   &lphc->buttonRect,
+			   &lphc->droppedRect);
+
+	   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
+	 }
+	 else
+	 {
+	   CBForceDummyResize(lphc);
+	 }
+
+	   lRet = height;
+       }
+   }
+   else if ( CB_OWNERDRAWN(lphc) )	/* set listbox item height */
+       lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
+   return lRet;
+}
+
+/***********************************************************************
+ *           COMBO_SelectString
+ */
+static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText)
+{
+   INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
+   if( index >= 0 )
+   {
+     if( lphc->wState & CBF_EDIT )
+       CBUpdateEdit( lphc, index );
+     else
+     {
+       InvalidateRect(lphc->self, &lphc->textRect, TRUE);
+     }
+   }
+   return (LRESULT)index;
+}
+
+/***********************************************************************
+ *           COMBO_LButtonDown
+ */
+static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
+{
+   POINT     pt;
+   BOOL      bButton;
+   HWND      hWnd = lphc->self;
+
+   pt.x = (short)LOWORD(lParam);
+   pt.y = (short)HIWORD(lParam);
+   bButton = PtInRect(&lphc->buttonRect, pt);
+
+   if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
+       (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
+   {
+       lphc->wState |= CBF_BUTTONDOWN;
+       if( lphc->wState & CBF_DROPPED )
+       {
+	   /* got a click to cancel selection */
+
+           lphc->wState &= ~CBF_BUTTONDOWN;
+           CBRollUp( lphc, TRUE, FALSE );
+	   if( !IsWindow( hWnd ) ) return;
+
+           if( lphc->wState & CBF_CAPTURE )
+           {
+               lphc->wState &= ~CBF_CAPTURE;
+               ReleaseCapture();
+           }
+       }
+       else
+       {
+	   /* drop down the listbox and start tracking */
+
+           lphc->wState |= CBF_CAPTURE;
+           SetCapture( hWnd );
+           CBDropDown( lphc );
+       }
+       if( bButton ) CBRepaintButton( lphc );
+   }
+}
+
+/***********************************************************************
+ *           COMBO_LButtonUp
+ *
+ * Release capture and stop tracking if needed.
+ */
+static void COMBO_LButtonUp( LPHEADCOMBO lphc )
+{
+   if( lphc->wState & CBF_CAPTURE )
+   {
+       lphc->wState &= ~CBF_CAPTURE;
+       if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
+       {
+	   INT index = CBUpdateLBox( lphc, TRUE );
+	   /* Update edit only if item is in the list */
+	   if(index >= 0)
+	   {
+	       lphc->wState |= CBF_NOLBSELECT;
+	       CBUpdateEdit( lphc, index );
+	       lphc->wState &= ~CBF_NOLBSELECT;
+	   }
+       }
+       ReleaseCapture();
+       SetCapture(lphc->hWndLBox);
+   }
+
+   if( lphc->wState & CBF_BUTTONDOWN )
+   {
+       lphc->wState &= ~CBF_BUTTONDOWN;
+       CBRepaintButton( lphc );
+   }
+}
+
+/***********************************************************************
+ *           COMBO_MouseMove
+ *
+ * Two things to do - track combo button and release capture when
+ * pointer goes into the listbox.
+ */
+static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
+{
+   POINT  pt;
+   RECT   lbRect;
+
+   pt.x = (short)LOWORD(lParam);
+   pt.y = (short)HIWORD(lParam);
+
+   if( lphc->wState & CBF_BUTTONDOWN )
+   {
+     BOOL bButton;
+
+     bButton = PtInRect(&lphc->buttonRect, pt);
+
+     if( !bButton )
+     {
+       lphc->wState &= ~CBF_BUTTONDOWN;
+       CBRepaintButton( lphc );
+     }
+   }
+
+   GetClientRect( lphc->hWndLBox, &lbRect );
+   MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
+   if( PtInRect(&lbRect, pt) )
+   {
+       lphc->wState &= ~CBF_CAPTURE;
+       ReleaseCapture();
+       if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
+
+       /* hand over pointer tracking */
+       SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
+   }
+}
+
+static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
+{
+    if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
+        return FALSE;
+
+    pcbi->rcItem = lphc->textRect;
+    pcbi->rcButton = lphc->buttonRect;
+    pcbi->stateButton = 0;
+    if (lphc->wState & CBF_BUTTONDOWN)
+        pcbi->stateButton |= STATE_SYSTEM_PRESSED;
+    if (IsRectEmpty(&lphc->buttonRect))
+        pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
+    pcbi->hwndCombo = lphc->self;
+    pcbi->hwndItem = lphc->hWndEdit;
+    pcbi->hwndList = lphc->hWndLBox;
+    return TRUE;
+}
+
+LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+    HEADCOMBO *lphc = (HEADCOMBO *)GetWindowLongPtrW( hwnd, 0 );
+
+    TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", hwnd, message, wParam, lParam );
+
+    if (!IsWindow(hwnd)) return 0;
+
+    if (lphc || message == WM_NCCREATE)
+    switch(message)
+    {
+    case WM_NCCREATE:
+    {
+        LONG style = ((CREATESTRUCTW *)lParam)->style;
+        return COMBO_NCCreate(hwnd, style);
+    }
+
+    case WM_NCDESTROY:
+        COMBO_NCDestroy(lphc);
+        break;/* -> DefWindowProc */
+
+    case WM_CREATE:
+    {
+        HWND hwndParent;
+        LONG style;
+
+        hwndParent = ((CREATESTRUCTW *)lParam)->hwndParent;
+        style = ((CREATESTRUCTW *)lParam)->style;
+        return COMBO_Create(hwnd, lphc, hwndParent, style);
+    }
+
+    case WM_PRINTCLIENT:
+    case WM_PAINT:
+        return  COMBO_Paint(lphc, (HDC)wParam);
+
+    case WM_ERASEBKGND:
+        /* do all painting in WM_PAINT like Windows does */
+        return 1;
+
+    case WM_GETDLGCODE:
+    {
+        LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
+        if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
+        {
+            int vk = (int)((LPMSG)lParam)->wParam;
+
+            if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
+                result |= DLGC_WANTMESSAGE;
+        }
+        return  result;
+    }
+
+    case WM_SIZE:
+        if (lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE))
+            COMBO_Size( lphc );
+        return  TRUE;
+
+    case WM_SETFONT:
+        COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
+        return TRUE;
+
+    case WM_GETFONT:
+        return (LRESULT)lphc->hFont;
+
+    case WM_SETFOCUS:
+        if (lphc->wState & CBF_EDIT)
+        {
+            SetFocus( lphc->hWndEdit );
+            /* The first time focus is received, select all the text */
+            if (!(lphc->wState & CBF_BEENFOCUSED))
+            {
+                SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
+                lphc->wState |= CBF_BEENFOCUSED;
+            }
+        }
+        else
+            COMBO_SetFocus( lphc );
+        return  TRUE;
+
+    case WM_KILLFOCUS:
+    {
+        HWND hwndFocus = (HWND)wParam;
+        if (!hwndFocus || (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox))
+            COMBO_KillFocus( lphc );
+        return  TRUE;
+    }
+
+    case WM_COMMAND:
+        return COMBO_Command( lphc, wParam, (HWND)lParam );
+
+    case WM_GETTEXT:
+        return COMBO_GetText( lphc, wParam, (LPWSTR)lParam );
+
+    case WM_SETTEXT:
+    case WM_GETTEXTLENGTH:
+    case WM_CLEAR:
+        if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
+        {
+            int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
+            if (j == -1) return 0;
+            return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
+        }
+        else if ( lphc->wState & CBF_EDIT )
+        {
+            LRESULT ret;
+            lphc->wState |= CBF_NOEDITNOTIFY;
+            ret = SendMessageW(lphc->hWndEdit, message, wParam, lParam);
+            lphc->wState &= ~CBF_NOEDITNOTIFY;
+            return ret;
+        }
+        else
+            return CB_ERR;
+
+    case WM_CUT:
+    case WM_PASTE:
+    case WM_COPY:
+        if (lphc->wState & CBF_EDIT)
+            return SendMessageW(lphc->hWndEdit, message, wParam, lParam);
+        else return  CB_ERR;
+
+    case WM_DRAWITEM:
+    case WM_DELETEITEM:
+    case WM_COMPAREITEM:
+    case WM_MEASUREITEM:
+        return COMBO_ItemOp(lphc, message, lParam);
+
+    case WM_ENABLE:
+        if (lphc->wState & CBF_EDIT)
+            EnableWindow( lphc->hWndEdit, (BOOL)wParam );
+        EnableWindow( lphc->hWndLBox, (BOOL)wParam );
+
+        /* Force the control to repaint when the enabled state changes. */
+        InvalidateRect(lphc->self, NULL, TRUE);
+        return  TRUE;
+
+    case WM_SETREDRAW:
+        if (wParam)
+            lphc->wState &= ~CBF_NOREDRAW;
+        else
+            lphc->wState |= CBF_NOREDRAW;
+
+        if ( lphc->wState & CBF_EDIT )
+            SendMessageW(lphc->hWndEdit, message, wParam, lParam);
+        SendMessageW(lphc->hWndLBox, message, wParam, lParam);
+        return  0;
+
+    case WM_SYSKEYDOWN:
+        if ( KEYDATA_ALT & HIWORD(lParam) )
+            if( wParam == VK_UP || wParam == VK_DOWN )
+                COMBO_FlipListbox( lphc, FALSE, FALSE );
+        return  0;
+
+    case WM_KEYDOWN:
+        if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
+                (lphc->wState & CBF_DROPPED))
+        {
+            CBRollUp( lphc, wParam == VK_RETURN, FALSE );
+            return TRUE;
+        }
+        else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
+        {
+            COMBO_FlipListbox( lphc, FALSE, FALSE );
+            return TRUE;
+        }
+        /* fall through */
+    case WM_CHAR:
+    case WM_IME_CHAR:
+        {
+            HWND hwndTarget;
+
+            if ( lphc->wState & CBF_EDIT )
+                hwndTarget = lphc->hWndEdit;
+            else
+                hwndTarget = lphc->hWndLBox;
+
+           return SendMessageW(hwndTarget, message, wParam, lParam);
+        }
+
+    case WM_LBUTTONDOWN:
+        if ( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
+        if ( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
+        return  TRUE;
+
+    case WM_LBUTTONUP:
+        COMBO_LButtonUp( lphc );
+        return  TRUE;
+
+    case WM_MOUSEMOVE:
+        if ( lphc->wState & CBF_CAPTURE )
+            COMBO_MouseMove( lphc, wParam, lParam );
+        return  TRUE;
+
+    case WM_MOUSEWHEEL:
+        if (wParam & (MK_SHIFT | MK_CONTROL))
+            return DefWindowProcW(hwnd, message, wParam, lParam);
+
+        if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
+        if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
+        return TRUE;
+
+    /* Combo messages */
+    case CB_ADDSTRING:
+        if (lphc->dwStyle & CBS_LOWERCASE)
+            CharLowerW((LPWSTR)lParam);
+        else if (lphc->dwStyle & CBS_UPPERCASE)
+            CharUpperW((LPWSTR)lParam);
+        return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
+
+    case CB_INSERTSTRING:
+        if (lphc->dwStyle & CBS_LOWERCASE)
+            CharLowerW((LPWSTR)lParam);
+        else if (lphc->dwStyle & CBS_UPPERCASE)
+            CharUpperW((LPWSTR)lParam);
+        return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
+
+    case CB_DELETESTRING:
+        return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
+
+    case CB_SELECTSTRING:
+        return COMBO_SelectString(lphc, (INT)wParam, lParam);
+
+    case CB_FINDSTRING:
+        return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
+
+    case CB_FINDSTRINGEXACT:
+        return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
+
+    case CB_SETITEMHEIGHT:
+        return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
+
+    case CB_GETITEMHEIGHT:
+        if ((INT)wParam >= 0) /* listbox item */
+            return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
+        return  CBGetTextAreaHeight(hwnd, lphc);
+
+    case CB_RESETCONTENT:
+        SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
+
+        if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc))
+        {
+            static const WCHAR empty_stringW[] = { 0 };
+            SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
+        }
+        else
+            InvalidateRect(lphc->self, NULL, TRUE);
+        return  TRUE;
+
+    case CB_INITSTORAGE:
+        return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
+
+    case CB_GETHORIZONTALEXTENT:
+        return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
+
+    case CB_SETHORIZONTALEXTENT:
+        return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
+
+    case CB_GETTOPINDEX:
+        return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
+
+    case CB_GETLOCALE:
+        return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
+
+    case CB_SETLOCALE:
+        return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
+
+    case CB_SETDROPPEDWIDTH:
+        if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768)
+            return CB_ERR;
+
+        /* new value must be higher than combobox width */
+        if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
+            lphc->droppedWidth = wParam;
+        else if (wParam)
+            lphc->droppedWidth = 0;
+
+        /* recalculate the combobox area */
+        CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
+
+        /* fall through */
+    case CB_GETDROPPEDWIDTH:
+        if (lphc->droppedWidth)
+            return lphc->droppedWidth;
+        return  lphc->droppedRect.right - lphc->droppedRect.left;
+
+    case CB_GETDROPPEDCONTROLRECT:
+        if (lParam)
+            CBGetDroppedControlRect(lphc, (LPRECT)lParam );
+        return  CB_OKAY;
+
+    case CB_GETDROPPEDSTATE:
+        return (lphc->wState & CBF_DROPPED) != 0;
+
+    case CB_DIR:
+        return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam);
+
+    case CB_SHOWDROPDOWN:
+        if (CB_GETTYPE(lphc) != CBS_SIMPLE)
+        {
+            if (wParam)
+            {
+                if (!(lphc->wState & CBF_DROPPED))
+                    CBDropDown( lphc );
+            }
+            else if (lphc->wState & CBF_DROPPED)
+                CBRollUp( lphc, FALSE, TRUE );
+        }
+        return  TRUE;
+
+    case CB_GETCOUNT:
+        return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
+
+    case CB_GETCURSEL:
+        return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
+
+    case CB_SETCURSEL:
+        lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
+        if (lParam >= 0)
+            SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
+
+        /* no LBN_SELCHANGE in this case, update manually */
+        CBPaintText(lphc, NULL);
+        lphc->wState &= ~CBF_SELCHANGE;
+        return lParam;
+
+    case CB_GETLBTEXT:
+        return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
+
+    case CB_GETLBTEXTLEN:
+        return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
+
+    case CB_GETITEMDATA:
+        return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
+
+    case CB_SETITEMDATA:
+        return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
+
+    case CB_GETEDITSEL:
+        /* Edit checks passed parameters itself */
+        if (lphc->wState & CBF_EDIT)
+            return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
+        return  CB_ERR;
+
+    case CB_SETEDITSEL:
+        if (lphc->wState & CBF_EDIT)
+            return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
+        return  CB_ERR;
+
+    case CB_SETEXTENDEDUI:
+        if (CB_GETTYPE(lphc) == CBS_SIMPLE )
+            return  CB_ERR;
+        if (wParam)
+            lphc->wState |= CBF_EUI;
+        else
+            lphc->wState &= ~CBF_EUI;
+        return  CB_OKAY;
+
+    case CB_GETEXTENDEDUI:
+        return (lphc->wState & CBF_EUI) != 0;
+
+    case CB_GETCOMBOBOXINFO:
+        return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
+
+    case CB_LIMITTEXT:
+        if (lphc->wState & CBF_EDIT)
+            return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
+        return  TRUE;
+
+    default:
+        if (message >= WM_USER)
+            WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n", message - WM_USER, wParam, lParam );
+        break;
+    }
+
+    return DefWindowProcW(hwnd, message, wParam, lParam);
+}
+
+void COMBO_Register(void)
+{
+    WNDCLASSW wndClass;
+
+    memset(&wndClass, 0, sizeof(wndClass));
+    wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
+    wndClass.lpfnWndProc = COMBO_WindowProc;
+    wndClass.cbClsExtra = 0;
+    wndClass.cbWndExtra = sizeof(HEADCOMBO *);
+    wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
+    wndClass.hbrBackground = NULL;
+    wndClass.lpszClassName = WC_COMBOBOXW;
+    RegisterClassW(&wndClass);
+}
+
diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h
index 960ec882ef..1ce9752951 100644
--- a/dlls/comctl32/comctl32.h
+++ b/dlls/comctl32/comctl32.h
@@ -178,6 +178,7 @@ HRGN set_control_clipping(HDC hdc, const RECT *rect) DECLSPEC_HIDDEN;
 extern void ANIMATE_Register(void) DECLSPEC_HIDDEN;
 extern void ANIMATE_Unregister(void) DECLSPEC_HIDDEN;
 extern void BUTTON_Register(void) DECLSPEC_HIDDEN;
+extern void COMBO_Register(void) DECLSPEC_HIDDEN;
 extern void COMBOEX_Register(void) DECLSPEC_HIDDEN;
 extern void COMBOEX_Unregister(void) DECLSPEC_HIDDEN;
 extern void DATETIME_Register(void) DECLSPEC_HIDDEN;
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c
index f04ab136b8..42c05e69fa 100644
--- a/dlls/comctl32/commctrl.c
+++ b/dlls/comctl32/commctrl.c
@@ -99,6 +99,7 @@ static void unregister_versioned_classes(void)
     static const char *classes[] =
     {
         VERSION WC_BUTTONA,
+        VERSION WC_COMBOBOXA,
         VERSION WC_EDITA,
         VERSION WC_STATICA,
     };
@@ -170,6 +171,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
             UPDOWN_Register ();
 
             BUTTON_Register ();
+            COMBO_Register ();
             EDIT_Register ();
             STATIC_Register ();
 
diff --git a/dlls/comctl32/tests/misc.c b/dlls/comctl32/tests/misc.c
index f5b361e6f0..7c9b4cd02e 100644
--- a/dlls/comctl32/tests/misc.c
+++ b/dlls/comctl32/tests/misc.c
@@ -346,6 +346,7 @@ static void check_class( const char *name, int must_exist, UINT style, UINT igno
     if (GetClassInfoA( 0, name, &wc ))
     {
 todo_wine_if(strcmp(name, "Button") &&
+                strcmp(name, "ComboBox") &&
                 strcmp(name, "Edit") &&
                 strcmp(name, "Static"))
         ok( !(~wc.style & style & ~ignore), "System class %s is missing bits %x (%08x/%08x)\n",
diff --git a/dlls/user32/class.c b/dlls/user32/class.c
index d4b95609a8..0e05c1f426 100644
--- a/dlls/user32/class.c
+++ b/dlls/user32/class.c
@@ -166,7 +166,6 @@ static BOOL is_builtin_class( const WCHAR *name )
 {
     static const WCHAR classesW[][20] =
     {
-        {'C','o','m','b','o','B','o','x',0},
         {'C','o','m','b','o','L','B','o','x',0},
         {'I','M','E',0},
         {'L','i','s','t','B','o','x',0},
-- 
2.15.1




More information about the wine-devel mailing list