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
26 WINE_DEFAULT_DEBUG_CHANNEL(combo
);
29 * Additional combo box definitions
32 #define CB_NOTIFY( lphc, code ) \
33 (SendMessageW((lphc)->owner, WM_COMMAND, \
34 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
36 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
37 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
38 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
39 #define CB_HWND( lphc ) ((lphc)->self)
41 /* ReactOS already define in include/controls.h We have it here as a sync note. */
42 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
45 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
50 static HBITMAP hComboBmp
= 0;
51 static UINT CBitHeight
, CBitWidth
;
54 * Look and feel dependent "constants"
57 #define COMBO_YBORDERGAP 5
58 #define COMBO_XBORDERSIZE() 2
59 #define COMBO_YBORDERSIZE() 2
60 #define COMBO_EDITBUTTONSPACE() 0
61 #define EDIT_CONTROL_PADDING() 1
63 /*********************************************************************
64 * combo class descriptor
66 static const WCHAR comboboxW
[] = {'C','o','m','b','o','B','o','x',0};
67 const struct builtin_class_descr COMBO_builtin_class
=
70 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
72 ComboWndProcA
, /* procA */
73 ComboWndProcW
, /* procW */
75 WINPROC_COMBO
, /* proc */
77 sizeof(HEADCOMBO
*), /* extra */
78 IDC_ARROW
, /* cursor */
83 /***********************************************************************
86 * Load combo button bitmap.
88 static BOOL
COMBO_Init(void)
92 if( hComboBmp
) return TRUE
;
93 if( (hDC
= CreateCompatibleDC(0)) )
96 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
102 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
103 CBitHeight
= bm
.bmHeight
;
104 CBitWidth
= bm
.bmWidth
;
106 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
108 hPrevB
= SelectObject( hDC
, hComboBmp
);
109 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
110 InvertRect( hDC
, &r
);
111 SelectObject( hDC
, hPrevB
);
121 /* Retrieve the UI state for the control */
122 static BOOL
COMBO_update_uistate(LPHEADCOMBO lphc
)
126 prev_flags
= lphc
->UIState
;
127 lphc
->UIState
= DefWindowProcW(lphc
->self
, WM_QUERYUISTATE
, 0, 0);
128 return prev_flags
!= lphc
->UIState
;
132 /***********************************************************************
135 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
139 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
142 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)lphc
);
145 COMBO_update_uistate(lphc
);
148 /* some braindead apps do try to use scrollbar/border flags */
150 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
151 SetWindowLongPtrW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
154 * We also have to remove the client edge style to make sure
155 * we don't end-up with a non client area.
157 SetWindowLongPtrW( hwnd
, GWL_EXSTYLE
,
158 GetWindowLongPtrW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
160 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
161 lphc
->dwStyle
|= CBS_HASSTRINGS
;
162 if( !(GetWindowLongPtrW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
163 lphc
->wState
|= CBF_NOTIFY
;
165 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
171 /***********************************************************************
174 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
179 TRACE("[%p]: freeing storage\n", lphc
->self
);
181 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
182 DestroyWindow( lphc
->hWndLBox
);
184 SetWindowLongPtrW( lphc
->self
, 0, 0 );
185 HeapFree( GetProcessHeap(), 0, lphc
);
190 /***********************************************************************
191 * CBGetTextAreaHeight
193 * This method will calculate the height of the text area of the
195 * The height of the text area is set in two ways.
196 * It can be set explicitly through a combobox message or through a
197 * WM_MEASUREITEM callback.
198 * If this is not the case, the height is set to font height + 4px
199 * This height was determined through experimentation.
200 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
202 static INT
CBGetTextAreaHeight(
208 if( lphc
->editHeight
) /* explicitly set height */
210 iTextItemHeight
= lphc
->editHeight
;
215 HDC hDC
= GetDC(hwnd
);
220 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
222 GetTextMetricsW(hDC
, &tm
);
224 baseUnitY
= tm
.tmHeight
;
227 SelectObject( hDC
, hPrevFont
);
229 ReleaseDC(hwnd
, hDC
);
231 iTextItemHeight
= baseUnitY
+ 4;
235 * Check the ownerdraw case if we haven't asked the parent the size
238 if ( CB_OWNERDRAWN(lphc
) &&
239 (lphc
->wState
& CBF_MEASUREITEM
) )
241 MEASUREITEMSTRUCT measureItem
;
243 INT originalItemHeight
= iTextItemHeight
;
244 UINT id
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
247 * We use the client rect for the width of the item.
249 GetClientRect(hwnd
, &clientRect
);
251 lphc
->wState
&= ~CBF_MEASUREITEM
;
254 * Send a first one to measure the size of the text area
256 measureItem
.CtlType
= ODT_COMBOBOX
;
257 measureItem
.CtlID
= id
;
258 measureItem
.itemID
= -1;
259 measureItem
.itemWidth
= clientRect
.right
;
260 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
261 measureItem
.itemData
= 0;
262 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
263 iTextItemHeight
= 6 + measureItem
.itemHeight
;
266 * Send a second one in the case of a fixed ownerdraw list to calculate the
267 * size of the list items. (we basically do this on behalf of the listbox)
269 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
271 measureItem
.CtlType
= ODT_COMBOBOX
;
272 measureItem
.CtlID
= id
;
273 measureItem
.itemID
= 0;
274 measureItem
.itemWidth
= clientRect
.right
;
275 measureItem
.itemHeight
= originalItemHeight
;
276 measureItem
.itemData
= 0;
277 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
278 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
282 * Keep the size for the next time
284 lphc
->editHeight
= iTextItemHeight
;
287 return iTextItemHeight
;
290 /***********************************************************************
293 * The dummy resize is used for listboxes that have a popup to trigger
294 * a re-arranging of the contents of the combobox and the recalculation
295 * of the size of the "real" control window.
297 static void CBForceDummyResize(
303 newComboHeight
= CBGetTextAreaHeight(lphc
->self
,lphc
) + 2*COMBO_YBORDERSIZE();
305 GetWindowRect(lphc
->self
, &windowRect
);
308 * We have to be careful, resizing a combobox also has the meaning that the
309 * dropped rect will be resized. In this case, we want to trigger a resize
310 * to recalculate layout but we don't want to change the dropped rectangle
311 * So, we pass the height of text area of control as the height.
312 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
315 SetWindowPos( lphc
->self
,
318 windowRect
.right
- windowRect
.left
,
320 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
323 /***********************************************************************
326 * Set up component coordinates given valid lphc->RectCombo.
328 static void CBCalcPlacement(
336 * Again, start with the client rectangle.
338 GetClientRect(hwnd
, lprEdit
);
343 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
346 * Chop off the bottom part to fit with the height of the text area.
348 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
351 * The button starts the same vertical position as the text area.
353 CopyRect(lprButton
, lprEdit
);
356 * If the combobox is "simple" there is no button.
358 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
359 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
363 * Let's assume the combobox button is the same width as the
365 * size the button horizontally and cut-off the text area.
367 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
368 lprEdit
->right
= lprButton
->left
;
372 * In the case of a dropdown, there is an additional spacing between the
373 * text area and the button.
375 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
377 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
381 * If we have an edit control, we space it away from the borders slightly.
383 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
385 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
389 * Adjust the size of the listbox popup.
391 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
394 * Use the client rectangle to initialize the listbox rectangle
396 GetClientRect(hwnd
, lprLB
);
399 * Then, chop-off the top part.
401 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
406 * Make sure the dropped width is as large as the combobox itself.
408 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
410 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
413 * In the case of a dropdown, the popup listbox is offset to the right.
414 * so, we want to make sure it's flush with the right side of the
417 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
418 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
421 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
424 /* don't allow negative window width */
425 if (lprEdit
->right
< lprEdit
->left
)
426 lprEdit
->right
= lprEdit
->left
;
428 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit
));
430 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton
));
432 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB
));
435 /***********************************************************************
436 * CBGetDroppedControlRect
438 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
440 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
441 of the combo box and the lower right corner of the listbox */
443 GetWindowRect(lphc
->self
, lpRect
);
445 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
446 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
450 /***********************************************************************
453 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
456 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
457 static const WCHAR editName
[] = {'E','d','i','t',0};
459 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
460 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
462 lphc
->owner
= hwndParent
;
465 * The item height and dropped width are not set when the control
468 lphc
->droppedWidth
= lphc
->editHeight
= 0;
471 * The first time we go through, we want to measure the ownerdraw item
473 lphc
->wState
|= CBF_MEASUREITEM
;
475 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
477 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
483 * Initialize the dropped rect to the size of the client area of the
484 * control and then, force all the areas of the combobox to be
487 GetClientRect( hwnd
, &lphc
->droppedRect
);
488 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
491 * Adjust the position of the popup listbox if it's necessary
493 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
495 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
498 * If it's a dropdown, the listbox is offset
500 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
501 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
503 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
504 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
505 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
506 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
507 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
510 /* create listbox popup */
512 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
513 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
515 if( lphc
->dwStyle
& CBS_SORT
)
516 lbeStyle
|= LBS_SORT
;
517 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
518 lbeStyle
|= LBS_HASSTRINGS
;
519 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
520 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
521 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
522 lbeStyle
|= LBS_DISABLENOSCROLL
;
524 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
526 lbeStyle
|= WS_VISIBLE
;
529 * In win 95 look n feel, the listbox in the simple combobox has
530 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
532 lbeStyle
&= ~WS_BORDER
;
533 lbeExStyle
|= WS_EX_CLIENTEDGE
;
537 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
541 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
542 lphc
->droppedRect
.left
,
543 lphc
->droppedRect
.top
,
544 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
545 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
546 hwnd
, (HMENU
)ID_CB_LISTBOX
,
547 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
549 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
550 lphc
->droppedRect
.left
,
551 lphc
->droppedRect
.top
,
552 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
553 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
554 hwnd
, (HMENU
)ID_CB_LISTBOX
,
555 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
560 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
562 if( lphc
->wState
& CBF_EDIT
)
564 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
565 lbeStyle
|= ES_OEMCONVERT
;
566 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
567 lbeStyle
|= ES_AUTOHSCROLL
;
568 if( lphc
->dwStyle
& CBS_LOWERCASE
)
569 lbeStyle
|= ES_LOWERCASE
;
570 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
571 lbeStyle
|= ES_UPPERCASE
;
573 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
576 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
577 lphc
->textRect
.left
, lphc
->textRect
.top
,
578 lphc
->textRect
.right
- lphc
->textRect
.left
,
579 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
580 hwnd
, (HMENU
)ID_CB_EDIT
,
581 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
583 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
584 lphc
->textRect
.left
, lphc
->textRect
.top
,
585 lphc
->textRect
.right
- lphc
->textRect
.left
,
586 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
587 hwnd
, (HMENU
)ID_CB_EDIT
,
588 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
590 if( !lphc
->hWndEdit
)
596 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
598 /* Now do the trick with parent */
599 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
601 * If the combo is a dropdown, we must resize the control
602 * to fit only the text area and button. To do this,
603 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
604 * will take care of setting the height for us.
606 CBForceDummyResize(lphc
);
609 TRACE("init done\n");
612 ERR("edit control failure.\n");
613 } else ERR("listbox failure.\n");
614 } else ERR("no owner for visible combo.\n");
616 /* CreateWindow() will send WM_NCDESTROY to cleanup */
621 /***********************************************************************
624 * Paint combo button (normal, pressed, and disabled states).
626 static void CBPaintButton( LPHEADCOMBO lphc
, HDC hdc
, RECT rectButton
)
628 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
630 if( lphc
->wState
& CBF_NOREDRAW
)
634 if (lphc
->wState
& CBF_BUTTONDOWN
)
635 buttonState
|= DFCS_PUSHED
;
637 if (CB_DISABLED(lphc
))
638 buttonState
|= DFCS_INACTIVE
;
640 DrawFrameControl(hdc
, &rectButton
, DFC_SCROLL
, buttonState
);
643 /***********************************************************************
644 * COMBO_PrepareColors
646 * This method will sent the appropriate WM_CTLCOLOR message to
647 * prepare and setup the colors for the combo's DC.
649 * It also returns the brush to use for the background.
651 static HBRUSH
COMBO_PrepareColors(
658 * Get the background brush for this control.
660 if (CB_DISABLED(lphc
))
663 hBkgBrush
= GetControlColor(lphc
->owner
, lphc
->self
, hDC
, WM_CTLCOLORSTATIC
);
665 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
666 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
669 * We have to change the text color since WM_CTLCOLORSTATIC will
670 * set it to the "enabled" color. This is the same behavior as the
673 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
677 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
679 hBkgBrush
= GetControlColor(lphc
->owner
, lphc
->self
, hDC
, WM_CTLCOLOREDIT
);
681 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
682 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
690 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
695 /***********************************************************************
698 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
700 static void CBPaintText(
704 RECT rectEdit
= lphc
->textRect
;
710 /* follow Windows combobox that sends a bunch of text
711 * inquiries to its listbox while processing WM_PAINT. */
713 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
715 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
717 FIXME("LB_ERR probably not handled yet\n");
718 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
720 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
721 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
722 pText
[size
] = '\0'; /* just in case */
726 if( lphc
->wState
& CBF_EDIT
)
728 static const WCHAR empty_stringW
[] = { 0 };
729 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
730 if( lphc
->wState
& CBF_FOCUSED
)
731 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, MAXLONG
);
733 else if(!(lphc
->wState
& CBF_NOREDRAW
) && IsWindowVisible( lphc
->self
))
735 /* paint text field ourselves */
736 HDC hdc
= hdc_paint
? hdc_paint
: GetDC(lphc
->self
);
737 UINT itemState
= ODS_COMBOBOXEDIT
;
738 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
739 HBRUSH hPrevBrush
, hBkgBrush
;
742 * Give ourselves some space.
744 InflateRect( &rectEdit
, -1, -1 );
746 hBkgBrush
= COMBO_PrepareColors( lphc
, hdc
);
747 hPrevBrush
= SelectObject( hdc
, hBkgBrush
);
748 FillRect( hdc
, &rectEdit
, hBkgBrush
);
750 if( CB_OWNERDRAWN(lphc
) )
754 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
756 /* setup state for DRAWITEM message. Owner will highlight */
757 if ( (lphc
->wState
& CBF_FOCUSED
) &&
758 !(lphc
->wState
& CBF_DROPPED
) )
759 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
761 if (!IsWindowEnabled(lphc
->self
)) itemState
|= ODS_DISABLED
;
763 dis
.CtlType
= ODT_COMBOBOX
;
765 dis
.hwndItem
= lphc
->self
;
766 dis
.itemAction
= ODA_DRAWENTIRE
;
768 dis
.itemState
= itemState
;
770 dis
.rcItem
= rectEdit
;
771 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, id
, 0);
774 * Clip the DC and have the parent draw the item.
776 clipRegion
= set_control_clipping( hdc
, &rectEdit
);
778 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
780 SelectClipRgn( hdc
, clipRegion
);
781 if (clipRegion
) DeleteObject( clipRegion
);
785 static const WCHAR empty_stringW
[] = { 0 };
787 if ( (lphc
->wState
& CBF_FOCUSED
) &&
788 !(lphc
->wState
& CBF_DROPPED
) ) {
791 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
792 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
793 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
799 ETO_OPAQUE
| ETO_CLIPPED
,
801 pText
? pText
: empty_stringW
, size
, NULL
);
804 if(lphc
->wState
& CBF_FOCUSED
&&
805 !(lphc
->wState
& CBF_DROPPED
) &&
806 !(lphc
->UIState
& UISF_HIDEFOCUS
))
808 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
810 DrawFocusRect( hdc
, &rectEdit
);
814 SelectObject(hdc
, hPrevFont
);
817 SelectObject( hdc
, hPrevBrush
);
820 ReleaseDC( lphc
->self
, hdc
);
825 HeapFree( GetProcessHeap(), 0, pText
);
828 /***********************************************************************
831 static void CBPaintBorder(
833 const HEADCOMBO
*lphc
,
838 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
840 GetClientRect(hwnd
, &clientRect
);
844 CopyRect(&clientRect
, &lphc
->textRect
);
846 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
847 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
850 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
853 /***********************************************************************
856 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
861 hDC
= (hParamDC
) ? hParamDC
862 : BeginPaint( lphc
->self
, &ps
);
864 TRACE("hdc=%p\n", hDC
);
866 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
868 HBRUSH hPrevBrush
, hBkgBrush
;
871 * Retrieve the background brush and select it in the
874 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
876 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
877 if (!(lphc
->wState
& CBF_EDIT
))
878 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
881 * In non 3.1 look, there is a sunken border on the combobox
883 CBPaintBorder(lphc
->self
, lphc
, hDC
);
885 if( !IsRectEmpty(&lphc
->buttonRect
) )
887 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
890 /* paint the edit control padding area */
891 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
893 RECT rPadEdit
= lphc
->textRect
;
895 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
897 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
900 if( !(lphc
->wState
& CBF_EDIT
) )
901 CBPaintText( lphc
, hDC
);
904 SelectObject( hDC
, hPrevBrush
);
908 EndPaint(lphc
->self
, &ps
);
913 /***********************************************************************
916 * Select listbox entry according to the contents of the edit control.
918 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
924 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
927 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
929 TRACE("\t edit text length %i\n", length
);
933 GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
934 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, (WPARAM
)(-1), (LPARAM
)pText
);
935 HeapFree( GetProcessHeap(), 0, pText
);
938 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)(bSelect
? idx
: -1), 0);
940 /* probably superfluous but Windows sends this too */
941 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
942 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
947 /***********************************************************************
950 * Copy a listbox entry to the edit control.
952 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
956 static const WCHAR empty_stringW
[] = { 0 };
958 TRACE("\t %i\n", index
);
960 if( index
>= 0 ) /* got an entry */
962 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
963 if( length
!= LB_ERR
)
965 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
967 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)index
, (LPARAM
)pText
);
972 if( CB_HASSTRINGS(lphc
) )
974 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
975 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
976 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
979 if( lphc
->wState
& CBF_FOCUSED
)
980 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
982 HeapFree( GetProcessHeap(), 0, pText
);
985 /***********************************************************************
988 * Show listbox popup.
990 static void CBDropDown( LPHEADCOMBO lphc
)
993 MONITORINFO mon_info
;
998 TRACE("[%p]: drop down\n", lphc
->self
);
1000 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1004 lphc
->wState
|= CBF_DROPPED
;
1005 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1007 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
1009 /* Update edit only if item is in the list */
1010 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
1011 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1015 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1017 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
1018 (WPARAM
)(lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
), 0 );
1019 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1022 /* now set popup position */
1023 GetWindowRect( lphc
->self
, &rect
);
1026 * If it's a dropdown, the listbox is offset
1028 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1029 rect
.left
+= COMBO_EDITBUTTONSPACE();
1031 /* if the dropped height is greater than the total height of the dropped
1032 items list, then force the drop down list height to be the total height
1033 of the items in the dropped list */
1035 /* And Remove any extra space (Best Fit) */
1036 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1037 /* if listbox length has been set directly by its handle */
1038 GetWindowRect(lphc
->hWndLBox
, &r
);
1039 if (nDroppedHeight
< r
.bottom
- r
.top
)
1040 nDroppedHeight
= r
.bottom
- r
.top
;
1041 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1048 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1050 nHeight
= nIHeight
*nItems
;
1052 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1053 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1057 r
.top
= rect
.bottom
;
1058 r
.right
= r
.left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1059 r
.bottom
= r
.top
+ nDroppedHeight
;
1061 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1062 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
1063 mon_info
.cbSize
= sizeof(mon_info
);
1064 GetMonitorInfoW( monitor
, &mon_info
);
1066 if (r
.bottom
> mon_info
.rcWork
.bottom
)
1068 r
.top
= max( rect
.top
- nDroppedHeight
, mon_info
.rcWork
.top
);
1069 r
.bottom
= min( r
.top
+ nDroppedHeight
, mon_info
.rcWork
.bottom
);
1072 SetWindowPos( lphc
->hWndLBox
, HWND_TOPMOST
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
1073 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1076 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1077 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1078 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1080 EnableWindow( lphc
->hWndLBox
, TRUE
);
1081 if (GetCapture() != lphc
->self
)
1082 SetCapture(lphc
->hWndLBox
);
1085 /***********************************************************************
1088 * Hide listbox popup.
1090 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1092 HWND hWnd
= lphc
->self
;
1094 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1095 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1097 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1099 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1102 if( lphc
->wState
& CBF_DROPPED
)
1106 lphc
->wState
&= ~CBF_DROPPED
;
1107 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1109 if(GetCapture() == lphc
->hWndLBox
)
1114 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1116 rect
= lphc
->buttonRect
;
1127 rect
= lphc
->textRect
;
1132 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1133 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1134 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1135 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1140 /***********************************************************************
1143 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1145 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1147 if( lphc
->wState
& CBF_DROPPED
)
1149 CBRollUp( lphc
, ok
, bRedrawButton
);
1157 /***********************************************************************
1160 static void CBRepaintButton( LPHEADCOMBO lphc
)
1162 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1163 UpdateWindow(lphc
->self
);
1166 /***********************************************************************
1169 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1171 if( !(lphc
->wState
& CBF_FOCUSED
) )
1173 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1174 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1176 /* This is wrong. Message sequences seem to indicate that this
1177 is set *after* the notify. */
1178 /* lphc->wState |= CBF_FOCUSED; */
1180 if( !(lphc
->wState
& CBF_EDIT
) )
1181 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1183 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1184 lphc
->wState
|= CBF_FOCUSED
;
1188 /***********************************************************************
1191 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1193 HWND hWnd
= lphc
->self
;
1195 if( lphc
->wState
& CBF_FOCUSED
)
1197 CBRollUp( lphc
, FALSE
, TRUE
);
1198 if( IsWindow( hWnd
) )
1200 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1201 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1203 lphc
->wState
&= ~CBF_FOCUSED
;
1206 if( !(lphc
->wState
& CBF_EDIT
) )
1207 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1209 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1214 /***********************************************************************
1217 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1219 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1221 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1223 switch( HIWORD(wParam
) >> 8 )
1225 case (EN_SETFOCUS
>> 8):
1227 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1229 COMBO_SetFocus( lphc
);
1232 case (EN_KILLFOCUS
>> 8):
1234 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1236 /* NOTE: it seems that Windows' edit control sends an
1237 * undocumented message WM_USER + 0x1B instead of this
1238 * notification (only when it happens to be a part of
1239 * the combo). ?? - AK.
1242 COMBO_KillFocus( lphc
);
1246 case (EN_CHANGE
>> 8):
1248 * In some circumstances (when the selection of the combobox
1249 * is changed for example) we don't want the EN_CHANGE notification
1250 * to be forwarded to the parent of the combobox. This code
1251 * checks a flag that is set in these occasions and ignores the
1254 if (lphc
->wState
& CBF_NOLBSELECT
)
1256 lphc
->wState
&= ~CBF_NOLBSELECT
;
1260 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1263 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1264 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1267 case (EN_UPDATE
>> 8):
1268 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1269 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1272 case (EN_ERRSPACE
>> 8):
1273 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1276 else if( lphc
->hWndLBox
== hWnd
)
1278 switch( (short)HIWORD(wParam
) )
1281 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1285 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1291 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1293 /* do not roll up if selection is being tracked
1294 * by arrow keys in the dropdown listbox */
1295 if (!(lphc
->wState
& CBF_NOROLLUP
))
1297 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1299 else lphc
->wState
&= ~CBF_NOROLLUP
;
1301 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1303 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1305 if( lphc
->wState
& CBF_EDIT
)
1307 INT index
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1308 lphc
->wState
|= CBF_NOLBSELECT
;
1309 CBUpdateEdit( lphc
, index
);
1310 /* select text in edit, as Windows does */
1311 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1315 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1316 UpdateWindow(lphc
->self
);
1323 /* nothing to do here since ComboLBox always resets the focus to its
1324 * combo/edit counterpart */
1331 /***********************************************************************
1334 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1336 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1338 HWND hWnd
= lphc
->self
;
1339 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1341 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1347 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1348 lpIS
->CtlType
= ODT_COMBOBOX
;
1350 lpIS
->hwndItem
= hWnd
;
1355 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1356 lpIS
->CtlType
= ODT_COMBOBOX
;
1358 lpIS
->hwndItem
= hWnd
;
1361 case WM_COMPAREITEM
:
1363 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1364 lpIS
->CtlType
= ODT_COMBOBOX
;
1366 lpIS
->hwndItem
= hWnd
;
1369 case WM_MEASUREITEM
:
1371 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1372 lpIS
->CtlType
= ODT_COMBOBOX
;
1377 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1381 /***********************************************************************
1384 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1388 if( lphc
->wState
& CBF_EDIT
)
1389 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1391 /* get it from the listbox */
1393 if (!count
|| !buf
) return 0;
1394 if( lphc
->hWndLBox
)
1396 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1397 if (idx
== LB_ERR
) goto error
;
1398 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1399 if (length
== LB_ERR
) goto error
;
1401 /* 'length' is without the terminating character */
1402 if (length
>= count
)
1404 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1405 if (!lpBuffer
) goto error
;
1406 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1408 /* truncate if buffer is too short */
1409 if (length
!= LB_ERR
)
1411 lstrcpynW( buf
, lpBuffer
, count
);
1414 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1416 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1418 if (length
== LB_ERR
) return 0;
1422 error
: /* error - truncate string, return zero */
1428 /***********************************************************************
1431 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1432 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1434 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1438 if( lphc
->wState
& CBF_EDIT
)
1439 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1441 /* get it from the listbox */
1443 if (!count
|| !buf
) return 0;
1444 if( lphc
->hWndLBox
)
1446 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1447 if (idx
== LB_ERR
) goto error
;
1448 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1449 if (length
== LB_ERR
) goto error
;
1451 /* 'length' is without the terminating character */
1452 if (length
>= count
)
1454 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1455 if (!lpBuffer
) goto error
;
1456 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1458 /* truncate if buffer is too short */
1459 if (length
!= LB_ERR
)
1461 lstrcpynA( buf
, lpBuffer
, count
);
1464 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1466 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1468 if (length
== LB_ERR
) return 0;
1472 error
: /* error - truncate string, return zero */
1478 /***********************************************************************
1481 * This function sets window positions according to the updated
1482 * component placement struct.
1484 static void CBResetPos(
1486 const RECT
*rectEdit
,
1490 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1492 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1493 * sizing messages */
1495 if( lphc
->wState
& CBF_EDIT
)
1496 SetWindowPos( lphc
->hWndEdit
, 0,
1497 rectEdit
->left
, rectEdit
->top
,
1498 rectEdit
->right
- rectEdit
->left
,
1499 rectEdit
->bottom
- rectEdit
->top
,
1500 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1502 SetWindowPos( lphc
->hWndLBox
, 0,
1503 rectLB
->left
, rectLB
->top
,
1504 rectLB
->right
- rectLB
->left
,
1505 rectLB
->bottom
- rectLB
->top
,
1506 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1510 if( lphc
->wState
& CBF_DROPPED
)
1512 lphc
->wState
&= ~CBF_DROPPED
;
1513 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1516 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1517 RedrawWindow( lphc
->self
, NULL
, 0,
1518 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1523 /***********************************************************************
1526 static void COMBO_Size( LPHEADCOMBO lphc
)
1529 * Those controls are always the same height. So we have to make sure
1530 * they are not resized to another value.
1532 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1534 int newComboHeight
, curComboHeight
, curComboWidth
;
1537 GetWindowRect(lphc
->self
, &rc
);
1538 curComboHeight
= rc
.bottom
- rc
.top
;
1539 curComboWidth
= rc
.right
- rc
.left
;
1540 newComboHeight
= CBGetTextAreaHeight(lphc
->self
, lphc
) + 2*COMBO_YBORDERSIZE();
1543 * Resizing a combobox has another side effect, it resizes the dropped
1544 * rectangle as well. However, it does it only if the new height for the
1545 * combobox is more than the height it should have. In other words,
1546 * if the application resizing the combobox only had the intention to resize
1547 * the actual control, for example, to do the layout of a dialog that is
1548 * resized, the height of the dropdown is not changed.
1550 if( curComboHeight
> newComboHeight
)
1552 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1553 curComboHeight
, newComboHeight
, lphc
->droppedRect
.bottom
,
1554 lphc
->droppedRect
.top
);
1555 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ curComboHeight
- newComboHeight
;
1558 * Restore original height
1560 if( curComboHeight
!= newComboHeight
)
1561 SetWindowPos(lphc
->self
, 0, 0, 0, curComboWidth
, newComboHeight
,
1562 SWP_NOZORDER
|SWP_NOMOVE
|SWP_NOACTIVATE
|SWP_NOREDRAW
);
1565 CBCalcPlacement(lphc
->self
,
1569 &lphc
->droppedRect
);
1571 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, FALSE
);
1575 /***********************************************************************
1578 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1583 lphc
->hFont
= hFont
;
1586 * Propagate to owned windows.
1588 if( lphc
->wState
& CBF_EDIT
)
1589 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1590 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1593 * Redo the layout of the control.
1595 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1597 CBCalcPlacement(lphc
->self
,
1601 &lphc
->droppedRect
);
1603 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1607 CBForceDummyResize(lphc
);
1612 /***********************************************************************
1613 * COMBO_SetItemHeight
1615 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1617 LRESULT lRet
= CB_ERR
;
1619 if( index
== -1 ) /* set text field height */
1621 if( height
< 32768 )
1623 lphc
->editHeight
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1626 * Redo the layout of the control.
1628 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1630 CBCalcPlacement(lphc
->self
,
1634 &lphc
->droppedRect
);
1636 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1640 CBForceDummyResize(lphc
);
1646 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1647 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
, (WPARAM
)index
, (LPARAM
)height
);
1651 /***********************************************************************
1652 * COMBO_SelectString
1654 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1656 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
) :
1657 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
);
1660 if( lphc
->wState
& CBF_EDIT
)
1661 CBUpdateEdit( lphc
, index
);
1664 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1667 return (LRESULT
)index
;
1670 /***********************************************************************
1673 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1677 HWND hWnd
= lphc
->self
;
1679 pt
.x
= (short)LOWORD(lParam
);
1680 pt
.y
= (short)HIWORD(lParam
);
1681 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1683 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1684 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1686 lphc
->wState
|= CBF_BUTTONDOWN
;
1687 if( lphc
->wState
& CBF_DROPPED
)
1689 /* got a click to cancel selection */
1691 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1692 CBRollUp( lphc
, TRUE
, FALSE
);
1693 if( !IsWindow( hWnd
) ) return;
1695 if( lphc
->wState
& CBF_CAPTURE
)
1697 lphc
->wState
&= ~CBF_CAPTURE
;
1703 /* drop down the listbox and start tracking */
1705 lphc
->wState
|= CBF_CAPTURE
;
1709 if( bButton
) CBRepaintButton( lphc
);
1713 /***********************************************************************
1716 * Release capture and stop tracking if needed.
1718 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1720 if( lphc
->wState
& CBF_CAPTURE
)
1722 lphc
->wState
&= ~CBF_CAPTURE
;
1723 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1725 INT index
= CBUpdateLBox( lphc
, TRUE
);
1726 /* Update edit only if item is in the list */
1729 lphc
->wState
|= CBF_NOLBSELECT
;
1730 CBUpdateEdit( lphc
, index
);
1731 lphc
->wState
&= ~CBF_NOLBSELECT
;
1735 SetCapture(lphc
->hWndLBox
);
1738 if( lphc
->wState
& CBF_BUTTONDOWN
)
1740 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1741 CBRepaintButton( lphc
);
1745 /***********************************************************************
1748 * Two things to do - track combo button and release capture when
1749 * pointer goes into the listbox.
1751 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1756 pt
.x
= (short)LOWORD(lParam
);
1757 pt
.y
= (short)HIWORD(lParam
);
1759 if( lphc
->wState
& CBF_BUTTONDOWN
)
1763 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1767 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1768 CBRepaintButton( lphc
);
1772 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1773 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1774 if( PtInRect(&lbRect
, pt
) )
1776 lphc
->wState
&= ~CBF_CAPTURE
;
1778 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1780 /* hand over pointer tracking */
1781 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1785 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1787 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1790 pcbi
->rcItem
= lphc
->textRect
;
1791 pcbi
->rcButton
= lphc
->buttonRect
;
1792 pcbi
->stateButton
= 0;
1793 if (lphc
->wState
& CBF_BUTTONDOWN
)
1794 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1795 if (IsRectEmpty(&lphc
->buttonRect
))
1796 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1797 pcbi
->hwndCombo
= lphc
->self
;
1798 pcbi
->hwndItem
= lphc
->hWndEdit
;
1799 pcbi
->hwndList
= lphc
->hWndLBox
;
1803 static char *strdupA(LPCSTR str
)
1808 if(!str
) return NULL
;
1811 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1815 memcpy(ret
, str
, len
+ 1);
1819 /***********************************************************************
1820 * ComboWndProc_common
1822 LRESULT WINAPI
ComboWndProc_common( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1824 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1828 pWnd
= ValidateHwnd(hwnd
);
1833 NtUserSetWindowFNID(hwnd
, FNID_COMBOBOX
);
1837 if (pWnd
->fnid
!= FNID_COMBOBOX
)
1839 ERR("Wrong window class for ComboBox! fnId 0x%x\n",pWnd
->fnid
);
1846 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1847 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1850 if (!IsWindow(hwnd
)) return 0;
1853 if( lphc
|| message
== WM_NCCREATE
)
1857 /* System messages */
1861 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1862 ((LPCREATESTRUCTA
)lParam
)->style
;
1863 return COMBO_NCCreate(hwnd
, style
);
1866 COMBO_NCDestroy(lphc
);
1868 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
1870 break;/* -> DefWindowProc */
1878 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1879 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1883 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1884 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1886 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1889 case WM_PRINTCLIENT
:
1892 /* wParam may contain a valid HDC! */
1893 return COMBO_Paint(lphc
, (HDC
)wParam
);
1896 /* do all painting in WM_PAINT like Windows does */
1901 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1902 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1904 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1906 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1907 result
|= DLGC_WANTMESSAGE
;
1912 if( lphc
->hWndLBox
&&
1913 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1916 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1919 return (LRESULT
)lphc
->hFont
;
1921 if( lphc
->wState
& CBF_EDIT
) {
1922 SetFocus( lphc
->hWndEdit
);
1923 /* The first time focus is received, select all the text */
1924 if( !(lphc
->wState
& CBF_BEENFOCUSED
) ) {
1925 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
1926 lphc
->wState
|= CBF_BEENFOCUSED
;
1930 COMBO_SetFocus( lphc
);
1934 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1936 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1937 COMBO_KillFocus( lphc
);
1941 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1943 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1944 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1946 case WM_GETTEXTLENGTH
:
1948 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1950 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1951 if (j
== -1) return 0;
1952 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1953 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1955 else if( lphc
->wState
& CBF_EDIT
)
1958 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1959 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1960 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1961 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1968 if( lphc
->wState
& CBF_EDIT
)
1970 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1971 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1977 case WM_COMPAREITEM
:
1978 case WM_MEASUREITEM
:
1979 return COMBO_ItemOp(lphc
, message
, lParam
);
1981 if( lphc
->wState
& CBF_EDIT
)
1982 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1983 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1985 /* Force the control to repaint when the enabled state changes. */
1986 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1990 lphc
->wState
&= ~CBF_NOREDRAW
;
1992 lphc
->wState
|= CBF_NOREDRAW
;
1994 if( lphc
->wState
& CBF_EDIT
)
1995 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1996 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
2000 if( KF_ALTDOWN
& HIWORD(lParam
) )
2002 if( KEYDATA_ALT
& HIWORD(lParam
) )
2004 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
2005 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2009 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
2010 (lphc
->wState
& CBF_DROPPED
))
2012 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
2015 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
2017 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2026 if( lphc
->wState
& CBF_EDIT
)
2027 hwndTarget
= lphc
->hWndEdit
;
2029 hwndTarget
= lphc
->hWndLBox
;
2031 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
2032 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
2034 case WM_LBUTTONDOWN
:
2035 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
2036 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
2039 COMBO_LButtonUp( lphc
);
2042 if( lphc
->wState
& CBF_CAPTURE
)
2043 COMBO_MouseMove( lphc
, wParam
, lParam
);
2047 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2048 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2049 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2051 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
2052 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2055 /* Combo messages */
2060 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2061 CharLowerW((LPWSTR
)lParam
);
2062 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2063 CharUpperW((LPWSTR
)lParam
);
2064 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
2066 else /* unlike the unicode version, the ansi version does not overwrite
2067 the string if converting case */
2069 char *string
= NULL
;
2071 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2073 string
= strdupA((LPSTR
)lParam
);
2077 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2079 string
= strdupA((LPSTR
)lParam
);
2083 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
2084 HeapFree(GetProcessHeap(), 0, string
);
2087 case CB_INSERTSTRING
:
2090 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2091 CharLowerW((LPWSTR
)lParam
);
2092 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2093 CharUpperW((LPWSTR
)lParam
);
2094 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2098 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2099 CharLowerA((LPSTR
)lParam
);
2100 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2101 CharUpperA((LPSTR
)lParam
);
2103 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2105 case CB_DELETESTRING
:
2106 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2107 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2108 case CB_SELECTSTRING
:
2109 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2111 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2112 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2113 case CB_FINDSTRINGEXACT
:
2114 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2115 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2116 case CB_SETITEMHEIGHT
:
2117 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2118 case CB_GETITEMHEIGHT
:
2119 if( (INT
)wParam
>= 0 ) /* listbox item */
2120 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2121 return CBGetTextAreaHeight(hwnd
, lphc
);
2122 case CB_RESETCONTENT
:
2123 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2124 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2126 static const WCHAR empty_stringW
[] = { 0 };
2127 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2130 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2132 case CB_INITSTORAGE
:
2133 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2134 case CB_GETHORIZONTALEXTENT
:
2135 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2136 case CB_SETHORIZONTALEXTENT
:
2137 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2138 case CB_GETTOPINDEX
:
2139 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2141 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2143 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2144 case CB_SETDROPPEDWIDTH
:
2145 if( (CB_GETTYPE(lphc
) == CBS_SIMPLE
) ||
2146 (INT
)wParam
>= 32768 )
2148 /* new value must be higher than combobox width */
2149 if((INT
)wParam
>= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
)
2150 lphc
->droppedWidth
= wParam
;
2152 lphc
->droppedWidth
= 0;
2154 /* recalculate the combobox area */
2155 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
2158 case CB_GETDROPPEDWIDTH
:
2159 if( lphc
->droppedWidth
)
2160 return lphc
->droppedWidth
;
2161 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2162 case CB_GETDROPPEDCONTROLRECT
:
2163 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2165 case CB_GETDROPPEDSTATE
:
2166 return (lphc
->wState
& CBF_DROPPED
) != 0;
2168 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2169 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2171 case CB_SHOWDROPDOWN
:
2172 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2176 if( !(lphc
->wState
& CBF_DROPPED
) )
2180 if( lphc
->wState
& CBF_DROPPED
)
2181 CBRollUp( lphc
, FALSE
, TRUE
);
2185 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2187 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2189 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2191 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2193 /* no LBN_SELCHANGE in this case, update manually */
2194 CBPaintText( lphc
, NULL
);
2195 lphc
->wState
&= ~CBF_SELCHANGE
;
2198 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2199 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2200 case CB_GETLBTEXTLEN
:
2201 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2202 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2203 case CB_GETITEMDATA
:
2204 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2205 case CB_SETITEMDATA
:
2206 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2208 /* Edit checks passed parameters itself */
2209 if( lphc
->wState
& CBF_EDIT
)
2210 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2213 if( lphc
->wState
& CBF_EDIT
)
2214 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2215 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2217 case CB_SETEXTENDEDUI
:
2218 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2221 lphc
->wState
|= CBF_EUI
;
2222 else lphc
->wState
&= ~CBF_EUI
;
2224 case CB_GETEXTENDEDUI
:
2225 return (lphc
->wState
& CBF_EUI
) != 0;
2226 case CB_GETCOMBOBOXINFO
:
2227 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2229 if( lphc
->wState
& CBF_EDIT
)
2230 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2234 case WM_UPDATEUISTATE
:
2236 DefWindowProcW(lphc
->self
, message
, wParam
, lParam
);
2238 DefWindowProcA(lphc
->self
, message
, wParam
, lParam
);
2240 if (COMBO_update_uistate(lphc
))
2243 if( !(lphc
->wState
& CBF_EDIT
) )
2244 NtUserInvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2248 case WM_CBLOSTTEXTFOCUS
: /* undocumented message - deselects the text when focus is lost */
2249 if (lphc
->hWndEdit
!= NULL
)
2251 SendMessage(lphc
->self
, WM_LBUTTONUP
, 0, 0xFFFFFFFF);
2252 SendMessage(lphc
->hWndEdit
, EM_SETSEL
, 0, 0);
2253 lphc
->wState
&= ~(CBF_FOCUSED
| CBF_BEENFOCUSED
);
2254 CB_NOTIFY(lphc
, CBN_KILLFOCUS
);
2261 if (message
>= WM_USER
)
2262 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2263 message
- WM_USER
, wParam
, lParam
);
2266 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2267 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2272 /***********************************************************************
2275 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2278 LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2280 if (!IsWindow(hwnd
)) return 0;
2281 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, FALSE
);
2284 /***********************************************************************
2287 LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2289 if (!IsWindow(hwnd
)) return 0;
2290 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, TRUE
);
2293 #endif /* __REACTOS__ */
2295 /*************************************************************************
2296 * GetComboBoxInfo (USER32.@)
2298 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2299 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2301 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2303 return NtUserGetComboBoxInfo(hwndCombo
, pcbi
);
2305 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);