Fixes and cleans up some of the GetEnhMetaFile* functions.
[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   else 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( pText )
1117        HeapFree( GetProcessHeap(), 0, pText );
1118 }
1119
1120 /***********************************************************************
1121  *           CBDropDown
1122  * 
1123  * Show listbox popup.
1124  */
1125 static void CBDropDown( LPHEADCOMBO lphc )
1126 {
1127    RECT rect;
1128    int nItems = 0;
1129    int i;
1130    int nHeight;
1131    int nDroppedHeight, nTempDroppedHeight;
1132
1133    TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1134
1135    CB_NOTIFY( lphc, CBN_DROPDOWN );
1136
1137    /* set selection */
1138
1139    lphc->wState |= CBF_DROPPED;
1140    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1141    {
1142        lphc->droppedIndex = CBUpdateLBox( lphc );
1143
1144        if( !(lphc->wState & CBF_CAPTURE) ) 
1145          CBUpdateEdit( lphc, lphc->droppedIndex );
1146    }
1147    else
1148    {
1149        lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1150
1151        if( lphc->droppedIndex == LB_ERR ) 
1152          lphc->droppedIndex = 0;
1153
1154        SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)lphc->droppedIndex, 0 );
1155        SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1156    }
1157
1158    /* now set popup position */
1159    GetWindowRect( lphc->self->hwndSelf, &rect );
1160    
1161    /*
1162     * If it's a dropdown, the listbox is offset
1163     */
1164    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1165      rect.left += COMBO_EDITBUTTONSPACE();
1166
1167   /* if the dropped height is greater than the total height of the dropped
1168      items list, then force the drop down list height to be the total height
1169      of the items in the dropped list */
1170
1171   /* And Remove any extra space (Best Fit) */
1172    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1173    nItems = (int)SendMessageA (lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1174    nHeight = COMBO_YBORDERSIZE();
1175    nTempDroppedHeight = 0;
1176    for (i = 0; i < nItems; i++)
1177    {
1178      nHeight += (int)SendMessageA (lphc->hWndLBox, LB_GETITEMHEIGHT, i, 0);
1179
1180      /* Did we pass the limit of what can be displayed */
1181      if (nHeight > nDroppedHeight)
1182      {
1183        break;
1184    }
1185      nTempDroppedHeight = nHeight;
1186    }
1187
1188    nDroppedHeight = nTempDroppedHeight;
1189
1190    SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom, 
1191                  lphc->droppedRect.right - lphc->droppedRect.left,
1192       nDroppedHeight,
1193                  SWP_NOACTIVATE | SWP_NOREDRAW);
1194
1195    if( !(lphc->wState & CBF_NOREDRAW) )
1196      RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE | 
1197                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1198
1199    ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1200 }
1201
1202 /***********************************************************************
1203  *           CBRollUp
1204  *
1205  * Hide listbox popup.
1206  */
1207 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1208 {
1209    HWND hWnd = lphc->self->hwndSelf;
1210
1211    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1212
1213    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1214    {
1215
1216        TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1217
1218        if( lphc->wState & CBF_DROPPED ) 
1219        {
1220            RECT rect;
1221
1222            lphc->wState &= ~CBF_DROPPED;
1223            ShowWindow( lphc->hWndLBox, SW_HIDE );
1224            if(GetCapture() == lphc->hWndLBox)
1225            {
1226                ReleaseCapture();
1227            }
1228
1229            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1230            {
1231                INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1232                CBUpdateEdit( lphc, index );
1233                rect = lphc->buttonRect;
1234            }
1235            else 
1236            {
1237                if( bButton )
1238                {
1239                  UnionRect( &rect,
1240                             &lphc->buttonRect,
1241                             &lphc->textRect);
1242                }
1243                else
1244                  rect = lphc->textRect;
1245
1246                bButton = TRUE;
1247            }
1248
1249            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1250                RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | 
1251                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1252            CB_NOTIFY( lphc, CBN_CLOSEUP );
1253        }
1254    }
1255 }
1256
1257 /***********************************************************************
1258  *           COMBO_FlipListbox
1259  *
1260  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1261  */
1262 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1263 {
1264    if( lphc->wState & CBF_DROPPED )
1265    {
1266        CBRollUp( lphc, TRUE, bRedrawButton );
1267        return FALSE;
1268    }
1269
1270    CBDropDown( lphc );
1271    return TRUE;
1272 }
1273
1274 /***********************************************************************
1275  *           COMBO_GetLBWindow
1276  *
1277  * Edit control helper.
1278  */
1279 HWND COMBO_GetLBWindow( WND* pWnd )
1280 {
1281   LPHEADCOMBO       lphc = CB_GETPTR(pWnd);
1282   if( lphc ) return lphc->hWndLBox;
1283   return 0;
1284 }
1285
1286
1287 /***********************************************************************
1288  *           CBRepaintButton
1289  */
1290 static void CBRepaintButton( LPHEADCOMBO lphc )
1291    {
1292   InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1293   UpdateWindow(CB_HWND(lphc));
1294 }
1295
1296 /***********************************************************************
1297  *           COMBO_SetFocus
1298  */
1299 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1300 {
1301    if( !(lphc->wState & CBF_FOCUSED) )
1302    {
1303        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1304            SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1305
1306        lphc->wState |= CBF_FOCUSED;
1307
1308        if( !(lphc->wState & CBF_EDIT) )
1309          InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1310
1311        CB_NOTIFY( lphc, CBN_SETFOCUS );
1312    }
1313 }
1314
1315 /***********************************************************************
1316  *           COMBO_KillFocus
1317  */
1318 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1319 {
1320    HWND hWnd = lphc->self->hwndSelf;
1321
1322    if( lphc->wState & CBF_FOCUSED )
1323    {
1324        CBRollUp( lphc, FALSE, TRUE );
1325        if( IsWindow( hWnd ) )
1326        {
1327            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1328                SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1329
1330            lphc->wState &= ~CBF_FOCUSED;
1331
1332            /* redraw text */
1333            if( !(lphc->wState & CBF_EDIT) )
1334              InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1335
1336            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1337        }
1338    }
1339 }
1340
1341 /***********************************************************************
1342  *           COMBO_Command
1343  */
1344 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1345 {
1346    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1347    {
1348        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1349
1350        switch( HIWORD(wParam) >> 8 )
1351        {   
1352            case (EN_SETFOCUS >> 8):
1353
1354                 TRACE("[%04x]: edit [%04x] got focus\n", 
1355                              CB_HWND(lphc), lphc->hWndEdit );
1356
1357                 COMBO_SetFocus( lphc );
1358                 break;
1359
1360            case (EN_KILLFOCUS >> 8):
1361
1362                 TRACE("[%04x]: edit [%04x] lost focus\n",
1363                              CB_HWND(lphc), lphc->hWndEdit );
1364
1365                 /* NOTE: it seems that Windows' edit control sends an
1366                  * undocumented message WM_USER + 0x1B instead of this
1367                  * notification (only when it happens to be a part of 
1368                  * the combo). ?? - AK.
1369                  */
1370
1371                 COMBO_KillFocus( lphc );
1372                 break;
1373
1374
1375            case (EN_CHANGE >> 8):
1376                /*
1377                 * In some circumstances (when the selection of the combobox
1378                 * is changed for example) we don't wans the EN_CHANGE notification
1379                 * to be forwarded to the parent of the combobox. This code
1380                 * checks a flag that is set in these occasions and ignores the 
1381                 * notification.
1382                 */
1383                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1384                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1385
1386                 CBUpdateLBox( lphc );
1387                 break;
1388
1389            case (EN_UPDATE >> 8):
1390                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1391                   CB_NOTIFY( lphc, CBN_EDITUPDATE );
1392                 break;
1393
1394            case (EN_ERRSPACE >> 8):
1395                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1396        }
1397    }
1398    else if( lphc->hWndLBox == hWnd )
1399    {
1400        switch( HIWORD(wParam) )
1401        {
1402            case LBN_ERRSPACE:
1403                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1404                 break;
1405
1406            case LBN_DBLCLK:
1407                 CB_NOTIFY( lphc, CBN_DBLCLK );
1408                 break;
1409
1410            case LBN_SELCHANGE:
1411            case LBN_SELCANCEL:
1412
1413                 TRACE("[%04x]: lbox selection change [%04x]\n", 
1414                              CB_HWND(lphc), lphc->wState );
1415
1416                 /* do not roll up if selection is being tracked 
1417                  * by arrowkeys in the dropdown listbox */
1418
1419                 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1420                      CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1421                 else lphc->wState &= ~CBF_NOROLLUP;
1422
1423                 if( lphc->wState & CBF_EDIT )
1424                 {
1425                     INT index = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1426                     CBUpdateEdit( lphc, index );
1427                     /* select text in edit, as Windows does */
1428                     SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1429                 }
1430                 else
1431                     InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1432
1433                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1434                 /* fall through */
1435
1436            case LBN_SETFOCUS:
1437            case LBN_KILLFOCUS:
1438                 /* nothing to do here since ComboLBox always resets the focus to its
1439                  * combo/edit counterpart */
1440                  break;
1441        }
1442    }
1443    return 0;
1444 }
1445
1446 /***********************************************************************
1447  *           COMBO_ItemOp
1448  *
1449  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1450  */
1451 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, 
1452                                WPARAM wParam, LPARAM lParam ) 
1453 {
1454    HWND hWnd = lphc->self->hwndSelf;
1455
1456    TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1457
1458 #define lpIS    ((LPDELETEITEMSTRUCT)lParam)
1459
1460    /* two first items are the same in all 4 structs */
1461    lpIS->CtlType = ODT_COMBOBOX;
1462    lpIS->CtlID   = lphc->self->wIDmenu;
1463
1464    switch( msg )        /* patch window handle */
1465    {
1466         case WM_DELETEITEM: 
1467              lpIS->hwndItem = hWnd; 
1468 #undef  lpIS
1469              break;
1470         case WM_DRAWITEM: 
1471 #define lpIS    ((LPDRAWITEMSTRUCT)lParam)
1472              lpIS->hwndItem = hWnd; 
1473 #undef  lpIS
1474              break;
1475         case WM_COMPAREITEM: 
1476 #define lpIS    ((LPCOMPAREITEMSTRUCT)lParam)
1477              lpIS->hwndItem = hWnd; 
1478 #undef  lpIS
1479              break;
1480    }
1481
1482    return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1483 }
1484
1485 /***********************************************************************
1486  *           COMBO_GetText
1487  */
1488 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1489 {
1490    if( lphc->wState & CBF_EDIT )
1491        return SendMessageA( lphc->hWndEdit, WM_GETTEXT, 
1492                              (WPARAM)N, (LPARAM)lpText );     
1493
1494    /* get it from the listbox */
1495
1496    if( lphc->hWndLBox )
1497    {
1498        INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1499        if( idx != LB_ERR )
1500        {
1501            LPSTR        lpBuffer;
1502            INT  length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1503                                                 (WPARAM)idx, 0 );
1504
1505            /* 'length' is without the terminating character */
1506            if( length >= N )
1507                lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1508            else 
1509                lpBuffer = lpText;
1510
1511            if( lpBuffer )
1512            {
1513                INT    n = SendMessageA( lphc->hWndLBox, LB_GETTEXT, 
1514                                            (WPARAM)idx, (LPARAM)lpBuffer );
1515
1516                /* truncate if buffer is too short */
1517
1518                if( length >= N )
1519                {
1520                    if (N && lpText) {
1521                    if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1522                    lpText[N - 1] = '\0';
1523                    }
1524                    HeapFree( GetProcessHeap(), 0, lpBuffer );
1525                }
1526                return (LRESULT)n;
1527            }
1528        }
1529    }
1530    return 0;
1531 }
1532
1533
1534 /***********************************************************************
1535  *           CBResetPos
1536  *
1537  * This function sets window positions according to the updated 
1538  * component placement struct.
1539  */
1540 static void CBResetPos(
1541   LPHEADCOMBO lphc, 
1542   LPRECT      rectEdit,
1543   LPRECT      rectLB,
1544   BOOL        bRedraw)
1545 {
1546    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1547
1548    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1549     * sizing messages */
1550
1551    if( lphc->wState & CBF_EDIT )
1552      SetWindowPos( lphc->hWndEdit, 0, 
1553                    rectEdit->left, rectEdit->top,
1554                    rectEdit->right - rectEdit->left,
1555                    rectEdit->bottom - rectEdit->top,
1556                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1557
1558    SetWindowPos( lphc->hWndLBox, 0,
1559                  rectLB->left, rectLB->top,
1560                  rectLB->right - rectLB->left, 
1561                  rectLB->bottom - rectLB->top, 
1562                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1563
1564    if( bDrop )
1565    {
1566        if( lphc->wState & CBF_DROPPED )
1567        {
1568            lphc->wState &= ~CBF_DROPPED;
1569            ShowWindow( lphc->hWndLBox, SW_HIDE );
1570        }
1571
1572        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1573            RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1574                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1575    }
1576 }
1577
1578
1579 /***********************************************************************
1580  *           COMBO_Size
1581  */
1582 static void COMBO_Size( LPHEADCOMBO lphc )
1583   {
1584   CBCalcPlacement(lphc->self->hwndSelf,
1585                   lphc, 
1586                   &lphc->textRect, 
1587                   &lphc->buttonRect, 
1588                   &lphc->droppedRect);
1589
1590   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1591 }
1592
1593
1594 /***********************************************************************
1595  *           COMBO_Font
1596  */
1597 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1598 {
1599   /*
1600    * Set the font
1601    */
1602   lphc->hFont = hFont;
1603
1604   /*
1605    * Propagate to owned windows.
1606    */
1607   if( lphc->wState & CBF_EDIT )
1608       SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1609   SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1610
1611   /*
1612    * Redo the layout of the control.
1613    */
1614   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1615   {
1616     CBCalcPlacement(lphc->self->hwndSelf,
1617                     lphc, 
1618                     &lphc->textRect, 
1619                     &lphc->buttonRect, 
1620                     &lphc->droppedRect);
1621     
1622     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1623   }
1624   else
1625   {
1626     CBForceDummyResize(lphc);
1627   }
1628 }
1629
1630
1631 /***********************************************************************
1632  *           COMBO_SetItemHeight
1633  */
1634 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1635 {
1636    LRESULT      lRet = CB_ERR;
1637
1638    if( index == -1 ) /* set text field height */
1639    {
1640        if( height < 32768 )
1641        {
1642            lphc->editHeight = height;
1643
1644          /*
1645           * Redo the layout of the control.
1646           */
1647          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1648          {
1649            CBCalcPlacement(lphc->self->hwndSelf,
1650                            lphc, 
1651                            &lphc->textRect, 
1652                            &lphc->buttonRect, 
1653                            &lphc->droppedRect);
1654            
1655            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1656          }
1657          else
1658          {
1659            CBForceDummyResize(lphc);
1660          }
1661                  
1662            lRet = height;
1663        }
1664    } 
1665    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1666         lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT, 
1667                               (WPARAM)index, (LPARAM)height );
1668    return lRet;
1669 }
1670
1671 /***********************************************************************
1672  *           COMBO_SelectString
1673  */
1674 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1675 {
1676    INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING, 
1677                                  (WPARAM)start, (LPARAM)pText );
1678    if( index >= 0 )
1679    {
1680      if( lphc->wState & CBF_EDIT )
1681        CBUpdateEdit( lphc, index );
1682      else
1683      {
1684        InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1685      }
1686    }
1687    return (LRESULT)index;
1688 }
1689
1690 /***********************************************************************
1691  *           COMBO_LButtonDown
1692  */
1693 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1694 {
1695    POINT     pt;
1696    BOOL      bButton;
1697    HWND      hWnd = lphc->self->hwndSelf;
1698
1699    pt.x = LOWORD(lParam);
1700    pt.y = HIWORD(lParam);
1701    bButton = PtInRect(&lphc->buttonRect, pt);
1702
1703    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1704        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1705    {
1706        lphc->wState |= CBF_BUTTONDOWN;
1707        if( lphc->wState & CBF_DROPPED )
1708        {
1709            /* got a click to cancel selection */
1710
1711            lphc->wState &= ~CBF_BUTTONDOWN;
1712            CBRollUp( lphc, TRUE, FALSE );
1713            if( !IsWindow( hWnd ) ) return;
1714
1715            if( lphc->wState & CBF_CAPTURE )
1716            {
1717                lphc->wState &= ~CBF_CAPTURE;
1718                ReleaseCapture();
1719            }
1720        }
1721        else
1722        {
1723            /* drop down the listbox and start tracking */
1724
1725            lphc->wState |= CBF_CAPTURE;
1726            CBDropDown( lphc );
1727            SetCapture( hWnd );
1728        }
1729        if( bButton ) CBRepaintButton( lphc );
1730    }
1731 }
1732
1733 /***********************************************************************
1734  *           COMBO_LButtonUp
1735  *
1736  * Release capture and stop tracking if needed.
1737  */
1738 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1739 {
1740    if( lphc->wState & CBF_CAPTURE )
1741    {
1742        lphc->wState &= ~CBF_CAPTURE;
1743        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1744        {
1745            INT index = CBUpdateLBox( lphc );
1746            CBUpdateEdit( lphc, index );
1747        }
1748        ReleaseCapture();
1749        SetCapture(lphc->hWndLBox);
1750
1751    }
1752
1753    if( lphc->wState & CBF_BUTTONDOWN )
1754    {
1755        lphc->wState &= ~CBF_BUTTONDOWN;
1756        CBRepaintButton( lphc );
1757    }
1758 }
1759
1760 /***********************************************************************
1761  *           COMBO_MouseMove
1762  *
1763  * Two things to do - track combo button and release capture when
1764  * pointer goes into the listbox.
1765  */
1766 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1767 {
1768    POINT  pt;
1769    RECT   lbRect;
1770    
1771    pt.x = LOWORD(lParam);
1772    pt.y = HIWORD(lParam);
1773    
1774    if( lphc->wState & CBF_BUTTONDOWN )
1775    {
1776      BOOL bButton;
1777
1778      bButton = PtInRect(&lphc->buttonRect, pt);
1779
1780      if( !bButton )
1781      {
1782        lphc->wState &= ~CBF_BUTTONDOWN;
1783        CBRepaintButton( lphc );
1784      }
1785    }
1786
1787    GetClientRect( lphc->hWndLBox, &lbRect );
1788    MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1789    if( PtInRect(&lbRect, pt) )
1790    {
1791        lphc->wState &= ~CBF_CAPTURE;
1792        ReleaseCapture();
1793        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1794
1795        /* hand over pointer tracking */
1796        SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1797    }
1798 }
1799
1800
1801 /***********************************************************************
1802  *           ComboWndProc_locked
1803  *
1804  * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1805  */
1806 static inline LRESULT WINAPI ComboWndProc_locked( WND* pWnd, UINT message,
1807                              WPARAM wParam, LPARAM lParam )
1808 {
1809     if( pWnd ) {
1810       LPHEADCOMBO       lphc = CB_GETPTR(pWnd);
1811       HWND              hwnd = pWnd->hwndSelf;
1812
1813       TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1814                    pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1815
1816       if( lphc || message == WM_NCCREATE )
1817       switch(message) 
1818       { 
1819
1820         /* System messages */
1821
1822         case WM_NCCREATE: 
1823                 return COMBO_NCCreate(pWnd, lParam);
1824         case WM_NCDESTROY: 
1825                 COMBO_NCDestroy(lphc);
1826                 break;/* -> DefWindowProc */
1827
1828         case WM_CREATE: 
1829                 return COMBO_Create(lphc, pWnd, lParam);
1830
1831         case WM_PRINTCLIENT:
1832                 if (lParam & PRF_ERASEBKGND)
1833                   COMBO_EraseBackground(hwnd, lphc, wParam);
1834
1835                 /* Fallthrough */
1836         case WM_PAINT:
1837                 /* wParam may contain a valid HDC! */
1838                 return  COMBO_Paint(lphc, wParam);
1839         case WM_ERASEBKGND:
1840                 return  COMBO_EraseBackground(hwnd, lphc, wParam);
1841         case WM_GETDLGCODE: 
1842                 return  (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1843         case WM_WINDOWPOSCHANGING:
1844                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1845         case WM_SIZE:
1846                 if( lphc->hWndLBox && 
1847                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1848                 return  TRUE;
1849         case WM_SETFONT:
1850                 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1851                 return  TRUE;
1852         case WM_GETFONT:
1853                 return  (LRESULT)lphc->hFont;
1854         case WM_SETFOCUS:
1855                 if( lphc->wState & CBF_EDIT )
1856                     SetFocus( lphc->hWndEdit );
1857                 else
1858                     COMBO_SetFocus( lphc );
1859                 return  TRUE;
1860         case WM_KILLFOCUS:
1861 #define hwndFocus ((HWND16)wParam)
1862                 if( !hwndFocus ||
1863                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1864                     COMBO_KillFocus( lphc );
1865 #undef hwndFocus
1866                 return  TRUE;
1867         case WM_COMMAND:
1868                 return  COMBO_Command( lphc, wParam, (HWND)lParam );
1869         case WM_GETTEXT:
1870                 return  COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1871         case WM_SETTEXT:
1872         case WM_GETTEXTLENGTH:
1873         case WM_CLEAR:
1874         case WM_CUT:
1875         case WM_PASTE:
1876         case WM_COPY:
1877                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1878                 {
1879                 int j = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1880                 if (j == -1) return 0;
1881                 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1882                 }
1883                 else if( lphc->wState & CBF_EDIT ) 
1884                 {
1885                     LRESULT ret;
1886                     lphc->wState |= CBF_NOEDITNOTIFY;
1887                     ret = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1888                     lphc->wState &= ~CBF_NOEDITNOTIFY;
1889                     return ret;
1890                 }
1891                 else return  CB_ERR;
1892
1893         case WM_DRAWITEM:
1894         case WM_DELETEITEM:
1895         case WM_COMPAREITEM:
1896         case WM_MEASUREITEM:
1897                 return  COMBO_ItemOp( lphc, message, wParam, lParam );
1898         case WM_ENABLE:
1899                 if( lphc->wState & CBF_EDIT )
1900                     EnableWindow( lphc->hWndEdit, (BOOL)wParam ); 
1901                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1902
1903                 /* Force the control to repaint when the enabled state changes. */
1904                 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1905                 return  TRUE;
1906         case WM_SETREDRAW:
1907                 if( wParam )
1908                     lphc->wState &= ~CBF_NOREDRAW;
1909                 else
1910                     lphc->wState |= CBF_NOREDRAW;
1911
1912                 if( lphc->wState & CBF_EDIT )
1913                     SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1914                 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1915                 return  0;
1916         case WM_SYSKEYDOWN:
1917                 if( KEYDATA_ALT & HIWORD(lParam) )
1918                     if( wParam == VK_UP || wParam == VK_DOWN )
1919                         COMBO_FlipListbox( lphc, TRUE );
1920                 break;/* -> DefWindowProc */
1921
1922         case WM_CHAR:
1923         case WM_KEYDOWN:
1924                 if( lphc->wState & CBF_EDIT )
1925                     return  SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1926                 else
1927                     return  SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1928         case WM_LBUTTONDOWN: 
1929                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1930                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1931                 return  TRUE;
1932         case WM_LBUTTONUP:
1933                 COMBO_LButtonUp( lphc, lParam );
1934                 return  TRUE;
1935         case WM_MOUSEMOVE: 
1936                 if( lphc->wState & CBF_CAPTURE ) 
1937                     COMBO_MouseMove( lphc, wParam, lParam );
1938                 return  TRUE;
1939         /* Combo messages */
1940
1941         case CB_ADDSTRING16:
1942                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1943         case CB_ADDSTRING:
1944                 return  SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1945         case CB_INSERTSTRING16:
1946                 wParam = (INT)(INT16)wParam;
1947                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1948         case CB_INSERTSTRING:
1949                 return  SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1950         case CB_DELETESTRING16:
1951         case CB_DELETESTRING:
1952                 return  SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1953         case CB_SELECTSTRING16:
1954                 wParam = (INT)(INT16)wParam;
1955                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1956         case CB_SELECTSTRING:
1957                 return  COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1958         case CB_FINDSTRING16:
1959                 wParam = (INT)(INT16)wParam;
1960                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1961         case CB_FINDSTRING:
1962                 return  SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1963         case CB_FINDSTRINGEXACT16:
1964                 wParam = (INT)(INT16)wParam;
1965                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1966         case CB_FINDSTRINGEXACT:
1967                 return  SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT, 
1968                                                        wParam, lParam );
1969         case CB_SETITEMHEIGHT16:
1970                 wParam = (INT)(INT16)wParam;    /* signed integer */
1971         case CB_SETITEMHEIGHT:
1972                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1973         case CB_GETITEMHEIGHT16:
1974                 wParam = (INT)(INT16)wParam;
1975         case CB_GETITEMHEIGHT:
1976                 if( (INT)wParam >= 0 )  /* listbox item */
1977                     return  SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1978                 return  CBGetTextAreaHeight(hwnd, lphc);
1979         case CB_RESETCONTENT16: 
1980         case CB_RESETCONTENT:
1981                 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1982                 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1983                 return  TRUE;
1984         case CB_INITSTORAGE:
1985                 return  SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1986         case CB_GETHORIZONTALEXTENT:
1987                 return  SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1988         case CB_SETHORIZONTALEXTENT:
1989                 return  SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1990         case CB_GETTOPINDEX:
1991                 return  SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1992         case CB_GETLOCALE:
1993                 return  SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1994         case CB_SETLOCALE:
1995                 return  SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1996         case CB_GETDROPPEDWIDTH:
1997                 if( lphc->droppedWidth )
1998                     return  lphc->droppedWidth;
1999                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2000         case CB_SETDROPPEDWIDTH:
2001                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2002                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2003                 return  CB_ERR;
2004         case CB_GETDROPPEDCONTROLRECT16:
2005                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2006                 if( lParam ) 
2007                 {
2008                     RECT        r;
2009                     CBGetDroppedControlRect( lphc, &r );
2010                     CONV_RECT32TO16( &r, (LPRECT16)lParam );
2011                 }
2012                 return  CB_OKAY;
2013         case CB_GETDROPPEDCONTROLRECT:
2014                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2015                 return  CB_OKAY;
2016         case CB_GETDROPPEDSTATE16:
2017         case CB_GETDROPPEDSTATE:
2018                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2019         case CB_DIR16: 
2020                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2021                 /* fall through */
2022         case CB_DIR:
2023                 return  COMBO_Directory( lphc, (UINT)wParam, 
2024                                        (LPSTR)lParam, (message == CB_DIR));
2025         case CB_SHOWDROPDOWN16:
2026         case CB_SHOWDROPDOWN:
2027                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2028                 {
2029                     if( wParam )
2030                     {
2031                         if( !(lphc->wState & CBF_DROPPED) )
2032                             CBDropDown( lphc );
2033                     }
2034                     else 
2035                         if( lphc->wState & CBF_DROPPED ) 
2036                             CBRollUp( lphc, FALSE, TRUE );
2037                 }
2038                 return  TRUE;
2039         case CB_GETCOUNT16: 
2040         case CB_GETCOUNT:
2041                 return  SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2042         case CB_GETCURSEL16: 
2043         case CB_GETCURSEL:
2044                 return  SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2045         case CB_SETCURSEL16:
2046                 wParam = (INT)(INT16)wParam;
2047         case CB_SETCURSEL:
2048                 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2049                 if( lphc->wState & CBF_SELCHANGE )
2050                 {
2051                     /* no LBN_SELCHANGE in this case, update manually */
2052                     if( lphc->wState & CBF_EDIT )
2053                         CBUpdateEdit( lphc, (INT)wParam );
2054                     else
2055                         InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2056                     lphc->wState &= ~CBF_SELCHANGE;
2057                 }
2058                 return  lParam;
2059         case CB_GETLBTEXT16: 
2060                 wParam = (INT)(INT16)wParam;
2061                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2062         case CB_GETLBTEXT:
2063                 return  SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2064         case CB_GETLBTEXTLEN16: 
2065                 wParam = (INT)(INT16)wParam;
2066         case CB_GETLBTEXTLEN:
2067                 return  SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2068         case CB_GETITEMDATA16:
2069                 wParam = (INT)(INT16)wParam;
2070         case CB_GETITEMDATA:
2071                 return  SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2072         case CB_SETITEMDATA16:
2073                 wParam = (INT)(INT16)wParam;
2074         case CB_SETITEMDATA:
2075                 return  SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2076         case CB_GETEDITSEL16: 
2077                 wParam = lParam = 0;   /* just in case */
2078         case CB_GETEDITSEL:
2079                 if( lphc->wState & CBF_EDIT )
2080                 {
2081                     INT a, b;
2082
2083                     return  SendMessageA( lphc->hWndEdit, EM_GETSEL,
2084                                            (wParam) ? wParam : (WPARAM)&a,
2085                                            (lParam) ? lParam : (LPARAM)&b );
2086                 }
2087                 return  CB_ERR;
2088         case CB_SETEDITSEL16: 
2089         case CB_SETEDITSEL:
2090                 if( lphc->wState & CBF_EDIT ) 
2091                     return  SendMessageA( lphc->hWndEdit, EM_SETSEL,
2092                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2093                 return  CB_ERR;
2094         case CB_SETEXTENDEDUI16:
2095         case CB_SETEXTENDEDUI:
2096                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2097                     return  CB_ERR;
2098                 if( wParam )
2099                     lphc->wState |= CBF_EUI;
2100                 else lphc->wState &= ~CBF_EUI;
2101                 return  CB_OKAY;
2102         case CB_GETEXTENDEDUI16:
2103         case CB_GETEXTENDEDUI:
2104                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2105
2106         default:
2107                 if (message >= WM_USER)
2108                     WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2109                         message - WM_USER, wParam, lParam );
2110                 break;
2111     }
2112     return DefWindowProcA(hwnd, message, wParam, lParam);
2113   }
2114   return CB_ERR;
2115 }
2116
2117 /***********************************************************************
2118  *           ComboWndProc
2119  *
2120  * This is just a wrapper for the real ComboWndProc which locks/unlocks
2121  * window structs.
2122  */
2123 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
2124                              WPARAM wParam, LPARAM lParam )
2125 {
2126     WND*        pWnd = WIN_FindWndPtr(hwnd);
2127     LRESULT retvalue = ComboWndProc_locked(pWnd,message,wParam,lParam);
2128
2129     
2130     WIN_ReleaseWndPtr(pWnd);
2131     return retvalue;
2132 }
2133