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
);
40 /* bits in the dwKeyData */
41 #define KEYDATA_ALT 0x2000
42 #define KEYDATA_PREVSTATE 0x4000
45 * Additional combo box definitions
48 #define CB_NOTIFY( lphc, code ) \
49 (SendMessageW((lphc)->owner, WM_COMMAND, \
50 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
52 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
53 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
54 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
55 #define CB_HWND( lphc ) ((lphc)->self)
56 // ReactOS already define in include/controls.h We have it here as a sync note.
57 //#define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
59 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
64 static HBITMAP hComboBmp
= 0;
65 static UINT CBitHeight
, CBitWidth
;
68 * Look and feel dependent "constants"
71 #define COMBO_YBORDERGAP 5
72 #define COMBO_XBORDERSIZE() 2
73 #define COMBO_YBORDERSIZE() 2
74 #define COMBO_EDITBUTTONSPACE() 0
75 #define EDIT_CONTROL_PADDING() 1
77 //static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
78 //static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
80 /*********************************************************************
81 * combo class descriptor
83 static const WCHAR comboboxW
[] = {'C','o','m','b','o','B','o','x',0};
84 const struct builtin_class_descr COMBO_builtin_class
=
87 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
88 ComboWndProcA
, /* procA */
89 ComboWndProcW
, /* procW */
90 sizeof(HEADCOMBO
*), /* extra */
91 IDC_ARROW
, /* cursor */
96 /***********************************************************************
99 * Load combo button bitmap.
101 static BOOL
COMBO_Init(void)
105 if( hComboBmp
) return TRUE
;
106 if( (hDC
= CreateCompatibleDC(0)) )
109 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
115 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
116 CBitHeight
= bm
.bmHeight
;
117 CBitWidth
= bm
.bmWidth
;
119 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
121 hPrevB
= SelectObject( hDC
, hComboBmp
);
122 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
123 InvertRect( hDC
, &r
);
124 SelectObject( hDC
, hPrevB
);
134 /* Retrieve the UI state for the control */
135 static BOOL
COMBO_update_uistate(LPHEADCOMBO lphc
)
139 prev_flags
= lphc
->UIState
;
140 lphc
->UIState
= DefWindowProcW(lphc
->self
, WM_QUERYUISTATE
, 0, 0);
141 return prev_flags
!= lphc
->UIState
;
144 /***********************************************************************
147 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
151 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
154 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)lphc
);
156 COMBO_update_uistate(lphc
);
158 /* some braindead apps do try to use scrollbar/border flags */
160 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
161 SetWindowLongPtrW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
164 * We also have to remove the client edge style to make sure
165 * we don't end-up with a non client area.
167 SetWindowLongPtrW( hwnd
, GWL_EXSTYLE
,
168 GetWindowLongPtrW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
170 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
171 lphc
->dwStyle
|= CBS_HASSTRINGS
;
172 if( !(GetWindowLongPtrW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
173 lphc
->wState
|= CBF_NOTIFY
;
175 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
181 /***********************************************************************
184 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
189 TRACE("[%p]: freeing storage\n", lphc
->self
);
191 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
192 DestroyWindow( lphc
->hWndLBox
);
194 SetWindowLongPtrW( lphc
->self
, 0, 0 );
195 HeapFree( GetProcessHeap(), 0, lphc
);
200 /***********************************************************************
201 * CBGetTextAreaHeight
203 * This method will calculate the height of the text area of the
205 * The height of the text area is set in two ways.
206 * It can be set explicitly through a combobox message or through a
207 * WM_MEASUREITEM callback.
208 * If this is not the case, the height is set to font height + 4px
209 * This height was determined through experimentation.
210 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
212 static INT
CBGetTextAreaHeight(
218 if( lphc
->editHeight
) /* explicitly set height */
220 iTextItemHeight
= lphc
->editHeight
;
225 HDC hDC
= GetDC(hwnd
);
230 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
232 GetTextMetricsW(hDC
, &tm
);
234 baseUnitY
= tm
.tmHeight
;
237 SelectObject( hDC
, hPrevFont
);
239 ReleaseDC(hwnd
, hDC
);
241 iTextItemHeight
= baseUnitY
+ 4;
245 * Check the ownerdraw case if we haven't asked the parent the size
248 if ( CB_OWNERDRAWN(lphc
) &&
249 (lphc
->wState
& CBF_MEASUREITEM
) )
251 MEASUREITEMSTRUCT measureItem
;
253 INT originalItemHeight
= iTextItemHeight
;
254 UINT id
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
257 * We use the client rect for the width of the item.
259 GetClientRect(hwnd
, &clientRect
);
261 lphc
->wState
&= ~CBF_MEASUREITEM
;
264 * Send a first one to measure the size of the text area
266 measureItem
.CtlType
= ODT_COMBOBOX
;
267 measureItem
.CtlID
= id
;
268 measureItem
.itemID
= -1;
269 measureItem
.itemWidth
= clientRect
.right
;
270 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
271 measureItem
.itemData
= 0;
272 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
273 iTextItemHeight
= 6 + measureItem
.itemHeight
;
276 * Send a second one in the case of a fixed ownerdraw list to calculate the
277 * size of the list items. (we basically do this on behalf of the listbox)
279 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
281 measureItem
.CtlType
= ODT_COMBOBOX
;
282 measureItem
.CtlID
= id
;
283 measureItem
.itemID
= 0;
284 measureItem
.itemWidth
= clientRect
.right
;
285 measureItem
.itemHeight
= originalItemHeight
;
286 measureItem
.itemData
= 0;
287 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
288 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
292 * Keep the size for the next time
294 lphc
->editHeight
= iTextItemHeight
;
297 return iTextItemHeight
;
300 /***********************************************************************
303 * The dummy resize is used for listboxes that have a popup to trigger
304 * a re-arranging of the contents of the combobox and the recalculation
305 * of the size of the "real" control window.
307 static void CBForceDummyResize(
313 newComboHeight
= CBGetTextAreaHeight(lphc
->self
,lphc
) + 2*COMBO_YBORDERSIZE();
315 GetWindowRect(lphc
->self
, &windowRect
);
318 * We have to be careful, resizing a combobox also has the meaning that the
319 * dropped rect will be resized. In this case, we want to trigger a resize
320 * to recalculate layout but we don't want to change the dropped rectangle
321 * So, we pass the height of text area of control as the height.
322 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
325 SetWindowPos( lphc
->self
,
328 windowRect
.right
- windowRect
.left
,
330 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
333 /***********************************************************************
336 * Set up component coordinates given valid lphc->RectCombo.
338 static void CBCalcPlacement(
346 * Again, start with the client rectangle.
348 GetClientRect(hwnd
, lprEdit
);
353 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
356 * Chop off the bottom part to fit with the height of the text area.
358 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
361 * The button starts the same vertical position as the text area.
363 CopyRect(lprButton
, lprEdit
);
366 * If the combobox is "simple" there is no button.
368 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
369 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
373 * Let's assume the combobox button is the same width as the
375 * size the button horizontally and cut-off the text area.
377 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
378 lprEdit
->right
= lprButton
->left
;
382 * In the case of a dropdown, there is an additional spacing between the
383 * text area and the button.
385 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
387 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
391 * If we have an edit control, we space it away from the borders slightly.
393 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
395 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
399 * Adjust the size of the listbox popup.
401 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
404 * Use the client rectangle to initialize the listbox rectangle
406 GetClientRect(hwnd
, lprLB
);
409 * Then, chop-off the top part.
411 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
416 * Make sure the dropped width is as large as the combobox itself.
418 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
420 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
423 * In the case of a dropdown, the popup listbox is offset to the right.
424 * so, we want to make sure it's flush with the right side of the
427 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
428 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
431 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
434 /* don't allow negative window width */
435 if (lprEdit
->right
< lprEdit
->left
)
436 lprEdit
->right
= lprEdit
->left
;
438 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit
));
440 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton
));
442 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB
));
445 /***********************************************************************
446 * CBGetDroppedControlRect
448 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
450 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
451 of the combo box and the lower right corner of the listbox */
453 GetWindowRect(lphc
->self
, lpRect
);
455 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
456 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
460 /***********************************************************************
461 * COMBO_WindowPosChanging
463 static LRESULT
COMBO_WindowPosChanging(
466 WINDOWPOS
* posChanging
)
469 * We need to override the WM_WINDOWPOSCHANGING method to handle all
470 * the non-simple comboboxes. The problem is that those controls are
471 * always the same height. We have to make sure they are not resized
474 if ( ( CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
475 ((posChanging
->flags
& SWP_NOSIZE
) == 0) )
479 newComboHeight
= CBGetTextAreaHeight(hwnd
,lphc
) +
480 2*COMBO_YBORDERSIZE();
483 * Resizing a combobox has another side effect, it resizes the dropped
484 * rectangle as well. However, it does it only if the new height for the
485 * combobox is more than the height it should have. In other words,
486 * if the application resizing the combobox only had the intention to resize
487 * the actual control, for example, to do the layout of a dialog that is
488 * resized, the height of the dropdown is not changed.
490 if (posChanging
->cy
> newComboHeight
)
492 TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
493 posChanging
->cy
, newComboHeight
, lphc
->droppedRect
.bottom
,
494 lphc
->droppedRect
.top
);
495 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ posChanging
->cy
- newComboHeight
;
498 posChanging
->cy
= newComboHeight
;
504 /***********************************************************************
507 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
510 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
511 static const WCHAR editName
[] = {'E','d','i','t',0};
513 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
514 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
516 lphc
->owner
= hwndParent
;
519 * The item height and dropped width are not set when the control
522 lphc
->droppedWidth
= lphc
->editHeight
= 0;
525 * The first time we go through, we want to measure the ownerdraw item
527 lphc
->wState
|= CBF_MEASUREITEM
;
529 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
531 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
537 * Initialize the dropped rect to the size of the client area of the
538 * control and then, force all the areas of the combobox to be
541 GetClientRect( hwnd
, &lphc
->droppedRect
);
542 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
545 * Adjust the position of the popup listbox if it's necessary
547 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
549 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
552 * If it's a dropdown, the listbox is offset
554 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
555 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
557 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
558 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
559 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
560 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
561 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
564 /* create listbox popup */
566 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
567 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
569 if( lphc
->dwStyle
& CBS_SORT
)
570 lbeStyle
|= LBS_SORT
;
571 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
572 lbeStyle
|= LBS_HASSTRINGS
;
573 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
574 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
575 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
576 lbeStyle
|= LBS_DISABLENOSCROLL
;
578 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
580 lbeStyle
|= WS_VISIBLE
;
583 * In win 95 look n feel, the listbox in the simple combobox has
584 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
586 lbeStyle
&= ~WS_BORDER
;
587 lbeExStyle
|= WS_EX_CLIENTEDGE
;
591 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
595 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
596 lphc
->droppedRect
.left
,
597 lphc
->droppedRect
.top
,
598 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
599 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
600 hwnd
, (HMENU
)ID_CB_LISTBOX
,
601 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
603 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
604 lphc
->droppedRect
.left
,
605 lphc
->droppedRect
.top
,
606 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
607 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
608 hwnd
, (HMENU
)ID_CB_LISTBOX
,
609 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
614 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
616 if( lphc
->wState
& CBF_EDIT
)
618 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
619 lbeStyle
|= ES_OEMCONVERT
;
620 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
621 lbeStyle
|= ES_AUTOHSCROLL
;
622 if( lphc
->dwStyle
& CBS_LOWERCASE
)
623 lbeStyle
|= ES_LOWERCASE
;
624 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
625 lbeStyle
|= ES_UPPERCASE
;
627 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
630 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
631 lphc
->textRect
.left
, lphc
->textRect
.top
,
632 lphc
->textRect
.right
- lphc
->textRect
.left
,
633 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
634 hwnd
, (HMENU
)ID_CB_EDIT
,
635 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
637 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
638 lphc
->textRect
.left
, lphc
->textRect
.top
,
639 lphc
->textRect
.right
- lphc
->textRect
.left
,
640 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
641 hwnd
, (HMENU
)ID_CB_EDIT
,
642 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
644 if( !lphc
->hWndEdit
)
650 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
652 /* Now do the trick with parent */
653 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
655 * If the combo is a dropdown, we must resize the control
656 * to fit only the text area and button. To do this,
657 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
658 * will take care of setting the height for us.
660 CBForceDummyResize(lphc
);
663 TRACE("init done\n");
666 ERR("edit control failure.\n");
667 } else ERR("listbox failure.\n");
668 } else ERR("no owner for visible combo.\n");
670 /* CreateWindow() will send WM_NCDESTROY to cleanup */
675 /***********************************************************************
678 * Paint combo button (normal, pressed, and disabled states).
680 static void CBPaintButton( LPHEADCOMBO lphc
, HDC hdc
, RECT rectButton
)
682 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
684 if( lphc
->wState
& CBF_NOREDRAW
)
688 if (lphc
->wState
& CBF_BUTTONDOWN
)
689 buttonState
|= DFCS_PUSHED
;
691 if (CB_DISABLED(lphc
))
692 buttonState
|= DFCS_INACTIVE
;
694 DrawFrameControl(hdc
, &rectButton
, DFC_SCROLL
, buttonState
);
697 /***********************************************************************
700 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
702 static void CBPaintText(
710 if( lphc
->wState
& CBF_NOREDRAW
) return;
714 /* follow Windows combobox that sends a bunch of text
715 * inquiries to its listbox while processing WM_PAINT. */
717 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
719 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
721 FIXME("LB_ERR probably not handled yet\n");
722 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
724 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
725 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
726 pText
[size
] = '\0'; /* just in case */
730 if( !CB_OWNERDRAWN(lphc
) )
733 if( lphc
->wState
& CBF_EDIT
)
735 static const WCHAR empty_stringW
[] = { 0 };
736 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
737 if( lphc
->wState
& CBF_FOCUSED
)
738 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
740 else /* paint text field ourselves */
742 UINT itemState
= ODS_COMBOBOXEDIT
;
743 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
746 * Give ourselves some space.
748 InflateRect( &rectEdit
, -1, -1 );
750 if( CB_OWNERDRAWN(lphc
) )
754 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
756 /* setup state for DRAWITEM message. Owner will highlight */
757 if ( (lphc
->wState
& CBF_FOCUSED
) &&
758 !(lphc
->wState
& CBF_DROPPED
) )
759 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
762 * Save the current clip region.
763 * To retrieve the clip region, we need to create one "dummy"
766 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
768 if (GetClipRgn(hdc
, clipRegion
)!=1)
770 DeleteObject(clipRegion
);
774 if (!IsWindowEnabled(lphc
->self
)) itemState
|= ODS_DISABLED
;
776 dis
.CtlType
= ODT_COMBOBOX
;
778 dis
.hwndItem
= lphc
->self
;
779 dis
.itemAction
= ODA_DRAWENTIRE
;
781 dis
.itemState
= itemState
;
783 dis
.rcItem
= rectEdit
;
784 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
,
788 * Clip the DC and have the parent draw the item.
790 IntersectClipRect(hdc
,
791 rectEdit
.left
, rectEdit
.top
,
792 rectEdit
.right
, rectEdit
.bottom
);
794 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
797 * Reset the clipping region.
799 SelectClipRgn(hdc
, clipRegion
);
803 static const WCHAR empty_stringW
[] = { 0 };
805 if ( (lphc
->wState
& CBF_FOCUSED
) &&
806 !(lphc
->wState
& CBF_DROPPED
) ) {
809 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
810 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
811 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
817 ETO_OPAQUE
| ETO_CLIPPED
,
819 pText
? pText
: empty_stringW
, size
, NULL
);
821 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
) &&
822 !(lphc
->UIState
& UISF_HIDEFOCUS
))
823 DrawFocusRect( hdc
, &rectEdit
);
827 SelectObject(hdc
, hPrevFont
);
830 HeapFree( GetProcessHeap(), 0, pText
);
833 /***********************************************************************
836 static void CBPaintBorder(
838 const HEADCOMBO
*lphc
,
843 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
845 GetClientRect(hwnd
, &clientRect
);
849 CopyRect(&clientRect
, &lphc
->textRect
);
851 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
852 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
855 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
858 /***********************************************************************
859 * COMBO_PrepareColors
861 * This method will sent the appropriate WM_CTLCOLOR message to
862 * prepare and setup the colors for the combo's DC.
864 * It also returns the brush to use for the background.
866 static HBRUSH
COMBO_PrepareColors(
873 * Get the background brush for this control.
875 if (CB_DISABLED(lphc
))
877 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
878 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
881 * We have to change the text color since WM_CTLCOLORSTATIC will
882 * set it to the "enabled" color. This is the same behavior as the
885 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
889 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
890 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
891 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
898 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
904 /***********************************************************************
907 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
912 hDC
= (hParamDC
) ? hParamDC
913 : BeginPaint( lphc
->self
, &ps
);
915 TRACE("hdc=%p\n", hDC
);
917 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
919 HBRUSH hPrevBrush
, hBkgBrush
;
922 * Retrieve the background brush and select it in the
925 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
927 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
928 if (!(lphc
->wState
& CBF_EDIT
))
929 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
932 * In non 3.1 look, there is a sunken border on the combobox
934 CBPaintBorder(lphc
->self
, lphc
, hDC
);
936 if( !IsRectEmpty(&lphc
->buttonRect
) )
938 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
941 /* paint the edit control padding area */
942 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
944 RECT rPadEdit
= lphc
->textRect
;
946 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
948 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
951 if( !(lphc
->wState
& CBF_EDIT
) )
952 CBPaintText( lphc
, hDC
, lphc
->textRect
);
955 SelectObject( hDC
, hPrevBrush
);
959 EndPaint(lphc
->self
, &ps
);
964 /***********************************************************************
967 * Select listbox entry according to the contents of the edit control.
969 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
975 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
978 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
980 TRACE("\t edit text length %i\n", length
);
984 GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
985 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
,
986 (WPARAM
)(-1), (LPARAM
)pText
);
987 HeapFree( GetProcessHeap(), 0, pText
);
990 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)(bSelect
? idx
: -1), 0);
992 /* probably superfluous but Windows sends this too */
993 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
994 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
999 /***********************************************************************
1002 * Copy a listbox entry to the edit control.
1004 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1007 LPWSTR pText
= NULL
;
1008 static const WCHAR empty_stringW
[] = { 0 };
1010 TRACE("\t %i\n", index
);
1012 if( index
>= 0 ) /* got an entry */
1014 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1015 if( length
!= LB_ERR
)
1017 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
1019 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
,
1020 (WPARAM
)index
, (LPARAM
)pText
);
1025 if( CB_HASSTRINGS(lphc
) )
1027 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1028 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
1029 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1032 if( lphc
->wState
& CBF_FOCUSED
)
1033 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1035 HeapFree( GetProcessHeap(), 0, pText
);
1038 /***********************************************************************
1041 * Show listbox popup.
1043 static void CBDropDown( LPHEADCOMBO lphc
)
1046 MONITORINFO mon_info
;
1051 TRACE("[%p]: drop down\n", lphc
->self
);
1053 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1057 lphc
->wState
|= CBF_DROPPED
;
1058 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1060 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
1062 /* Update edit only if item is in the list */
1063 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
1064 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1068 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1070 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
1071 (WPARAM
)(lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
), 0 );
1072 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1075 /* now set popup position */
1076 GetWindowRect( lphc
->self
, &rect
);
1079 * If it's a dropdown, the listbox is offset
1081 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1082 rect
.left
+= COMBO_EDITBUTTONSPACE();
1084 /* if the dropped height is greater than the total height of the dropped
1085 items list, then force the drop down list height to be the total height
1086 of the items in the dropped list */
1088 /* And Remove any extra space (Best Fit) */
1089 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1090 /* if listbox length has been set directly by its handle */
1091 GetWindowRect(lphc
->hWndLBox
, &r
);
1092 if (nDroppedHeight
< r
.bottom
- r
.top
)
1093 nDroppedHeight
= r
.bottom
- r
.top
;
1094 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1101 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1103 nHeight
= nIHeight
*nItems
;
1105 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1106 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1108 if (nDroppedHeight
< nHeight
)
1111 nDroppedHeight
= (nItems
+1)*nIHeight
;
1112 else if (nDroppedHeight
< 6*nIHeight
)
1113 nDroppedHeight
= 6*nIHeight
;
1117 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1118 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
1119 mon_info
.cbSize
= sizeof(mon_info
);
1120 GetMonitorInfoW( monitor
, &mon_info
);
1122 if( (rect
.bottom
+ nDroppedHeight
) >= mon_info
.rcWork
.bottom
)
1123 rect
.bottom
= rect
.top
- nDroppedHeight
;
1125 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1126 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1128 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1131 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1132 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1133 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1135 EnableWindow( lphc
->hWndLBox
, TRUE
);
1136 if (GetCapture() != lphc
->self
)
1137 SetCapture(lphc
->hWndLBox
);
1140 /***********************************************************************
1143 * Hide listbox popup.
1145 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1147 HWND hWnd
= lphc
->self
;
1149 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1150 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1152 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1154 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1157 if( lphc
->wState
& CBF_DROPPED
)
1161 lphc
->wState
&= ~CBF_DROPPED
;
1162 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1164 if(GetCapture() == lphc
->hWndLBox
)
1169 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1171 rect
= lphc
->buttonRect
;
1182 rect
= lphc
->textRect
;
1187 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1188 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1189 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1190 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1195 /***********************************************************************
1198 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1200 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1202 if( lphc
->wState
& CBF_DROPPED
)
1204 CBRollUp( lphc
, ok
, bRedrawButton
);
1212 /***********************************************************************
1215 static void CBRepaintButton( LPHEADCOMBO lphc
)
1217 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1218 UpdateWindow(lphc
->self
);
1221 /***********************************************************************
1224 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1226 if( !(lphc
->wState
& CBF_FOCUSED
) )
1228 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1229 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1231 /* This is wrong. Message sequences seem to indicate that this
1232 is set *after* the notify. */
1233 /* lphc->wState |= CBF_FOCUSED; */
1235 if( !(lphc
->wState
& CBF_EDIT
) )
1236 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1238 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1239 lphc
->wState
|= CBF_FOCUSED
;
1243 /***********************************************************************
1246 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1248 HWND hWnd
= lphc
->self
;
1250 if( lphc
->wState
& CBF_FOCUSED
)
1252 CBRollUp( lphc
, FALSE
, TRUE
);
1253 if( IsWindow( hWnd
) )
1255 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1256 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1258 lphc
->wState
&= ~CBF_FOCUSED
;
1261 if( !(lphc
->wState
& CBF_EDIT
) )
1262 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1264 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1269 /***********************************************************************
1272 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1274 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1276 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1278 switch( HIWORD(wParam
) >> 8 )
1280 case (EN_SETFOCUS
>> 8):
1282 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1284 COMBO_SetFocus( lphc
);
1287 case (EN_KILLFOCUS
>> 8):
1289 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1291 /* NOTE: it seems that Windows' edit control sends an
1292 * undocumented message WM_USER + 0x1B instead of this
1293 * notification (only when it happens to be a part of
1294 * the combo). ?? - AK.
1297 COMBO_KillFocus( lphc
);
1301 case (EN_CHANGE
>> 8):
1303 * In some circumstances (when the selection of the combobox
1304 * is changed for example) we don't want the EN_CHANGE notification
1305 * to be forwarded to the parent of the combobox. This code
1306 * checks a flag that is set in these occasions and ignores the
1309 if (lphc
->wState
& CBF_NOLBSELECT
)
1311 lphc
->wState
&= ~CBF_NOLBSELECT
;
1315 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1318 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1319 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1322 case (EN_UPDATE
>> 8):
1323 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1324 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1327 case (EN_ERRSPACE
>> 8):
1328 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1331 else if( lphc
->hWndLBox
== hWnd
)
1333 switch( (short)HIWORD(wParam
) )
1336 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1340 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1346 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1348 /* do not roll up if selection is being tracked
1349 * by arrow keys in the dropdown listbox */
1350 if (!(lphc
->wState
& CBF_NOROLLUP
))
1352 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1354 else lphc
->wState
&= ~CBF_NOROLLUP
;
1356 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1358 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1360 if( lphc
->wState
& CBF_EDIT
)
1362 INT index
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1363 lphc
->wState
|= CBF_NOLBSELECT
;
1364 CBUpdateEdit( lphc
, index
);
1365 /* select text in edit, as Windows does */
1366 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1370 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1371 UpdateWindow(lphc
->self
);
1378 /* nothing to do here since ComboLBox always resets the focus to its
1379 * combo/edit counterpart */
1386 /***********************************************************************
1389 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1391 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1393 HWND hWnd
= lphc
->self
;
1394 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1396 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1402 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1403 lpIS
->CtlType
= ODT_COMBOBOX
;
1405 lpIS
->hwndItem
= hWnd
;
1410 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1411 lpIS
->CtlType
= ODT_COMBOBOX
;
1413 lpIS
->hwndItem
= hWnd
;
1416 case WM_COMPAREITEM
:
1418 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1419 lpIS
->CtlType
= ODT_COMBOBOX
;
1421 lpIS
->hwndItem
= hWnd
;
1424 case WM_MEASUREITEM
:
1426 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1427 lpIS
->CtlType
= ODT_COMBOBOX
;
1432 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1436 /***********************************************************************
1439 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1443 if( lphc
->wState
& CBF_EDIT
)
1444 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1446 /* get it from the listbox */
1448 if (!count
|| !buf
) return 0;
1449 if( lphc
->hWndLBox
)
1451 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1452 if (idx
== LB_ERR
) goto error
;
1453 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1454 if (length
== LB_ERR
) goto error
;
1456 /* 'length' is without the terminating character */
1457 if (length
>= count
)
1459 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1460 if (!lpBuffer
) goto error
;
1461 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1463 /* truncate if buffer is too short */
1464 if (length
!= LB_ERR
)
1466 lstrcpynW( buf
, lpBuffer
, count
);
1469 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1471 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1473 if (length
== LB_ERR
) return 0;
1477 error
: /* error - truncate string, return zero */
1483 /***********************************************************************
1486 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1487 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1489 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1493 if( lphc
->wState
& CBF_EDIT
)
1494 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1496 /* get it from the listbox */
1498 if (!count
|| !buf
) return 0;
1499 if( lphc
->hWndLBox
)
1501 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1502 if (idx
== LB_ERR
) goto error
;
1503 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1504 if (length
== LB_ERR
) goto error
;
1506 /* 'length' is without the terminating character */
1507 if (length
>= count
)
1509 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1510 if (!lpBuffer
) goto error
;
1511 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1513 /* truncate if buffer is too short */
1514 if (length
!= LB_ERR
)
1516 lstrcpynA( buf
, lpBuffer
, count
);
1519 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1521 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1523 if (length
== LB_ERR
) return 0;
1527 error
: /* error - truncate string, return zero */
1533 /***********************************************************************
1536 * This function sets window positions according to the updated
1537 * component placement struct.
1539 static void CBResetPos(
1541 const RECT
*rectEdit
,
1545 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1547 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1548 * sizing messages */
1550 if( lphc
->wState
& CBF_EDIT
)
1551 SetWindowPos( lphc
->hWndEdit
, 0,
1552 rectEdit
->left
, rectEdit
->top
,
1553 rectEdit
->right
- rectEdit
->left
,
1554 rectEdit
->bottom
- rectEdit
->top
,
1555 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1557 SetWindowPos( lphc
->hWndLBox
, 0,
1558 rectLB
->left
, rectLB
->top
,
1559 rectLB
->right
- rectLB
->left
,
1560 rectLB
->bottom
- rectLB
->top
,
1561 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1565 if( lphc
->wState
& CBF_DROPPED
)
1567 lphc
->wState
&= ~CBF_DROPPED
;
1568 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1571 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1572 RedrawWindow( lphc
->self
, NULL
, 0,
1573 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1578 /***********************************************************************
1581 static void COMBO_Size( LPHEADCOMBO lphc
, BOOL bRedraw
)
1583 CBCalcPlacement(lphc
->self
,
1587 &lphc
->droppedRect
);
1589 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, bRedraw
);
1593 /***********************************************************************
1596 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1601 lphc
->hFont
= hFont
;
1604 * Propagate to owned windows.
1606 if( lphc
->wState
& CBF_EDIT
)
1607 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1608 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1611 * Redo the layout of the control.
1613 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1615 CBCalcPlacement(lphc
->self
,
1619 &lphc
->droppedRect
);
1621 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1625 CBForceDummyResize(lphc
);
1630 /***********************************************************************
1631 * COMBO_SetItemHeight
1633 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1635 LRESULT lRet
= CB_ERR
;
1637 if( index
== -1 ) /* set text field height */
1639 if( height
< 32768 )
1641 lphc
->editHeight
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1644 * Redo the layout of the control.
1646 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1648 CBCalcPlacement(lphc
->self
,
1652 &lphc
->droppedRect
);
1654 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1658 CBForceDummyResize(lphc
);
1664 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1665 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1666 (WPARAM
)index
, (LPARAM
)height
);
1670 /***********************************************************************
1671 * COMBO_SelectString
1673 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1675 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
) :
1676 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
);
1679 if( lphc
->wState
& CBF_EDIT
)
1680 CBUpdateEdit( lphc
, index
);
1683 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1686 return (LRESULT
)index
;
1689 /***********************************************************************
1692 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1696 HWND hWnd
= lphc
->self
;
1698 pt
.x
= (short)LOWORD(lParam
);
1699 pt
.y
= (short)HIWORD(lParam
);
1700 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1702 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1703 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1705 lphc
->wState
|= CBF_BUTTONDOWN
;
1706 if( lphc
->wState
& CBF_DROPPED
)
1708 /* got a click to cancel selection */
1710 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1711 CBRollUp( lphc
, TRUE
, FALSE
);
1712 if( !IsWindow( hWnd
) ) return;
1714 if( lphc
->wState
& CBF_CAPTURE
)
1716 lphc
->wState
&= ~CBF_CAPTURE
;
1722 /* drop down the listbox and start tracking */
1724 lphc
->wState
|= CBF_CAPTURE
;
1728 if( bButton
) CBRepaintButton( lphc
);
1732 /***********************************************************************
1735 * Release capture and stop tracking if needed.
1737 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1739 if( lphc
->wState
& CBF_CAPTURE
)
1741 lphc
->wState
&= ~CBF_CAPTURE
;
1742 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1744 INT index
= CBUpdateLBox( lphc
, TRUE
);
1745 /* Update edit only if item is in the list */
1748 lphc
->wState
|= CBF_NOLBSELECT
;
1749 CBUpdateEdit( lphc
, index
);
1750 lphc
->wState
&= ~CBF_NOLBSELECT
;
1754 SetCapture(lphc
->hWndLBox
);
1757 if( lphc
->wState
& CBF_BUTTONDOWN
)
1759 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1760 CBRepaintButton( lphc
);
1764 /***********************************************************************
1767 * Two things to do - track combo button and release capture when
1768 * pointer goes into the listbox.
1770 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1775 pt
.x
= (short)LOWORD(lParam
);
1776 pt
.y
= (short)HIWORD(lParam
);
1778 if( lphc
->wState
& CBF_BUTTONDOWN
)
1782 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1786 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1787 CBRepaintButton( lphc
);
1791 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1792 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1793 if( PtInRect(&lbRect
, pt
) )
1795 lphc
->wState
&= ~CBF_CAPTURE
;
1797 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1799 /* hand over pointer tracking */
1800 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1804 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1806 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1809 pcbi
->rcItem
= lphc
->textRect
;
1810 pcbi
->rcButton
= lphc
->buttonRect
;
1811 pcbi
->stateButton
= 0;
1812 if (lphc
->wState
& CBF_BUTTONDOWN
)
1813 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1814 if (IsRectEmpty(&lphc
->buttonRect
))
1815 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1816 pcbi
->hwndCombo
= lphc
->self
;
1817 pcbi
->hwndItem
= lphc
->hWndEdit
;
1818 pcbi
->hwndList
= lphc
->hWndLBox
;
1822 static char *strdupA(LPCSTR str
)
1827 if(!str
) return NULL
;
1830 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1832 memcpy(ret
, str
, len
+ 1);
1836 /***********************************************************************
1837 * ComboWndProc_common
1839 LRESULT WINAPI
ComboWndProc_common( HWND hwnd
, UINT message
,
1840 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1842 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1844 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1845 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1847 if( lphc
|| message
== WM_NCCREATE
)
1851 /* System messages */
1855 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1856 ((LPCREATESTRUCTA
)lParam
)->style
;
1857 return COMBO_NCCreate(hwnd
, style
);
1860 COMBO_NCDestroy(lphc
);
1861 break;/* -> DefWindowProc */
1869 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1870 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1874 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1875 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1877 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1880 case WM_PRINTCLIENT
:
1883 /* wParam may contain a valid HDC! */
1884 return COMBO_Paint(lphc
, (HDC
)wParam
);
1887 /* do all painting in WM_PAINT like Windows does */
1892 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1893 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1895 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1897 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1898 result
|= DLGC_WANTMESSAGE
;
1902 case WM_WINDOWPOSCHANGING
:
1903 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1904 case WM_WINDOWPOSCHANGED
:
1905 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1906 * In that case, the Combobox itself will not be resized, so we won't
1907 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1910 /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
1911 * Z-order based painting.
1915 if( lphc
->hWndLBox
&&
1916 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
, message
== WM_SIZE
);
1919 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1922 return (LRESULT
)lphc
->hFont
;
1924 if( lphc
->wState
& CBF_EDIT
)
1925 SetFocus( lphc
->hWndEdit
);
1927 COMBO_SetFocus( lphc
);
1931 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1933 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1934 COMBO_KillFocus( lphc
);
1938 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1940 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1941 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1943 case WM_GETTEXTLENGTH
:
1945 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1947 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1948 if (j
== -1) return 0;
1949 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1950 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1952 else if( lphc
->wState
& CBF_EDIT
)
1955 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1956 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1957 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1958 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1965 if( lphc
->wState
& CBF_EDIT
)
1967 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1968 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1974 case WM_COMPAREITEM
:
1975 case WM_MEASUREITEM
:
1976 return COMBO_ItemOp(lphc
, message
, lParam
);
1978 if( lphc
->wState
& CBF_EDIT
)
1979 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1980 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1982 /* Force the control to repaint when the enabled state changes. */
1983 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1987 lphc
->wState
&= ~CBF_NOREDRAW
;
1989 lphc
->wState
|= CBF_NOREDRAW
;
1991 if( lphc
->wState
& CBF_EDIT
)
1992 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1993 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
1996 if( KEYDATA_ALT
& HIWORD(lParam
) )
1997 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1998 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2007 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
2008 (lphc
->wState
& CBF_DROPPED
))
2010 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
2013 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
2015 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2019 if( lphc
->wState
& CBF_EDIT
)
2020 hwndTarget
= lphc
->hWndEdit
;
2022 hwndTarget
= lphc
->hWndLBox
;
2024 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
2025 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
2027 case WM_LBUTTONDOWN
:
2028 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
2029 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
2032 COMBO_LButtonUp( lphc
);
2035 if( lphc
->wState
& CBF_CAPTURE
)
2036 COMBO_MouseMove( lphc
, wParam
, lParam
);
2040 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2041 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2042 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2044 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
2045 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2048 /* Combo messages */
2051 case CB_ADDSTRING16
:
2052 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2058 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2059 CharLowerW((LPWSTR
)lParam
);
2060 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2061 CharUpperW((LPWSTR
)lParam
);
2062 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
2064 else /* unlike the unicode version, the ansi version does not overwrite
2065 the string if converting case */
2067 char *string
= NULL
;
2069 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2071 string
= strdupA((LPSTR
)lParam
);
2075 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2077 string
= strdupA((LPSTR
)lParam
);
2081 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
2082 HeapFree(GetProcessHeap(), 0, string
);
2086 case CB_INSERTSTRING16
:
2087 wParam
= (INT
)(INT16
)wParam
;
2088 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2091 case CB_INSERTSTRING
:
2094 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2095 CharLowerW((LPWSTR
)lParam
);
2096 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2097 CharUpperW((LPWSTR
)lParam
);
2098 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2102 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2103 CharLowerA((LPSTR
)lParam
);
2104 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2105 CharUpperA((LPSTR
)lParam
);
2107 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2110 case CB_DELETESTRING16
:
2112 case CB_DELETESTRING
:
2113 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2114 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2116 case CB_SELECTSTRING16
:
2117 wParam
= (INT
)(INT16
)wParam
;
2118 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2121 case CB_SELECTSTRING
:
2122 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2124 case CB_FINDSTRING16
:
2125 wParam
= (INT
)(INT16
)wParam
;
2126 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2130 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2131 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2133 case CB_FINDSTRINGEXACT16
:
2134 wParam
= (INT
)(INT16
)wParam
;
2135 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2138 case CB_FINDSTRINGEXACT
:
2139 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2140 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2142 case CB_SETITEMHEIGHT16
:
2143 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
2146 case CB_SETITEMHEIGHT
:
2147 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2149 case CB_GETITEMHEIGHT16
:
2150 wParam
= (INT
)(INT16
)wParam
;
2153 case CB_GETITEMHEIGHT
:
2154 if( (INT
)wParam
>= 0 ) /* listbox item */
2155 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2156 return CBGetTextAreaHeight(hwnd
, lphc
);
2158 case CB_RESETCONTENT16
:
2160 case CB_RESETCONTENT
:
2161 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2162 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2164 static const WCHAR empty_stringW
[] = { 0 };
2165 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2168 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2170 case CB_INITSTORAGE
:
2171 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2172 case CB_GETHORIZONTALEXTENT
:
2173 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2174 case CB_SETHORIZONTALEXTENT
:
2175 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2176 case CB_GETTOPINDEX
:
2177 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2179 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2181 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2182 case CB_GETDROPPEDWIDTH
:
2183 if( lphc
->droppedWidth
)
2184 return lphc
->droppedWidth
;
2185 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2186 case CB_SETDROPPEDWIDTH
:
2187 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
2188 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
2191 case CB_GETDROPPEDCONTROLRECT16
:
2192 lParam
= (LPARAM
)MapSL(lParam
);
2196 RECT16
*r16
= (RECT16
*)lParam
;
2197 CBGetDroppedControlRect( lphc
, &r
);
2200 r16
->right
= r
.right
;
2201 r16
->bottom
= r
.bottom
;
2205 case CB_GETDROPPEDCONTROLRECT
:
2206 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2209 case CB_GETDROPPEDSTATE16
:
2211 case CB_GETDROPPEDSTATE
:
2212 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
2215 return SendMessageA(lphc
->hWndLBox
, LB_DIR16
, wParam
, lParam
);
2218 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2219 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2222 case CB_SHOWDROPDOWN16
:
2224 case CB_SHOWDROPDOWN
:
2225 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2229 if( !(lphc
->wState
& CBF_DROPPED
) )
2233 if( lphc
->wState
& CBF_DROPPED
)
2234 CBRollUp( lphc
, FALSE
, TRUE
);
2241 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2243 case CB_GETCURSEL16
:
2246 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2248 case CB_SETCURSEL16
:
2249 wParam
= (INT
)(INT16
)wParam
;
2253 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2255 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2257 /* no LBN_SELCHANGE in this case, update manually */
2258 if( lphc
->wState
& CBF_EDIT
)
2259 CBUpdateEdit( lphc
, (INT
)wParam
);
2261 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2262 lphc
->wState
&= ~CBF_SELCHANGE
;
2265 case CB_GETLBTEXT16
:
2266 wParam
= (INT
)(INT16
)wParam
;
2267 lParam
= (LPARAM
)MapSL(lParam
);
2271 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2272 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2274 case CB_GETLBTEXTLEN16
:
2275 wParam
= (INT
)(INT16
)wParam
;
2278 case CB_GETLBTEXTLEN
:
2279 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2280 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2282 case CB_GETITEMDATA16
:
2283 wParam
= (INT
)(INT16
)wParam
;
2286 case CB_GETITEMDATA
:
2287 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2289 case CB_SETITEMDATA16
:
2290 wParam
= (INT
)(INT16
)wParam
;
2293 case CB_SETITEMDATA
:
2294 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2296 case CB_GETEDITSEL16
:
2297 wParam
= lParam
= 0; /* just in case */
2301 /* Edit checks passed parameters itself */
2302 if( lphc
->wState
& CBF_EDIT
)
2303 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2306 case CB_SETEDITSEL16
:
2309 if( lphc
->wState
& CBF_EDIT
)
2310 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2311 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2314 case CB_SETEXTENDEDUI16
:
2316 case CB_SETEXTENDEDUI
:
2317 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2320 lphc
->wState
|= CBF_EUI
;
2321 else lphc
->wState
&= ~CBF_EUI
;
2324 case CB_GETEXTENDEDUI16
:
2326 case CB_GETEXTENDEDUI
:
2327 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2328 case CB_GETCOMBOBOXINFO
:
2329 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2331 if( lphc
->wState
& CBF_EDIT
)
2332 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2334 case WM_UPDATEUISTATE
:
2336 DefWindowProcW(lphc
->self
, message
, wParam
, lParam
);
2338 DefWindowProcA(lphc
->self
, message
, wParam
, lParam
);
2340 if (COMBO_update_uistate(lphc
))
2343 if( !(lphc
->wState
& CBF_EDIT
) )
2344 NtUserInvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2349 if (message
>= WM_USER
)
2350 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2351 message
- WM_USER
, wParam
, lParam
);
2354 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2355 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2358 /***********************************************************************
2361 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2364 LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2366 if (!IsWindow(hwnd
)) return 0;
2367 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, FALSE
);
2370 /***********************************************************************
2373 LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2375 if (!IsWindow(hwnd
)) return 0;
2376 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, TRUE
);
2379 /*************************************************************************
2380 * GetComboBoxInfo (USER32.@)
2382 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2383 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2385 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2387 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);
2389 return NtUserGetComboBoxInfo(hwndCombo
, pcbi
);