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