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