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