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
32 #include "user_private.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(combo
);
40 /* bits in the dwKeyData */
41 #define KEYDATA_ALT 0x2000
42 #define KEYDATA_PREVSTATE 0x4000
45 * Additional combo box definitions
48 #define CB_NOTIFY( lphc, code ) \
49 (SendMessageW((lphc)->owner, WM_COMMAND, \
50 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
52 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
53 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
54 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
55 #define CB_HWND( lphc ) ((lphc)->self)
56 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
58 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
63 static HBITMAP hComboBmp
= 0;
64 static UINT CBitHeight
, CBitWidth
;
67 * Look and feel dependent "constants"
70 #define COMBO_YBORDERGAP 5
71 #define COMBO_XBORDERSIZE() 2
72 #define COMBO_YBORDERSIZE() 2
73 #define COMBO_EDITBUTTONSPACE() 0
74 #define EDIT_CONTROL_PADDING() 1
76 static void CBCalcPlacement(HEADCOMBO
*combo
);
77 static void CBResetPos(HEADCOMBO
*combo
, BOOL redraw
);
79 /*********************************************************************
80 * combo class descriptor
82 const struct builtin_class_descr COMBO_builtin_class
=
84 L
"ComboBox", /* name */
85 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
86 WINPROC_COMBO
, /* proc */
87 sizeof(HEADCOMBO
*), /* extra */
88 IDC_ARROW
, /* cursor */
93 /***********************************************************************
96 * Load combo button bitmap.
98 static BOOL
COMBO_Init(void)
102 if( hComboBmp
) return TRUE
;
103 if( (hDC
= CreateCompatibleDC(0)) )
106 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
112 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
113 CBitHeight
= bm
.bmHeight
;
114 CBitWidth
= bm
.bmWidth
;
116 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
118 hPrevB
= SelectObject( hDC
, hComboBmp
);
119 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
120 InvertRect( hDC
, &r
);
121 SelectObject( hDC
, hPrevB
);
130 /***********************************************************************
133 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
137 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
140 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)lphc
);
142 /* some braindead apps do try to use scrollbar/border flags */
144 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
145 SetWindowLongW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
148 * We also have to remove the client edge style to make sure
149 * we don't end-up with a non client area.
151 SetWindowLongW( hwnd
, GWL_EXSTYLE
,
152 GetWindowLongW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
154 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
155 lphc
->dwStyle
|= CBS_HASSTRINGS
;
156 if( !(GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
157 lphc
->wState
|= CBF_NOTIFY
;
159 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
165 /***********************************************************************
168 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
173 TRACE("[%p]: freeing storage\n", lphc
->self
);
175 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
176 NtUserDestroyWindow( lphc
->hWndLBox
);
178 SetWindowLongPtrW( lphc
->self
, 0, 0 );
179 HeapFree( GetProcessHeap(), 0, lphc
);
184 static INT
combo_get_text_height(const HEADCOMBO
*combo
)
186 HDC hdc
= GetDC(combo
->self
);
191 prev_font
= SelectObject(hdc
, combo
->hFont
);
193 GetTextMetricsW(hdc
, &tm
);
196 SelectObject(hdc
, prev_font
);
198 NtUserReleaseDC( combo
->self
, hdc
);
200 return tm
.tmHeight
+ 4;
203 /***********************************************************************
204 * CBGetTextAreaHeight
206 * This method will calculate the height of the text area of the
208 * The height of the text area is set in two ways.
209 * It can be set explicitly through a combobox message or through a
210 * WM_MEASUREITEM callback.
211 * If this is not the case, the height is set to font height + 4px
212 * This height was determined through experimentation.
213 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
215 static INT
CBGetTextAreaHeight(HEADCOMBO
*lphc
, BOOL clip_item_height
)
217 INT item_height
, text_height
;
219 if (clip_item_height
&& !CB_OWNERDRAWN(lphc
))
221 text_height
= combo_get_text_height(lphc
);
222 if (lphc
->item_height
< text_height
)
223 lphc
->item_height
= text_height
;
226 item_height
= lphc
->item_height
;
229 * Check the ownerdraw case if we haven't asked the parent the size
232 if ( CB_OWNERDRAWN(lphc
) &&
233 (lphc
->wState
& CBF_MEASUREITEM
) )
235 MEASUREITEMSTRUCT measureItem
;
237 INT originalItemHeight
= item_height
;
238 UINT id
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
241 * We use the client rect for the width of the item.
243 GetClientRect(lphc
->self
, &clientRect
);
245 lphc
->wState
&= ~CBF_MEASUREITEM
;
248 * Send a first one to measure the size of the text area
250 measureItem
.CtlType
= ODT_COMBOBOX
;
251 measureItem
.CtlID
= id
;
252 measureItem
.itemID
= -1;
253 measureItem
.itemWidth
= clientRect
.right
;
254 measureItem
.itemHeight
= item_height
- 6; /* ownerdrawn cb is taller */
255 measureItem
.itemData
= 0;
256 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
257 item_height
= 6 + measureItem
.itemHeight
;
260 * Send a second one in the case of a fixed ownerdraw list to calculate the
261 * size of the list items. (we basically do this on behalf of the listbox)
263 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
265 measureItem
.CtlType
= ODT_COMBOBOX
;
266 measureItem
.CtlID
= id
;
267 measureItem
.itemID
= 0;
268 measureItem
.itemWidth
= clientRect
.right
;
269 measureItem
.itemHeight
= originalItemHeight
;
270 measureItem
.itemData
= 0;
271 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
272 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
276 * Keep the size for the next time
278 lphc
->item_height
= item_height
;
284 /***********************************************************************
287 * The dummy resize is used for listboxes that have a popup to trigger
288 * a re-arranging of the contents of the combobox and the recalculation
289 * of the size of the "real" control window.
291 static void CBForceDummyResize(LPHEADCOMBO lphc
)
296 newComboHeight
= CBGetTextAreaHeight(lphc
, FALSE
) + 2*COMBO_YBORDERSIZE();
298 GetWindowRect(lphc
->self
, &windowRect
);
301 * We have to be careful, resizing a combobox also has the meaning that the
302 * dropped rect will be resized. In this case, we want to trigger a resize
303 * to recalculate layout but we don't want to change the dropped rectangle
304 * So, we pass the height of text area of control as the height.
305 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
308 lphc
->wState
|= CBF_NORESIZE
;
309 NtUserSetWindowPos( lphc
->self
,
312 windowRect
.right
- windowRect
.left
,
314 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
315 lphc
->wState
&= ~CBF_NORESIZE
;
317 CBCalcPlacement(lphc
);
318 CBResetPos(lphc
, FALSE
);
321 /***********************************************************************
324 * Set up component coordinates given valid lphc->RectCombo.
326 static void CBCalcPlacement(HEADCOMBO
*combo
)
328 /* Start with the client rectangle. */
329 GetClientRect(combo
->self
, &combo
->textRect
);
331 /* Remove the borders */
332 InflateRect(&combo
->textRect
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
334 /* Chop off the bottom part to fit with the height of the text area. */
335 combo
->textRect
.bottom
= combo
->textRect
.top
+ CBGetTextAreaHeight(combo
, FALSE
);
337 /* The button starts the same vertical position as the text area. */
338 combo
->buttonRect
= combo
->textRect
;
340 /* If the combobox is "simple" there is no button. */
341 if (CB_GETTYPE(combo
) == CBS_SIMPLE
)
342 combo
->buttonRect
.left
= combo
->buttonRect
.right
= combo
->buttonRect
.bottom
= 0;
346 * Let's assume the combobox button is the same width as the
348 * size the button horizontally and cut-off the text area.
350 combo
->buttonRect
.left
= combo
->buttonRect
.right
- GetSystemMetrics(SM_CXVSCROLL
);
351 combo
->textRect
.right
= combo
->buttonRect
.left
;
354 /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
355 if (CB_GETTYPE(combo
) == CBS_DROPDOWN
)
356 combo
->textRect
.right
-= COMBO_EDITBUTTONSPACE();
358 /* If we have an edit control, we space it away from the borders slightly. */
359 if (CB_GETTYPE(combo
) != CBS_DROPDOWNLIST
)
360 InflateRect(&combo
->textRect
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
362 /* Adjust the size of the listbox popup. */
363 if (CB_GETTYPE(combo
) == CBS_SIMPLE
)
365 GetClientRect(combo
->self
, &combo
->droppedRect
);
366 combo
->droppedRect
.top
= combo
->textRect
.bottom
+ COMBO_YBORDERSIZE();
370 /* Make sure the dropped width is as large as the combobox itself. */
371 if (combo
->droppedWidth
< (combo
->buttonRect
.right
+ COMBO_XBORDERSIZE()))
373 combo
->droppedRect
.right
= combo
->droppedRect
.left
+ (combo
->buttonRect
.right
+ COMBO_XBORDERSIZE());
375 /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
376 with the right side of the combobox. */
377 if (CB_GETTYPE(combo
) == CBS_DROPDOWN
)
378 combo
->droppedRect
.right
-= COMBO_EDITBUTTONSPACE();
381 combo
->droppedRect
.right
= combo
->droppedRect
.left
+ combo
->droppedWidth
;
384 /* Disallow negative window width */
385 if (combo
->textRect
.right
< combo
->textRect
.left
)
386 combo
->textRect
.right
= combo
->textRect
.left
;
388 TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo
->textRect
), wine_dbgstr_rect(&combo
->buttonRect
),
389 wine_dbgstr_rect(&combo
->droppedRect
));
392 /***********************************************************************
393 * CBGetDroppedControlRect
395 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
397 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
398 of the combo box and the lower right corner of the listbox */
400 GetWindowRect(lphc
->self
, lpRect
);
402 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
403 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
407 /***********************************************************************
410 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
413 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
414 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
416 lphc
->owner
= hwndParent
;
418 lphc
->droppedWidth
= 0;
420 lphc
->item_height
= combo_get_text_height(lphc
);
423 * The first time we go through, we want to measure the ownerdraw item
425 lphc
->wState
|= CBF_MEASUREITEM
;
427 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
429 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
435 * Initialize the dropped rect to the size of the client area of the
436 * control and then, force all the areas of the combobox to be
439 GetClientRect( hwnd
, &lphc
->droppedRect
);
440 CBCalcPlacement(lphc
);
443 * Adjust the position of the popup listbox if it's necessary
445 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
447 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
450 * If it's a dropdown, the listbox is offset
452 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
453 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
455 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
456 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
457 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
458 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
459 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
462 /* create listbox popup */
464 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
465 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
467 if( lphc
->dwStyle
& CBS_SORT
)
468 lbeStyle
|= LBS_SORT
;
469 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
470 lbeStyle
|= LBS_HASSTRINGS
;
471 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
472 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
473 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
474 lbeStyle
|= LBS_DISABLENOSCROLL
;
476 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
478 lbeStyle
|= WS_VISIBLE
;
481 * In win 95 look n feel, the listbox in the simple combobox has
482 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
484 lbeStyle
&= ~WS_BORDER
;
485 lbeExStyle
|= WS_EX_CLIENTEDGE
;
489 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
493 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, L
"ComboLBox", NULL
, lbeStyle
,
494 lphc
->droppedRect
.left
,
495 lphc
->droppedRect
.top
,
496 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
497 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
498 hwnd
, (HMENU
)ID_CB_LISTBOX
,
499 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
501 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
502 lphc
->droppedRect
.left
,
503 lphc
->droppedRect
.top
,
504 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
505 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
506 hwnd
, (HMENU
)ID_CB_LISTBOX
,
507 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
512 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
514 if( lphc
->wState
& CBF_EDIT
)
516 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
517 lbeStyle
|= ES_OEMCONVERT
;
518 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
519 lbeStyle
|= ES_AUTOHSCROLL
;
520 if( lphc
->dwStyle
& CBS_LOWERCASE
)
521 lbeStyle
|= ES_LOWERCASE
;
522 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
523 lbeStyle
|= ES_UPPERCASE
;
525 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
528 lphc
->hWndEdit
= CreateWindowExW(0, L
"Edit", NULL
, lbeStyle
,
529 lphc
->textRect
.left
, lphc
->textRect
.top
,
530 lphc
->textRect
.right
- lphc
->textRect
.left
,
531 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
532 hwnd
, (HMENU
)ID_CB_EDIT
,
533 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
535 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
536 lphc
->textRect
.left
, lphc
->textRect
.top
,
537 lphc
->textRect
.right
- lphc
->textRect
.left
,
538 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
539 hwnd
, (HMENU
)ID_CB_EDIT
,
540 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
542 if( !lphc
->hWndEdit
)
548 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
550 /* Now do the trick with parent */
551 NtUserSetParent( lphc
->hWndLBox
, HWND_DESKTOP
);
553 * If the combo is a dropdown, we must resize the control
554 * to fit only the text area and button. To do this,
555 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
556 * will take care of setting the height for us.
558 CBForceDummyResize(lphc
);
561 TRACE("init done\n");
564 ERR("edit control failure.\n");
565 } else ERR("listbox failure.\n");
566 } else ERR("no owner for visible combo.\n");
568 /* CreateWindow() will send WM_NCDESTROY to cleanup */
573 /***********************************************************************
576 * Paint combo button (normal, pressed, and disabled states).
578 static void CBPaintButton(HEADCOMBO
*lphc
, HDC hdc
)
580 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
582 if (IsRectEmpty(&lphc
->buttonRect
))
585 if( lphc
->wState
& CBF_NOREDRAW
)
589 if (lphc
->wState
& CBF_BUTTONDOWN
)
590 buttonState
|= DFCS_PUSHED
;
592 if (CB_DISABLED(lphc
))
593 buttonState
|= DFCS_INACTIVE
;
595 DrawFrameControl(hdc
, &lphc
->buttonRect
, DFC_SCROLL
, buttonState
);
598 /***********************************************************************
599 * COMBO_PrepareColors
601 * This method will sent the appropriate WM_CTLCOLOR message to
602 * prepare and setup the colors for the combo's DC.
604 * It also returns the brush to use for the background.
606 static HBRUSH
COMBO_PrepareColors(
613 * Get the background brush for this control.
615 if (CB_DISABLED(lphc
))
617 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
618 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
621 * We have to change the text color since WM_CTLCOLORSTATIC will
622 * set it to the "enabled" color. This is the same behavior as the
625 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
629 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
630 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
631 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
638 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
643 /***********************************************************************
646 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
648 static void CBPaintText(
652 RECT rectEdit
= lphc
->textRect
;
658 /* follow Windows combobox that sends a bunch of text
659 * inquiries to its listbox while processing WM_PAINT. */
661 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
663 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
665 FIXME("LB_ERR probably not handled yet\n");
666 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
668 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
669 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, id
, (LPARAM
)pText
);
670 pText
[size
] = '\0'; /* just in case */
674 if( lphc
->wState
& CBF_EDIT
)
676 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: L
"" );
677 if( lphc
->wState
& CBF_FOCUSED
)
678 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, MAXLONG
);
680 else if(!(lphc
->wState
& CBF_NOREDRAW
) && IsWindowVisible( lphc
->self
))
682 /* paint text field ourselves */
683 HDC hdc
= hdc_paint
? hdc_paint
: GetDC(lphc
->self
);
684 UINT itemState
= ODS_COMBOBOXEDIT
;
685 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
686 HBRUSH hPrevBrush
, hBkgBrush
;
689 * Give ourselves some space.
691 InflateRect( &rectEdit
, -1, -1 );
693 hBkgBrush
= COMBO_PrepareColors( lphc
, hdc
);
694 hPrevBrush
= SelectObject( hdc
, hBkgBrush
);
695 FillRect( hdc
, &rectEdit
, hBkgBrush
);
697 if( CB_OWNERDRAWN(lphc
) )
701 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
703 /* setup state for DRAWITEM message. Owner will highlight */
704 if ( (lphc
->wState
& CBF_FOCUSED
) &&
705 !(lphc
->wState
& CBF_DROPPED
) )
706 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
708 if (!IsWindowEnabled(lphc
->self
)) itemState
|= ODS_DISABLED
;
710 dis
.CtlType
= ODT_COMBOBOX
;
712 dis
.hwndItem
= lphc
->self
;
713 dis
.itemAction
= ODA_DRAWENTIRE
;
715 dis
.itemState
= itemState
;
717 dis
.rcItem
= rectEdit
;
718 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, id
, 0);
721 * Clip the DC and have the parent draw the item.
723 clipRegion
= set_control_clipping( hdc
, &rectEdit
);
725 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
727 SelectClipRgn( hdc
, clipRegion
);
728 if (clipRegion
) DeleteObject( clipRegion
);
732 if ( (lphc
->wState
& CBF_FOCUSED
) &&
733 !(lphc
->wState
& CBF_DROPPED
) ) {
736 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
737 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
738 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
744 ETO_OPAQUE
| ETO_CLIPPED
,
746 pText
? pText
: L
"" , size
, NULL
);
748 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
749 DrawFocusRect( hdc
, &rectEdit
);
753 SelectObject(hdc
, hPrevFont
);
756 SelectObject( hdc
, hPrevBrush
);
759 NtUserReleaseDC( lphc
->self
, hdc
);
761 HeapFree( GetProcessHeap(), 0, pText
);
764 /***********************************************************************
767 static void CBPaintBorder(const HEADCOMBO
*lphc
, HDC hdc
)
771 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
773 GetClientRect(lphc
->self
, &clientRect
);
777 clientRect
= lphc
->textRect
;
779 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
780 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
783 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
786 /***********************************************************************
789 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
794 hDC
= hParamDC
? hParamDC
: NtUserBeginPaint( lphc
->self
, &ps
);
796 TRACE("hdc=%p\n", hDC
);
798 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
800 HBRUSH hPrevBrush
, hBkgBrush
;
803 * Retrieve the background brush and select it in the
806 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
808 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
809 if (!(lphc
->wState
& CBF_EDIT
))
810 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
813 * In non 3.1 look, there is a sunken border on the combobox
815 CBPaintBorder(lphc
, hDC
);
816 CBPaintButton(lphc
, hDC
);
818 /* paint the edit control padding area */
819 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
821 RECT rPadEdit
= lphc
->textRect
;
823 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
825 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
828 if( !(lphc
->wState
& CBF_EDIT
) )
829 CBPaintText( lphc
, hDC
);
832 SelectObject( hDC
, hPrevBrush
);
836 NtUserEndPaint( lphc
->self
, &ps
);
841 /***********************************************************************
844 * Select listbox entry according to the contents of the edit control.
846 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
852 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
855 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
857 TRACE("\t edit text length %i\n", length
);
861 GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
862 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, -1, (LPARAM
)pText
);
863 HeapFree( GetProcessHeap(), 0, pText
);
866 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, bSelect
? idx
: -1, 0);
868 /* probably superfluous but Windows sends this too */
869 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, idx
< 0 ? 0 : idx
, 0);
870 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, idx
< 0 ? 0 : idx
, 0);
875 /***********************************************************************
878 * Copy a listbox entry to the edit control.
880 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
885 TRACE("\t %i\n", index
);
887 if( index
>= 0 ) /* got an entry */
889 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, index
, 0);
890 if( length
!= LB_ERR
)
892 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
894 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, index
, (LPARAM
)pText
);
899 if( CB_HASSTRINGS(lphc
) )
901 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
902 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)L
"");
903 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
906 if( lphc
->wState
& CBF_FOCUSED
)
907 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
909 HeapFree( GetProcessHeap(), 0, pText
);
912 /***********************************************************************
915 * Show listbox popup.
917 static void CBDropDown( LPHEADCOMBO lphc
)
920 MONITORINFO mon_info
;
925 TRACE("[%p]: drop down\n", lphc
->self
);
927 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
931 lphc
->wState
|= CBF_DROPPED
;
932 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
934 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
936 /* Update edit only if item is in the list */
937 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
938 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
942 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
944 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
945 lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
, 0);
946 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
949 /* now set popup position */
950 GetWindowRect( lphc
->self
, &rect
);
953 * If it's a dropdown, the listbox is offset
955 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
956 rect
.left
+= COMBO_EDITBUTTONSPACE();
958 /* if the dropped height is greater than the total height of the dropped
959 items list, then force the drop down list height to be the total height
960 of the items in the dropped list */
962 /* And Remove any extra space (Best Fit) */
963 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
964 /* if listbox length has been set directly by its handle */
965 GetWindowRect(lphc
->hWndLBox
, &r
);
966 if (nDroppedHeight
< r
.bottom
- r
.top
)
967 nDroppedHeight
= r
.bottom
- r
.top
;
968 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
975 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
977 nHeight
= nIHeight
*nItems
;
979 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
980 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
985 r
.right
= r
.left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
986 r
.bottom
= r
.top
+ nDroppedHeight
;
988 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
989 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
990 mon_info
.cbSize
= sizeof(mon_info
);
991 GetMonitorInfoW( monitor
, &mon_info
);
993 if (r
.bottom
> mon_info
.rcWork
.bottom
)
995 r
.top
= max( rect
.top
- nDroppedHeight
, mon_info
.rcWork
.top
);
996 r
.bottom
= min( r
.top
+ nDroppedHeight
, mon_info
.rcWork
.bottom
);
999 NtUserSetWindowPos( lphc
->hWndLBox
, HWND_TOPMOST
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
1000 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1003 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1004 NtUserRedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1006 EnableWindow( lphc
->hWndLBox
, TRUE
);
1007 if (GetCapture() != lphc
->self
)
1008 NtUserSetCapture(lphc
->hWndLBox
);
1011 /***********************************************************************
1014 * Hide listbox popup.
1016 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1018 HWND hWnd
= lphc
->self
;
1020 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1021 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1023 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1025 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1028 if( lphc
->wState
& CBF_DROPPED
)
1032 lphc
->wState
&= ~CBF_DROPPED
;
1033 NtUserShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1035 if(GetCapture() == lphc
->hWndLBox
)
1040 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1042 rect
= lphc
->buttonRect
;
1053 rect
= lphc
->textRect
;
1058 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1059 NtUserRedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1060 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1061 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1066 /***********************************************************************
1069 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1071 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1073 if( lphc
->wState
& CBF_DROPPED
)
1075 CBRollUp( lphc
, ok
, bRedrawButton
);
1083 /***********************************************************************
1086 static void CBRepaintButton( LPHEADCOMBO lphc
)
1088 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1089 UpdateWindow(lphc
->self
);
1092 /***********************************************************************
1095 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1097 if( !(lphc
->wState
& CBF_FOCUSED
) )
1099 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1100 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1102 /* This is wrong. Message sequences seem to indicate that this
1103 is set *after* the notify. */
1104 /* lphc->wState |= CBF_FOCUSED; */
1106 if( !(lphc
->wState
& CBF_EDIT
) )
1107 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1109 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1110 lphc
->wState
|= CBF_FOCUSED
;
1114 /***********************************************************************
1117 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1119 HWND hWnd
= lphc
->self
;
1121 if( lphc
->wState
& CBF_FOCUSED
)
1123 CBRollUp( lphc
, FALSE
, TRUE
);
1124 if( IsWindow( hWnd
) )
1126 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1127 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1129 lphc
->wState
&= ~CBF_FOCUSED
;
1132 if( !(lphc
->wState
& CBF_EDIT
) )
1133 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1135 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1140 /***********************************************************************
1143 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1145 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1147 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1149 switch( HIWORD(wParam
) >> 8 )
1151 case (EN_SETFOCUS
>> 8):
1153 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1155 COMBO_SetFocus( lphc
);
1158 case (EN_KILLFOCUS
>> 8):
1160 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1162 /* NOTE: it seems that Windows' edit control sends an
1163 * undocumented message WM_USER + 0x1B instead of this
1164 * notification (only when it happens to be a part of
1165 * the combo). ?? - AK.
1168 COMBO_KillFocus( lphc
);
1172 case (EN_CHANGE
>> 8):
1174 * In some circumstances (when the selection of the combobox
1175 * is changed for example) we don't want the EN_CHANGE notification
1176 * to be forwarded to the parent of the combobox. This code
1177 * checks a flag that is set in these occasions and ignores the
1180 if (lphc
->wState
& CBF_NOLBSELECT
)
1182 lphc
->wState
&= ~CBF_NOLBSELECT
;
1186 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1189 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1190 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1193 case (EN_UPDATE
>> 8):
1194 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1195 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1198 case (EN_ERRSPACE
>> 8):
1199 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1202 else if( lphc
->hWndLBox
== hWnd
)
1204 switch( (short)HIWORD(wParam
) )
1207 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1211 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1217 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1219 /* do not roll up if selection is being tracked
1220 * by arrow keys in the dropdown listbox */
1221 if (!(lphc
->wState
& CBF_NOROLLUP
))
1223 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1225 else lphc
->wState
&= ~CBF_NOROLLUP
;
1227 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1229 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1231 if( lphc
->wState
& CBF_EDIT
)
1232 lphc
->wState
|= CBF_NOLBSELECT
;
1233 CBPaintText( lphc
, NULL
);
1239 /* nothing to do here since ComboLBox always resets the focus to its
1240 * combo/edit counterpart */
1247 /***********************************************************************
1250 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1252 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1254 HWND hWnd
= lphc
->self
;
1255 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1257 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1263 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1264 lpIS
->CtlType
= ODT_COMBOBOX
;
1266 lpIS
->hwndItem
= hWnd
;
1271 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1272 lpIS
->CtlType
= ODT_COMBOBOX
;
1274 lpIS
->hwndItem
= hWnd
;
1277 case WM_COMPAREITEM
:
1279 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1280 lpIS
->CtlType
= ODT_COMBOBOX
;
1282 lpIS
->hwndItem
= hWnd
;
1285 case WM_MEASUREITEM
:
1287 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1288 lpIS
->CtlType
= ODT_COMBOBOX
;
1293 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1297 /***********************************************************************
1300 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1304 if( lphc
->wState
& CBF_EDIT
)
1305 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1307 /* get it from the listbox */
1309 if (!count
|| !buf
) return 0;
1310 if( lphc
->hWndLBox
)
1312 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1313 if (idx
== LB_ERR
) goto error
;
1314 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1315 if (length
== LB_ERR
) goto error
;
1317 /* 'length' is without the terminating character */
1318 if (length
>= count
)
1320 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1321 if (!lpBuffer
) goto error
;
1322 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1324 /* truncate if buffer is too short */
1325 if (length
!= LB_ERR
)
1327 lstrcpynW( buf
, lpBuffer
, count
);
1330 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1332 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1334 if (length
== LB_ERR
) return 0;
1338 error
: /* error - truncate string, return zero */
1344 /***********************************************************************
1347 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1348 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1350 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1354 if( lphc
->wState
& CBF_EDIT
)
1355 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1357 /* get it from the listbox */
1359 if (!count
|| !buf
) return 0;
1360 if( lphc
->hWndLBox
)
1362 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1363 if (idx
== LB_ERR
) goto error
;
1364 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1365 if (length
== LB_ERR
) goto error
;
1367 /* 'length' is without the terminating character */
1368 if (length
>= count
)
1370 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1371 if (!lpBuffer
) goto error
;
1372 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1374 /* truncate if buffer is too short */
1375 if (length
!= LB_ERR
)
1377 lstrcpynA( buf
, lpBuffer
, count
);
1380 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1382 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1384 if (length
== LB_ERR
) return 0;
1388 error
: /* error - truncate string, return zero */
1394 /***********************************************************************
1397 * This function sets window positions according to the updated
1398 * component placement struct.
1400 static void CBResetPos(HEADCOMBO
*combo
, BOOL redraw
)
1402 BOOL drop
= CB_GETTYPE(combo
) != CBS_SIMPLE
;
1404 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1405 * sizing messages */
1406 if (combo
->wState
& CBF_EDIT
)
1407 NtUserSetWindowPos( combo
->hWndEdit
, 0, combo
->textRect
.left
, combo
->textRect
.top
,
1408 combo
->textRect
.right
- combo
->textRect
.left
,
1409 combo
->textRect
.bottom
- combo
->textRect
.top
,
1410 SWP_NOZORDER
| SWP_NOACTIVATE
| (drop
? SWP_NOREDRAW
: 0) );
1412 NtUserSetWindowPos( combo
->hWndLBox
, 0, combo
->droppedRect
.left
, combo
->droppedRect
.top
,
1413 combo
->droppedRect
.right
- combo
->droppedRect
.left
,
1414 combo
->droppedRect
.bottom
- combo
->droppedRect
.top
,
1415 SWP_NOACTIVATE
| SWP_NOZORDER
| (drop
? SWP_NOREDRAW
: 0) );
1419 if (combo
->wState
& CBF_DROPPED
)
1421 combo
->wState
&= ~CBF_DROPPED
;
1422 NtUserShowWindow( combo
->hWndLBox
, SW_HIDE
);
1425 if (redraw
&& !(combo
->wState
& CBF_NOREDRAW
))
1426 NtUserRedrawWindow( combo
->self
, NULL
, 0, RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1430 /***********************************************************************
1433 static void COMBO_Size( HEADCOMBO
*lphc
)
1435 if (!lphc
->hWndLBox
|| (lphc
->wState
& CBF_NORESIZE
))
1439 * Those controls are always the same height. So we have to make sure
1440 * they are not resized to another value.
1442 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1444 int newComboHeight
, curComboHeight
, curComboWidth
;
1447 GetWindowRect(lphc
->self
, &rc
);
1448 curComboHeight
= rc
.bottom
- rc
.top
;
1449 curComboWidth
= rc
.right
- rc
.left
;
1450 newComboHeight
= CBGetTextAreaHeight(lphc
, TRUE
) + 2*COMBO_YBORDERSIZE();
1453 * Resizing a combobox has another side effect, it resizes the dropped
1454 * rectangle as well. However, it does it only if the new height for the
1455 * combobox is more than the height it should have. In other words,
1456 * if the application resizing the combobox only had the intention to resize
1457 * the actual control, for example, to do the layout of a dialog that is
1458 * resized, the height of the dropdown is not changed.
1460 if( curComboHeight
> newComboHeight
)
1462 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%ld, oldDropTop=%ld\n",
1463 curComboHeight
, newComboHeight
, lphc
->droppedRect
.bottom
,
1464 lphc
->droppedRect
.top
);
1465 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ curComboHeight
- newComboHeight
;
1468 * Restore original height
1470 if (curComboHeight
!= newComboHeight
)
1472 lphc
->wState
|= CBF_NORESIZE
;
1473 NtUserSetWindowPos( lphc
->self
, 0, 0, 0, curComboWidth
, newComboHeight
,
1474 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOREDRAW
);
1475 lphc
->wState
&= ~CBF_NORESIZE
;
1479 CBCalcPlacement(lphc
);
1481 CBResetPos(lphc
, FALSE
);
1485 /***********************************************************************
1488 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1490 lphc
->hFont
= hFont
;
1491 lphc
->item_height
= combo_get_text_height(lphc
);
1494 * Propagate to owned windows.
1496 if( lphc
->wState
& CBF_EDIT
)
1497 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1498 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1501 * Redo the layout of the control.
1503 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1505 CBCalcPlacement(lphc
);
1507 CBResetPos(lphc
, TRUE
);
1511 CBForceDummyResize(lphc
);
1516 /***********************************************************************
1517 * COMBO_SetItemHeight
1519 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1521 LRESULT lRet
= CB_ERR
;
1523 if( index
== -1 ) /* set text field height */
1525 if( height
< 32768 )
1527 lphc
->item_height
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1530 * Redo the layout of the control.
1532 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1534 CBCalcPlacement(lphc
);
1536 CBResetPos(lphc
, TRUE
);
1540 CBForceDummyResize(lphc
);
1546 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1547 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
, index
, height
);
1551 /***********************************************************************
1552 * COMBO_SelectString
1554 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1556 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, start
, pText
) :
1557 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, start
, pText
);
1560 if( lphc
->wState
& CBF_EDIT
)
1561 CBUpdateEdit( lphc
, index
);
1564 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1567 return (LRESULT
)index
;
1570 /***********************************************************************
1573 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1577 HWND hWnd
= lphc
->self
;
1579 pt
.x
= (short)LOWORD(lParam
);
1580 pt
.y
= (short)HIWORD(lParam
);
1581 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1583 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1584 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1586 lphc
->wState
|= CBF_BUTTONDOWN
;
1587 if( lphc
->wState
& CBF_DROPPED
)
1589 /* got a click to cancel selection */
1591 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1592 CBRollUp( lphc
, TRUE
, FALSE
);
1593 if( !IsWindow( hWnd
) ) return;
1595 if( lphc
->wState
& CBF_CAPTURE
)
1597 lphc
->wState
&= ~CBF_CAPTURE
;
1603 /* drop down the listbox and start tracking */
1605 lphc
->wState
|= CBF_CAPTURE
;
1606 NtUserSetCapture( hWnd
);
1609 if( bButton
) CBRepaintButton( lphc
);
1613 /***********************************************************************
1616 * Release capture and stop tracking if needed.
1618 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1620 if( lphc
->wState
& CBF_CAPTURE
)
1622 lphc
->wState
&= ~CBF_CAPTURE
;
1623 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1625 INT index
= CBUpdateLBox( lphc
, TRUE
);
1626 /* Update edit only if item is in the list */
1629 lphc
->wState
|= CBF_NOLBSELECT
;
1630 CBUpdateEdit( lphc
, index
);
1631 lphc
->wState
&= ~CBF_NOLBSELECT
;
1635 NtUserSetCapture(lphc
->hWndLBox
);
1638 if( lphc
->wState
& CBF_BUTTONDOWN
)
1640 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1641 CBRepaintButton( lphc
);
1645 /***********************************************************************
1648 * Two things to do - track combo button and release capture when
1649 * pointer goes into the listbox.
1651 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1656 pt
.x
= (short)LOWORD(lParam
);
1657 pt
.y
= (short)HIWORD(lParam
);
1659 if( lphc
->wState
& CBF_BUTTONDOWN
)
1663 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1667 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1668 CBRepaintButton( lphc
);
1672 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1673 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1674 if( PtInRect(&lbRect
, pt
) )
1676 lphc
->wState
&= ~CBF_CAPTURE
;
1678 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1680 /* hand over pointer tracking */
1681 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1685 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1687 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1690 pcbi
->rcItem
= lphc
->textRect
;
1691 pcbi
->rcButton
= lphc
->buttonRect
;
1692 pcbi
->stateButton
= 0;
1693 if (lphc
->wState
& CBF_BUTTONDOWN
)
1694 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1695 if (IsRectEmpty(&lphc
->buttonRect
))
1696 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1697 pcbi
->hwndCombo
= lphc
->self
;
1698 pcbi
->hwndItem
= lphc
->hWndEdit
;
1699 pcbi
->hwndList
= lphc
->hWndLBox
;
1703 static char *strdupA(LPCSTR str
)
1708 if(!str
) return NULL
;
1711 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1712 memcpy(ret
, str
, len
+ 1);
1716 /***********************************************************************
1717 * ComboWndProc_common
1719 LRESULT
ComboWndProc_common( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1721 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1723 TRACE("[%p]: msg %s wp %08Ix lp %08Ix\n",
1724 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1726 if (!IsWindow(hwnd
)) return 0;
1728 if( lphc
|| message
== WM_NCCREATE
)
1732 /* System messages */
1736 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1737 ((LPCREATESTRUCTA
)lParam
)->style
;
1738 return COMBO_NCCreate(hwnd
, style
);
1741 COMBO_NCDestroy(lphc
);
1742 break;/* -> DefWindowProc */
1750 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1751 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1755 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1756 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1758 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1761 case WM_PRINTCLIENT
:
1764 /* wParam may contain a valid HDC! */
1765 return COMBO_Paint(lphc
, (HDC
)wParam
);
1768 /* do all painting in WM_PAINT like Windows does */
1773 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1774 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1776 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1778 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1779 result
|= DLGC_WANTMESSAGE
;
1787 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1790 return (LRESULT
)lphc
->hFont
;
1792 if( lphc
->wState
& CBF_EDIT
) {
1793 NtUserSetFocus( lphc
->hWndEdit
);
1794 /* The first time focus is received, select all the text */
1795 if( !(lphc
->wState
& CBF_BEENFOCUSED
) ) {
1796 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
1797 lphc
->wState
|= CBF_BEENFOCUSED
;
1801 COMBO_SetFocus( lphc
);
1805 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1807 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1808 COMBO_KillFocus( lphc
);
1812 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1814 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1815 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1817 case WM_GETTEXTLENGTH
:
1819 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1821 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1822 if (j
== -1) return 0;
1823 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1824 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1826 else if( lphc
->wState
& CBF_EDIT
)
1829 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1830 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1831 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1832 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1839 if( lphc
->wState
& CBF_EDIT
)
1841 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1842 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1848 case WM_COMPAREITEM
:
1849 case WM_MEASUREITEM
:
1850 return COMBO_ItemOp(lphc
, message
, lParam
);
1852 if( lphc
->wState
& CBF_EDIT
)
1853 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1854 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1856 /* Force the control to repaint when the enabled state changes. */
1857 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1861 lphc
->wState
&= ~CBF_NOREDRAW
;
1863 lphc
->wState
|= CBF_NOREDRAW
;
1865 if( lphc
->wState
& CBF_EDIT
)
1866 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1867 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
1870 if( KEYDATA_ALT
& HIWORD(lParam
) )
1871 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1872 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1876 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
1877 (lphc
->wState
& CBF_DROPPED
))
1879 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
1882 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
1884 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1893 if( lphc
->wState
& CBF_EDIT
)
1894 hwndTarget
= lphc
->hWndEdit
;
1896 hwndTarget
= lphc
->hWndLBox
;
1898 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
1899 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
1901 case WM_LBUTTONDOWN
:
1902 if (!(lphc
->wState
& CBF_FOCUSED
)) NtUserSetFocus( lphc
->self
);
1903 if (lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1906 COMBO_LButtonUp( lphc
);
1909 if( lphc
->wState
& CBF_CAPTURE
)
1910 COMBO_MouseMove( lphc
, wParam
, lParam
);
1914 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
1915 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
1916 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
1918 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
1919 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
1923 case WM_CTLCOLORMSGBOX
:
1924 case WM_CTLCOLOREDIT
:
1925 case WM_CTLCOLORLISTBOX
:
1926 case WM_CTLCOLORBTN
:
1927 case WM_CTLCOLORDLG
:
1928 case WM_CTLCOLORSCROLLBAR
:
1929 case WM_CTLCOLORSTATIC
:
1931 return SendMessageW(lphc
->owner
, message
, wParam
, lParam
);
1934 /* Combo messages */
1939 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1940 CharLowerW((LPWSTR
)lParam
);
1941 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1942 CharUpperW((LPWSTR
)lParam
);
1943 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1945 else /* unlike the unicode version, the ansi version does not overwrite
1946 the string if converting case */
1948 char *string
= NULL
;
1950 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1952 string
= strdupA((LPSTR
)lParam
);
1956 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1958 string
= strdupA((LPSTR
)lParam
);
1962 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
1963 HeapFree(GetProcessHeap(), 0, string
);
1966 case CB_INSERTSTRING
:
1969 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1970 CharLowerW((LPWSTR
)lParam
);
1971 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1972 CharUpperW((LPWSTR
)lParam
);
1973 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1977 if( lphc
->dwStyle
& CBS_LOWERCASE
)
1978 CharLowerA((LPSTR
)lParam
);
1979 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
1980 CharUpperA((LPSTR
)lParam
);
1982 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1984 case CB_DELETESTRING
:
1985 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
1986 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1987 case CB_SELECTSTRING
:
1988 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
1990 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
1991 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
1992 case CB_FINDSTRINGEXACT
:
1993 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
1994 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
1995 case CB_SETITEMHEIGHT
:
1996 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
1997 case CB_GETITEMHEIGHT
:
1998 if( (INT
)wParam
>= 0 ) /* listbox item */
1999 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2000 return CBGetTextAreaHeight(lphc
, FALSE
);
2001 case CB_RESETCONTENT
:
2002 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2003 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2004 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)L
"");
2006 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2008 case CB_INITSTORAGE
:
2009 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2010 case CB_GETHORIZONTALEXTENT
:
2011 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2012 case CB_SETHORIZONTALEXTENT
:
2013 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2014 case CB_GETTOPINDEX
:
2015 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2017 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2019 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2020 case CB_SETDROPPEDWIDTH
:
2021 if( (CB_GETTYPE(lphc
) == CBS_SIMPLE
) ||
2022 (INT
)wParam
>= 32768 )
2024 /* new value must be higher than combobox width */
2025 if((INT
)wParam
>= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
)
2026 lphc
->droppedWidth
= wParam
;
2028 lphc
->droppedWidth
= 0;
2030 /* recalculate the combobox area */
2031 CBCalcPlacement(lphc
);
2034 case CB_GETDROPPEDWIDTH
:
2035 if( lphc
->droppedWidth
)
2036 return lphc
->droppedWidth
;
2037 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2038 case CB_GETDROPPEDCONTROLRECT
:
2039 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2041 case CB_GETDROPPEDSTATE
:
2042 return (lphc
->wState
& CBF_DROPPED
) != 0;
2044 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2045 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2047 case CB_SHOWDROPDOWN
:
2048 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2052 if( !(lphc
->wState
& CBF_DROPPED
) )
2056 if( lphc
->wState
& CBF_DROPPED
)
2057 CBRollUp( lphc
, FALSE
, TRUE
);
2061 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2063 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2065 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2067 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2069 /* no LBN_SELCHANGE in this case, update manually */
2070 CBPaintText( lphc
, NULL
);
2071 lphc
->wState
&= ~CBF_SELCHANGE
;
2074 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2075 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2076 case CB_GETLBTEXTLEN
:
2077 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2078 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2079 case CB_GETITEMDATA
:
2080 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2081 case CB_SETITEMDATA
:
2082 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2084 /* Edit checks passed parameters itself */
2085 if( lphc
->wState
& CBF_EDIT
)
2086 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2089 if( lphc
->wState
& CBF_EDIT
)
2090 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2091 (INT
)(SHORT
)LOWORD(lParam
), (INT
)(SHORT
)HIWORD(lParam
) );
2093 case CB_SETEXTENDEDUI
:
2094 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2097 lphc
->wState
|= CBF_EUI
;
2098 else lphc
->wState
&= ~CBF_EUI
;
2100 case CB_GETEXTENDEDUI
:
2101 return (lphc
->wState
& CBF_EUI
) != 0;
2102 case CB_GETCOMBOBOXINFO
:
2103 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2105 if( lphc
->wState
& CBF_EDIT
)
2106 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2109 if (message
>= WM_USER
)
2110 WARN("unknown msg WM_USER+%04x wp=%04Ix lp=%08Ix\n",
2111 message
- WM_USER
, wParam
, lParam
);
2114 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2115 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2118 /*************************************************************************
2119 * GetComboBoxInfo (USER32.@)
2121 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2122 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2124 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2125 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);