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