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