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