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