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
33 #include "wine/unicode.h"
34 #include "user_private.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(combo
);
42 /* bits in the dwKeyData */
43 #define KEYDATA_ALT 0x2000
44 #define KEYDATA_PREVSTATE 0x4000
47 * Additional combo box definitions
50 #define CB_NOTIFY( lphc, code ) \
51 (SendMessageW((lphc)->owner, WM_COMMAND, \
52 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
54 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
55 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
56 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
57 #define CB_HWND( lphc ) ((lphc)->self)
58 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
60 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
65 static HBITMAP hComboBmp
= 0;
66 static UINT CBitHeight
, CBitWidth
;
69 * Look and feel dependent "constants"
72 #define COMBO_YBORDERGAP 5
73 #define COMBO_XBORDERSIZE() 2
74 #define COMBO_YBORDERSIZE() 2
75 #define COMBO_EDITBUTTONSPACE() 0
76 #define EDIT_CONTROL_PADDING() 1
78 static void CBCalcPlacement(HEADCOMBO
*combo
);
79 static void CBResetPos(HEADCOMBO
*combo
, BOOL redraw
);
81 /*********************************************************************
82 * combo class descriptor
84 static const WCHAR comboboxW
[] = {'C','o','m','b','o','B','o','x',0};
85 const struct builtin_class_descr COMBO_builtin_class
=
88 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
89 WINPROC_COMBO
, /* proc */
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
);
133 /***********************************************************************
136 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
140 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
143 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)lphc
);
145 /* some braindead apps do try to use scrollbar/border flags */
147 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
148 SetWindowLongW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
151 * We also have to remove the client edge style to make sure
152 * we don't end-up with a non client area.
154 SetWindowLongW( hwnd
, GWL_EXSTYLE
,
155 GetWindowLongW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
157 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
158 lphc
->dwStyle
|= CBS_HASSTRINGS
;
159 if( !(GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
160 lphc
->wState
|= CBF_NOTIFY
;
162 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
168 /***********************************************************************
171 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
176 TRACE("[%p]: freeing storage\n", lphc
->self
);
178 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
179 DestroyWindow( lphc
->hWndLBox
);
181 SetWindowLongPtrW( lphc
->self
, 0, 0 );
182 HeapFree( GetProcessHeap(), 0, lphc
);
187 static INT
combo_get_text_height(const HEADCOMBO
*combo
)
189 HDC hdc
= GetDC(combo
->self
);
194 prev_font
= SelectObject(hdc
, combo
->hFont
);
196 GetTextMetricsW(hdc
, &tm
);
199 SelectObject(hdc
, prev_font
);
201 ReleaseDC(combo
->self
, hdc
);
203 return tm
.tmHeight
+ 4;
206 /***********************************************************************
207 * CBGetTextAreaHeight
209 * This method will calculate the height of the text area of the
211 * The height of the text area is set in two ways.
212 * It can be set explicitly through a combobox message or through a
213 * WM_MEASUREITEM callback.
214 * If this is not the case, the height is set to font height + 4px
215 * This height was determined through experimentation.
216 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
218 static INT
CBGetTextAreaHeight(HEADCOMBO
*lphc
, BOOL clip_item_height
)
220 INT item_height
, text_height
;
222 if (clip_item_height
&& !CB_OWNERDRAWN(lphc
))
224 text_height
= combo_get_text_height(lphc
);
225 if (lphc
->item_height
< text_height
)
226 lphc
->item_height
= text_height
;
229 item_height
= lphc
->item_height
;
232 * Check the ownerdraw case if we haven't asked the parent the size
235 if ( CB_OWNERDRAWN(lphc
) &&
236 (lphc
->wState
& CBF_MEASUREITEM
) )
238 MEASUREITEMSTRUCT measureItem
;
240 INT originalItemHeight
= item_height
;
241 UINT id
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
244 * We use the client rect for the width of the item.
246 GetClientRect(lphc
->self
, &clientRect
);
248 lphc
->wState
&= ~CBF_MEASUREITEM
;
251 * Send a first one to measure the size of the text area
253 measureItem
.CtlType
= ODT_COMBOBOX
;
254 measureItem
.CtlID
= id
;
255 measureItem
.itemID
= -1;
256 measureItem
.itemWidth
= clientRect
.right
;
257 measureItem
.itemHeight
= item_height
- 6; /* ownerdrawn cb is taller */
258 measureItem
.itemData
= 0;
259 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
260 item_height
= 6 + measureItem
.itemHeight
;
263 * Send a second one in the case of a fixed ownerdraw list to calculate the
264 * size of the list items. (we basically do this on behalf of the listbox)
266 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
268 measureItem
.CtlType
= ODT_COMBOBOX
;
269 measureItem
.CtlID
= id
;
270 measureItem
.itemID
= 0;
271 measureItem
.itemWidth
= clientRect
.right
;
272 measureItem
.itemHeight
= originalItemHeight
;
273 measureItem
.itemData
= 0;
274 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
275 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
279 * Keep the size for the next time
281 lphc
->item_height
= item_height
;
287 /***********************************************************************
290 * The dummy resize is used for listboxes that have a popup to trigger
291 * a re-arranging of the contents of the combobox and the recalculation
292 * of the size of the "real" control window.
294 static void CBForceDummyResize(LPHEADCOMBO lphc
)
299 newComboHeight
= CBGetTextAreaHeight(lphc
, FALSE
) + 2*COMBO_YBORDERSIZE();
301 GetWindowRect(lphc
->self
, &windowRect
);
304 * We have to be careful, resizing a combobox also has the meaning that the
305 * dropped rect will be resized. In this case, we want to trigger a resize
306 * to recalculate layout but we don't want to change the dropped rectangle
307 * So, we pass the height of text area of control as the height.
308 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
311 lphc
->wState
|= CBF_NORESIZE
;
312 SetWindowPos( lphc
->self
,
315 windowRect
.right
- windowRect
.left
,
317 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
318 lphc
->wState
&= ~CBF_NORESIZE
;
320 CBCalcPlacement(lphc
);
321 CBResetPos(lphc
, FALSE
);
324 /***********************************************************************
327 * Set up component coordinates given valid lphc->RectCombo.
329 static void CBCalcPlacement(HEADCOMBO
*combo
)
331 /* Start with the client rectangle. */
332 GetClientRect(combo
->self
, &combo
->textRect
);
334 /* Remove the borders */
335 InflateRect(&combo
->textRect
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
337 /* Chop off the bottom part to fit with the height of the text area. */
338 combo
->textRect
.bottom
= combo
->textRect
.top
+ CBGetTextAreaHeight(combo
, FALSE
);
340 /* The button starts the same vertical position as the text area. */
341 combo
->buttonRect
= combo
->textRect
;
343 /* If the combobox is "simple" there is no button. */
344 if (CB_GETTYPE(combo
) == CBS_SIMPLE
)
345 combo
->buttonRect
.left
= combo
->buttonRect
.right
= combo
->buttonRect
.bottom
= 0;
349 * Let's assume the combobox button is the same width as the
351 * size the button horizontally and cut-off the text area.
353 combo
->buttonRect
.left
= combo
->buttonRect
.right
- GetSystemMetrics(SM_CXVSCROLL
);
354 combo
->textRect
.right
= combo
->buttonRect
.left
;
357 /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
358 if (CB_GETTYPE(combo
) == CBS_DROPDOWN
)
359 combo
->textRect
.right
-= COMBO_EDITBUTTONSPACE();
361 /* If we have an edit control, we space it away from the borders slightly. */
362 if (CB_GETTYPE(combo
) != CBS_DROPDOWNLIST
)
363 InflateRect(&combo
->textRect
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
365 /* Adjust the size of the listbox popup. */
366 if (CB_GETTYPE(combo
) == CBS_SIMPLE
)
368 GetClientRect(combo
->self
, &combo
->droppedRect
);
369 combo
->droppedRect
.top
= combo
->textRect
.bottom
+ COMBO_YBORDERSIZE();
373 /* Make sure the dropped width is as large as the combobox itself. */
374 if (combo
->droppedWidth
< (combo
->buttonRect
.right
+ COMBO_XBORDERSIZE()))
376 combo
->droppedRect
.right
= combo
->droppedRect
.left
+ (combo
->buttonRect
.right
+ COMBO_XBORDERSIZE());
378 /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
379 with the right side of the combobox. */
380 if (CB_GETTYPE(combo
) == CBS_DROPDOWN
)
381 combo
->droppedRect
.right
-= COMBO_EDITBUTTONSPACE();
384 combo
->droppedRect
.right
= combo
->droppedRect
.left
+ combo
->droppedWidth
;
387 /* Disallow negative window width */
388 if (combo
->textRect
.right
< combo
->textRect
.left
)
389 combo
->textRect
.right
= combo
->textRect
.left
;
391 TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo
->textRect
), wine_dbgstr_rect(&combo
->buttonRect
),
392 wine_dbgstr_rect(&combo
->droppedRect
));
395 /***********************************************************************
396 * CBGetDroppedControlRect
398 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
400 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
401 of the combo box and the lower right corner of the listbox */
403 GetWindowRect(lphc
->self
, lpRect
);
405 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
406 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
410 /***********************************************************************
413 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
416 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
417 static const WCHAR editName
[] = {'E','d','i','t',0};
419 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
420 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
422 lphc
->owner
= hwndParent
;
424 lphc
->droppedWidth
= 0;
426 lphc
->item_height
= combo_get_text_height(lphc
);
429 * The first time we go through, we want to measure the ownerdraw item
431 lphc
->wState
|= CBF_MEASUREITEM
;
433 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
435 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
441 * Initialize the dropped rect to the size of the client area of the
442 * control and then, force all the areas of the combobox to be
445 GetClientRect( hwnd
, &lphc
->droppedRect
);
446 CBCalcPlacement(lphc
);
449 * Adjust the position of the popup listbox if it's necessary
451 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
453 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
456 * If it's a dropdown, the listbox is offset
458 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
459 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
461 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
462 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
463 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
464 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
465 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
468 /* create listbox popup */
470 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
471 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
473 if( lphc
->dwStyle
& CBS_SORT
)
474 lbeStyle
|= LBS_SORT
;
475 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
476 lbeStyle
|= LBS_HASSTRINGS
;
477 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
478 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
479 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
480 lbeStyle
|= LBS_DISABLENOSCROLL
;
482 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
484 lbeStyle
|= WS_VISIBLE
;
487 * In win 95 look n feel, the listbox in the simple combobox has
488 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
490 lbeStyle
&= ~WS_BORDER
;
491 lbeExStyle
|= WS_EX_CLIENTEDGE
;
495 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
499 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
500 lphc
->droppedRect
.left
,
501 lphc
->droppedRect
.top
,
502 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
503 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
504 hwnd
, (HMENU
)ID_CB_LISTBOX
,
505 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
507 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
508 lphc
->droppedRect
.left
,
509 lphc
->droppedRect
.top
,
510 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
511 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
512 hwnd
, (HMENU
)ID_CB_LISTBOX
,
513 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
518 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
520 if( lphc
->wState
& CBF_EDIT
)
522 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
523 lbeStyle
|= ES_OEMCONVERT
;
524 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
525 lbeStyle
|= ES_AUTOHSCROLL
;
526 if( lphc
->dwStyle
& CBS_LOWERCASE
)
527 lbeStyle
|= ES_LOWERCASE
;
528 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
529 lbeStyle
|= ES_UPPERCASE
;
531 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
534 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
535 lphc
->textRect
.left
, lphc
->textRect
.top
,
536 lphc
->textRect
.right
- lphc
->textRect
.left
,
537 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
538 hwnd
, (HMENU
)ID_CB_EDIT
,
539 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
541 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
542 lphc
->textRect
.left
, lphc
->textRect
.top
,
543 lphc
->textRect
.right
- lphc
->textRect
.left
,
544 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
545 hwnd
, (HMENU
)ID_CB_EDIT
,
546 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
548 if( !lphc
->hWndEdit
)
554 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
556 /* Now do the trick with parent */
557 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
559 * If the combo is a dropdown, we must resize the control
560 * to fit only the text area and button. To do this,
561 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
562 * will take care of setting the height for us.
564 CBForceDummyResize(lphc
);
567 TRACE("init done\n");
570 ERR("edit control failure.\n");
571 } else ERR("listbox failure.\n");
572 } else ERR("no owner for visible combo.\n");
574 /* CreateWindow() will send WM_NCDESTROY to cleanup */
579 /***********************************************************************
582 * Paint combo button (normal, pressed, and disabled states).
584 static void CBPaintButton(HEADCOMBO
*lphc
, HDC hdc
)
586 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
588 if (IsRectEmpty(&lphc
->buttonRect
))
591 if( lphc
->wState
& CBF_NOREDRAW
)
595 if (lphc
->wState
& CBF_BUTTONDOWN
)
596 buttonState
|= DFCS_PUSHED
;
598 if (CB_DISABLED(lphc
))
599 buttonState
|= DFCS_INACTIVE
;
601 DrawFrameControl(hdc
, &lphc
->buttonRect
, DFC_SCROLL
, buttonState
);
604 /***********************************************************************
605 * COMBO_PrepareColors
607 * This method will sent the appropriate WM_CTLCOLOR message to
608 * prepare and setup the colors for the combo's DC.
610 * It also returns the brush to use for the background.
612 static HBRUSH
COMBO_PrepareColors(
619 * Get the background brush for this control.
621 if (CB_DISABLED(lphc
))
623 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
624 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
627 * We have to change the text color since WM_CTLCOLORSTATIC will
628 * set it to the "enabled" color. This is the same behavior as the
631 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
635 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
636 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
637 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
644 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
649 /***********************************************************************
652 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
654 static void CBPaintText(
658 RECT rectEdit
= lphc
->textRect
;
664 /* follow Windows combobox that sends a bunch of text
665 * inquiries to its listbox while processing WM_PAINT. */
667 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
669 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
671 FIXME("LB_ERR probably not handled yet\n");
672 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
674 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
675 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, id
, (LPARAM
)pText
);
676 pText
[size
] = '\0'; /* just in case */
680 if( lphc
->wState
& CBF_EDIT
)
682 static const WCHAR empty_stringW
[] = { 0 };
683 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
684 if( lphc
->wState
& CBF_FOCUSED
)
685 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, MAXLONG
);
687 else if(!(lphc
->wState
& CBF_NOREDRAW
) && IsWindowVisible( lphc
->self
))
689 /* paint text field ourselves */
690 HDC hdc
= hdc_paint
? hdc_paint
: GetDC(lphc
->self
);
691 UINT itemState
= ODS_COMBOBOXEDIT
;
692 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
693 HBRUSH hPrevBrush
, hBkgBrush
;
696 * Give ourselves some space.
698 InflateRect( &rectEdit
, -1, -1 );
700 hBkgBrush
= COMBO_PrepareColors( lphc
, hdc
);
701 hPrevBrush
= SelectObject( hdc
, hBkgBrush
);
702 FillRect( hdc
, &rectEdit
, hBkgBrush
);
704 if( CB_OWNERDRAWN(lphc
) )
708 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
710 /* setup state for DRAWITEM message. Owner will highlight */
711 if ( (lphc
->wState
& CBF_FOCUSED
) &&
712 !(lphc
->wState
& CBF_DROPPED
) )
713 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
715 if (!IsWindowEnabled(lphc
->self
)) itemState
|= ODS_DISABLED
;
717 dis
.CtlType
= ODT_COMBOBOX
;
719 dis
.hwndItem
= lphc
->self
;
720 dis
.itemAction
= ODA_DRAWENTIRE
;
722 dis
.itemState
= itemState
;
724 dis
.rcItem
= rectEdit
;
725 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, id
, 0);
728 * Clip the DC and have the parent draw the item.
730 clipRegion
= set_control_clipping( hdc
, &rectEdit
);
732 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
734 SelectClipRgn( hdc
, clipRegion
);
735 if (clipRegion
) DeleteObject( clipRegion
);
739 static const WCHAR empty_stringW
[] = { 0 };
741 if ( (lphc
->wState
& CBF_FOCUSED
) &&
742 !(lphc
->wState
& CBF_DROPPED
) ) {
745 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
746 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
747 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
753 ETO_OPAQUE
| ETO_CLIPPED
,
755 pText
? pText
: empty_stringW
, size
, NULL
);
757 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
758 DrawFocusRect( hdc
, &rectEdit
);
762 SelectObject(hdc
, hPrevFont
);
765 SelectObject( hdc
, hPrevBrush
);
768 ReleaseDC( lphc
->self
, hdc
);
770 HeapFree( GetProcessHeap(), 0, pText
);
773 /***********************************************************************
776 static void CBPaintBorder(const HEADCOMBO
*lphc
, HDC hdc
)
780 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
782 GetClientRect(lphc
->self
, &clientRect
);
786 clientRect
= lphc
->textRect
;
788 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
789 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
792 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
795 /***********************************************************************
798 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
803 hDC
= (hParamDC
) ? hParamDC
804 : BeginPaint( lphc
->self
, &ps
);
806 TRACE("hdc=%p\n", hDC
);
808 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
810 HBRUSH hPrevBrush
, hBkgBrush
;
813 * Retrieve the background brush and select it in the
816 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
818 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
819 if (!(lphc
->wState
& CBF_EDIT
))
820 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
823 * In non 3.1 look, there is a sunken border on the combobox
825 CBPaintBorder(lphc
, hDC
);
826 CBPaintButton(lphc
, hDC
);
828 /* paint the edit control padding area */
829 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
831 RECT rPadEdit
= lphc
->textRect
;
833 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
835 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
838 if( !(lphc
->wState
& CBF_EDIT
) )
839 CBPaintText( lphc
, hDC
);
842 SelectObject( hDC
, hPrevBrush
);
846 EndPaint(lphc
->self
, &ps
);
851 /***********************************************************************
854 * Select listbox entry according to the contents of the edit control.
856 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
862 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
865 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
867 TRACE("\t edit text length %i\n", length
);
871 GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
872 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, -1, (LPARAM
)pText
);
873 HeapFree( GetProcessHeap(), 0, pText
);
876 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, bSelect
? idx
: -1, 0);
878 /* probably superfluous but Windows sends this too */
879 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, idx
< 0 ? 0 : idx
, 0);
880 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, idx
< 0 ? 0 : idx
, 0);
885 /***********************************************************************
888 * Copy a listbox entry to the edit control.
890 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
894 static const WCHAR empty_stringW
[] = { 0 };
896 TRACE("\t %i\n", index
);
898 if( index
>= 0 ) /* got an entry */
900 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, index
, 0);
901 if( length
!= LB_ERR
)
903 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
905 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, index
, (LPARAM
)pText
);
910 if( CB_HASSTRINGS(lphc
) )
912 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
913 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
914 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
917 if( lphc
->wState
& CBF_FOCUSED
)
918 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
920 HeapFree( GetProcessHeap(), 0, pText
);
923 /***********************************************************************
926 * Show listbox popup.
928 static void CBDropDown( LPHEADCOMBO lphc
)
931 MONITORINFO mon_info
;
936 TRACE("[%p]: drop down\n", lphc
->self
);
938 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
942 lphc
->wState
|= CBF_DROPPED
;
943 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
945 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
947 /* Update edit only if item is in the list */
948 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
949 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
953 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
955 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
956 lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
, 0);
957 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
960 /* now set popup position */
961 GetWindowRect( lphc
->self
, &rect
);
964 * If it's a dropdown, the listbox is offset
966 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
967 rect
.left
+= COMBO_EDITBUTTONSPACE();
969 /* if the dropped height is greater than the total height of the dropped
970 items list, then force the drop down list height to be the total height
971 of the items in the dropped list */
973 /* And Remove any extra space (Best Fit) */
974 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
975 /* if listbox length has been set directly by its handle */
976 GetWindowRect(lphc
->hWndLBox
, &r
);
977 if (nDroppedHeight
< r
.bottom
- r
.top
)
978 nDroppedHeight
= r
.bottom
- r
.top
;
979 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
986 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
988 nHeight
= nIHeight
*nItems
;
990 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
991 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
996 r
.right
= r
.left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
997 r
.bottom
= r
.top
+ nDroppedHeight
;
999 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1000 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
1001 mon_info
.cbSize
= sizeof(mon_info
);
1002 GetMonitorInfoW( monitor
, &mon_info
);
1004 if (r
.bottom
> mon_info
.rcWork
.bottom
)
1006 r
.top
= max( rect
.top
- nDroppedHeight
, mon_info
.rcWork
.top
);
1007 r
.bottom
= min( r
.top
+ nDroppedHeight
, mon_info
.rcWork
.bottom
);
1010 SetWindowPos( lphc
->hWndLBox
, HWND_TOPMOST
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
1011 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1014 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1015 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1016 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1018 EnableWindow( lphc
->hWndLBox
, TRUE
);
1019 if (GetCapture() != lphc
->self
)
1020 SetCapture(lphc
->hWndLBox
);
1023 /***********************************************************************
1026 * Hide listbox popup.
1028 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1030 HWND hWnd
= lphc
->self
;
1032 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1033 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1035 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1037 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1040 if( lphc
->wState
& CBF_DROPPED
)
1044 lphc
->wState
&= ~CBF_DROPPED
;
1045 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1047 if(GetCapture() == lphc
->hWndLBox
)
1052 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1054 rect
= lphc
->buttonRect
;
1065 rect
= lphc
->textRect
;
1070 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1071 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1072 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1073 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1078 /***********************************************************************
1081 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1083 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1085 if( lphc
->wState
& CBF_DROPPED
)
1087 CBRollUp( lphc
, ok
, bRedrawButton
);
1095 /***********************************************************************
1098 static void CBRepaintButton( LPHEADCOMBO lphc
)
1100 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1101 UpdateWindow(lphc
->self
);
1104 /***********************************************************************
1107 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1109 if( !(lphc
->wState
& CBF_FOCUSED
) )
1111 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1112 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1114 /* This is wrong. Message sequences seem to indicate that this
1115 is set *after* the notify. */
1116 /* lphc->wState |= CBF_FOCUSED; */
1118 if( !(lphc
->wState
& CBF_EDIT
) )
1119 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1121 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1122 lphc
->wState
|= CBF_FOCUSED
;
1126 /***********************************************************************
1129 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1131 HWND hWnd
= lphc
->self
;
1133 if( lphc
->wState
& CBF_FOCUSED
)
1135 CBRollUp( lphc
, FALSE
, TRUE
);
1136 if( IsWindow( hWnd
) )
1138 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1139 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1141 lphc
->wState
&= ~CBF_FOCUSED
;
1144 if( !(lphc
->wState
& CBF_EDIT
) )
1145 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1147 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1152 /***********************************************************************
1155 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1157 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1159 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1161 switch( HIWORD(wParam
) >> 8 )
1163 case (EN_SETFOCUS
>> 8):
1165 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1167 COMBO_SetFocus( lphc
);
1170 case (EN_KILLFOCUS
>> 8):
1172 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1174 /* NOTE: it seems that Windows' edit control sends an
1175 * undocumented message WM_USER + 0x1B instead of this
1176 * notification (only when it happens to be a part of
1177 * the combo). ?? - AK.
1180 COMBO_KillFocus( lphc
);
1184 case (EN_CHANGE
>> 8):
1186 * In some circumstances (when the selection of the combobox
1187 * is changed for example) we don't want the EN_CHANGE notification
1188 * to be forwarded to the parent of the combobox. This code
1189 * checks a flag that is set in these occasions and ignores the
1192 if (lphc
->wState
& CBF_NOLBSELECT
)
1194 lphc
->wState
&= ~CBF_NOLBSELECT
;
1198 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1201 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1202 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1205 case (EN_UPDATE
>> 8):
1206 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1207 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1210 case (EN_ERRSPACE
>> 8):
1211 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1214 else if( lphc
->hWndLBox
== hWnd
)
1216 switch( (short)HIWORD(wParam
) )
1219 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1223 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1229 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1231 /* do not roll up if selection is being tracked
1232 * by arrow keys in the dropdown listbox */
1233 if (!(lphc
->wState
& CBF_NOROLLUP
))
1235 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1237 else lphc
->wState
&= ~CBF_NOROLLUP
;
1239 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1241 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1243 if( lphc
->wState
& CBF_EDIT
)
1244 lphc
->wState
|= CBF_NOLBSELECT
;
1245 CBPaintText( lphc
, NULL
);
1251 /* nothing to do here since ComboLBox always resets the focus to its
1252 * combo/edit counterpart */
1259 /***********************************************************************
1262 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1264 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1266 HWND hWnd
= lphc
->self
;
1267 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1269 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1275 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1276 lpIS
->CtlType
= ODT_COMBOBOX
;
1278 lpIS
->hwndItem
= hWnd
;
1283 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1284 lpIS
->CtlType
= ODT_COMBOBOX
;
1286 lpIS
->hwndItem
= hWnd
;
1289 case WM_COMPAREITEM
:
1291 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1292 lpIS
->CtlType
= ODT_COMBOBOX
;
1294 lpIS
->hwndItem
= hWnd
;
1297 case WM_MEASUREITEM
:
1299 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1300 lpIS
->CtlType
= ODT_COMBOBOX
;
1305 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1309 /***********************************************************************
1312 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1316 if( lphc
->wState
& CBF_EDIT
)
1317 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1319 /* get it from the listbox */
1321 if (!count
|| !buf
) return 0;
1322 if( lphc
->hWndLBox
)
1324 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1325 if (idx
== LB_ERR
) goto error
;
1326 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1327 if (length
== LB_ERR
) goto error
;
1329 /* 'length' is without the terminating character */
1330 if (length
>= count
)
1332 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1333 if (!lpBuffer
) goto error
;
1334 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1336 /* truncate if buffer is too short */
1337 if (length
!= LB_ERR
)
1339 lstrcpynW( buf
, lpBuffer
, count
);
1342 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1344 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1346 if (length
== LB_ERR
) return 0;
1350 error
: /* error - truncate string, return zero */
1356 /***********************************************************************
1359 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1360 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1362 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1366 if( lphc
->wState
& CBF_EDIT
)
1367 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1369 /* get it from the listbox */
1371 if (!count
|| !buf
) return 0;
1372 if( lphc
->hWndLBox
)
1374 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1375 if (idx
== LB_ERR
) goto error
;
1376 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1377 if (length
== LB_ERR
) goto error
;
1379 /* 'length' is without the terminating character */
1380 if (length
>= count
)
1382 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1383 if (!lpBuffer
) goto error
;
1384 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1386 /* truncate if buffer is too short */
1387 if (length
!= LB_ERR
)
1389 lstrcpynA( buf
, lpBuffer
, count
);
1392 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1394 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1396 if (length
== LB_ERR
) return 0;
1400 error
: /* error - truncate string, return zero */
1406 /***********************************************************************
1409 * This function sets window positions according to the updated
1410 * component placement struct.
1412 static void CBResetPos(HEADCOMBO
*combo
, BOOL redraw
)
1414 BOOL drop
= CB_GETTYPE(combo
) != CBS_SIMPLE
;
1416 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1417 * sizing messages */
1418 if (combo
->wState
& CBF_EDIT
)
1419 SetWindowPos(combo
->hWndEdit
, 0, combo
->textRect
.left
, combo
->textRect
.top
,
1420 combo
->textRect
.right
- combo
->textRect
.left
,
1421 combo
->textRect
.bottom
- combo
->textRect
.top
,
1422 SWP_NOZORDER
| SWP_NOACTIVATE
| (drop
? SWP_NOREDRAW
: 0));
1424 SetWindowPos(combo
->hWndLBox
, 0, combo
->droppedRect
.left
, combo
->droppedRect
.top
,
1425 combo
->droppedRect
.right
- combo
->droppedRect
.left
,
1426 combo
->droppedRect
.bottom
- combo
->droppedRect
.top
,
1427 SWP_NOACTIVATE
| SWP_NOZORDER
| (drop
? SWP_NOREDRAW
: 0));
1431 if (combo
->wState
& CBF_DROPPED
)
1433 combo
->wState
&= ~CBF_DROPPED
;
1434 ShowWindow(combo
->hWndLBox
, SW_HIDE
);
1437 if (redraw
&& !(combo
->wState
& CBF_NOREDRAW
))
1438 RedrawWindow(combo
->self
, NULL
, 0, RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1442 /***********************************************************************
1445 static void COMBO_Size( HEADCOMBO
*lphc
)
1447 if (!lphc
->hWndLBox
|| (lphc
->wState
& CBF_NORESIZE
))
1451 * Those controls are always the same height. So we have to make sure
1452 * they are not resized to another value.
1454 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1456 int newComboHeight
, curComboHeight
, curComboWidth
;
1459 GetWindowRect(lphc
->self
, &rc
);
1460 curComboHeight
= rc
.bottom
- rc
.top
;
1461 curComboWidth
= rc
.right
- rc
.left
;
1462 newComboHeight
= CBGetTextAreaHeight(lphc
, TRUE
) + 2*COMBO_YBORDERSIZE();
1465 * Resizing a combobox has another side effect, it resizes the dropped
1466 * rectangle as well. However, it does it only if the new height for the
1467 * combobox is more than the height it should have. In other words,
1468 * if the application resizing the combobox only had the intention to resize
1469 * the actual control, for example, to do the layout of a dialog that is
1470 * resized, the height of the dropdown is not changed.
1472 if( curComboHeight
> newComboHeight
)
1474 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1475 curComboHeight
, newComboHeight
, lphc
->droppedRect
.bottom
,
1476 lphc
->droppedRect
.top
);
1477 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ curComboHeight
- newComboHeight
;
1480 * Restore original height
1482 if (curComboHeight
!= newComboHeight
)
1484 lphc
->wState
|= CBF_NORESIZE
;
1485 SetWindowPos(lphc
->self
, 0, 0, 0, curComboWidth
, newComboHeight
,
1486 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
1487 lphc
->wState
&= ~CBF_NORESIZE
;
1491 CBCalcPlacement(lphc
);
1493 CBResetPos(lphc
, FALSE
);
1497 /***********************************************************************
1500 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1502 lphc
->hFont
= hFont
;
1503 lphc
->item_height
= combo_get_text_height(lphc
);
1506 * Propagate to owned windows.
1508 if( lphc
->wState
& CBF_EDIT
)
1509 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1510 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1513 * Redo the layout of the control.
1515 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1517 CBCalcPlacement(lphc
);
1519 CBResetPos(lphc
, TRUE
);
1523 CBForceDummyResize(lphc
);
1528 /***********************************************************************
1529 * COMBO_SetItemHeight
1531 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1533 LRESULT lRet
= CB_ERR
;
1535 if( index
== -1 ) /* set text field height */
1537 if( height
< 32768 )
1539 lphc
->item_height
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1542 * Redo the layout of the control.
1544 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1546 CBCalcPlacement(lphc
);
1548 CBResetPos(lphc
, TRUE
);
1552 CBForceDummyResize(lphc
);
1558 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1559 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
, index
, height
);
1563 /***********************************************************************
1564 * COMBO_SelectString
1566 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1568 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, start
, pText
) :
1569 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, start
, pText
);
1572 if( lphc
->wState
& CBF_EDIT
)
1573 CBUpdateEdit( lphc
, index
);
1576 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1579 return (LRESULT
)index
;
1582 /***********************************************************************
1585 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1589 HWND hWnd
= lphc
->self
;
1591 pt
.x
= (short)LOWORD(lParam
);
1592 pt
.y
= (short)HIWORD(lParam
);
1593 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1595 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1596 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1598 lphc
->wState
|= CBF_BUTTONDOWN
;
1599 if( lphc
->wState
& CBF_DROPPED
)
1601 /* got a click to cancel selection */
1603 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1604 CBRollUp( lphc
, TRUE
, FALSE
);
1605 if( !IsWindow( hWnd
) ) return;
1607 if( lphc
->wState
& CBF_CAPTURE
)
1609 lphc
->wState
&= ~CBF_CAPTURE
;
1615 /* drop down the listbox and start tracking */
1617 lphc
->wState
|= CBF_CAPTURE
;
1621 if( bButton
) CBRepaintButton( lphc
);
1625 /***********************************************************************
1628 * Release capture and stop tracking if needed.
1630 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1632 if( lphc
->wState
& CBF_CAPTURE
)
1634 lphc
->wState
&= ~CBF_CAPTURE
;
1635 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1637 INT index
= CBUpdateLBox( lphc
, TRUE
);
1638 /* Update edit only if item is in the list */
1641 lphc
->wState
|= CBF_NOLBSELECT
;
1642 CBUpdateEdit( lphc
, index
);
1643 lphc
->wState
&= ~CBF_NOLBSELECT
;
1647 SetCapture(lphc
->hWndLBox
);
1650 if( lphc
->wState
& CBF_BUTTONDOWN
)
1652 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1653 CBRepaintButton( lphc
);
1657 /***********************************************************************
1660 * Two things to do - track combo button and release capture when
1661 * pointer goes into the listbox.
1663 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1668 pt
.x
= (short)LOWORD(lParam
);
1669 pt
.y
= (short)HIWORD(lParam
);
1671 if( lphc
->wState
& CBF_BUTTONDOWN
)
1675 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1679 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1680 CBRepaintButton( lphc
);
1684 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1685 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1686 if( PtInRect(&lbRect
, pt
) )
1688 lphc
->wState
&= ~CBF_CAPTURE
;
1690 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1692 /* hand over pointer tracking */
1693 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1697 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1699 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1702 pcbi
->rcItem
= lphc
->textRect
;
1703 pcbi
->rcButton
= lphc
->buttonRect
;
1704 pcbi
->stateButton
= 0;
1705 if (lphc
->wState
& CBF_BUTTONDOWN
)
1706 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1707 if (IsRectEmpty(&lphc
->buttonRect
))
1708 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1709 pcbi
->hwndCombo
= lphc
->self
;
1710 pcbi
->hwndItem
= lphc
->hWndEdit
;
1711 pcbi
->hwndList
= lphc
->hWndLBox
;
1715 static char *strdupA(LPCSTR str
)
1720 if(!str
) return NULL
;
1723 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1724 memcpy(ret
, str
, len
+ 1);
1728 /***********************************************************************
1729 * ComboWndProc_common
1731 LRESULT
ComboWndProc_common( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1733 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1735 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1736 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1738 if (!IsWindow(hwnd
)) return 0;
1740 if( lphc
|| message
== WM_NCCREATE
)
1744 /* System messages */
1748 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1749 ((LPCREATESTRUCTA
)lParam
)->style
;
1750 return COMBO_NCCreate(hwnd
, style
);
1753 COMBO_NCDestroy(lphc
);
1754 break;/* -> DefWindowProc */
1762 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1763 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1767 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1768 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1770 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1773 case WM_PRINTCLIENT
:
1776 /* wParam may contain a valid HDC! */
1777 return COMBO_Paint(lphc
, (HDC
)wParam
);
1780 /* do all painting in WM_PAINT like Windows does */
1785 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1786 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1788 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1790 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1791 result
|= DLGC_WANTMESSAGE
;
1799 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1802 return (LRESULT
)lphc
->hFont
;
1804 if( lphc
->wState
& CBF_EDIT
) {
1805 SetFocus( lphc
->hWndEdit
);
1806 /* The first time focus is received, select all the text */
1807 if( !(lphc
->wState
& CBF_BEENFOCUSED
) ) {
1808 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
1809 lphc
->wState
|= CBF_BEENFOCUSED
;
1813 COMBO_SetFocus( lphc
);
1817 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1819 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1820 COMBO_KillFocus( lphc
);
1824 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1826 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1827 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1829 case WM_GETTEXTLENGTH
:
1831 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1833 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1834 if (j
== -1) return 0;
1835 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1836 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1838 else if( lphc
->wState
& CBF_EDIT
)
1841 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1842 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1843 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1844 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1851 if( lphc
->wState
& CBF_EDIT
)
1853 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1854 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1860 case WM_COMPAREITEM
:
1861 case WM_MEASUREITEM
:
1862 return COMBO_ItemOp(lphc
, message
, lParam
);
1864 if( lphc
->wState
& CBF_EDIT
)
1865 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1866 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1868 /* Force the control to repaint when the enabled state changes. */
1869 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1873 lphc
->wState
&= ~CBF_NOREDRAW
;
1875 lphc
->wState
|= CBF_NOREDRAW
;
1877 if( lphc
->wState
& CBF_EDIT
)
1878 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1879 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
1882 if( KEYDATA_ALT
& HIWORD(lParam
) )
1883 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1884 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1888 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
1889 (lphc
->wState
& CBF_DROPPED
))
1891 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
1894 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
1896 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1905 if( lphc
->wState
& CBF_EDIT
)
1906 hwndTarget
= lphc
->hWndEdit
;
1908 hwndTarget
= lphc
->hWndLBox
;
1910 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
1911 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
1913 case WM_LBUTTONDOWN
:
1914 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
1915 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1918 COMBO_LButtonUp( lphc
);
1921 if( lphc
->wState
& CBF_CAPTURE
)
1922 COMBO_MouseMove( lphc
, wParam
, lParam
);
1926 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
1927 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
1928 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
1930 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
1931 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
1935 case WM_CTLCOLORMSGBOX
:
1936 case WM_CTLCOLOREDIT
:
1937 case WM_CTLCOLORLISTBOX
:
1938 case WM_CTLCOLORBTN
:
1939 case WM_CTLCOLORDLG
:
1940 case WM_CTLCOLORSCROLLBAR
:
1941 case WM_CTLCOLORSTATIC
:
1943 return SendMessageW(lphc
->owner
, message
, wParam
, lParam
);
1946 /* Combo messages */
1951 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1952 CharLowerW((LPWSTR
)lParam
);
1953 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1954 CharUpperW((LPWSTR
)lParam
);
1955 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1957 else /* unlike the unicode version, the ansi version does not overwrite
1958 the string if converting case */
1960 char *string
= NULL
;
1962 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1964 string
= strdupA((LPSTR
)lParam
);
1968 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1970 string
= strdupA((LPSTR
)lParam
);
1974 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
1975 HeapFree(GetProcessHeap(), 0, string
);
1978 case CB_INSERTSTRING
:
1981 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1982 CharLowerW((LPWSTR
)lParam
);
1983 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1984 CharUpperW((LPWSTR
)lParam
);
1985 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1989 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1990 CharLowerA((LPSTR
)lParam
);
1991 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1992 CharUpperA((LPSTR
)lParam
);
1994 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1996 case CB_DELETESTRING
:
1997 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
1998 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1999 case CB_SELECTSTRING
:
2000 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2002 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2003 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2004 case CB_FINDSTRINGEXACT
:
2005 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2006 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2007 case CB_SETITEMHEIGHT
:
2008 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2009 case CB_GETITEMHEIGHT
:
2010 if( (INT
)wParam
>= 0 ) /* listbox item */
2011 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2012 return CBGetTextAreaHeight(lphc
, FALSE
);
2013 case CB_RESETCONTENT
:
2014 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2015 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2017 static const WCHAR empty_stringW
[] = { 0 };
2018 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2021 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2023 case CB_INITSTORAGE
:
2024 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2025 case CB_GETHORIZONTALEXTENT
:
2026 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2027 case CB_SETHORIZONTALEXTENT
:
2028 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2029 case CB_GETTOPINDEX
:
2030 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2032 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2034 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2035 case CB_SETDROPPEDWIDTH
:
2036 if( (CB_GETTYPE(lphc
) == CBS_SIMPLE
) ||
2037 (INT
)wParam
>= 32768 )
2039 /* new value must be higher than combobox width */
2040 if((INT
)wParam
>= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
)
2041 lphc
->droppedWidth
= wParam
;
2043 lphc
->droppedWidth
= 0;
2045 /* recalculate the combobox area */
2046 CBCalcPlacement(lphc
);
2049 case CB_GETDROPPEDWIDTH
:
2050 if( lphc
->droppedWidth
)
2051 return lphc
->droppedWidth
;
2052 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2053 case CB_GETDROPPEDCONTROLRECT
:
2054 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2056 case CB_GETDROPPEDSTATE
:
2057 return (lphc
->wState
& CBF_DROPPED
) != 0;
2059 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2060 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2062 case CB_SHOWDROPDOWN
:
2063 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2067 if( !(lphc
->wState
& CBF_DROPPED
) )
2071 if( lphc
->wState
& CBF_DROPPED
)
2072 CBRollUp( lphc
, FALSE
, TRUE
);
2076 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2078 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2080 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2082 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2084 /* no LBN_SELCHANGE in this case, update manually */
2085 CBPaintText( lphc
, NULL
);
2086 lphc
->wState
&= ~CBF_SELCHANGE
;
2089 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2090 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2091 case CB_GETLBTEXTLEN
:
2092 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2093 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2094 case CB_GETITEMDATA
:
2095 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2096 case CB_SETITEMDATA
:
2097 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2099 /* Edit checks passed parameters itself */
2100 if( lphc
->wState
& CBF_EDIT
)
2101 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2104 if( lphc
->wState
& CBF_EDIT
)
2105 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2106 (INT
)(SHORT
)LOWORD(lParam
), (INT
)(SHORT
)HIWORD(lParam
) );
2108 case CB_SETEXTENDEDUI
:
2109 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2112 lphc
->wState
|= CBF_EUI
;
2113 else lphc
->wState
&= ~CBF_EUI
;
2115 case CB_GETEXTENDEDUI
:
2116 return (lphc
->wState
& CBF_EUI
) != 0;
2117 case CB_GETCOMBOBOXINFO
:
2118 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2120 if( lphc
->wState
& CBF_EDIT
)
2121 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2124 if (message
>= WM_USER
)
2125 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2126 message
- WM_USER
, wParam
, lParam
);
2129 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2130 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2133 /*************************************************************************
2134 * GetComboBoxInfo (USER32.@)
2136 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2137 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2139 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2140 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);