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