Janitorial. Get rid of W->A call.
[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 "message.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("[%p]: 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                 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= (%ld,%ld-%ld,%ld)\n",
424         lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
425
426   TRACE("\tbutton\t= (%ld,%ld-%ld,%ld)\n",
427         lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
428
429   TRACE("\tlbox\t= (%ld,%ld-%ld,%ld)\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=%ld, oldtop=%ld\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=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=%p\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("[%p]: 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 = (nItems+1)*nIHeight;
1214             else
1215                 nDroppedHeight = 6*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("[%p]: 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("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1381
1382                 COMBO_SetFocus( lphc );
1383                 break;
1384
1385            case (EN_KILLFOCUS >> 8):
1386
1387                TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1388
1389                 /* NOTE: it seems that Windows' edit control sends an
1390                  * undocumented message WM_USER + 0x1B instead of this
1391                  * notification (only when it happens to be a part of
1392                  * the combo). ?? - AK.
1393                  */
1394
1395                 COMBO_KillFocus( lphc );
1396                 break;
1397
1398
1399            case (EN_CHANGE >> 8):
1400                /*
1401                 * In some circumstances (when the selection of the combobox
1402                 * is changed for example) we don't wans the EN_CHANGE notification
1403                 * to be forwarded to the parent of the combobox. This code
1404                 * checks a flag that is set in these occasions and ignores the
1405                 * notification.
1406                 */
1407                 if (lphc->wState & CBF_NOLBSELECT)
1408                 {
1409                   lphc->wState &= ~CBF_NOLBSELECT;
1410                 }
1411                 else
1412                 {
1413                   CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1414                 }
1415
1416                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1417                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1418                 break;
1419
1420            case (EN_UPDATE >> 8):
1421                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1422                   CB_NOTIFY( lphc, CBN_EDITUPDATE );
1423                 break;
1424
1425            case (EN_ERRSPACE >> 8):
1426                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1427        }
1428    }
1429    else if( lphc->hWndLBox == hWnd )
1430    {
1431        switch( HIWORD(wParam) )
1432        {
1433            case LBN_ERRSPACE:
1434                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1435                 break;
1436
1437            case LBN_DBLCLK:
1438                 CB_NOTIFY( lphc, CBN_DBLCLK );
1439                 break;
1440
1441            case LBN_SELCHANGE:
1442            case LBN_SELCANCEL:
1443
1444                TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1445
1446                 if( HIWORD(wParam) == LBN_SELCHANGE)
1447                 {
1448                    if( lphc->wState & CBF_EDIT )
1449                    {
1450                        INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1451                        lphc->wState |= CBF_NOLBSELECT;
1452                        CBUpdateEdit( lphc, index );
1453                        /* select text in edit, as Windows does */
1454                        SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1455                    }
1456                    else
1457                        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1458                 }
1459
1460                 /* do not roll up if selection is being tracked
1461                  * by arrowkeys in the dropdown listbox */
1462                 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1463                 {
1464                    CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1465                 }
1466                 else lphc->wState &= ~CBF_NOROLLUP;
1467
1468                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1469
1470                 /* fall through */
1471
1472            case LBN_SETFOCUS:
1473            case LBN_KILLFOCUS:
1474                 /* nothing to do here since ComboLBox always resets the focus to its
1475                  * combo/edit counterpart */
1476                  break;
1477        }
1478    }
1479    return 0;
1480 }
1481
1482 /***********************************************************************
1483  *           COMBO_ItemOp
1484  *
1485  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1486  */
1487 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1488 {
1489    HWND hWnd = lphc->self;
1490    UINT id = GetWindowLongA( hWnd, GWL_ID );
1491
1492    TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1493
1494    switch( msg )
1495    {
1496    case WM_DELETEITEM:
1497        {
1498            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1499            lpIS->CtlType  = ODT_COMBOBOX;
1500            lpIS->CtlID    = id;
1501            lpIS->hwndItem = hWnd;
1502            break;
1503        }
1504    case WM_DRAWITEM:
1505        {
1506            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1507            lpIS->CtlType  = ODT_COMBOBOX;
1508            lpIS->CtlID    = id;
1509            lpIS->hwndItem = hWnd;
1510            break;
1511        }
1512    case WM_COMPAREITEM:
1513        {
1514            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1515            lpIS->CtlType  = ODT_COMBOBOX;
1516            lpIS->CtlID    = id;
1517            lpIS->hwndItem = hWnd;
1518            break;
1519        }
1520    case WM_MEASUREITEM:
1521        {
1522            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1523            lpIS->CtlType  = ODT_COMBOBOX;
1524            lpIS->CtlID    = id;
1525            break;
1526        }
1527    }
1528    return SendMessageW(lphc->owner, msg, id, lParam);
1529 }
1530
1531
1532 /***********************************************************************
1533  *           COMBO_GetTextW
1534  */
1535 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1536 {
1537     INT length;
1538
1539     if( lphc->wState & CBF_EDIT )
1540         return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1541
1542     /* get it from the listbox */
1543
1544     if (!count || !buf) return 0;
1545     if( lphc->hWndLBox )
1546     {
1547         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1548         if (idx == LB_ERR) goto error;
1549         length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1550         if (length == LB_ERR) goto error;
1551
1552         /* 'length' is without the terminating character */
1553         if (length >= count)
1554         {
1555             LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1556             if (!lpBuffer) goto error;
1557             length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1558
1559             /* truncate if buffer is too short */
1560             if (length != LB_ERR)
1561             {
1562                 lstrcpynW( buf, lpBuffer, count );
1563                 length = count;
1564             }
1565             HeapFree( GetProcessHeap(), 0, lpBuffer );
1566         }
1567         else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1568
1569         if (length == LB_ERR) return 0;
1570         return length;
1571     }
1572
1573  error:  /* error - truncate string, return zero */
1574     buf[0] = 0;
1575     return 0;
1576 }
1577
1578
1579 /***********************************************************************
1580  *           COMBO_GetTextA
1581  *
1582  * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1583  *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1584  */
1585 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1586 {
1587     INT length;
1588
1589     if( lphc->wState & CBF_EDIT )
1590         return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1591
1592     /* get it from the listbox */
1593
1594     if (!count || !buf) return 0;
1595     if( lphc->hWndLBox )
1596     {
1597         INT idx = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1598         if (idx == LB_ERR) goto error;
1599         length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1600         if (length == LB_ERR) goto error;
1601
1602         /* 'length' is without the terminating character */
1603         if (length >= count)
1604         {
1605             LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1606             if (!lpBuffer) goto error;
1607             length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1608
1609             /* truncate if buffer is too short */
1610             if (length != LB_ERR)
1611             {
1612                 lstrcpynA( buf, lpBuffer, count );
1613                 length = count;
1614             }
1615             HeapFree( GetProcessHeap(), 0, lpBuffer );
1616         }
1617         else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1618
1619         if (length == LB_ERR) return 0;
1620         return length;
1621     }
1622
1623  error:  /* error - truncate string, return zero */
1624     buf[0] = 0;
1625     return 0;
1626 }
1627
1628
1629 /***********************************************************************
1630  *           CBResetPos
1631  *
1632  * This function sets window positions according to the updated
1633  * component placement struct.
1634  */
1635 static void CBResetPos(
1636   LPHEADCOMBO lphc,
1637   LPRECT      rectEdit,
1638   LPRECT      rectLB,
1639   BOOL        bRedraw)
1640 {
1641    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1642
1643    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1644     * sizing messages */
1645
1646    if( lphc->wState & CBF_EDIT )
1647      SetWindowPos( lphc->hWndEdit, 0,
1648                    rectEdit->left, rectEdit->top,
1649                    rectEdit->right - rectEdit->left,
1650                    rectEdit->bottom - rectEdit->top,
1651                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1652
1653    SetWindowPos( lphc->hWndLBox, 0,
1654                  rectLB->left, rectLB->top,
1655                  rectLB->right - rectLB->left,
1656                  rectLB->bottom - rectLB->top,
1657                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1658
1659    if( bDrop )
1660    {
1661        if( lphc->wState & CBF_DROPPED )
1662        {
1663            lphc->wState &= ~CBF_DROPPED;
1664            ShowWindow( lphc->hWndLBox, SW_HIDE );
1665        }
1666
1667        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1668            RedrawWindow( lphc->self, NULL, 0,
1669                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1670    }
1671 }
1672
1673
1674 /***********************************************************************
1675  *           COMBO_Size
1676  */
1677 static void COMBO_Size( LPHEADCOMBO lphc )
1678   {
1679   CBCalcPlacement(lphc->self,
1680                   lphc,
1681                   &lphc->textRect,
1682                   &lphc->buttonRect,
1683                   &lphc->droppedRect);
1684
1685   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1686 }
1687
1688
1689 /***********************************************************************
1690  *           COMBO_Font
1691  */
1692 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1693 {
1694   /*
1695    * Set the font
1696    */
1697   lphc->hFont = hFont;
1698
1699   /*
1700    * Propagate to owned windows.
1701    */
1702   if( lphc->wState & CBF_EDIT )
1703       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1704   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1705
1706   /*
1707    * Redo the layout of the control.
1708    */
1709   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1710   {
1711     CBCalcPlacement(lphc->self,
1712                     lphc,
1713                     &lphc->textRect,
1714                     &lphc->buttonRect,
1715                     &lphc->droppedRect);
1716
1717     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1718   }
1719   else
1720   {
1721     CBForceDummyResize(lphc);
1722   }
1723 }
1724
1725
1726 /***********************************************************************
1727  *           COMBO_SetItemHeight
1728  */
1729 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1730 {
1731    LRESULT      lRet = CB_ERR;
1732
1733    if( index == -1 ) /* set text field height */
1734    {
1735        if( height < 32768 )
1736        {
1737            lphc->editHeight = height;
1738
1739          /*
1740           * Redo the layout of the control.
1741           */
1742          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1743          {
1744            CBCalcPlacement(lphc->self,
1745                            lphc,
1746                            &lphc->textRect,
1747                            &lphc->buttonRect,
1748                            &lphc->droppedRect);
1749
1750            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1751          }
1752          else
1753          {
1754            CBForceDummyResize(lphc);
1755          }
1756
1757            lRet = height;
1758        }
1759    }
1760    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1761         lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1762                               (WPARAM)index, (LPARAM)height );
1763    return lRet;
1764 }
1765
1766 /***********************************************************************
1767  *           COMBO_SelectString
1768  */
1769 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1770 {
1771    INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1772                          SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1773    if( index >= 0 )
1774    {
1775      if( lphc->wState & CBF_EDIT )
1776        CBUpdateEdit( lphc, index );
1777      else
1778      {
1779        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1780      }
1781    }
1782    return (LRESULT)index;
1783 }
1784
1785 /***********************************************************************
1786  *           COMBO_LButtonDown
1787  */
1788 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1789 {
1790    POINT     pt;
1791    BOOL      bButton;
1792    HWND      hWnd = lphc->self;
1793
1794    pt.x = LOWORD(lParam);
1795    pt.y = HIWORD(lParam);
1796    bButton = PtInRect(&lphc->buttonRect, pt);
1797
1798    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1799        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1800    {
1801        lphc->wState |= CBF_BUTTONDOWN;
1802        if( lphc->wState & CBF_DROPPED )
1803        {
1804            /* got a click to cancel selection */
1805
1806            lphc->wState &= ~CBF_BUTTONDOWN;
1807            CBRollUp( lphc, TRUE, FALSE );
1808            if( !IsWindow( hWnd ) ) return;
1809
1810            if( lphc->wState & CBF_CAPTURE )
1811            {
1812                lphc->wState &= ~CBF_CAPTURE;
1813                ReleaseCapture();
1814            }
1815        }
1816        else
1817        {
1818            /* drop down the listbox and start tracking */
1819
1820            lphc->wState |= CBF_CAPTURE;
1821            SetCapture( hWnd );
1822            CBDropDown( lphc );
1823        }
1824        if( bButton ) CBRepaintButton( lphc );
1825    }
1826 }
1827
1828 /***********************************************************************
1829  *           COMBO_LButtonUp
1830  *
1831  * Release capture and stop tracking if needed.
1832  */
1833 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1834 {
1835    if( lphc->wState & CBF_CAPTURE )
1836    {
1837        lphc->wState &= ~CBF_CAPTURE;
1838        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1839        {
1840            INT index = CBUpdateLBox( lphc, TRUE );
1841            /* Update edit only if item is in the list */
1842            if(index >= 0)
1843            {
1844                lphc->wState |= CBF_NOLBSELECT;
1845                CBUpdateEdit( lphc, index );
1846                lphc->wState &= ~CBF_NOLBSELECT;
1847            }
1848        }
1849        ReleaseCapture();
1850        SetCapture(lphc->hWndLBox);
1851    }
1852
1853    if( lphc->wState & CBF_BUTTONDOWN )
1854    {
1855        lphc->wState &= ~CBF_BUTTONDOWN;
1856        CBRepaintButton( lphc );
1857    }
1858 }
1859
1860 /***********************************************************************
1861  *           COMBO_MouseMove
1862  *
1863  * Two things to do - track combo button and release capture when
1864  * pointer goes into the listbox.
1865  */
1866 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1867 {
1868    POINT  pt;
1869    RECT   lbRect;
1870
1871    pt.x = LOWORD(lParam);
1872    pt.y = HIWORD(lParam);
1873
1874    if( lphc->wState & CBF_BUTTONDOWN )
1875    {
1876      BOOL bButton;
1877
1878      bButton = PtInRect(&lphc->buttonRect, pt);
1879
1880      if( !bButton )
1881      {
1882        lphc->wState &= ~CBF_BUTTONDOWN;
1883        CBRepaintButton( lphc );
1884      }
1885    }
1886
1887    GetClientRect( lphc->hWndLBox, &lbRect );
1888    MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1889    if( PtInRect(&lbRect, pt) )
1890    {
1891        lphc->wState &= ~CBF_CAPTURE;
1892        ReleaseCapture();
1893        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1894
1895        /* hand over pointer tracking */
1896        SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1897    }
1898 }
1899
1900
1901 /***********************************************************************
1902  *           ComboWndProc_common
1903  *
1904  * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1905  */
1906 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1907                                     WPARAM wParam, LPARAM lParam, BOOL unicode )
1908 {
1909       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
1910
1911       TRACE("[%p]: msg %s wp %08x lp %08lx\n",
1912             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1913
1914       if( lphc || message == WM_NCCREATE )
1915       switch(message)
1916       {
1917
1918         /* System messages */
1919
1920         case WM_NCCREATE:
1921         {
1922                 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1923                                        ((LPCREATESTRUCTA)lParam)->style;
1924                 return COMBO_NCCreate(hwnd, style);
1925         }
1926         case WM_NCDESTROY:
1927                 COMBO_NCDestroy(lphc);
1928                 break;/* -> DefWindowProc */
1929
1930         case WM_CREATE:
1931         {
1932                 HWND hwndParent;
1933                 LONG style;
1934                 if(unicode)
1935                 {
1936                     hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1937                     style = ((LPCREATESTRUCTW)lParam)->style;
1938                 }
1939                 else
1940                 {
1941                     hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1942                     style = ((LPCREATESTRUCTA)lParam)->style;
1943                 }
1944                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1945         }
1946
1947         case WM_PRINTCLIENT:
1948                 if (lParam & PRF_ERASEBKGND)
1949                   COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1950
1951                 /* Fallthrough */
1952         case WM_PAINT:
1953                 /* wParam may contain a valid HDC! */
1954                 return  COMBO_Paint(lphc, (HDC)wParam);
1955         case WM_ERASEBKGND:
1956                 return  COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1957         case WM_GETDLGCODE:
1958         {
1959                 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1960                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1961                 {
1962                    int vk = (int)((LPMSG)lParam)->wParam;
1963
1964                    if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1965                        result |= DLGC_WANTMESSAGE;
1966                 }
1967                 return  result;
1968         }
1969         case WM_WINDOWPOSCHANGING:
1970                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1971     case WM_WINDOWPOSCHANGED:
1972         /* SetWindowPos can be called on a Combobox to resize its Listbox.
1973          * In that case, the Combobox itself will not be resized, so we won't
1974          * get a WM_SIZE. Since we still want to update the Listbox, we have to
1975          * do it here.
1976          */
1977         /* fall through */
1978         case WM_SIZE:
1979                 if( lphc->hWndLBox &&
1980                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1981                 return  TRUE;
1982         case WM_SETFONT:
1983                 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1984                 return  TRUE;
1985         case WM_GETFONT:
1986                 return  (LRESULT)lphc->hFont;
1987         case WM_SETFOCUS:
1988                 if( lphc->wState & CBF_EDIT )
1989                     SetFocus( lphc->hWndEdit );
1990                 else
1991                     COMBO_SetFocus( lphc );
1992                 return  TRUE;
1993         case WM_KILLFOCUS:
1994             {
1995                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1996                 if( !hwndFocus ||
1997                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1998                     COMBO_KillFocus( lphc );
1999                 return  TRUE;
2000             }
2001         case WM_COMMAND:
2002                 return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
2003         case WM_GETTEXT:
2004             return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
2005                            : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
2006         case WM_SETTEXT:
2007         case WM_GETTEXTLENGTH:
2008         case WM_CLEAR:
2009                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
2010                 {
2011                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2012                     if (j == -1) return 0;
2013                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
2014                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
2015                 }
2016                 else if( lphc->wState & CBF_EDIT )
2017                 {
2018                     LRESULT ret;
2019                     lphc->wState |= CBF_NOEDITNOTIFY;
2020                     ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
2021                                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
2022                     lphc->wState &= ~CBF_NOEDITNOTIFY;
2023                     return ret;
2024                 }
2025                 else return CB_ERR;
2026         case WM_CUT:
2027         case WM_PASTE:
2028         case WM_COPY:
2029                 if( lphc->wState & CBF_EDIT )
2030                 {
2031                     return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
2032                                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
2033                 }
2034                 else return  CB_ERR;
2035
2036         case WM_DRAWITEM:
2037         case WM_DELETEITEM:
2038         case WM_COMPAREITEM:
2039         case WM_MEASUREITEM:
2040                 return COMBO_ItemOp(lphc, message, lParam);
2041         case WM_ENABLE:
2042                 if( lphc->wState & CBF_EDIT )
2043                     EnableWindow( lphc->hWndEdit, (BOOL)wParam );
2044                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
2045
2046                 /* Force the control to repaint when the enabled state changes. */
2047                 InvalidateRect(lphc->self, NULL, TRUE);
2048                 return  TRUE;
2049         case WM_SETREDRAW:
2050                 if( wParam )
2051                     lphc->wState &= ~CBF_NOREDRAW;
2052                 else
2053                     lphc->wState |= CBF_NOREDRAW;
2054
2055                 if( lphc->wState & CBF_EDIT )
2056                     SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2057                 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2058                 return  0;
2059         case WM_SYSKEYDOWN:
2060                 if( KEYDATA_ALT & HIWORD(lParam) )
2061                     if( wParam == VK_UP || wParam == VK_DOWN )
2062                         COMBO_FlipListbox( lphc, FALSE, FALSE );
2063                 return  0;
2064
2065         case WM_CHAR:
2066         case WM_KEYDOWN:
2067         {
2068                 HWND hwndTarget;
2069
2070                 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2071                      (lphc->wState & CBF_DROPPED))
2072                 {
2073                    CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2074                    return TRUE;
2075                 }
2076
2077                 if( lphc->wState & CBF_EDIT )
2078                     hwndTarget = lphc->hWndEdit;
2079                 else
2080                     hwndTarget = lphc->hWndLBox;
2081
2082                 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2083                                  SendMessageA(hwndTarget, message, wParam, lParam);
2084         }
2085         case WM_LBUTTONDOWN:
2086                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2087                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2088                 return  TRUE;
2089         case WM_LBUTTONUP:
2090                 COMBO_LButtonUp( lphc );
2091                 return  TRUE;
2092         case WM_MOUSEMOVE:
2093                 if( lphc->wState & CBF_CAPTURE )
2094                     COMBO_MouseMove( lphc, wParam, lParam );
2095                 return  TRUE;
2096
2097         case WM_MOUSEWHEEL:
2098                 if (wParam & (MK_SHIFT | MK_CONTROL))
2099                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2100                                      DefWindowProcA(hwnd, message, wParam, lParam);
2101                 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2102                 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2103                 return TRUE;
2104
2105         /* Combo messages */
2106
2107         case CB_ADDSTRING16:
2108                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2109                 /* fall through */
2110         case CB_ADDSTRING:
2111                 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2112                                  SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2113         case CB_INSERTSTRING16:
2114                 wParam = (INT)(INT16)wParam;
2115                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2116                 /* fall through */
2117         case CB_INSERTSTRING:
2118                 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2119                                  SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2120         case CB_DELETESTRING16:
2121         case CB_DELETESTRING:
2122                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2123                                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2124         case CB_SELECTSTRING16:
2125                 wParam = (INT)(INT16)wParam;
2126                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2127                 /* fall through */
2128         case CB_SELECTSTRING:
2129                 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2130         case CB_FINDSTRING16:
2131                 wParam = (INT)(INT16)wParam;
2132                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2133                 /* fall through */
2134         case CB_FINDSTRING:
2135                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2136                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2137         case CB_FINDSTRINGEXACT16:
2138                 wParam = (INT)(INT16)wParam;
2139                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2140                 /* fall through */
2141         case CB_FINDSTRINGEXACT:
2142                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2143                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2144         case CB_SETITEMHEIGHT16:
2145                 wParam = (INT)(INT16)wParam;    /* signed integer */
2146                 /* fall through */
2147         case CB_SETITEMHEIGHT:
2148                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2149         case CB_GETITEMHEIGHT16:
2150                 wParam = (INT)(INT16)wParam;
2151                 /* fall through */
2152         case CB_GETITEMHEIGHT:
2153                 if( (INT)wParam >= 0 )  /* listbox item */
2154                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2155                 return  CBGetTextAreaHeight(hwnd, lphc);
2156         case CB_RESETCONTENT16:
2157         case CB_RESETCONTENT:
2158                 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2159                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2160                 {
2161                     static const WCHAR empty_stringW[] = { 0 };
2162                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2163                 }
2164                 else
2165                     InvalidateRect(lphc->self, NULL, TRUE);
2166                 return  TRUE;
2167         case CB_INITSTORAGE:
2168                 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2169         case CB_GETHORIZONTALEXTENT:
2170                 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2171         case CB_SETHORIZONTALEXTENT:
2172                 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2173         case CB_GETTOPINDEX:
2174                 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2175         case CB_GETLOCALE:
2176                 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2177         case CB_SETLOCALE:
2178                 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2179         case CB_GETDROPPEDWIDTH:
2180                 if( lphc->droppedWidth )
2181                     return  lphc->droppedWidth;
2182                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2183         case CB_SETDROPPEDWIDTH:
2184                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2185                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2186                 return  CB_ERR;
2187         case CB_GETDROPPEDCONTROLRECT16:
2188                 lParam = (LPARAM)MapSL(lParam);
2189                 if( lParam )
2190                 {
2191                     RECT        r;
2192                     CBGetDroppedControlRect( lphc, &r );
2193                     CONV_RECT32TO16( &r, (LPRECT16)lParam );
2194                 }
2195                 return  CB_OKAY;
2196         case CB_GETDROPPEDCONTROLRECT:
2197                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2198                 return  CB_OKAY;
2199         case CB_GETDROPPEDSTATE16:
2200         case CB_GETDROPPEDSTATE:
2201                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2202         case CB_DIR16:
2203                 lParam = (LPARAM)MapSL(lParam);
2204                 message = LB_DIR16;
2205                 /* fall through */
2206         case CB_DIR:
2207                 if(message == CB_DIR) message = LB_DIR;
2208                 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2209                                  SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2210
2211         case CB_SHOWDROPDOWN16:
2212         case CB_SHOWDROPDOWN:
2213                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2214                 {
2215                     if( wParam )
2216                     {
2217                         if( !(lphc->wState & CBF_DROPPED) )
2218                             CBDropDown( lphc );
2219                     }
2220                     else
2221                         if( lphc->wState & CBF_DROPPED )
2222                             CBRollUp( lphc, FALSE, TRUE );
2223                 }
2224                 return  TRUE;
2225         case CB_GETCOUNT16:
2226         case CB_GETCOUNT:
2227                 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2228         case CB_GETCURSEL16:
2229         case CB_GETCURSEL:
2230                 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2231         case CB_SETCURSEL16:
2232                 wParam = (INT)(INT16)wParam;
2233                 /* fall through */
2234         case CB_SETCURSEL:
2235                 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2236                 if( lParam >= 0 )
2237                     SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2238
2239                 /* no LBN_SELCHANGE in this case, update manually */
2240                 if( lphc->wState & CBF_EDIT )
2241                     CBUpdateEdit( lphc, (INT)wParam );
2242                 else
2243                     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2244                 lphc->wState &= ~CBF_SELCHANGE;
2245                 return  lParam;
2246         case CB_GETLBTEXT16:
2247                 wParam = (INT)(INT16)wParam;
2248                 lParam = (LPARAM)MapSL(lParam);
2249                 /* fall through */
2250         case CB_GETLBTEXT:
2251                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2252                                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2253         case CB_GETLBTEXTLEN16:
2254                 wParam = (INT)(INT16)wParam;
2255                 /* fall through */
2256         case CB_GETLBTEXTLEN:
2257                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2258                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2259         case CB_GETITEMDATA16:
2260                 wParam = (INT)(INT16)wParam;
2261                 /* fall through */
2262         case CB_GETITEMDATA:
2263                 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2264         case CB_SETITEMDATA16:
2265                 wParam = (INT)(INT16)wParam;
2266                 /* fall through */
2267         case CB_SETITEMDATA:
2268                 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2269         case CB_GETEDITSEL16:
2270                 wParam = lParam = 0;   /* just in case */
2271                 /* fall through */
2272         case CB_GETEDITSEL:
2273                 /* Edit checks passed parameters itself */
2274                 if( lphc->wState & CBF_EDIT )
2275                     return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2276                 return  CB_ERR;
2277         case CB_SETEDITSEL16:
2278         case CB_SETEDITSEL:
2279                 if( lphc->wState & CBF_EDIT )
2280                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2281                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2282                 return  CB_ERR;
2283         case CB_SETEXTENDEDUI16:
2284         case CB_SETEXTENDEDUI:
2285                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2286                     return  CB_ERR;
2287                 if( wParam )
2288                     lphc->wState |= CBF_EUI;
2289                 else lphc->wState &= ~CBF_EUI;
2290                 return  CB_OKAY;
2291         case CB_GETEXTENDEDUI16:
2292         case CB_GETEXTENDEDUI:
2293                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2294
2295         default:
2296                 if (message >= WM_USER)
2297                     WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2298                         message - WM_USER, wParam, lParam );
2299                 break;
2300       }
2301       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2302                        DefWindowProcA(hwnd, message, wParam, lParam);
2303 }
2304
2305 /***********************************************************************
2306  *           ComboWndProcA
2307  *
2308  * This is just a wrapper for the real ComboWndProc which locks/unlocks
2309  * window structs.
2310  */
2311 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2312 {
2313     if (!IsWindow(hwnd)) return 0;
2314     return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2315 }
2316
2317 /***********************************************************************
2318  *           ComboWndProcW
2319  */
2320 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2321 {
2322     if (!IsWindow(hwnd)) return 0;
2323     return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2324 }
2325
2326 /*************************************************************************
2327  *           GetComboBoxInfo   (USER32.@)
2328  */
2329 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
2330                             PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
2331 {
2332     FIXME("\n");
2333     return FALSE;
2334
2335 }