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