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