Add a stub dll for the System Event Notification Service.
[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 "message.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
1832 /***********************************************************************
1833  *           ComboWndProc_common
1834  *
1835  * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/comboboxes/comboboxes.asp
1836  */
1837 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1838                                     WPARAM wParam, LPARAM lParam, BOOL unicode )
1839 {
1840       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongW( hwnd, 0 );
1841
1842       TRACE("[%p]: msg %s wp %08x lp %08lx\n",
1843             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1844
1845       if( lphc || message == WM_NCCREATE )
1846       switch(message)
1847       {
1848
1849         /* System messages */
1850
1851         case WM_NCCREATE:
1852         {
1853                 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1854                                        ((LPCREATESTRUCTA)lParam)->style;
1855                 return COMBO_NCCreate(hwnd, style);
1856         }
1857         case WM_NCDESTROY:
1858                 COMBO_NCDestroy(lphc);
1859                 break;/* -> DefWindowProc */
1860
1861         case WM_CREATE:
1862         {
1863                 HWND hwndParent;
1864                 LONG style;
1865                 if(unicode)
1866                 {
1867                     hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1868                     style = ((LPCREATESTRUCTW)lParam)->style;
1869                 }
1870                 else
1871                 {
1872                     hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1873                     style = ((LPCREATESTRUCTA)lParam)->style;
1874                 }
1875                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1876         }
1877
1878         case WM_PRINTCLIENT:
1879                 if (lParam & PRF_ERASEBKGND)
1880                   COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1881
1882                 /* Fallthrough */
1883         case WM_PAINT:
1884                 /* wParam may contain a valid HDC! */
1885                 return  COMBO_Paint(lphc, (HDC)wParam);
1886         case WM_ERASEBKGND:
1887                 return  COMBO_EraseBackground(hwnd, lphc, (HDC)wParam);
1888         case WM_GETDLGCODE:
1889         {
1890                 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1891                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1892                 {
1893                    int vk = (int)((LPMSG)lParam)->wParam;
1894
1895                    if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1896                        result |= DLGC_WANTMESSAGE;
1897                 }
1898                 return  result;
1899         }
1900         case WM_WINDOWPOSCHANGING:
1901                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1902     case WM_WINDOWPOSCHANGED:
1903         /* SetWindowPos can be called on a Combobox to resize its Listbox.
1904          * In that case, the Combobox itself will not be resized, so we won't
1905          * get a WM_SIZE. Since we still want to update the Listbox, we have to
1906          * do it here.
1907          */
1908         /* fall through */
1909         case WM_SIZE:
1910                 if( lphc->hWndLBox &&
1911                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1912                 return  TRUE;
1913         case WM_SETFONT:
1914                 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1915                 return  TRUE;
1916         case WM_GETFONT:
1917                 return  (LRESULT)lphc->hFont;
1918         case WM_SETFOCUS:
1919                 if( lphc->wState & CBF_EDIT )
1920                     SetFocus( lphc->hWndEdit );
1921                 else
1922                     COMBO_SetFocus( lphc );
1923                 return  TRUE;
1924         case WM_KILLFOCUS:
1925             {
1926                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1927                 if( !hwndFocus ||
1928                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1929                     COMBO_KillFocus( lphc );
1930                 return  TRUE;
1931             }
1932         case WM_COMMAND:
1933                 return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1934         case WM_GETTEXT:
1935             return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1936                            : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1937         case WM_SETTEXT:
1938         case WM_GETTEXTLENGTH:
1939         case WM_CLEAR:
1940                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1941                 {
1942                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1943                     if (j == -1) return 0;
1944                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1945                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1946                 }
1947                 else if( lphc->wState & CBF_EDIT )
1948                 {
1949                     LRESULT ret;
1950                     lphc->wState |= CBF_NOEDITNOTIFY;
1951                     ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1952                                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1953                     lphc->wState &= ~CBF_NOEDITNOTIFY;
1954                     return ret;
1955                 }
1956                 else return CB_ERR;
1957         case WM_CUT:
1958         case WM_PASTE:
1959         case WM_COPY:
1960                 if( lphc->wState & CBF_EDIT )
1961                 {
1962                     return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1963                                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1964                 }
1965                 else return  CB_ERR;
1966
1967         case WM_DRAWITEM:
1968         case WM_DELETEITEM:
1969         case WM_COMPAREITEM:
1970         case WM_MEASUREITEM:
1971                 return COMBO_ItemOp(lphc, message, lParam);
1972         case WM_ENABLE:
1973                 if( lphc->wState & CBF_EDIT )
1974                     EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1975                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1976
1977                 /* Force the control to repaint when the enabled state changes. */
1978                 InvalidateRect(lphc->self, NULL, TRUE);
1979                 return  TRUE;
1980         case WM_SETREDRAW:
1981                 if( wParam )
1982                     lphc->wState &= ~CBF_NOREDRAW;
1983                 else
1984                     lphc->wState |= CBF_NOREDRAW;
1985
1986                 if( lphc->wState & CBF_EDIT )
1987                     SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1988                 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1989                 return  0;
1990         case WM_SYSKEYDOWN:
1991                 if( KEYDATA_ALT & HIWORD(lParam) )
1992                     if( wParam == VK_UP || wParam == VK_DOWN )
1993                         COMBO_FlipListbox( lphc, FALSE, FALSE );
1994                 return  0;
1995
1996         case WM_CHAR:
1997         case WM_IME_CHAR:
1998         case WM_KEYDOWN:
1999         {
2000                 HWND hwndTarget;
2001
2002                 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2003                      (lphc->wState & CBF_DROPPED))
2004                 {
2005                    CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2006                    return TRUE;
2007                 }
2008                else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
2009                {
2010                   COMBO_FlipListbox( lphc, FALSE, FALSE );
2011                   return TRUE;
2012                }
2013
2014                 if( lphc->wState & CBF_EDIT )
2015                     hwndTarget = lphc->hWndEdit;
2016                 else
2017                     hwndTarget = lphc->hWndLBox;
2018
2019                 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2020                                  SendMessageA(hwndTarget, message, wParam, lParam);
2021         }
2022         case WM_LBUTTONDOWN:
2023                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2024                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2025                 return  TRUE;
2026         case WM_LBUTTONUP:
2027                 COMBO_LButtonUp( lphc );
2028                 return  TRUE;
2029         case WM_MOUSEMOVE:
2030                 if( lphc->wState & CBF_CAPTURE )
2031                     COMBO_MouseMove( lphc, wParam, lParam );
2032                 return  TRUE;
2033
2034         case WM_MOUSEWHEEL:
2035                 if (wParam & (MK_SHIFT | MK_CONTROL))
2036                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2037                                      DefWindowProcA(hwnd, message, wParam, lParam);
2038
2039                 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2040                 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2041                 return TRUE;
2042
2043         /* Combo messages */
2044
2045         case CB_ADDSTRING16:
2046                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2047                 /* fall through */
2048         case CB_ADDSTRING:
2049                 if( unicode )
2050                 {
2051                     if( lphc->dwStyle & CBS_LOWERCASE )
2052                         strlwrW((LPWSTR)lParam);
2053                     else if( lphc->dwStyle & CBS_UPPERCASE )
2054                         struprW((LPWSTR)lParam);
2055                     return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2056                 }
2057                 else
2058                 {
2059                     if( lphc->dwStyle & CBS_LOWERCASE )
2060                         _strlwr((LPSTR)lParam);
2061                     else if( lphc->dwStyle & CBS_UPPERCASE )
2062                         _strupr((LPSTR)lParam);
2063                     return SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2064                 }
2065         case CB_INSERTSTRING16:
2066                 wParam = (INT)(INT16)wParam;
2067                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2068                 /* fall through */
2069         case CB_INSERTSTRING:
2070                 if( unicode )
2071                 {
2072                     if( lphc->dwStyle & CBS_LOWERCASE )
2073                         strlwrW((LPWSTR)lParam);
2074                     else if( lphc->dwStyle & CBS_UPPERCASE )
2075                         struprW((LPWSTR)lParam);
2076                     return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2077                 }
2078                 else
2079                 {
2080                     if( lphc->dwStyle & CBS_LOWERCASE )
2081                         _strlwr((LPSTR)lParam);
2082                     else if( lphc->dwStyle & CBS_UPPERCASE )
2083                         _strupr((LPSTR)lParam);
2084                     return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2085                 }
2086         case CB_DELETESTRING16:
2087         case CB_DELETESTRING:
2088                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2089                                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2090         case CB_SELECTSTRING16:
2091                 wParam = (INT)(INT16)wParam;
2092                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2093                 /* fall through */
2094         case CB_SELECTSTRING:
2095                 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2096         case CB_FINDSTRING16:
2097                 wParam = (INT)(INT16)wParam;
2098                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2099                 /* fall through */
2100         case CB_FINDSTRING:
2101                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2102                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2103         case CB_FINDSTRINGEXACT16:
2104                 wParam = (INT)(INT16)wParam;
2105                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2106                 /* fall through */
2107         case CB_FINDSTRINGEXACT:
2108                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2109                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2110         case CB_SETITEMHEIGHT16:
2111                 wParam = (INT)(INT16)wParam;    /* signed integer */
2112                 /* fall through */
2113         case CB_SETITEMHEIGHT:
2114                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2115         case CB_GETITEMHEIGHT16:
2116                 wParam = (INT)(INT16)wParam;
2117                 /* fall through */
2118         case CB_GETITEMHEIGHT:
2119                 if( (INT)wParam >= 0 )  /* listbox item */
2120                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2121                 return  CBGetTextAreaHeight(hwnd, lphc);
2122         case CB_RESETCONTENT16:
2123         case CB_RESETCONTENT:
2124                 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2125                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2126                 {
2127                     static const WCHAR empty_stringW[] = { 0 };
2128                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2129                 }
2130                 else
2131                     InvalidateRect(lphc->self, NULL, TRUE);
2132                 return  TRUE;
2133         case CB_INITSTORAGE:
2134                 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2135         case CB_GETHORIZONTALEXTENT:
2136                 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2137         case CB_SETHORIZONTALEXTENT:
2138                 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2139         case CB_GETTOPINDEX:
2140                 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2141         case CB_GETLOCALE:
2142                 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2143         case CB_SETLOCALE:
2144                 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2145         case CB_GETDROPPEDWIDTH:
2146                 if( lphc->droppedWidth )
2147                     return  lphc->droppedWidth;
2148                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2149         case CB_SETDROPPEDWIDTH:
2150                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2151                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2152                 return  CB_ERR;
2153         case CB_GETDROPPEDCONTROLRECT16:
2154                 lParam = (LPARAM)MapSL(lParam);
2155                 if( lParam )
2156                 {
2157                     RECT r;
2158                     RECT16 *r16 = (RECT16 *)lParam;
2159                     CBGetDroppedControlRect( lphc, &r );
2160                     r16->left   = r.left;
2161                     r16->top    = r.top;
2162                     r16->right  = r.right;
2163                     r16->bottom = r.bottom;
2164                 }
2165                 return  CB_OKAY;
2166         case CB_GETDROPPEDCONTROLRECT:
2167                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2168                 return  CB_OKAY;
2169         case CB_GETDROPPEDSTATE16:
2170         case CB_GETDROPPEDSTATE:
2171                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2172         case CB_DIR16:
2173                 return SendMessageA(lphc->hWndLBox, LB_DIR16, wParam, lParam);
2174         case CB_DIR:
2175                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2176                                  SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2177
2178         case CB_SHOWDROPDOWN16:
2179         case CB_SHOWDROPDOWN:
2180                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2181                 {
2182                     if( wParam )
2183                     {
2184                         if( !(lphc->wState & CBF_DROPPED) )
2185                             CBDropDown( lphc );
2186                     }
2187                     else
2188                         if( lphc->wState & CBF_DROPPED )
2189                             CBRollUp( lphc, FALSE, TRUE );
2190                 }
2191                 return  TRUE;
2192         case CB_GETCOUNT16:
2193         case CB_GETCOUNT:
2194                 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2195         case CB_GETCURSEL16:
2196         case CB_GETCURSEL:
2197                 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2198         case CB_SETCURSEL16:
2199                 wParam = (INT)(INT16)wParam;
2200                 /* fall through */
2201         case CB_SETCURSEL:
2202                 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2203                 if( lParam >= 0 )
2204                     SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2205
2206                 /* no LBN_SELCHANGE in this case, update manually */
2207                 if( lphc->wState & CBF_EDIT )
2208                     CBUpdateEdit( lphc, (INT)wParam );
2209                 else
2210                     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2211                 lphc->wState &= ~CBF_SELCHANGE;
2212                 return  lParam;
2213         case CB_GETLBTEXT16:
2214                 wParam = (INT)(INT16)wParam;
2215                 lParam = (LPARAM)MapSL(lParam);
2216                 /* fall through */
2217         case CB_GETLBTEXT:
2218                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2219                                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2220         case CB_GETLBTEXTLEN16:
2221                 wParam = (INT)(INT16)wParam;
2222                 /* fall through */
2223         case CB_GETLBTEXTLEN:
2224                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2225                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2226         case CB_GETITEMDATA16:
2227                 wParam = (INT)(INT16)wParam;
2228                 /* fall through */
2229         case CB_GETITEMDATA:
2230                 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2231         case CB_SETITEMDATA16:
2232                 wParam = (INT)(INT16)wParam;
2233                 /* fall through */
2234         case CB_SETITEMDATA:
2235                 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2236         case CB_GETEDITSEL16:
2237                 wParam = lParam = 0;   /* just in case */
2238                 /* fall through */
2239         case CB_GETEDITSEL:
2240                 /* Edit checks passed parameters itself */
2241                 if( lphc->wState & CBF_EDIT )
2242                     return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2243                 return  CB_ERR;
2244         case CB_SETEDITSEL16:
2245         case CB_SETEDITSEL:
2246                 if( lphc->wState & CBF_EDIT )
2247                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2248                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2249                 return  CB_ERR;
2250         case CB_SETEXTENDEDUI16:
2251         case CB_SETEXTENDEDUI:
2252                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2253                     return  CB_ERR;
2254                 if( wParam )
2255                     lphc->wState |= CBF_EUI;
2256                 else lphc->wState &= ~CBF_EUI;
2257                 return  CB_OKAY;
2258         case CB_GETEXTENDEDUI16:
2259         case CB_GETEXTENDEDUI:
2260                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2261
2262         default:
2263                 if (message >= WM_USER)
2264                     WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2265                         message - WM_USER, wParam, lParam );
2266                 break;
2267       }
2268       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2269                        DefWindowProcA(hwnd, message, wParam, lParam);
2270 }
2271
2272 /***********************************************************************
2273  *           ComboWndProcA
2274  *
2275  * This is just a wrapper for the real ComboWndProc which locks/unlocks
2276  * window structs.
2277  */
2278 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2279 {
2280     if (!IsWindow(hwnd)) return 0;
2281     return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2282 }
2283
2284 /***********************************************************************
2285  *           ComboWndProcW
2286  */
2287 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2288 {
2289     if (!IsWindow(hwnd)) return 0;
2290     return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2291 }
2292
2293 /*************************************************************************
2294  *           GetComboBoxInfo   (USER32.@)
2295  */
2296 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo,      /* [in] handle to combo box */
2297                             PCOMBOBOXINFO pcbi   /* [in/out] combo box information */)
2298 {
2299     FIXME("\n");
2300     return FALSE;
2301
2302 }