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