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