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