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
37 #include <wine/debug.h>
38 WINE_DEFAULT_DEBUG_CHANNEL(combo
);
41 * Additional combo box definitions
44 #define CB_NOTIFY( lphc, code ) \
45 (SendMessageW((lphc)->owner, WM_COMMAND, \
46 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
48 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
49 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
50 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
51 #define CB_HWND( lphc ) ((lphc)->self)
52 // ReactOS already define in include/controls.h We have it here as a sync note.
53 //#define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
55 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
60 static HBITMAP hComboBmp
= 0;
61 static UINT CBitHeight
, CBitWidth
;
64 * Look and feel dependent "constants"
67 #define COMBO_YBORDERGAP 5
68 #define COMBO_XBORDERSIZE() 2
69 #define COMBO_YBORDERSIZE() 2
70 #define COMBO_EDITBUTTONSPACE() 0
71 #define EDIT_CONTROL_PADDING() 1
73 /*********************************************************************
74 * combo class descriptor
76 static const WCHAR comboboxW
[] = {'C','o','m','b','o','B','o','x',0};
77 const struct builtin_class_descr COMBO_builtin_class
=
80 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
81 ComboWndProcA
, /* procA */
82 ComboWndProcW
, /* procW */
83 sizeof(HEADCOMBO
*), /* extra */
84 IDC_ARROW
, /* cursor */
89 /***********************************************************************
92 * Load combo button bitmap.
94 static BOOL
COMBO_Init(void)
98 if( hComboBmp
) return TRUE
;
99 if( (hDC
= CreateCompatibleDC(0)) )
102 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
108 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
109 CBitHeight
= bm
.bmHeight
;
110 CBitWidth
= bm
.bmWidth
;
112 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
114 hPrevB
= SelectObject( hDC
, hComboBmp
);
115 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
116 InvertRect( hDC
, &r
);
117 SelectObject( hDC
, hPrevB
);
127 /* Retrieve the UI state for the control */
128 static BOOL
COMBO_update_uistate(LPHEADCOMBO lphc
)
132 prev_flags
= lphc
->UIState
;
133 lphc
->UIState
= DefWindowProcW(lphc
->self
, WM_QUERYUISTATE
, 0, 0);
134 return prev_flags
!= lphc
->UIState
;
137 /***********************************************************************
140 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
144 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
147 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)lphc
);
149 COMBO_update_uistate(lphc
);
151 /* some braindead apps do try to use scrollbar/border flags */
153 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
154 SetWindowLongPtrW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
157 * We also have to remove the client edge style to make sure
158 * we don't end-up with a non client area.
160 SetWindowLongPtrW( hwnd
, GWL_EXSTYLE
,
161 GetWindowLongPtrW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
163 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
164 lphc
->dwStyle
|= CBS_HASSTRINGS
;
165 if( !(GetWindowLongPtrW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
166 lphc
->wState
|= CBF_NOTIFY
;
168 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
174 /***********************************************************************
177 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
182 TRACE("[%p]: freeing storage\n", lphc
->self
);
184 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
185 DestroyWindow( lphc
->hWndLBox
);
187 SetWindowLongPtrW( lphc
->self
, 0, 0 );
188 HeapFree( GetProcessHeap(), 0, lphc
);
193 /***********************************************************************
194 * CBGetTextAreaHeight
196 * This method will calculate the height of the text area of the
198 * The height of the text area is set in two ways.
199 * It can be set explicitly through a combobox message or through a
200 * WM_MEASUREITEM callback.
201 * If this is not the case, the height is set to font height + 4px
202 * This height was determined through experimentation.
203 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
205 static INT
CBGetTextAreaHeight(
211 if( lphc
->editHeight
) /* explicitly set height */
213 iTextItemHeight
= lphc
->editHeight
;
218 HDC hDC
= GetDC(hwnd
);
223 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
225 GetTextMetricsW(hDC
, &tm
);
227 baseUnitY
= tm
.tmHeight
;
230 SelectObject( hDC
, hPrevFont
);
232 ReleaseDC(hwnd
, hDC
);
234 iTextItemHeight
= baseUnitY
+ 4;
238 * Check the ownerdraw case if we haven't asked the parent the size
241 if ( CB_OWNERDRAWN(lphc
) &&
242 (lphc
->wState
& CBF_MEASUREITEM
) )
244 MEASUREITEMSTRUCT measureItem
;
246 INT originalItemHeight
= iTextItemHeight
;
247 UINT id
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
250 * We use the client rect for the width of the item.
252 GetClientRect(hwnd
, &clientRect
);
254 lphc
->wState
&= ~CBF_MEASUREITEM
;
257 * Send a first one to measure the size of the text area
259 measureItem
.CtlType
= ODT_COMBOBOX
;
260 measureItem
.CtlID
= id
;
261 measureItem
.itemID
= -1;
262 measureItem
.itemWidth
= clientRect
.right
;
263 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
264 measureItem
.itemData
= 0;
265 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
266 iTextItemHeight
= 6 + measureItem
.itemHeight
;
269 * Send a second one in the case of a fixed ownerdraw list to calculate the
270 * size of the list items. (we basically do this on behalf of the listbox)
272 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
274 measureItem
.CtlType
= ODT_COMBOBOX
;
275 measureItem
.CtlID
= id
;
276 measureItem
.itemID
= 0;
277 measureItem
.itemWidth
= clientRect
.right
;
278 measureItem
.itemHeight
= originalItemHeight
;
279 measureItem
.itemData
= 0;
280 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
281 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
285 * Keep the size for the next time
287 lphc
->editHeight
= iTextItemHeight
;
290 return iTextItemHeight
;
293 /***********************************************************************
296 * The dummy resize is used for listboxes that have a popup to trigger
297 * a re-arranging of the contents of the combobox and the recalculation
298 * of the size of the "real" control window.
300 static void CBForceDummyResize(
306 newComboHeight
= CBGetTextAreaHeight(lphc
->self
,lphc
) + 2*COMBO_YBORDERSIZE();
308 GetWindowRect(lphc
->self
, &windowRect
);
311 * We have to be careful, resizing a combobox also has the meaning that the
312 * dropped rect will be resized. In this case, we want to trigger a resize
313 * to recalculate layout but we don't want to change the dropped rectangle
314 * So, we pass the height of text area of control as the height.
315 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
318 SetWindowPos( lphc
->self
,
321 windowRect
.right
- windowRect
.left
,
323 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
326 /***********************************************************************
329 * Set up component coordinates given valid lphc->RectCombo.
331 static void CBCalcPlacement(
339 * Again, start with the client rectangle.
341 GetClientRect(hwnd
, lprEdit
);
346 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
349 * Chop off the bottom part to fit with the height of the text area.
351 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
354 * The button starts the same vertical position as the text area.
356 CopyRect(lprButton
, lprEdit
);
359 * If the combobox is "simple" there is no button.
361 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
362 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
366 * Let's assume the combobox button is the same width as the
368 * size the button horizontally and cut-off the text area.
370 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
371 lprEdit
->right
= lprButton
->left
;
375 * In the case of a dropdown, there is an additional spacing between the
376 * text area and the button.
378 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
380 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
384 * If we have an edit control, we space it away from the borders slightly.
386 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
388 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
392 * Adjust the size of the listbox popup.
394 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
397 * Use the client rectangle to initialize the listbox rectangle
399 GetClientRect(hwnd
, lprLB
);
402 * Then, chop-off the top part.
404 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
409 * Make sure the dropped width is as large as the combobox itself.
411 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
413 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
416 * In the case of a dropdown, the popup listbox is offset to the right.
417 * so, we want to make sure it's flush with the right side of the
420 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
421 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
424 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
427 /* don't allow negative window width */
428 if (lprEdit
->right
< lprEdit
->left
)
429 lprEdit
->right
= lprEdit
->left
;
431 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit
));
433 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton
));
435 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB
));
438 /***********************************************************************
439 * CBGetDroppedControlRect
441 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
443 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
444 of the combo box and the lower right corner of the listbox */
446 GetWindowRect(lphc
->self
, lpRect
);
448 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
449 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
453 /***********************************************************************
456 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
459 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
460 static const WCHAR editName
[] = {'E','d','i','t',0};
462 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
463 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
465 lphc
->owner
= hwndParent
;
468 * The item height and dropped width are not set when the control
471 lphc
->droppedWidth
= lphc
->editHeight
= 0;
474 * The first time we go through, we want to measure the ownerdraw item
476 lphc
->wState
|= CBF_MEASUREITEM
;
478 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
480 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
486 * Initialize the dropped rect to the size of the client area of the
487 * control and then, force all the areas of the combobox to be
490 GetClientRect( hwnd
, &lphc
->droppedRect
);
491 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
494 * Adjust the position of the popup listbox if it's necessary
496 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
498 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
501 * If it's a dropdown, the listbox is offset
503 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
504 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
506 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
507 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
508 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
509 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
510 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
513 /* create listbox popup */
515 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
516 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
518 if( lphc
->dwStyle
& CBS_SORT
)
519 lbeStyle
|= LBS_SORT
;
520 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
521 lbeStyle
|= LBS_HASSTRINGS
;
522 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
523 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
524 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
525 lbeStyle
|= LBS_DISABLENOSCROLL
;
527 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
529 lbeStyle
|= WS_VISIBLE
;
532 * In win 95 look n feel, the listbox in the simple combobox has
533 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
535 lbeStyle
&= ~WS_BORDER
;
536 lbeExStyle
|= WS_EX_CLIENTEDGE
;
540 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
544 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
545 lphc
->droppedRect
.left
,
546 lphc
->droppedRect
.top
,
547 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
548 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
549 hwnd
, (HMENU
)ID_CB_LISTBOX
,
550 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
552 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
553 lphc
->droppedRect
.left
,
554 lphc
->droppedRect
.top
,
555 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
556 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
557 hwnd
, (HMENU
)ID_CB_LISTBOX
,
558 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
563 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
565 if( lphc
->wState
& CBF_EDIT
)
567 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
568 lbeStyle
|= ES_OEMCONVERT
;
569 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
570 lbeStyle
|= ES_AUTOHSCROLL
;
571 if( lphc
->dwStyle
& CBS_LOWERCASE
)
572 lbeStyle
|= ES_LOWERCASE
;
573 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
574 lbeStyle
|= ES_UPPERCASE
;
576 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
579 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
580 lphc
->textRect
.left
, lphc
->textRect
.top
,
581 lphc
->textRect
.right
- lphc
->textRect
.left
,
582 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
583 hwnd
, (HMENU
)ID_CB_EDIT
,
584 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
586 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
587 lphc
->textRect
.left
, lphc
->textRect
.top
,
588 lphc
->textRect
.right
- lphc
->textRect
.left
,
589 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
590 hwnd
, (HMENU
)ID_CB_EDIT
,
591 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
593 if( !lphc
->hWndEdit
)
599 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
601 /* Now do the trick with parent */
602 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
604 * If the combo is a dropdown, we must resize the control
605 * to fit only the text area and button. To do this,
606 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
607 * will take care of setting the height for us.
609 CBForceDummyResize(lphc
);
612 TRACE("init done\n");
615 ERR("edit control failure.\n");
616 } else ERR("listbox failure.\n");
617 } else ERR("no owner for visible combo.\n");
619 /* CreateWindow() will send WM_NCDESTROY to cleanup */
624 /***********************************************************************
627 * Paint combo button (normal, pressed, and disabled states).
629 static void CBPaintButton( LPHEADCOMBO lphc
, HDC hdc
, RECT rectButton
)
631 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
633 if( lphc
->wState
& CBF_NOREDRAW
)
637 if (lphc
->wState
& CBF_BUTTONDOWN
)
638 buttonState
|= DFCS_PUSHED
;
640 if (CB_DISABLED(lphc
))
641 buttonState
|= DFCS_INACTIVE
;
643 DrawFrameControl(hdc
, &rectButton
, DFC_SCROLL
, buttonState
);
646 /***********************************************************************
649 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
651 static void CBPaintText(
659 if( lphc
->wState
& CBF_NOREDRAW
) return;
663 /* follow Windows combobox that sends a bunch of text
664 * inquiries to its listbox while processing WM_PAINT. */
666 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
668 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
670 FIXME("LB_ERR probably not handled yet\n");
671 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
673 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
674 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
675 pText
[size
] = '\0'; /* just in case */
679 if( !CB_OWNERDRAWN(lphc
) )
682 if( lphc
->wState
& CBF_EDIT
)
684 static const WCHAR empty_stringW
[] = { 0 };
685 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
686 if( lphc
->wState
& CBF_FOCUSED
)
687 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
689 else /* paint text field ourselves */
691 UINT itemState
= ODS_COMBOBOXEDIT
;
692 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
695 * Give ourselves some space.
697 InflateRect( &rectEdit
, -1, -1 );
699 if( CB_OWNERDRAWN(lphc
) )
703 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
705 /* setup state for DRAWITEM message. Owner will highlight */
706 if ( (lphc
->wState
& CBF_FOCUSED
) &&
707 !(lphc
->wState
& CBF_DROPPED
) )
708 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
710 if (!IsWindowEnabled(lphc
->self
)) itemState
|= ODS_DISABLED
;
712 dis
.CtlType
= ODT_COMBOBOX
;
714 dis
.hwndItem
= lphc
->self
;
715 dis
.itemAction
= ODA_DRAWENTIRE
;
717 dis
.itemState
= itemState
;
719 dis
.rcItem
= rectEdit
;
720 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, id
, 0 );
723 * Clip the DC and have the parent draw the item.
725 clipRegion
= set_control_clipping( hdc
, &rectEdit
);
727 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
729 SelectClipRgn( hdc
, clipRegion
);
730 if (clipRegion
) DeleteObject( clipRegion
);
734 static const WCHAR empty_stringW
[] = { 0 };
736 if ( (lphc
->wState
& CBF_FOCUSED
) &&
737 !(lphc
->wState
& CBF_DROPPED
) ) {
740 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
741 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
742 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
748 ETO_OPAQUE
| ETO_CLIPPED
,
750 pText
? pText
: empty_stringW
, size
, NULL
);
752 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
) &&
753 !(lphc
->UIState
& UISF_HIDEFOCUS
))
754 DrawFocusRect( hdc
, &rectEdit
);
758 SelectObject(hdc
, hPrevFont
);
761 HeapFree( GetProcessHeap(), 0, pText
);
764 /***********************************************************************
767 static void CBPaintBorder(
769 const HEADCOMBO
*lphc
,
774 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
776 GetClientRect(hwnd
, &clientRect
);
780 CopyRect(&clientRect
, &lphc
->textRect
);
782 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
783 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
786 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
789 /***********************************************************************
790 * COMBO_PrepareColors
792 * This method will sent the appropriate WM_CTLCOLOR message to
793 * prepare and setup the colors for the combo's DC.
795 * It also returns the brush to use for the background.
797 static HBRUSH
COMBO_PrepareColors(
804 * Get the background brush for this control.
806 if (CB_DISABLED(lphc
))
808 hBkgBrush
= GetControlColor(lphc
->owner
, lphc
->self
, hDC
, WM_CTLCOLORSTATIC
);
810 * We have to change the text color since WM_CTLCOLORSTATIC will
811 * set it to the "enabled" color. This is the same behavior as the
814 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
818 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
819 hBkgBrush
= GetControlColor(lphc
->owner
, lphc
->self
, hDC
, WM_CTLCOLOREDIT
);
826 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
832 /***********************************************************************
835 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
840 hDC
= (hParamDC
) ? hParamDC
841 : BeginPaint( lphc
->self
, &ps
);
843 TRACE("hdc=%p\n", hDC
);
845 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
847 HBRUSH hPrevBrush
, hBkgBrush
;
850 * Retrieve the background brush and select it in the
853 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
855 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
856 if (!(lphc
->wState
& CBF_EDIT
))
857 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
860 * In non 3.1 look, there is a sunken border on the combobox
862 CBPaintBorder(lphc
->self
, lphc
, hDC
);
864 if( !IsRectEmpty(&lphc
->buttonRect
) )
866 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
869 /* paint the edit control padding area */
870 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
872 RECT rPadEdit
= lphc
->textRect
;
874 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
876 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
879 if( !(lphc
->wState
& CBF_EDIT
) )
880 CBPaintText( lphc
, hDC
, lphc
->textRect
);
883 SelectObject( hDC
, hPrevBrush
);
887 EndPaint(lphc
->self
, &ps
);
892 /***********************************************************************
895 * Select listbox entry according to the contents of the edit control.
897 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
903 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
906 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
908 TRACE("\t edit text length %i\n", length
);
912 GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
913 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
,
914 (WPARAM
)(-1), (LPARAM
)pText
);
915 HeapFree( GetProcessHeap(), 0, pText
);
918 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)(bSelect
? idx
: -1), 0);
920 /* probably superfluous but Windows sends this too */
921 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
922 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
927 /***********************************************************************
930 * Copy a listbox entry to the edit control.
932 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
936 static const WCHAR empty_stringW
[] = { 0 };
938 TRACE("\t %i\n", index
);
940 if( index
>= 0 ) /* got an entry */
942 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
943 if( length
!= LB_ERR
)
945 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
947 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
,
948 (WPARAM
)index
, (LPARAM
)pText
);
953 if( CB_HASSTRINGS(lphc
) )
955 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
956 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
957 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
960 if( lphc
->wState
& CBF_FOCUSED
)
961 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
963 HeapFree( GetProcessHeap(), 0, pText
);
966 /***********************************************************************
969 * Show listbox popup.
971 static void CBDropDown( LPHEADCOMBO lphc
)
974 MONITORINFO mon_info
;
979 TRACE("[%p]: drop down\n", lphc
->self
);
981 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
985 lphc
->wState
|= CBF_DROPPED
;
986 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
988 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
990 /* Update edit only if item is in the list */
991 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
992 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
996 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
998 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
999 (WPARAM
)(lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
), 0 );
1000 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1003 /* now set popup position */
1004 GetWindowRect( lphc
->self
, &rect
);
1007 * If it's a dropdown, the listbox is offset
1009 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1010 rect
.left
+= COMBO_EDITBUTTONSPACE();
1012 /* if the dropped height is greater than the total height of the dropped
1013 items list, then force the drop down list height to be the total height
1014 of the items in the dropped list */
1016 /* And Remove any extra space (Best Fit) */
1017 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1018 /* if listbox length has been set directly by its handle */
1019 GetWindowRect(lphc
->hWndLBox
, &r
);
1020 if (nDroppedHeight
< r
.bottom
- r
.top
)
1021 nDroppedHeight
= r
.bottom
- r
.top
;
1022 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1029 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1031 nHeight
= nIHeight
*nItems
;
1033 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1034 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1036 if (nDroppedHeight
< nHeight
)
1039 nDroppedHeight
= (nItems
+1)*nIHeight
;
1040 else if (nDroppedHeight
< 6*nIHeight
)
1041 nDroppedHeight
= 6*nIHeight
;
1045 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1046 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
1047 mon_info
.cbSize
= sizeof(mon_info
);
1048 GetMonitorInfoW( monitor
, &mon_info
);
1050 if( (rect
.bottom
+ nDroppedHeight
) >= mon_info
.rcWork
.bottom
)
1051 rect
.bottom
= rect
.top
- nDroppedHeight
;
1053 SetWindowPos( lphc
->hWndLBox
, HWND_TOPMOST
, rect
.left
, rect
.bottom
,
1054 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1056 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1059 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1060 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1061 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1063 EnableWindow( lphc
->hWndLBox
, TRUE
);
1064 if (GetCapture() != lphc
->self
)
1065 SetCapture(lphc
->hWndLBox
);
1068 /***********************************************************************
1071 * Hide listbox popup.
1073 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1075 HWND hWnd
= lphc
->self
;
1077 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1078 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1080 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1082 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1085 if( lphc
->wState
& CBF_DROPPED
)
1089 lphc
->wState
&= ~CBF_DROPPED
;
1090 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1092 if(GetCapture() == lphc
->hWndLBox
)
1097 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1099 rect
= lphc
->buttonRect
;
1110 rect
= lphc
->textRect
;
1115 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1116 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1117 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1118 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1123 /***********************************************************************
1126 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1128 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1130 if( lphc
->wState
& CBF_DROPPED
)
1132 CBRollUp( lphc
, ok
, bRedrawButton
);
1140 /***********************************************************************
1143 static void CBRepaintButton( LPHEADCOMBO lphc
)
1145 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1146 UpdateWindow(lphc
->self
);
1149 /***********************************************************************
1152 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1154 if( !(lphc
->wState
& CBF_FOCUSED
) )
1156 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1157 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1159 /* This is wrong. Message sequences seem to indicate that this
1160 is set *after* the notify. */
1161 /* lphc->wState |= CBF_FOCUSED; */
1163 if( !(lphc
->wState
& CBF_EDIT
) )
1164 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1166 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1167 lphc
->wState
|= CBF_FOCUSED
;
1171 /***********************************************************************
1174 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1176 HWND hWnd
= lphc
->self
;
1178 if( lphc
->wState
& CBF_FOCUSED
)
1180 CBRollUp( lphc
, FALSE
, TRUE
);
1181 if( IsWindow( hWnd
) )
1183 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1184 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1186 lphc
->wState
&= ~CBF_FOCUSED
;
1189 if( !(lphc
->wState
& CBF_EDIT
) )
1190 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1192 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1197 /***********************************************************************
1200 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1202 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1204 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1206 switch( HIWORD(wParam
) >> 8 )
1208 case (EN_SETFOCUS
>> 8):
1210 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1212 COMBO_SetFocus( lphc
);
1215 case (EN_KILLFOCUS
>> 8):
1217 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1219 /* NOTE: it seems that Windows' edit control sends an
1220 * undocumented message WM_USER + 0x1B instead of this
1221 * notification (only when it happens to be a part of
1222 * the combo). ?? - AK.
1225 COMBO_KillFocus( lphc
);
1229 case (EN_CHANGE
>> 8):
1231 * In some circumstances (when the selection of the combobox
1232 * is changed for example) we don't want the EN_CHANGE notification
1233 * to be forwarded to the parent of the combobox. This code
1234 * checks a flag that is set in these occasions and ignores the
1237 if (lphc
->wState
& CBF_NOLBSELECT
)
1239 lphc
->wState
&= ~CBF_NOLBSELECT
;
1243 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1246 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1247 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1250 case (EN_UPDATE
>> 8):
1251 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1252 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1255 case (EN_ERRSPACE
>> 8):
1256 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1259 else if( lphc
->hWndLBox
== hWnd
)
1261 switch( (short)HIWORD(wParam
) )
1264 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1268 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1274 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1276 /* do not roll up if selection is being tracked
1277 * by arrow keys in the dropdown listbox */
1278 if (!(lphc
->wState
& CBF_NOROLLUP
))
1280 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1282 else lphc
->wState
&= ~CBF_NOROLLUP
;
1284 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1286 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1288 if( lphc
->wState
& CBF_EDIT
)
1290 INT index
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1291 lphc
->wState
|= CBF_NOLBSELECT
;
1292 CBUpdateEdit( lphc
, index
);
1293 /* select text in edit, as Windows does */
1294 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1298 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1299 UpdateWindow(lphc
->self
);
1306 /* nothing to do here since ComboLBox always resets the focus to its
1307 * combo/edit counterpart */
1314 /***********************************************************************
1317 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1319 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1321 HWND hWnd
= lphc
->self
;
1322 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1324 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1330 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1331 lpIS
->CtlType
= ODT_COMBOBOX
;
1333 lpIS
->hwndItem
= hWnd
;
1338 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1339 lpIS
->CtlType
= ODT_COMBOBOX
;
1341 lpIS
->hwndItem
= hWnd
;
1344 case WM_COMPAREITEM
:
1346 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1347 lpIS
->CtlType
= ODT_COMBOBOX
;
1349 lpIS
->hwndItem
= hWnd
;
1352 case WM_MEASUREITEM
:
1354 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1355 lpIS
->CtlType
= ODT_COMBOBOX
;
1360 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1364 /***********************************************************************
1367 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1371 if( lphc
->wState
& CBF_EDIT
)
1372 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1374 /* get it from the listbox */
1376 if (!count
|| !buf
) return 0;
1377 if( lphc
->hWndLBox
)
1379 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1380 if (idx
== LB_ERR
) goto error
;
1381 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1382 if (length
== LB_ERR
) goto error
;
1384 /* 'length' is without the terminating character */
1385 if (length
>= count
)
1387 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1388 if (!lpBuffer
) goto error
;
1389 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1391 /* truncate if buffer is too short */
1392 if (length
!= LB_ERR
)
1394 lstrcpynW( buf
, lpBuffer
, count
);
1397 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1399 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1401 if (length
== LB_ERR
) return 0;
1405 error
: /* error - truncate string, return zero */
1411 /***********************************************************************
1414 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1415 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1417 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1421 if( lphc
->wState
& CBF_EDIT
)
1422 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1424 /* get it from the listbox */
1426 if (!count
|| !buf
) return 0;
1427 if( lphc
->hWndLBox
)
1429 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1430 if (idx
== LB_ERR
) goto error
;
1431 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1432 if (length
== LB_ERR
) goto error
;
1434 /* 'length' is without the terminating character */
1435 if (length
>= count
)
1437 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1438 if (!lpBuffer
) goto error
;
1439 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1441 /* truncate if buffer is too short */
1442 if (length
!= LB_ERR
)
1444 lstrcpynA( buf
, lpBuffer
, count
);
1447 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1449 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1451 if (length
== LB_ERR
) return 0;
1455 error
: /* error - truncate string, return zero */
1461 /***********************************************************************
1464 * This function sets window positions according to the updated
1465 * component placement struct.
1467 static void CBResetPos(
1469 const RECT
*rectEdit
,
1473 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1475 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1476 * sizing messages */
1478 if( lphc
->wState
& CBF_EDIT
)
1479 SetWindowPos( lphc
->hWndEdit
, 0,
1480 rectEdit
->left
, rectEdit
->top
,
1481 rectEdit
->right
- rectEdit
->left
,
1482 rectEdit
->bottom
- rectEdit
->top
,
1483 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1485 SetWindowPos( lphc
->hWndLBox
, 0,
1486 rectLB
->left
, rectLB
->top
,
1487 rectLB
->right
- rectLB
->left
,
1488 rectLB
->bottom
- rectLB
->top
,
1489 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1493 if( lphc
->wState
& CBF_DROPPED
)
1495 lphc
->wState
&= ~CBF_DROPPED
;
1496 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1499 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1500 RedrawWindow( lphc
->self
, NULL
, 0,
1501 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1506 /***********************************************************************
1509 static void COMBO_Size( LPHEADCOMBO lphc
, LPARAM lParam
)
1512 * Those controls are always the same height. So we have to make sure
1513 * they are not resized to another value.
1515 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1519 newComboHeight
= CBGetTextAreaHeight(lphc
->self
, lphc
) + 2*COMBO_YBORDERSIZE();
1522 * Resizing a combobox has another side effect, it resizes the dropped
1523 * rectangle as well. However, it does it only if the new height for the
1524 * combobox is more than the height it should have. In other words,
1525 * if the application resizing the combobox only had the intention to resize
1526 * the actual control, for example, to do the layout of a dialog that is
1527 * resized, the height of the dropdown is not changed.
1529 if( HIWORD(lParam
) > newComboHeight
)
1531 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1532 HIWORD(lParam
), newComboHeight
, lphc
->droppedRect
.bottom
,
1533 lphc
->droppedRect
.top
);
1534 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ HIWORD(lParam
) - newComboHeight
;
1537 * Restore original height
1539 if( HIWORD(lParam
) != newComboHeight
)
1540 SetWindowPos(lphc
->self
, 0, 0, 0, LOWORD(lParam
), newComboHeight
,
1541 SWP_NOZORDER
|SWP_NOMOVE
|SWP_NOACTIVATE
|SWP_NOREDRAW
);
1544 CBCalcPlacement(lphc
->self
,
1548 &lphc
->droppedRect
);
1550 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1554 /***********************************************************************
1557 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1562 lphc
->hFont
= hFont
;
1565 * Propagate to owned windows.
1567 if( lphc
->wState
& CBF_EDIT
)
1568 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1569 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1572 * Redo the layout of the control.
1574 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1576 CBCalcPlacement(lphc
->self
,
1580 &lphc
->droppedRect
);
1582 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1586 CBForceDummyResize(lphc
);
1591 /***********************************************************************
1592 * COMBO_SetItemHeight
1594 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1596 LRESULT lRet
= CB_ERR
;
1598 if( index
== -1 ) /* set text field height */
1600 if( height
< 32768 )
1602 lphc
->editHeight
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1605 * Redo the layout of the control.
1607 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1609 CBCalcPlacement(lphc
->self
,
1613 &lphc
->droppedRect
);
1615 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1619 CBForceDummyResize(lphc
);
1625 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1626 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1627 (WPARAM
)index
, (LPARAM
)height
);
1631 /***********************************************************************
1632 * COMBO_SelectString
1634 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1636 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
) :
1637 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
);
1640 if( lphc
->wState
& CBF_EDIT
)
1641 CBUpdateEdit( lphc
, index
);
1644 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1647 return (LRESULT
)index
;
1650 /***********************************************************************
1653 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1657 HWND hWnd
= lphc
->self
;
1659 pt
.x
= (short)LOWORD(lParam
);
1660 pt
.y
= (short)HIWORD(lParam
);
1661 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1663 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1664 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1666 lphc
->wState
|= CBF_BUTTONDOWN
;
1667 if( lphc
->wState
& CBF_DROPPED
)
1669 /* got a click to cancel selection */
1671 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1672 CBRollUp( lphc
, TRUE
, FALSE
);
1673 if( !IsWindow( hWnd
) ) return;
1675 if( lphc
->wState
& CBF_CAPTURE
)
1677 lphc
->wState
&= ~CBF_CAPTURE
;
1683 /* drop down the listbox and start tracking */
1685 lphc
->wState
|= CBF_CAPTURE
;
1689 if( bButton
) CBRepaintButton( lphc
);
1693 /***********************************************************************
1696 * Release capture and stop tracking if needed.
1698 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1700 if( lphc
->wState
& CBF_CAPTURE
)
1702 lphc
->wState
&= ~CBF_CAPTURE
;
1703 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1705 INT index
= CBUpdateLBox( lphc
, TRUE
);
1706 /* Update edit only if item is in the list */
1709 lphc
->wState
|= CBF_NOLBSELECT
;
1710 CBUpdateEdit( lphc
, index
);
1711 lphc
->wState
&= ~CBF_NOLBSELECT
;
1715 SetCapture(lphc
->hWndLBox
);
1718 if( lphc
->wState
& CBF_BUTTONDOWN
)
1720 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1721 CBRepaintButton( lphc
);
1725 /***********************************************************************
1728 * Two things to do - track combo button and release capture when
1729 * pointer goes into the listbox.
1731 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1736 pt
.x
= (short)LOWORD(lParam
);
1737 pt
.y
= (short)HIWORD(lParam
);
1739 if( lphc
->wState
& CBF_BUTTONDOWN
)
1743 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1747 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1748 CBRepaintButton( lphc
);
1752 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1753 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1754 if( PtInRect(&lbRect
, pt
) )
1756 lphc
->wState
&= ~CBF_CAPTURE
;
1758 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1760 /* hand over pointer tracking */
1761 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1765 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1767 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1770 pcbi
->rcItem
= lphc
->textRect
;
1771 pcbi
->rcButton
= lphc
->buttonRect
;
1772 pcbi
->stateButton
= 0;
1773 if (lphc
->wState
& CBF_BUTTONDOWN
)
1774 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1775 if (IsRectEmpty(&lphc
->buttonRect
))
1776 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1777 pcbi
->hwndCombo
= lphc
->self
;
1778 pcbi
->hwndItem
= lphc
->hWndEdit
;
1779 pcbi
->hwndList
= lphc
->hWndLBox
;
1783 static char *strdupA(LPCSTR str
)
1788 if(!str
) return NULL
;
1791 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1793 memcpy(ret
, str
, len
+ 1);
1797 /***********************************************************************
1798 * ComboWndProc_common
1800 LRESULT WINAPI
ComboWndProc_common( HWND hwnd
, UINT message
,
1801 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1803 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1807 pWnd
= ValidateHwnd(hwnd
);
1812 NtUserSetWindowFNID(hwnd
, FNID_COMBOBOX
);
1816 if (pWnd
->fnid
!= FNID_COMBOBOX
)
1818 ERR("Wrong window class for ComboBox! fnId 0x%x\n",pWnd
->fnid
);
1825 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1826 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1828 if( lphc
|| message
== WM_NCCREATE
)
1832 /* System messages */
1836 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1837 ((LPCREATESTRUCTA
)lParam
)->style
;
1838 return COMBO_NCCreate(hwnd
, style
);
1841 COMBO_NCDestroy(lphc
);
1843 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
1845 break;/* -> DefWindowProc */
1853 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1854 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1858 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1859 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1861 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1864 case WM_PRINTCLIENT
:
1867 /* wParam may contain a valid HDC! */
1868 return COMBO_Paint(lphc
, (HDC
)wParam
);
1871 /* do all painting in WM_PAINT like Windows does */
1876 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1877 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1879 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1881 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1882 result
|= DLGC_WANTMESSAGE
;
1887 if( lphc
->hWndLBox
&&
1888 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
, lParam
);
1891 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1894 return (LRESULT
)lphc
->hFont
;
1896 if( lphc
->wState
& CBF_EDIT
) {
1897 SetFocus( lphc
->hWndEdit
);
1898 /* The first time focus is received, select all the text */
1899 if( !(lphc
->wState
& CBF_BEENFOCUSED
) ) {
1900 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
1901 lphc
->wState
|= CBF_BEENFOCUSED
;
1905 COMBO_SetFocus( lphc
);
1909 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1911 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1912 COMBO_KillFocus( lphc
);
1916 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1918 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1919 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1921 case WM_GETTEXTLENGTH
:
1923 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1925 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1926 if (j
== -1) return 0;
1927 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1928 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1930 else if( lphc
->wState
& CBF_EDIT
)
1933 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1934 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1935 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1936 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1943 if( lphc
->wState
& CBF_EDIT
)
1945 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1946 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1952 case WM_COMPAREITEM
:
1953 case WM_MEASUREITEM
:
1954 return COMBO_ItemOp(lphc
, message
, lParam
);
1956 if( lphc
->wState
& CBF_EDIT
)
1957 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1958 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1960 /* Force the control to repaint when the enabled state changes. */
1961 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1965 lphc
->wState
&= ~CBF_NOREDRAW
;
1967 lphc
->wState
|= CBF_NOREDRAW
;
1969 if( lphc
->wState
& CBF_EDIT
)
1970 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1971 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
1974 if( KF_ALTDOWN
& HIWORD(lParam
) ) // ReactOS (wine) KEYDATA_ALT
1975 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1976 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1980 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
1981 (lphc
->wState
& CBF_DROPPED
))
1983 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
1986 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
1988 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1997 if( lphc
->wState
& CBF_EDIT
)
1998 hwndTarget
= lphc
->hWndEdit
;
2000 hwndTarget
= lphc
->hWndLBox
;
2002 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
2003 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
2005 case WM_LBUTTONDOWN
:
2006 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
2007 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
2010 COMBO_LButtonUp( lphc
);
2013 if( lphc
->wState
& CBF_CAPTURE
)
2014 COMBO_MouseMove( lphc
, wParam
, lParam
);
2018 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2019 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2020 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2022 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
2023 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2026 /* Combo messages */
2031 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2032 CharLowerW((LPWSTR
)lParam
);
2033 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2034 CharUpperW((LPWSTR
)lParam
);
2035 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
2037 else /* unlike the unicode version, the ansi version does not overwrite
2038 the string if converting case */
2040 char *string
= NULL
;
2042 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2044 string
= strdupA((LPSTR
)lParam
);
2048 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2050 string
= strdupA((LPSTR
)lParam
);
2054 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
2055 HeapFree(GetProcessHeap(), 0, string
);
2058 case CB_INSERTSTRING
:
2061 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2062 CharLowerW((LPWSTR
)lParam
);
2063 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2064 CharUpperW((LPWSTR
)lParam
);
2065 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2069 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2070 CharLowerA((LPSTR
)lParam
);
2071 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2072 CharUpperA((LPSTR
)lParam
);
2074 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2076 case CB_DELETESTRING
:
2077 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2078 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2079 case CB_SELECTSTRING
:
2080 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2082 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2083 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2084 case CB_FINDSTRINGEXACT
:
2085 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2086 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2087 case CB_SETITEMHEIGHT
:
2088 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2089 case CB_GETITEMHEIGHT
:
2090 if( (INT
)wParam
>= 0 ) /* listbox item */
2091 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2092 return CBGetTextAreaHeight(hwnd
, lphc
);
2093 case CB_RESETCONTENT
:
2094 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2095 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2097 static const WCHAR empty_stringW
[] = { 0 };
2098 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2101 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2103 case CB_INITSTORAGE
:
2104 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2105 case CB_GETHORIZONTALEXTENT
:
2106 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2107 case CB_SETHORIZONTALEXTENT
:
2108 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2109 case CB_GETTOPINDEX
:
2110 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2112 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2114 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2115 case CB_SETDROPPEDWIDTH
:
2116 if( (CB_GETTYPE(lphc
) == CBS_SIMPLE
) ||
2117 (INT
)wParam
>= 32768 )
2119 /* new value must be higher than combobox width */
2120 if((INT
)wParam
>= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
)
2121 lphc
->droppedWidth
= wParam
;
2123 lphc
->droppedWidth
= 0;
2125 /* recalculate the combobox area */
2126 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
2129 case CB_GETDROPPEDWIDTH
:
2130 if( lphc
->droppedWidth
)
2131 return lphc
->droppedWidth
;
2132 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2133 case CB_GETDROPPEDCONTROLRECT
:
2134 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2136 case CB_GETDROPPEDSTATE
:
2137 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
2139 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2140 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2142 case CB_SHOWDROPDOWN
:
2143 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2147 if( !(lphc
->wState
& CBF_DROPPED
) )
2151 if( lphc
->wState
& CBF_DROPPED
)
2152 CBRollUp( lphc
, FALSE
, TRUE
);
2156 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2158 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2160 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2162 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2164 /* no LBN_SELCHANGE in this case, update manually */
2165 if( lphc
->wState
& CBF_EDIT
)
2166 CBUpdateEdit( lphc
, (INT
)wParam
);
2168 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2169 lphc
->wState
&= ~CBF_SELCHANGE
;
2172 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2173 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2174 case CB_GETLBTEXTLEN
:
2175 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2176 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2177 case CB_GETITEMDATA
:
2178 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2179 case CB_SETITEMDATA
:
2180 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2182 /* Edit checks passed parameters itself */
2183 if( lphc
->wState
& CBF_EDIT
)
2184 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2187 if( lphc
->wState
& CBF_EDIT
)
2188 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2189 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2191 case CB_SETEXTENDEDUI
:
2192 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2195 lphc
->wState
|= CBF_EUI
;
2196 else lphc
->wState
&= ~CBF_EUI
;
2198 case CB_GETEXTENDEDUI
:
2199 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2200 case CB_GETCOMBOBOXINFO
:
2201 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2203 if( lphc
->wState
& CBF_EDIT
)
2204 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2207 case WM_UPDATEUISTATE
:
2209 DefWindowProcW(lphc
->self
, message
, wParam
, lParam
);
2211 DefWindowProcA(lphc
->self
, message
, wParam
, lParam
);
2213 if (COMBO_update_uistate(lphc
))
2216 if( !(lphc
->wState
& CBF_EDIT
) )
2217 NtUserInvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2222 if (message
>= WM_USER
)
2223 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2224 message
- WM_USER
, wParam
, lParam
);
2227 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2228 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2231 /***********************************************************************
2234 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2237 LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2239 if (!IsWindow(hwnd
)) return 0;
2240 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, FALSE
);
2243 /***********************************************************************
2246 LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2248 if (!IsWindow(hwnd
)) return 0;
2249 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, TRUE
);
2252 /*************************************************************************
2253 * GetComboBoxInfo (USER32.@)
2255 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2256 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2258 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2260 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);
2262 return NtUserGetComboBoxInfo(hwndCombo
, pcbi
);