xref: /reactos/dll/win32/comctl32/combo.c (revision f04935d8)
1 /*
2  * Combo controls
3  *
4  * Copyright 1997 Alex Korobka
5  * Copyright (c) 2005 by Frank Richter
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * TODO:
22  *   - ComboBox_[GS]etMinVisible()
23  *   - CB_GETMINVISIBLE, CB_SETMINVISIBLE
24  *   - CB_SETTOPINDEX
25  */
26 
27 #include <stdarg.h>
28 #include <string.h>
29 
30 #define OEMRESOURCE
31 
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "uxtheme.h"
37 #include "vssym32.h"
38 #include "commctrl.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
41 #include "wine/heap.h"
42 
43 #include "comctl32.h"
44 
45 WINE_DEFAULT_DEBUG_CHANNEL(combo);
46 
47   /* bits in the dwKeyData */
48 #define KEYDATA_ALT             0x2000
49 #define KEYDATA_PREVSTATE       0x4000
50 
51 /*
52  * Additional combo box definitions
53  */
54 
55 #define CB_NOTIFY( lphc, code ) \
56     (SendMessageW((lphc)->owner, WM_COMMAND, \
57                   MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
58 
59 #define CB_DISABLED( lphc )   (!IsWindowEnabled((lphc)->self))
60 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
61 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
62 #define CB_HWND( lphc )       ((lphc)->self)
63 #define CB_GETTYPE( lphc )    ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
64 
65 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
66 
67 /*
68  * Drawing globals
69  */
70 static HBITMAP 	hComboBmp = 0;
71 static UINT	CBitHeight, CBitWidth;
72 
73 /*
74  * Look and feel dependent "constants"
75  */
76 
77 #define COMBO_YBORDERGAP         5
78 #define COMBO_XBORDERSIZE()      2
79 #define COMBO_YBORDERSIZE()      2
80 #define COMBO_EDITBUTTONSPACE()  0
81 #define EDIT_CONTROL_PADDING()   1
82 
83 #define ID_CB_LISTBOX           1000
84 #define ID_CB_EDIT              1001
85 
86 /***********************************************************************
87  *           COMBO_Init
88  *
89  * Load combo button bitmap.
90  */
91 static BOOL COMBO_Init(void)
92 {
93   HDC		hDC;
94 
95   if( hComboBmp ) return TRUE;
96   if( (hDC = CreateCompatibleDC(0)) )
97   {
98     BOOL	bRet = FALSE;
99     if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
100     {
101       BITMAP      bm;
102       HBITMAP     hPrevB;
103       RECT        r;
104 
105       GetObjectW( hComboBmp, sizeof(bm), &bm );
106       CBitHeight = bm.bmHeight;
107       CBitWidth  = bm.bmWidth;
108 
109       TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
110 
111       hPrevB = SelectObject( hDC, hComboBmp);
112       SetRect( &r, 0, 0, CBitWidth, CBitHeight );
113       InvertRect( hDC, &r );
114       SelectObject( hDC, hPrevB );
115       bRet = TRUE;
116     }
117     DeleteDC( hDC );
118     return bRet;
119   }
120   return FALSE;
121 }
122 
123 /***********************************************************************
124  *           COMBO_NCCreate
125  */
126 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
127 {
128     HEADCOMBO *lphc;
129 
130     if (COMBO_Init() && (lphc = heap_alloc_zero(sizeof(*lphc))))
131     {
132         lphc->self = hwnd;
133         SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
134 
135        /* some braindead apps do try to use scrollbar/border flags */
136 
137 	lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
138         SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
139 
140 	/*
141 	 * We also have to remove the client edge style to make sure
142 	 * we don't end-up with a non client area.
143 	 */
144         SetWindowLongW( hwnd, GWL_EXSTYLE,
145                         GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
146 
147 	if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
148               lphc->dwStyle |= CBS_HASSTRINGS;
149 	if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
150 	      lphc->wState |= CBF_NOTIFY;
151 
152         TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
153         return TRUE;
154     }
155     return FALSE;
156 }
157 
158 /***********************************************************************
159  *           COMBO_NCDestroy
160  */
161 static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc )
162 {
163     if (lphc)
164     {
165         TRACE("[%p]: freeing storage\n", lphc->self);
166 
167         if ( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
168             DestroyWindow( lphc->hWndLBox );
169 
170         SetWindowLongPtrW( lphc->self, 0, 0 );
171         heap_free( lphc );
172     }
173 
174     return 0;
175 }
176 
177 /***********************************************************************
178  *           CBGetTextAreaHeight
179  *
180  * This method will calculate the height of the text area of the
181  * combobox.
182  * The height of the text area is set in two ways.
183  * It can be set explicitly through a combobox message or through a
184  * WM_MEASUREITEM callback.
185  * If this is not the case, the height is set to font height + 4px
186  * This height was determined through experimentation.
187  * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
188  */
189 static INT CBGetTextAreaHeight(
190   HWND        hwnd,
191   LPHEADCOMBO lphc)
192 {
193   INT iTextItemHeight;
194 
195   if( lphc->editHeight ) /* explicitly set height */
196   {
197     iTextItemHeight = lphc->editHeight;
198   }
199   else
200   {
201     TEXTMETRICW tm;
202     HDC         hDC       = GetDC(hwnd);
203     HFONT       hPrevFont = 0;
204     INT         baseUnitY;
205 
206     if (lphc->hFont)
207       hPrevFont = SelectObject( hDC, lphc->hFont );
208 
209     GetTextMetricsW(hDC, &tm);
210 
211     baseUnitY = tm.tmHeight;
212 
213     if( hPrevFont )
214       SelectObject( hDC, hPrevFont );
215 
216     ReleaseDC(hwnd, hDC);
217 
218     iTextItemHeight = baseUnitY + 4;
219   }
220 
221   /*
222    * Check the ownerdraw case if we haven't asked the parent the size
223    * of the item yet.
224    */
225   if ( CB_OWNERDRAWN(lphc) &&
226        (lphc->wState & CBF_MEASUREITEM) )
227   {
228     MEASUREITEMSTRUCT measureItem;
229     RECT              clientRect;
230     INT               originalItemHeight = iTextItemHeight;
231     UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
232 
233     /*
234      * We use the client rect for the width of the item.
235      */
236     GetClientRect(hwnd, &clientRect);
237 
238     lphc->wState &= ~CBF_MEASUREITEM;
239 
240     /*
241      * Send a first one to measure the size of the text area
242      */
243     measureItem.CtlType    = ODT_COMBOBOX;
244     measureItem.CtlID      = id;
245     measureItem.itemID     = -1;
246     measureItem.itemWidth  = clientRect.right;
247     measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
248     measureItem.itemData   = 0;
249     SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
250     iTextItemHeight = 6 + measureItem.itemHeight;
251 
252     /*
253      * Send a second one in the case of a fixed ownerdraw list to calculate the
254      * size of the list items. (we basically do this on behalf of the listbox)
255      */
256     if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
257     {
258       measureItem.CtlType    = ODT_COMBOBOX;
259       measureItem.CtlID      = id;
260       measureItem.itemID     = 0;
261       measureItem.itemWidth  = clientRect.right;
262       measureItem.itemHeight = originalItemHeight;
263       measureItem.itemData   = 0;
264       SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
265       lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
266     }
267 
268     /*
269      * Keep the size for the next time
270      */
271     lphc->editHeight = iTextItemHeight;
272   }
273 
274   return iTextItemHeight;
275 }
276 
277 /***********************************************************************
278  *           CBForceDummyResize
279  *
280  * The dummy resize is used for listboxes that have a popup to trigger
281  * a re-arranging of the contents of the combobox and the recalculation
282  * of the size of the "real" control window.
283  */
284 static void CBForceDummyResize(
285   LPHEADCOMBO lphc)
286 {
287   RECT windowRect;
288   int newComboHeight;
289 
290   newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
291 
292   GetWindowRect(lphc->self, &windowRect);
293 
294   /*
295    * We have to be careful, resizing a combobox also has the meaning that the
296    * dropped rect will be resized. In this case, we want to trigger a resize
297    * to recalculate layout but we don't want to change the dropped rectangle
298    * So, we pass the height of text area of control as the height.
299    * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
300    * message.
301    */
302   SetWindowPos( lphc->self,
303 		NULL,
304 		0, 0,
305 		windowRect.right  - windowRect.left,
306 		newComboHeight,
307 		SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
308 }
309 
310 /***********************************************************************
311  *           CBCalcPlacement
312  *
313  * Set up component coordinates given valid lphc->RectCombo.
314  */
315 static void CBCalcPlacement(
316   HWND        hwnd,
317   LPHEADCOMBO lphc,
318   LPRECT      lprEdit,
319   LPRECT      lprButton,
320   LPRECT      lprLB)
321 {
322   /*
323    * Again, start with the client rectangle.
324    */
325   GetClientRect(hwnd, lprEdit);
326 
327   /*
328    * Remove the borders
329    */
330   InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
331 
332   /*
333    * Chop off the bottom part to fit with the height of the text area.
334    */
335   lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
336 
337   /*
338    * The button starts the same vertical position as the text area.
339    */
340   CopyRect(lprButton, lprEdit);
341 
342   /*
343    * If the combobox is "simple" there is no button.
344    */
345   if( CB_GETTYPE(lphc) == CBS_SIMPLE )
346     lprButton->left = lprButton->right = lprButton->bottom = 0;
347   else
348   {
349     /*
350      * Let's assume the combobox button is the same width as the
351      * scrollbar button.
352      * size the button horizontally and cut-off the text area.
353      */
354     lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
355     lprEdit->right  = lprButton->left;
356   }
357 
358   /*
359    * In the case of a dropdown, there is an additional spacing between the
360    * text area and the button.
361    */
362   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
363   {
364     lprEdit->right -= COMBO_EDITBUTTONSPACE();
365   }
366 
367   /*
368    * If we have an edit control, we space it away from the borders slightly.
369    */
370   if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
371   {
372     InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
373   }
374 
375   /*
376    * Adjust the size of the listbox popup.
377    */
378   if( CB_GETTYPE(lphc) == CBS_SIMPLE )
379   {
380     /*
381      * Use the client rectangle to initialize the listbox rectangle
382      */
383     GetClientRect(hwnd, lprLB);
384 
385     /*
386      * Then, chop-off the top part.
387      */
388     lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
389   }
390   else
391   {
392     /*
393      * Make sure the dropped width is as large as the combobox itself.
394      */
395     if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
396     {
397       lprLB->right  = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
398 
399       /*
400        * In the case of a dropdown, the popup listbox is offset to the right.
401        * so, we want to make sure it's flush with the right side of the
402        * combobox
403        */
404       if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
405 	lprLB->right -= COMBO_EDITBUTTONSPACE();
406     }
407     else
408        lprLB->right = lprLB->left + lphc->droppedWidth;
409   }
410 
411   /* don't allow negative window width */
412   if (lprEdit->right < lprEdit->left)
413     lprEdit->right = lprEdit->left;
414 
415   TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
416 
417   TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
418 
419   TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
420 }
421 
422 /***********************************************************************
423  *           CBGetDroppedControlRect
424  */
425 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
426 {
427     /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
428      of the combo box and the lower right corner of the listbox */
429 
430     GetWindowRect(lphc->self, lpRect);
431 
432     lpRect->right =  lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
433     lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
434 
435 }
436 
437 /***********************************************************************
438  *           COMBO_Create
439  */
440 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style )
441 {
442   static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
443   static const WCHAR editName[] = {'E','d','i','t',0};
444 
445   OpenThemeData( hwnd, WC_COMBOBOXW );
446   if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
447   if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
448 
449   lphc->owner = hwndParent;
450 
451   /*
452    * The item height and dropped width are not set when the control
453    * is created.
454    */
455   lphc->droppedWidth = lphc->editHeight = 0;
456 
457   /*
458    * The first time we go through, we want to measure the ownerdraw item
459    */
460   lphc->wState |= CBF_MEASUREITEM;
461 
462   /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
463 
464   if( lphc->owner || !(style & WS_VISIBLE) )
465   {
466       UINT lbeStyle   = 0;
467       UINT lbeExStyle = 0;
468 
469       /*
470        * Initialize the dropped rect to the size of the client area of the
471        * control and then, force all the areas of the combobox to be
472        * recalculated.
473        */
474       GetClientRect( hwnd, &lphc->droppedRect );
475       CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
476 
477       /*
478        * Adjust the position of the popup listbox if it's necessary
479        */
480       if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
481       {
482 	lphc->droppedRect.top   = lphc->textRect.bottom + COMBO_YBORDERSIZE();
483 
484 	/*
485 	 * If it's a dropdown, the listbox is offset
486 	 */
487 	if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
488 	  lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
489 
490         if (lphc->droppedRect.bottom < lphc->droppedRect.top)
491             lphc->droppedRect.bottom = lphc->droppedRect.top;
492         if (lphc->droppedRect.right < lphc->droppedRect.left)
493             lphc->droppedRect.right = lphc->droppedRect.left;
494         MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
495       }
496 
497       /* create listbox popup */
498 
499       lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
500                  (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
501 
502       if( lphc->dwStyle & CBS_SORT )
503 	lbeStyle |= LBS_SORT;
504       if( lphc->dwStyle & CBS_HASSTRINGS )
505 	lbeStyle |= LBS_HASSTRINGS;
506       if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
507 	lbeStyle |= LBS_NOINTEGRALHEIGHT;
508       if( lphc->dwStyle & CBS_DISABLENOSCROLL )
509 	lbeStyle |= LBS_DISABLENOSCROLL;
510 
511       if( CB_GETTYPE(lphc) == CBS_SIMPLE ) 	/* child listbox */
512       {
513 	lbeStyle |= WS_VISIBLE;
514 
515 	/*
516 	 * In win 95 look n feel, the listbox in the simple combobox has
517 	 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
518 	 */
519 	lbeStyle   &= ~WS_BORDER;
520 	lbeExStyle |= WS_EX_CLIENTEDGE;
521       }
522       else
523       {
524         lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
525       }
526 
527       lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
528               lphc->droppedRect.left, lphc->droppedRect.top, lphc->droppedRect.right - lphc->droppedRect.left,
529               lphc->droppedRect.bottom - lphc->droppedRect.top, hwnd, (HMENU)ID_CB_LISTBOX,
530               (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
531       if( lphc->hWndLBox )
532       {
533 	  BOOL	bEdit = TRUE;
534 	  lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
535 
536 	  if( lphc->wState & CBF_EDIT )
537 	  {
538 	      if( lphc->dwStyle & CBS_OEMCONVERT )
539 		  lbeStyle |= ES_OEMCONVERT;
540 	      if( lphc->dwStyle & CBS_AUTOHSCROLL )
541 		  lbeStyle |= ES_AUTOHSCROLL;
542 	      if( lphc->dwStyle & CBS_LOWERCASE )
543 		  lbeStyle |= ES_LOWERCASE;
544 	      else if( lphc->dwStyle & CBS_UPPERCASE )
545 		  lbeStyle |= ES_UPPERCASE;
546 
547               if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
548 
549               lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
550                                                lphc->textRect.left, lphc->textRect.top,
551                                                lphc->textRect.right - lphc->textRect.left,
552                                                lphc->textRect.bottom - lphc->textRect.top,
553                                                hwnd, (HMENU)ID_CB_EDIT,
554                                                (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
555 	      if( !lphc->hWndEdit )
556 		bEdit = FALSE;
557 	  }
558 
559           if( bEdit )
560 	  {
561 	    if( CB_GETTYPE(lphc) != CBS_SIMPLE )
562 	    {
563               /* Now do the trick with parent */
564 	      SetParent(lphc->hWndLBox, HWND_DESKTOP);
565               /*
566                * If the combo is a dropdown, we must resize the control
567 	       * to fit only the text area and button. To do this,
568 	       * we send a dummy resize and the WM_WINDOWPOSCHANGING message
569 	       * will take care of setting the height for us.
570                */
571 	      CBForceDummyResize(lphc);
572 	    }
573 
574 	    TRACE("init done\n");
575 	    return 0;
576 	  }
577 	  ERR("edit control failure.\n");
578       } else ERR("listbox failure.\n");
579   } else ERR("no owner for visible combo.\n");
580 
581   /* CreateWindow() will send WM_NCDESTROY to cleanup */
582 
583   return -1;
584 }
585 
586 /***********************************************************************
587  *           CBPaintButton
588  *
589  * Paint combo button (normal, pressed, and disabled states).
590  */
591 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
592 {
593     UINT buttonState = DFCS_SCROLLCOMBOBOX;
594 
595     if( lphc->wState & CBF_NOREDRAW )
596       return;
597 
598 
599     if (lphc->wState & CBF_BUTTONDOWN)
600 	buttonState |= DFCS_PUSHED;
601 
602     if (CB_DISABLED(lphc))
603 	buttonState |= DFCS_INACTIVE;
604 
605     DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
606 }
607 
608 /***********************************************************************
609  *           COMBO_PrepareColors
610  *
611  * This method will sent the appropriate WM_CTLCOLOR message to
612  * prepare and setup the colors for the combo's DC.
613  *
614  * It also returns the brush to use for the background.
615  */
616 static HBRUSH COMBO_PrepareColors(
617         LPHEADCOMBO lphc,
618         HDC         hDC)
619 {
620     HBRUSH  hBkgBrush;
621 
622     /*
623      * Get the background brush for this control.
624      */
625     if (CB_DISABLED(lphc))
626     {
627         hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
628                 (WPARAM)hDC, (LPARAM)lphc->self );
629 
630         /*
631          * We have to change the text color since WM_CTLCOLORSTATIC will
632          * set it to the "enabled" color. This is the same behavior as the
633          * edit control
634          */
635         SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
636     }
637     else
638     {
639         /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
640         hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
641                 (WPARAM)hDC, (LPARAM)lphc->self );
642     }
643 
644     /*
645      * Catch errors.
646      */
647     if( !hBkgBrush )
648         hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
649 
650     return hBkgBrush;
651 }
652 
653 /***********************************************************************
654  *           CBPaintText
655  *
656  * Paint CBS_DROPDOWNLIST text field / update edit control contents.
657  */
658 static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint)
659 {
660    RECT rectEdit = lphc->textRect;
661    INT	id, size = 0;
662    LPWSTR pText = NULL;
663 
664    TRACE("\n");
665 
666    /* follow Windows combobox that sends a bunch of text
667     * inquiries to its listbox while processing WM_PAINT. */
668 
669    if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
670    {
671         size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
672 	if (size == LB_ERR)
673 	  FIXME("LB_ERR probably not handled yet\n");
674         if ((pText = heap_alloc((size + 1) * sizeof(WCHAR))))
675 	{
676             /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
677            size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
678 	    pText[size] = '\0';	/* just in case */
679 	} else return;
680    }
681 
682    if( lphc->wState & CBF_EDIT )
683    {
684         static const WCHAR empty_stringW[] = { 0 };
685 	if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
686 	if( lphc->wState & CBF_FOCUSED )
687            SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
688    }
689    else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
690    {
691      /* paint text field ourselves */
692      HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
693      UINT itemState = ODS_COMBOBOXEDIT;
694      HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
695      HBRUSH hPrevBrush, hBkgBrush;
696 
697      /*
698       * Give ourselves some space.
699       */
700      InflateRect( &rectEdit, -1, -1 );
701 
702      hBkgBrush = COMBO_PrepareColors( lphc, hdc );
703      hPrevBrush = SelectObject( hdc, hBkgBrush );
704      FillRect( hdc, &rectEdit, hBkgBrush );
705 
706      if( CB_OWNERDRAWN(lphc) )
707      {
708        DRAWITEMSTRUCT dis;
709        HRGN           clipRegion;
710        UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
711 
712        /* setup state for DRAWITEM message. Owner will highlight */
713        if ( (lphc->wState & CBF_FOCUSED) &&
714 	    !(lphc->wState & CBF_DROPPED) )
715 	   itemState |= ODS_SELECTED | ODS_FOCUS;
716 
717        if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
718 
719        dis.CtlType	= ODT_COMBOBOX;
720        dis.CtlID	= ctlid;
721        dis.hwndItem	= lphc->self;
722        dis.itemAction	= ODA_DRAWENTIRE;
723        dis.itemID	= id;
724        dis.itemState	= itemState;
725        dis.hDC		= hdc;
726        dis.rcItem	= rectEdit;
727        dis.itemData     = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
728 
729        /*
730 	* Clip the DC and have the parent draw the item.
731 	*/
732        clipRegion = set_control_clipping( hdc, &rectEdit );
733 
734        SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
735 
736        SelectClipRgn( hdc, clipRegion );
737        if (clipRegion) DeleteObject( clipRegion );
738      }
739      else
740      {
741        static const WCHAR empty_stringW[] = { 0 };
742 
743        if ( (lphc->wState & CBF_FOCUSED) &&
744 	    !(lphc->wState & CBF_DROPPED) ) {
745 
746 	   /* highlight */
747 	   FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
748 	   SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
749 	   SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
750        }
751 
752        ExtTextOutW( hdc,
753 		    rectEdit.left + 1,
754 		    rectEdit.top + 1,
755 		    ETO_OPAQUE | ETO_CLIPPED,
756 		    &rectEdit,
757 		    pText ? pText : empty_stringW , size, NULL );
758 
759        if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
760 	 DrawFocusRect( hdc, &rectEdit );
761      }
762 
763      if( hPrevFont )
764        SelectObject(hdc, hPrevFont );
765 
766     if( hPrevBrush )
767         SelectObject( hdc, hPrevBrush );
768 
769      if( !hdc_paint )
770        ReleaseDC( lphc->self, hdc );
771    }
772 
773     heap_free(pText);
774 }
775 
776 /***********************************************************************
777  *           CBPaintBorder
778  */
779 static void CBPaintBorder(
780   HWND            hwnd,
781   const HEADCOMBO *lphc,
782   HDC             hdc)
783 {
784   RECT clientRect;
785 
786   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
787   {
788     GetClientRect(hwnd, &clientRect);
789   }
790   else
791   {
792     clientRect = lphc->textRect;
793 
794     InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
795     InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
796   }
797 
798   DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
799 }
800 
801 static LRESULT COMBO_ThemedPaint(HTHEME theme, HEADCOMBO *lphc, HDC hdc)
802 {
803     int button_state;
804     RECT frame;
805 
806     /* paint border */
807     if (CB_GETTYPE(lphc) != CBS_SIMPLE)
808         GetClientRect(lphc->self, &frame);
809     else
810     {
811         frame = lphc->textRect;
812         InflateRect(&frame, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
813         InflateRect(&frame, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
814     }
815 
816     DrawThemeBackground(theme, hdc, 0, IsWindowEnabled(lphc->self) ? CBXS_NORMAL : CBXS_DISABLED, &frame, NULL);
817 
818     /* Paint button */
819     if (!IsRectEmpty(&lphc->buttonRect))
820     {
821         if (!IsWindowEnabled(lphc->self))
822             button_state = CBXS_DISABLED;
823         else if (lphc->wState & CBF_BUTTONDOWN)
824             button_state = CBXS_PRESSED;
825         else if (lphc->wState & CBF_HOT)
826             button_state = CBXS_HOT;
827         else
828             button_state = CBXS_NORMAL;
829         DrawThemeBackground(theme, hdc, CP_DROPDOWNBUTTON, button_state, &lphc->buttonRect, NULL);
830     }
831 
832     if ((lphc->dwStyle & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST)
833         CBPaintText(lphc, hdc);
834 
835     return 0;
836 }
837 
838 /***********************************************************************
839  *           COMBO_Paint
840  */
841 static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc)
842 {
843     HBRUSH hPrevBrush, hBkgBrush;
844 
845     TRACE("hdc=%p\n", hdc);
846 
847     /*
848      * Retrieve the background brush and select it in the
849      * DC.
850      */
851     hBkgBrush = COMBO_PrepareColors(lphc, hdc);
852     hPrevBrush = SelectObject(hdc, hBkgBrush);
853     if (!(lphc->wState & CBF_EDIT))
854         FillRect(hdc, &lphc->textRect, hBkgBrush);
855 
856     /*
857      * In non 3.1 look, there is a sunken border on the combobox
858      */
859     CBPaintBorder(lphc->self, lphc, hdc);
860 
861     if (!IsRectEmpty(&lphc->buttonRect))
862         CBPaintButton(lphc, hdc, lphc->buttonRect);
863 
864     /* paint the edit control padding area */
865     if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
866     {
867         RECT rPadEdit = lphc->textRect;
868 
869         InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
870 
871         FrameRect(hdc, &rPadEdit, GetSysColorBrush(COLOR_WINDOW));
872     }
873 
874     if (!(lphc->wState & CBF_EDIT))
875         CBPaintText( lphc, hdc );
876 
877     if (hPrevBrush)
878         SelectObject( hdc, hPrevBrush );
879 
880     return 0;
881 }
882 
883 /***********************************************************************
884  *           CBUpdateLBox
885  *
886  * Select listbox entry according to the contents of the edit control.
887  */
888 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
889 {
890    INT	length, idx;
891    LPWSTR pText = NULL;
892 
893    idx = LB_ERR;
894    length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
895 
896     if (length > 0)
897         pText = heap_alloc((length + 1) * sizeof(WCHAR));
898 
899    TRACE("\t edit text length %i\n", length );
900 
901    if( pText )
902    {
903        GetWindowTextW( lphc->hWndEdit, pText, length + 1);
904        idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
905        heap_free( pText );
906    }
907 
908    SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
909 
910    /* probably superfluous but Windows sends this too */
911    SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
912    SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
913 
914    return idx;
915 }
916 
917 /***********************************************************************
918  *           CBUpdateEdit
919  *
920  * Copy a listbox entry to the edit control.
921  */
922 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
923 {
924    INT	length;
925    LPWSTR pText = NULL;
926    static const WCHAR empty_stringW[] = { 0 };
927 
928    TRACE("\t %i\n", index );
929 
930    if( index >= 0 ) /* got an entry */
931    {
932        length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
933        if( length != LB_ERR)
934        {
935            if ((pText = heap_alloc((length + 1) * sizeof(WCHAR))))
936                SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
937        }
938    }
939 
940    if( CB_HASSTRINGS(lphc) )
941    {
942       lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
943       SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
944       lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
945    }
946 
947    if( lphc->wState & CBF_FOCUSED )
948       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
949 
950     heap_free( pText );
951 }
952 
953 /***********************************************************************
954  *           CBDropDown
955  *
956  * Show listbox popup.
957  */
958 static void CBDropDown( LPHEADCOMBO lphc )
959 {
960     HMONITOR monitor;
961     MONITORINFO mon_info;
962    RECT rect,r;
963    int nItems;
964    int nDroppedHeight;
965 
966    TRACE("[%p]: drop down\n", lphc->self);
967 
968    CB_NOTIFY( lphc, CBN_DROPDOWN );
969 
970    /* set selection */
971 
972    lphc->wState |= CBF_DROPPED;
973    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
974    {
975        lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
976 
977        /* Update edit only if item is in the list */
978        if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
979 	 CBUpdateEdit( lphc, lphc->droppedIndex );
980    }
981    else
982    {
983        lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
984 
985        SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
986                     lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
987        SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
988    }
989 
990    /* now set popup position */
991    GetWindowRect( lphc->self, &rect );
992 
993    /*
994     * If it's a dropdown, the listbox is offset
995     */
996    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
997      rect.left += COMBO_EDITBUTTONSPACE();
998 
999   /* if the dropped height is greater than the total height of the dropped
1000      items list, then force the drop down list height to be the total height
1001      of the items in the dropped list */
1002 
1003   /* And Remove any extra space (Best Fit) */
1004    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1005   /* if listbox length has been set directly by its handle */
1006    GetWindowRect(lphc->hWndLBox, &r);
1007    if (nDroppedHeight < r.bottom - r.top)
1008        nDroppedHeight = r.bottom - r.top;
1009    nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1010 
1011    if (nItems > 0)
1012    {
1013       int nHeight;
1014       int nIHeight;
1015 
1016       nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1017 
1018       nHeight = nIHeight*nItems;
1019 
1020       if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1021          nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1022 
1023       if (nDroppedHeight < nHeight)
1024       {
1025             if (nItems < 5)
1026                 nDroppedHeight = (nItems+1)*nIHeight;
1027             else if (nDroppedHeight < 6*nIHeight)
1028                 nDroppedHeight = 6*nIHeight;
1029       }
1030    }
1031 
1032    r.left = rect.left;
1033    r.top = rect.bottom;
1034    r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
1035    r.bottom = r.top + nDroppedHeight;
1036 
1037    /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1038    monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1039    mon_info.cbSize = sizeof(mon_info);
1040    GetMonitorInfoW( monitor, &mon_info );
1041 
1042    if (r.bottom > mon_info.rcWork.bottom)
1043    {
1044        r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
1045        r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1046    }
1047 
1048    SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1049                  SWP_NOACTIVATE | SWP_SHOWWINDOW );
1050 
1051 
1052    if( !(lphc->wState & CBF_NOREDRAW) )
1053      RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1054 			   RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1055 
1056    EnableWindow( lphc->hWndLBox, TRUE );
1057    if (GetCapture() != lphc->self)
1058       SetCapture(lphc->hWndLBox);
1059 }
1060 
1061 /***********************************************************************
1062  *           CBRollUp
1063  *
1064  * Hide listbox popup.
1065  */
1066 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1067 {
1068    HWND	hWnd = lphc->self;
1069 
1070    TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1071 	 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1072 
1073    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1074 
1075    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1076    {
1077 
1078        if( lphc->wState & CBF_DROPPED )
1079        {
1080 	   RECT	rect;
1081 
1082 	   lphc->wState &= ~CBF_DROPPED;
1083 	   ShowWindow( lphc->hWndLBox, SW_HIDE );
1084 
1085            if(GetCapture() == lphc->hWndLBox)
1086            {
1087                ReleaseCapture();
1088            }
1089 
1090 	   if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1091 	   {
1092 	       rect = lphc->buttonRect;
1093 	   }
1094 	   else
1095            {
1096 	       if( bButton )
1097 	       {
1098 		 UnionRect( &rect,
1099 			    &lphc->buttonRect,
1100 			    &lphc->textRect);
1101 	       }
1102 	       else
1103 		 rect = lphc->textRect;
1104 
1105 	       bButton = TRUE;
1106 	   }
1107 
1108 	   if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1109 	       RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1110 			       RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1111 	   CB_NOTIFY( lphc, CBN_CLOSEUP );
1112        }
1113    }
1114 }
1115 
1116 /***********************************************************************
1117  *           COMBO_FlipListbox
1118  *
1119  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1120  */
1121 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1122 {
1123    if( lphc->wState & CBF_DROPPED )
1124    {
1125        CBRollUp( lphc, ok, bRedrawButton );
1126        return FALSE;
1127    }
1128 
1129    CBDropDown( lphc );
1130    return TRUE;
1131 }
1132 
1133 /***********************************************************************
1134  *           CBRepaintButton
1135  */
1136 static void CBRepaintButton( LPHEADCOMBO lphc )
1137    {
1138   InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1139   UpdateWindow(lphc->self);
1140 }
1141 
1142 /***********************************************************************
1143  *           COMBO_SetFocus
1144  */
1145 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1146 {
1147    if( !(lphc->wState & CBF_FOCUSED) )
1148    {
1149        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1150            SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1151 
1152        /* This is wrong. Message sequences seem to indicate that this
1153           is set *after* the notify. */
1154        /* lphc->wState |= CBF_FOCUSED;  */
1155 
1156        if( !(lphc->wState & CBF_EDIT) )
1157 	 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1158 
1159        CB_NOTIFY( lphc, CBN_SETFOCUS );
1160        lphc->wState |= CBF_FOCUSED;
1161    }
1162 }
1163 
1164 /***********************************************************************
1165  *           COMBO_KillFocus
1166  */
1167 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1168 {
1169    HWND	hWnd = lphc->self;
1170 
1171    if( lphc->wState & CBF_FOCUSED )
1172    {
1173        CBRollUp( lphc, FALSE, TRUE );
1174        if( IsWindow( hWnd ) )
1175        {
1176            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1177                SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1178 
1179 	   lphc->wState &= ~CBF_FOCUSED;
1180 
1181            /* redraw text */
1182 	   if( !(lphc->wState & CBF_EDIT) )
1183 	     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1184 
1185            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1186        }
1187    }
1188 }
1189 
1190 /***********************************************************************
1191  *           COMBO_Command
1192  */
1193 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1194 {
1195    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1196    {
1197        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1198 
1199        switch( HIWORD(wParam) >> 8 )
1200        {
1201 	   case (EN_SETFOCUS >> 8):
1202 
1203                TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1204 
1205 		COMBO_SetFocus( lphc );
1206 	        break;
1207 
1208 	   case (EN_KILLFOCUS >> 8):
1209 
1210                TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1211 
1212 		/* NOTE: it seems that Windows' edit control sends an
1213 		 * undocumented message WM_USER + 0x1B instead of this
1214 		 * notification (only when it happens to be a part of
1215 		 * the combo). ?? - AK.
1216 		 */
1217 
1218 		COMBO_KillFocus( lphc );
1219 		break;
1220 
1221 
1222 	   case (EN_CHANGE >> 8):
1223 	       /*
1224 	        * In some circumstances (when the selection of the combobox
1225 		* is changed for example) we don't want the EN_CHANGE notification
1226 		* to be forwarded to the parent of the combobox. This code
1227 		* checks a flag that is set in these occasions and ignores the
1228 		* notification.
1229 	        */
1230 		if (lphc->wState & CBF_NOLBSELECT)
1231 		{
1232 		  lphc->wState &= ~CBF_NOLBSELECT;
1233 		}
1234 		else
1235 		{
1236 		  CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1237 		}
1238 
1239 	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
1240 		  CB_NOTIFY( lphc, CBN_EDITCHANGE );
1241 		break;
1242 
1243 	   case (EN_UPDATE >> 8):
1244 	        if (!(lphc->wState & CBF_NOEDITNOTIFY))
1245 		  CB_NOTIFY( lphc, CBN_EDITUPDATE );
1246 		break;
1247 
1248 	   case (EN_ERRSPACE >> 8):
1249 		CB_NOTIFY( lphc, CBN_ERRSPACE );
1250        }
1251    }
1252    else if( lphc->hWndLBox == hWnd )
1253    {
1254        switch( (short)HIWORD(wParam) )
1255        {
1256 	   case LBN_ERRSPACE:
1257 		CB_NOTIFY( lphc, CBN_ERRSPACE );
1258 		break;
1259 
1260 	   case LBN_DBLCLK:
1261 		CB_NOTIFY( lphc, CBN_DBLCLK );
1262 		break;
1263 
1264 	   case LBN_SELCHANGE:
1265 	   case LBN_SELCANCEL:
1266 
1267                 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1268 
1269                 /* do not roll up if selection is being tracked
1270                  * by arrow keys in the dropdown listbox */
1271                 if (!(lphc->wState & CBF_NOROLLUP))
1272                 {
1273                     CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1274                 }
1275                 else lphc->wState &= ~CBF_NOROLLUP;
1276 
1277 		CB_NOTIFY( lphc, CBN_SELCHANGE );
1278 
1279 		if( HIWORD(wParam) == LBN_SELCHANGE)
1280 		{
1281 		   if( lphc->wState & CBF_EDIT )
1282 		       lphc->wState |= CBF_NOLBSELECT;
1283 		   CBPaintText( lphc, NULL );
1284 		}
1285                 break;
1286 
1287 	   case LBN_SETFOCUS:
1288 	   case LBN_KILLFOCUS:
1289 		/* nothing to do here since ComboLBox always resets the focus to its
1290 		 * combo/edit counterpart */
1291 		 break;
1292        }
1293    }
1294    return 0;
1295 }
1296 
1297 /***********************************************************************
1298  *           COMBO_ItemOp
1299  *
1300  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1301  */
1302 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1303 {
1304    HWND hWnd = lphc->self;
1305    UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1306 
1307    TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1308 
1309    switch( msg )
1310    {
1311    case WM_DELETEITEM:
1312        {
1313            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1314            lpIS->CtlType  = ODT_COMBOBOX;
1315            lpIS->CtlID    = id;
1316            lpIS->hwndItem = hWnd;
1317            break;
1318        }
1319    case WM_DRAWITEM:
1320        {
1321            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1322            lpIS->CtlType  = ODT_COMBOBOX;
1323            lpIS->CtlID    = id;
1324            lpIS->hwndItem = hWnd;
1325            break;
1326        }
1327    case WM_COMPAREITEM:
1328        {
1329            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1330            lpIS->CtlType  = ODT_COMBOBOX;
1331            lpIS->CtlID    = id;
1332            lpIS->hwndItem = hWnd;
1333            break;
1334        }
1335    case WM_MEASUREITEM:
1336        {
1337            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1338            lpIS->CtlType  = ODT_COMBOBOX;
1339            lpIS->CtlID    = id;
1340            break;
1341        }
1342    }
1343    return SendMessageW(lphc->owner, msg, id, lParam);
1344 }
1345 
1346 
1347 /***********************************************************************
1348  *           COMBO_GetTextW
1349  */
1350 static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
1351 {
1352     INT length;
1353 
1354     if( lphc->wState & CBF_EDIT )
1355         return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1356 
1357     /* get it from the listbox */
1358 
1359     if (!count || !buf) return 0;
1360     if( lphc->hWndLBox )
1361     {
1362         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1363         if (idx == LB_ERR) goto error;
1364         length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1365         if (length == LB_ERR) goto error;
1366 
1367         /* 'length' is without the terminating character */
1368         if (length >= count)
1369         {
1370             WCHAR *lpBuffer = heap_alloc((length + 1) * sizeof(WCHAR));
1371             if (!lpBuffer) goto error;
1372             length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1373 
1374             /* truncate if buffer is too short */
1375             if (length != LB_ERR)
1376             {
1377                 lstrcpynW( buf, lpBuffer, count );
1378                 length = count;
1379             }
1380             heap_free( lpBuffer );
1381         }
1382         else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1383 
1384         if (length == LB_ERR) return 0;
1385         return length;
1386     }
1387 
1388  error:  /* error - truncate string, return zero */
1389     buf[0] = 0;
1390     return 0;
1391 }
1392 
1393 /***********************************************************************
1394  *           CBResetPos
1395  *
1396  * This function sets window positions according to the updated
1397  * component placement struct.
1398  */
1399 static void CBResetPos(
1400   LPHEADCOMBO lphc,
1401   const RECT  *rectEdit,
1402   const RECT  *rectLB,
1403   BOOL        bRedraw)
1404 {
1405    BOOL	bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1406 
1407    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1408     * sizing messages */
1409 
1410    if( lphc->wState & CBF_EDIT )
1411      SetWindowPos( lphc->hWndEdit, 0,
1412 		   rectEdit->left, rectEdit->top,
1413 		   rectEdit->right - rectEdit->left,
1414 		   rectEdit->bottom - rectEdit->top,
1415                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1416 
1417    SetWindowPos( lphc->hWndLBox, 0,
1418 		 rectLB->left, rectLB->top,
1419                  rectLB->right - rectLB->left,
1420 		 rectLB->bottom - rectLB->top,
1421 		   SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1422 
1423    if( bDrop )
1424    {
1425        if( lphc->wState & CBF_DROPPED )
1426        {
1427            lphc->wState &= ~CBF_DROPPED;
1428            ShowWindow( lphc->hWndLBox, SW_HIDE );
1429        }
1430 
1431        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1432            RedrawWindow( lphc->self, NULL, 0,
1433                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1434    }
1435 }
1436 
1437 
1438 /***********************************************************************
1439  *           COMBO_Size
1440  */
1441 static void COMBO_Size( LPHEADCOMBO lphc )
1442 {
1443   /*
1444    * Those controls are always the same height. So we have to make sure
1445    * they are not resized to another value.
1446    */
1447   if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1448   {
1449     int newComboHeight, curComboHeight, curComboWidth;
1450     RECT rc;
1451 
1452     GetWindowRect(lphc->self, &rc);
1453     curComboHeight = rc.bottom - rc.top;
1454     curComboWidth = rc.right - rc.left;
1455     newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE();
1456 
1457     /*
1458      * Resizing a combobox has another side effect, it resizes the dropped
1459      * rectangle as well. However, it does it only if the new height for the
1460      * combobox is more than the height it should have. In other words,
1461      * if the application resizing the combobox only had the intention to resize
1462      * the actual control, for example, to do the layout of a dialog that is
1463      * resized, the height of the dropdown is not changed.
1464      */
1465     if( curComboHeight > newComboHeight )
1466     {
1467       TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1468             curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1469             lphc->droppedRect.top);
1470       lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1471     }
1472     /*
1473      * Restore original height
1474      */
1475     if( curComboHeight != newComboHeight )
1476       SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1477             SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW);
1478   }
1479 
1480   CBCalcPlacement(lphc->self,
1481 		  lphc,
1482 		  &lphc->textRect,
1483 		  &lphc->buttonRect,
1484 		  &lphc->droppedRect);
1485 
1486   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1487 }
1488 
1489 
1490 /***********************************************************************
1491  *           COMBO_Font
1492  */
1493 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1494 {
1495   /*
1496    * Set the font
1497    */
1498   lphc->hFont = hFont;
1499 
1500   /*
1501    * Propagate to owned windows.
1502    */
1503   if( lphc->wState & CBF_EDIT )
1504       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1505   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1506 
1507   /*
1508    * Redo the layout of the control.
1509    */
1510   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1511   {
1512     CBCalcPlacement(lphc->self,
1513 		    lphc,
1514 		    &lphc->textRect,
1515 		    &lphc->buttonRect,
1516 		    &lphc->droppedRect);
1517 
1518     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1519   }
1520   else
1521   {
1522     CBForceDummyResize(lphc);
1523   }
1524 }
1525 
1526 
1527 /***********************************************************************
1528  *           COMBO_SetItemHeight
1529  */
1530 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1531 {
1532    LRESULT	lRet = CB_ERR;
1533 
1534    if( index == -1 ) /* set text field height */
1535    {
1536        if( height < 32768 )
1537        {
1538            lphc->editHeight = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1539 
1540 	 /*
1541 	  * Redo the layout of the control.
1542 	  */
1543 	 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1544 	 {
1545 	   CBCalcPlacement(lphc->self,
1546 			   lphc,
1547 			   &lphc->textRect,
1548 			   &lphc->buttonRect,
1549 			   &lphc->droppedRect);
1550 
1551 	   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1552 	 }
1553 	 else
1554 	 {
1555 	   CBForceDummyResize(lphc);
1556 	 }
1557 
1558 	   lRet = height;
1559        }
1560    }
1561    else if ( CB_OWNERDRAWN(lphc) )	/* set listbox item height */
1562        lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1563    return lRet;
1564 }
1565 
1566 /***********************************************************************
1567  *           COMBO_SelectString
1568  */
1569 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText)
1570 {
1571    INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1572    if( index >= 0 )
1573    {
1574      if( lphc->wState & CBF_EDIT )
1575        CBUpdateEdit( lphc, index );
1576      else
1577      {
1578        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1579      }
1580    }
1581    return (LRESULT)index;
1582 }
1583 
1584 /***********************************************************************
1585  *           COMBO_LButtonDown
1586  */
1587 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1588 {
1589    POINT     pt;
1590    BOOL      bButton;
1591    HWND      hWnd = lphc->self;
1592 
1593    pt.x = (short)LOWORD(lParam);
1594    pt.y = (short)HIWORD(lParam);
1595    bButton = PtInRect(&lphc->buttonRect, pt);
1596 
1597    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1598        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1599    {
1600        lphc->wState |= CBF_BUTTONDOWN;
1601        if( lphc->wState & CBF_DROPPED )
1602        {
1603 	   /* got a click to cancel selection */
1604 
1605            lphc->wState &= ~CBF_BUTTONDOWN;
1606            CBRollUp( lphc, TRUE, FALSE );
1607 	   if( !IsWindow( hWnd ) ) return;
1608 
1609            if( lphc->wState & CBF_CAPTURE )
1610            {
1611                lphc->wState &= ~CBF_CAPTURE;
1612                ReleaseCapture();
1613            }
1614        }
1615        else
1616        {
1617 	   /* drop down the listbox and start tracking */
1618 
1619            lphc->wState |= CBF_CAPTURE;
1620            SetCapture( hWnd );
1621            CBDropDown( lphc );
1622        }
1623        if( bButton ) CBRepaintButton( lphc );
1624    }
1625 }
1626 
1627 /***********************************************************************
1628  *           COMBO_LButtonUp
1629  *
1630  * Release capture and stop tracking if needed.
1631  */
1632 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1633 {
1634    if( lphc->wState & CBF_CAPTURE )
1635    {
1636        lphc->wState &= ~CBF_CAPTURE;
1637        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1638        {
1639 	   INT index = CBUpdateLBox( lphc, TRUE );
1640 	   /* Update edit only if item is in the list */
1641 	   if(index >= 0)
1642 	   {
1643 	       lphc->wState |= CBF_NOLBSELECT;
1644 	       CBUpdateEdit( lphc, index );
1645 	       lphc->wState &= ~CBF_NOLBSELECT;
1646 	   }
1647        }
1648        ReleaseCapture();
1649        SetCapture(lphc->hWndLBox);
1650    }
1651 
1652    if( lphc->wState & CBF_BUTTONDOWN )
1653    {
1654        lphc->wState &= ~CBF_BUTTONDOWN;
1655        CBRepaintButton( lphc );
1656    }
1657 }
1658 
1659 /***********************************************************************
1660  *           COMBO_MouseMove
1661  *
1662  * Two things to do - track combo button and release capture when
1663  * pointer goes into the listbox.
1664  */
1665 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1666 {
1667    POINT  pt;
1668    RECT   lbRect;
1669 
1670    pt.x = (short)LOWORD(lParam);
1671    pt.y = (short)HIWORD(lParam);
1672 
1673    if( lphc->wState & CBF_BUTTONDOWN )
1674    {
1675      BOOL bButton;
1676 
1677      bButton = PtInRect(&lphc->buttonRect, pt);
1678 
1679      if( !bButton )
1680      {
1681        lphc->wState &= ~CBF_BUTTONDOWN;
1682        CBRepaintButton( lphc );
1683      }
1684    }
1685 
1686    GetClientRect( lphc->hWndLBox, &lbRect );
1687    MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1688    if( PtInRect(&lbRect, pt) )
1689    {
1690        lphc->wState &= ~CBF_CAPTURE;
1691        ReleaseCapture();
1692        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1693 
1694        /* hand over pointer tracking */
1695        SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1696    }
1697 }
1698 
1699 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1700 {
1701     if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1702         return FALSE;
1703 
1704     pcbi->rcItem = lphc->textRect;
1705     pcbi->rcButton = lphc->buttonRect;
1706     pcbi->stateButton = 0;
1707     if (lphc->wState & CBF_BUTTONDOWN)
1708         pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1709     if (IsRectEmpty(&lphc->buttonRect))
1710         pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1711     pcbi->hwndCombo = lphc->self;
1712     pcbi->hwndItem = lphc->hWndEdit;
1713     pcbi->hwndList = lphc->hWndLBox;
1714     return TRUE;
1715 }
1716 
1717 static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1718 {
1719     HEADCOMBO *lphc = (HEADCOMBO *)GetWindowLongPtrW( hwnd, 0 );
1720     HTHEME theme;
1721 
1722     TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", hwnd, message, wParam, lParam );
1723 
1724     if (!IsWindow(hwnd)) return 0;
1725 
1726     if (lphc || message == WM_NCCREATE)
1727     switch(message)
1728     {
1729     case WM_NCCREATE:
1730     {
1731         LONG style = ((CREATESTRUCTW *)lParam)->style;
1732         return COMBO_NCCreate(hwnd, style);
1733     }
1734 
1735     case WM_NCDESTROY:
1736         COMBO_NCDestroy(lphc);
1737         break;/* -> DefWindowProc */
1738 
1739     case WM_CREATE:
1740     {
1741         HWND hwndParent;
1742         LONG style;
1743 
1744         hwndParent = ((CREATESTRUCTW *)lParam)->hwndParent;
1745         style = ((CREATESTRUCTW *)lParam)->style;
1746         return COMBO_Create(hwnd, lphc, hwndParent, style);
1747     }
1748 
1749     case WM_DESTROY:
1750         theme = GetWindowTheme( hwnd );
1751         CloseThemeData( theme );
1752         break;
1753 
1754     case WM_THEMECHANGED:
1755         theme = GetWindowTheme( hwnd );
1756         CloseThemeData( theme );
1757         OpenThemeData( hwnd, WC_COMBOBOXW );
1758         break;
1759 
1760     case WM_PRINTCLIENT:
1761     case WM_PAINT:
1762     {
1763         LRESULT ret = 0;
1764         PAINTSTRUCT ps;
1765         HDC hdc;
1766 
1767         hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1768 
1769         if (hdc && !(lphc->wState & CBF_NOREDRAW))
1770         {
1771             HTHEME theme = GetWindowTheme(hwnd);
1772 
1773             if (theme)
1774                 ret = COMBO_ThemedPaint(theme, lphc, hdc);
1775             else
1776                 ret = COMBO_Paint(lphc, hdc);
1777         }
1778 
1779         if (!wParam)
1780             EndPaint(hwnd, &ps);
1781 
1782         return ret;
1783     }
1784     case WM_ERASEBKGND:
1785         /* do all painting in WM_PAINT like Windows does */
1786         return 1;
1787 
1788     case WM_GETDLGCODE:
1789     {
1790         LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1791         if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1792         {
1793             int vk = (int)((LPMSG)lParam)->wParam;
1794 
1795             if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1796                 result |= DLGC_WANTMESSAGE;
1797         }
1798         return  result;
1799     }
1800 
1801     case WM_SIZE:
1802         if (lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE))
1803             COMBO_Size( lphc );
1804         return  TRUE;
1805 
1806     case WM_SETFONT:
1807         COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1808         return TRUE;
1809 
1810     case WM_GETFONT:
1811         return (LRESULT)lphc->hFont;
1812 
1813     case WM_SETFOCUS:
1814         if (lphc->wState & CBF_EDIT)
1815         {
1816             SetFocus( lphc->hWndEdit );
1817             /* The first time focus is received, select all the text */
1818             if (!(lphc->wState & CBF_BEENFOCUSED))
1819             {
1820                 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1821                 lphc->wState |= CBF_BEENFOCUSED;
1822             }
1823         }
1824         else
1825             COMBO_SetFocus( lphc );
1826         return  TRUE;
1827 
1828     case WM_KILLFOCUS:
1829     {
1830         HWND hwndFocus = (HWND)wParam;
1831         if (!hwndFocus || (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox))
1832             COMBO_KillFocus( lphc );
1833         return  TRUE;
1834     }
1835 
1836     case WM_COMMAND:
1837         return COMBO_Command( lphc, wParam, (HWND)lParam );
1838 
1839     case WM_GETTEXT:
1840         return COMBO_GetText( lphc, wParam, (LPWSTR)lParam );
1841 
1842     case WM_SETTEXT:
1843     case WM_GETTEXTLENGTH:
1844     case WM_CLEAR:
1845         if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1846         {
1847             int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1848             if (j == -1) return 0;
1849             return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1850         }
1851         else if ( lphc->wState & CBF_EDIT )
1852         {
1853             LRESULT ret;
1854             lphc->wState |= CBF_NOEDITNOTIFY;
1855             ret = SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1856             lphc->wState &= ~CBF_NOEDITNOTIFY;
1857             return ret;
1858         }
1859         else
1860             return CB_ERR;
1861 
1862     case WM_CUT:
1863     case WM_PASTE:
1864     case WM_COPY:
1865         if (lphc->wState & CBF_EDIT)
1866             return SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1867         else return  CB_ERR;
1868 
1869     case WM_DRAWITEM:
1870     case WM_DELETEITEM:
1871     case WM_COMPAREITEM:
1872     case WM_MEASUREITEM:
1873         return COMBO_ItemOp(lphc, message, lParam);
1874 
1875     case WM_ENABLE:
1876         if (lphc->wState & CBF_EDIT)
1877             EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1878         EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1879 
1880         /* Force the control to repaint when the enabled state changes. */
1881         InvalidateRect(lphc->self, NULL, TRUE);
1882         return  TRUE;
1883 
1884     case WM_SETREDRAW:
1885         if (wParam)
1886             lphc->wState &= ~CBF_NOREDRAW;
1887         else
1888             lphc->wState |= CBF_NOREDRAW;
1889 
1890         if ( lphc->wState & CBF_EDIT )
1891             SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1892         SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1893         return  0;
1894 
1895     case WM_SYSKEYDOWN:
1896         if ( KEYDATA_ALT & HIWORD(lParam) )
1897             if( wParam == VK_UP || wParam == VK_DOWN )
1898                 COMBO_FlipListbox( lphc, FALSE, FALSE );
1899         return  0;
1900 
1901     case WM_KEYDOWN:
1902         if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1903                 (lphc->wState & CBF_DROPPED))
1904         {
1905             CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1906             return TRUE;
1907         }
1908         else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1909         {
1910             COMBO_FlipListbox( lphc, FALSE, FALSE );
1911             return TRUE;
1912         }
1913         /* fall through */
1914     case WM_CHAR:
1915     case WM_IME_CHAR:
1916         {
1917             HWND hwndTarget;
1918 
1919             if ( lphc->wState & CBF_EDIT )
1920                 hwndTarget = lphc->hWndEdit;
1921             else
1922                 hwndTarget = lphc->hWndLBox;
1923 
1924            return SendMessageW(hwndTarget, message, wParam, lParam);
1925         }
1926 
1927     case WM_LBUTTONDOWN:
1928         if ( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1929         if ( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1930         return  TRUE;
1931 
1932     case WM_LBUTTONUP:
1933         COMBO_LButtonUp( lphc );
1934         return  TRUE;
1935 
1936     case WM_MOUSEMOVE:
1937         if (!IsRectEmpty(&lphc->buttonRect))
1938         {
1939             POINT pt;
1940 
1941             pt.x = (short)LOWORD(lParam);
1942             pt.y = (short)HIWORD(lParam);
1943 
1944             if (PtInRect(&lphc->buttonRect, pt))
1945             {
1946                 if (!(lphc->wState & CBF_HOT))
1947                 {
1948                     lphc->wState |= CBF_HOT;
1949                     RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1950                 }
1951             }
1952             else if (lphc->wState & CBF_HOT)
1953             {
1954                 lphc->wState &= ~CBF_HOT;
1955                 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1956             }
1957         }
1958 
1959         if ( lphc->wState & CBF_CAPTURE )
1960             COMBO_MouseMove( lphc, wParam, lParam );
1961         return  TRUE;
1962 
1963     case WM_MOUSEWHEEL:
1964         if (wParam & (MK_SHIFT | MK_CONTROL))
1965             return DefWindowProcW(hwnd, message, wParam, lParam);
1966 
1967         if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1968         if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1969         return TRUE;
1970 
1971     /* Combo messages */
1972     case CB_ADDSTRING:
1973         if (lphc->dwStyle & CBS_LOWERCASE)
1974             CharLowerW((LPWSTR)lParam);
1975         else if (lphc->dwStyle & CBS_UPPERCASE)
1976             CharUpperW((LPWSTR)lParam);
1977         return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1978 
1979     case CB_INSERTSTRING:
1980         if (lphc->dwStyle & CBS_LOWERCASE)
1981             CharLowerW((LPWSTR)lParam);
1982         else if (lphc->dwStyle & CBS_UPPERCASE)
1983             CharUpperW((LPWSTR)lParam);
1984         return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1985 
1986     case CB_DELETESTRING:
1987         return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1988 
1989     case CB_SELECTSTRING:
1990         return COMBO_SelectString(lphc, (INT)wParam, lParam);
1991 
1992     case CB_FINDSTRING:
1993         return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1994 
1995     case CB_FINDSTRINGEXACT:
1996         return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1997 
1998     case CB_SETITEMHEIGHT:
1999         return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2000 
2001     case CB_GETITEMHEIGHT:
2002         if ((INT)wParam >= 0) /* listbox item */
2003             return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2004         return  CBGetTextAreaHeight(hwnd, lphc);
2005 
2006     case CB_RESETCONTENT:
2007         SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2008 
2009         if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc))
2010         {
2011             static const WCHAR empty_stringW[] = { 0 };
2012             SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2013         }
2014         else
2015             InvalidateRect(lphc->self, NULL, TRUE);
2016         return  TRUE;
2017 
2018     case CB_INITSTORAGE:
2019         return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2020 
2021     case CB_GETHORIZONTALEXTENT:
2022         return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2023 
2024     case CB_SETHORIZONTALEXTENT:
2025         return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2026 
2027     case CB_GETTOPINDEX:
2028         return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2029 
2030     case CB_GETLOCALE:
2031         return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2032 
2033     case CB_SETLOCALE:
2034         return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2035 
2036     case CB_SETDROPPEDWIDTH:
2037         if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768)
2038             return CB_ERR;
2039 
2040         /* new value must be higher than combobox width */
2041         if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2042             lphc->droppedWidth = wParam;
2043         else if (wParam)
2044             lphc->droppedWidth = 0;
2045 
2046         /* recalculate the combobox area */
2047         CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
2048 
2049         /* fall through */
2050     case CB_GETDROPPEDWIDTH:
2051         if (lphc->droppedWidth)
2052             return lphc->droppedWidth;
2053         return  lphc->droppedRect.right - lphc->droppedRect.left;
2054 
2055     case CB_GETDROPPEDCONTROLRECT:
2056         if (lParam)
2057             CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2058         return  CB_OKAY;
2059 
2060     case CB_GETDROPPEDSTATE:
2061         return (lphc->wState & CBF_DROPPED) != 0;
2062 
2063     case CB_DIR:
2064         return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam);
2065 
2066     case CB_SHOWDROPDOWN:
2067         if (CB_GETTYPE(lphc) != CBS_SIMPLE)
2068         {
2069             if (wParam)
2070             {
2071                 if (!(lphc->wState & CBF_DROPPED))
2072                     CBDropDown( lphc );
2073             }
2074             else if (lphc->wState & CBF_DROPPED)
2075                 CBRollUp( lphc, FALSE, TRUE );
2076         }
2077         return  TRUE;
2078 
2079     case CB_GETCOUNT:
2080         return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2081 
2082     case CB_GETCURSEL:
2083         return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2084 
2085     case CB_SETCURSEL:
2086         lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2087         if (lParam >= 0)
2088             SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2089 
2090         /* no LBN_SELCHANGE in this case, update manually */
2091         CBPaintText(lphc, NULL);
2092         lphc->wState &= ~CBF_SELCHANGE;
2093         return lParam;
2094 
2095     case CB_GETLBTEXT:
2096         return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2097 
2098     case CB_GETLBTEXTLEN:
2099         return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2100 
2101     case CB_GETITEMDATA:
2102         return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2103 
2104     case CB_SETITEMDATA:
2105         return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2106 
2107     case CB_GETEDITSEL:
2108         /* Edit checks passed parameters itself */
2109         if (lphc->wState & CBF_EDIT)
2110             return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2111         return  CB_ERR;
2112 
2113     case CB_SETEDITSEL:
2114         if (lphc->wState & CBF_EDIT)
2115             return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2116         return  CB_ERR;
2117 
2118     case CB_SETEXTENDEDUI:
2119         if (CB_GETTYPE(lphc) == CBS_SIMPLE )
2120             return  CB_ERR;
2121         if (wParam)
2122             lphc->wState |= CBF_EUI;
2123         else
2124             lphc->wState &= ~CBF_EUI;
2125         return  CB_OKAY;
2126 
2127     case CB_GETEXTENDEDUI:
2128         return (lphc->wState & CBF_EUI) != 0;
2129 
2130     case CB_GETCOMBOBOXINFO:
2131         return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2132 
2133     case CB_LIMITTEXT:
2134         if (lphc->wState & CBF_EDIT)
2135             return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2136         return  TRUE;
2137 
2138     default:
2139         if (message >= WM_USER)
2140             WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n", message - WM_USER, wParam, lParam );
2141         break;
2142     }
2143 
2144     return DefWindowProcW(hwnd, message, wParam, lParam);
2145 }
2146 
2147 void COMBO_Register(void)
2148 {
2149     WNDCLASSW wndClass;
2150 
2151     memset(&wndClass, 0, sizeof(wndClass));
2152     wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2153     wndClass.lpfnWndProc = COMBO_WindowProc;
2154     wndClass.cbClsExtra = 0;
2155     wndClass.cbWndExtra = sizeof(HEADCOMBO *);
2156     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2157     wndClass.hbrBackground = NULL;
2158     wndClass.lpszClassName = WC_COMBOBOXW;
2159     RegisterClassW(&wndClass);
2160 }
2161 
2162 #ifdef __REACTOS__
2163 void COMBO_Unregister(void)
2164 {
2165     UnregisterClassW(WC_COMBOBOXW, NULL);
2166 }
2167 #endif
2168