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