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