If we have no dropdown height give ourselves a minimum height of 5
[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       int nIHeight;
1199
1200       nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1201
1202       nHeight = nIHeight*nItems;
1203
1204       if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1205          nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1206
1207       if (nDroppedHeight < nIHeight)
1208       {
1209             if (nItems < 5)
1210                 nDroppedHeight = nHeight;
1211             else
1212                 nDroppedHeight = 5*nIHeight;
1213       }
1214    }
1215
1216    /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1217    if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1218       rect.bottom = rect.top - nDroppedHeight;
1219
1220    SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1221                  lphc->droppedRect.right - lphc->droppedRect.left,
1222                  nDroppedHeight,
1223                  SWP_NOACTIVATE | SWP_SHOWWINDOW);
1224
1225
1226    if( !(lphc->wState & CBF_NOREDRAW) )
1227      RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1228                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1229
1230    EnableWindow( lphc->hWndLBox, TRUE );
1231    if (GetCapture() != lphc->self)
1232       SetCapture(lphc->hWndLBox);
1233 }
1234
1235 /***********************************************************************
1236  *           CBRollUp
1237  *
1238  * Hide listbox popup.
1239  */
1240 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1241 {
1242    HWND hWnd = lphc->self;
1243
1244    TRACE("[%04x]: sel ok? [%i] dropped? [%i]\n",
1245          lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1246
1247    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1248
1249    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1250    {
1251
1252        if( lphc->wState & CBF_DROPPED )
1253        {
1254            RECT rect;
1255
1256            lphc->wState &= ~CBF_DROPPED;
1257            ShowWindow( lphc->hWndLBox, SW_HIDE );
1258
1259            if(GetCapture() == lphc->hWndLBox)
1260            {
1261                ReleaseCapture();
1262            }
1263
1264            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1265            {
1266                rect = lphc->buttonRect;
1267            }
1268            else
1269            {
1270                if( bButton )
1271                {
1272                  UnionRect( &rect,
1273                             &lphc->buttonRect,
1274                             &lphc->textRect);
1275                }
1276                else
1277                  rect = lphc->textRect;
1278
1279                bButton = TRUE;
1280            }
1281
1282            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1283                RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1284                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1285            CB_NOTIFY( lphc, CBN_CLOSEUP );
1286        }
1287    }
1288 }
1289
1290 /***********************************************************************
1291  *           COMBO_FlipListbox
1292  *
1293  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1294  */
1295 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1296 {
1297    if( lphc->wState & CBF_DROPPED )
1298    {
1299        CBRollUp( lphc, ok, bRedrawButton );
1300        return FALSE;
1301    }
1302
1303    CBDropDown( lphc );
1304    return TRUE;
1305 }
1306
1307 /***********************************************************************
1308  *           CBRepaintButton
1309  */
1310 static void CBRepaintButton( LPHEADCOMBO lphc )
1311    {
1312   InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1313   UpdateWindow(lphc->self);
1314 }
1315
1316 /***********************************************************************
1317  *           COMBO_SetFocus
1318  */
1319 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1320 {
1321    if( !(lphc->wState & CBF_FOCUSED) )
1322    {
1323        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1324            SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1325
1326        /* This is wrong. Message sequences seem to indicate that this
1327           is set *after* the notify. */
1328        /* lphc->wState |= CBF_FOCUSED;  */
1329
1330        if( !(lphc->wState & CBF_EDIT) )
1331          InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1332
1333        CB_NOTIFY( lphc, CBN_SETFOCUS );
1334        lphc->wState |= CBF_FOCUSED;
1335    }
1336 }
1337
1338 /***********************************************************************
1339  *           COMBO_KillFocus
1340  */
1341 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1342 {
1343    HWND hWnd = lphc->self;
1344
1345    if( lphc->wState & CBF_FOCUSED )
1346    {
1347        CBRollUp( lphc, FALSE, TRUE );
1348        if( IsWindow( hWnd ) )
1349        {
1350            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1351                SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1352
1353            lphc->wState &= ~CBF_FOCUSED;
1354
1355            /* redraw text */
1356            if( !(lphc->wState & CBF_EDIT) )
1357              InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1358
1359            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1360        }
1361    }
1362 }
1363
1364 /***********************************************************************
1365  *           COMBO_Command
1366  */
1367 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1368 {
1369    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1370    {
1371        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1372
1373        switch( HIWORD(wParam) >> 8 )
1374        {
1375            case (EN_SETFOCUS >> 8):
1376
1377                 TRACE("[%04x]: edit [%04x] got focus\n",
1378                              lphc->self, lphc->hWndEdit );
1379
1380                 COMBO_SetFocus( lphc );
1381                 break;
1382
1383            case (EN_KILLFOCUS >> 8):
1384
1385                 TRACE("[%04x]: edit [%04x] lost focus\n",
1386                              lphc->self, lphc->hWndEdit );
1387
1388                 /* NOTE: it seems that Windows' edit control sends an
1389                  * undocumented message WM_USER + 0x1B instead of this
1390                  * notification (only when it happens to be a part of
1391                  * the combo). ?? - AK.
1392                  */
1393
1394                 COMBO_KillFocus( lphc );
1395                 break;
1396
1397
1398            case (EN_CHANGE >> 8):
1399                /*
1400                 * In some circumstances (when the selection of the combobox
1401                 * is changed for example) we don't wans the EN_CHANGE notification
1402                 * to be forwarded to the parent of the combobox. This code
1403                 * checks a flag that is set in these occasions and ignores the
1404                 * notification.
1405                 */
1406                 if (lphc->wState & CBF_NOLBSELECT)
1407                 {
1408                   lphc->wState &= ~CBF_NOLBSELECT;
1409                 }
1410                 else
1411                 {
1412                   CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1413                 }
1414
1415                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1416                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1417                 break;
1418
1419            case (EN_UPDATE >> 8):
1420                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1421                   CB_NOTIFY( lphc, CBN_EDITUPDATE );
1422                 break;
1423
1424            case (EN_ERRSPACE >> 8):
1425                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1426        }
1427    }
1428    else if( lphc->hWndLBox == hWnd )
1429    {
1430        switch( HIWORD(wParam) )
1431        {
1432            case LBN_ERRSPACE:
1433                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1434                 break;
1435
1436            case LBN_DBLCLK:
1437                 CB_NOTIFY( lphc, CBN_DBLCLK );
1438                 break;
1439
1440            case LBN_SELCHANGE:
1441            case LBN_SELCANCEL:
1442
1443                 TRACE("[%04x]: lbox selection change [%04x]\n",
1444                              lphc->self, lphc->wState );
1445
1446                 if( HIWORD(wParam) == LBN_SELCHANGE)
1447                 {
1448                    if( lphc->wState & CBF_EDIT )
1449                    {
1450                        INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1451                        lphc->wState |= CBF_NOLBSELECT;
1452                        CBUpdateEdit( lphc, index );
1453                        /* select text in edit, as Windows does */
1454                        SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1455                    }
1456                    else
1457                        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1458                 }
1459
1460                 /* do not roll up if selection is being tracked
1461                  * by arrowkeys in the dropdown listbox */
1462                 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1463                 {
1464                    CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1465                 }
1466                 else lphc->wState &= ~CBF_NOROLLUP;
1467
1468                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1469
1470                 /* fall through */
1471
1472            case LBN_SETFOCUS:
1473            case LBN_KILLFOCUS:
1474                 /* nothing to do here since ComboLBox always resets the focus to its
1475                  * combo/edit counterpart */
1476                  break;
1477        }
1478    }
1479    return 0;
1480 }
1481
1482 /***********************************************************************
1483  *           COMBO_ItemOp
1484  *
1485  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1486  */
1487 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1488 {
1489    HWND hWnd = lphc->self;
1490    UINT id = GetWindowLongA( hWnd, GWL_ID );
1491
1492    TRACE("[%04x]: ownerdraw op %04x\n", lphc->self, msg );
1493
1494    switch( msg )
1495    {
1496    case WM_DELETEITEM:
1497        {
1498            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1499            lpIS->CtlType  = ODT_COMBOBOX;
1500            lpIS->CtlID    = id;
1501            lpIS->hwndItem = hWnd;
1502            break;
1503        }
1504    case WM_DRAWITEM:
1505        {
1506            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1507            lpIS->CtlType  = ODT_COMBOBOX;
1508            lpIS->CtlID    = id;
1509            lpIS->hwndItem = hWnd;
1510            break;
1511        }
1512    case WM_COMPAREITEM:
1513        {
1514            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1515            lpIS->CtlType  = ODT_COMBOBOX;
1516            lpIS->CtlID    = id;
1517            lpIS->hwndItem = hWnd;
1518            break;
1519        }
1520    case WM_MEASUREITEM:
1521        {
1522            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1523            lpIS->CtlType  = ODT_COMBOBOX;
1524            lpIS->CtlID    = id;
1525            break;
1526        }
1527    }
1528    return SendMessageW(lphc->owner, msg, id, lParam);
1529 }
1530
1531
1532 /***********************************************************************
1533  *           COMBO_GetTextW
1534  */
1535 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1536 {
1537     INT length;
1538
1539     if( lphc->wState & CBF_EDIT )
1540         return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1541
1542     /* get it from the listbox */
1543
1544     if (!count || !buf) return 0;
1545     if( lphc->hWndLBox )
1546     {
1547         INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1548         if (idx == LB_ERR) goto error;
1549         length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1550         if (length == LB_ERR) goto error;
1551
1552         /* 'length' is without the terminating character */
1553         if (length >= count)
1554         {
1555             LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1556             if (!lpBuffer) goto error;
1557             length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1558
1559             /* truncate if buffer is too short */
1560             if (length != LB_ERR)
1561             {
1562                 lstrcpynW( buf, lpBuffer, count );
1563                 length = count;
1564             }
1565             HeapFree( GetProcessHeap(), 0, lpBuffer );
1566         }
1567         else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1568
1569         if (length == LB_ERR) return 0;
1570         return length;
1571     }
1572
1573  error:  /* error - truncate string, return zero */
1574     buf[0] = 0;
1575     return 0;
1576 }
1577
1578
1579 /***********************************************************************
1580  *           COMBO_GetTextA
1581  *
1582  * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1583  *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1584  */
1585 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1586 {
1587     INT length;
1588
1589     if( lphc->wState & CBF_EDIT )
1590         return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1591
1592     /* get it from the listbox */
1593
1594     if (!count || !buf) return 0;
1595     if( lphc->hWndLBox )
1596     {
1597         INT idx = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1598         if (idx == LB_ERR) goto error;
1599         length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1600         if (length == LB_ERR) goto error;
1601
1602         /* 'length' is without the terminating character */
1603         if (length >= count)
1604         {
1605             LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1606             if (!lpBuffer) goto error;
1607             length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1608
1609             /* truncate if buffer is too short */
1610             if (length != LB_ERR)
1611             {
1612                 lstrcpynA( buf, lpBuffer, count );
1613                 length = count;
1614             }
1615             HeapFree( GetProcessHeap(), 0, lpBuffer );
1616         }
1617         else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1618
1619         if (length == LB_ERR) return 0;
1620         return length;
1621     }
1622
1623  error:  /* error - truncate string, return zero */
1624     buf[0] = 0;
1625     return 0;
1626 }
1627
1628
1629 /***********************************************************************
1630  *           CBResetPos
1631  *
1632  * This function sets window positions according to the updated
1633  * component placement struct.
1634  */
1635 static void CBResetPos(
1636   LPHEADCOMBO lphc,
1637   LPRECT      rectEdit,
1638   LPRECT      rectLB,
1639   BOOL        bRedraw)
1640 {
1641    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1642
1643    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1644     * sizing messages */
1645
1646    if( lphc->wState & CBF_EDIT )
1647      SetWindowPos( lphc->hWndEdit, 0,
1648                    rectEdit->left, rectEdit->top,
1649                    rectEdit->right - rectEdit->left,
1650                    rectEdit->bottom - rectEdit->top,
1651                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1652
1653    SetWindowPos( lphc->hWndLBox, 0,
1654                  rectLB->left, rectLB->top,
1655                  rectLB->right - rectLB->left,
1656                  rectLB->bottom - rectLB->top,
1657                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1658
1659    if( bDrop )
1660    {
1661        if( lphc->wState & CBF_DROPPED )
1662        {
1663            lphc->wState &= ~CBF_DROPPED;
1664            ShowWindow( lphc->hWndLBox, SW_HIDE );
1665        }
1666
1667        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1668            RedrawWindow( lphc->self, NULL, 0,
1669                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1670    }
1671 }
1672
1673
1674 /***********************************************************************
1675  *           COMBO_Size
1676  */
1677 static void COMBO_Size( LPHEADCOMBO lphc )
1678   {
1679   CBCalcPlacement(lphc->self,
1680                   lphc,
1681                   &lphc->textRect,
1682                   &lphc->buttonRect,
1683                   &lphc->droppedRect);
1684
1685   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1686 }
1687
1688
1689 /***********************************************************************
1690  *           COMBO_Font
1691  */
1692 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1693 {
1694   /*
1695    * Set the font
1696    */
1697   lphc->hFont = hFont;
1698
1699   /*
1700    * Propagate to owned windows.
1701    */
1702   if( lphc->wState & CBF_EDIT )
1703       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1704   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1705
1706   /*
1707    * Redo the layout of the control.
1708    */
1709   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1710   {
1711     CBCalcPlacement(lphc->self,
1712                     lphc,
1713                     &lphc->textRect,
1714                     &lphc->buttonRect,
1715                     &lphc->droppedRect);
1716
1717     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1718   }
1719   else
1720   {
1721     CBForceDummyResize(lphc);
1722   }
1723 }
1724
1725
1726 /***********************************************************************
1727  *           COMBO_SetItemHeight
1728  */
1729 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1730 {
1731    LRESULT      lRet = CB_ERR;
1732
1733    if( index == -1 ) /* set text field height */
1734    {
1735        if( height < 32768 )
1736        {
1737            lphc->editHeight = height;
1738
1739          /*
1740           * Redo the layout of the control.
1741           */
1742          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1743          {
1744            CBCalcPlacement(lphc->self,
1745                            lphc,
1746                            &lphc->textRect,
1747                            &lphc->buttonRect,
1748                            &lphc->droppedRect);
1749
1750            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1751          }
1752          else
1753          {
1754            CBForceDummyResize(lphc);
1755          }
1756
1757            lRet = height;
1758        }
1759    }
1760    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1761         lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1762                               (WPARAM)index, (LPARAM)height );
1763    return lRet;
1764 }
1765
1766 /***********************************************************************
1767  *           COMBO_SelectString
1768  */
1769 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1770 {
1771    INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1772                          SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1773    if( index >= 0 )
1774    {
1775      if( lphc->wState & CBF_EDIT )
1776        CBUpdateEdit( lphc, index );
1777      else
1778      {
1779        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1780      }
1781    }
1782    return (LRESULT)index;
1783 }
1784
1785 /***********************************************************************
1786  *           COMBO_LButtonDown
1787  */
1788 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1789 {
1790    POINT     pt;
1791    BOOL      bButton;
1792    HWND      hWnd = lphc->self;
1793
1794    pt.x = LOWORD(lParam);
1795    pt.y = HIWORD(lParam);
1796    bButton = PtInRect(&lphc->buttonRect, pt);
1797
1798    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1799        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1800    {
1801        lphc->wState |= CBF_BUTTONDOWN;
1802        if( lphc->wState & CBF_DROPPED )
1803        {
1804            /* got a click to cancel selection */
1805
1806            lphc->wState &= ~CBF_BUTTONDOWN;
1807            CBRollUp( lphc, TRUE, FALSE );
1808            if( !IsWindow( hWnd ) ) return;
1809
1810            if( lphc->wState & CBF_CAPTURE )
1811            {
1812                lphc->wState &= ~CBF_CAPTURE;
1813                ReleaseCapture();
1814            }
1815        }
1816        else
1817        {
1818            /* drop down the listbox and start tracking */
1819
1820            lphc->wState |= CBF_CAPTURE;
1821            SetCapture( hWnd );
1822            CBDropDown( lphc );
1823        }
1824        if( bButton ) CBRepaintButton( lphc );
1825    }
1826 }
1827
1828 /***********************************************************************
1829  *           COMBO_LButtonUp
1830  *
1831  * Release capture and stop tracking if needed.
1832  */
1833 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1834 {
1835    if( lphc->wState & CBF_CAPTURE )
1836    {
1837        lphc->wState &= ~CBF_CAPTURE;
1838        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1839        {
1840            INT index = CBUpdateLBox( lphc, TRUE );
1841            /* Update edit only if item is in the list */
1842            if(index >= 0)
1843            {
1844                lphc->wState |= CBF_NOLBSELECT;
1845                CBUpdateEdit( lphc, index );
1846                lphc->wState &= ~CBF_NOLBSELECT;
1847            }
1848        }
1849        ReleaseCapture();
1850        SetCapture(lphc->hWndLBox);
1851    }
1852
1853    if( lphc->wState & CBF_BUTTONDOWN )
1854    {
1855        lphc->wState &= ~CBF_BUTTONDOWN;
1856        CBRepaintButton( lphc );
1857    }
1858 }
1859
1860 /***********************************************************************
1861  *           COMBO_MouseMove
1862  *
1863  * Two things to do - track combo button and release capture when
1864  * pointer goes into the listbox.
1865  */
1866 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1867 {
1868    POINT  pt;
1869    RECT   lbRect;
1870
1871    pt.x = LOWORD(lParam);
1872    pt.y = HIWORD(lParam);
1873
1874    if( lphc->wState & CBF_BUTTONDOWN )
1875    {
1876      BOOL bButton;
1877
1878      bButton = PtInRect(&lphc->buttonRect, pt);
1879
1880      if( !bButton )
1881      {
1882        lphc->wState &= ~CBF_BUTTONDOWN;
1883        CBRepaintButton( lphc );
1884      }
1885    }
1886
1887    GetClientRect( lphc->hWndLBox, &lbRect );
1888    MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1889    if( PtInRect(&lbRect, pt) )
1890    {
1891        lphc->wState &= ~CBF_CAPTURE;
1892        ReleaseCapture();
1893        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1894
1895        /* hand over pointer tracking */
1896        SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1897    }
1898 }
1899
1900
1901 /***********************************************************************
1902  *           ComboWndProc_common
1903  *
1904  * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1905  */
1906 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1907                                     WPARAM wParam, LPARAM lParam, BOOL unicode )
1908 {
1909       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
1910
1911       TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1912             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1913
1914       if( lphc || message == WM_NCCREATE )
1915       switch(message)
1916       {
1917
1918         /* System messages */
1919
1920         case WM_NCCREATE:
1921         {
1922                 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1923                                        ((LPCREATESTRUCTA)lParam)->style;
1924                 return COMBO_NCCreate(hwnd, style);
1925         }
1926         case WM_NCDESTROY:
1927                 COMBO_NCDestroy(lphc);
1928                 break;/* -> DefWindowProc */
1929
1930         case WM_CREATE:
1931         {
1932                 HWND hwndParent;
1933                 LONG style;
1934                 if(unicode)
1935                 {
1936                     hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1937                     style = ((LPCREATESTRUCTW)lParam)->style;
1938                 }
1939                 else
1940                 {
1941                     hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1942                     style = ((LPCREATESTRUCTA)lParam)->style;
1943                 }
1944                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1945         }
1946
1947         case WM_PRINTCLIENT:
1948                 if (lParam & PRF_ERASEBKGND)
1949                   COMBO_EraseBackground(hwnd, lphc, wParam);
1950
1951                 /* Fallthrough */
1952         case WM_PAINT:
1953                 /* wParam may contain a valid HDC! */
1954                 return  COMBO_Paint(lphc, wParam);
1955         case WM_ERASEBKGND:
1956                 return  COMBO_EraseBackground(hwnd, lphc, wParam);
1957         case WM_GETDLGCODE:
1958         {
1959                 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1960                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1961                 {
1962                    int vk = (int)((LPMSG)lParam)->wParam;
1963
1964                    if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1965                        result |= DLGC_WANTMESSAGE;
1966                 }
1967                 return  result;
1968         }
1969         case WM_WINDOWPOSCHANGING:
1970                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1971     case WM_WINDOWPOSCHANGED:
1972         /* SetWindowPos can be called on a Combobox to resize its Listbox.
1973          * In that case, the Combobox itself will not be resized, so we won't
1974          * get a WM_SIZE. Since we still want to update the Listbox, we have to
1975          * do it here.
1976          */
1977         /* fall through */
1978         case WM_SIZE:
1979                 if( lphc->hWndLBox &&
1980                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1981                 return  TRUE;
1982         case WM_SETFONT:
1983                 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1984                 return  TRUE;
1985         case WM_GETFONT:
1986                 return  (LRESULT)lphc->hFont;
1987         case WM_SETFOCUS:
1988                 if( lphc->wState & CBF_EDIT )
1989                     SetFocus( lphc->hWndEdit );
1990                 else
1991                     COMBO_SetFocus( lphc );
1992                 return  TRUE;
1993         case WM_KILLFOCUS:
1994             {
1995                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1996                 if( !hwndFocus ||
1997                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1998                     COMBO_KillFocus( lphc );
1999                 return  TRUE;
2000             }
2001         case WM_COMMAND:
2002                 return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
2003         case WM_GETTEXT:
2004             return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
2005                            : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
2006         case WM_SETTEXT:
2007         case WM_GETTEXTLENGTH:
2008         case WM_CLEAR:
2009                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
2010                 {
2011                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2012                     if (j == -1) return 0;
2013                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
2014                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
2015                 }
2016                 else if( lphc->wState & CBF_EDIT )
2017                 {
2018                     LRESULT ret;
2019                     lphc->wState |= CBF_NOEDITNOTIFY;
2020                     ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
2021                                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
2022                     lphc->wState &= ~CBF_NOEDITNOTIFY;
2023                     return ret;
2024                 }
2025                 else return CB_ERR;
2026         case WM_CUT:
2027         case WM_PASTE:
2028         case WM_COPY:
2029                 if( lphc->wState & CBF_EDIT )
2030                 {
2031                     return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
2032                                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
2033                 }
2034                 else return  CB_ERR;
2035
2036         case WM_DRAWITEM:
2037         case WM_DELETEITEM:
2038         case WM_COMPAREITEM:
2039         case WM_MEASUREITEM:
2040                 return COMBO_ItemOp(lphc, message, lParam);
2041         case WM_ENABLE:
2042                 if( lphc->wState & CBF_EDIT )
2043                     EnableWindow( lphc->hWndEdit, (BOOL)wParam );
2044                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
2045
2046                 /* Force the control to repaint when the enabled state changes. */
2047                 InvalidateRect(lphc->self, NULL, TRUE);
2048                 return  TRUE;
2049         case WM_SETREDRAW:
2050                 if( wParam )
2051                     lphc->wState &= ~CBF_NOREDRAW;
2052                 else
2053                     lphc->wState |= CBF_NOREDRAW;
2054
2055                 if( lphc->wState & CBF_EDIT )
2056                     SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2057                 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2058                 return  0;
2059         case WM_SYSKEYDOWN:
2060                 if( KEYDATA_ALT & HIWORD(lParam) )
2061                     if( wParam == VK_UP || wParam == VK_DOWN )
2062                         COMBO_FlipListbox( lphc, FALSE, FALSE );
2063                 return  0;
2064
2065         case WM_CHAR:
2066         case WM_KEYDOWN:
2067         {
2068                 HWND hwndTarget;
2069
2070                 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2071                      (lphc->wState & CBF_DROPPED))
2072                 {
2073                    CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2074                    return TRUE;
2075                 }
2076
2077                 if( lphc->wState & CBF_EDIT )
2078                     hwndTarget = lphc->hWndEdit;
2079                 else
2080                     hwndTarget = lphc->hWndLBox;
2081
2082                 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2083                                  SendMessageA(hwndTarget, message, wParam, lParam);
2084         }
2085         case WM_LBUTTONDOWN:
2086                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2087                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2088                 return  TRUE;
2089         case WM_LBUTTONUP:
2090                 COMBO_LButtonUp( lphc );
2091                 return  TRUE;
2092         case WM_MOUSEMOVE:
2093                 if( lphc->wState & CBF_CAPTURE )
2094                     COMBO_MouseMove( lphc, wParam, lParam );
2095                 return  TRUE;
2096
2097         case WM_MOUSEWHEEL:
2098                 if (wParam & (MK_SHIFT | MK_CONTROL))
2099                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2100                                      DefWindowProcA(hwnd, message, wParam, lParam);
2101                 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2102                 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2103                 return TRUE;
2104
2105         /* Combo messages */
2106
2107         case CB_ADDSTRING16:
2108                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2109                 /* fall through */
2110         case CB_ADDSTRING:
2111                 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2112                                  SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2113         case CB_INSERTSTRING16:
2114                 wParam = (INT)(INT16)wParam;
2115                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2116                 /* fall through */
2117         case CB_INSERTSTRING:
2118                 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2119                                  SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2120         case CB_DELETESTRING16:
2121         case CB_DELETESTRING:
2122                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2123                                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2124         case CB_SELECTSTRING16:
2125                 wParam = (INT)(INT16)wParam;
2126                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2127                 /* fall through */
2128         case CB_SELECTSTRING:
2129                 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2130         case CB_FINDSTRING16:
2131                 wParam = (INT)(INT16)wParam;
2132                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2133                 /* fall through */
2134         case CB_FINDSTRING:
2135                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2136                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2137         case CB_FINDSTRINGEXACT16:
2138                 wParam = (INT)(INT16)wParam;
2139                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2140                 /* fall through */
2141         case CB_FINDSTRINGEXACT:
2142                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2143                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2144         case CB_SETITEMHEIGHT16:
2145                 wParam = (INT)(INT16)wParam;    /* signed integer */
2146                 /* fall through */
2147         case CB_SETITEMHEIGHT:
2148                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2149         case CB_GETITEMHEIGHT16:
2150                 wParam = (INT)(INT16)wParam;
2151                 /* fall through */
2152         case CB_GETITEMHEIGHT:
2153                 if( (INT)wParam >= 0 )  /* listbox item */
2154                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2155                 return  CBGetTextAreaHeight(hwnd, lphc);
2156         case CB_RESETCONTENT16:
2157         case CB_RESETCONTENT:
2158                 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2159                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2160                 {
2161                     static const WCHAR empty_stringW[] = { 0 };
2162                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2163                 }
2164                 else
2165                     InvalidateRect(lphc->self, NULL, TRUE);
2166                 return  TRUE;
2167         case CB_INITSTORAGE:
2168                 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2169         case CB_GETHORIZONTALEXTENT:
2170                 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2171         case CB_SETHORIZONTALEXTENT:
2172                 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2173         case CB_GETTOPINDEX:
2174                 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2175         case CB_GETLOCALE:
2176                 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2177         case CB_SETLOCALE:
2178                 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2179         case CB_GETDROPPEDWIDTH:
2180                 if( lphc->droppedWidth )
2181                     return  lphc->droppedWidth;
2182                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2183         case CB_SETDROPPEDWIDTH:
2184                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2185                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2186                 return  CB_ERR;
2187         case CB_GETDROPPEDCONTROLRECT16:
2188                 lParam = (LPARAM)MapSL(lParam);
2189                 if( lParam )
2190                 {
2191                     RECT        r;
2192                     CBGetDroppedControlRect( lphc, &r );
2193                     CONV_RECT32TO16( &r, (LPRECT16)lParam );
2194                 }
2195                 return  CB_OKAY;
2196         case CB_GETDROPPEDCONTROLRECT:
2197                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2198                 return  CB_OKAY;
2199         case CB_GETDROPPEDSTATE16:
2200         case CB_GETDROPPEDSTATE:
2201                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2202         case CB_DIR16:
2203                 lParam = (LPARAM)MapSL(lParam);
2204                 message = LB_DIR16;
2205                 /* fall through */
2206         case CB_DIR:
2207                 if(message == CB_DIR) message = LB_DIR;
2208                 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2209                                  SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2210
2211         case CB_SHOWDROPDOWN16:
2212         case CB_SHOWDROPDOWN:
2213                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2214                 {
2215                     if( wParam )
2216                     {
2217                         if( !(lphc->wState & CBF_DROPPED) )
2218                             CBDropDown( lphc );
2219                     }
2220                     else
2221                         if( lphc->wState & CBF_DROPPED )
2222                             CBRollUp( lphc, FALSE, TRUE );
2223                 }
2224                 return  TRUE;
2225         case CB_GETCOUNT16:
2226         case CB_GETCOUNT:
2227                 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2228         case CB_GETCURSEL16:
2229         case CB_GETCURSEL:
2230                 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2231         case CB_SETCURSEL16:
2232                 wParam = (INT)(INT16)wParam;
2233                 /* fall through */
2234         case CB_SETCURSEL:
2235                 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2236                 if( lParam >= 0 )
2237                     SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2238
2239                 /* no LBN_SELCHANGE in this case, update manually */
2240                 if( lphc->wState & CBF_EDIT )
2241                     CBUpdateEdit( lphc, (INT)wParam );
2242                 else
2243                     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2244                 lphc->wState &= ~CBF_SELCHANGE;
2245                 return  lParam;
2246         case CB_GETLBTEXT16:
2247                 wParam = (INT)(INT16)wParam;
2248                 lParam = (LPARAM)MapSL(lParam);
2249                 /* fall through */
2250         case CB_GETLBTEXT:
2251                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2252                                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2253         case CB_GETLBTEXTLEN16:
2254                 wParam = (INT)(INT16)wParam;
2255                 /* fall through */
2256         case CB_GETLBTEXTLEN:
2257                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2258                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2259         case CB_GETITEMDATA16:
2260                 wParam = (INT)(INT16)wParam;
2261                 /* fall through */
2262         case CB_GETITEMDATA:
2263                 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2264         case CB_SETITEMDATA16:
2265                 wParam = (INT)(INT16)wParam;
2266                 /* fall through */
2267         case CB_SETITEMDATA:
2268                 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2269         case CB_GETEDITSEL16:
2270                 wParam = lParam = 0;   /* just in case */
2271                 /* fall through */
2272         case CB_GETEDITSEL:
2273                 /* Edit checks passed parameters itself */
2274                 if( lphc->wState & CBF_EDIT )
2275                     return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2276                 return  CB_ERR;
2277         case CB_SETEDITSEL16:
2278         case CB_SETEDITSEL:
2279                 if( lphc->wState & CBF_EDIT )
2280                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2281                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2282                 return  CB_ERR;
2283         case CB_SETEXTENDEDUI16:
2284         case CB_SETEXTENDEDUI:
2285                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2286                     return  CB_ERR;
2287                 if( wParam )
2288                     lphc->wState |= CBF_EUI;
2289                 else lphc->wState &= ~CBF_EUI;
2290                 return  CB_OKAY;
2291         case CB_GETEXTENDEDUI16:
2292         case CB_GETEXTENDEDUI:
2293                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2294
2295         default:
2296                 if (message >= WM_USER)
2297                     WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2298                         message - WM_USER, wParam, lParam );
2299                 break;
2300       }
2301       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2302                        DefWindowProcA(hwnd, message, wParam, lParam);
2303 }
2304
2305 /***********************************************************************
2306  *           ComboWndProcA
2307  *
2308  * This is just a wrapper for the real ComboWndProc which locks/unlocks
2309  * window structs.
2310  */
2311 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2312 {
2313     if (!IsWindow(hwnd)) return 0;
2314     return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2315 }
2316
2317 /***********************************************************************
2318  *           ComboWndProcW
2319  */
2320 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2321 {
2322     if (!IsWindow(hwnd)) return 0;
2323     return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2324 }