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