4 * Copyright 1997 Alex Korobka
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 4, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
30 * - ComboBox_[GS]etMinVisible()
31 * - CB_GETMINVISIBLE, CB_SETMINVISIBLE
44 #include "wine/unicode.h"
45 #include "user_private.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(combo
);
53 /* bits in the dwKeyData */
54 #define KEYDATA_ALT 0x2000
55 #define KEYDATA_PREVSTATE 0x4000
58 * Additional combo box definitions
61 #define CB_NOTIFY( lphc, code ) \
62 (SendMessageW((lphc)->owner, WM_COMMAND, \
63 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
65 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
66 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
67 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
68 #define CB_HWND( lphc ) ((lphc)->self)
69 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
71 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
76 static HBITMAP hComboBmp
= 0;
77 static UINT CBitHeight
, CBitWidth
;
80 * Look and feel dependent "constants"
83 #define COMBO_YBORDERGAP 5
84 #define COMBO_XBORDERSIZE() 2
85 #define COMBO_YBORDERSIZE() 2
86 #define COMBO_EDITBUTTONSPACE() 0
87 #define EDIT_CONTROL_PADDING() 1
89 /*********************************************************************
90 * combo class descriptor
92 static const WCHAR comboboxW
[] = {'C','o','m','b','o','B','o','x',0};
93 const struct builtin_class_descr COMBO_builtin_class
=
96 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
98 BUILTIN_WINPROC(WINPROC_COMBO
), /* procW */
99 sizeof(HEADCOMBO
*), /* extra */
100 IDC_ARROW
, /* cursor */
105 /***********************************************************************
108 * Load combo button bitmap.
110 static BOOL
COMBO_Init(void)
114 if( hComboBmp
) return TRUE
;
115 if( (hDC
= CreateCompatibleDC(0)) )
118 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
124 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
125 CBitHeight
= bm
.bmHeight
;
126 CBitWidth
= bm
.bmWidth
;
128 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
130 hPrevB
= SelectObject( hDC
, hComboBmp
);
131 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
132 InvertRect( hDC
, &r
);
133 SelectObject( hDC
, hPrevB
);
142 /***********************************************************************
145 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
149 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
152 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)lphc
);
154 /* some braindead apps do try to use scrollbar/border flags */
156 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
157 SetWindowLongW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
160 * We also have to remove the client edge style to make sure
161 * we don't end-up with a non client area.
163 SetWindowLongW( hwnd
, GWL_EXSTYLE
,
164 GetWindowLongW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
166 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
167 lphc
->dwStyle
|= CBS_HASSTRINGS
;
168 if( !(GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
169 lphc
->wState
|= CBF_NOTIFY
;
171 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
177 /***********************************************************************
180 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
185 TRACE("[%p]: freeing storage\n", lphc
->self
);
187 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
188 DestroyWindow( lphc
->hWndLBox
);
190 SetWindowLongPtrW( lphc
->self
, 0, 0 );
191 HeapFree( GetProcessHeap(), 0, lphc
);
196 /***********************************************************************
197 * CBGetTextAreaHeight
199 * This method will calculate the height of the text area of the
201 * The height of the text area is set in two ways.
202 * It can be set explicitly through a combobox message or through a
203 * WM_MEASUREITEM callback.
204 * If this is not the case, the height is set to font height + 4px
205 * This height was determined through experimentation.
206 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
208 static INT
CBGetTextAreaHeight(
214 if( lphc
->editHeight
) /* explicitly set height */
216 iTextItemHeight
= lphc
->editHeight
;
221 HDC hDC
= GetDC(hwnd
);
226 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
228 GetTextMetricsW(hDC
, &tm
);
230 baseUnitY
= tm
.tmHeight
;
233 SelectObject( hDC
, hPrevFont
);
235 ReleaseDC(hwnd
, hDC
);
237 iTextItemHeight
= baseUnitY
+ 4;
241 * Check the ownerdraw case if we haven't asked the parent the size
244 if ( CB_OWNERDRAWN(lphc
) &&
245 (lphc
->wState
& CBF_MEASUREITEM
) )
247 MEASUREITEMSTRUCT measureItem
;
249 INT originalItemHeight
= iTextItemHeight
;
250 UINT id
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
253 * We use the client rect for the width of the item.
255 GetClientRect(hwnd
, &clientRect
);
257 lphc
->wState
&= ~CBF_MEASUREITEM
;
260 * Send a first one to measure the size of the text area
262 measureItem
.CtlType
= ODT_COMBOBOX
;
263 measureItem
.CtlID
= id
;
264 measureItem
.itemID
= -1;
265 measureItem
.itemWidth
= clientRect
.right
;
266 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
267 measureItem
.itemData
= 0;
268 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
269 iTextItemHeight
= 6 + measureItem
.itemHeight
;
272 * Send a second one in the case of a fixed ownerdraw list to calculate the
273 * size of the list items. (we basically do this on behalf of the listbox)
275 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
277 measureItem
.CtlType
= ODT_COMBOBOX
;
278 measureItem
.CtlID
= id
;
279 measureItem
.itemID
= 0;
280 measureItem
.itemWidth
= clientRect
.right
;
281 measureItem
.itemHeight
= originalItemHeight
;
282 measureItem
.itemData
= 0;
283 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
284 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
288 * Keep the size for the next time
290 lphc
->editHeight
= iTextItemHeight
;
293 return iTextItemHeight
;
296 /***********************************************************************
299 * The dummy resize is used for listboxes that have a popup to trigger
300 * a re-arranging of the contents of the combobox and the recalculation
301 * of the size of the "real" control window.
303 static void CBForceDummyResize(
309 newComboHeight
= CBGetTextAreaHeight(lphc
->self
,lphc
) + 2*COMBO_YBORDERSIZE();
311 GetWindowRect(lphc
->self
, &windowRect
);
314 * We have to be careful, resizing a combobox also has the meaning that the
315 * dropped rect will be resized. In this case, we want to trigger a resize
316 * to recalculate layout but we don't want to change the dropped rectangle
317 * So, we pass the height of text area of control as the height.
318 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
321 SetWindowPos( lphc
->self
,
324 windowRect
.right
- windowRect
.left
,
326 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
329 /***********************************************************************
332 * Set up component coordinates given valid lphc->RectCombo.
334 static void CBCalcPlacement(
342 * Again, start with the client rectangle.
344 GetClientRect(hwnd
, lprEdit
);
349 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
352 * Chop off the bottom part to fit with the height of the text area.
354 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
357 * The button starts the same vertical position as the text area.
359 CopyRect(lprButton
, lprEdit
);
362 * If the combobox is "simple" there is no button.
364 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
365 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
369 * Let's assume the combobox button is the same width as the
371 * size the button horizontally and cut-off the text area.
373 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
374 lprEdit
->right
= lprButton
->left
;
378 * In the case of a dropdown, there is an additional spacing between the
379 * text area and the button.
381 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
383 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
387 * If we have an edit control, we space it away from the borders slightly.
389 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
391 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
395 * Adjust the size of the listbox popup.
397 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
400 * Use the client rectangle to initialize the listbox rectangle
402 GetClientRect(hwnd
, lprLB
);
405 * Then, chop-off the top part.
407 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
412 * Make sure the dropped width is as large as the combobox itself.
414 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
416 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
419 * In the case of a dropdown, the popup listbox is offset to the right.
420 * so, we want to make sure it's flush with the right side of the
423 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
424 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
427 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
430 /* don't allow negative window width */
431 if (lprEdit
->right
< lprEdit
->left
)
432 lprEdit
->right
= lprEdit
->left
;
434 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit
));
436 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton
));
438 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB
));
441 /***********************************************************************
442 * CBGetDroppedControlRect
444 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
446 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
447 of the combo box and the lower right corner of the listbox */
449 GetWindowRect(lphc
->self
, lpRect
);
451 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
452 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
456 /***********************************************************************
457 * COMBO_WindowPosChanging
459 static LRESULT
COMBO_WindowPosChanging(
462 WINDOWPOS
* posChanging
)
465 * We need to override the WM_WINDOWPOSCHANGING method to handle all
466 * the non-simple comboboxes. The problem is that those controls are
467 * always the same height. We have to make sure they are not resized
470 if ( ( CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
471 ((posChanging
->flags
& SWP_NOSIZE
) == 0) )
475 newComboHeight
= CBGetTextAreaHeight(hwnd
,lphc
) +
476 2*COMBO_YBORDERSIZE();
479 * Resizing a combobox has another side effect, it resizes the dropped
480 * rectangle as well. However, it does it only if the new height for the
481 * combobox is more than the height it should have. In other words,
482 * if the application resizing the combobox only had the intention to resize
483 * the actual control, for example, to do the layout of a dialog that is
484 * resized, the height of the dropdown is not changed.
486 if (posChanging
->cy
> newComboHeight
)
488 TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
489 posChanging
->cy
, newComboHeight
, lphc
->droppedRect
.bottom
,
490 lphc
->droppedRect
.top
);
491 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ posChanging
->cy
- newComboHeight
;
494 posChanging
->cy
= newComboHeight
;
500 /***********************************************************************
503 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
506 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
507 static const WCHAR editName
[] = {'E','d','i','t',0};
509 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
510 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
512 lphc
->owner
= hwndParent
;
515 * The item height and dropped width are not set when the control
518 lphc
->droppedWidth
= lphc
->editHeight
= 0;
521 * The first time we go through, we want to measure the ownerdraw item
523 lphc
->wState
|= CBF_MEASUREITEM
;
525 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
527 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
533 * Initialize the dropped rect to the size of the client area of the
534 * control and then, force all the areas of the combobox to be
537 GetClientRect( hwnd
, &lphc
->droppedRect
);
538 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
541 * Adjust the position of the popup listbox if it's necessary
543 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
545 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
548 * If it's a dropdown, the listbox is offset
550 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
551 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
553 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
554 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
555 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
556 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
557 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
560 /* create listbox popup */
562 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
563 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
565 if( lphc
->dwStyle
& CBS_SORT
)
566 lbeStyle
|= LBS_SORT
;
567 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
568 lbeStyle
|= LBS_HASSTRINGS
;
569 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
570 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
571 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
572 lbeStyle
|= LBS_DISABLENOSCROLL
;
574 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
576 lbeStyle
|= WS_VISIBLE
;
579 * In win 95 look n feel, the listbox in the simple combobox has
580 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
582 lbeStyle
&= ~WS_BORDER
;
583 lbeExStyle
|= WS_EX_CLIENTEDGE
;
587 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
591 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
592 lphc
->droppedRect
.left
,
593 lphc
->droppedRect
.top
,
594 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
595 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
596 hwnd
, (HMENU
)ID_CB_LISTBOX
,
597 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
599 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
600 lphc
->droppedRect
.left
,
601 lphc
->droppedRect
.top
,
602 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
603 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
604 hwnd
, (HMENU
)ID_CB_LISTBOX
,
605 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
610 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
612 if( lphc
->wState
& CBF_EDIT
)
614 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
615 lbeStyle
|= ES_OEMCONVERT
;
616 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
617 lbeStyle
|= ES_AUTOHSCROLL
;
618 if( lphc
->dwStyle
& CBS_LOWERCASE
)
619 lbeStyle
|= ES_LOWERCASE
;
620 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
621 lbeStyle
|= ES_UPPERCASE
;
623 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
626 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
627 lphc
->textRect
.left
, lphc
->textRect
.top
,
628 lphc
->textRect
.right
- lphc
->textRect
.left
,
629 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
630 hwnd
, (HMENU
)ID_CB_EDIT
,
631 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
633 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
634 lphc
->textRect
.left
, lphc
->textRect
.top
,
635 lphc
->textRect
.right
- lphc
->textRect
.left
,
636 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
637 hwnd
, (HMENU
)ID_CB_EDIT
,
638 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
640 if( !lphc
->hWndEdit
)
646 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
648 /* Now do the trick with parent */
649 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
651 * If the combo is a dropdown, we must resize the control
652 * to fit only the text area and button. To do this,
653 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
654 * will take care of setting the height for us.
656 CBForceDummyResize(lphc
);
659 TRACE("init done\n");
662 ERR("edit control failure.\n");
663 } else ERR("listbox failure.\n");
664 } else ERR("no owner for visible combo.\n");
666 /* CreateWindow() will send WM_NCDESTROY to cleanup */
671 /***********************************************************************
674 * Paint combo button (normal, pressed, and disabled states).
676 static void CBPaintButton( LPHEADCOMBO lphc
, HDC hdc
, RECT rectButton
)
678 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
680 if( lphc
->wState
& CBF_NOREDRAW
)
684 if (lphc
->wState
& CBF_BUTTONDOWN
)
685 buttonState
|= DFCS_PUSHED
;
687 if (CB_DISABLED(lphc
))
688 buttonState
|= DFCS_INACTIVE
;
690 DrawFrameControl(hdc
, &rectButton
, DFC_SCROLL
, buttonState
);
693 /***********************************************************************
696 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
698 static void CBPaintText(
706 if( lphc
->wState
& CBF_NOREDRAW
) return;
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( !CB_OWNERDRAWN(lphc
) )
729 if( lphc
->wState
& CBF_EDIT
)
731 static const WCHAR empty_stringW
[] = { 0 };
732 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
733 if( lphc
->wState
& CBF_FOCUSED
)
734 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
736 else /* paint text field ourselves */
738 UINT itemState
= ODS_COMBOBOXEDIT
;
739 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
742 * Give ourselves some space.
744 InflateRect( &rectEdit
, -1, -1 );
746 if( CB_OWNERDRAWN(lphc
) )
750 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
752 /* setup state for DRAWITEM message. Owner will highlight */
753 if ( (lphc
->wState
& CBF_FOCUSED
) &&
754 !(lphc
->wState
& CBF_DROPPED
) )
755 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
758 * Save the current clip region.
759 * To retrieve the clip region, we need to create one "dummy"
762 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
764 if (GetClipRgn(hdc
, clipRegion
)!=1)
766 DeleteObject(clipRegion
);
770 if (!IsWindowEnabled(lphc
->self
)) itemState
|= ODS_DISABLED
;
772 dis
.CtlType
= ODT_COMBOBOX
;
774 dis
.hwndItem
= lphc
->self
;
775 dis
.itemAction
= ODA_DRAWENTIRE
;
777 dis
.itemState
= itemState
;
779 dis
.rcItem
= rectEdit
;
780 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
,
784 * Clip the DC and have the parent draw the item.
786 IntersectClipRect(hdc
,
787 rectEdit
.left
, rectEdit
.top
,
788 rectEdit
.right
, rectEdit
.bottom
);
790 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
793 * Reset the clipping region.
795 SelectClipRgn(hdc
, clipRegion
);
799 static const WCHAR empty_stringW
[] = { 0 };
801 if ( (lphc
->wState
& CBF_FOCUSED
) &&
802 !(lphc
->wState
& CBF_DROPPED
) ) {
805 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
806 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
807 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
813 ETO_OPAQUE
| ETO_CLIPPED
,
815 pText
? pText
: empty_stringW
, size
, NULL
);
817 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
818 DrawFocusRect( hdc
, &rectEdit
);
822 SelectObject(hdc
, hPrevFont
);
824 HeapFree( GetProcessHeap(), 0, pText
);
827 /***********************************************************************
830 static void CBPaintBorder(
832 const HEADCOMBO
*lphc
,
837 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
839 GetClientRect(hwnd
, &clientRect
);
843 CopyRect(&clientRect
, &lphc
->textRect
);
845 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
846 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
849 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
852 /***********************************************************************
853 * COMBO_PrepareColors
855 * This method will sent the appropriate WM_CTLCOLOR message to
856 * prepare and setup the colors for the combo's DC.
858 * It also returns the brush to use for the background.
860 static HBRUSH
COMBO_PrepareColors(
867 * Get the background brush for this control.
869 if (CB_DISABLED(lphc
))
871 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
872 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
875 * We have to change the text color since WM_CTLCOLORSTATIC will
876 * set it to the "enabled" color. This is the same behavior as the
879 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
883 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
884 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
885 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
892 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
898 /***********************************************************************
901 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
906 hDC
= (hParamDC
) ? hParamDC
907 : BeginPaint( lphc
->self
, &ps
);
909 TRACE("hdc=%p\n", hDC
);
911 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
913 HBRUSH hPrevBrush
, hBkgBrush
;
916 * Retrieve the background brush and select it in the
919 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
921 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
922 if (!(lphc
->wState
& CBF_EDIT
))
923 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
926 * In non 3.1 look, there is a sunken border on the combobox
928 CBPaintBorder(lphc
->self
, lphc
, hDC
);
930 if( !IsRectEmpty(&lphc
->buttonRect
) )
932 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
935 /* paint the edit control padding area */
936 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
938 RECT rPadEdit
= lphc
->textRect
;
940 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
942 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
945 if( !(lphc
->wState
& CBF_EDIT
) )
946 CBPaintText( lphc
, hDC
, lphc
->textRect
);
949 SelectObject( hDC
, hPrevBrush
);
953 EndPaint(lphc
->self
, &ps
);
958 /***********************************************************************
961 * Select listbox entry according to the contents of the edit control.
963 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
969 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
972 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
974 TRACE("\t edit text length %i\n", length
);
978 GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
979 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
,
980 (WPARAM
)(-1), (LPARAM
)pText
);
981 HeapFree( GetProcessHeap(), 0, pText
);
984 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)(bSelect
? idx
: -1), 0);
986 /* probably superfluous but Windows sends this too */
987 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
988 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
993 /***********************************************************************
996 * Copy a listbox entry to the edit control.
998 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1001 LPWSTR pText
= NULL
;
1002 static const WCHAR empty_stringW
[] = { 0 };
1004 TRACE("\t %i\n", index
);
1006 if( index
>= 0 ) /* got an entry */
1008 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1009 if( length
!= LB_ERR
)
1011 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
1013 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
,
1014 (WPARAM
)index
, (LPARAM
)pText
);
1019 if( CB_HASSTRINGS(lphc
) )
1021 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1022 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
1023 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1026 if( lphc
->wState
& CBF_FOCUSED
)
1027 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1029 HeapFree( GetProcessHeap(), 0, pText
);
1032 /***********************************************************************
1035 * Show listbox popup.
1037 static void CBDropDown( LPHEADCOMBO lphc
)
1040 MONITORINFO mon_info
;
1045 TRACE("[%p]: drop down\n", lphc
->self
);
1047 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1051 lphc
->wState
|= CBF_DROPPED
;
1052 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1054 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
1056 /* Update edit only if item is in the list */
1057 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
1058 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1062 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1064 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
1065 (WPARAM
)(lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
), 0 );
1066 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1069 /* now set popup position */
1070 GetWindowRect( lphc
->self
, &rect
);
1073 * If it's a dropdown, the listbox is offset
1075 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1076 rect
.left
+= COMBO_EDITBUTTONSPACE();
1078 /* if the dropped height is greater than the total height of the dropped
1079 items list, then force the drop down list height to be the total height
1080 of the items in the dropped list */
1082 /* And Remove any extra space (Best Fit) */
1083 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1084 /* if listbox length has been set directly by its handle */
1085 GetWindowRect(lphc
->hWndLBox
, &r
);
1086 if (nDroppedHeight
< r
.bottom
- r
.top
)
1087 nDroppedHeight
= r
.bottom
- r
.top
;
1088 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1095 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1097 nHeight
= nIHeight
*nItems
;
1099 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1100 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1102 if (nDroppedHeight
< nHeight
)
1105 nDroppedHeight
= (nItems
+1)*nIHeight
;
1106 else if (nDroppedHeight
< 6*nIHeight
)
1107 nDroppedHeight
= 6*nIHeight
;
1111 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1112 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
1113 mon_info
.cbSize
= sizeof(mon_info
);
1114 GetMonitorInfoW( monitor
, &mon_info
);
1116 if( (rect
.bottom
+ nDroppedHeight
) >= mon_info
.rcWork
.bottom
)
1117 rect
.bottom
= rect
.top
- nDroppedHeight
;
1119 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1120 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1122 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1125 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1126 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1127 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1129 EnableWindow( lphc
->hWndLBox
, TRUE
);
1130 if (GetCapture() != lphc
->self
)
1131 SetCapture(lphc
->hWndLBox
);
1134 /***********************************************************************
1137 * Hide listbox popup.
1139 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1141 HWND hWnd
= lphc
->self
;
1143 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1144 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1146 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1148 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1151 if( lphc
->wState
& CBF_DROPPED
)
1155 lphc
->wState
&= ~CBF_DROPPED
;
1156 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1158 if(GetCapture() == lphc
->hWndLBox
)
1163 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1165 rect
= lphc
->buttonRect
;
1176 rect
= lphc
->textRect
;
1181 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1182 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1183 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1184 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1189 /***********************************************************************
1192 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1194 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1196 if( lphc
->wState
& CBF_DROPPED
)
1198 CBRollUp( lphc
, ok
, bRedrawButton
);
1206 /***********************************************************************
1209 static void CBRepaintButton( LPHEADCOMBO lphc
)
1211 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1212 UpdateWindow(lphc
->self
);
1215 /***********************************************************************
1218 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1220 if( !(lphc
->wState
& CBF_FOCUSED
) )
1222 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1223 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1225 /* This is wrong. Message sequences seem to indicate that this
1226 is set *after* the notify. */
1227 /* lphc->wState |= CBF_FOCUSED; */
1229 if( !(lphc
->wState
& CBF_EDIT
) )
1230 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1232 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1233 lphc
->wState
|= CBF_FOCUSED
;
1237 /***********************************************************************
1240 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1242 HWND hWnd
= lphc
->self
;
1244 if( lphc
->wState
& CBF_FOCUSED
)
1246 CBRollUp( lphc
, FALSE
, TRUE
);
1247 if( IsWindow( hWnd
) )
1249 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1250 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1252 lphc
->wState
&= ~CBF_FOCUSED
;
1255 if( !(lphc
->wState
& CBF_EDIT
) )
1256 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1258 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1263 /***********************************************************************
1266 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1268 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1270 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1272 switch( HIWORD(wParam
) >> 8 )
1274 case (EN_SETFOCUS
>> 8):
1276 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1278 COMBO_SetFocus( lphc
);
1281 case (EN_KILLFOCUS
>> 8):
1283 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1285 /* NOTE: it seems that Windows' edit control sends an
1286 * undocumented message WM_USER + 0x1B instead of this
1287 * notification (only when it happens to be a part of
1288 * the combo). ?? - AK.
1291 COMBO_KillFocus( lphc
);
1295 case (EN_CHANGE
>> 8):
1297 * In some circumstances (when the selection of the combobox
1298 * is changed for example) we don't want the EN_CHANGE notification
1299 * to be forwarded to the parent of the combobox. This code
1300 * checks a flag that is set in these occasions and ignores the
1303 if (lphc
->wState
& CBF_NOLBSELECT
)
1305 lphc
->wState
&= ~CBF_NOLBSELECT
;
1309 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1312 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1313 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1316 case (EN_UPDATE
>> 8):
1317 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1318 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1321 case (EN_ERRSPACE
>> 8):
1322 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1325 else if( lphc
->hWndLBox
== hWnd
)
1327 switch( (short)HIWORD(wParam
) )
1330 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1334 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1340 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1342 /* do not roll up if selection is being tracked
1343 * by arrow keys in the dropdown listbox */
1344 if (!(lphc
->wState
& CBF_NOROLLUP
))
1346 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1348 else lphc
->wState
&= ~CBF_NOROLLUP
;
1350 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1352 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1354 if( lphc
->wState
& CBF_EDIT
)
1356 INT index
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1357 lphc
->wState
|= CBF_NOLBSELECT
;
1358 CBUpdateEdit( lphc
, index
);
1359 /* select text in edit, as Windows does */
1360 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1364 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1365 UpdateWindow(lphc
->self
);
1372 /* nothing to do here since ComboLBox always resets the focus to its
1373 * combo/edit counterpart */
1380 /***********************************************************************
1383 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1385 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1387 HWND hWnd
= lphc
->self
;
1388 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1390 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1396 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1397 lpIS
->CtlType
= ODT_COMBOBOX
;
1399 lpIS
->hwndItem
= hWnd
;
1404 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1405 lpIS
->CtlType
= ODT_COMBOBOX
;
1407 lpIS
->hwndItem
= hWnd
;
1410 case WM_COMPAREITEM
:
1412 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1413 lpIS
->CtlType
= ODT_COMBOBOX
;
1415 lpIS
->hwndItem
= hWnd
;
1418 case WM_MEASUREITEM
:
1420 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1421 lpIS
->CtlType
= ODT_COMBOBOX
;
1426 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1430 /***********************************************************************
1433 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1437 if( lphc
->wState
& CBF_EDIT
)
1438 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1440 /* get it from the listbox */
1442 if (!count
|| !buf
) return 0;
1443 if( lphc
->hWndLBox
)
1445 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1446 if (idx
== LB_ERR
) goto error
;
1447 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1448 if (length
== LB_ERR
) goto error
;
1450 /* 'length' is without the terminating character */
1451 if (length
>= count
)
1453 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1454 if (!lpBuffer
) goto error
;
1455 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1457 /* truncate if buffer is too short */
1458 if (length
!= LB_ERR
)
1460 lstrcpynW( buf
, lpBuffer
, count
);
1463 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1465 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1467 if (length
== LB_ERR
) return 0;
1471 error
: /* error - truncate string, return zero */
1477 /***********************************************************************
1480 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1481 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1483 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1487 if( lphc
->wState
& CBF_EDIT
)
1488 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1490 /* get it from the listbox */
1492 if (!count
|| !buf
) return 0;
1493 if( lphc
->hWndLBox
)
1495 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1496 if (idx
== LB_ERR
) goto error
;
1497 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1498 if (length
== LB_ERR
) goto error
;
1500 /* 'length' is without the terminating character */
1501 if (length
>= count
)
1503 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1504 if (!lpBuffer
) goto error
;
1505 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1507 /* truncate if buffer is too short */
1508 if (length
!= LB_ERR
)
1510 lstrcpynA( buf
, lpBuffer
, count
);
1513 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1515 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1517 if (length
== LB_ERR
) return 0;
1521 error
: /* error - truncate string, return zero */
1527 /***********************************************************************
1530 * This function sets window positions according to the updated
1531 * component placement struct.
1533 static void CBResetPos(
1535 const RECT
*rectEdit
,
1539 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1541 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1542 * sizing messages */
1544 if( lphc
->wState
& CBF_EDIT
)
1545 SetWindowPos( lphc
->hWndEdit
, 0,
1546 rectEdit
->left
, rectEdit
->top
,
1547 rectEdit
->right
- rectEdit
->left
,
1548 rectEdit
->bottom
- rectEdit
->top
,
1549 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1551 SetWindowPos( lphc
->hWndLBox
, 0,
1552 rectLB
->left
, rectLB
->top
,
1553 rectLB
->right
- rectLB
->left
,
1554 rectLB
->bottom
- rectLB
->top
,
1555 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1559 if( lphc
->wState
& CBF_DROPPED
)
1561 lphc
->wState
&= ~CBF_DROPPED
;
1562 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1565 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1566 RedrawWindow( lphc
->self
, NULL
, 0,
1567 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1572 /***********************************************************************
1575 static void COMBO_Size( LPHEADCOMBO lphc
, BOOL bRedraw
)
1577 CBCalcPlacement(lphc
->self
,
1581 &lphc
->droppedRect
);
1583 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, bRedraw
);
1587 /***********************************************************************
1590 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1595 lphc
->hFont
= hFont
;
1598 * Propagate to owned windows.
1600 if( lphc
->wState
& CBF_EDIT
)
1601 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1602 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1605 * Redo the layout of the control.
1607 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1609 CBCalcPlacement(lphc
->self
,
1613 &lphc
->droppedRect
);
1615 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1619 CBForceDummyResize(lphc
);
1624 /***********************************************************************
1625 * COMBO_SetItemHeight
1627 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1629 LRESULT lRet
= CB_ERR
;
1631 if( index
== -1 ) /* set text field height */
1633 if( height
< 32768 )
1635 lphc
->editHeight
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1638 * Redo the layout of the control.
1640 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1642 CBCalcPlacement(lphc
->self
,
1646 &lphc
->droppedRect
);
1648 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1652 CBForceDummyResize(lphc
);
1658 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1659 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1660 (WPARAM
)index
, (LPARAM
)height
);
1664 /***********************************************************************
1665 * COMBO_SelectString
1667 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1669 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
) :
1670 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
);
1673 if( lphc
->wState
& CBF_EDIT
)
1674 CBUpdateEdit( lphc
, index
);
1677 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1680 return (LRESULT
)index
;
1683 /***********************************************************************
1686 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1690 HWND hWnd
= lphc
->self
;
1692 pt
.x
= (short)LOWORD(lParam
);
1693 pt
.y
= (short)HIWORD(lParam
);
1694 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1696 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1697 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1699 lphc
->wState
|= CBF_BUTTONDOWN
;
1700 if( lphc
->wState
& CBF_DROPPED
)
1702 /* got a click to cancel selection */
1704 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1705 CBRollUp( lphc
, TRUE
, FALSE
);
1706 if( !IsWindow( hWnd
) ) return;
1708 if( lphc
->wState
& CBF_CAPTURE
)
1710 lphc
->wState
&= ~CBF_CAPTURE
;
1716 /* drop down the listbox and start tracking */
1718 lphc
->wState
|= CBF_CAPTURE
;
1722 if( bButton
) CBRepaintButton( lphc
);
1726 /***********************************************************************
1729 * Release capture and stop tracking if needed.
1731 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1733 if( lphc
->wState
& CBF_CAPTURE
)
1735 lphc
->wState
&= ~CBF_CAPTURE
;
1736 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1738 INT index
= CBUpdateLBox( lphc
, TRUE
);
1739 /* Update edit only if item is in the list */
1742 lphc
->wState
|= CBF_NOLBSELECT
;
1743 CBUpdateEdit( lphc
, index
);
1744 lphc
->wState
&= ~CBF_NOLBSELECT
;
1748 SetCapture(lphc
->hWndLBox
);
1751 if( lphc
->wState
& CBF_BUTTONDOWN
)
1753 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1754 CBRepaintButton( lphc
);
1758 /***********************************************************************
1761 * Two things to do - track combo button and release capture when
1762 * pointer goes into the listbox.
1764 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1769 pt
.x
= (short)LOWORD(lParam
);
1770 pt
.y
= (short)HIWORD(lParam
);
1772 if( lphc
->wState
& CBF_BUTTONDOWN
)
1776 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1780 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1781 CBRepaintButton( lphc
);
1785 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1786 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1787 if( PtInRect(&lbRect
, pt
) )
1789 lphc
->wState
&= ~CBF_CAPTURE
;
1791 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1793 /* hand over pointer tracking */
1794 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1798 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1800 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1803 pcbi
->rcItem
= lphc
->textRect
;
1804 pcbi
->rcButton
= lphc
->buttonRect
;
1805 pcbi
->stateButton
= 0;
1806 if (lphc
->wState
& CBF_BUTTONDOWN
)
1807 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1808 if (IsRectEmpty(&lphc
->buttonRect
))
1809 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1810 pcbi
->hwndCombo
= lphc
->self
;
1811 pcbi
->hwndItem
= lphc
->hWndEdit
;
1812 pcbi
->hwndList
= lphc
->hWndLBox
;
1816 static char *strdupA(LPCSTR str
)
1821 if(!str
) return NULL
;
1824 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1825 memcpy(ret
, str
, len
+ 1);
1829 /***********************************************************************
1830 * ComboWndProc_common
1832 LRESULT
ComboWndProc_common( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1834 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1836 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1837 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1839 if (!IsWindow(hwnd
)) return 0;
1841 if( lphc
|| message
== WM_NCCREATE
)
1845 /* System messages */
1849 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1850 ((LPCREATESTRUCTA
)lParam
)->style
;
1851 return COMBO_NCCreate(hwnd
, style
);
1854 COMBO_NCDestroy(lphc
);
1855 break;/* -> DefWindowProc */
1863 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1864 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1868 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1869 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1871 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1874 case WM_PRINTCLIENT
:
1877 /* wParam may contain a valid HDC! */
1878 return COMBO_Paint(lphc
, (HDC
)wParam
);
1881 /* do all painting in WM_PAINT like Windows does */
1886 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1887 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1889 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1891 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1892 result
|= DLGC_WANTMESSAGE
;
1896 case WM_WINDOWPOSCHANGING
:
1897 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1898 case WM_WINDOWPOSCHANGED
:
1899 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1900 * In that case, the Combobox itself will not be resized, so we won't
1901 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1904 /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
1905 * Z-order based painting.
1909 if( lphc
->hWndLBox
&&
1910 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
, message
== WM_SIZE
);
1913 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1916 return (LRESULT
)lphc
->hFont
;
1918 if( lphc
->wState
& CBF_EDIT
)
1919 SetFocus( lphc
->hWndEdit
);
1921 COMBO_SetFocus( lphc
);
1925 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1927 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1928 COMBO_KillFocus( lphc
);
1932 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1934 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1935 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1937 case WM_GETTEXTLENGTH
:
1939 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1941 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1942 if (j
== -1) return 0;
1943 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1944 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1946 else if( lphc
->wState
& CBF_EDIT
)
1949 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1950 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1951 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1952 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1959 if( lphc
->wState
& CBF_EDIT
)
1961 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1962 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1968 case WM_COMPAREITEM
:
1969 case WM_MEASUREITEM
:
1970 return COMBO_ItemOp(lphc
, message
, lParam
);
1972 if( lphc
->wState
& CBF_EDIT
)
1973 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1974 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1976 /* Force the control to repaint when the enabled state changes. */
1977 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1981 lphc
->wState
&= ~CBF_NOREDRAW
;
1983 lphc
->wState
|= CBF_NOREDRAW
;
1985 if( lphc
->wState
& CBF_EDIT
)
1986 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1987 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
1990 if( KEYDATA_ALT
& HIWORD(lParam
) )
1991 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1992 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2001 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
2002 (lphc
->wState
& CBF_DROPPED
))
2004 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
2007 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
2009 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2013 if( lphc
->wState
& CBF_EDIT
)
2014 hwndTarget
= lphc
->hWndEdit
;
2016 hwndTarget
= lphc
->hWndLBox
;
2018 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
2019 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
2021 case WM_LBUTTONDOWN
:
2022 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
2023 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
2026 COMBO_LButtonUp( lphc
);
2029 if( lphc
->wState
& CBF_CAPTURE
)
2030 COMBO_MouseMove( lphc
, wParam
, lParam
);
2034 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2035 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2036 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2038 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
2039 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2042 /* Combo messages */
2047 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2048 CharLowerW((LPWSTR
)lParam
);
2049 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2050 CharUpperW((LPWSTR
)lParam
);
2051 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
2053 else /* unlike the unicode version, the ansi version does not overwrite
2054 the string if converting case */
2056 char *string
= NULL
;
2058 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2060 string
= strdupA((LPSTR
)lParam
);
2064 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2066 string
= strdupA((LPSTR
)lParam
);
2070 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
2071 HeapFree(GetProcessHeap(), 0, string
);
2074 case CB_INSERTSTRING
:
2077 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2078 CharLowerW((LPWSTR
)lParam
);
2079 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2080 CharUpperW((LPWSTR
)lParam
);
2081 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2085 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2086 CharLowerA((LPSTR
)lParam
);
2087 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2088 CharUpperA((LPSTR
)lParam
);
2090 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2092 case CB_DELETESTRING
:
2093 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2094 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2095 case CB_SELECTSTRING
:
2096 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2098 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2099 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2100 case CB_FINDSTRINGEXACT
:
2101 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2102 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2103 case CB_SETITEMHEIGHT
:
2104 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2105 case CB_GETITEMHEIGHT
:
2106 if( (INT
)wParam
>= 0 ) /* listbox item */
2107 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2108 return CBGetTextAreaHeight(hwnd
, lphc
);
2109 case CB_RESETCONTENT
:
2110 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2111 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2113 static const WCHAR empty_stringW
[] = { 0 };
2114 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2117 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2119 case CB_INITSTORAGE
:
2120 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2121 case CB_GETHORIZONTALEXTENT
:
2122 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2123 case CB_SETHORIZONTALEXTENT
:
2124 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2125 case CB_GETTOPINDEX
:
2126 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2128 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2130 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2131 case CB_GETDROPPEDWIDTH
:
2132 if( lphc
->droppedWidth
)
2133 return lphc
->droppedWidth
;
2134 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2135 case CB_SETDROPPEDWIDTH
:
2136 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
2137 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
2139 case CB_GETDROPPEDCONTROLRECT
:
2140 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2142 case CB_GETDROPPEDSTATE
:
2143 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
2145 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2146 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2148 case CB_SHOWDROPDOWN
:
2149 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2153 if( !(lphc
->wState
& CBF_DROPPED
) )
2157 if( lphc
->wState
& CBF_DROPPED
)
2158 CBRollUp( lphc
, FALSE
, TRUE
);
2162 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2164 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2166 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2168 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2170 /* no LBN_SELCHANGE in this case, update manually */
2171 if( lphc
->wState
& CBF_EDIT
)
2172 CBUpdateEdit( lphc
, (INT
)wParam
);
2174 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2175 lphc
->wState
&= ~CBF_SELCHANGE
;
2178 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2179 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2180 case CB_GETLBTEXTLEN
:
2181 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2182 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2183 case CB_GETITEMDATA
:
2184 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2185 case CB_SETITEMDATA
:
2186 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2188 /* Edit checks passed parameters itself */
2189 if( lphc
->wState
& CBF_EDIT
)
2190 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2193 if( lphc
->wState
& CBF_EDIT
)
2194 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2195 (INT
)(SHORT
)LOWORD(lParam
), (INT
)(SHORT
)HIWORD(lParam
) );
2197 case CB_SETEXTENDEDUI
:
2198 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2201 lphc
->wState
|= CBF_EUI
;
2202 else lphc
->wState
&= ~CBF_EUI
;
2204 case CB_GETEXTENDEDUI
:
2205 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2206 case CB_GETCOMBOBOXINFO
:
2207 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2209 if( lphc
->wState
& CBF_EDIT
)
2210 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2212 if (message
>= WM_USER
)
2213 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2214 message
- WM_USER
, wParam
, lParam
);
2217 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2218 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2221 /*************************************************************************
2222 * GetComboBoxInfo (USER32.@)
2224 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2225 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2227 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2228 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);