Several focus and selection problems in listboxes.
[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;
492
493       /*
494        * Initialize the dropped rect to the size of the client area of the
495        * control and then, force all the areas of the combobox to be
496        * recalculated.
497        */
498       GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
499
500       CBCalcPlacement(wnd->hwndSelf, 
501                       lphc, 
502                       &lphc->textRect, 
503                       &lphc->buttonRect, 
504                       &lphc->droppedRect );
505
506       /*
507        * Adjust the position of the popup listbox if it's necessary
508        */
509       if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
510       {
511         lphc->droppedRect.top   = lphc->textRect.bottom + COMBO_YBORDERSIZE();
512
513         /*
514          * If it's a dropdown, the listbox is offset
515          */
516         if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
517           lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
518
519         ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
520         ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
521       }
522
523       /* create listbox popup */
524
525       lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) | 
526                  (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
527
528       if( lphc->dwStyle & CBS_SORT )
529         lbeStyle |= LBS_SORT;
530       if( lphc->dwStyle & CBS_HASSTRINGS )
531         lbeStyle |= LBS_HASSTRINGS;
532       if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
533         lbeStyle |= LBS_NOINTEGRALHEIGHT;
534       if( lphc->dwStyle & CBS_DISABLENOSCROLL )
535         lbeStyle |= LBS_DISABLENOSCROLL;
536   
537       if( CB_GETTYPE(lphc) == CBS_SIMPLE )      /* child listbox */
538         lbeStyle |= WS_CHILD | WS_VISIBLE;
539       else                                      /* popup listbox */
540         lbeStyle |= WS_POPUP;
541
542      /* Dropdown ComboLBox is not a child window and we cannot pass 
543       * ID_CB_LISTBOX directly because it will be treated as a menu handle.
544       */
545       lphc->hWndLBox = CreateWindowExA(0,
546                                        clbName,
547                                        NULL, 
548                                        lbeStyle, 
549                                        lphc->droppedRect.left, 
550                                        lphc->droppedRect.top, 
551                                        lphc->droppedRect.right - lphc->droppedRect.left, 
552                                        lphc->droppedRect.bottom - lphc->droppedRect.top, 
553                                        lphc->self->hwndSelf, 
554                        (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
555                                        lphc->self->hInstance,
556                                        (LPVOID)lphc );
557
558       /*
559        * The ComboLBox is a strange little beast (when it's not a CBS_SIMPLE)...
560        * It's a popup window but, when you get the window style, you get WS_CHILD.
561        * When created, it's parent is the combobox but, when you ask for it's parent
562        * after that, you're supposed to get the desktop. (see MFC code function
563        * AfxCancelModes)
564        * To achieve this in Wine, we have to create it as a popup and change 
565        * it's style to child after the creation. 
566        */
567       if ( (lphc->hWndLBox!= 0) &&
568            (CB_GETTYPE(lphc) != CBS_SIMPLE) )
569       {
570         SetWindowLongA(lphc->hWndLBox, 
571                        GWL_STYLE, 
572                        (GetWindowLongA(lphc->hWndLBox, GWL_STYLE) | WS_CHILD) & ~WS_POPUP);
573       }
574
575       if( lphc->hWndLBox )
576       {
577           BOOL  bEdit = TRUE;
578           lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT;
579
580           /*
581            * In Win95 look, the border fo the edit control is 
582            * provided by the combobox
583            */
584           if (TWEAK_WineLook == WIN31_LOOK)
585             lbeStyle |= WS_BORDER;
586             
587           if( lphc->wState & CBF_EDIT ) 
588           {
589               if( lphc->dwStyle & CBS_OEMCONVERT )
590                   lbeStyle |= ES_OEMCONVERT;
591               if( lphc->dwStyle & CBS_AUTOHSCROLL )
592                   lbeStyle |= ES_AUTOHSCROLL;
593               if( lphc->dwStyle & CBS_LOWERCASE )
594                   lbeStyle |= ES_LOWERCASE;
595               else if( lphc->dwStyle & CBS_UPPERCASE )
596                   lbeStyle |= ES_UPPERCASE;
597
598               lphc->hWndEdit = CreateWindowExA(0,
599                                                editName, 
600                                                NULL, 
601                                                lbeStyle,
602                                                lphc->textRect.left, lphc->textRect.top, 
603                                                lphc->textRect.right - lphc->textRect.left,
604                                                lphc->textRect.bottom - lphc->textRect.top, 
605                                                lphc->self->hwndSelf, 
606                                                (HMENU)ID_CB_EDIT, 
607                                                lphc->self->hInstance, 
608                                                NULL );
609
610               if( !lphc->hWndEdit )
611                 bEdit = FALSE;
612           } 
613
614           if( bEdit )
615           {
616             /* 
617              * If the combo is a dropdown, we must resize the control to fit only
618              * the text area and button. To do this, we send a dummy resize and the
619              * WM_WINDOWPOSCHANGING message will take care of setting the height for
620              * us.
621              */
622             if( CB_GETTYPE(lphc) != CBS_SIMPLE )
623             {
624               CBForceDummyResize(lphc);
625             }
626             
627             TRACE("init done\n");
628             return wnd->hwndSelf;
629           }
630           ERR("edit control failure.\n");
631       } else ERR("listbox failure.\n");
632   } else ERR("no owner for visible combo.\n");
633
634   /* CreateWindow() will send WM_NCDESTROY to cleanup */
635
636   return -1;
637 }
638
639 /***********************************************************************
640  *           CBPaintButton
641  *
642  * Paint combo button (normal, pressed, and disabled states).
643  */
644 static void CBPaintButton(
645   LPHEADCOMBO lphc, 
646   HDC         hdc,
647   RECT        rectButton)
648 {
649     UINT        x, y;
650     BOOL        bBool;
651     HDC       hMemDC;
652     HBRUSH    hPrevBrush;
653     COLORREF    oldTextColor, oldBkColor;
654
655     if( lphc->wState & CBF_NOREDRAW ) 
656       return;
657
658     hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
659
660     /*
661      * Draw the button background
662      */
663     PatBlt( hdc,
664             rectButton.left,
665             rectButton.top,
666             rectButton.right-rectButton.left,
667             rectButton.bottom-rectButton.top,
668             PATCOPY );
669
670     if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
671     {
672         DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
673     } 
674     else 
675     {
676         DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
677     }
678
679     /*
680      * Remove the edge of the button from the rectangle
681      * and calculate the position of the bitmap.
682      */
683     InflateRect( &rectButton, -2, -2);  
684
685     x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
686     y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
687
688
689     hMemDC = CreateCompatibleDC( hdc );
690     SelectObject( hMemDC, hComboBmp );
691     oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
692     oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
693                                RGB(0,0,0) );
694     BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
695     SetBkColor( hdc, oldBkColor );
696     SetTextColor( hdc, oldTextColor );
697     DeleteDC( hMemDC );
698     SelectObject( hdc, hPrevBrush );
699 }
700
701 /***********************************************************************
702  *           CBPaintText
703  *
704  * Paint CBS_DROPDOWNLIST text field / update edit control contents.
705  */
706 static void CBPaintText(
707   LPHEADCOMBO lphc, 
708   HDC         hdc,
709   RECT        rectEdit)
710 {
711    INT  id, size = 0;
712    LPSTR        pText = NULL;
713
714    if( lphc->wState & CBF_NOREDRAW ) return;
715
716    /* follow Windows combobox that sends a bunch of text 
717     * inquiries to its listbox while processing WM_PAINT. */
718
719    if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
720    {
721         size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
722         if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
723         {
724             SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
725             pText[size] = '\0'; /* just in case */
726         } else return;
727    }
728
729    if( lphc->wState & CBF_EDIT )
730    {
731         if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
732         if( lphc->wState & CBF_FOCUSED ) 
733             SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
734    }
735    else /* paint text field ourselves */
736    {
737         HBRUSH hPrevBrush = 0;
738         HDC      hDC = hdc;
739
740         if( !hDC ) 
741         {
742             if ((hDC = GetDC(lphc->self->hwndSelf)))
743             {
744                 HBRUSH hBrush = SendMessageA( lphc->owner,
745                                                   WM_CTLCOLORLISTBOX, 
746                                                   hDC, lphc->self->hwndSelf );
747                 hPrevBrush = SelectObject( hDC, 
748                            (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
749             }
750         }
751         if( hDC )
752         {
753             UINT        itemState;
754             HFONT       hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
755
756             /*
757              * Give ourselves some space.
758              */
759             InflateRect( &rectEdit, -1, -1 );
760
761             if ( (lphc->wState & CBF_FOCUSED) && 
762                 !(lphc->wState & CBF_DROPPED) )
763             {
764                 /* highlight */
765
766                 FillRect( hDC, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
767                 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
768                 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
769                 itemState = ODS_SELECTED | ODS_FOCUS;
770             } 
771             else 
772               itemState = 0;
773
774             if( CB_OWNERDRAWN(lphc) )
775             {
776                 DRAWITEMSTRUCT dis;
777                 HRGN           clipRegion;
778
779                 /*
780                  * Save the current clip region.
781                  * To retrieve the clip region, we need to create one "dummy"
782                  * clip region.
783                  */
784                 clipRegion = CreateRectRgnIndirect(&rectEdit);
785
786                 if (GetClipRgn(hDC, clipRegion)!=1)
787                 {
788                   DeleteObject(clipRegion);
789                   clipRegion=(HRGN)NULL;
790                 }
791
792                 if ( lphc->self->dwStyle & WS_DISABLED )
793                   itemState |= ODS_DISABLED;
794
795                 dis.CtlType     = ODT_COMBOBOX;
796                 dis.CtlID       = lphc->self->wIDmenu;
797                 dis.hwndItem    = lphc->self->hwndSelf;
798                 dis.itemAction  = ODA_DRAWENTIRE;
799                 dis.itemID      = id;
800                 dis.itemState   = itemState;
801                 dis.hDC         = hDC;
802                 dis.rcItem      = rectEdit;
803                 dis.itemData    = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, 
804                                                                   (WPARAM)id, 0 );
805
806                 /*
807                  * Clip the DC and have the parent draw the item.
808                  */
809                 IntersectClipRect(hDC,
810                                   rectEdit.left,  rectEdit.top,
811                                   rectEdit.right, rectEdit.bottom);
812
813                 SendMessageA(lphc->owner, WM_DRAWITEM, 
814                              lphc->self->wIDmenu, (LPARAM)&dis );
815
816                 /*
817                  * Reset the clipping region.
818                  */
819                 SelectClipRgn(hDC, clipRegion);         
820             }
821             else
822             {
823                 ExtTextOutA( hDC, 
824                              rectEdit.left + 1, 
825                              rectEdit.top + 1,
826                              ETO_OPAQUE | ETO_CLIPPED, 
827                              &rectEdit,
828                                pText ? pText : "" , size, NULL );
829
830                 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
831                     DrawFocusRect( hDC, &rectEdit );
832             }
833
834             if( hPrevFont ) 
835               SelectObject(hDC, hPrevFont );
836
837             if( !hdc ) 
838             {
839                 if( hPrevBrush ) 
840                   SelectObject( hDC, hPrevBrush );
841
842                 ReleaseDC( lphc->self->hwndSelf, hDC );
843             }
844         }
845    }
846    if (pText)
847         HeapFree( GetProcessHeap(), 0, pText );
848 }
849
850 /***********************************************************************
851  *           CBPaintBorder
852  */
853 static void CBPaintBorder(
854   HWND        hwnd,
855   LPHEADCOMBO lphc,   
856   HDC         hdc)
857 {
858   RECT clientRect;
859
860   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
861   {
862     GetClientRect(hwnd, &clientRect);
863   }
864   else
865   {
866     CopyRect(&clientRect, &lphc->textRect);
867
868     InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
869     InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
870   }
871
872   DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
873 }
874
875 /***********************************************************************
876  *           COMBO_EraseBackground
877  */
878 static LRESULT COMBO_EraseBackground(
879   HWND        hwnd, 
880   LPHEADCOMBO lphc,
881   HDC         hParamDC)
882 {
883   HBRUSH  hBkgBrush;
884   RECT    clientRect;
885   HDC     hDC;
886   
887   hDC = (hParamDC) ? hParamDC
888                    : GetDC(hwnd);
889
890   /*
891    * Calculate the area that we want to erase.
892    */
893   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
894   {
895     GetClientRect(hwnd, &clientRect);
896   }
897   else
898   {
899     CopyRect(&clientRect, &lphc->textRect);
900
901     InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
902   }
903
904   hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
905                             hDC, hwnd);
906
907   if( !hBkgBrush ) 
908     hBkgBrush = GetStockObject(WHITE_BRUSH);
909   
910   FillRect(hDC, &clientRect, hBkgBrush);
911
912   if (!hParamDC)
913     ReleaseDC(hwnd, hDC);
914
915   return TRUE;
916 }
917
918 /***********************************************************************
919  *           COMBO_Paint
920  */
921 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
922 {
923   PAINTSTRUCT ps;
924   HDC   hDC;
925   
926   hDC = (hParamDC) ? hParamDC
927                    : BeginPaint( lphc->self->hwndSelf, &ps);
928
929
930   if( hDC && !(lphc->wState & CBF_NOREDRAW) )
931   {
932       HBRUSH    hPrevBrush, hBkgBrush;
933
934       hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
935                                   hDC, lphc->self->hwndSelf );
936
937       if( !hBkgBrush ) 
938         hBkgBrush = GetStockObject(WHITE_BRUSH);
939
940       hPrevBrush = SelectObject( hDC, hBkgBrush );
941
942       /*
943        * In non 3.1 look, there is a sunken border on the combobox
944        */
945       if (TWEAK_WineLook != WIN31_LOOK)
946       {
947         CBPaintBorder(CB_HWND(lphc), lphc, hDC);
948       }
949
950       if( !IsRectEmpty(&lphc->buttonRect) )
951       {
952         CBPaintButton(lphc, hDC, lphc->buttonRect);
953       }
954
955       if( !(lphc->wState & CBF_EDIT) )
956       {
957         /*
958          * The text area has a border only in Win 3.1 look.
959          */
960         if (TWEAK_WineLook == WIN31_LOOK)
961         {
962           HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
963           
964           Rectangle( hDC, 
965                      lphc->textRect.left, lphc->textRect.top,
966                      lphc->textRect.right - 1, lphc->textRect.bottom - 1);
967
968           SelectObject( hDC, hPrevPen );
969         }
970
971         CBPaintText( lphc, hDC, lphc->textRect);
972       }
973
974       if( hPrevBrush )
975         SelectObject( hDC, hPrevBrush );
976   }
977
978   if( !hParamDC ) 
979     EndPaint(lphc->self->hwndSelf, &ps);
980
981   return 0;
982 }
983
984 /***********************************************************************
985  *           CBUpdateLBox
986  *
987  * Select listbox entry according to the contents of the edit control.
988  */
989 static INT CBUpdateLBox( LPHEADCOMBO lphc )
990 {
991    INT  length, idx, ret;
992    LPSTR        pText = NULL;
993    
994    idx = ret = LB_ERR;
995    length = CB_GETEDITTEXTLENGTH( lphc );
996  
997    if( length > 0 ) 
998        pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
999
1000    TRACE("\t edit text length %i\n", length );
1001
1002    if( pText )
1003    {
1004        if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
1005        else pText[0] = '\0';
1006        idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING, 
1007                              (WPARAM)(-1), (LPARAM)pText );
1008        if( idx == LB_ERR ) idx = 0;     /* select first item */
1009        else ret = idx;
1010        HeapFree( GetProcessHeap(), 0, pText );
1011    }
1012
1013    /* select entry */
1014
1015    SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
1016    
1017    if( idx >= 0 )
1018    {
1019        SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
1020        /* probably superfluous but Windows sends this too */
1021        SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
1022    }
1023    return ret;
1024 }
1025
1026 /***********************************************************************
1027  *           CBUpdateEdit
1028  *
1029  * Copy a listbox entry to the edit control.
1030  */
1031 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1032 {
1033    INT  length;
1034    LPSTR        pText = NULL;
1035
1036    TRACE("\t %i\n", index );
1037
1038    if( index == -1 )
1039    {
1040        length = CB_GETEDITTEXTLENGTH( lphc );
1041        if( length )
1042        {
1043            if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1044            {
1045                 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
1046                 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1047                                         (WPARAM)(-1), (LPARAM)pText );
1048                 HeapFree( GetProcessHeap(), 0, pText );
1049            }
1050        }
1051    }
1052
1053    if( index >= 0 ) /* got an entry */
1054    {
1055        length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1056        if( length )
1057        {
1058            if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1059            {
1060                 SendMessageA( lphc->hWndLBox, LB_GETTEXT, 
1061                                 (WPARAM)index, (LPARAM)pText );
1062
1063                 lphc->wState |= CBF_NOEDITNOTIFY;
1064
1065                 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
1066                 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1067                 HeapFree( GetProcessHeap(), 0, pText );
1068            }
1069        }
1070    }
1071 }
1072
1073 /***********************************************************************
1074  *           CBDropDown
1075  * 
1076  * Show listbox popup.
1077  */
1078 static void CBDropDown( LPHEADCOMBO lphc )
1079 {
1080    RECT rect;
1081    int nItems = 0;
1082    int i;
1083    int nHeight;
1084    int nDroppedHeight;
1085
1086    TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1087
1088    CB_NOTIFY( lphc, CBN_DROPDOWN );
1089
1090    /* set selection */
1091
1092    lphc->wState |= CBF_DROPPED;
1093    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1094    {
1095        lphc->droppedIndex = CBUpdateLBox( lphc );
1096
1097        if( !(lphc->wState & CBF_CAPTURE) ) 
1098          CBUpdateEdit( lphc, lphc->droppedIndex );
1099    }
1100    else
1101    {
1102        lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1103
1104        if( lphc->droppedIndex == LB_ERR ) 
1105          lphc->droppedIndex = 0;
1106
1107        SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)lphc->droppedIndex, 0 );
1108        SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1109    }
1110
1111    /* now set popup position */
1112    GetWindowRect( lphc->self->hwndSelf, &rect );
1113    
1114    /*
1115     * If it's a dropdown, the listbox is offset
1116     */
1117    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1118      rect.left += COMBO_EDITBUTTONSPACE();
1119
1120   /* if the dropped height is greater than the total height of the dropped
1121      items list, then force the drop down list height to be the total height
1122      of the items in the dropped list */
1123
1124    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1125    nItems = (int)SendMessageA (lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1126    nHeight = COMBO_YBORDERGAP;
1127    for (i = 0; i < nItems; i++)
1128    {
1129      nHeight += (int)SendMessageA (lphc->hWndLBox, LB_GETITEMHEIGHT, i, 0);
1130
1131      if (nHeight >= nDroppedHeight)
1132        break;
1133    }
1134
1135    if (nHeight < nDroppedHeight)
1136       nDroppedHeight = nHeight;
1137
1138    SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom, 
1139                  lphc->droppedRect.right - lphc->droppedRect.left,
1140       nDroppedHeight,
1141                  SWP_NOACTIVATE | SWP_NOREDRAW);
1142
1143    if( !(lphc->wState & CBF_NOREDRAW) )
1144      RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE | 
1145                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1146
1147    ShowWindow( lphc->hWndLBox, SW_SHOWNA );
1148 }
1149
1150 /***********************************************************************
1151  *           CBRollUp
1152  *
1153  * Hide listbox popup.
1154  */
1155 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1156 {
1157    HWND hWnd = lphc->self->hwndSelf;
1158
1159    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1160
1161    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1162    {
1163
1164        TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1165
1166        if( lphc->wState & CBF_DROPPED ) 
1167        {
1168            RECT rect;
1169
1170            /* 
1171             * It seems useful to send the WM_LBUTTONUP with (-1,-1) when cancelling 
1172             * and with (0,0) (anywhere in the listbox) when Oking.
1173             */
1174            SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, ok ? (LPARAM)0 : (LPARAM)(-1) );
1175
1176            lphc->wState &= ~CBF_DROPPED;
1177            ShowWindow( lphc->hWndLBox, SW_HIDE );
1178
1179            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1180            {
1181                INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1182                CBUpdateEdit( lphc, index );
1183                rect = lphc->buttonRect;
1184            }
1185            else 
1186            {
1187                if( bButton )
1188                {
1189                  UnionRect( &rect,
1190                             &lphc->buttonRect,
1191                             &lphc->textRect);
1192                }
1193                else
1194                  rect = lphc->textRect;
1195
1196                bButton = TRUE;
1197            }
1198
1199            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1200                RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | 
1201                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1202            CB_NOTIFY( lphc, CBN_CLOSEUP );
1203        }
1204    }
1205 }
1206
1207 /***********************************************************************
1208  *           COMBO_FlipListbox
1209  *
1210  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1211  */
1212 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
1213 {
1214    if( lphc->wState & CBF_DROPPED )
1215    {
1216        CBRollUp( lphc, TRUE, bRedrawButton );
1217        return FALSE;
1218    }
1219
1220    CBDropDown( lphc );
1221    return TRUE;
1222 }
1223
1224 /***********************************************************************
1225  *           COMBO_GetLBWindow
1226  *
1227  * Edit control helper.
1228  */
1229 HWND COMBO_GetLBWindow( WND* pWnd )
1230 {
1231   LPHEADCOMBO       lphc = CB_GETPTR(pWnd);
1232   if( lphc ) return lphc->hWndLBox;
1233   return 0;
1234 }
1235
1236
1237 /***********************************************************************
1238  *           CBRepaintButton
1239  */
1240 static void CBRepaintButton( LPHEADCOMBO lphc )
1241    {
1242   InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1243   UpdateWindow(CB_HWND(lphc));
1244 }
1245
1246 /***********************************************************************
1247  *           COMBO_SetFocus
1248  */
1249 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1250 {
1251    if( !(lphc->wState & CBF_FOCUSED) )
1252    {
1253        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1254            SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1255
1256        if( lphc->wState & CBF_EDIT )
1257            SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1258        lphc->wState |= CBF_FOCUSED;
1259        if( !(lphc->wState & CBF_EDIT) )
1260        {
1261          InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1262        }
1263
1264        CB_NOTIFY( lphc, CBN_SETFOCUS );
1265    }
1266 }
1267
1268 /***********************************************************************
1269  *           COMBO_KillFocus
1270  */
1271 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1272 {
1273    HWND hWnd = lphc->self->hwndSelf;
1274
1275    if( lphc->wState & CBF_FOCUSED )
1276    {
1277        SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
1278
1279        CBRollUp( lphc, FALSE, TRUE );
1280        if( IsWindow( hWnd ) )
1281        {
1282            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1283                SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1284
1285            lphc->wState &= ~CBF_FOCUSED;
1286
1287            /* redraw text */
1288            if( lphc->wState & CBF_EDIT )
1289                SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
1290            else
1291            {
1292              InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1293            }
1294
1295            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1296        }
1297    }
1298 }
1299
1300 /***********************************************************************
1301  *           COMBO_Command
1302  */
1303 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1304 {
1305    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1306    {
1307        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1308
1309        switch( HIWORD(wParam) >> 8 )
1310        {   
1311            case (EN_SETFOCUS >> 8):
1312
1313                 TRACE("[%04x]: edit [%04x] got focus\n", 
1314                              CB_HWND(lphc), lphc->hWndEdit );
1315
1316                 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
1317                 break;
1318
1319            case (EN_KILLFOCUS >> 8):
1320
1321                 TRACE("[%04x]: edit [%04x] lost focus\n",
1322                              CB_HWND(lphc), lphc->hWndEdit );
1323
1324                 /* NOTE: it seems that Windows' edit control sends an
1325                  * undocumented message WM_USER + 0x1B instead of this
1326                  * notification (only when it happens to be a part of 
1327                  * the combo). ?? - AK.
1328                  */
1329
1330                 COMBO_KillFocus( lphc );
1331                 break;
1332
1333
1334            case (EN_CHANGE >> 8):
1335                /*
1336                 * In some circumstances (when the selection of the combobox
1337                 * is changed for example) we don't wans the EN_CHANGE notification
1338                 * to be forwarded to the parent of the combobox. This code
1339                 * checks a flag that is set in these occasions and ignores the 
1340                 * notification.
1341                 */
1342                 if (lphc->wState & CBF_NOEDITNOTIFY)
1343                 {
1344                   lphc->wState &= ~CBF_NOEDITNOTIFY;
1345                 }
1346                 else
1347                 {
1348                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1349                 }
1350
1351                 CBUpdateLBox( lphc );
1352                 break;
1353
1354            case (EN_UPDATE >> 8):
1355                 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1356                 break;
1357
1358            case (EN_ERRSPACE >> 8):
1359                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1360        }
1361    }
1362    else if( lphc->hWndLBox == hWnd )
1363    {
1364        switch( HIWORD(wParam) )
1365        {
1366            case LBN_ERRSPACE:
1367                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1368                 break;
1369
1370            case LBN_DBLCLK:
1371                 CB_NOTIFY( lphc, CBN_DBLCLK );
1372                 break;
1373
1374            case LBN_SELCHANGE:
1375            case LBN_SELCANCEL:
1376
1377                 TRACE("[%04x]: lbox selection change [%04x]\n", 
1378                              CB_HWND(lphc), lphc->wState );
1379
1380                 /* do not roll up if selection is being tracked 
1381                  * by arrowkeys in the dropdown listbox */
1382
1383                 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
1384                      CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1385                 else lphc->wState &= ~CBF_NOROLLUP;
1386
1387                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1388                 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1389                 /* fall through */
1390
1391            case LBN_SETFOCUS:
1392            case LBN_KILLFOCUS:
1393                 /* nothing to do here since ComboLBox always resets the focus to its
1394                  * combo/edit counterpart */
1395                  break;
1396        }
1397    }
1398    return 0;
1399 }
1400
1401 /***********************************************************************
1402  *           COMBO_ItemOp
1403  *
1404  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1405  */
1406 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, 
1407                                WPARAM wParam, LPARAM lParam ) 
1408 {
1409    HWND hWnd = lphc->self->hwndSelf;
1410
1411    TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1412
1413 #define lpIS    ((LPDELETEITEMSTRUCT)lParam)
1414
1415    /* two first items are the same in all 4 structs */
1416    lpIS->CtlType = ODT_COMBOBOX;
1417    lpIS->CtlID   = lphc->self->wIDmenu;
1418
1419    switch( msg )        /* patch window handle */
1420    {
1421         case WM_DELETEITEM: 
1422              lpIS->hwndItem = hWnd; 
1423 #undef  lpIS
1424              break;
1425         case WM_DRAWITEM: 
1426 #define lpIS    ((LPDRAWITEMSTRUCT)lParam)
1427              lpIS->hwndItem = hWnd; 
1428 #undef  lpIS
1429              break;
1430         case WM_COMPAREITEM: 
1431 #define lpIS    ((LPCOMPAREITEMSTRUCT)lParam)
1432              lpIS->hwndItem = hWnd; 
1433 #undef  lpIS
1434              break;
1435    }
1436
1437    return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1438 }
1439
1440 /***********************************************************************
1441  *           COMBO_GetText
1442  */
1443 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1444 {
1445    if( lphc->wState & CBF_EDIT )
1446        return SendMessageA( lphc->hWndEdit, WM_GETTEXT, 
1447                              (WPARAM)N, (LPARAM)lpText );     
1448
1449    /* get it from the listbox */
1450
1451    if( lphc->hWndLBox )
1452    {
1453        INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1454        if( idx != LB_ERR )
1455        {
1456            LPSTR        lpBuffer;
1457            INT  length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1458                                                 (WPARAM)idx, 0 );
1459
1460            /* 'length' is without the terminating character */
1461            if( length >= N )
1462                lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1463            else 
1464                lpBuffer = lpText;
1465
1466            if( lpBuffer )
1467            {
1468                INT    n = SendMessageA( lphc->hWndLBox, LB_GETTEXT, 
1469                                            (WPARAM)idx, (LPARAM)lpBuffer );
1470
1471                /* truncate if buffer is too short */
1472
1473                if( length >= N )
1474                {
1475                    if (N && lpText) {
1476                    if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1477                    lpText[N - 1] = '\0';
1478                    }
1479                    HeapFree( GetProcessHeap(), 0, lpBuffer );
1480                }
1481                return (LRESULT)n;
1482            }
1483        }
1484    }
1485    return 0;
1486 }
1487
1488
1489 /***********************************************************************
1490  *           CBResetPos
1491  *
1492  * This function sets window positions according to the updated 
1493  * component placement struct.
1494  */
1495 static void CBResetPos(
1496   LPHEADCOMBO lphc, 
1497   LPRECT      rectEdit,
1498   LPRECT      rectLB,
1499   BOOL        bRedraw)
1500 {
1501    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1502
1503    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1504     * sizing messages */
1505
1506    if( lphc->wState & CBF_EDIT )
1507      SetWindowPos( lphc->hWndEdit, 0, 
1508                    rectEdit->left, rectEdit->top,
1509                    rectEdit->right - rectEdit->left,
1510                    rectEdit->bottom - rectEdit->top,
1511                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1512
1513    SetWindowPos( lphc->hWndLBox, 0,
1514                  rectLB->left, rectLB->top,
1515                  rectLB->right - rectLB->left, 
1516                  rectLB->bottom - rectLB->top, 
1517                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1518
1519    if( bDrop )
1520    {
1521        if( lphc->wState & CBF_DROPPED )
1522        {
1523            lphc->wState &= ~CBF_DROPPED;
1524            ShowWindow( lphc->hWndLBox, SW_HIDE );
1525        }
1526
1527        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1528            RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1529                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1530    }
1531 }
1532
1533
1534 /***********************************************************************
1535  *           COMBO_Size
1536  */
1537 static void COMBO_Size( LPHEADCOMBO lphc )
1538   {
1539   CBCalcPlacement(lphc->self->hwndSelf,
1540                   lphc, 
1541                   &lphc->textRect, 
1542                   &lphc->buttonRect, 
1543                   &lphc->droppedRect);
1544
1545   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1546 }
1547
1548
1549 /***********************************************************************
1550  *           COMBO_Font
1551  */
1552 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1553 {
1554   /*
1555    * Set the font
1556    */
1557   lphc->hFont = hFont;
1558
1559   /*
1560    * Propagate to owned windows.
1561    */
1562   if( lphc->wState & CBF_EDIT )
1563       SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1564   SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1565
1566   /*
1567    * Redo the layout of the control.
1568    */
1569   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1570   {
1571     CBCalcPlacement(lphc->self->hwndSelf,
1572                     lphc, 
1573                     &lphc->textRect, 
1574                     &lphc->buttonRect, 
1575                     &lphc->droppedRect);
1576     
1577     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1578   }
1579   else
1580   {
1581     CBForceDummyResize(lphc);
1582   }
1583 }
1584
1585
1586 /***********************************************************************
1587  *           COMBO_SetItemHeight
1588  */
1589 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1590 {
1591    LRESULT      lRet = CB_ERR;
1592
1593    if( index == -1 ) /* set text field height */
1594    {
1595        if( height < 32768 )
1596        {
1597            lphc->editHeight = height;
1598
1599          /*
1600           * Redo the layout of the control.
1601           */
1602          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1603          {
1604            CBCalcPlacement(lphc->self->hwndSelf,
1605                            lphc, 
1606                            &lphc->textRect, 
1607                            &lphc->buttonRect, 
1608                            &lphc->droppedRect);
1609            
1610            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1611          }
1612          else
1613          {
1614            CBForceDummyResize(lphc);
1615          }
1616                  
1617            lRet = height;
1618        }
1619    } 
1620    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1621         lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT, 
1622                               (WPARAM)index, (LPARAM)height );
1623    return lRet;
1624 }
1625
1626 /***********************************************************************
1627  *           COMBO_SelectString
1628  */
1629 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1630 {
1631    INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING, 
1632                                  (WPARAM)start, (LPARAM)pText );
1633    if( index >= 0 )
1634    {
1635      if( lphc->wState & CBF_EDIT )
1636        CBUpdateEdit( lphc, index );
1637      else
1638      {
1639        InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1640      }
1641    }
1642    return (LRESULT)index;
1643 }
1644
1645 /***********************************************************************
1646  *           COMBO_LButtonDown
1647  */
1648 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1649 {
1650    POINT     pt;
1651    BOOL      bButton;
1652    HWND      hWnd = lphc->self->hwndSelf;
1653
1654    pt.x = LOWORD(lParam);
1655    pt.y = HIWORD(lParam);
1656    bButton = PtInRect(&lphc->buttonRect, pt);
1657
1658    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1659        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1660    {
1661        lphc->wState |= CBF_BUTTONDOWN;
1662        if( lphc->wState & CBF_DROPPED )
1663        {
1664            /* got a click to cancel selection */
1665
1666            lphc->wState &= ~CBF_BUTTONDOWN;
1667            CBRollUp( lphc, TRUE, FALSE );
1668            if( !IsWindow( hWnd ) ) return;
1669
1670            if( lphc->wState & CBF_CAPTURE )
1671            {
1672                lphc->wState &= ~CBF_CAPTURE;
1673                ReleaseCapture();
1674            }
1675        }
1676        else
1677        {
1678            /* drop down the listbox and start tracking */
1679
1680            lphc->wState |= CBF_CAPTURE;
1681            CBDropDown( lphc );
1682            SetCapture( hWnd );
1683        }
1684        if( bButton ) CBRepaintButton( lphc );
1685    }
1686 }
1687
1688 /***********************************************************************
1689  *           COMBO_LButtonUp
1690  *
1691  * Release capture and stop tracking if needed.
1692  */
1693 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1694 {
1695    if( lphc->wState & CBF_CAPTURE )
1696    {
1697        lphc->wState &= ~CBF_CAPTURE;
1698        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1699        {
1700            INT index = CBUpdateLBox( lphc );
1701            CBUpdateEdit( lphc, index );
1702        }
1703        ReleaseCapture();
1704    }
1705
1706    if( lphc->wState & CBF_BUTTONDOWN )
1707    {
1708        lphc->wState &= ~CBF_BUTTONDOWN;
1709        CBRepaintButton( lphc );
1710    }
1711 }
1712
1713 /***********************************************************************
1714  *           COMBO_MouseMove
1715  *
1716  * Two things to do - track combo button and release capture when
1717  * pointer goes into the listbox.
1718  */
1719 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1720 {
1721    POINT  pt;
1722    RECT   lbRect;
1723    
1724    pt.x = LOWORD(lParam);
1725    pt.y = HIWORD(lParam);
1726    
1727    if( lphc->wState & CBF_BUTTONDOWN )
1728    {
1729      BOOL bButton;
1730
1731      bButton = PtInRect(&lphc->buttonRect, pt);
1732
1733      if( !bButton )
1734      {
1735        lphc->wState &= ~CBF_BUTTONDOWN;
1736        CBRepaintButton( lphc );
1737      }
1738    }
1739
1740    GetClientRect( lphc->hWndLBox, &lbRect );
1741    MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1742    if( PtInRect(&lbRect, pt) )
1743    {
1744        lphc->wState &= ~CBF_CAPTURE;
1745        ReleaseCapture();
1746        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1747
1748        /* hand over pointer tracking */
1749        SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1750    }
1751 }
1752
1753
1754 /***********************************************************************
1755  *           ComboWndProc_locked
1756  *
1757  * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1758  */
1759 static inline LRESULT WINAPI ComboWndProc_locked( WND* pWnd, UINT message,
1760                              WPARAM wParam, LPARAM lParam )
1761 {
1762     if( pWnd ) {
1763       LPHEADCOMBO       lphc = CB_GETPTR(pWnd);
1764       HWND              hwnd = pWnd->hwndSelf;
1765
1766       TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1767                    pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1768
1769       if( lphc || message == WM_NCCREATE )
1770       switch(message) 
1771       { 
1772
1773         /* System messages */
1774
1775         case WM_NCCREATE: 
1776                 return COMBO_NCCreate(pWnd, lParam);
1777         case WM_NCDESTROY: 
1778                 COMBO_NCDestroy(lphc);
1779                 break;/* -> DefWindowProc */
1780
1781         case WM_CREATE: 
1782                 return COMBO_Create(lphc, pWnd, lParam);
1783
1784         case WM_PRINTCLIENT:
1785                 if (lParam & PRF_ERASEBKGND)
1786                   COMBO_EraseBackground(hwnd, lphc, wParam);
1787
1788                 /* Fallthrough */
1789         case WM_PAINT:
1790                 /* wParam may contain a valid HDC! */
1791                 return  COMBO_Paint(lphc, wParam);
1792         case WM_ERASEBKGND:
1793                 return  COMBO_EraseBackground(hwnd, lphc, wParam);
1794         case WM_GETDLGCODE: 
1795                 return  (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1796         case WM_WINDOWPOSCHANGING:
1797                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1798         case WM_SIZE:
1799                 if( lphc->hWndLBox && 
1800                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1801                 return  TRUE;
1802         case WM_SETFONT:
1803                 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1804                 return  TRUE;
1805         case WM_GETFONT:
1806                 return  (LRESULT)lphc->hFont;
1807         case WM_SETFOCUS:
1808                 if( lphc->wState & CBF_EDIT )
1809                     SetFocus( lphc->hWndEdit );
1810                 else
1811                     COMBO_SetFocus( lphc );
1812                 return  TRUE;
1813         case WM_KILLFOCUS:
1814 #define hwndFocus ((HWND16)wParam)
1815                 if( !hwndFocus ||
1816                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1817                     COMBO_KillFocus( lphc );
1818 #undef hwndFocus
1819                 return  TRUE;
1820         case WM_COMMAND:
1821                 return  COMBO_Command( lphc, wParam, (HWND)lParam );
1822         case WM_GETTEXT:
1823                 return  COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1824         case WM_SETTEXT:
1825         case WM_GETTEXTLENGTH:
1826         case WM_CLEAR:
1827         case WM_CUT:
1828         case WM_PASTE:
1829         case WM_COPY:
1830                 if( lphc->wState & CBF_EDIT )
1831                 {
1832                     lphc->wState |= CBF_NOEDITNOTIFY;
1833
1834                     return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1835                 }
1836                 return  CB_ERR;
1837         case WM_DRAWITEM:
1838         case WM_DELETEITEM:
1839         case WM_COMPAREITEM:
1840         case WM_MEASUREITEM:
1841                 return  COMBO_ItemOp( lphc, message, wParam, lParam );
1842         case WM_ENABLE:
1843                 if( lphc->wState & CBF_EDIT )
1844                     EnableWindow( lphc->hWndEdit, (BOOL)wParam ); 
1845                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1846                 return  TRUE;
1847         case WM_SETREDRAW:
1848                 if( wParam )
1849                     lphc->wState &= ~CBF_NOREDRAW;
1850                 else
1851                     lphc->wState |= CBF_NOREDRAW;
1852
1853                 if( lphc->wState & CBF_EDIT )
1854                     SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1855                 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1856                 return  0;
1857         case WM_SYSKEYDOWN:
1858                 if( KEYDATA_ALT & HIWORD(lParam) )
1859                     if( wParam == VK_UP || wParam == VK_DOWN )
1860                         COMBO_FlipListbox( lphc, TRUE );
1861                 break;/* -> DefWindowProc */
1862
1863         case WM_CHAR:
1864         case WM_KEYDOWN:
1865                 if( lphc->wState & CBF_EDIT )
1866                     return  SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1867                 else
1868                     return  SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1869         case WM_LBUTTONDOWN: 
1870                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1871                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1872                 return  TRUE;
1873         case WM_LBUTTONUP:
1874                 COMBO_LButtonUp( lphc, lParam );
1875                 return  TRUE;
1876         case WM_MOUSEMOVE: 
1877                 if( lphc->wState & CBF_CAPTURE ) 
1878                     COMBO_MouseMove( lphc, wParam, lParam );
1879                 return  TRUE;
1880         /* Combo messages */
1881
1882         case CB_ADDSTRING16:
1883                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1884         case CB_ADDSTRING:
1885                 return  SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1886         case CB_INSERTSTRING16:
1887                 wParam = (INT)(INT16)wParam;
1888                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1889         case CB_INSERTSTRING:
1890                 return  SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1891         case CB_DELETESTRING16:
1892         case CB_DELETESTRING:
1893                 return  SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1894         case CB_SELECTSTRING16:
1895                 wParam = (INT)(INT16)wParam;
1896                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1897         case CB_SELECTSTRING:
1898                 return  COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1899         case CB_FINDSTRING16:
1900                 wParam = (INT)(INT16)wParam;
1901                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1902         case CB_FINDSTRING:
1903                 return  SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1904         case CB_FINDSTRINGEXACT16:
1905                 wParam = (INT)(INT16)wParam;
1906                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1907         case CB_FINDSTRINGEXACT:
1908                 return  SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT, 
1909                                                        wParam, lParam );
1910         case CB_SETITEMHEIGHT16:
1911                 wParam = (INT)(INT16)wParam;    /* signed integer */
1912         case CB_SETITEMHEIGHT:
1913                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1914         case CB_GETITEMHEIGHT16:
1915                 wParam = (INT)(INT16)wParam;
1916         case CB_GETITEMHEIGHT:
1917                 if( (INT)wParam >= 0 )  /* listbox item */
1918                     return  SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1919                 return  CBGetTextAreaHeight(hwnd, lphc);
1920         case CB_RESETCONTENT16: 
1921         case CB_RESETCONTENT:
1922                 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1923                 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1924                 return  TRUE;
1925         case CB_INITSTORAGE:
1926                 return  SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1927         case CB_GETHORIZONTALEXTENT:
1928                 return  SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1929         case CB_SETHORIZONTALEXTENT:
1930                 return  SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1931         case CB_GETTOPINDEX:
1932                 return  SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1933         case CB_GETLOCALE:
1934                 return  SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1935         case CB_SETLOCALE:
1936                 return  SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1937         case CB_GETDROPPEDWIDTH:
1938                 if( lphc->droppedWidth )
1939                     return  lphc->droppedWidth;
1940                 return  lphc->droppedRect.right - lphc->droppedRect.left;
1941         case CB_SETDROPPEDWIDTH:
1942                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1943                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1944                 return  CB_ERR;
1945         case CB_GETDROPPEDCONTROLRECT16:
1946                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1947                 if( lParam ) 
1948                 {
1949                     RECT        r;
1950                     CBGetDroppedControlRect( lphc, &r );
1951                     CONV_RECT32TO16( &r, (LPRECT16)lParam );
1952                 }
1953                 return  CB_OKAY;
1954         case CB_GETDROPPEDCONTROLRECT:
1955                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1956                 return  CB_OKAY;
1957         case CB_GETDROPPEDSTATE16:
1958         case CB_GETDROPPEDSTATE:
1959                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1960         case CB_DIR16: 
1961                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1962                 /* fall through */
1963         case CB_DIR:
1964                 return  COMBO_Directory( lphc, (UINT)wParam, 
1965                                        (LPSTR)lParam, (message == CB_DIR));
1966         case CB_SHOWDROPDOWN16:
1967         case CB_SHOWDROPDOWN:
1968                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1969                 {
1970                     if( wParam )
1971                     {
1972                         if( !(lphc->wState & CBF_DROPPED) )
1973                             CBDropDown( lphc );
1974                     }
1975                     else 
1976                         if( lphc->wState & CBF_DROPPED ) 
1977                             CBRollUp( lphc, FALSE, TRUE );
1978                 }
1979                 return  TRUE;
1980         case CB_GETCOUNT16: 
1981         case CB_GETCOUNT:
1982                 return  SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1983         case CB_GETCURSEL16: 
1984         case CB_GETCURSEL:
1985                 return  SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1986         case CB_SETCURSEL16:
1987                 wParam = (INT)(INT16)wParam;
1988         case CB_SETCURSEL:
1989                 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1990                 if( lphc->wState & CBF_SELCHANGE )
1991                 {
1992                     /* no LBN_SELCHANGE in this case, update manually */
1993                     InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1994                     lphc->wState &= ~CBF_SELCHANGE;
1995                 }
1996                 return  lParam;
1997         case CB_GETLBTEXT16: 
1998                 wParam = (INT)(INT16)wParam;
1999                 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2000         case CB_GETLBTEXT:
2001                 return  SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2002         case CB_GETLBTEXTLEN16: 
2003                 wParam = (INT)(INT16)wParam;
2004         case CB_GETLBTEXTLEN:
2005                 return  SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2006         case CB_GETITEMDATA16:
2007                 wParam = (INT)(INT16)wParam;
2008         case CB_GETITEMDATA:
2009                 return  SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2010         case CB_SETITEMDATA16:
2011                 wParam = (INT)(INT16)wParam;
2012         case CB_SETITEMDATA:
2013                 return  SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2014         case CB_GETEDITSEL16: 
2015                 wParam = lParam = 0;   /* just in case */
2016         case CB_GETEDITSEL:
2017                 if( lphc->wState & CBF_EDIT )
2018                 {
2019                     INT a, b;
2020
2021                     return  SendMessageA( lphc->hWndEdit, EM_GETSEL,
2022                                            (wParam) ? wParam : (WPARAM)&a,
2023                                            (lParam) ? lParam : (LPARAM)&b );
2024                 }
2025                 return  CB_ERR;
2026         case CB_SETEDITSEL16: 
2027         case CB_SETEDITSEL:
2028                 if( lphc->wState & CBF_EDIT ) 
2029                     return  SendMessageA( lphc->hWndEdit, EM_SETSEL,
2030                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2031                 return  CB_ERR;
2032         case CB_SETEXTENDEDUI16:
2033         case CB_SETEXTENDEDUI:
2034                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2035                     return  CB_ERR;
2036                 if( wParam )
2037                     lphc->wState |= CBF_EUI;
2038                 else lphc->wState &= ~CBF_EUI;
2039                 return  CB_OKAY;
2040         case CB_GETEXTENDEDUI16:
2041         case CB_GETEXTENDEDUI:
2042                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2043         case (WM_USER + 0x1B):
2044                 WARN("[%04x]: undocumented msg!\n", hwnd );
2045     }
2046     return DefWindowProcA(hwnd, message, wParam, lParam);
2047   }
2048   return CB_ERR;
2049 }
2050
2051 /***********************************************************************
2052  *           ComboWndProc
2053  *
2054  * This is just a wrapper for the real ComboWndProc which locks/unlocks
2055  * window structs.
2056  */
2057 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
2058                              WPARAM wParam, LPARAM lParam )
2059 {
2060     WND*        pWnd = WIN_FindWndPtr(hwnd);
2061     LRESULT retvalue = ComboWndProc_locked(pWnd,message,wParam,lParam);
2062
2063     
2064     WIN_ReleaseWndPtr(pWnd);
2065     return retvalue;
2066 }
2067