4 * Copyright 1997 Alex Korobka
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.
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.
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
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 4, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
30 * - ComboBox_[GS]etMinVisible()
31 * - CB_GETMINVISIBLE, CB_SETMINVISIBLE
44 #include "wine/winuser16.h"
45 #include "wine/unicode.h"
46 #include "user_private.h"
50 #include "wine/debug.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(combo);
54 /* bits in the dwKeyData */
55 #define KEYDATA_ALT 0x2000
56 #define KEYDATA_PREVSTATE 0x4000
59 * Additional combo box definitions
62 #define CB_NOTIFY( lphc, code ) \
63 (SendMessageW((lphc)->owner, WM_COMMAND, \
64 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
66 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
67 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
68 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
69 #define CB_HWND( lphc ) ((lphc)->self)
70 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
72 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
77 static HBITMAP hComboBmp = 0;
78 static UINT CBitHeight, CBitWidth;
81 * Look and feel dependent "constants"
84 #define COMBO_YBORDERGAP 5
85 #define COMBO_XBORDERSIZE() 2
86 #define COMBO_YBORDERSIZE() 2
87 #define COMBO_EDITBUTTONSPACE() 0
88 #define EDIT_CONTROL_PADDING() 1
90 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
91 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
93 /*********************************************************************
94 * combo class descriptor
96 static const WCHAR comboboxW[] = {'C','o','m','b','o','B','o','x',0};
97 const struct builtin_class_descr COMBO_builtin_class =
100 CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */
101 ComboWndProcA, /* procA */
102 ComboWndProcW, /* procW */
103 sizeof(HEADCOMBO *), /* extra */
104 IDC_ARROW, /* cursor */
109 /***********************************************************************
112 * Load combo button bitmap.
114 static BOOL COMBO_Init(void)
118 if( hComboBmp ) return TRUE;
119 if( (hDC = CreateCompatibleDC(0)) )
122 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
128 GetObjectW( hComboBmp, sizeof(bm), &bm );
129 CBitHeight = bm.bmHeight;
130 CBitWidth = bm.bmWidth;
132 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
134 hPrevB = SelectObject( hDC, hComboBmp);
135 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
136 InvertRect( hDC, &r );
137 SelectObject( hDC, hPrevB );
146 /***********************************************************************
149 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
153 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
156 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
158 /* some braindead apps do try to use scrollbar/border flags */
160 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
161 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
164 * We also have to remove the client edge style to make sure
165 * we don't end-up with a non client area.
167 SetWindowLongW( hwnd, GWL_EXSTYLE,
168 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
170 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
171 lphc->dwStyle |= CBS_HASSTRINGS;
172 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
173 lphc->wState |= CBF_NOTIFY;
175 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
181 /***********************************************************************
184 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
189 TRACE("[%p]: freeing storage\n", lphc->self);
191 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
192 DestroyWindow( lphc->hWndLBox );
194 SetWindowLongPtrW( lphc->self, 0, 0 );
195 HeapFree( GetProcessHeap(), 0, lphc );
200 /***********************************************************************
201 * CBGetTextAreaHeight
203 * This method will calculate the height of the text area of the
205 * The height of the text area is set in two ways.
206 * It can be set explicitly through a combobox message or through a
207 * WM_MEASUREITEM callback.
208 * If this is not the case, the height is set to font height + 4px
209 * This height was determined through experimentation.
210 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
212 static INT CBGetTextAreaHeight(
218 if( lphc->editHeight ) /* explicitly set height */
220 iTextItemHeight = lphc->editHeight;
225 HDC hDC = GetDC(hwnd);
230 hPrevFont = SelectObject( hDC, lphc->hFont );
232 GetTextMetricsW(hDC, &tm);
234 baseUnitY = tm.tmHeight;
237 SelectObject( hDC, hPrevFont );
239 ReleaseDC(hwnd, hDC);
241 iTextItemHeight = baseUnitY + 4;
245 * Check the ownerdraw case if we haven't asked the parent the size
248 if ( CB_OWNERDRAWN(lphc) &&
249 (lphc->wState & CBF_MEASUREITEM) )
251 MEASUREITEMSTRUCT measureItem;
253 INT originalItemHeight = iTextItemHeight;
254 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
257 * We use the client rect for the width of the item.
259 GetClientRect(hwnd, &clientRect);
261 lphc->wState &= ~CBF_MEASUREITEM;
264 * Send a first one to measure the size of the text area
266 measureItem.CtlType = ODT_COMBOBOX;
267 measureItem.CtlID = id;
268 measureItem.itemID = -1;
269 measureItem.itemWidth = clientRect.right;
270 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
271 measureItem.itemData = 0;
272 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
273 iTextItemHeight = 6 + measureItem.itemHeight;
276 * Send a second one in the case of a fixed ownerdraw list to calculate the
277 * size of the list items. (we basically do this on behalf of the listbox)
279 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
281 measureItem.CtlType = ODT_COMBOBOX;
282 measureItem.CtlID = id;
283 measureItem.itemID = 0;
284 measureItem.itemWidth = clientRect.right;
285 measureItem.itemHeight = originalItemHeight;
286 measureItem.itemData = 0;
287 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
288 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
292 * Keep the size for the next time
294 lphc->editHeight = iTextItemHeight;
297 return iTextItemHeight;
300 /***********************************************************************
303 * The dummy resize is used for listboxes that have a popup to trigger
304 * a re-arranging of the contents of the combobox and the recalculation
305 * of the size of the "real" control window.
307 static void CBForceDummyResize(
313 newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
315 GetWindowRect(lphc->self, &windowRect);
318 * We have to be careful, resizing a combobox also has the meaning that the
319 * dropped rect will be resized. In this case, we want to trigger a resize
320 * to recalculate layout but we don't want to change the dropped rectangle
321 * So, we pass the height of text area of control as the height.
322 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
325 SetWindowPos( lphc->self,
328 windowRect.right - windowRect.left,
330 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
333 /***********************************************************************
336 * Set up component coordinates given valid lphc->RectCombo.
338 static void CBCalcPlacement(
346 * Again, start with the client rectangle.
348 GetClientRect(hwnd, lprEdit);
353 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
356 * Chop off the bottom part to fit with the height of the text area.
358 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
361 * The button starts the same vertical position as the text area.
363 CopyRect(lprButton, lprEdit);
366 * If the combobox is "simple" there is no button.
368 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
369 lprButton->left = lprButton->right = lprButton->bottom = 0;
373 * Let's assume the combobox button is the same width as the
375 * size the button horizontally and cut-off the text area.
377 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
378 lprEdit->right = lprButton->left;
382 * In the case of a dropdown, there is an additional spacing between the
383 * text area and the button.
385 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
387 lprEdit->right -= COMBO_EDITBUTTONSPACE();
391 * If we have an edit control, we space it away from the borders slightly.
393 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
395 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
399 * Adjust the size of the listbox popup.
401 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
404 * Use the client rectangle to initialize the listbox rectangle
406 GetClientRect(hwnd, lprLB);
409 * Then, chop-off the top part.
411 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
416 * Make sure the dropped width is as large as the combobox itself.
418 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
420 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
423 * In the case of a dropdown, the popup listbox is offset to the right.
424 * so, we want to make sure it's flush with the right side of the
427 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
428 lprLB->right -= COMBO_EDITBUTTONSPACE();
431 lprLB->right = lprLB->left + lphc->droppedWidth;
434 /* don't allow negative window width */
435 if (lprEdit->right < lprEdit->left)
436 lprEdit->right = lprEdit->left;
438 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
440 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
442 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
445 /***********************************************************************
446 * CBGetDroppedControlRect
448 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
450 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
451 of the combo box and the lower right corner of the listbox */
453 GetWindowRect(lphc->self, lpRect);
455 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
456 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
460 /***********************************************************************
461 * COMBO_WindowPosChanging
463 static LRESULT COMBO_WindowPosChanging(
466 WINDOWPOS* posChanging)
469 * We need to override the WM_WINDOWPOSCHANGING method to handle all
470 * the non-simple comboboxes. The problem is that those controls are
471 * always the same height. We have to make sure they are not resized
474 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
475 ((posChanging->flags & SWP_NOSIZE) == 0) )
479 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
480 2*COMBO_YBORDERSIZE();
483 * Resizing a combobox has another side effect, it resizes the dropped
484 * rectangle as well. However, it does it only if the new height for the
485 * combobox is different from the height it should have. In other words,
486 * if the application resizing the combobox only had the intention to resize
487 * the actual control, for example, to do the layout of a dialog that is
488 * resized, the height of the dropdown is not changed.
490 if (posChanging->cy != newComboHeight)
492 TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
493 posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
494 lphc->droppedRect.top);
495 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
497 posChanging->cy = newComboHeight;
504 /***********************************************************************
507 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
510 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
511 static const WCHAR editName[] = {'E','d','i','t',0};
513 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
514 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
516 lphc->owner = hwndParent;
519 * The item height and dropped width are not set when the control
522 lphc->droppedWidth = lphc->editHeight = 0;
525 * The first time we go through, we want to measure the ownerdraw item
527 lphc->wState |= CBF_MEASUREITEM;
529 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
531 if( lphc->owner || !(style & WS_VISIBLE) )
537 * Initialize the dropped rect to the size of the client area of the
538 * control and then, force all the areas of the combobox to be
541 GetClientRect( hwnd, &lphc->droppedRect );
542 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
545 * Adjust the position of the popup listbox if it's necessary
547 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
549 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
552 * If it's a dropdown, the listbox is offset
554 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
555 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
557 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
558 lphc->droppedRect.bottom = lphc->droppedRect.top;
559 if (lphc->droppedRect.right < lphc->droppedRect.left)
560 lphc->droppedRect.right = lphc->droppedRect.left;
561 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
564 /* create listbox popup */
566 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
567 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
569 if( lphc->dwStyle & CBS_SORT )
570 lbeStyle |= LBS_SORT;
571 if( lphc->dwStyle & CBS_HASSTRINGS )
572 lbeStyle |= LBS_HASSTRINGS;
573 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
574 lbeStyle |= LBS_NOINTEGRALHEIGHT;
575 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
576 lbeStyle |= LBS_DISABLENOSCROLL;
578 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
580 lbeStyle |= WS_VISIBLE;
583 * In win 95 look n feel, the listbox in the simple combobox has
584 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
586 lbeStyle &= ~WS_BORDER;
587 lbeExStyle |= WS_EX_CLIENTEDGE;
591 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
595 lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
596 lphc->droppedRect.left,
597 lphc->droppedRect.top,
598 lphc->droppedRect.right - lphc->droppedRect.left,
599 lphc->droppedRect.bottom - lphc->droppedRect.top,
600 hwnd, (HMENU)ID_CB_LISTBOX,
601 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
603 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
604 lphc->droppedRect.left,
605 lphc->droppedRect.top,
606 lphc->droppedRect.right - lphc->droppedRect.left,
607 lphc->droppedRect.bottom - lphc->droppedRect.top,
608 hwnd, (HMENU)ID_CB_LISTBOX,
609 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
614 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
616 if( lphc->wState & CBF_EDIT )
618 if( lphc->dwStyle & CBS_OEMCONVERT )
619 lbeStyle |= ES_OEMCONVERT;
620 if( lphc->dwStyle & CBS_AUTOHSCROLL )
621 lbeStyle |= ES_AUTOHSCROLL;
622 if( lphc->dwStyle & CBS_LOWERCASE )
623 lbeStyle |= ES_LOWERCASE;
624 else if( lphc->dwStyle & CBS_UPPERCASE )
625 lbeStyle |= ES_UPPERCASE;
627 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
630 lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
631 lphc->textRect.left, lphc->textRect.top,
632 lphc->textRect.right - lphc->textRect.left,
633 lphc->textRect.bottom - lphc->textRect.top,
634 hwnd, (HMENU)ID_CB_EDIT,
635 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
637 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
638 lphc->textRect.left, lphc->textRect.top,
639 lphc->textRect.right - lphc->textRect.left,
640 lphc->textRect.bottom - lphc->textRect.top,
641 hwnd, (HMENU)ID_CB_EDIT,
642 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
644 if( !lphc->hWndEdit )
650 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
652 /* Now do the trick with parent */
653 SetParent(lphc->hWndLBox, HWND_DESKTOP);
655 * If the combo is a dropdown, we must resize the control
656 * to fit only the text area and button. To do this,
657 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
658 * will take care of setting the height for us.
660 CBForceDummyResize(lphc);
663 TRACE("init done\n");
666 ERR("edit control failure.\n");
667 } else ERR("listbox failure.\n");
668 } else ERR("no owner for visible combo.\n");
670 /* CreateWindow() will send WM_NCDESTROY to cleanup */
675 /***********************************************************************
678 * Paint combo button (normal, pressed, and disabled states).
680 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
682 UINT buttonState = DFCS_SCROLLCOMBOBOX;
684 if( lphc->wState & CBF_NOREDRAW )
688 if (lphc->wState & CBF_BUTTONDOWN)
689 buttonState |= DFCS_PUSHED;
691 if (CB_DISABLED(lphc))
692 buttonState |= DFCS_INACTIVE;
694 DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
697 /***********************************************************************
700 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
702 static void CBPaintText(
710 if( lphc->wState & CBF_NOREDRAW ) return;
714 /* follow Windows combobox that sends a bunch of text
715 * inquiries to its listbox while processing WM_PAINT. */
717 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
719 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
721 FIXME("LB_ERR probably not handled yet\n");
722 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
724 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
725 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
726 pText[size] = '\0'; /* just in case */
730 if( !CB_OWNERDRAWN(lphc) )
733 if( lphc->wState & CBF_EDIT )
735 static const WCHAR empty_stringW[] = { 0 };
736 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
737 if( lphc->wState & CBF_FOCUSED )
738 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
740 else /* paint text field ourselves */
742 UINT itemState = ODS_COMBOBOXEDIT;
743 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
746 * Give ourselves some space.
748 InflateRect( &rectEdit, -1, -1 );
750 if( CB_OWNERDRAWN(lphc) )
754 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
756 /* setup state for DRAWITEM message. Owner will highlight */
757 if ( (lphc->wState & CBF_FOCUSED) &&
758 !(lphc->wState & CBF_DROPPED) )
759 itemState |= ODS_SELECTED | ODS_FOCUS;
762 * Save the current clip region.
763 * To retrieve the clip region, we need to create one "dummy"
766 clipRegion = CreateRectRgnIndirect(&rectEdit);
768 if (GetClipRgn(hdc, clipRegion)!=1)
770 DeleteObject(clipRegion);
774 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
776 dis.CtlType = ODT_COMBOBOX;
778 dis.hwndItem = lphc->self;
779 dis.itemAction = ODA_DRAWENTIRE;
781 dis.itemState = itemState;
783 dis.rcItem = rectEdit;
784 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
788 * Clip the DC and have the parent draw the item.
790 IntersectClipRect(hdc,
791 rectEdit.left, rectEdit.top,
792 rectEdit.right, rectEdit.bottom);
794 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
797 * Reset the clipping region.
799 SelectClipRgn(hdc, clipRegion);
803 static const WCHAR empty_stringW[] = { 0 };
805 if ( (lphc->wState & CBF_FOCUSED) &&
806 !(lphc->wState & CBF_DROPPED) ) {
809 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
810 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
811 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
817 ETO_OPAQUE | ETO_CLIPPED,
819 pText ? pText : empty_stringW , size, NULL );
821 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
822 DrawFocusRect( hdc, &rectEdit );
826 SelectObject(hdc, hPrevFont );
828 HeapFree( GetProcessHeap(), 0, pText );
831 /***********************************************************************
834 static void CBPaintBorder(
836 const HEADCOMBO *lphc,
841 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
843 GetClientRect(hwnd, &clientRect);
847 CopyRect(&clientRect, &lphc->textRect);
849 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
850 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
853 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
856 /***********************************************************************
857 * COMBO_PrepareColors
859 * This method will sent the appropriate WM_CTLCOLOR message to
860 * prepare and setup the colors for the combo's DC.
862 * It also returns the brush to use for the background.
864 static HBRUSH COMBO_PrepareColors(
871 * Get the background brush for this control.
873 if (CB_DISABLED(lphc))
875 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
876 (WPARAM)hDC, (LPARAM)lphc->self );
879 * We have to change the text color since WM_CTLCOLORSTATIC will
880 * set it to the "enabled" color. This is the same behavior as the
883 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
887 if (lphc->wState & CBF_EDIT)
889 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
890 (WPARAM)hDC, (LPARAM)lphc->self );
894 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX,
895 (WPARAM)hDC, (LPARAM)lphc->self );
903 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
909 /***********************************************************************
912 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
917 hDC = (hParamDC) ? hParamDC
918 : BeginPaint( lphc->self, &ps);
920 TRACE("hdc=%p\n", hDC);
922 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
924 HBRUSH hPrevBrush, hBkgBrush;
927 * Retrieve the background brush and select it in the
930 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
932 hPrevBrush = SelectObject( hDC, hBkgBrush );
933 if (!(lphc->wState & CBF_EDIT))
934 FillRect(hDC, &lphc->textRect, hBkgBrush);
937 * In non 3.1 look, there is a sunken border on the combobox
939 CBPaintBorder(lphc->self, lphc, hDC);
941 if( !IsRectEmpty(&lphc->buttonRect) )
943 CBPaintButton(lphc, hDC, lphc->buttonRect);
946 /* paint the edit control padding area */
947 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
949 RECT rPadEdit = lphc->textRect;
951 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
953 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
956 if( !(lphc->wState & CBF_EDIT) )
957 CBPaintText( lphc, hDC, lphc->textRect);
960 SelectObject( hDC, hPrevBrush );
964 EndPaint(lphc->self, &ps);
969 /***********************************************************************
972 * Select listbox entry according to the contents of the edit control.
974 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
980 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
983 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
985 TRACE("\t edit text length %i\n", length );
989 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
990 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
991 (WPARAM)(-1), (LPARAM)pText );
992 HeapFree( GetProcessHeap(), 0, pText );
995 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
997 /* probably superfluous but Windows sends this too */
998 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
999 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1004 /***********************************************************************
1007 * Copy a listbox entry to the edit control.
1009 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1012 LPWSTR pText = NULL;
1013 static const WCHAR empty_stringW[] = { 0 };
1015 TRACE("\t %i\n", index );
1017 if( index >= 0 ) /* got an entry */
1019 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1020 if( length != LB_ERR)
1022 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1024 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1025 (WPARAM)index, (LPARAM)pText );
1030 if( CB_HASSTRINGS(lphc) )
1032 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1033 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1034 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1037 if( lphc->wState & CBF_FOCUSED )
1038 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1040 HeapFree( GetProcessHeap(), 0, pText );
1043 /***********************************************************************
1046 * Show listbox popup.
1048 static void CBDropDown( LPHEADCOMBO lphc )
1051 MONITORINFO mon_info;
1056 TRACE("[%p]: drop down\n", lphc->self);
1058 CB_NOTIFY( lphc, CBN_DROPDOWN );
1062 lphc->wState |= CBF_DROPPED;
1063 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1065 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1067 /* Update edit only if item is in the list */
1068 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1069 CBUpdateEdit( lphc, lphc->droppedIndex );
1073 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1075 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1076 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1077 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1080 /* now set popup position */
1081 GetWindowRect( lphc->self, &rect );
1084 * If it's a dropdown, the listbox is offset
1086 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1087 rect.left += COMBO_EDITBUTTONSPACE();
1089 /* if the dropped height is greater than the total height of the dropped
1090 items list, then force the drop down list height to be the total height
1091 of the items in the dropped list */
1093 /* And Remove any extra space (Best Fit) */
1094 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1095 /* if listbox length has been set directly by its handle */
1096 GetWindowRect(lphc->hWndLBox, &r);
1097 if (nDroppedHeight < r.bottom - r.top)
1098 nDroppedHeight = r.bottom - r.top;
1099 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1106 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1108 nHeight = nIHeight*nItems;
1110 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1111 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1113 if (nDroppedHeight < nHeight)
1116 nDroppedHeight = (nItems+1)*nIHeight;
1118 nDroppedHeight = 6*nIHeight;
1122 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1123 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1124 mon_info.cbSize = sizeof(mon_info);
1125 GetMonitorInfoW( monitor, &mon_info );
1127 if( (rect.bottom + nDroppedHeight) >= mon_info.rcWork.bottom )
1128 rect.bottom = rect.top - nDroppedHeight;
1130 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1131 lphc->droppedRect.right - lphc->droppedRect.left,
1133 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1136 if( !(lphc->wState & CBF_NOREDRAW) )
1137 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1138 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1140 EnableWindow( lphc->hWndLBox, TRUE );
1141 if (GetCapture() != lphc->self)
1142 SetCapture(lphc->hWndLBox);
1145 /***********************************************************************
1148 * Hide listbox popup.
1150 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1152 HWND hWnd = lphc->self;
1154 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1155 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1157 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1159 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1162 if( lphc->wState & CBF_DROPPED )
1166 lphc->wState &= ~CBF_DROPPED;
1167 ShowWindow( lphc->hWndLBox, SW_HIDE );
1169 if(GetCapture() == lphc->hWndLBox)
1174 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1176 rect = lphc->buttonRect;
1187 rect = lphc->textRect;
1192 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1193 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1194 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1195 CB_NOTIFY( lphc, CBN_CLOSEUP );
1200 /***********************************************************************
1203 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1205 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1207 if( lphc->wState & CBF_DROPPED )
1209 CBRollUp( lphc, ok, bRedrawButton );
1217 /***********************************************************************
1220 static void CBRepaintButton( LPHEADCOMBO lphc )
1222 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1223 UpdateWindow(lphc->self);
1226 /***********************************************************************
1229 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1231 if( !(lphc->wState & CBF_FOCUSED) )
1233 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1234 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1236 /* This is wrong. Message sequences seem to indicate that this
1237 is set *after* the notify. */
1238 /* lphc->wState |= CBF_FOCUSED; */
1240 if( !(lphc->wState & CBF_EDIT) )
1241 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1243 CB_NOTIFY( lphc, CBN_SETFOCUS );
1244 lphc->wState |= CBF_FOCUSED;
1248 /***********************************************************************
1251 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1253 HWND hWnd = lphc->self;
1255 if( lphc->wState & CBF_FOCUSED )
1257 CBRollUp( lphc, FALSE, TRUE );
1258 if( IsWindow( hWnd ) )
1260 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1261 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1263 lphc->wState &= ~CBF_FOCUSED;
1266 if( !(lphc->wState & CBF_EDIT) )
1267 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1269 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1274 /***********************************************************************
1277 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1279 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1281 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1283 switch( HIWORD(wParam) >> 8 )
1285 case (EN_SETFOCUS >> 8):
1287 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1289 COMBO_SetFocus( lphc );
1292 case (EN_KILLFOCUS >> 8):
1294 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1296 /* NOTE: it seems that Windows' edit control sends an
1297 * undocumented message WM_USER + 0x1B instead of this
1298 * notification (only when it happens to be a part of
1299 * the combo). ?? - AK.
1302 COMBO_KillFocus( lphc );
1306 case (EN_CHANGE >> 8):
1308 * In some circumstances (when the selection of the combobox
1309 * is changed for example) we don't want the EN_CHANGE notification
1310 * to be forwarded to the parent of the combobox. This code
1311 * checks a flag that is set in these occasions and ignores the
1314 if (lphc->wState & CBF_NOLBSELECT)
1316 lphc->wState &= ~CBF_NOLBSELECT;
1320 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1323 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1324 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1327 case (EN_UPDATE >> 8):
1328 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1329 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1332 case (EN_ERRSPACE >> 8):
1333 CB_NOTIFY( lphc, CBN_ERRSPACE );
1336 else if( lphc->hWndLBox == hWnd )
1338 switch( (short)HIWORD(wParam) )
1341 CB_NOTIFY( lphc, CBN_ERRSPACE );
1345 CB_NOTIFY( lphc, CBN_DBLCLK );
1351 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1353 CB_NOTIFY( lphc, CBN_SELCHANGE );
1355 if( HIWORD(wParam) == LBN_SELCHANGE)
1357 if( lphc->wState & CBF_EDIT )
1359 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1360 lphc->wState |= CBF_NOLBSELECT;
1361 CBUpdateEdit( lphc, index );
1362 /* select text in edit, as Windows does */
1363 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1366 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1369 /* do not roll up if selection is being tracked
1370 * by arrowkeys in the dropdown listbox */
1371 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1373 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1375 else lphc->wState &= ~CBF_NOROLLUP;
1381 /* nothing to do here since ComboLBox always resets the focus to its
1382 * combo/edit counterpart */
1389 /***********************************************************************
1392 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1394 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1396 HWND hWnd = lphc->self;
1397 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1399 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1405 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1406 lpIS->CtlType = ODT_COMBOBOX;
1408 lpIS->hwndItem = hWnd;
1413 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1414 lpIS->CtlType = ODT_COMBOBOX;
1416 lpIS->hwndItem = hWnd;
1419 case WM_COMPAREITEM:
1421 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1422 lpIS->CtlType = ODT_COMBOBOX;
1424 lpIS->hwndItem = hWnd;
1427 case WM_MEASUREITEM:
1429 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1430 lpIS->CtlType = ODT_COMBOBOX;
1435 return SendMessageW(lphc->owner, msg, id, lParam);
1439 /***********************************************************************
1442 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1446 if( lphc->wState & CBF_EDIT )
1447 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1449 /* get it from the listbox */
1451 if (!count || !buf) return 0;
1452 if( lphc->hWndLBox )
1454 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1455 if (idx == LB_ERR) goto error;
1456 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1457 if (length == LB_ERR) goto error;
1459 /* 'length' is without the terminating character */
1460 if (length >= count)
1462 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1463 if (!lpBuffer) goto error;
1464 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1466 /* truncate if buffer is too short */
1467 if (length != LB_ERR)
1469 lstrcpynW( buf, lpBuffer, count );
1472 HeapFree( GetProcessHeap(), 0, lpBuffer );
1474 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1476 if (length == LB_ERR) return 0;
1480 error: /* error - truncate string, return zero */
1486 /***********************************************************************
1489 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1490 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1492 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1496 if( lphc->wState & CBF_EDIT )
1497 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1499 /* get it from the listbox */
1501 if (!count || !buf) return 0;
1502 if( lphc->hWndLBox )
1504 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1505 if (idx == LB_ERR) goto error;
1506 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1507 if (length == LB_ERR) goto error;
1509 /* 'length' is without the terminating character */
1510 if (length >= count)
1512 LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1513 if (!lpBuffer) goto error;
1514 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1516 /* truncate if buffer is too short */
1517 if (length != LB_ERR)
1519 lstrcpynA( buf, lpBuffer, count );
1522 HeapFree( GetProcessHeap(), 0, lpBuffer );
1524 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1526 if (length == LB_ERR) return 0;
1530 error: /* error - truncate string, return zero */
1536 /***********************************************************************
1539 * This function sets window positions according to the updated
1540 * component placement struct.
1542 static void CBResetPos(
1544 const RECT *rectEdit,
1548 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1550 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1551 * sizing messages */
1553 if( lphc->wState & CBF_EDIT )
1554 SetWindowPos( lphc->hWndEdit, 0,
1555 rectEdit->left, rectEdit->top,
1556 rectEdit->right - rectEdit->left,
1557 rectEdit->bottom - rectEdit->top,
1558 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1560 SetWindowPos( lphc->hWndLBox, 0,
1561 rectLB->left, rectLB->top,
1562 rectLB->right - rectLB->left,
1563 rectLB->bottom - rectLB->top,
1564 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1568 if( lphc->wState & CBF_DROPPED )
1570 lphc->wState &= ~CBF_DROPPED;
1571 ShowWindow( lphc->hWndLBox, SW_HIDE );
1574 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1575 RedrawWindow( lphc->self, NULL, 0,
1576 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1581 /***********************************************************************
1584 static void COMBO_Size( LPHEADCOMBO lphc, BOOL bRedraw )
1586 CBCalcPlacement(lphc->self,
1590 &lphc->droppedRect);
1592 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, bRedraw );
1596 /***********************************************************************
1599 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1604 lphc->hFont = hFont;
1607 * Propagate to owned windows.
1609 if( lphc->wState & CBF_EDIT )
1610 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1611 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1614 * Redo the layout of the control.
1616 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1618 CBCalcPlacement(lphc->self,
1622 &lphc->droppedRect);
1624 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1628 CBForceDummyResize(lphc);
1633 /***********************************************************************
1634 * COMBO_SetItemHeight
1636 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1638 LRESULT lRet = CB_ERR;
1640 if( index == -1 ) /* set text field height */
1642 if( height < 32768 )
1644 lphc->editHeight = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1647 * Redo the layout of the control.
1649 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1651 CBCalcPlacement(lphc->self,
1655 &lphc->droppedRect);
1657 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1661 CBForceDummyResize(lphc);
1667 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1668 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1669 (WPARAM)index, (LPARAM)height );
1673 /***********************************************************************
1674 * COMBO_SelectString
1676 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1678 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1679 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1682 if( lphc->wState & CBF_EDIT )
1683 CBUpdateEdit( lphc, index );
1686 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1689 return (LRESULT)index;
1692 /***********************************************************************
1695 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1699 HWND hWnd = lphc->self;
1701 pt.x = (short)LOWORD(lParam);
1702 pt.y = (short)HIWORD(lParam);
1703 bButton = PtInRect(&lphc->buttonRect, pt);
1705 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1706 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1708 lphc->wState |= CBF_BUTTONDOWN;
1709 if( lphc->wState & CBF_DROPPED )
1711 /* got a click to cancel selection */
1713 lphc->wState &= ~CBF_BUTTONDOWN;
1714 CBRollUp( lphc, TRUE, FALSE );
1715 if( !IsWindow( hWnd ) ) return;
1717 if( lphc->wState & CBF_CAPTURE )
1719 lphc->wState &= ~CBF_CAPTURE;
1725 /* drop down the listbox and start tracking */
1727 lphc->wState |= CBF_CAPTURE;
1731 if( bButton ) CBRepaintButton( lphc );
1735 /***********************************************************************
1738 * Release capture and stop tracking if needed.
1740 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1742 if( lphc->wState & CBF_CAPTURE )
1744 lphc->wState &= ~CBF_CAPTURE;
1745 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1747 INT index = CBUpdateLBox( lphc, TRUE );
1748 /* Update edit only if item is in the list */
1751 lphc->wState |= CBF_NOLBSELECT;
1752 CBUpdateEdit( lphc, index );
1753 lphc->wState &= ~CBF_NOLBSELECT;
1757 SetCapture(lphc->hWndLBox);
1760 if( lphc->wState & CBF_BUTTONDOWN )
1762 lphc->wState &= ~CBF_BUTTONDOWN;
1763 CBRepaintButton( lphc );
1767 /***********************************************************************
1770 * Two things to do - track combo button and release capture when
1771 * pointer goes into the listbox.
1773 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1778 pt.x = (short)LOWORD(lParam);
1779 pt.y = (short)HIWORD(lParam);
1781 if( lphc->wState & CBF_BUTTONDOWN )
1785 bButton = PtInRect(&lphc->buttonRect, pt);
1789 lphc->wState &= ~CBF_BUTTONDOWN;
1790 CBRepaintButton( lphc );
1794 GetClientRect( lphc->hWndLBox, &lbRect );
1795 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1796 if( PtInRect(&lbRect, pt) )
1798 lphc->wState &= ~CBF_CAPTURE;
1800 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1802 /* hand over pointer tracking */
1803 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1807 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1809 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1812 pcbi->rcItem = lphc->textRect;
1813 pcbi->rcButton = lphc->buttonRect;
1814 pcbi->stateButton = 0;
1815 if (lphc->wState & CBF_BUTTONDOWN)
1816 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1817 if (IsRectEmpty(&lphc->buttonRect))
1818 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1819 pcbi->hwndCombo = lphc->self;
1820 pcbi->hwndItem = lphc->hWndEdit;
1821 pcbi->hwndList = lphc->hWndLBox;
1825 static char *strdupA(LPCSTR str)
1830 if(!str) return NULL;
1833 ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1834 memcpy(ret, str, len + 1);
1838 /***********************************************************************
1839 * ComboWndProc_common
1841 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1842 WPARAM wParam, LPARAM lParam, BOOL unicode )
1844 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1846 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1847 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1849 if( lphc || message == WM_NCCREATE )
1853 /* System messages */
1857 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1858 ((LPCREATESTRUCTA)lParam)->style;
1859 return COMBO_NCCreate(hwnd, style);
1862 COMBO_NCDestroy(lphc);
1863 break;/* -> DefWindowProc */
1871 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1872 style = ((LPCREATESTRUCTW)lParam)->style;
1876 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1877 style = ((LPCREATESTRUCTA)lParam)->style;
1879 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1882 case WM_PRINTCLIENT:
1885 /* wParam may contain a valid HDC! */
1886 return COMBO_Paint(lphc, (HDC)wParam);
1889 /* do all painting in WM_PAINT like Windows does */
1894 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1895 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1897 int vk = (int)((LPMSG)lParam)->wParam;
1899 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1900 result |= DLGC_WANTMESSAGE;
1904 case WM_WINDOWPOSCHANGING:
1905 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1906 case WM_WINDOWPOSCHANGED:
1907 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1908 * In that case, the Combobox itself will not be resized, so we won't
1909 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1912 /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
1913 * Z-order based painting.
1917 if( lphc->hWndLBox &&
1918 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc, message == WM_SIZE );
1921 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1924 return (LRESULT)lphc->hFont;
1926 if( lphc->wState & CBF_EDIT )
1927 SetFocus( lphc->hWndEdit );
1929 COMBO_SetFocus( lphc );
1933 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1935 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1936 COMBO_KillFocus( lphc );
1940 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1942 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1943 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1945 case WM_GETTEXTLENGTH:
1947 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1949 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1950 if (j == -1) return 0;
1951 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1952 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1954 else if( lphc->wState & CBF_EDIT )
1957 lphc->wState |= CBF_NOEDITNOTIFY;
1958 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1959 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1960 lphc->wState &= ~CBF_NOEDITNOTIFY;
1967 if( lphc->wState & CBF_EDIT )
1969 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1970 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1976 case WM_COMPAREITEM:
1977 case WM_MEASUREITEM:
1978 return COMBO_ItemOp(lphc, message, lParam);
1980 if( lphc->wState & CBF_EDIT )
1981 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1982 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1984 /* Force the control to repaint when the enabled state changes. */
1985 InvalidateRect(lphc->self, NULL, TRUE);
1989 lphc->wState &= ~CBF_NOREDRAW;
1991 lphc->wState |= CBF_NOREDRAW;
1993 if( lphc->wState & CBF_EDIT )
1994 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1995 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1998 if( KEYDATA_ALT & HIWORD(lParam) )
1999 if( wParam == VK_UP || wParam == VK_DOWN )
2000 COMBO_FlipListbox( lphc, FALSE, FALSE );
2009 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2010 (lphc->wState & CBF_DROPPED))
2012 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2015 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
2017 COMBO_FlipListbox( lphc, FALSE, FALSE );
2021 if( lphc->wState & CBF_EDIT )
2022 hwndTarget = lphc->hWndEdit;
2024 hwndTarget = lphc->hWndLBox;
2026 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2027 SendMessageA(hwndTarget, message, wParam, lParam);
2029 case WM_LBUTTONDOWN:
2030 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2031 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2034 COMBO_LButtonUp( lphc );
2037 if( lphc->wState & CBF_CAPTURE )
2038 COMBO_MouseMove( lphc, wParam, lParam );
2042 if (wParam & (MK_SHIFT | MK_CONTROL))
2043 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2044 DefWindowProcA(hwnd, message, wParam, lParam);
2046 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2047 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2050 /* Combo messages */
2052 case CB_ADDSTRING16:
2053 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2058 if( lphc->dwStyle & CBS_LOWERCASE )
2059 CharLowerW((LPWSTR)lParam);
2060 else if( lphc->dwStyle & CBS_UPPERCASE )
2061 CharUpperW((LPWSTR)lParam);
2062 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2064 else /* unlike the unicode version, the ansi version does not overwrite
2065 the string if converting case */
2067 char *string = NULL;
2069 if( lphc->dwStyle & CBS_LOWERCASE )
2071 string = strdupA((LPSTR)lParam);
2075 else if( lphc->dwStyle & CBS_UPPERCASE )
2077 string = strdupA((LPSTR)lParam);
2081 ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
2082 HeapFree(GetProcessHeap(), 0, string);
2085 case CB_INSERTSTRING16:
2086 wParam = (INT)(INT16)wParam;
2087 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2089 case CB_INSERTSTRING:
2092 if( lphc->dwStyle & CBS_LOWERCASE )
2093 CharLowerW((LPWSTR)lParam);
2094 else if( lphc->dwStyle & CBS_UPPERCASE )
2095 CharUpperW((LPWSTR)lParam);
2096 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2100 if( lphc->dwStyle & CBS_LOWERCASE )
2101 CharLowerA((LPSTR)lParam);
2102 else if( lphc->dwStyle & CBS_UPPERCASE )
2103 CharUpperA((LPSTR)lParam);
2105 return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2107 case CB_DELETESTRING16:
2108 case CB_DELETESTRING:
2109 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2110 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2111 case CB_SELECTSTRING16:
2112 wParam = (INT)(INT16)wParam;
2113 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2115 case CB_SELECTSTRING:
2116 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2117 case CB_FINDSTRING16:
2118 wParam = (INT)(INT16)wParam;
2119 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2122 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2123 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2124 case CB_FINDSTRINGEXACT16:
2125 wParam = (INT)(INT16)wParam;
2126 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2128 case CB_FINDSTRINGEXACT:
2129 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2130 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2131 case CB_SETITEMHEIGHT16:
2132 wParam = (INT)(INT16)wParam; /* signed integer */
2134 case CB_SETITEMHEIGHT:
2135 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2136 case CB_GETITEMHEIGHT16:
2137 wParam = (INT)(INT16)wParam;
2139 case CB_GETITEMHEIGHT:
2140 if( (INT)wParam >= 0 ) /* listbox item */
2141 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2142 return CBGetTextAreaHeight(hwnd, lphc);
2143 case CB_RESETCONTENT16:
2144 case CB_RESETCONTENT:
2145 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2146 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2148 static const WCHAR empty_stringW[] = { 0 };
2149 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2152 InvalidateRect(lphc->self, NULL, TRUE);
2154 case CB_INITSTORAGE:
2155 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2156 case CB_GETHORIZONTALEXTENT:
2157 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2158 case CB_SETHORIZONTALEXTENT:
2159 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2160 case CB_GETTOPINDEX:
2161 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2163 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2165 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2166 case CB_GETDROPPEDWIDTH:
2167 if( lphc->droppedWidth )
2168 return lphc->droppedWidth;
2169 return lphc->droppedRect.right - lphc->droppedRect.left;
2170 case CB_SETDROPPEDWIDTH:
2171 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2172 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2174 case CB_GETDROPPEDCONTROLRECT16:
2175 lParam = (LPARAM)MapSL(lParam);
2179 RECT16 *r16 = (RECT16 *)lParam;
2180 CBGetDroppedControlRect( lphc, &r );
2183 r16->right = r.right;
2184 r16->bottom = r.bottom;
2187 case CB_GETDROPPEDCONTROLRECT:
2188 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2190 case CB_GETDROPPEDSTATE16:
2191 case CB_GETDROPPEDSTATE:
2192 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2194 return SendMessageA(lphc->hWndLBox, LB_DIR16, wParam, lParam);
2196 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2197 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2199 case CB_SHOWDROPDOWN16:
2200 case CB_SHOWDROPDOWN:
2201 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2205 if( !(lphc->wState & CBF_DROPPED) )
2209 if( lphc->wState & CBF_DROPPED )
2210 CBRollUp( lphc, FALSE, TRUE );
2215 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2216 case CB_GETCURSEL16:
2218 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2219 case CB_SETCURSEL16:
2220 wParam = (INT)(INT16)wParam;
2223 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2225 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2227 /* no LBN_SELCHANGE in this case, update manually */
2228 if( lphc->wState & CBF_EDIT )
2229 CBUpdateEdit( lphc, (INT)wParam );
2231 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2232 lphc->wState &= ~CBF_SELCHANGE;
2234 case CB_GETLBTEXT16:
2235 wParam = (INT)(INT16)wParam;
2236 lParam = (LPARAM)MapSL(lParam);
2239 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2240 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2241 case CB_GETLBTEXTLEN16:
2242 wParam = (INT)(INT16)wParam;
2244 case CB_GETLBTEXTLEN:
2245 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2246 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2247 case CB_GETITEMDATA16:
2248 wParam = (INT)(INT16)wParam;
2250 case CB_GETITEMDATA:
2251 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2252 case CB_SETITEMDATA16:
2253 wParam = (INT)(INT16)wParam;
2255 case CB_SETITEMDATA:
2256 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2257 case CB_GETEDITSEL16:
2258 wParam = lParam = 0; /* just in case */
2261 /* Edit checks passed parameters itself */
2262 if( lphc->wState & CBF_EDIT )
2263 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2265 case CB_SETEDITSEL16:
2267 if( lphc->wState & CBF_EDIT )
2268 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2269 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2271 case CB_SETEXTENDEDUI16:
2272 case CB_SETEXTENDEDUI:
2273 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2276 lphc->wState |= CBF_EUI;
2277 else lphc->wState &= ~CBF_EUI;
2279 case CB_GETEXTENDEDUI16:
2280 case CB_GETEXTENDEDUI:
2281 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2282 case CB_GETCOMBOBOXINFO:
2283 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2285 if( lphc->wState & CBF_EDIT )
2286 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2288 if (message >= WM_USER)
2289 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2290 message - WM_USER, wParam, lParam );
2293 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2294 DefWindowProcA(hwnd, message, wParam, lParam);
2297 /***********************************************************************
2300 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2303 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2305 if (!IsWindow(hwnd)) return 0;
2306 return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2309 /***********************************************************************
2312 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2314 if (!IsWindow(hwnd)) return 0;
2315 return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );
2318 /*************************************************************************
2319 * GetComboBoxInfo (USER32.@)
2321 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2322 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2324 TRACE("(%p, %p)\n", hwndCombo, pcbi);
2325 return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);