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