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
;
1046 r
.top
= rect
.bottom
;
1047 r
.right
= r
.left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1048 r
.bottom
= r
.top
+ nDroppedHeight
;
1050 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1051 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
1052 mon_info
.cbSize
= sizeof(mon_info
);
1053 GetMonitorInfoW( monitor
, &mon_info
);
1055 if (r
.bottom
> mon_info
.rcWork
.bottom
)
1057 r
.top
= max( rect
.top
- nDroppedHeight
, mon_info
.rcWork
.top
);
1058 r
.bottom
= min( r
.top
+ nDroppedHeight
, mon_info
.rcWork
.bottom
);
1060 //// ReactOS : Use HWND_TOPMOST See Bug 5705 or CORE-5186....
1061 SetWindowPos( lphc
->hWndLBox
, HWND_TOPMOST
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
1062 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1065 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1066 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1067 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1069 EnableWindow( lphc
->hWndLBox
, TRUE
);
1070 if (GetCapture() != lphc
->self
)
1071 SetCapture(lphc
->hWndLBox
);
1074 /***********************************************************************
1077 * Hide listbox popup.
1079 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1081 HWND hWnd
= lphc
->self
;
1083 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1084 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1086 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1088 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1091 if( lphc
->wState
& CBF_DROPPED
)
1095 lphc
->wState
&= ~CBF_DROPPED
;
1096 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1098 if(GetCapture() == lphc
->hWndLBox
)
1103 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1105 rect
= lphc
->buttonRect
;
1116 rect
= lphc
->textRect
;
1121 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1122 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1123 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1124 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1129 /***********************************************************************
1132 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1134 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1136 if( lphc
->wState
& CBF_DROPPED
)
1138 CBRollUp( lphc
, ok
, bRedrawButton
);
1146 /***********************************************************************
1149 static void CBRepaintButton( LPHEADCOMBO lphc
)
1151 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1152 UpdateWindow(lphc
->self
);
1155 /***********************************************************************
1158 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1160 if( !(lphc
->wState
& CBF_FOCUSED
) )
1162 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1163 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1165 /* This is wrong. Message sequences seem to indicate that this
1166 is set *after* the notify. */
1167 /* lphc->wState |= CBF_FOCUSED; */
1169 if( !(lphc
->wState
& CBF_EDIT
) )
1170 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1172 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1173 lphc
->wState
|= CBF_FOCUSED
;
1177 /***********************************************************************
1180 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1182 HWND hWnd
= lphc
->self
;
1184 if( lphc
->wState
& CBF_FOCUSED
)
1186 CBRollUp( lphc
, FALSE
, TRUE
);
1187 if( IsWindow( hWnd
) )
1189 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1190 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1192 lphc
->wState
&= ~CBF_FOCUSED
;
1195 if( !(lphc
->wState
& CBF_EDIT
) )
1196 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1198 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1203 /***********************************************************************
1206 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1208 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1210 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1212 switch( HIWORD(wParam
) >> 8 )
1214 case (EN_SETFOCUS
>> 8):
1216 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1218 COMBO_SetFocus( lphc
);
1221 case (EN_KILLFOCUS
>> 8):
1223 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1225 /* NOTE: it seems that Windows' edit control sends an
1226 * undocumented message WM_USER + 0x1B instead of this
1227 * notification (only when it happens to be a part of
1228 * the combo). ?? - AK.
1231 COMBO_KillFocus( lphc
);
1235 case (EN_CHANGE
>> 8):
1237 * In some circumstances (when the selection of the combobox
1238 * is changed for example) we don't want the EN_CHANGE notification
1239 * to be forwarded to the parent of the combobox. This code
1240 * checks a flag that is set in these occasions and ignores the
1243 if (lphc
->wState
& CBF_NOLBSELECT
)
1245 lphc
->wState
&= ~CBF_NOLBSELECT
;
1249 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1252 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1253 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1256 case (EN_UPDATE
>> 8):
1257 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1258 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1261 case (EN_ERRSPACE
>> 8):
1262 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1265 else if( lphc
->hWndLBox
== hWnd
)
1267 switch( (short)HIWORD(wParam
) )
1270 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1274 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1280 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1282 /* do not roll up if selection is being tracked
1283 * by arrow keys in the dropdown listbox */
1284 if (!(lphc
->wState
& CBF_NOROLLUP
))
1286 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1288 else lphc
->wState
&= ~CBF_NOROLLUP
;
1290 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1292 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1294 if( lphc
->wState
& CBF_EDIT
)
1296 INT index
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1297 lphc
->wState
|= CBF_NOLBSELECT
;
1298 CBUpdateEdit( lphc
, index
);
1299 /* select text in edit, as Windows does */
1300 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1304 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1305 UpdateWindow(lphc
->self
);
1312 /* nothing to do here since ComboLBox always resets the focus to its
1313 * combo/edit counterpart */
1320 /***********************************************************************
1323 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1325 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1327 HWND hWnd
= lphc
->self
;
1328 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1330 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1336 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1337 lpIS
->CtlType
= ODT_COMBOBOX
;
1339 lpIS
->hwndItem
= hWnd
;
1344 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1345 lpIS
->CtlType
= ODT_COMBOBOX
;
1347 lpIS
->hwndItem
= hWnd
;
1350 case WM_COMPAREITEM
:
1352 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1353 lpIS
->CtlType
= ODT_COMBOBOX
;
1355 lpIS
->hwndItem
= hWnd
;
1358 case WM_MEASUREITEM
:
1360 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1361 lpIS
->CtlType
= ODT_COMBOBOX
;
1366 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1370 /***********************************************************************
1373 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1377 if( lphc
->wState
& CBF_EDIT
)
1378 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1380 /* get it from the listbox */
1382 if (!count
|| !buf
) return 0;
1383 if( lphc
->hWndLBox
)
1385 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1386 if (idx
== LB_ERR
) goto error
;
1387 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1388 if (length
== LB_ERR
) goto error
;
1390 /* 'length' is without the terminating character */
1391 if (length
>= count
)
1393 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1394 if (!lpBuffer
) goto error
;
1395 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1397 /* truncate if buffer is too short */
1398 if (length
!= LB_ERR
)
1400 lstrcpynW( buf
, lpBuffer
, count
);
1403 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1405 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1407 if (length
== LB_ERR
) return 0;
1411 error
: /* error - truncate string, return zero */
1417 /***********************************************************************
1420 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1421 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1423 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1427 if( lphc
->wState
& CBF_EDIT
)
1428 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1430 /* get it from the listbox */
1432 if (!count
|| !buf
) return 0;
1433 if( lphc
->hWndLBox
)
1435 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1436 if (idx
== LB_ERR
) goto error
;
1437 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1438 if (length
== LB_ERR
) goto error
;
1440 /* 'length' is without the terminating character */
1441 if (length
>= count
)
1443 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1444 if (!lpBuffer
) goto error
;
1445 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1447 /* truncate if buffer is too short */
1448 if (length
!= LB_ERR
)
1450 lstrcpynA( buf
, lpBuffer
, count
);
1453 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1455 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1457 if (length
== LB_ERR
) return 0;
1461 error
: /* error - truncate string, return zero */
1467 /***********************************************************************
1470 * This function sets window positions according to the updated
1471 * component placement struct.
1473 static void CBResetPos(
1475 const RECT
*rectEdit
,
1479 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1481 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1482 * sizing messages */
1484 if( lphc
->wState
& CBF_EDIT
)
1485 SetWindowPos( lphc
->hWndEdit
, 0,
1486 rectEdit
->left
, rectEdit
->top
,
1487 rectEdit
->right
- rectEdit
->left
,
1488 rectEdit
->bottom
- rectEdit
->top
,
1489 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1491 SetWindowPos( lphc
->hWndLBox
, 0,
1492 rectLB
->left
, rectLB
->top
,
1493 rectLB
->right
- rectLB
->left
,
1494 rectLB
->bottom
- rectLB
->top
,
1495 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1499 if( lphc
->wState
& CBF_DROPPED
)
1501 lphc
->wState
&= ~CBF_DROPPED
;
1502 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1505 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1506 RedrawWindow( lphc
->self
, NULL
, 0,
1507 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1512 /***********************************************************************
1515 static void COMBO_Size( LPHEADCOMBO lphc
)
1518 * Those controls are always the same height. So we have to make sure
1519 * they are not resized to another value.
1521 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1523 int newComboHeight
, curComboHeight
, curComboWidth
;
1526 GetWindowRect(lphc
->self
, &rc
);
1527 curComboHeight
= rc
.bottom
- rc
.top
;
1528 curComboWidth
= rc
.right
- rc
.left
;
1529 newComboHeight
= CBGetTextAreaHeight(lphc
->self
, lphc
) + 2*COMBO_YBORDERSIZE();
1532 * Resizing a combobox has another side effect, it resizes the dropped
1533 * rectangle as well. However, it does it only if the new height for the
1534 * combobox is more than the height it should have. In other words,
1535 * if the application resizing the combobox only had the intention to resize
1536 * the actual control, for example, to do the layout of a dialog that is
1537 * resized, the height of the dropdown is not changed.
1539 if( curComboHeight
> newComboHeight
)
1541 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1542 curComboHeight
, newComboHeight
, lphc
->droppedRect
.bottom
,
1543 lphc
->droppedRect
.top
);
1544 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ curComboHeight
- newComboHeight
;
1547 * Restore original height
1549 if( curComboHeight
!= newComboHeight
)
1550 SetWindowPos(lphc
->self
, 0, 0, 0, curComboWidth
, newComboHeight
,
1551 SWP_NOZORDER
|SWP_NOMOVE
|SWP_NOACTIVATE
|SWP_NOREDRAW
);
1554 CBCalcPlacement(lphc
->self
,
1558 &lphc
->droppedRect
);
1560 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1564 /***********************************************************************
1567 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1572 lphc
->hFont
= hFont
;
1575 * Propagate to owned windows.
1577 if( lphc
->wState
& CBF_EDIT
)
1578 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1579 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1582 * Redo the layout of the control.
1584 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1586 CBCalcPlacement(lphc
->self
,
1590 &lphc
->droppedRect
);
1592 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1596 CBForceDummyResize(lphc
);
1601 /***********************************************************************
1602 * COMBO_SetItemHeight
1604 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1606 LRESULT lRet
= CB_ERR
;
1608 if( index
== -1 ) /* set text field height */
1610 if( height
< 32768 )
1612 lphc
->editHeight
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1615 * Redo the layout of the control.
1617 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1619 CBCalcPlacement(lphc
->self
,
1623 &lphc
->droppedRect
);
1625 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1629 CBForceDummyResize(lphc
);
1635 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1636 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1637 (WPARAM
)index
, (LPARAM
)height
);
1641 /***********************************************************************
1642 * COMBO_SelectString
1644 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1646 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
) :
1647 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
);
1650 if( lphc
->wState
& CBF_EDIT
)
1651 CBUpdateEdit( lphc
, index
);
1654 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1657 return (LRESULT
)index
;
1660 /***********************************************************************
1663 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1667 HWND hWnd
= lphc
->self
;
1669 pt
.x
= (short)LOWORD(lParam
);
1670 pt
.y
= (short)HIWORD(lParam
);
1671 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1673 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1674 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1676 lphc
->wState
|= CBF_BUTTONDOWN
;
1677 if( lphc
->wState
& CBF_DROPPED
)
1679 /* got a click to cancel selection */
1681 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1682 CBRollUp( lphc
, TRUE
, FALSE
);
1683 if( !IsWindow( hWnd
) ) return;
1685 if( lphc
->wState
& CBF_CAPTURE
)
1687 lphc
->wState
&= ~CBF_CAPTURE
;
1693 /* drop down the listbox and start tracking */
1695 lphc
->wState
|= CBF_CAPTURE
;
1699 if( bButton
) CBRepaintButton( lphc
);
1703 /***********************************************************************
1706 * Release capture and stop tracking if needed.
1708 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1710 if( lphc
->wState
& CBF_CAPTURE
)
1712 lphc
->wState
&= ~CBF_CAPTURE
;
1713 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1715 INT index
= CBUpdateLBox( lphc
, TRUE
);
1716 /* Update edit only if item is in the list */
1719 lphc
->wState
|= CBF_NOLBSELECT
;
1720 CBUpdateEdit( lphc
, index
);
1721 lphc
->wState
&= ~CBF_NOLBSELECT
;
1725 SetCapture(lphc
->hWndLBox
);
1728 if( lphc
->wState
& CBF_BUTTONDOWN
)
1730 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1731 CBRepaintButton( lphc
);
1735 /***********************************************************************
1738 * Two things to do - track combo button and release capture when
1739 * pointer goes into the listbox.
1741 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1746 pt
.x
= (short)LOWORD(lParam
);
1747 pt
.y
= (short)HIWORD(lParam
);
1749 if( lphc
->wState
& CBF_BUTTONDOWN
)
1753 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1757 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1758 CBRepaintButton( lphc
);
1762 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1763 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1764 if( PtInRect(&lbRect
, pt
) )
1766 lphc
->wState
&= ~CBF_CAPTURE
;
1768 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1770 /* hand over pointer tracking */
1771 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1775 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1777 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1780 pcbi
->rcItem
= lphc
->textRect
;
1781 pcbi
->rcButton
= lphc
->buttonRect
;
1782 pcbi
->stateButton
= 0;
1783 if (lphc
->wState
& CBF_BUTTONDOWN
)
1784 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1785 if (IsRectEmpty(&lphc
->buttonRect
))
1786 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1787 pcbi
->hwndCombo
= lphc
->self
;
1788 pcbi
->hwndItem
= lphc
->hWndEdit
;
1789 pcbi
->hwndList
= lphc
->hWndLBox
;
1793 static char *strdupA(LPCSTR str
)
1798 if(!str
) return NULL
;
1801 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1803 memcpy(ret
, str
, len
+ 1);
1807 /***********************************************************************
1808 * ComboWndProc_common
1810 LRESULT WINAPI
ComboWndProc_common( HWND hwnd
, UINT message
,
1811 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1813 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1817 pWnd
= ValidateHwnd(hwnd
);
1822 NtUserSetWindowFNID(hwnd
, FNID_COMBOBOX
);
1826 if (pWnd
->fnid
!= FNID_COMBOBOX
)
1828 ERR("Wrong window class for ComboBox! fnId 0x%x\n",pWnd
->fnid
);
1835 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1836 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1838 if( lphc
|| message
== WM_NCCREATE
)
1842 /* System messages */
1846 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1847 ((LPCREATESTRUCTA
)lParam
)->style
;
1848 return COMBO_NCCreate(hwnd
, style
);
1851 COMBO_NCDestroy(lphc
);
1853 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
1855 break;/* -> DefWindowProc */
1863 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1864 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1868 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1869 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1871 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1874 case WM_PRINTCLIENT
:
1877 /* wParam may contain a valid HDC! */
1878 return COMBO_Paint(lphc
, (HDC
)wParam
);
1881 /* do all painting in WM_PAINT like Windows does */
1886 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1887 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1889 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1891 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1892 result
|= DLGC_WANTMESSAGE
;
1897 if( lphc
->hWndLBox
&&
1898 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1901 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1904 return (LRESULT
)lphc
->hFont
;
1906 if( lphc
->wState
& CBF_EDIT
) {
1907 SetFocus( lphc
->hWndEdit
);
1908 /* The first time focus is received, select all the text */
1909 if( !(lphc
->wState
& CBF_BEENFOCUSED
) ) {
1910 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
1911 lphc
->wState
|= CBF_BEENFOCUSED
;
1915 COMBO_SetFocus( lphc
);
1919 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1921 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1922 COMBO_KillFocus( lphc
);
1926 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1928 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1929 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1931 case WM_GETTEXTLENGTH
:
1933 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1935 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1936 if (j
== -1) return 0;
1937 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1938 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1940 else if( lphc
->wState
& CBF_EDIT
)
1943 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1944 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1945 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1946 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1953 if( lphc
->wState
& CBF_EDIT
)
1955 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1956 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1962 case WM_COMPAREITEM
:
1963 case WM_MEASUREITEM
:
1964 return COMBO_ItemOp(lphc
, message
, lParam
);
1966 if( lphc
->wState
& CBF_EDIT
)
1967 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1968 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1970 /* Force the control to repaint when the enabled state changes. */
1971 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1975 lphc
->wState
&= ~CBF_NOREDRAW
;
1977 lphc
->wState
|= CBF_NOREDRAW
;
1979 if( lphc
->wState
& CBF_EDIT
)
1980 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1981 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
1984 if( KF_ALTDOWN
& HIWORD(lParam
) ) // ReactOS (wine) KEYDATA_ALT
1985 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1986 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1990 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
1991 (lphc
->wState
& CBF_DROPPED
))
1993 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
1996 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
1998 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2007 if( lphc
->wState
& CBF_EDIT
)
2008 hwndTarget
= lphc
->hWndEdit
;
2010 hwndTarget
= lphc
->hWndLBox
;
2012 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
2013 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
2015 case WM_LBUTTONDOWN
:
2016 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
2017 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
2020 COMBO_LButtonUp( lphc
);
2023 if( lphc
->wState
& CBF_CAPTURE
)
2024 COMBO_MouseMove( lphc
, wParam
, lParam
);
2028 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2029 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2030 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2032 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
2033 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2036 /* Combo messages */
2041 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2042 CharLowerW((LPWSTR
)lParam
);
2043 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2044 CharUpperW((LPWSTR
)lParam
);
2045 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
2047 else /* unlike the unicode version, the ansi version does not overwrite
2048 the string if converting case */
2050 char *string
= NULL
;
2052 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2054 string
= strdupA((LPSTR
)lParam
);
2058 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2060 string
= strdupA((LPSTR
)lParam
);
2064 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
2065 HeapFree(GetProcessHeap(), 0, string
);
2068 case CB_INSERTSTRING
:
2071 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2072 CharLowerW((LPWSTR
)lParam
);
2073 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2074 CharUpperW((LPWSTR
)lParam
);
2075 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2079 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2080 CharLowerA((LPSTR
)lParam
);
2081 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2082 CharUpperA((LPSTR
)lParam
);
2084 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2086 case CB_DELETESTRING
:
2087 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2088 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2089 case CB_SELECTSTRING
:
2090 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2092 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2093 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2094 case CB_FINDSTRINGEXACT
:
2095 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2096 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2097 case CB_SETITEMHEIGHT
:
2098 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2099 case CB_GETITEMHEIGHT
:
2100 if( (INT
)wParam
>= 0 ) /* listbox item */
2101 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2102 return CBGetTextAreaHeight(hwnd
, lphc
);
2103 case CB_RESETCONTENT
:
2104 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2105 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2107 static const WCHAR empty_stringW
[] = { 0 };
2108 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2111 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2113 case CB_INITSTORAGE
:
2114 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2115 case CB_GETHORIZONTALEXTENT
:
2116 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2117 case CB_SETHORIZONTALEXTENT
:
2118 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2119 case CB_GETTOPINDEX
:
2120 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2122 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2124 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2125 case CB_SETDROPPEDWIDTH
:
2126 if( (CB_GETTYPE(lphc
) == CBS_SIMPLE
) ||
2127 (INT
)wParam
>= 32768 )
2129 /* new value must be higher than combobox width */
2130 if((INT
)wParam
>= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
)
2131 lphc
->droppedWidth
= wParam
;
2133 lphc
->droppedWidth
= 0;
2135 /* recalculate the combobox area */
2136 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
2139 case CB_GETDROPPEDWIDTH
:
2140 if( lphc
->droppedWidth
)
2141 return lphc
->droppedWidth
;
2142 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2143 case CB_GETDROPPEDCONTROLRECT
:
2144 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2146 case CB_GETDROPPEDSTATE
:
2147 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
2149 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2150 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2152 case CB_SHOWDROPDOWN
:
2153 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2157 if( !(lphc
->wState
& CBF_DROPPED
) )
2161 if( lphc
->wState
& CBF_DROPPED
)
2162 CBRollUp( lphc
, FALSE
, TRUE
);
2166 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2168 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2170 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2172 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2174 /* no LBN_SELCHANGE in this case, update manually */
2175 if( lphc
->wState
& CBF_EDIT
)
2176 CBUpdateEdit( lphc
, (INT
)wParam
);
2178 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2179 lphc
->wState
&= ~CBF_SELCHANGE
;
2182 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2183 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2184 case CB_GETLBTEXTLEN
:
2185 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2186 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2187 case CB_GETITEMDATA
:
2188 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2189 case CB_SETITEMDATA
:
2190 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2192 /* Edit checks passed parameters itself */
2193 if( lphc
->wState
& CBF_EDIT
)
2194 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2197 if( lphc
->wState
& CBF_EDIT
)
2198 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2199 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2201 case CB_SETEXTENDEDUI
:
2202 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2205 lphc
->wState
|= CBF_EUI
;
2206 else lphc
->wState
&= ~CBF_EUI
;
2208 case CB_GETEXTENDEDUI
:
2209 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2210 case CB_GETCOMBOBOXINFO
:
2211 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2213 if( lphc
->wState
& CBF_EDIT
)
2214 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2217 case WM_UPDATEUISTATE
:
2219 DefWindowProcW(lphc
->self
, message
, wParam
, lParam
);
2221 DefWindowProcA(lphc
->self
, message
, wParam
, lParam
);
2223 if (COMBO_update_uistate(lphc
))
2226 if( !(lphc
->wState
& CBF_EDIT
) )
2227 NtUserInvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2232 if (message
>= WM_USER
)
2233 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2234 message
- WM_USER
, wParam
, lParam
);
2237 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2238 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2241 /***********************************************************************
2244 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2247 LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2249 if (!IsWindow(hwnd
)) return 0;
2250 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, FALSE
);
2253 /***********************************************************************
2256 LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2258 if (!IsWindow(hwnd
)) return 0;
2259 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, TRUE
);
2262 /*************************************************************************
2263 * GetComboBoxInfo (USER32.@)
2265 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2266 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2268 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2270 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);
2272 return NtUserGetComboBoxInfo(hwndCombo
, pcbi
);