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