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