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