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