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