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