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