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