wined3d: Filter out some shader compilation spam.
[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    lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1034    SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1035    lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1036
1037    if( lphc->wState & CBF_FOCUSED )
1038       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1039
1040    HeapFree( GetProcessHeap(), 0, pText );
1041 }
1042
1043 /***********************************************************************
1044  *           CBDropDown
1045  *
1046  * Show listbox popup.
1047  */
1048 static void CBDropDown( LPHEADCOMBO lphc )
1049 {
1050     HMONITOR monitor;
1051     MONITORINFO mon_info;
1052    RECT rect,r;
1053    int nItems = 0;
1054    int nDroppedHeight;
1055
1056    TRACE("[%p]: drop down\n", lphc->self);
1057
1058    CB_NOTIFY( lphc, CBN_DROPDOWN );
1059
1060    /* set selection */
1061
1062    lphc->wState |= CBF_DROPPED;
1063    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1064    {
1065        lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1066
1067        /* Update edit only if item is in the list */
1068        if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1069          CBUpdateEdit( lphc, lphc->droppedIndex );
1070    }
1071    else
1072    {
1073        lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1074
1075        SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1076                      (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1077        SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1078    }
1079
1080    /* now set popup position */
1081    GetWindowRect( lphc->self, &rect );
1082
1083    /*
1084     * If it's a dropdown, the listbox is offset
1085     */
1086    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1087      rect.left += COMBO_EDITBUTTONSPACE();
1088
1089   /* if the dropped height is greater than the total height of the dropped
1090      items list, then force the drop down list height to be the total height
1091      of the items in the dropped list */
1092
1093   /* And Remove any extra space (Best Fit) */
1094    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1095   /* if listbox length has been set directly by its handle */
1096    GetWindowRect(lphc->hWndLBox, &r);
1097    if (nDroppedHeight < r.bottom - r.top)
1098        nDroppedHeight = r.bottom - r.top;
1099    nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1100
1101    if (nItems > 0)
1102    {
1103       int nHeight;
1104       int nIHeight;
1105
1106       nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1107
1108       nHeight = nIHeight*nItems;
1109
1110       if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1111          nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1112
1113       if (nDroppedHeight < nHeight)
1114       {
1115             if (nItems < 5)
1116                 nDroppedHeight = (nItems+1)*nIHeight;
1117             else
1118                 nDroppedHeight = 6*nIHeight;
1119       }
1120    }
1121
1122    /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1123    monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1124    mon_info.cbSize = sizeof(mon_info);
1125    GetMonitorInfoW( monitor, &mon_info );
1126
1127    if( (rect.bottom + nDroppedHeight) >= mon_info.rcWork.bottom )
1128       rect.bottom = rect.top - nDroppedHeight;
1129
1130    SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1131                  lphc->droppedRect.right - lphc->droppedRect.left,
1132                  nDroppedHeight,
1133                  SWP_NOACTIVATE | SWP_SHOWWINDOW);
1134
1135
1136    if( !(lphc->wState & CBF_NOREDRAW) )
1137      RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1138                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1139
1140    EnableWindow( lphc->hWndLBox, TRUE );
1141    if (GetCapture() != lphc->self)
1142       SetCapture(lphc->hWndLBox);
1143 }
1144
1145 /***********************************************************************
1146  *           CBRollUp
1147  *
1148  * Hide listbox popup.
1149  */
1150 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1151 {
1152    HWND hWnd = lphc->self;
1153
1154    TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1155          lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1156
1157    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1158
1159    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1160    {
1161
1162        if( lphc->wState & CBF_DROPPED )
1163        {
1164            RECT rect;
1165
1166            lphc->wState &= ~CBF_DROPPED;
1167            ShowWindow( lphc->hWndLBox, SW_HIDE );
1168
1169            if(GetCapture() == lphc->hWndLBox)
1170            {
1171                ReleaseCapture();
1172            }
1173
1174            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1175            {
1176                rect = lphc->buttonRect;
1177            }
1178            else
1179            {
1180                if( bButton )
1181                {
1182                  UnionRect( &rect,
1183                             &lphc->buttonRect,
1184                             &lphc->textRect);
1185                }
1186                else
1187                  rect = lphc->textRect;
1188
1189                bButton = TRUE;
1190            }
1191
1192            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1193                RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1194                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1195            CB_NOTIFY( lphc, CBN_CLOSEUP );
1196        }
1197    }
1198 }
1199
1200 /***********************************************************************
1201  *           COMBO_FlipListbox
1202  *
1203  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1204  */
1205 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1206 {
1207    if( lphc->wState & CBF_DROPPED )
1208    {
1209        CBRollUp( lphc, ok, bRedrawButton );
1210        return FALSE;
1211    }
1212
1213    CBDropDown( lphc );
1214    return TRUE;
1215 }
1216
1217 /***********************************************************************
1218  *           CBRepaintButton
1219  */
1220 static void CBRepaintButton( LPHEADCOMBO lphc )
1221    {
1222   InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1223   UpdateWindow(lphc->self);
1224 }
1225
1226 /***********************************************************************
1227  *           COMBO_SetFocus
1228  */
1229 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1230 {
1231    if( !(lphc->wState & CBF_FOCUSED) )
1232    {
1233        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1234            SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1235
1236        /* This is wrong. Message sequences seem to indicate that this
1237           is set *after* the notify. */
1238        /* lphc->wState |= CBF_FOCUSED;  */
1239
1240        if( !(lphc->wState & CBF_EDIT) )
1241          InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1242
1243        CB_NOTIFY( lphc, CBN_SETFOCUS );
1244        lphc->wState |= CBF_FOCUSED;
1245    }
1246 }
1247
1248 /***********************************************************************
1249  *           COMBO_KillFocus
1250  */
1251 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1252 {
1253    HWND hWnd = lphc->self;
1254
1255    if( lphc->wState & CBF_FOCUSED )
1256    {
1257        CBRollUp( lphc, FALSE, TRUE );
1258        if( IsWindow( hWnd ) )
1259        {
1260            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1261                SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1262
1263            lphc->wState &= ~CBF_FOCUSED;
1264
1265            /* redraw text */
1266            if( !(lphc->wState & CBF_EDIT) )
1267              InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1268
1269            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1270        }
1271    }
1272 }
1273
1274 /***********************************************************************
1275  *           COMBO_Command
1276  */
1277 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1278 {
1279    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1280    {
1281        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1282
1283        switch( HIWORD(wParam) >> 8 )
1284        {
1285            case (EN_SETFOCUS >> 8):
1286
1287                TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1288
1289                 COMBO_SetFocus( lphc );
1290                 break;
1291
1292            case (EN_KILLFOCUS >> 8):
1293
1294                TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1295
1296                 /* NOTE: it seems that Windows' edit control sends an
1297                  * undocumented message WM_USER + 0x1B instead of this
1298                  * notification (only when it happens to be a part of
1299                  * the combo). ?? - AK.
1300                  */
1301
1302                 COMBO_KillFocus( lphc );
1303                 break;
1304
1305
1306            case (EN_CHANGE >> 8):
1307                /*
1308                 * In some circumstances (when the selection of the combobox
1309                 * is changed for example) we don't want the EN_CHANGE notification
1310                 * to be forwarded to the parent of the combobox. This code
1311                 * checks a flag that is set in these occasions and ignores the
1312                 * notification.
1313                 */
1314                 if (lphc->wState & CBF_NOLBSELECT)
1315                 {
1316                   lphc->wState &= ~CBF_NOLBSELECT;
1317                 }
1318                 else
1319                 {
1320                   CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1321                 }
1322
1323                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1324                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1325                 break;
1326
1327            case (EN_UPDATE >> 8):
1328                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1329                   CB_NOTIFY( lphc, CBN_EDITUPDATE );
1330                 break;
1331
1332            case (EN_ERRSPACE >> 8):
1333                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1334        }
1335    }
1336    else if( lphc->hWndLBox == hWnd )
1337    {
1338        switch( (short)HIWORD(wParam) )
1339        {
1340            case LBN_ERRSPACE:
1341                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1342                 break;
1343
1344            case LBN_DBLCLK:
1345                 CB_NOTIFY( lphc, CBN_DBLCLK );
1346                 break;
1347
1348            case LBN_SELCHANGE:
1349            case LBN_SELCANCEL:
1350
1351                 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1352
1353                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1354
1355                 if( HIWORD(wParam) == LBN_SELCHANGE)
1356                 {
1357                    if( lphc->wState & CBF_EDIT )
1358                    {
1359                        INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1360                        lphc->wState |= CBF_NOLBSELECT;
1361                        CBUpdateEdit( lphc, index );
1362                        /* select text in edit, as Windows does */
1363                        SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1364                    }
1365                    else
1366                        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1367                 }
1368
1369                 /* do not roll up if selection is being tracked
1370                  * by arrowkeys in the dropdown listbox */
1371                 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1372                 {
1373                    CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1374                 }
1375                 else lphc->wState &= ~CBF_NOROLLUP;
1376
1377                 break;
1378
1379            case LBN_SETFOCUS:
1380            case LBN_KILLFOCUS:
1381                 /* nothing to do here since ComboLBox always resets the focus to its
1382                  * combo/edit counterpart */
1383                  break;
1384        }
1385    }
1386    return 0;
1387 }
1388
1389 /***********************************************************************
1390  *           COMBO_ItemOp
1391  *
1392  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1393  */
1394 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1395 {
1396    HWND hWnd = lphc->self;
1397    UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1398
1399    TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1400
1401    switch( msg )
1402    {
1403    case WM_DELETEITEM:
1404        {
1405            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1406            lpIS->CtlType  = ODT_COMBOBOX;
1407            lpIS->CtlID    = id;
1408            lpIS->hwndItem = hWnd;
1409            break;
1410        }
1411    case WM_DRAWITEM:
1412        {
1413            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1414            lpIS->CtlType  = ODT_COMBOBOX;
1415            lpIS->CtlID    = id;
1416            lpIS->hwndItem = hWnd;
1417            break;
1418        }
1419    case WM_COMPAREITEM:
1420        {
1421            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1422            lpIS->CtlType  = ODT_COMBOBOX;
1423            lpIS->CtlID    = id;
1424            lpIS->hwndItem = hWnd;
1425            break;
1426        }
1427    case WM_MEASUREITEM:
1428        {
1429            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1430            lpIS->CtlType  = ODT_COMBOBOX;
1431            lpIS->CtlID    = id;
1432            break;
1433        }
1434    }
1435    return SendMessageW(lphc->owner, msg, id, lParam);
1436 }
1437
1438
1439 /***********************************************************************
1440  *           COMBO_GetTextW
1441  */
1442 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1443 {
1444     INT length;
1445
1446     if( lphc->wState & CBF_EDIT )
1447         return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1448
1449     /* get it from the listbox */
1450
1451     if (!count || !buf) return 0;
1452     if( lphc->hWndLBox )
1453     {
1454         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1455         if (idx == LB_ERR) goto error;
1456         length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1457         if (length == LB_ERR) goto error;
1458
1459         /* 'length' is without the terminating character */
1460         if (length >= count)
1461         {
1462             LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1463             if (!lpBuffer) goto error;
1464             length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1465
1466             /* truncate if buffer is too short */
1467             if (length != LB_ERR)
1468             {
1469                 lstrcpynW( buf, lpBuffer, count );
1470                 length = count;
1471             }
1472             HeapFree( GetProcessHeap(), 0, lpBuffer );
1473         }
1474         else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1475
1476         if (length == LB_ERR) return 0;
1477         return length;
1478     }
1479
1480  error:  /* error - truncate string, return zero */
1481     buf[0] = 0;
1482     return 0;
1483 }
1484
1485
1486 /***********************************************************************
1487  *           COMBO_GetTextA
1488  *
1489  * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1490  *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1491  */
1492 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1493 {
1494     INT length;
1495
1496     if( lphc->wState & CBF_EDIT )
1497         return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1498
1499     /* get it from the listbox */
1500
1501     if (!count || !buf) return 0;
1502     if( lphc->hWndLBox )
1503     {
1504         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1505         if (idx == LB_ERR) goto error;
1506         length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1507         if (length == LB_ERR) goto error;
1508
1509         /* 'length' is without the terminating character */
1510         if (length >= count)
1511         {
1512             LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1513             if (!lpBuffer) goto error;
1514             length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1515
1516             /* truncate if buffer is too short */
1517             if (length != LB_ERR)
1518             {
1519                 lstrcpynA( buf, lpBuffer, count );
1520                 length = count;
1521             }
1522             HeapFree( GetProcessHeap(), 0, lpBuffer );
1523         }
1524         else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1525
1526         if (length == LB_ERR) return 0;
1527         return length;
1528     }
1529
1530  error:  /* error - truncate string, return zero */
1531     buf[0] = 0;
1532     return 0;
1533 }
1534
1535
1536 /***********************************************************************
1537  *           CBResetPos
1538  *
1539  * This function sets window positions according to the updated
1540  * component placement struct.
1541  */
1542 static void CBResetPos(
1543   LPHEADCOMBO lphc,
1544   const RECT  *rectEdit,
1545   const RECT  *rectLB,
1546   BOOL        bRedraw)
1547 {
1548    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1549
1550    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1551     * sizing messages */
1552
1553    if( lphc->wState & CBF_EDIT )
1554      SetWindowPos( lphc->hWndEdit, 0,
1555                    rectEdit->left, rectEdit->top,
1556                    rectEdit->right - rectEdit->left,
1557                    rectEdit->bottom - rectEdit->top,
1558                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1559
1560    SetWindowPos( lphc->hWndLBox, 0,
1561                  rectLB->left, rectLB->top,
1562                  rectLB->right - rectLB->left,
1563                  rectLB->bottom - rectLB->top,
1564                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1565
1566    if( bDrop )
1567    {
1568        if( lphc->wState & CBF_DROPPED )
1569        {
1570            lphc->wState &= ~CBF_DROPPED;
1571            ShowWindow( lphc->hWndLBox, SW_HIDE );
1572        }
1573
1574        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1575            RedrawWindow( lphc->self, NULL, 0,
1576                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1577    }
1578 }
1579
1580
1581 /***********************************************************************
1582  *           COMBO_Size
1583  */
1584 static void COMBO_Size( LPHEADCOMBO lphc, BOOL bRedraw )
1585   {
1586   CBCalcPlacement(lphc->self,
1587                   lphc,
1588                   &lphc->textRect,
1589                   &lphc->buttonRect,
1590                   &lphc->droppedRect);
1591
1592   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, bRedraw );
1593 }
1594
1595
1596 /***********************************************************************
1597  *           COMBO_Font
1598  */
1599 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1600 {
1601   /*
1602    * Set the font
1603    */
1604   lphc->hFont = hFont;
1605
1606   /*
1607    * Propagate to owned windows.
1608    */
1609   if( lphc->wState & CBF_EDIT )
1610       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1611   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1612
1613   /*
1614    * Redo the layout of the control.
1615    */
1616   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1617   {
1618     CBCalcPlacement(lphc->self,
1619                     lphc,
1620                     &lphc->textRect,
1621                     &lphc->buttonRect,
1622                     &lphc->droppedRect);
1623
1624     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1625   }
1626   else
1627   {
1628     CBForceDummyResize(lphc);
1629   }
1630 }
1631
1632
1633 /***********************************************************************
1634  *           COMBO_SetItemHeight
1635  */
1636 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1637 {
1638    LRESULT      lRet = CB_ERR;
1639
1640    if( index == -1 ) /* set text field height */
1641    {
1642        if( height < 32768 )
1643        {
1644            lphc->editHeight = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1645
1646          /*
1647           * Redo the layout of the control.
1648           */
1649          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1650          {
1651            CBCalcPlacement(lphc->self,
1652                            lphc,
1653                            &lphc->textRect,
1654                            &lphc->buttonRect,
1655                            &lphc->droppedRect);
1656
1657            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1658          }
1659          else
1660          {
1661            CBForceDummyResize(lphc);
1662          }
1663
1664            lRet = height;
1665        }
1666    }
1667    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1668         lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1669                               (WPARAM)index, (LPARAM)height );
1670    return lRet;
1671 }
1672
1673 /***********************************************************************
1674  *           COMBO_SelectString
1675  */
1676 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1677 {
1678    INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1679                          SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1680    if( index >= 0 )
1681    {
1682      if( lphc->wState & CBF_EDIT )
1683        CBUpdateEdit( lphc, index );
1684      else
1685      {
1686        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1687      }
1688    }
1689    return (LRESULT)index;
1690 }
1691
1692 /***********************************************************************
1693  *           COMBO_LButtonDown
1694  */
1695 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1696 {
1697    POINT     pt;
1698    BOOL      bButton;
1699    HWND      hWnd = lphc->self;
1700
1701    pt.x = (short)LOWORD(lParam);
1702    pt.y = (short)HIWORD(lParam);
1703    bButton = PtInRect(&lphc->buttonRect, pt);
1704
1705    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1706        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1707    {
1708        lphc->wState |= CBF_BUTTONDOWN;
1709        if( lphc->wState & CBF_DROPPED )
1710        {
1711            /* got a click to cancel selection */
1712
1713            lphc->wState &= ~CBF_BUTTONDOWN;
1714            CBRollUp( lphc, TRUE, FALSE );
1715            if( !IsWindow( hWnd ) ) return;
1716
1717            if( lphc->wState & CBF_CAPTURE )
1718            {
1719                lphc->wState &= ~CBF_CAPTURE;
1720                ReleaseCapture();
1721            }
1722        }
1723        else
1724        {
1725            /* drop down the listbox and start tracking */
1726
1727            lphc->wState |= CBF_CAPTURE;
1728            SetCapture( hWnd );
1729            CBDropDown( lphc );
1730        }
1731        if( bButton ) CBRepaintButton( lphc );
1732    }
1733 }
1734
1735 /***********************************************************************
1736  *           COMBO_LButtonUp
1737  *
1738  * Release capture and stop tracking if needed.
1739  */
1740 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1741 {
1742    if( lphc->wState & CBF_CAPTURE )
1743    {
1744        lphc->wState &= ~CBF_CAPTURE;
1745        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1746        {
1747            INT index = CBUpdateLBox( lphc, TRUE );
1748            /* Update edit only if item is in the list */
1749            if(index >= 0)
1750            {
1751                lphc->wState |= CBF_NOLBSELECT;
1752                CBUpdateEdit( lphc, index );
1753                lphc->wState &= ~CBF_NOLBSELECT;
1754            }
1755        }
1756        ReleaseCapture();
1757        SetCapture(lphc->hWndLBox);
1758    }
1759
1760    if( lphc->wState & CBF_BUTTONDOWN )
1761    {
1762        lphc->wState &= ~CBF_BUTTONDOWN;
1763        CBRepaintButton( lphc );
1764    }
1765 }
1766
1767 /***********************************************************************
1768  *           COMBO_MouseMove
1769  *
1770  * Two things to do - track combo button and release capture when
1771  * pointer goes into the listbox.
1772  */
1773 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1774 {
1775    POINT  pt;
1776    RECT   lbRect;
1777
1778    pt.x = (short)LOWORD(lParam);
1779    pt.y = (short)HIWORD(lParam);
1780
1781    if( lphc->wState & CBF_BUTTONDOWN )
1782    {
1783      BOOL bButton;
1784
1785      bButton = PtInRect(&lphc->buttonRect, pt);
1786
1787      if( !bButton )
1788      {
1789        lphc->wState &= ~CBF_BUTTONDOWN;
1790        CBRepaintButton( lphc );
1791      }
1792    }
1793
1794    GetClientRect( lphc->hWndLBox, &lbRect );
1795    MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1796    if( PtInRect(&lbRect, pt) )
1797    {
1798        lphc->wState &= ~CBF_CAPTURE;
1799        ReleaseCapture();
1800        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1801
1802        /* hand over pointer tracking */
1803        SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1804    }
1805 }
1806
1807 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1808 {
1809     if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1810         return FALSE;
1811
1812     pcbi->rcItem = lphc->textRect;
1813     pcbi->rcButton = lphc->buttonRect;
1814     pcbi->stateButton = 0;
1815     if (lphc->wState & CBF_BUTTONDOWN)
1816         pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1817     if (IsRectEmpty(&lphc->buttonRect))
1818         pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1819     pcbi->hwndCombo = lphc->self;
1820     pcbi->hwndItem = lphc->hWndEdit;
1821     pcbi->hwndList = lphc->hWndLBox;
1822     return TRUE;
1823 }
1824
1825 static char *strdupA(LPCSTR str)
1826 {
1827     char *ret;
1828     DWORD len;
1829
1830     if(!str) return NULL;
1831
1832     len = strlen(str);
1833     ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1834     memcpy(ret, str, len + 1);
1835     return ret;
1836 }
1837
1838 /***********************************************************************
1839  *           ComboWndProc_common
1840  *
1841  * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/comboboxes/comboboxes.asp
1842  */
1843 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1844                                     WPARAM wParam, LPARAM lParam, BOOL unicode )
1845 {
1846       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1847
1848       TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1849             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1850
1851       if( lphc || message == WM_NCCREATE )
1852       switch(message)
1853       {
1854
1855         /* System messages */
1856
1857         case WM_NCCREATE:
1858         {
1859                 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1860                                        ((LPCREATESTRUCTA)lParam)->style;
1861                 return COMBO_NCCreate(hwnd, style);
1862         }
1863         case WM_NCDESTROY:
1864                 COMBO_NCDestroy(lphc);
1865                 break;/* -> DefWindowProc */
1866
1867         case WM_CREATE:
1868         {
1869                 HWND hwndParent;
1870                 LONG style;
1871                 if(unicode)
1872                 {
1873                     hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1874                     style = ((LPCREATESTRUCTW)lParam)->style;
1875                 }
1876                 else
1877                 {
1878                     hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1879                     style = ((LPCREATESTRUCTA)lParam)->style;
1880                 }
1881                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1882         }
1883
1884         case WM_PRINTCLIENT:
1885                 /* Fallthrough */
1886         case WM_PAINT:
1887                 /* wParam may contain a valid HDC! */
1888                 return  COMBO_Paint(lphc, (HDC)wParam);
1889
1890         case WM_ERASEBKGND:
1891                 /* do all painting in WM_PAINT like Windows does */
1892                 return 1;
1893
1894         case WM_GETDLGCODE:
1895         {
1896                 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1897                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1898                 {
1899                    int vk = (int)((LPMSG)lParam)->wParam;
1900
1901                    if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1902                        result |= DLGC_WANTMESSAGE;
1903                 }
1904                 return  result;
1905         }
1906         case WM_WINDOWPOSCHANGING:
1907                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1908     case WM_WINDOWPOSCHANGED:
1909         /* SetWindowPos can be called on a Combobox to resize its Listbox.
1910          * In that case, the Combobox itself will not be resized, so we won't
1911          * get a WM_SIZE. Since we still want to update the Listbox, we have to
1912          * do it here.
1913          */
1914         /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
1915          * Z-order based painting.
1916          */
1917         /* fall through */
1918         case WM_SIZE:
1919                 if( lphc->hWndLBox &&
1920                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc, message == WM_SIZE );
1921                 return  TRUE;
1922         case WM_SETFONT:
1923                 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1924                 return  TRUE;
1925         case WM_GETFONT:
1926                 return  (LRESULT)lphc->hFont;
1927         case WM_SETFOCUS:
1928                 if( lphc->wState & CBF_EDIT )
1929                     SetFocus( lphc->hWndEdit );
1930                 else
1931                     COMBO_SetFocus( lphc );
1932                 return  TRUE;
1933         case WM_KILLFOCUS:
1934             {
1935                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1936                 if( !hwndFocus ||
1937                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1938                     COMBO_KillFocus( lphc );
1939                 return  TRUE;
1940             }
1941         case WM_COMMAND:
1942                 return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1943         case WM_GETTEXT:
1944             return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1945                            : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1946         case WM_SETTEXT:
1947         case WM_GETTEXTLENGTH:
1948         case WM_CLEAR:
1949                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1950                 {
1951                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1952                     if (j == -1) return 0;
1953                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1954                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1955                 }
1956                 else if( lphc->wState & CBF_EDIT )
1957                 {
1958                     LRESULT ret;
1959                     lphc->wState |= CBF_NOEDITNOTIFY;
1960                     ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1961                                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1962                     lphc->wState &= ~CBF_NOEDITNOTIFY;
1963                     return ret;
1964                 }
1965                 else return CB_ERR;
1966         case WM_CUT:
1967         case WM_PASTE:
1968         case WM_COPY:
1969                 if( lphc->wState & CBF_EDIT )
1970                 {
1971                     return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1972                                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1973                 }
1974                 else return  CB_ERR;
1975
1976         case WM_DRAWITEM:
1977         case WM_DELETEITEM:
1978         case WM_COMPAREITEM:
1979         case WM_MEASUREITEM:
1980                 return COMBO_ItemOp(lphc, message, lParam);
1981         case WM_ENABLE:
1982                 if( lphc->wState & CBF_EDIT )
1983                     EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1984                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1985
1986                 /* Force the control to repaint when the enabled state changes. */
1987                 InvalidateRect(lphc->self, NULL, TRUE);
1988                 return  TRUE;
1989         case WM_SETREDRAW:
1990                 if( wParam )
1991                     lphc->wState &= ~CBF_NOREDRAW;
1992                 else
1993                     lphc->wState |= CBF_NOREDRAW;
1994
1995                 if( lphc->wState & CBF_EDIT )
1996                     SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1997                 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1998                 return  0;
1999         case WM_SYSKEYDOWN:
2000                 if( KEYDATA_ALT & HIWORD(lParam) )
2001                     if( wParam == VK_UP || wParam == VK_DOWN )
2002                         COMBO_FlipListbox( lphc, FALSE, FALSE );
2003                 return  0;
2004
2005         case WM_CHAR:
2006         case WM_IME_CHAR:
2007         case WM_KEYDOWN:
2008         {
2009                 HWND hwndTarget;
2010
2011                 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2012                      (lphc->wState & CBF_DROPPED))
2013                 {
2014                    CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2015                    return TRUE;
2016                 }
2017                else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
2018                {
2019                   COMBO_FlipListbox( lphc, FALSE, FALSE );
2020                   return TRUE;
2021                }
2022
2023                 if( lphc->wState & CBF_EDIT )
2024                     hwndTarget = lphc->hWndEdit;
2025                 else
2026                     hwndTarget = lphc->hWndLBox;
2027
2028                 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2029                                  SendMessageA(hwndTarget, message, wParam, lParam);
2030         }
2031         case WM_LBUTTONDOWN:
2032                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2033                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2034                 return  TRUE;
2035         case WM_LBUTTONUP:
2036                 COMBO_LButtonUp( lphc );
2037                 return  TRUE;
2038         case WM_MOUSEMOVE:
2039                 if( lphc->wState & CBF_CAPTURE )
2040                     COMBO_MouseMove( lphc, wParam, lParam );
2041                 return  TRUE;
2042
2043         case WM_MOUSEWHEEL:
2044                 if (wParam & (MK_SHIFT | MK_CONTROL))
2045                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2046                                      DefWindowProcA(hwnd, message, wParam, lParam);
2047
2048                 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2049                 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2050                 return TRUE;
2051
2052         /* Combo messages */
2053
2054         case CB_ADDSTRING16:
2055                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2056                 /* fall through */
2057         case CB_ADDSTRING:
2058                 if( unicode )
2059                 {
2060                     if( lphc->dwStyle & CBS_LOWERCASE )
2061                         CharLowerW((LPWSTR)lParam);
2062                     else if( lphc->dwStyle & CBS_UPPERCASE )
2063                         CharUpperW((LPWSTR)lParam);
2064                     return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2065                 }
2066                 else /* unlike the unicode version, the ansi version does not overwrite
2067                         the string if converting case */
2068                 {
2069                     char *string = NULL;
2070                     LRESULT ret;
2071                     if( lphc->dwStyle & CBS_LOWERCASE )
2072                     {
2073                         string = strdupA((LPSTR)lParam);
2074                         CharLowerA(string);
2075                     }
2076
2077                     else if( lphc->dwStyle & CBS_UPPERCASE )
2078                     {
2079                         string = strdupA((LPSTR)lParam);
2080                         CharUpperA(string);
2081                     }
2082
2083                     ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
2084                     HeapFree(GetProcessHeap(), 0, string);
2085                     return ret;
2086                 }
2087         case CB_INSERTSTRING16:
2088                 wParam = (INT)(INT16)wParam;
2089                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2090                 /* fall through */
2091         case CB_INSERTSTRING:
2092                 if( unicode )
2093                 {
2094                     if( lphc->dwStyle & CBS_LOWERCASE )
2095                         CharLowerW((LPWSTR)lParam);
2096                     else if( lphc->dwStyle & CBS_UPPERCASE )
2097                         CharUpperW((LPWSTR)lParam);
2098                     return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2099                 }
2100                 else
2101                 {
2102                     if( lphc->dwStyle & CBS_LOWERCASE )
2103                         CharLowerA((LPSTR)lParam);
2104                     else if( lphc->dwStyle & CBS_UPPERCASE )
2105                         CharUpperA((LPSTR)lParam);
2106
2107                     return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2108                 }
2109         case CB_DELETESTRING16:
2110         case CB_DELETESTRING:
2111                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2112                                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2113         case CB_SELECTSTRING16:
2114                 wParam = (INT)(INT16)wParam;
2115                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2116                 /* fall through */
2117         case CB_SELECTSTRING:
2118                 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2119         case CB_FINDSTRING16:
2120                 wParam = (INT)(INT16)wParam;
2121                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2122                 /* fall through */
2123         case CB_FINDSTRING:
2124                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2125                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2126         case CB_FINDSTRINGEXACT16:
2127                 wParam = (INT)(INT16)wParam;
2128                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2129                 /* fall through */
2130         case CB_FINDSTRINGEXACT:
2131                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2132                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2133         case CB_SETITEMHEIGHT16:
2134                 wParam = (INT)(INT16)wParam;    /* signed integer */
2135                 /* fall through */
2136         case CB_SETITEMHEIGHT:
2137                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2138         case CB_GETITEMHEIGHT16:
2139                 wParam = (INT)(INT16)wParam;
2140                 /* fall through */
2141         case CB_GETITEMHEIGHT:
2142                 if( (INT)wParam >= 0 )  /* listbox item */
2143                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2144                 return  CBGetTextAreaHeight(hwnd, lphc);
2145         case CB_RESETCONTENT16:
2146         case CB_RESETCONTENT:
2147                 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2148                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2149                 {
2150                     static const WCHAR empty_stringW[] = { 0 };
2151                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2152                 }
2153                 else
2154                     InvalidateRect(lphc->self, NULL, TRUE);
2155                 return  TRUE;
2156         case CB_INITSTORAGE:
2157                 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2158         case CB_GETHORIZONTALEXTENT:
2159                 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2160         case CB_SETHORIZONTALEXTENT:
2161                 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2162         case CB_GETTOPINDEX:
2163                 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2164         case CB_GETLOCALE:
2165                 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2166         case CB_SETLOCALE:
2167                 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2168         case CB_GETDROPPEDWIDTH:
2169                 if( lphc->droppedWidth )
2170                     return  lphc->droppedWidth;
2171                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2172         case CB_SETDROPPEDWIDTH:
2173                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2174                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2175                 return  CB_ERR;
2176         case CB_GETDROPPEDCONTROLRECT16:
2177                 lParam = (LPARAM)MapSL(lParam);
2178                 if( lParam )
2179                 {
2180                     RECT r;
2181                     RECT16 *r16 = (RECT16 *)lParam;
2182                     CBGetDroppedControlRect( lphc, &r );
2183                     r16->left   = r.left;
2184                     r16->top    = r.top;
2185                     r16->right  = r.right;
2186                     r16->bottom = r.bottom;
2187                 }
2188                 return  CB_OKAY;
2189         case CB_GETDROPPEDCONTROLRECT:
2190                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2191                 return  CB_OKAY;
2192         case CB_GETDROPPEDSTATE16:
2193         case CB_GETDROPPEDSTATE:
2194                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2195         case CB_DIR16:
2196                 return SendMessageA(lphc->hWndLBox, LB_DIR16, wParam, lParam);
2197         case CB_DIR:
2198                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2199                                  SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2200
2201         case CB_SHOWDROPDOWN16:
2202         case CB_SHOWDROPDOWN:
2203                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2204                 {
2205                     if( wParam )
2206                     {
2207                         if( !(lphc->wState & CBF_DROPPED) )
2208                             CBDropDown( lphc );
2209                     }
2210                     else
2211                         if( lphc->wState & CBF_DROPPED )
2212                             CBRollUp( lphc, FALSE, TRUE );
2213                 }
2214                 return  TRUE;
2215         case CB_GETCOUNT16:
2216         case CB_GETCOUNT:
2217                 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2218         case CB_GETCURSEL16:
2219         case CB_GETCURSEL:
2220                 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2221         case CB_SETCURSEL16:
2222                 wParam = (INT)(INT16)wParam;
2223                 /* fall through */
2224         case CB_SETCURSEL:
2225                 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2226                 if( lParam >= 0 )
2227                     SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2228
2229                 /* no LBN_SELCHANGE in this case, update manually */
2230                 if( lphc->wState & CBF_EDIT )
2231                     CBUpdateEdit( lphc, (INT)wParam );
2232                 else
2233                     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2234                 lphc->wState &= ~CBF_SELCHANGE;
2235                 return  lParam;
2236         case CB_GETLBTEXT16:
2237                 wParam = (INT)(INT16)wParam;
2238                 lParam = (LPARAM)MapSL(lParam);
2239                 /* fall through */
2240         case CB_GETLBTEXT:
2241                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2242                                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2243         case CB_GETLBTEXTLEN16:
2244                 wParam = (INT)(INT16)wParam;
2245                 /* fall through */
2246         case CB_GETLBTEXTLEN:
2247                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2248                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2249         case CB_GETITEMDATA16:
2250                 wParam = (INT)(INT16)wParam;
2251                 /* fall through */
2252         case CB_GETITEMDATA:
2253                 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2254         case CB_SETITEMDATA16:
2255                 wParam = (INT)(INT16)wParam;
2256                 /* fall through */
2257         case CB_SETITEMDATA:
2258                 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2259         case CB_GETEDITSEL16:
2260                 wParam = lParam = 0;   /* just in case */
2261                 /* fall through */
2262         case CB_GETEDITSEL:
2263                 /* Edit checks passed parameters itself */
2264                 if( lphc->wState & CBF_EDIT )
2265                     return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2266                 return  CB_ERR;
2267         case CB_SETEDITSEL16:
2268         case CB_SETEDITSEL:
2269                 if( lphc->wState & CBF_EDIT )
2270                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2271                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2272                 return  CB_ERR;
2273         case CB_SETEXTENDEDUI16:
2274         case CB_SETEXTENDEDUI:
2275                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2276                     return  CB_ERR;
2277                 if( wParam )
2278                     lphc->wState |= CBF_EUI;
2279                 else lphc->wState &= ~CBF_EUI;
2280                 return  CB_OKAY;
2281         case CB_GETEXTENDEDUI16:
2282         case CB_GETEXTENDEDUI:
2283                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2284         case CB_GETCOMBOBOXINFO:
2285                 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2286         case CB_LIMITTEXT:
2287                 if( lphc->wState & CBF_EDIT )
2288                         return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2289         default:
2290                 if (message >= WM_USER)
2291                     WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2292                         message - WM_USER, wParam, lParam );
2293                 break;
2294       }
2295       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2296                        DefWindowProcA(hwnd, message, wParam, lParam);
2297 }
2298
2299 /***********************************************************************
2300  *           ComboWndProcA
2301  *
2302  * This is just a wrapper for the real ComboWndProc which locks/unlocks
2303  * window structs.
2304  */
2305 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2306 {
2307     if (!IsWindow(hwnd)) return 0;
2308     return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2309 }
2310
2311 /***********************************************************************
2312  *           ComboWndProcW
2313  */
2314 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2315 {
2316     if (!IsWindow(hwnd)) return 0;
2317     return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2318 }
2319
2320 /*************************************************************************
2321  *           GetComboBoxInfo   (USER32.@)
2322  */
2323 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
2324                             PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
2325 {
2326     TRACE("(%p, %p)\n", hwndCombo, pcbi);
2327     return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);
2328 }