user32: Get rid of 16-bit handles in the driver clipboard interface.
[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, (WPARAM)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, (LPARAM)(-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,
780                                         (WPARAM)id, 0 );
781
782        /*
783         * Clip the DC and have the parent draw the item.
784         */
785        IntersectClipRect(hdc,
786                          rectEdit.left,  rectEdit.top,
787                          rectEdit.right, rectEdit.bottom);
788
789        SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
790
791        /*
792         * Reset the clipping region.
793         */
794        SelectClipRgn(hdc, clipRegion);
795      }
796      else
797      {
798        static const WCHAR empty_stringW[] = { 0 };
799
800        if ( (lphc->wState & CBF_FOCUSED) &&
801             !(lphc->wState & CBF_DROPPED) ) {
802
803            /* highlight */
804            FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
805            SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
806            SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
807        }
808
809        ExtTextOutW( hdc,
810                     rectEdit.left + 1,
811                     rectEdit.top + 1,
812                     ETO_OPAQUE | ETO_CLIPPED,
813                     &rectEdit,
814                     pText ? pText : empty_stringW , size, NULL );
815
816        if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
817          DrawFocusRect( hdc, &rectEdit );
818      }
819
820      if( hPrevFont )
821        SelectObject(hdc, hPrevFont );
822    }
823    HeapFree( GetProcessHeap(), 0, pText );
824 }
825
826 /***********************************************************************
827  *           CBPaintBorder
828  */
829 static void CBPaintBorder(
830   HWND            hwnd,
831   const HEADCOMBO *lphc,
832   HDC             hdc)
833 {
834   RECT clientRect;
835
836   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
837   {
838     GetClientRect(hwnd, &clientRect);
839   }
840   else
841   {
842     CopyRect(&clientRect, &lphc->textRect);
843
844     InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
845     InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
846   }
847
848   DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
849 }
850
851 /***********************************************************************
852  *           COMBO_PrepareColors
853  *
854  * This method will sent the appropriate WM_CTLCOLOR message to
855  * prepare and setup the colors for the combo's DC.
856  *
857  * It also returns the brush to use for the background.
858  */
859 static HBRUSH COMBO_PrepareColors(
860   LPHEADCOMBO lphc,
861   HDC         hDC)
862 {
863   HBRUSH  hBkgBrush;
864
865   /*
866    * Get the background brush for this control.
867    */
868   if (CB_DISABLED(lphc))
869   {
870     hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
871                                      (WPARAM)hDC, (LPARAM)lphc->self );
872
873     /*
874      * We have to change the text color since WM_CTLCOLORSTATIC will
875      * set it to the "enabled" color. This is the same behavior as the
876      * edit control
877      */
878     SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
879   }
880   else
881   {
882       /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
883       hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
884                                        (WPARAM)hDC, (LPARAM)lphc->self );
885   }
886
887   /*
888    * Catch errors.
889    */
890   if( !hBkgBrush )
891     hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
892
893   return hBkgBrush;
894 }
895
896
897 /***********************************************************************
898  *           COMBO_Paint
899  */
900 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
901 {
902   PAINTSTRUCT ps;
903   HDC   hDC;
904
905   hDC = (hParamDC) ? hParamDC
906                    : BeginPaint( lphc->self, &ps);
907
908   TRACE("hdc=%p\n", hDC);
909
910   if( hDC && !(lphc->wState & CBF_NOREDRAW) )
911   {
912       HBRUSH    hPrevBrush, hBkgBrush;
913
914       /*
915        * Retrieve the background brush and select it in the
916        * DC.
917        */
918       hBkgBrush = COMBO_PrepareColors(lphc, hDC);
919
920       hPrevBrush = SelectObject( hDC, hBkgBrush );
921       if (!(lphc->wState & CBF_EDIT))
922         FillRect(hDC, &lphc->textRect, hBkgBrush);
923
924       /*
925        * In non 3.1 look, there is a sunken border on the combobox
926        */
927       CBPaintBorder(lphc->self, lphc, hDC);
928
929       if( !IsRectEmpty(&lphc->buttonRect) )
930       {
931         CBPaintButton(lphc, hDC, lphc->buttonRect);
932       }
933
934       /* paint the edit control padding area */
935       if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
936       {
937           RECT rPadEdit = lphc->textRect;
938
939           InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
940
941           FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
942       }
943
944       if( !(lphc->wState & CBF_EDIT) )
945         CBPaintText( lphc, hDC, lphc->textRect);
946
947       if( hPrevBrush )
948         SelectObject( hDC, hPrevBrush );
949   }
950
951   if( !hParamDC )
952     EndPaint(lphc->self, &ps);
953
954   return 0;
955 }
956
957 /***********************************************************************
958  *           CBUpdateLBox
959  *
960  * Select listbox entry according to the contents of the edit control.
961  */
962 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
963 {
964    INT  length, idx;
965    LPWSTR pText = NULL;
966
967    idx = LB_ERR;
968    length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
969
970    if( length > 0 )
971        pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
972
973    TRACE("\t edit text length %i\n", length );
974
975    if( pText )
976    {
977        GetWindowTextW( lphc->hWndEdit, pText, length + 1);
978        idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
979                              (WPARAM)(-1), (LPARAM)pText );
980        HeapFree( GetProcessHeap(), 0, pText );
981    }
982
983    SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
984
985    /* probably superfluous but Windows sends this too */
986    SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
987    SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
988
989    return idx;
990 }
991
992 /***********************************************************************
993  *           CBUpdateEdit
994  *
995  * Copy a listbox entry to the edit control.
996  */
997 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
998 {
999    INT  length;
1000    LPWSTR pText = NULL;
1001    static const WCHAR empty_stringW[] = { 0 };
1002
1003    TRACE("\t %i\n", index );
1004
1005    if( index >= 0 ) /* got an entry */
1006    {
1007        length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1008        if( length != LB_ERR)
1009        {
1010            if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1011            {
1012                 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1013                                 (WPARAM)index, (LPARAM)pText );
1014            }
1015        }
1016    }
1017
1018    if( CB_HASSTRINGS(lphc) )
1019    {
1020       lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1021       SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1022       lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1023    }
1024
1025    if( lphc->wState & CBF_FOCUSED )
1026       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1027
1028    HeapFree( GetProcessHeap(), 0, pText );
1029 }
1030
1031 /***********************************************************************
1032  *           CBDropDown
1033  *
1034  * Show listbox popup.
1035  */
1036 static void CBDropDown( LPHEADCOMBO lphc )
1037 {
1038     HMONITOR monitor;
1039     MONITORINFO mon_info;
1040    RECT rect,r;
1041    int nItems = 0;
1042    int nDroppedHeight;
1043
1044    TRACE("[%p]: drop down\n", lphc->self);
1045
1046    CB_NOTIFY( lphc, CBN_DROPDOWN );
1047
1048    /* set selection */
1049
1050    lphc->wState |= CBF_DROPPED;
1051    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1052    {
1053        lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1054
1055        /* Update edit only if item is in the list */
1056        if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1057          CBUpdateEdit( lphc, lphc->droppedIndex );
1058    }
1059    else
1060    {
1061        lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1062
1063        SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1064                      (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1065        SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1066    }
1067
1068    /* now set popup position */
1069    GetWindowRect( lphc->self, &rect );
1070
1071    /*
1072     * If it's a dropdown, the listbox is offset
1073     */
1074    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1075      rect.left += COMBO_EDITBUTTONSPACE();
1076
1077   /* if the dropped height is greater than the total height of the dropped
1078      items list, then force the drop down list height to be the total height
1079      of the items in the dropped list */
1080
1081   /* And Remove any extra space (Best Fit) */
1082    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1083   /* if listbox length has been set directly by its handle */
1084    GetWindowRect(lphc->hWndLBox, &r);
1085    if (nDroppedHeight < r.bottom - r.top)
1086        nDroppedHeight = r.bottom - r.top;
1087    nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1088
1089    if (nItems > 0)
1090    {
1091       int nHeight;
1092       int nIHeight;
1093
1094       nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1095
1096       nHeight = nIHeight*nItems;
1097
1098       if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1099          nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1100
1101       if (nDroppedHeight < nHeight)
1102       {
1103             if (nItems < 5)
1104                 nDroppedHeight = (nItems+1)*nIHeight;
1105             else if (nDroppedHeight < 6*nIHeight)
1106                 nDroppedHeight = 6*nIHeight;
1107       }
1108    }
1109
1110    /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1111    monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1112    mon_info.cbSize = sizeof(mon_info);
1113    GetMonitorInfoW( monitor, &mon_info );
1114
1115    if( (rect.bottom + nDroppedHeight) >= mon_info.rcWork.bottom )
1116       rect.bottom = rect.top - nDroppedHeight;
1117
1118    SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1119                  lphc->droppedRect.right - lphc->droppedRect.left,
1120                  nDroppedHeight,
1121                  SWP_NOACTIVATE | SWP_SHOWWINDOW);
1122
1123
1124    if( !(lphc->wState & CBF_NOREDRAW) )
1125      RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1126                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1127
1128    EnableWindow( lphc->hWndLBox, TRUE );
1129    if (GetCapture() != lphc->self)
1130       SetCapture(lphc->hWndLBox);
1131 }
1132
1133 /***********************************************************************
1134  *           CBRollUp
1135  *
1136  * Hide listbox popup.
1137  */
1138 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1139 {
1140    HWND hWnd = lphc->self;
1141
1142    TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1143          lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1144
1145    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1146
1147    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1148    {
1149
1150        if( lphc->wState & CBF_DROPPED )
1151        {
1152            RECT rect;
1153
1154            lphc->wState &= ~CBF_DROPPED;
1155            ShowWindow( lphc->hWndLBox, SW_HIDE );
1156
1157            if(GetCapture() == lphc->hWndLBox)
1158            {
1159                ReleaseCapture();
1160            }
1161
1162            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1163            {
1164                rect = lphc->buttonRect;
1165            }
1166            else
1167            {
1168                if( bButton )
1169                {
1170                  UnionRect( &rect,
1171                             &lphc->buttonRect,
1172                             &lphc->textRect);
1173                }
1174                else
1175                  rect = lphc->textRect;
1176
1177                bButton = TRUE;
1178            }
1179
1180            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1181                RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1182                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1183            CB_NOTIFY( lphc, CBN_CLOSEUP );
1184        }
1185    }
1186 }
1187
1188 /***********************************************************************
1189  *           COMBO_FlipListbox
1190  *
1191  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1192  */
1193 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1194 {
1195    if( lphc->wState & CBF_DROPPED )
1196    {
1197        CBRollUp( lphc, ok, bRedrawButton );
1198        return FALSE;
1199    }
1200
1201    CBDropDown( lphc );
1202    return TRUE;
1203 }
1204
1205 /***********************************************************************
1206  *           CBRepaintButton
1207  */
1208 static void CBRepaintButton( LPHEADCOMBO lphc )
1209    {
1210   InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1211   UpdateWindow(lphc->self);
1212 }
1213
1214 /***********************************************************************
1215  *           COMBO_SetFocus
1216  */
1217 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1218 {
1219    if( !(lphc->wState & CBF_FOCUSED) )
1220    {
1221        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1222            SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1223
1224        /* This is wrong. Message sequences seem to indicate that this
1225           is set *after* the notify. */
1226        /* lphc->wState |= CBF_FOCUSED;  */
1227
1228        if( !(lphc->wState & CBF_EDIT) )
1229          InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1230
1231        CB_NOTIFY( lphc, CBN_SETFOCUS );
1232        lphc->wState |= CBF_FOCUSED;
1233    }
1234 }
1235
1236 /***********************************************************************
1237  *           COMBO_KillFocus
1238  */
1239 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1240 {
1241    HWND hWnd = lphc->self;
1242
1243    if( lphc->wState & CBF_FOCUSED )
1244    {
1245        CBRollUp( lphc, FALSE, TRUE );
1246        if( IsWindow( hWnd ) )
1247        {
1248            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1249                SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1250
1251            lphc->wState &= ~CBF_FOCUSED;
1252
1253            /* redraw text */
1254            if( !(lphc->wState & CBF_EDIT) )
1255              InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1256
1257            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1258        }
1259    }
1260 }
1261
1262 /***********************************************************************
1263  *           COMBO_Command
1264  */
1265 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1266 {
1267    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1268    {
1269        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1270
1271        switch( HIWORD(wParam) >> 8 )
1272        {
1273            case (EN_SETFOCUS >> 8):
1274
1275                TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1276
1277                 COMBO_SetFocus( lphc );
1278                 break;
1279
1280            case (EN_KILLFOCUS >> 8):
1281
1282                TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1283
1284                 /* NOTE: it seems that Windows' edit control sends an
1285                  * undocumented message WM_USER + 0x1B instead of this
1286                  * notification (only when it happens to be a part of
1287                  * the combo). ?? - AK.
1288                  */
1289
1290                 COMBO_KillFocus( lphc );
1291                 break;
1292
1293
1294            case (EN_CHANGE >> 8):
1295                /*
1296                 * In some circumstances (when the selection of the combobox
1297                 * is changed for example) we don't want the EN_CHANGE notification
1298                 * to be forwarded to the parent of the combobox. This code
1299                 * checks a flag that is set in these occasions and ignores the
1300                 * notification.
1301                 */
1302                 if (lphc->wState & CBF_NOLBSELECT)
1303                 {
1304                   lphc->wState &= ~CBF_NOLBSELECT;
1305                 }
1306                 else
1307                 {
1308                   CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1309                 }
1310
1311                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1312                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1313                 break;
1314
1315            case (EN_UPDATE >> 8):
1316                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1317                   CB_NOTIFY( lphc, CBN_EDITUPDATE );
1318                 break;
1319
1320            case (EN_ERRSPACE >> 8):
1321                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1322        }
1323    }
1324    else if( lphc->hWndLBox == hWnd )
1325    {
1326        switch( (short)HIWORD(wParam) )
1327        {
1328            case LBN_ERRSPACE:
1329                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1330                 break;
1331
1332            case LBN_DBLCLK:
1333                 CB_NOTIFY( lphc, CBN_DBLCLK );
1334                 break;
1335
1336            case LBN_SELCHANGE:
1337            case LBN_SELCANCEL:
1338
1339                 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1340
1341                 /* do not roll up if selection is being tracked
1342                  * by arrow keys in the dropdown listbox */
1343                 if (!(lphc->wState & CBF_NOROLLUP))
1344                 {
1345                     CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1346                 }
1347                 else lphc->wState &= ~CBF_NOROLLUP;
1348
1349                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1350
1351                 if( HIWORD(wParam) == LBN_SELCHANGE)
1352                 {
1353                    if( lphc->wState & CBF_EDIT )
1354                    {
1355                        INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1356                        lphc->wState |= CBF_NOLBSELECT;
1357                        CBUpdateEdit( lphc, index );
1358                        /* select text in edit, as Windows does */
1359                        SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1360                    }
1361                    else
1362                    {
1363                        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1364                        UpdateWindow(lphc->self);
1365                    }
1366                 }
1367                 break;
1368
1369            case LBN_SETFOCUS:
1370            case LBN_KILLFOCUS:
1371                 /* nothing to do here since ComboLBox always resets the focus to its
1372                  * combo/edit counterpart */
1373                  break;
1374        }
1375    }
1376    return 0;
1377 }
1378
1379 /***********************************************************************
1380  *           COMBO_ItemOp
1381  *
1382  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1383  */
1384 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1385 {
1386    HWND hWnd = lphc->self;
1387    UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1388
1389    TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1390
1391    switch( msg )
1392    {
1393    case WM_DELETEITEM:
1394        {
1395            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1396            lpIS->CtlType  = ODT_COMBOBOX;
1397            lpIS->CtlID    = id;
1398            lpIS->hwndItem = hWnd;
1399            break;
1400        }
1401    case WM_DRAWITEM:
1402        {
1403            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1404            lpIS->CtlType  = ODT_COMBOBOX;
1405            lpIS->CtlID    = id;
1406            lpIS->hwndItem = hWnd;
1407            break;
1408        }
1409    case WM_COMPAREITEM:
1410        {
1411            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1412            lpIS->CtlType  = ODT_COMBOBOX;
1413            lpIS->CtlID    = id;
1414            lpIS->hwndItem = hWnd;
1415            break;
1416        }
1417    case WM_MEASUREITEM:
1418        {
1419            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1420            lpIS->CtlType  = ODT_COMBOBOX;
1421            lpIS->CtlID    = id;
1422            break;
1423        }
1424    }
1425    return SendMessageW(lphc->owner, msg, id, lParam);
1426 }
1427
1428
1429 /***********************************************************************
1430  *           COMBO_GetTextW
1431  */
1432 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1433 {
1434     INT length;
1435
1436     if( lphc->wState & CBF_EDIT )
1437         return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1438
1439     /* get it from the listbox */
1440
1441     if (!count || !buf) return 0;
1442     if( lphc->hWndLBox )
1443     {
1444         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1445         if (idx == LB_ERR) goto error;
1446         length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1447         if (length == LB_ERR) goto error;
1448
1449         /* 'length' is without the terminating character */
1450         if (length >= count)
1451         {
1452             LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1453             if (!lpBuffer) goto error;
1454             length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1455
1456             /* truncate if buffer is too short */
1457             if (length != LB_ERR)
1458             {
1459                 lstrcpynW( buf, lpBuffer, count );
1460                 length = count;
1461             }
1462             HeapFree( GetProcessHeap(), 0, lpBuffer );
1463         }
1464         else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1465
1466         if (length == LB_ERR) return 0;
1467         return length;
1468     }
1469
1470  error:  /* error - truncate string, return zero */
1471     buf[0] = 0;
1472     return 0;
1473 }
1474
1475
1476 /***********************************************************************
1477  *           COMBO_GetTextA
1478  *
1479  * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1480  *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1481  */
1482 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1483 {
1484     INT length;
1485
1486     if( lphc->wState & CBF_EDIT )
1487         return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1488
1489     /* get it from the listbox */
1490
1491     if (!count || !buf) return 0;
1492     if( lphc->hWndLBox )
1493     {
1494         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1495         if (idx == LB_ERR) goto error;
1496         length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1497         if (length == LB_ERR) goto error;
1498
1499         /* 'length' is without the terminating character */
1500         if (length >= count)
1501         {
1502             LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1503             if (!lpBuffer) goto error;
1504             length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1505
1506             /* truncate if buffer is too short */
1507             if (length != LB_ERR)
1508             {
1509                 lstrcpynA( buf, lpBuffer, count );
1510                 length = count;
1511             }
1512             HeapFree( GetProcessHeap(), 0, lpBuffer );
1513         }
1514         else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1515
1516         if (length == LB_ERR) return 0;
1517         return length;
1518     }
1519
1520  error:  /* error - truncate string, return zero */
1521     buf[0] = 0;
1522     return 0;
1523 }
1524
1525
1526 /***********************************************************************
1527  *           CBResetPos
1528  *
1529  * This function sets window positions according to the updated
1530  * component placement struct.
1531  */
1532 static void CBResetPos(
1533   LPHEADCOMBO lphc,
1534   const RECT  *rectEdit,
1535   const RECT  *rectLB,
1536   BOOL        bRedraw)
1537 {
1538    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1539
1540    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1541     * sizing messages */
1542
1543    if( lphc->wState & CBF_EDIT )
1544      SetWindowPos( lphc->hWndEdit, 0,
1545                    rectEdit->left, rectEdit->top,
1546                    rectEdit->right - rectEdit->left,
1547                    rectEdit->bottom - rectEdit->top,
1548                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1549
1550    SetWindowPos( lphc->hWndLBox, 0,
1551                  rectLB->left, rectLB->top,
1552                  rectLB->right - rectLB->left,
1553                  rectLB->bottom - rectLB->top,
1554                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1555
1556    if( bDrop )
1557    {
1558        if( lphc->wState & CBF_DROPPED )
1559        {
1560            lphc->wState &= ~CBF_DROPPED;
1561            ShowWindow( lphc->hWndLBox, SW_HIDE );
1562        }
1563
1564        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1565            RedrawWindow( lphc->self, NULL, 0,
1566                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1567    }
1568 }
1569
1570
1571 /***********************************************************************
1572  *           COMBO_Size
1573  */
1574 static void COMBO_Size( LPHEADCOMBO lphc, BOOL bRedraw )
1575   {
1576   CBCalcPlacement(lphc->self,
1577                   lphc,
1578                   &lphc->textRect,
1579                   &lphc->buttonRect,
1580                   &lphc->droppedRect);
1581
1582   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, bRedraw );
1583 }
1584
1585
1586 /***********************************************************************
1587  *           COMBO_Font
1588  */
1589 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1590 {
1591   /*
1592    * Set the font
1593    */
1594   lphc->hFont = hFont;
1595
1596   /*
1597    * Propagate to owned windows.
1598    */
1599   if( lphc->wState & CBF_EDIT )
1600       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1601   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1602
1603   /*
1604    * Redo the layout of the control.
1605    */
1606   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1607   {
1608     CBCalcPlacement(lphc->self,
1609                     lphc,
1610                     &lphc->textRect,
1611                     &lphc->buttonRect,
1612                     &lphc->droppedRect);
1613
1614     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1615   }
1616   else
1617   {
1618     CBForceDummyResize(lphc);
1619   }
1620 }
1621
1622
1623 /***********************************************************************
1624  *           COMBO_SetItemHeight
1625  */
1626 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1627 {
1628    LRESULT      lRet = CB_ERR;
1629
1630    if( index == -1 ) /* set text field height */
1631    {
1632        if( height < 32768 )
1633        {
1634            lphc->editHeight = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1635
1636          /*
1637           * Redo the layout of the control.
1638           */
1639          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1640          {
1641            CBCalcPlacement(lphc->self,
1642                            lphc,
1643                            &lphc->textRect,
1644                            &lphc->buttonRect,
1645                            &lphc->droppedRect);
1646
1647            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1648          }
1649          else
1650          {
1651            CBForceDummyResize(lphc);
1652          }
1653
1654            lRet = height;
1655        }
1656    }
1657    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1658         lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1659                               (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     memcpy(ret, str, len + 1);
1825     return ret;
1826 }
1827
1828 /***********************************************************************
1829  *           ComboWndProc_common
1830  */
1831 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1832 {
1833       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1834
1835       TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1836             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1837
1838       if (!IsWindow(hwnd)) return 0;
1839
1840       if( lphc || message == WM_NCCREATE )
1841       switch(message)
1842       {
1843
1844         /* System messages */
1845
1846         case WM_NCCREATE:
1847         {
1848                 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1849                                        ((LPCREATESTRUCTA)lParam)->style;
1850                 return COMBO_NCCreate(hwnd, style);
1851         }
1852         case WM_NCDESTROY:
1853                 COMBO_NCDestroy(lphc);
1854                 break;/* -> DefWindowProc */
1855
1856         case WM_CREATE:
1857         {
1858                 HWND hwndParent;
1859                 LONG style;
1860                 if(unicode)
1861                 {
1862                     hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1863                     style = ((LPCREATESTRUCTW)lParam)->style;
1864                 }
1865                 else
1866                 {
1867                     hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1868                     style = ((LPCREATESTRUCTA)lParam)->style;
1869                 }
1870                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1871         }
1872
1873         case WM_PRINTCLIENT:
1874                 /* Fallthrough */
1875         case WM_PAINT:
1876                 /* wParam may contain a valid HDC! */
1877                 return  COMBO_Paint(lphc, (HDC)wParam);
1878
1879         case WM_ERASEBKGND:
1880                 /* do all painting in WM_PAINT like Windows does */
1881                 return 1;
1882
1883         case WM_GETDLGCODE:
1884         {
1885                 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1886                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1887                 {
1888                    int vk = (int)((LPMSG)lParam)->wParam;
1889
1890                    if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1891                        result |= DLGC_WANTMESSAGE;
1892                 }
1893                 return  result;
1894         }
1895         case WM_WINDOWPOSCHANGING:
1896                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1897     case WM_WINDOWPOSCHANGED:
1898         /* SetWindowPos can be called on a Combobox to resize its Listbox.
1899          * In that case, the Combobox itself will not be resized, so we won't
1900          * get a WM_SIZE. Since we still want to update the Listbox, we have to
1901          * do it here.
1902          */
1903         /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
1904          * Z-order based painting.
1905          */
1906         /* fall through */
1907         case WM_SIZE:
1908                 if( lphc->hWndLBox &&
1909                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc, message == WM_SIZE );
1910                 return  TRUE;
1911         case WM_SETFONT:
1912                 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1913                 return  TRUE;
1914         case WM_GETFONT:
1915                 return  (LRESULT)lphc->hFont;
1916         case WM_SETFOCUS:
1917                 if( lphc->wState & CBF_EDIT )
1918                     SetFocus( lphc->hWndEdit );
1919                 else
1920                     COMBO_SetFocus( lphc );
1921                 return  TRUE;
1922         case WM_KILLFOCUS:
1923             {
1924                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1925                 if( !hwndFocus ||
1926                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1927                     COMBO_KillFocus( lphc );
1928                 return  TRUE;
1929             }
1930         case WM_COMMAND:
1931                 return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1932         case WM_GETTEXT:
1933             return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1934                            : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1935         case WM_SETTEXT:
1936         case WM_GETTEXTLENGTH:
1937         case WM_CLEAR:
1938                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1939                 {
1940                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1941                     if (j == -1) return 0;
1942                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1943                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1944                 }
1945                 else if( lphc->wState & CBF_EDIT )
1946                 {
1947                     LRESULT ret;
1948                     lphc->wState |= CBF_NOEDITNOTIFY;
1949                     ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1950                                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1951                     lphc->wState &= ~CBF_NOEDITNOTIFY;
1952                     return ret;
1953                 }
1954                 else return CB_ERR;
1955         case WM_CUT:
1956         case WM_PASTE:
1957         case WM_COPY:
1958                 if( lphc->wState & CBF_EDIT )
1959                 {
1960                     return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1961                                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1962                 }
1963                 else return  CB_ERR;
1964
1965         case WM_DRAWITEM:
1966         case WM_DELETEITEM:
1967         case WM_COMPAREITEM:
1968         case WM_MEASUREITEM:
1969                 return COMBO_ItemOp(lphc, message, lParam);
1970         case WM_ENABLE:
1971                 if( lphc->wState & CBF_EDIT )
1972                     EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1973                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1974
1975                 /* Force the control to repaint when the enabled state changes. */
1976                 InvalidateRect(lphc->self, NULL, TRUE);
1977                 return  TRUE;
1978         case WM_SETREDRAW:
1979                 if( wParam )
1980                     lphc->wState &= ~CBF_NOREDRAW;
1981                 else
1982                     lphc->wState |= CBF_NOREDRAW;
1983
1984                 if( lphc->wState & CBF_EDIT )
1985                     SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1986                 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1987                 return  0;
1988         case WM_SYSKEYDOWN:
1989                 if( KEYDATA_ALT & HIWORD(lParam) )
1990                     if( wParam == VK_UP || wParam == VK_DOWN )
1991                         COMBO_FlipListbox( lphc, FALSE, FALSE );
1992                 return  0;
1993
1994         case WM_CHAR:
1995         case WM_IME_CHAR:
1996         case WM_KEYDOWN:
1997         {
1998                 HWND hwndTarget;
1999
2000                 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2001                      (lphc->wState & CBF_DROPPED))
2002                 {
2003                    CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2004                    return TRUE;
2005                 }
2006                else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
2007                {
2008                   COMBO_FlipListbox( lphc, FALSE, FALSE );
2009                   return TRUE;
2010                }
2011
2012                 if( lphc->wState & CBF_EDIT )
2013                     hwndTarget = lphc->hWndEdit;
2014                 else
2015                     hwndTarget = lphc->hWndLBox;
2016
2017                 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2018                                  SendMessageA(hwndTarget, message, wParam, lParam);
2019         }
2020         case WM_LBUTTONDOWN:
2021                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2022                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2023                 return  TRUE;
2024         case WM_LBUTTONUP:
2025                 COMBO_LButtonUp( lphc );
2026                 return  TRUE;
2027         case WM_MOUSEMOVE:
2028                 if( lphc->wState & CBF_CAPTURE )
2029                     COMBO_MouseMove( lphc, wParam, lParam );
2030                 return  TRUE;
2031
2032         case WM_MOUSEWHEEL:
2033                 if (wParam & (MK_SHIFT | MK_CONTROL))
2034                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2035                                      DefWindowProcA(hwnd, message, wParam, lParam);
2036
2037                 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2038                 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2039                 return TRUE;
2040
2041         /* Combo messages */
2042
2043         case CB_ADDSTRING:
2044                 if( unicode )
2045                 {
2046                     if( lphc->dwStyle & CBS_LOWERCASE )
2047                         CharLowerW((LPWSTR)lParam);
2048                     else if( lphc->dwStyle & CBS_UPPERCASE )
2049                         CharUpperW((LPWSTR)lParam);
2050                     return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2051                 }
2052                 else /* unlike the unicode version, the ansi version does not overwrite
2053                         the string if converting case */
2054                 {
2055                     char *string = NULL;
2056                     LRESULT ret;
2057                     if( lphc->dwStyle & CBS_LOWERCASE )
2058                     {
2059                         string = strdupA((LPSTR)lParam);
2060                         CharLowerA(string);
2061                     }
2062
2063                     else if( lphc->dwStyle & CBS_UPPERCASE )
2064                     {
2065                         string = strdupA((LPSTR)lParam);
2066                         CharUpperA(string);
2067                     }
2068
2069                     ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
2070                     HeapFree(GetProcessHeap(), 0, string);
2071                     return ret;
2072                 }
2073         case CB_INSERTSTRING:
2074                 if( unicode )
2075                 {
2076                     if( lphc->dwStyle & CBS_LOWERCASE )
2077                         CharLowerW((LPWSTR)lParam);
2078                     else if( lphc->dwStyle & CBS_UPPERCASE )
2079                         CharUpperW((LPWSTR)lParam);
2080                     return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2081                 }
2082                 else
2083                 {
2084                     if( lphc->dwStyle & CBS_LOWERCASE )
2085                         CharLowerA((LPSTR)lParam);
2086                     else if( lphc->dwStyle & CBS_UPPERCASE )
2087                         CharUpperA((LPSTR)lParam);
2088
2089                     return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2090                 }
2091         case CB_DELETESTRING:
2092                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2093                                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2094         case CB_SELECTSTRING:
2095                 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2096         case CB_FINDSTRING:
2097                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2098                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2099         case CB_FINDSTRINGEXACT:
2100                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2101                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2102         case CB_SETITEMHEIGHT:
2103                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2104         case CB_GETITEMHEIGHT:
2105                 if( (INT)wParam >= 0 )  /* listbox item */
2106                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2107                 return  CBGetTextAreaHeight(hwnd, lphc);
2108         case CB_RESETCONTENT:
2109                 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2110                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2111                 {
2112                     static const WCHAR empty_stringW[] = { 0 };
2113                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2114                 }
2115                 else
2116                     InvalidateRect(lphc->self, NULL, TRUE);
2117                 return  TRUE;
2118         case CB_INITSTORAGE:
2119                 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2120         case CB_GETHORIZONTALEXTENT:
2121                 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2122         case CB_SETHORIZONTALEXTENT:
2123                 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2124         case CB_GETTOPINDEX:
2125                 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2126         case CB_GETLOCALE:
2127                 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2128         case CB_SETLOCALE:
2129                 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2130         case CB_GETDROPPEDWIDTH:
2131                 if( lphc->droppedWidth )
2132                     return  lphc->droppedWidth;
2133                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2134         case CB_SETDROPPEDWIDTH:
2135                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2136                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2137                 return  CB_ERR;
2138         case CB_GETDROPPEDCONTROLRECT:
2139                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2140                 return  CB_OKAY;
2141         case CB_GETDROPPEDSTATE:
2142                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2143         case CB_DIR:
2144                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2145                                  SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2146
2147         case CB_SHOWDROPDOWN:
2148                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2149                 {
2150                     if( wParam )
2151                     {
2152                         if( !(lphc->wState & CBF_DROPPED) )
2153                             CBDropDown( lphc );
2154                     }
2155                     else
2156                         if( lphc->wState & CBF_DROPPED )
2157                             CBRollUp( lphc, FALSE, TRUE );
2158                 }
2159                 return  TRUE;
2160         case CB_GETCOUNT:
2161                 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2162         case CB_GETCURSEL:
2163                 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2164         case CB_SETCURSEL:
2165                 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2166                 if( lParam >= 0 )
2167                     SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2168
2169                 /* no LBN_SELCHANGE in this case, update manually */
2170                 if( lphc->wState & CBF_EDIT )
2171                     CBUpdateEdit( lphc, (INT)wParam );
2172                 else
2173                     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2174                 lphc->wState &= ~CBF_SELCHANGE;
2175                 return  lParam;
2176         case CB_GETLBTEXT:
2177                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2178                                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2179         case CB_GETLBTEXTLEN:
2180                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2181                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2182         case CB_GETITEMDATA:
2183                 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2184         case CB_SETITEMDATA:
2185                 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2186         case CB_GETEDITSEL:
2187                 /* Edit checks passed parameters itself */
2188                 if( lphc->wState & CBF_EDIT )
2189                     return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2190                 return  CB_ERR;
2191         case CB_SETEDITSEL:
2192                 if( lphc->wState & CBF_EDIT )
2193                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2194                           (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2195                 return  CB_ERR;
2196         case CB_SETEXTENDEDUI:
2197                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2198                     return  CB_ERR;
2199                 if( wParam )
2200                     lphc->wState |= CBF_EUI;
2201                 else lphc->wState &= ~CBF_EUI;
2202                 return  CB_OKAY;
2203         case CB_GETEXTENDEDUI:
2204                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2205         case CB_GETCOMBOBOXINFO:
2206                 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2207         case CB_LIMITTEXT:
2208                 if( lphc->wState & CBF_EDIT )
2209                         return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2210         default:
2211                 if (message >= WM_USER)
2212                     WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2213                         message - WM_USER, wParam, lParam );
2214                 break;
2215       }
2216       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2217                        DefWindowProcA(hwnd, message, wParam, lParam);
2218 }
2219
2220 /*************************************************************************
2221  *           GetComboBoxInfo   (USER32.@)
2222  */
2223 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
2224                             PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
2225 {
2226     TRACE("(%p, %p)\n", hwndCombo, pcbi);
2227     return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);
2228 }