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