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