Make sure edit and listbox controls are of same ASCII/Unicode style as
[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( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
775         {
776             /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
777             size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
778             pText[size] = '\0'; /* just in case */
779         } else return;
780    }
781    else
782        if( !CB_OWNERDRAWN(lphc) )
783            return;
784
785    if( lphc->wState & CBF_EDIT )
786    {
787         static const WCHAR empty_stringW[] = { 0 };
788         if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
789         if( lphc->wState & CBF_FOCUSED )
790             SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
791    }
792    else /* paint text field ourselves */
793    {
794      UINT       itemState = ODS_COMBOBOXEDIT;
795      HFONT      hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
796
797      /*
798       * Give ourselves some space.
799       */
800      InflateRect( &rectEdit, -1, -1 );
801
802      if( CB_OWNERDRAWN(lphc) )
803      {
804        DRAWITEMSTRUCT dis;
805        HRGN           clipRegion;
806        UINT ctlid = GetWindowLongA( lphc->self, GWL_ID );
807
808        /* setup state for DRAWITEM message. Owner will highlight */
809        if ( (lphc->wState & CBF_FOCUSED) &&
810             !(lphc->wState & CBF_DROPPED) )
811            itemState |= ODS_SELECTED | ODS_FOCUS;
812
813        /*
814         * Save the current clip region.
815         * To retrieve the clip region, we need to create one "dummy"
816         * clip region.
817         */
818        clipRegion = CreateRectRgnIndirect(&rectEdit);
819
820        if (GetClipRgn(hdc, clipRegion)!=1)
821        {
822          DeleteObject(clipRegion);
823          clipRegion=(HRGN)NULL;
824        }
825
826        if (!IsWindowEnabled(lphc->self) & WS_DISABLED) itemState |= ODS_DISABLED;
827
828        dis.CtlType      = ODT_COMBOBOX;
829        dis.CtlID        = ctlid;
830        dis.hwndItem     = lphc->self;
831        dis.itemAction   = ODA_DRAWENTIRE;
832        dis.itemID       = id;
833        dis.itemState    = itemState;
834        dis.hDC          = hdc;
835        dis.rcItem       = rectEdit;
836        dis.itemData     = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
837                                         (WPARAM)id, 0 );
838
839        /*
840         * Clip the DC and have the parent draw the item.
841         */
842        IntersectClipRect(hdc,
843                          rectEdit.left,  rectEdit.top,
844                          rectEdit.right, rectEdit.bottom);
845
846        SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
847
848        /*
849         * Reset the clipping region.
850         */
851        SelectClipRgn(hdc, clipRegion);
852      }
853      else
854      {
855        static const WCHAR empty_stringW[] = { 0 };
856
857        if ( (lphc->wState & CBF_FOCUSED) &&
858             !(lphc->wState & CBF_DROPPED) ) {
859
860            /* highlight */
861            FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
862            SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
863            SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
864        }
865
866        ExtTextOutW( hdc,
867                     rectEdit.left + 1,
868                     rectEdit.top + 1,
869                     ETO_OPAQUE | ETO_CLIPPED,
870                     &rectEdit,
871                     pText ? pText : empty_stringW , size, NULL );
872
873        if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
874          DrawFocusRect( hdc, &rectEdit );
875      }
876
877      if( hPrevFont )
878        SelectObject(hdc, hPrevFont );
879    }
880    if (pText)
881         HeapFree( GetProcessHeap(), 0, pText );
882 }
883
884 /***********************************************************************
885  *           CBPaintBorder
886  */
887 static void CBPaintBorder(
888   HWND        hwnd,
889   LPHEADCOMBO lphc,
890   HDC         hdc)
891 {
892   RECT clientRect;
893
894   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
895   {
896     GetClientRect(hwnd, &clientRect);
897   }
898   else
899   {
900     CopyRect(&clientRect, &lphc->textRect);
901
902     InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
903     InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
904   }
905
906   DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
907 }
908
909 /***********************************************************************
910  *           COMBO_PrepareColors
911  *
912  * This method will sent the appropriate WM_CTLCOLOR message to
913  * prepare and setup the colors for the combo's DC.
914  *
915  * It also returns the brush to use for the background.
916  */
917 static HBRUSH COMBO_PrepareColors(
918   LPHEADCOMBO lphc,
919   HDC         hDC)
920 {
921   HBRUSH  hBkgBrush;
922
923   /*
924    * Get the background brush for this control.
925    */
926   if (CB_DISABLED(lphc))
927   {
928     hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORSTATIC, hDC, (LPARAM)lphc->self );
929
930     /*
931      * We have to change the text color since WM_CTLCOLORSTATIC will
932      * set it to the "enabled" color. This is the same behavior as the
933      * edit control
934      */
935     SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
936   }
937   else
938   {
939     if (lphc->wState & CBF_EDIT)
940     {
941       hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLOREDIT, hDC, (LPARAM)lphc->self );
942     }
943     else
944     {
945       hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX, hDC, (LPARAM)lphc->self );
946     }
947   }
948
949   /*
950    * Catch errors.
951    */
952   if( !hBkgBrush )
953     hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
954
955   return hBkgBrush;
956 }
957
958 /***********************************************************************
959  *           COMBO_EraseBackground
960  */
961 static LRESULT COMBO_EraseBackground(
962   HWND        hwnd,
963   LPHEADCOMBO lphc,
964   HDC         hParamDC)
965 {
966   HBRUSH  hBkgBrush;
967   HDC     hDC;
968
969   if(lphc->wState & CBF_EDIT)
970       return TRUE;
971
972   hDC = (hParamDC) ? hParamDC
973                    : GetDC(hwnd);
974   /*
975    * Retrieve the background brush
976    */
977   hBkgBrush = COMBO_PrepareColors(lphc, hDC);
978
979   FillRect(hDC, &lphc->textRect, hBkgBrush);
980
981   if (!hParamDC)
982     ReleaseDC(hwnd, hDC);
983
984   return TRUE;
985 }
986
987 /***********************************************************************
988  *           COMBO_Paint
989  */
990 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
991 {
992   PAINTSTRUCT ps;
993   HDC   hDC;
994
995   hDC = (hParamDC) ? hParamDC
996                    : BeginPaint( lphc->self, &ps);
997
998   TRACE("hdc=%04x\n", hDC);
999
1000   if( hDC && !(lphc->wState & CBF_NOREDRAW) )
1001   {
1002       HBRUSH    hPrevBrush, hBkgBrush;
1003
1004       /*
1005        * Retrieve the background brush and select it in the
1006        * DC.
1007        */
1008       hBkgBrush = COMBO_PrepareColors(lphc, hDC);
1009
1010       hPrevBrush = SelectObject( hDC, hBkgBrush );
1011
1012       /*
1013        * In non 3.1 look, there is a sunken border on the combobox
1014        */
1015       if (TWEAK_WineLook != WIN31_LOOK)
1016       {
1017         CBPaintBorder(lphc->self, lphc, hDC);
1018       }
1019
1020       if( !IsRectEmpty(&lphc->buttonRect) )
1021       {
1022         CBPaintButton(lphc, hDC, lphc->buttonRect);
1023       }
1024
1025       /* paint the edit control padding area */
1026       if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
1027       {
1028           RECT rPadEdit = lphc->textRect;
1029
1030           InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
1031
1032           FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
1033       }
1034
1035       if( !(lphc->wState & CBF_EDIT) )
1036       {
1037         /*
1038          * The text area has a border only in Win 3.1 look.
1039          */
1040         if (TWEAK_WineLook == WIN31_LOOK)
1041         {
1042           HPEN hPrevPen = SelectObject( hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1043
1044           Rectangle( hDC,
1045                      lphc->textRect.left, lphc->textRect.top,
1046                      lphc->textRect.right - 1, lphc->textRect.bottom - 1);
1047
1048           SelectObject( hDC, hPrevPen );
1049         }
1050
1051         CBPaintText( lphc, hDC, lphc->textRect);
1052       }
1053
1054       if( hPrevBrush )
1055         SelectObject( hDC, hPrevBrush );
1056   }
1057
1058   if( !hParamDC )
1059     EndPaint(lphc->self, &ps);
1060
1061   return 0;
1062 }
1063
1064 /***********************************************************************
1065  *           CBUpdateLBox
1066  *
1067  * Select listbox entry according to the contents of the edit control.
1068  */
1069 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1070 {
1071    INT  length, idx;
1072    LPWSTR pText = NULL;
1073
1074    idx = LB_ERR;
1075    length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
1076
1077    if( length > 0 )
1078        pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1079
1080    TRACE("\t edit text length %i\n", length );
1081
1082    if( pText )
1083    {
1084        if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
1085        else pText[0] = '\0';
1086        idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1087                              (WPARAM)(-1), (LPARAM)pText );
1088        HeapFree( GetProcessHeap(), 0, pText );
1089    }
1090
1091    SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
1092
1093    /* probably superfluous but Windows sends this too */
1094    SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1095    SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1096
1097    return idx;
1098 }
1099
1100 /***********************************************************************
1101  *           CBUpdateEdit
1102  *
1103  * Copy a listbox entry to the edit control.
1104  */
1105 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1106 {
1107    INT  length;
1108    LPWSTR pText = NULL;
1109    static const WCHAR empty_stringW[] = { 0 };
1110
1111    TRACE("\t %i\n", index );
1112
1113    if( index >= 0 ) /* got an entry */
1114    {
1115        length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1116        if( length )
1117        {
1118            if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1119            {
1120                 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1121                                 (WPARAM)index, (LPARAM)pText );
1122            }
1123        }
1124    }
1125
1126    lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1127    SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1128    lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1129
1130    if( lphc->wState & CBF_FOCUSED )
1131       SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1132
1133    if( pText )
1134        HeapFree( GetProcessHeap(), 0, pText );
1135 }
1136
1137 /***********************************************************************
1138  *           CBDropDown
1139  *
1140  * Show listbox popup.
1141  */
1142 static void CBDropDown( LPHEADCOMBO lphc )
1143 {
1144    RECT rect,r;
1145    int nItems = 0;
1146    int nDroppedHeight;
1147
1148    TRACE("[%04x]: drop down\n", lphc->self);
1149
1150    CB_NOTIFY( lphc, CBN_DROPDOWN );
1151
1152    /* set selection */
1153
1154    lphc->wState |= CBF_DROPPED;
1155    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1156    {
1157        lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1158
1159        /* Update edit only if item is in the list */
1160        if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1161          CBUpdateEdit( lphc, lphc->droppedIndex );
1162    }
1163    else
1164    {
1165        lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1166
1167        SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1168                      (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1169        SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1170    }
1171
1172    /* now set popup position */
1173    GetWindowRect( lphc->self, &rect );
1174
1175    /*
1176     * If it's a dropdown, the listbox is offset
1177     */
1178    if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1179      rect.left += COMBO_EDITBUTTONSPACE();
1180
1181   /* if the dropped height is greater than the total height of the dropped
1182      items list, then force the drop down list height to be the total height
1183      of the items in the dropped list */
1184
1185   /* And Remove any extra space (Best Fit) */
1186    nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1187   /* if listbox length has been set directly by its handle */
1188    GetWindowRect(lphc->hWndLBox, &r);
1189    if (nDroppedHeight < r.bottom - r.top)
1190        nDroppedHeight = r.bottom - r.top;
1191    nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1192
1193    if (nItems > 0)
1194    {
1195       int nHeight;
1196
1197       nHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1198       nHeight *= nItems;
1199
1200       if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1201          nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1202    }
1203
1204    /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1205    if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1206       rect.bottom = rect.top - nDroppedHeight;
1207
1208    SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1209                  lphc->droppedRect.right - lphc->droppedRect.left,
1210                  nDroppedHeight,
1211                  SWP_NOACTIVATE | SWP_SHOWWINDOW);
1212
1213
1214    if( !(lphc->wState & CBF_NOREDRAW) )
1215      RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1216                            RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1217
1218    EnableWindow( lphc->hWndLBox, TRUE );
1219    if (GetCapture() != lphc->self)
1220       SetCapture(lphc->hWndLBox);
1221 }
1222
1223 /***********************************************************************
1224  *           CBRollUp
1225  *
1226  * Hide listbox popup.
1227  */
1228 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1229 {
1230    HWND hWnd = lphc->self;
1231
1232    TRACE("[%04x]: sel ok? [%i] dropped? [%i]\n",
1233          lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1234
1235    CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1236
1237    if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1238    {
1239
1240        if( lphc->wState & CBF_DROPPED )
1241        {
1242            RECT rect;
1243
1244            lphc->wState &= ~CBF_DROPPED;
1245            ShowWindow( lphc->hWndLBox, SW_HIDE );
1246
1247            if(GetCapture() == lphc->hWndLBox)
1248            {
1249                ReleaseCapture();
1250            }
1251
1252            if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1253            {
1254                rect = lphc->buttonRect;
1255            }
1256            else
1257            {
1258                if( bButton )
1259                {
1260                  UnionRect( &rect,
1261                             &lphc->buttonRect,
1262                             &lphc->textRect);
1263                }
1264                else
1265                  rect = lphc->textRect;
1266
1267                bButton = TRUE;
1268            }
1269
1270            if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1271                RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1272                                RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1273            CB_NOTIFY( lphc, CBN_CLOSEUP );
1274        }
1275    }
1276 }
1277
1278 /***********************************************************************
1279  *           COMBO_FlipListbox
1280  *
1281  * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1282  */
1283 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1284 {
1285    if( lphc->wState & CBF_DROPPED )
1286    {
1287        CBRollUp( lphc, ok, bRedrawButton );
1288        return FALSE;
1289    }
1290
1291    CBDropDown( lphc );
1292    return TRUE;
1293 }
1294
1295 /***********************************************************************
1296  *           CBRepaintButton
1297  */
1298 static void CBRepaintButton( LPHEADCOMBO lphc )
1299    {
1300   InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1301   UpdateWindow(lphc->self);
1302 }
1303
1304 /***********************************************************************
1305  *           COMBO_SetFocus
1306  */
1307 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1308 {
1309    if( !(lphc->wState & CBF_FOCUSED) )
1310    {
1311        if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1312            SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1313
1314        /* This is wrong. Message sequences seem to indicate that this
1315           is set *after* the notify. */
1316        /* lphc->wState |= CBF_FOCUSED;  */
1317
1318        if( !(lphc->wState & CBF_EDIT) )
1319          InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1320
1321        CB_NOTIFY( lphc, CBN_SETFOCUS );
1322        lphc->wState |= CBF_FOCUSED;
1323    }
1324 }
1325
1326 /***********************************************************************
1327  *           COMBO_KillFocus
1328  */
1329 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1330 {
1331    HWND hWnd = lphc->self;
1332
1333    if( lphc->wState & CBF_FOCUSED )
1334    {
1335        CBRollUp( lphc, FALSE, TRUE );
1336        if( IsWindow( hWnd ) )
1337        {
1338            if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1339                SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1340
1341            lphc->wState &= ~CBF_FOCUSED;
1342
1343            /* redraw text */
1344            if( !(lphc->wState & CBF_EDIT) )
1345              InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1346
1347            CB_NOTIFY( lphc, CBN_KILLFOCUS );
1348        }
1349    }
1350 }
1351
1352 /***********************************************************************
1353  *           COMBO_Command
1354  */
1355 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1356 {
1357    if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1358    {
1359        /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1360
1361        switch( HIWORD(wParam) >> 8 )
1362        {
1363            case (EN_SETFOCUS >> 8):
1364
1365                 TRACE("[%04x]: edit [%04x] got focus\n",
1366                              lphc->self, lphc->hWndEdit );
1367
1368                 COMBO_SetFocus( lphc );
1369                 break;
1370
1371            case (EN_KILLFOCUS >> 8):
1372
1373                 TRACE("[%04x]: edit [%04x] lost focus\n",
1374                              lphc->self, lphc->hWndEdit );
1375
1376                 /* NOTE: it seems that Windows' edit control sends an
1377                  * undocumented message WM_USER + 0x1B instead of this
1378                  * notification (only when it happens to be a part of
1379                  * the combo). ?? - AK.
1380                  */
1381
1382                 COMBO_KillFocus( lphc );
1383                 break;
1384
1385
1386            case (EN_CHANGE >> 8):
1387                /*
1388                 * In some circumstances (when the selection of the combobox
1389                 * is changed for example) we don't wans the EN_CHANGE notification
1390                 * to be forwarded to the parent of the combobox. This code
1391                 * checks a flag that is set in these occasions and ignores the
1392                 * notification.
1393                 */
1394                 if (lphc->wState & CBF_NOLBSELECT)
1395                 {
1396                   lphc->wState &= ~CBF_NOLBSELECT;
1397                 }
1398                 else
1399                 {
1400                   CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1401                 }
1402
1403                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1404                   CB_NOTIFY( lphc, CBN_EDITCHANGE );
1405                 break;
1406
1407            case (EN_UPDATE >> 8):
1408                 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1409                   CB_NOTIFY( lphc, CBN_EDITUPDATE );
1410                 break;
1411
1412            case (EN_ERRSPACE >> 8):
1413                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1414        }
1415    }
1416    else if( lphc->hWndLBox == hWnd )
1417    {
1418        switch( HIWORD(wParam) )
1419        {
1420            case LBN_ERRSPACE:
1421                 CB_NOTIFY( lphc, CBN_ERRSPACE );
1422                 break;
1423
1424            case LBN_DBLCLK:
1425                 CB_NOTIFY( lphc, CBN_DBLCLK );
1426                 break;
1427
1428            case LBN_SELCHANGE:
1429            case LBN_SELCANCEL:
1430
1431                 TRACE("[%04x]: lbox selection change [%04x]\n",
1432                              lphc->self, lphc->wState );
1433
1434                 if( HIWORD(wParam) == LBN_SELCHANGE)
1435                 {
1436                    if( lphc->wState & CBF_EDIT )
1437                    {
1438                        INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1439                        lphc->wState |= CBF_NOLBSELECT;
1440                        CBUpdateEdit( lphc, index );
1441                        /* select text in edit, as Windows does */
1442                        SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1443                    }
1444                    else
1445                        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1446                 }
1447
1448                 /* do not roll up if selection is being tracked
1449                  * by arrowkeys in the dropdown listbox */
1450                 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1451                 {
1452                    CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1453                 }
1454                 else lphc->wState &= ~CBF_NOROLLUP;
1455
1456                 CB_NOTIFY( lphc, CBN_SELCHANGE );
1457
1458                 /* fall through */
1459
1460            case LBN_SETFOCUS:
1461            case LBN_KILLFOCUS:
1462                 /* nothing to do here since ComboLBox always resets the focus to its
1463                  * combo/edit counterpart */
1464                  break;
1465        }
1466    }
1467    return 0;
1468 }
1469
1470 /***********************************************************************
1471  *           COMBO_ItemOp
1472  *
1473  * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1474  */
1475 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1476 {
1477    HWND hWnd = lphc->self;
1478    UINT id = GetWindowLongA( hWnd, GWL_ID );
1479
1480    TRACE("[%04x]: ownerdraw op %04x\n", lphc->self, msg );
1481
1482    switch( msg )
1483    {
1484    case WM_DELETEITEM:
1485        {
1486            DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1487            lpIS->CtlType  = ODT_COMBOBOX;
1488            lpIS->CtlID    = id;
1489            lpIS->hwndItem = hWnd;
1490            break;
1491        }
1492    case WM_DRAWITEM:
1493        {
1494            DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1495            lpIS->CtlType  = ODT_COMBOBOX;
1496            lpIS->CtlID    = id;
1497            lpIS->hwndItem = hWnd;
1498            break;
1499        }
1500    case WM_COMPAREITEM:
1501        {
1502            COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1503            lpIS->CtlType  = ODT_COMBOBOX;
1504            lpIS->CtlID    = id;
1505            lpIS->hwndItem = hWnd;
1506            break;
1507        }
1508    case WM_MEASUREITEM:
1509        {
1510            MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1511            lpIS->CtlType  = ODT_COMBOBOX;
1512            lpIS->CtlID    = id;
1513            break;
1514        }
1515    }
1516    return SendMessageW(lphc->owner, msg, id, lParam);
1517 }
1518
1519 /***********************************************************************
1520  *           COMBO_GetText
1521  *
1522  * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1523  *       also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1524  */
1525 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, INT N, LPARAM lParam, BOOL unicode)
1526 {
1527    if( lphc->wState & CBF_EDIT )
1528        return unicode ? SendMessageW(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam) :
1529                         SendMessageA(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam);
1530
1531    /* get it from the listbox */
1532
1533    if( lphc->hWndLBox )
1534    {
1535        INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1536        if( idx != LB_ERR )
1537        {
1538            INT n = 0;
1539            INT length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN,
1540                                                 (WPARAM)idx, 0 );
1541
1542             if(unicode)
1543             {
1544                 LPWSTR lpBuffer, lpText = (LPWSTR)lParam;
1545
1546                 /* 'length' is without the terminating character */
1547                 if(length >= N)
1548                     lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1549                 else
1550                     lpBuffer = lpText;
1551
1552                 if(lpBuffer)
1553                 {
1554                     n = SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1555
1556                     /* truncate if buffer is too short */
1557                     if(length >= N)
1558                     {
1559                         if(N && lpText)
1560                         {
1561                             if(n != LB_ERR)
1562                                 strncpyW(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1563                             lpText[N - 1] = '\0';
1564                         }
1565                         HeapFree( GetProcessHeap(), 0, lpBuffer );
1566                    }
1567                }
1568            }
1569            else
1570            {
1571                 LPSTR lpBuffer, lpText = (LPSTR)lParam;
1572
1573                 /* 'length' is without the terminating character */
1574                 if(length >= N)
1575                     lpBuffer = HeapAlloc(GetProcessHeap(), 0, length + 1);
1576                 else
1577                     lpBuffer = lpText;
1578
1579                 if(lpBuffer)
1580                 {
1581                     n = SendMessageA(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1582
1583                     /* truncate if buffer is too short */
1584                     if(length >= N)
1585                     {
1586                         if(N && lpText)
1587                         {
1588                             if(n != LB_ERR)
1589                                 strncpy(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1590                             lpText[N - 1] = '\0';
1591                         }
1592                         HeapFree( GetProcessHeap(), 0, lpBuffer );
1593                    }
1594                }
1595            }
1596            if (n<0)
1597                n=0;
1598            else
1599                n++;
1600            return (LRESULT)n;
1601        }
1602    }
1603    return 0;
1604 }
1605
1606
1607 /***********************************************************************
1608  *           CBResetPos
1609  *
1610  * This function sets window positions according to the updated
1611  * component placement struct.
1612  */
1613 static void CBResetPos(
1614   LPHEADCOMBO lphc,
1615   LPRECT      rectEdit,
1616   LPRECT      rectLB,
1617   BOOL        bRedraw)
1618 {
1619    BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1620
1621    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1622     * sizing messages */
1623
1624    if( lphc->wState & CBF_EDIT )
1625      SetWindowPos( lphc->hWndEdit, 0,
1626                    rectEdit->left, rectEdit->top,
1627                    rectEdit->right - rectEdit->left,
1628                    rectEdit->bottom - rectEdit->top,
1629                        SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1630
1631    SetWindowPos( lphc->hWndLBox, 0,
1632                  rectLB->left, rectLB->top,
1633                  rectLB->right - rectLB->left,
1634                  rectLB->bottom - rectLB->top,
1635                    SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1636
1637    if( bDrop )
1638    {
1639        if( lphc->wState & CBF_DROPPED )
1640        {
1641            lphc->wState &= ~CBF_DROPPED;
1642            ShowWindow( lphc->hWndLBox, SW_HIDE );
1643        }
1644
1645        if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1646            RedrawWindow( lphc->self, NULL, 0,
1647                            RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1648    }
1649 }
1650
1651
1652 /***********************************************************************
1653  *           COMBO_Size
1654  */
1655 static void COMBO_Size( LPHEADCOMBO lphc )
1656   {
1657   CBCalcPlacement(lphc->self,
1658                   lphc,
1659                   &lphc->textRect,
1660                   &lphc->buttonRect,
1661                   &lphc->droppedRect);
1662
1663   CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1664 }
1665
1666
1667 /***********************************************************************
1668  *           COMBO_Font
1669  */
1670 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1671 {
1672   /*
1673    * Set the font
1674    */
1675   lphc->hFont = hFont;
1676
1677   /*
1678    * Propagate to owned windows.
1679    */
1680   if( lphc->wState & CBF_EDIT )
1681       SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1682   SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1683
1684   /*
1685    * Redo the layout of the control.
1686    */
1687   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1688   {
1689     CBCalcPlacement(lphc->self,
1690                     lphc,
1691                     &lphc->textRect,
1692                     &lphc->buttonRect,
1693                     &lphc->droppedRect);
1694
1695     CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1696   }
1697   else
1698   {
1699     CBForceDummyResize(lphc);
1700   }
1701 }
1702
1703
1704 /***********************************************************************
1705  *           COMBO_SetItemHeight
1706  */
1707 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1708 {
1709    LRESULT      lRet = CB_ERR;
1710
1711    if( index == -1 ) /* set text field height */
1712    {
1713        if( height < 32768 )
1714        {
1715            lphc->editHeight = height;
1716
1717          /*
1718           * Redo the layout of the control.
1719           */
1720          if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1721          {
1722            CBCalcPlacement(lphc->self,
1723                            lphc,
1724                            &lphc->textRect,
1725                            &lphc->buttonRect,
1726                            &lphc->droppedRect);
1727
1728            CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1729          }
1730          else
1731          {
1732            CBForceDummyResize(lphc);
1733          }
1734
1735            lRet = height;
1736        }
1737    }
1738    else if ( CB_OWNERDRAWN(lphc) )      /* set listbox item height */
1739         lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1740                               (WPARAM)index, (LPARAM)height );
1741    return lRet;
1742 }
1743
1744 /***********************************************************************
1745  *           COMBO_SelectString
1746  */
1747 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1748 {
1749    INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1750                          SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1751    if( index >= 0 )
1752    {
1753      if( lphc->wState & CBF_EDIT )
1754        CBUpdateEdit( lphc, index );
1755      else
1756      {
1757        InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1758      }
1759    }
1760    return (LRESULT)index;
1761 }
1762
1763 /***********************************************************************
1764  *           COMBO_LButtonDown
1765  */
1766 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1767 {
1768    POINT     pt;
1769    BOOL      bButton;
1770    HWND      hWnd = lphc->self;
1771
1772    pt.x = LOWORD(lParam);
1773    pt.y = HIWORD(lParam);
1774    bButton = PtInRect(&lphc->buttonRect, pt);
1775
1776    if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1777        (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1778    {
1779        lphc->wState |= CBF_BUTTONDOWN;
1780        if( lphc->wState & CBF_DROPPED )
1781        {
1782            /* got a click to cancel selection */
1783
1784            lphc->wState &= ~CBF_BUTTONDOWN;
1785            CBRollUp( lphc, TRUE, FALSE );
1786            if( !IsWindow( hWnd ) ) return;
1787
1788            if( lphc->wState & CBF_CAPTURE )
1789            {
1790                lphc->wState &= ~CBF_CAPTURE;
1791                ReleaseCapture();
1792            }
1793        }
1794        else
1795        {
1796            /* drop down the listbox and start tracking */
1797
1798            lphc->wState |= CBF_CAPTURE;
1799            SetCapture( hWnd );
1800            CBDropDown( lphc );
1801        }
1802        if( bButton ) CBRepaintButton( lphc );
1803    }
1804 }
1805
1806 /***********************************************************************
1807  *           COMBO_LButtonUp
1808  *
1809  * Release capture and stop tracking if needed.
1810  */
1811 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1812 {
1813    if( lphc->wState & CBF_CAPTURE )
1814    {
1815        lphc->wState &= ~CBF_CAPTURE;
1816        if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1817        {
1818            INT index = CBUpdateLBox( lphc, TRUE );
1819            /* Update edit only if item is in the list */
1820            if(index >= 0)
1821            {
1822                lphc->wState |= CBF_NOLBSELECT;
1823                CBUpdateEdit( lphc, index );
1824                lphc->wState &= ~CBF_NOLBSELECT;
1825            }
1826        }
1827        ReleaseCapture();
1828        SetCapture(lphc->hWndLBox);
1829    }
1830
1831    if( lphc->wState & CBF_BUTTONDOWN )
1832    {
1833        lphc->wState &= ~CBF_BUTTONDOWN;
1834        CBRepaintButton( lphc );
1835    }
1836 }
1837
1838 /***********************************************************************
1839  *           COMBO_MouseMove
1840  *
1841  * Two things to do - track combo button and release capture when
1842  * pointer goes into the listbox.
1843  */
1844 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1845 {
1846    POINT  pt;
1847    RECT   lbRect;
1848
1849    pt.x = LOWORD(lParam);
1850    pt.y = HIWORD(lParam);
1851
1852    if( lphc->wState & CBF_BUTTONDOWN )
1853    {
1854      BOOL bButton;
1855
1856      bButton = PtInRect(&lphc->buttonRect, pt);
1857
1858      if( !bButton )
1859      {
1860        lphc->wState &= ~CBF_BUTTONDOWN;
1861        CBRepaintButton( lphc );
1862      }
1863    }
1864
1865    GetClientRect( lphc->hWndLBox, &lbRect );
1866    MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1867    if( PtInRect(&lbRect, pt) )
1868    {
1869        lphc->wState &= ~CBF_CAPTURE;
1870        ReleaseCapture();
1871        if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1872
1873        /* hand over pointer tracking */
1874        SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1875    }
1876 }
1877
1878
1879 /***********************************************************************
1880  *           ComboWndProc_common
1881  *
1882  * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1883  */
1884 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1885                                     WPARAM wParam, LPARAM lParam, BOOL unicode )
1886 {
1887       LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
1888
1889       TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1890             hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1891
1892       if( lphc || message == WM_NCCREATE )
1893       switch(message)
1894       {
1895
1896         /* System messages */
1897
1898         case WM_NCCREATE:
1899         {
1900                 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1901                                        ((LPCREATESTRUCTA)lParam)->style;
1902                 return COMBO_NCCreate(hwnd, style);
1903         }
1904         case WM_NCDESTROY:
1905                 COMBO_NCDestroy(lphc);
1906                 break;/* -> DefWindowProc */
1907
1908         case WM_CREATE:
1909         {
1910                 HWND hwndParent;
1911                 LONG style;
1912                 if(unicode)
1913                 {
1914                     hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1915                     style = ((LPCREATESTRUCTW)lParam)->style;
1916                 }
1917                 else
1918                 {
1919                     hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1920                     style = ((LPCREATESTRUCTA)lParam)->style;
1921                 }
1922                 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1923         }
1924
1925         case WM_PRINTCLIENT:
1926                 if (lParam & PRF_ERASEBKGND)
1927                   COMBO_EraseBackground(hwnd, lphc, wParam);
1928
1929                 /* Fallthrough */
1930         case WM_PAINT:
1931                 /* wParam may contain a valid HDC! */
1932                 return  COMBO_Paint(lphc, wParam);
1933         case WM_ERASEBKGND:
1934                 return  COMBO_EraseBackground(hwnd, lphc, wParam);
1935         case WM_GETDLGCODE:
1936         {
1937                 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1938                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1939                 {
1940                    int vk = (int)((LPMSG)lParam)->wParam;
1941
1942                    if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1943                        result |= DLGC_WANTMESSAGE;
1944                 }
1945                 return  result;
1946         }
1947         case WM_WINDOWPOSCHANGING:
1948                 return  COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1949     case WM_WINDOWPOSCHANGED:
1950         /* SetWindowPos can be called on a Combobox to resize its Listbox.
1951          * In that case, the Combobox itself will not be resized, so we won't
1952          * get a WM_SIZE. Since we still want to update the Listbox, we have to
1953          * do it here.
1954          */
1955         /* fall through */
1956         case WM_SIZE:
1957                 if( lphc->hWndLBox &&
1958                   !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1959                 return  TRUE;
1960         case WM_SETFONT:
1961                 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1962                 return  TRUE;
1963         case WM_GETFONT:
1964                 return  (LRESULT)lphc->hFont;
1965         case WM_SETFOCUS:
1966                 if( lphc->wState & CBF_EDIT )
1967                     SetFocus( lphc->hWndEdit );
1968                 else
1969                     COMBO_SetFocus( lphc );
1970                 return  TRUE;
1971         case WM_KILLFOCUS:
1972             {
1973                 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1974                 if( !hwndFocus ||
1975                     (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1976                     COMBO_KillFocus( lphc );
1977                 return  TRUE;
1978             }
1979         case WM_COMMAND:
1980                 return  COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1981         case WM_GETTEXT:
1982                 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1983         case WM_SETTEXT:
1984         case WM_GETTEXTLENGTH:
1985         case WM_CLEAR:
1986                 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1987                 {
1988                     int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1989                     if (j == -1) return 0;
1990                     return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1991                                      SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1992                 }
1993                 else if( lphc->wState & CBF_EDIT )
1994                 {
1995                     LRESULT ret;
1996                     lphc->wState |= CBF_NOEDITNOTIFY;
1997                     ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1998                                     SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1999                     lphc->wState &= ~CBF_NOEDITNOTIFY;
2000                     return ret;
2001                 }
2002                 else return CB_ERR;
2003         case WM_CUT:
2004         case WM_PASTE:
2005         case WM_COPY:
2006                 if( lphc->wState & CBF_EDIT )
2007                 {
2008                     return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
2009                                      SendMessageA(lphc->hWndEdit, message, wParam, lParam);
2010                 }
2011                 else return  CB_ERR;
2012
2013         case WM_DRAWITEM:
2014         case WM_DELETEITEM:
2015         case WM_COMPAREITEM:
2016         case WM_MEASUREITEM:
2017                 return COMBO_ItemOp(lphc, message, lParam);
2018         case WM_ENABLE:
2019                 if( lphc->wState & CBF_EDIT )
2020                     EnableWindow( lphc->hWndEdit, (BOOL)wParam );
2021                 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
2022
2023                 /* Force the control to repaint when the enabled state changes. */
2024                 InvalidateRect(lphc->self, NULL, TRUE);
2025                 return  TRUE;
2026         case WM_SETREDRAW:
2027                 if( wParam )
2028                     lphc->wState &= ~CBF_NOREDRAW;
2029                 else
2030                     lphc->wState |= CBF_NOREDRAW;
2031
2032                 if( lphc->wState & CBF_EDIT )
2033                     SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2034                 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2035                 return  0;
2036         case WM_SYSKEYDOWN:
2037                 if( KEYDATA_ALT & HIWORD(lParam) )
2038                     if( wParam == VK_UP || wParam == VK_DOWN )
2039                         COMBO_FlipListbox( lphc, FALSE, FALSE );
2040                 return  0;
2041
2042         case WM_CHAR:
2043         case WM_KEYDOWN:
2044         {
2045                 HWND hwndTarget;
2046
2047                 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2048                      (lphc->wState & CBF_DROPPED))
2049                 {
2050                    CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2051                    return TRUE;
2052                 }
2053
2054                 if( lphc->wState & CBF_EDIT )
2055                     hwndTarget = lphc->hWndEdit;
2056                 else
2057                     hwndTarget = lphc->hWndLBox;
2058
2059                 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2060                                  SendMessageA(hwndTarget, message, wParam, lParam);
2061         }
2062         case WM_LBUTTONDOWN:
2063                 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2064                 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2065                 return  TRUE;
2066         case WM_LBUTTONUP:
2067                 COMBO_LButtonUp( lphc );
2068                 return  TRUE;
2069         case WM_MOUSEMOVE:
2070                 if( lphc->wState & CBF_CAPTURE )
2071                     COMBO_MouseMove( lphc, wParam, lParam );
2072                 return  TRUE;
2073
2074         case WM_MOUSEWHEEL:
2075                 if (wParam & (MK_SHIFT | MK_CONTROL))
2076                     return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2077                                      DefWindowProcA(hwnd, message, wParam, lParam);
2078                 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2079                 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2080                 return TRUE;
2081
2082         /* Combo messages */
2083
2084         case CB_ADDSTRING16:
2085                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2086                 /* fall through */
2087         case CB_ADDSTRING:
2088                 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2089                                  SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2090         case CB_INSERTSTRING16:
2091                 wParam = (INT)(INT16)wParam;
2092                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2093                 /* fall through */
2094         case CB_INSERTSTRING:
2095                 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2096                                  SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2097         case CB_DELETESTRING16:
2098         case CB_DELETESTRING:
2099                 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2100                                  SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2101         case CB_SELECTSTRING16:
2102                 wParam = (INT)(INT16)wParam;
2103                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2104                 /* fall through */
2105         case CB_SELECTSTRING:
2106                 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2107         case CB_FINDSTRING16:
2108                 wParam = (INT)(INT16)wParam;
2109                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2110                 /* fall through */
2111         case CB_FINDSTRING:
2112                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2113                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2114         case CB_FINDSTRINGEXACT16:
2115                 wParam = (INT)(INT16)wParam;
2116                 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2117                 /* fall through */
2118         case CB_FINDSTRINGEXACT:
2119                 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2120                                  SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2121         case CB_SETITEMHEIGHT16:
2122                 wParam = (INT)(INT16)wParam;    /* signed integer */
2123                 /* fall through */
2124         case CB_SETITEMHEIGHT:
2125                 return  COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2126         case CB_GETITEMHEIGHT16:
2127                 wParam = (INT)(INT16)wParam;
2128                 /* fall through */
2129         case CB_GETITEMHEIGHT:
2130                 if( (INT)wParam >= 0 )  /* listbox item */
2131                     return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2132                 return  CBGetTextAreaHeight(hwnd, lphc);
2133         case CB_RESETCONTENT16:
2134         case CB_RESETCONTENT:
2135                 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2136                 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2137                 {
2138                     static const WCHAR empty_stringW[] = { 0 };
2139                     SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2140                 }
2141                 else
2142                     InvalidateRect(lphc->self, NULL, TRUE);
2143                 return  TRUE;
2144         case CB_INITSTORAGE:
2145                 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2146         case CB_GETHORIZONTALEXTENT:
2147                 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2148         case CB_SETHORIZONTALEXTENT:
2149                 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2150         case CB_GETTOPINDEX:
2151                 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2152         case CB_GETLOCALE:
2153                 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2154         case CB_SETLOCALE:
2155                 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2156         case CB_GETDROPPEDWIDTH:
2157                 if( lphc->droppedWidth )
2158                     return  lphc->droppedWidth;
2159                 return  lphc->droppedRect.right - lphc->droppedRect.left;
2160         case CB_SETDROPPEDWIDTH:
2161                 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2162                     (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2163                 return  CB_ERR;
2164         case CB_GETDROPPEDCONTROLRECT16:
2165                 lParam = (LPARAM)MapSL(lParam);
2166                 if( lParam )
2167                 {
2168                     RECT        r;
2169                     CBGetDroppedControlRect( lphc, &r );
2170                     CONV_RECT32TO16( &r, (LPRECT16)lParam );
2171                 }
2172                 return  CB_OKAY;
2173         case CB_GETDROPPEDCONTROLRECT:
2174                 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2175                 return  CB_OKAY;
2176         case CB_GETDROPPEDSTATE16:
2177         case CB_GETDROPPEDSTATE:
2178                 return  (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2179         case CB_DIR16:
2180                 lParam = (LPARAM)MapSL(lParam);
2181                 message = LB_DIR16;
2182                 /* fall through */
2183         case CB_DIR:
2184                 if(message == CB_DIR) message = LB_DIR;
2185                 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2186                                  SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2187
2188         case CB_SHOWDROPDOWN16:
2189         case CB_SHOWDROPDOWN:
2190                 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2191                 {
2192                     if( wParam )
2193                     {
2194                         if( !(lphc->wState & CBF_DROPPED) )
2195                             CBDropDown( lphc );
2196                     }
2197                     else
2198                         if( lphc->wState & CBF_DROPPED )
2199                             CBRollUp( lphc, FALSE, TRUE );
2200                 }
2201                 return  TRUE;
2202         case CB_GETCOUNT16:
2203         case CB_GETCOUNT:
2204                 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2205         case CB_GETCURSEL16:
2206         case CB_GETCURSEL:
2207                 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2208         case CB_SETCURSEL16:
2209                 wParam = (INT)(INT16)wParam;
2210                 /* fall through */
2211         case CB_SETCURSEL:
2212                 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2213                 if( lParam >= 0 )
2214                     SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2215
2216                 /* no LBN_SELCHANGE in this case, update manually */
2217                 if( lphc->wState & CBF_EDIT )
2218                     CBUpdateEdit( lphc, (INT)wParam );
2219                 else
2220                     InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2221                 lphc->wState &= ~CBF_SELCHANGE;
2222                 return  lParam;
2223         case CB_GETLBTEXT16:
2224                 wParam = (INT)(INT16)wParam;
2225                 lParam = (LPARAM)MapSL(lParam);
2226                 /* fall through */
2227         case CB_GETLBTEXT:
2228                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2229                                  SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2230         case CB_GETLBTEXTLEN16:
2231                 wParam = (INT)(INT16)wParam;
2232                 /* fall through */
2233         case CB_GETLBTEXTLEN:
2234                 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2235                                  SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2236         case CB_GETITEMDATA16:
2237                 wParam = (INT)(INT16)wParam;
2238                 /* fall through */
2239         case CB_GETITEMDATA:
2240                 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2241         case CB_SETITEMDATA16:
2242                 wParam = (INT)(INT16)wParam;
2243                 /* fall through */
2244         case CB_SETITEMDATA:
2245                 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2246         case CB_GETEDITSEL16:
2247                 wParam = lParam = 0;   /* just in case */
2248                 /* fall through */
2249         case CB_GETEDITSEL:
2250                 /* Edit checks passed parameters itself */
2251                 if( lphc->wState & CBF_EDIT )
2252                     return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2253                 return  CB_ERR;
2254         case CB_SETEDITSEL16:
2255         case CB_SETEDITSEL:
2256                 if( lphc->wState & CBF_EDIT )
2257                     return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2258                           (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2259                 return  CB_ERR;
2260         case CB_SETEXTENDEDUI16:
2261         case CB_SETEXTENDEDUI:
2262                 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2263                     return  CB_ERR;
2264                 if( wParam )
2265                     lphc->wState |= CBF_EUI;
2266                 else lphc->wState &= ~CBF_EUI;
2267                 return  CB_OKAY;
2268         case CB_GETEXTENDEDUI16:
2269         case CB_GETEXTENDEDUI:
2270                 return  (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2271
2272         default:
2273                 if (message >= WM_USER)
2274                     WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2275                         message - WM_USER, wParam, lParam );
2276                 break;
2277       }
2278       return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2279                        DefWindowProcA(hwnd, message, wParam, lParam);
2280 }
2281
2282 /***********************************************************************
2283  *           ComboWndProcA
2284  *
2285  * This is just a wrapper for the real ComboWndProc which locks/unlocks
2286  * window structs.
2287  */
2288 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2289 {
2290     if (!IsWindow(hwnd)) return 0;
2291     return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2292 }
2293
2294 /***********************************************************************
2295  *           ComboWndProcW
2296  */
2297 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2298 {
2299     if (!IsWindow(hwnd)) return 0;
2300     return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2301 }