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