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