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