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
21 * - ComboBox_[GS]etMinVisible()
22 * - CB_GETMINVISIBLE, CB_SETMINVISIBLE
35 #include "wine/unicode.h"
36 #include "user_private.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(combo
);
44 /* bits in the dwKeyData */
45 #define KEYDATA_ALT 0x2000
46 #define KEYDATA_PREVSTATE 0x4000
49 * Additional combo box definitions
52 #define CB_NOTIFY( lphc, code ) \
53 (SendMessageW((lphc)->owner, WM_COMMAND, \
54 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
56 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
57 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
58 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
59 #define CB_HWND( lphc ) ((lphc)->self)
60 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
62 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
67 static HBITMAP hComboBmp
= 0;
68 static UINT CBitHeight
, CBitWidth
;
71 * Look and feel dependent "constants"
74 #define COMBO_YBORDERGAP 5
75 #define COMBO_XBORDERSIZE() 2
76 #define COMBO_YBORDERSIZE() 2
77 #define COMBO_EDITBUTTONSPACE() 0
78 #define EDIT_CONTROL_PADDING() 1
80 /*********************************************************************
81 * combo class descriptor
83 static const WCHAR comboboxW
[] = {'C','o','m','b','o','B','o','x',0};
84 const struct builtin_class_descr COMBO_builtin_class
=
87 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
88 WINPROC_COMBO
, /* proc */
89 sizeof(HEADCOMBO
*), /* extra */
90 IDC_ARROW
, /* cursor */
95 /***********************************************************************
98 * Load combo button bitmap.
100 static BOOL
COMBO_Init(void)
104 if( hComboBmp
) return TRUE
;
105 if( (hDC
= CreateCompatibleDC(0)) )
108 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
114 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
115 CBitHeight
= bm
.bmHeight
;
116 CBitWidth
= bm
.bmWidth
;
118 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
120 hPrevB
= SelectObject( hDC
, hComboBmp
);
121 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
122 InvertRect( hDC
, &r
);
123 SelectObject( hDC
, hPrevB
);
132 /***********************************************************************
135 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
139 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
142 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)lphc
);
144 /* some braindead apps do try to use scrollbar/border flags */
146 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
147 SetWindowLongW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
150 * We also have to remove the client edge style to make sure
151 * we don't end-up with a non client area.
153 SetWindowLongW( hwnd
, GWL_EXSTYLE
,
154 GetWindowLongW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
156 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
157 lphc
->dwStyle
|= CBS_HASSTRINGS
;
158 if( !(GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
159 lphc
->wState
|= CBF_NOTIFY
;
161 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
167 /***********************************************************************
170 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
175 TRACE("[%p]: freeing storage\n", lphc
->self
);
177 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
178 DestroyWindow( lphc
->hWndLBox
);
180 SetWindowLongPtrW( lphc
->self
, 0, 0 );
181 HeapFree( GetProcessHeap(), 0, lphc
);
186 /***********************************************************************
187 * CBGetTextAreaHeight
189 * This method will calculate the height of the text area of the
191 * The height of the text area is set in two ways.
192 * It can be set explicitly through a combobox message or through a
193 * WM_MEASUREITEM callback.
194 * If this is not the case, the height is set to font height + 4px
195 * This height was determined through experimentation.
196 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
198 static INT
CBGetTextAreaHeight(
204 if( lphc
->editHeight
) /* explicitly set height */
206 iTextItemHeight
= lphc
->editHeight
;
211 HDC hDC
= GetDC(hwnd
);
216 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
218 GetTextMetricsW(hDC
, &tm
);
220 baseUnitY
= tm
.tmHeight
;
223 SelectObject( hDC
, hPrevFont
);
225 ReleaseDC(hwnd
, hDC
);
227 iTextItemHeight
= baseUnitY
+ 4;
231 * Check the ownerdraw case if we haven't asked the parent the size
234 if ( CB_OWNERDRAWN(lphc
) &&
235 (lphc
->wState
& CBF_MEASUREITEM
) )
237 MEASUREITEMSTRUCT measureItem
;
239 INT originalItemHeight
= iTextItemHeight
;
240 UINT id
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
243 * We use the client rect for the width of the item.
245 GetClientRect(hwnd
, &clientRect
);
247 lphc
->wState
&= ~CBF_MEASUREITEM
;
250 * Send a first one to measure the size of the text area
252 measureItem
.CtlType
= ODT_COMBOBOX
;
253 measureItem
.CtlID
= id
;
254 measureItem
.itemID
= -1;
255 measureItem
.itemWidth
= clientRect
.right
;
256 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
257 measureItem
.itemData
= 0;
258 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
259 iTextItemHeight
= 6 + measureItem
.itemHeight
;
262 * Send a second one in the case of a fixed ownerdraw list to calculate the
263 * size of the list items. (we basically do this on behalf of the listbox)
265 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
267 measureItem
.CtlType
= ODT_COMBOBOX
;
268 measureItem
.CtlID
= id
;
269 measureItem
.itemID
= 0;
270 measureItem
.itemWidth
= clientRect
.right
;
271 measureItem
.itemHeight
= originalItemHeight
;
272 measureItem
.itemData
= 0;
273 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
274 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
278 * Keep the size for the next time
280 lphc
->editHeight
= iTextItemHeight
;
283 return iTextItemHeight
;
286 /***********************************************************************
289 * The dummy resize is used for listboxes that have a popup to trigger
290 * a re-arranging of the contents of the combobox and the recalculation
291 * of the size of the "real" control window.
293 static void CBForceDummyResize(
299 newComboHeight
= CBGetTextAreaHeight(lphc
->self
,lphc
) + 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 SetWindowPos( lphc
->self
,
314 windowRect
.right
- windowRect
.left
,
316 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
319 /***********************************************************************
322 * Set up component coordinates given valid lphc->RectCombo.
324 static void CBCalcPlacement(
332 * Again, start with the client rectangle.
334 GetClientRect(hwnd
, lprEdit
);
339 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
342 * Chop off the bottom part to fit with the height of the text area.
344 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
347 * The button starts the same vertical position as the text area.
349 CopyRect(lprButton
, lprEdit
);
352 * If the combobox is "simple" there is no button.
354 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
355 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
359 * Let's assume the combobox button is the same width as the
361 * size the button horizontally and cut-off the text area.
363 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
364 lprEdit
->right
= lprButton
->left
;
368 * In the case of a dropdown, there is an additional spacing between the
369 * text area and the button.
371 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
373 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
377 * If we have an edit control, we space it away from the borders slightly.
379 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
381 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
385 * Adjust the size of the listbox popup.
387 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
390 * Use the client rectangle to initialize the listbox rectangle
392 GetClientRect(hwnd
, lprLB
);
395 * Then, chop-off the top part.
397 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
402 * Make sure the dropped width is as large as the combobox itself.
404 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
406 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
409 * In the case of a dropdown, the popup listbox is offset to the right.
410 * so, we want to make sure it's flush with the right side of the
413 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
414 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
417 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
420 /* don't allow negative window width */
421 if (lprEdit
->right
< lprEdit
->left
)
422 lprEdit
->right
= lprEdit
->left
;
424 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit
));
426 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton
));
428 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB
));
431 /***********************************************************************
432 * CBGetDroppedControlRect
434 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
436 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
437 of the combo box and the lower right corner of the listbox */
439 GetWindowRect(lphc
->self
, lpRect
);
441 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
442 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
446 /***********************************************************************
449 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
452 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
453 static const WCHAR editName
[] = {'E','d','i','t',0};
455 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
456 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
458 lphc
->owner
= hwndParent
;
461 * The item height and dropped width are not set when the control
464 lphc
->droppedWidth
= lphc
->editHeight
= 0;
467 * The first time we go through, we want to measure the ownerdraw item
469 lphc
->wState
|= CBF_MEASUREITEM
;
471 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
473 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
479 * Initialize the dropped rect to the size of the client area of the
480 * control and then, force all the areas of the combobox to be
483 GetClientRect( hwnd
, &lphc
->droppedRect
);
484 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
487 * Adjust the position of the popup listbox if it's necessary
489 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
491 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
494 * If it's a dropdown, the listbox is offset
496 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
497 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
499 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
500 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
501 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
502 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
503 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
506 /* create listbox popup */
508 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
509 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
511 if( lphc
->dwStyle
& CBS_SORT
)
512 lbeStyle
|= LBS_SORT
;
513 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
514 lbeStyle
|= LBS_HASSTRINGS
;
515 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
516 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
517 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
518 lbeStyle
|= LBS_DISABLENOSCROLL
;
520 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
522 lbeStyle
|= WS_VISIBLE
;
525 * In win 95 look n feel, the listbox in the simple combobox has
526 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
528 lbeStyle
&= ~WS_BORDER
;
529 lbeExStyle
|= WS_EX_CLIENTEDGE
;
533 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
537 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
538 lphc
->droppedRect
.left
,
539 lphc
->droppedRect
.top
,
540 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
541 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
542 hwnd
, (HMENU
)ID_CB_LISTBOX
,
543 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
545 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
546 lphc
->droppedRect
.left
,
547 lphc
->droppedRect
.top
,
548 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
549 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
550 hwnd
, (HMENU
)ID_CB_LISTBOX
,
551 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
556 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
558 if( lphc
->wState
& CBF_EDIT
)
560 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
561 lbeStyle
|= ES_OEMCONVERT
;
562 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
563 lbeStyle
|= ES_AUTOHSCROLL
;
564 if( lphc
->dwStyle
& CBS_LOWERCASE
)
565 lbeStyle
|= ES_LOWERCASE
;
566 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
567 lbeStyle
|= ES_UPPERCASE
;
569 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
572 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
573 lphc
->textRect
.left
, lphc
->textRect
.top
,
574 lphc
->textRect
.right
- lphc
->textRect
.left
,
575 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
576 hwnd
, (HMENU
)ID_CB_EDIT
,
577 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
579 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
580 lphc
->textRect
.left
, lphc
->textRect
.top
,
581 lphc
->textRect
.right
- lphc
->textRect
.left
,
582 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
583 hwnd
, (HMENU
)ID_CB_EDIT
,
584 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
586 if( !lphc
->hWndEdit
)
592 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
594 /* Now do the trick with parent */
595 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
597 * If the combo is a dropdown, we must resize the control
598 * to fit only the text area and button. To do this,
599 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
600 * will take care of setting the height for us.
602 CBForceDummyResize(lphc
);
605 TRACE("init done\n");
608 ERR("edit control failure.\n");
609 } else ERR("listbox failure.\n");
610 } else ERR("no owner for visible combo.\n");
612 /* CreateWindow() will send WM_NCDESTROY to cleanup */
617 /***********************************************************************
620 * Paint combo button (normal, pressed, and disabled states).
622 static void CBPaintButton( LPHEADCOMBO lphc
, HDC hdc
, RECT rectButton
)
624 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
626 if( lphc
->wState
& CBF_NOREDRAW
)
630 if (lphc
->wState
& CBF_BUTTONDOWN
)
631 buttonState
|= DFCS_PUSHED
;
633 if (CB_DISABLED(lphc
))
634 buttonState
|= DFCS_INACTIVE
;
636 DrawFrameControl(hdc
, &rectButton
, DFC_SCROLL
, buttonState
);
639 /***********************************************************************
640 * COMBO_PrepareColors
642 * This method will sent the appropriate WM_CTLCOLOR message to
643 * prepare and setup the colors for the combo's DC.
645 * It also returns the brush to use for the background.
647 static HBRUSH
COMBO_PrepareColors(
654 * Get the background brush for this control.
656 if (CB_DISABLED(lphc
))
658 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
659 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
662 * We have to change the text color since WM_CTLCOLORSTATIC will
663 * set it to the "enabled" color. This is the same behavior as the
666 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
670 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
671 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
672 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
679 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
684 /***********************************************************************
687 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
689 static void CBPaintText(
693 RECT rectEdit
= lphc
->textRect
;
699 /* follow Windows combobox that sends a bunch of text
700 * inquiries to its listbox while processing WM_PAINT. */
702 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
704 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
706 FIXME("LB_ERR probably not handled yet\n");
707 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
709 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
710 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, id
, (LPARAM
)pText
);
711 pText
[size
] = '\0'; /* just in case */
715 if( lphc
->wState
& CBF_EDIT
)
717 static const WCHAR empty_stringW
[] = { 0 };
718 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
719 if( lphc
->wState
& CBF_FOCUSED
)
720 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, MAXLONG
);
722 else if(!(lphc
->wState
& CBF_NOREDRAW
) && IsWindowVisible( lphc
->self
))
724 /* paint text field ourselves */
725 HDC hdc
= hdc_paint
? hdc_paint
: GetDC(lphc
->self
);
726 UINT itemState
= ODS_COMBOBOXEDIT
;
727 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
728 HBRUSH hPrevBrush
, hBkgBrush
;
731 * Give ourselves some space.
733 InflateRect( &rectEdit
, -1, -1 );
735 hBkgBrush
= COMBO_PrepareColors( lphc
, hdc
);
736 hPrevBrush
= SelectObject( hdc
, hBkgBrush
);
737 FillRect( hdc
, &rectEdit
, hBkgBrush
);
739 if( CB_OWNERDRAWN(lphc
) )
743 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
745 /* setup state for DRAWITEM message. Owner will highlight */
746 if ( (lphc
->wState
& CBF_FOCUSED
) &&
747 !(lphc
->wState
& CBF_DROPPED
) )
748 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
750 if (!IsWindowEnabled(lphc
->self
)) itemState
|= ODS_DISABLED
;
752 dis
.CtlType
= ODT_COMBOBOX
;
754 dis
.hwndItem
= lphc
->self
;
755 dis
.itemAction
= ODA_DRAWENTIRE
;
757 dis
.itemState
= itemState
;
759 dis
.rcItem
= rectEdit
;
760 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, id
, 0);
763 * Clip the DC and have the parent draw the item.
765 clipRegion
= set_control_clipping( hdc
, &rectEdit
);
767 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
769 SelectClipRgn( hdc
, clipRegion
);
770 if (clipRegion
) DeleteObject( clipRegion
);
774 static const WCHAR empty_stringW
[] = { 0 };
776 if ( (lphc
->wState
& CBF_FOCUSED
) &&
777 !(lphc
->wState
& CBF_DROPPED
) ) {
780 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
781 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
782 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
788 ETO_OPAQUE
| ETO_CLIPPED
,
790 pText
? pText
: empty_stringW
, size
, NULL
);
792 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
793 DrawFocusRect( hdc
, &rectEdit
);
797 SelectObject(hdc
, hPrevFont
);
800 SelectObject( hdc
, hPrevBrush
);
803 ReleaseDC( lphc
->self
, hdc
);
805 HeapFree( GetProcessHeap(), 0, pText
);
808 /***********************************************************************
811 static void CBPaintBorder(
813 const HEADCOMBO
*lphc
,
818 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
820 GetClientRect(hwnd
, &clientRect
);
824 clientRect
= lphc
->textRect
;
826 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
827 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
830 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
833 /***********************************************************************
836 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
841 hDC
= (hParamDC
) ? hParamDC
842 : BeginPaint( lphc
->self
, &ps
);
844 TRACE("hdc=%p\n", hDC
);
846 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
848 HBRUSH hPrevBrush
, hBkgBrush
;
851 * Retrieve the background brush and select it in the
854 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
856 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
857 if (!(lphc
->wState
& CBF_EDIT
))
858 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
861 * In non 3.1 look, there is a sunken border on the combobox
863 CBPaintBorder(lphc
->self
, lphc
, hDC
);
865 if( !IsRectEmpty(&lphc
->buttonRect
) )
867 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
870 /* paint the edit control padding area */
871 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
873 RECT rPadEdit
= lphc
->textRect
;
875 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
877 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
880 if( !(lphc
->wState
& CBF_EDIT
) )
881 CBPaintText( lphc
, hDC
);
884 SelectObject( hDC
, hPrevBrush
);
888 EndPaint(lphc
->self
, &ps
);
893 /***********************************************************************
896 * Select listbox entry according to the contents of the edit control.
898 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
904 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
907 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
909 TRACE("\t edit text length %i\n", length
);
913 GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
914 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, -1, (LPARAM
)pText
);
915 HeapFree( GetProcessHeap(), 0, pText
);
918 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, bSelect
? idx
: -1, 0);
920 /* probably superfluous but Windows sends this too */
921 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, idx
< 0 ? 0 : idx
, 0);
922 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, idx
< 0 ? 0 : idx
, 0);
927 /***********************************************************************
930 * Copy a listbox entry to the edit control.
932 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
936 static const WCHAR empty_stringW
[] = { 0 };
938 TRACE("\t %i\n", index
);
940 if( index
>= 0 ) /* got an entry */
942 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, index
, 0);
943 if( length
!= LB_ERR
)
945 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
947 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, index
, (LPARAM
)pText
);
952 if( CB_HASSTRINGS(lphc
) )
954 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
955 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
956 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
959 if( lphc
->wState
& CBF_FOCUSED
)
960 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
962 HeapFree( GetProcessHeap(), 0, pText
);
965 /***********************************************************************
968 * Show listbox popup.
970 static void CBDropDown( LPHEADCOMBO lphc
)
973 MONITORINFO mon_info
;
978 TRACE("[%p]: drop down\n", lphc
->self
);
980 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
984 lphc
->wState
|= CBF_DROPPED
;
985 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
987 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
989 /* Update edit only if item is in the list */
990 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
991 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
995 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
997 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
998 lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
, 0);
999 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1002 /* now set popup position */
1003 GetWindowRect( lphc
->self
, &rect
);
1006 * If it's a dropdown, the listbox is offset
1008 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1009 rect
.left
+= COMBO_EDITBUTTONSPACE();
1011 /* if the dropped height is greater than the total height of the dropped
1012 items list, then force the drop down list height to be the total height
1013 of the items in the dropped list */
1015 /* And Remove any extra space (Best Fit) */
1016 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1017 /* if listbox length has been set directly by its handle */
1018 GetWindowRect(lphc
->hWndLBox
, &r
);
1019 if (nDroppedHeight
< r
.bottom
- r
.top
)
1020 nDroppedHeight
= r
.bottom
- r
.top
;
1021 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1028 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1030 nHeight
= nIHeight
*nItems
;
1032 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1033 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1035 if (nDroppedHeight
< nHeight
)
1038 nDroppedHeight
= (nItems
+1)*nIHeight
;
1039 else if (nDroppedHeight
< 6*nIHeight
)
1040 nDroppedHeight
= 6*nIHeight
;
1045 r
.top
= rect
.bottom
;
1046 r
.right
= r
.left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1047 r
.bottom
= r
.top
+ nDroppedHeight
;
1049 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1050 monitor
= MonitorFromRect( &rect
, MONITOR_DEFAULTTOPRIMARY
);
1051 mon_info
.cbSize
= sizeof(mon_info
);
1052 GetMonitorInfoW( monitor
, &mon_info
);
1054 if (r
.bottom
> mon_info
.rcWork
.bottom
)
1056 r
.top
= max( rect
.top
- nDroppedHeight
, mon_info
.rcWork
.top
);
1057 r
.bottom
= min( r
.top
+ nDroppedHeight
, mon_info
.rcWork
.bottom
);
1060 SetWindowPos( lphc
->hWndLBox
, HWND_TOPMOST
, r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
1061 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1064 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1065 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1066 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1068 EnableWindow( lphc
->hWndLBox
, TRUE
);
1069 if (GetCapture() != lphc
->self
)
1070 SetCapture(lphc
->hWndLBox
);
1073 /***********************************************************************
1076 * Hide listbox popup.
1078 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1080 HWND hWnd
= lphc
->self
;
1082 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1083 lphc
->self
, ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1085 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1087 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1090 if( lphc
->wState
& CBF_DROPPED
)
1094 lphc
->wState
&= ~CBF_DROPPED
;
1095 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1097 if(GetCapture() == lphc
->hWndLBox
)
1102 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1104 rect
= lphc
->buttonRect
;
1115 rect
= lphc
->textRect
;
1120 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1121 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1122 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1123 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1128 /***********************************************************************
1131 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1133 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1135 if( lphc
->wState
& CBF_DROPPED
)
1137 CBRollUp( lphc
, ok
, bRedrawButton
);
1145 /***********************************************************************
1148 static void CBRepaintButton( LPHEADCOMBO lphc
)
1150 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1151 UpdateWindow(lphc
->self
);
1154 /***********************************************************************
1157 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1159 if( !(lphc
->wState
& CBF_FOCUSED
) )
1161 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1162 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1164 /* This is wrong. Message sequences seem to indicate that this
1165 is set *after* the notify. */
1166 /* lphc->wState |= CBF_FOCUSED; */
1168 if( !(lphc
->wState
& CBF_EDIT
) )
1169 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1171 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1172 lphc
->wState
|= CBF_FOCUSED
;
1176 /***********************************************************************
1179 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1181 HWND hWnd
= lphc
->self
;
1183 if( lphc
->wState
& CBF_FOCUSED
)
1185 CBRollUp( lphc
, FALSE
, TRUE
);
1186 if( IsWindow( hWnd
) )
1188 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1189 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1191 lphc
->wState
&= ~CBF_FOCUSED
;
1194 if( !(lphc
->wState
& CBF_EDIT
) )
1195 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1197 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1202 /***********************************************************************
1205 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1207 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1209 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1211 switch( HIWORD(wParam
) >> 8 )
1213 case (EN_SETFOCUS
>> 8):
1215 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1217 COMBO_SetFocus( lphc
);
1220 case (EN_KILLFOCUS
>> 8):
1222 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1224 /* NOTE: it seems that Windows' edit control sends an
1225 * undocumented message WM_USER + 0x1B instead of this
1226 * notification (only when it happens to be a part of
1227 * the combo). ?? - AK.
1230 COMBO_KillFocus( lphc
);
1234 case (EN_CHANGE
>> 8):
1236 * In some circumstances (when the selection of the combobox
1237 * is changed for example) we don't want the EN_CHANGE notification
1238 * to be forwarded to the parent of the combobox. This code
1239 * checks a flag that is set in these occasions and ignores the
1242 if (lphc
->wState
& CBF_NOLBSELECT
)
1244 lphc
->wState
&= ~CBF_NOLBSELECT
;
1248 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1251 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1252 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1255 case (EN_UPDATE
>> 8):
1256 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1257 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1260 case (EN_ERRSPACE
>> 8):
1261 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1264 else if( lphc
->hWndLBox
== hWnd
)
1266 switch( (short)HIWORD(wParam
) )
1269 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1273 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1279 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1281 /* do not roll up if selection is being tracked
1282 * by arrow keys in the dropdown listbox */
1283 if (!(lphc
->wState
& CBF_NOROLLUP
))
1285 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1287 else lphc
->wState
&= ~CBF_NOROLLUP
;
1289 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1291 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1293 if( lphc
->wState
& CBF_EDIT
)
1294 lphc
->wState
|= CBF_NOLBSELECT
;
1295 CBPaintText( lphc
, NULL
);
1301 /* nothing to do here since ComboLBox always resets the focus to its
1302 * combo/edit counterpart */
1309 /***********************************************************************
1312 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1314 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1316 HWND hWnd
= lphc
->self
;
1317 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1319 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1325 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1326 lpIS
->CtlType
= ODT_COMBOBOX
;
1328 lpIS
->hwndItem
= hWnd
;
1333 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1334 lpIS
->CtlType
= ODT_COMBOBOX
;
1336 lpIS
->hwndItem
= hWnd
;
1339 case WM_COMPAREITEM
:
1341 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1342 lpIS
->CtlType
= ODT_COMBOBOX
;
1344 lpIS
->hwndItem
= hWnd
;
1347 case WM_MEASUREITEM
:
1349 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1350 lpIS
->CtlType
= ODT_COMBOBOX
;
1355 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1359 /***********************************************************************
1362 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1366 if( lphc
->wState
& CBF_EDIT
)
1367 return SendMessageW( 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
= SendMessageW(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 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1383 if (!lpBuffer
) goto error
;
1384 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1386 /* truncate if buffer is too short */
1387 if (length
!= LB_ERR
)
1389 lstrcpynW( buf
, lpBuffer
, count
);
1392 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1394 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1396 if (length
== LB_ERR
) return 0;
1400 error
: /* error - truncate string, return zero */
1406 /***********************************************************************
1409 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1410 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1412 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1416 if( lphc
->wState
& CBF_EDIT
)
1417 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1419 /* get it from the listbox */
1421 if (!count
|| !buf
) return 0;
1422 if( lphc
->hWndLBox
)
1424 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1425 if (idx
== LB_ERR
) goto error
;
1426 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1427 if (length
== LB_ERR
) goto error
;
1429 /* 'length' is without the terminating character */
1430 if (length
>= count
)
1432 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1433 if (!lpBuffer
) goto error
;
1434 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1436 /* truncate if buffer is too short */
1437 if (length
!= LB_ERR
)
1439 lstrcpynA( buf
, lpBuffer
, count
);
1442 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1444 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1446 if (length
== LB_ERR
) return 0;
1450 error
: /* error - truncate string, return zero */
1456 /***********************************************************************
1459 * This function sets window positions according to the updated
1460 * component placement struct.
1462 static void CBResetPos(
1464 const RECT
*rectEdit
,
1468 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1470 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1471 * sizing messages */
1473 if( lphc
->wState
& CBF_EDIT
)
1474 SetWindowPos( lphc
->hWndEdit
, 0,
1475 rectEdit
->left
, rectEdit
->top
,
1476 rectEdit
->right
- rectEdit
->left
,
1477 rectEdit
->bottom
- rectEdit
->top
,
1478 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1480 SetWindowPos( lphc
->hWndLBox
, 0,
1481 rectLB
->left
, rectLB
->top
,
1482 rectLB
->right
- rectLB
->left
,
1483 rectLB
->bottom
- rectLB
->top
,
1484 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1488 if( lphc
->wState
& CBF_DROPPED
)
1490 lphc
->wState
&= ~CBF_DROPPED
;
1491 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1494 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1495 RedrawWindow( lphc
->self
, NULL
, 0,
1496 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1501 /***********************************************************************
1504 static void COMBO_Size( LPHEADCOMBO lphc
)
1507 * Those controls are always the same height. So we have to make sure
1508 * they are not resized to another value.
1510 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1512 int newComboHeight
, curComboHeight
, curComboWidth
;
1515 GetWindowRect(lphc
->self
, &rc
);
1516 curComboHeight
= rc
.bottom
- rc
.top
;
1517 curComboWidth
= rc
.right
- rc
.left
;
1518 newComboHeight
= CBGetTextAreaHeight(lphc
->self
, lphc
) + 2*COMBO_YBORDERSIZE();
1521 * Resizing a combobox has another side effect, it resizes the dropped
1522 * rectangle as well. However, it does it only if the new height for the
1523 * combobox is more than the height it should have. In other words,
1524 * if the application resizing the combobox only had the intention to resize
1525 * the actual control, for example, to do the layout of a dialog that is
1526 * resized, the height of the dropdown is not changed.
1528 if( curComboHeight
> newComboHeight
)
1530 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1531 curComboHeight
, newComboHeight
, lphc
->droppedRect
.bottom
,
1532 lphc
->droppedRect
.top
);
1533 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ curComboHeight
- newComboHeight
;
1536 * Restore original height
1538 if( curComboHeight
!= newComboHeight
)
1539 SetWindowPos(lphc
->self
, 0, 0, 0, curComboWidth
, newComboHeight
,
1540 SWP_NOZORDER
|SWP_NOMOVE
|SWP_NOACTIVATE
|SWP_NOREDRAW
);
1543 CBCalcPlacement(lphc
->self
,
1547 &lphc
->droppedRect
);
1549 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1553 /***********************************************************************
1556 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1561 lphc
->hFont
= hFont
;
1564 * Propagate to owned windows.
1566 if( lphc
->wState
& CBF_EDIT
)
1567 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1568 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1571 * Redo the layout of the control.
1573 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1575 CBCalcPlacement(lphc
->self
,
1579 &lphc
->droppedRect
);
1581 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1585 CBForceDummyResize(lphc
);
1590 /***********************************************************************
1591 * COMBO_SetItemHeight
1593 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1595 LRESULT lRet
= CB_ERR
;
1597 if( index
== -1 ) /* set text field height */
1599 if( height
< 32768 )
1601 lphc
->editHeight
= height
+ 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1604 * Redo the layout of the control.
1606 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1608 CBCalcPlacement(lphc
->self
,
1612 &lphc
->droppedRect
);
1614 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1618 CBForceDummyResize(lphc
);
1624 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1625 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
, index
, height
);
1629 /***********************************************************************
1630 * COMBO_SelectString
1632 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1634 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, start
, pText
) :
1635 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, start
, pText
);
1638 if( lphc
->wState
& CBF_EDIT
)
1639 CBUpdateEdit( lphc
, index
);
1642 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1645 return (LRESULT
)index
;
1648 /***********************************************************************
1651 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1655 HWND hWnd
= lphc
->self
;
1657 pt
.x
= (short)LOWORD(lParam
);
1658 pt
.y
= (short)HIWORD(lParam
);
1659 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1661 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1662 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1664 lphc
->wState
|= CBF_BUTTONDOWN
;
1665 if( lphc
->wState
& CBF_DROPPED
)
1667 /* got a click to cancel selection */
1669 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1670 CBRollUp( lphc
, TRUE
, FALSE
);
1671 if( !IsWindow( hWnd
) ) return;
1673 if( lphc
->wState
& CBF_CAPTURE
)
1675 lphc
->wState
&= ~CBF_CAPTURE
;
1681 /* drop down the listbox and start tracking */
1683 lphc
->wState
|= CBF_CAPTURE
;
1687 if( bButton
) CBRepaintButton( lphc
);
1691 /***********************************************************************
1694 * Release capture and stop tracking if needed.
1696 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1698 if( lphc
->wState
& CBF_CAPTURE
)
1700 lphc
->wState
&= ~CBF_CAPTURE
;
1701 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1703 INT index
= CBUpdateLBox( lphc
, TRUE
);
1704 /* Update edit only if item is in the list */
1707 lphc
->wState
|= CBF_NOLBSELECT
;
1708 CBUpdateEdit( lphc
, index
);
1709 lphc
->wState
&= ~CBF_NOLBSELECT
;
1713 SetCapture(lphc
->hWndLBox
);
1716 if( lphc
->wState
& CBF_BUTTONDOWN
)
1718 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1719 CBRepaintButton( lphc
);
1723 /***********************************************************************
1726 * Two things to do - track combo button and release capture when
1727 * pointer goes into the listbox.
1729 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1734 pt
.x
= (short)LOWORD(lParam
);
1735 pt
.y
= (short)HIWORD(lParam
);
1737 if( lphc
->wState
& CBF_BUTTONDOWN
)
1741 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1745 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1746 CBRepaintButton( lphc
);
1750 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1751 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1752 if( PtInRect(&lbRect
, pt
) )
1754 lphc
->wState
&= ~CBF_CAPTURE
;
1756 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1758 /* hand over pointer tracking */
1759 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1763 static LRESULT
COMBO_GetComboBoxInfo(const HEADCOMBO
*lphc
, COMBOBOXINFO
*pcbi
)
1765 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1768 pcbi
->rcItem
= lphc
->textRect
;
1769 pcbi
->rcButton
= lphc
->buttonRect
;
1770 pcbi
->stateButton
= 0;
1771 if (lphc
->wState
& CBF_BUTTONDOWN
)
1772 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1773 if (IsRectEmpty(&lphc
->buttonRect
))
1774 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1775 pcbi
->hwndCombo
= lphc
->self
;
1776 pcbi
->hwndItem
= lphc
->hWndEdit
;
1777 pcbi
->hwndList
= lphc
->hWndLBox
;
1781 static char *strdupA(LPCSTR str
)
1786 if(!str
) return NULL
;
1789 ret
= HeapAlloc(GetProcessHeap(), 0, len
+ 1);
1790 memcpy(ret
, str
, len
+ 1);
1794 /***********************************************************************
1795 * ComboWndProc_common
1797 LRESULT
ComboWndProc_common( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1799 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1801 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1802 hwnd
, SPY_GetMsgName(message
, hwnd
), wParam
, lParam
);
1804 if (!IsWindow(hwnd
)) return 0;
1806 if( lphc
|| message
== WM_NCCREATE
)
1810 /* System messages */
1814 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1815 ((LPCREATESTRUCTA
)lParam
)->style
;
1816 return COMBO_NCCreate(hwnd
, style
);
1819 COMBO_NCDestroy(lphc
);
1820 break;/* -> DefWindowProc */
1828 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1829 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1833 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1834 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1836 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1839 case WM_PRINTCLIENT
:
1842 /* wParam may contain a valid HDC! */
1843 return COMBO_Paint(lphc
, (HDC
)wParam
);
1846 /* do all painting in WM_PAINT like Windows does */
1851 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1852 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1854 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1856 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1857 result
|= DLGC_WANTMESSAGE
;
1862 if( lphc
->hWndLBox
&&
1863 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1866 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1869 return (LRESULT
)lphc
->hFont
;
1871 if( lphc
->wState
& CBF_EDIT
) {
1872 SetFocus( lphc
->hWndEdit
);
1873 /* The first time focus is received, select all the text */
1874 if( !(lphc
->wState
& CBF_BEENFOCUSED
) ) {
1875 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, -1);
1876 lphc
->wState
|= CBF_BEENFOCUSED
;
1880 COMBO_SetFocus( lphc
);
1884 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1886 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1887 COMBO_KillFocus( lphc
);
1891 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1893 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1894 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1896 case WM_GETTEXTLENGTH
:
1898 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1900 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1901 if (j
== -1) return 0;
1902 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1903 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1905 else if( lphc
->wState
& CBF_EDIT
)
1908 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1909 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1910 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1911 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1918 if( lphc
->wState
& CBF_EDIT
)
1920 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1921 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1927 case WM_COMPAREITEM
:
1928 case WM_MEASUREITEM
:
1929 return COMBO_ItemOp(lphc
, message
, lParam
);
1931 if( lphc
->wState
& CBF_EDIT
)
1932 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1933 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1935 /* Force the control to repaint when the enabled state changes. */
1936 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1940 lphc
->wState
&= ~CBF_NOREDRAW
;
1942 lphc
->wState
|= CBF_NOREDRAW
;
1944 if( lphc
->wState
& CBF_EDIT
)
1945 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
1946 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
1949 if( KEYDATA_ALT
& HIWORD(lParam
) )
1950 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1951 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1955 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
1956 (lphc
->wState
& CBF_DROPPED
))
1958 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
1961 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
1963 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1972 if( lphc
->wState
& CBF_EDIT
)
1973 hwndTarget
= lphc
->hWndEdit
;
1975 hwndTarget
= lphc
->hWndLBox
;
1977 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
1978 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
1980 case WM_LBUTTONDOWN
:
1981 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
1982 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1985 COMBO_LButtonUp( lphc
);
1988 if( lphc
->wState
& CBF_CAPTURE
)
1989 COMBO_MouseMove( lphc
, wParam
, lParam
);
1993 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
1994 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
1995 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
1997 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
1998 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2001 /* Combo messages */
2006 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2007 CharLowerW((LPWSTR
)lParam
);
2008 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2009 CharUpperW((LPWSTR
)lParam
);
2010 return SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
2012 else /* unlike the unicode version, the ansi version does not overwrite
2013 the string if converting case */
2015 char *string
= NULL
;
2017 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2019 string
= strdupA((LPSTR
)lParam
);
2023 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2025 string
= strdupA((LPSTR
)lParam
);
2029 ret
= SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, string
? (LPARAM
)string
: lParam
);
2030 HeapFree(GetProcessHeap(), 0, string
);
2033 case CB_INSERTSTRING
:
2036 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2037 CharLowerW((LPWSTR
)lParam
);
2038 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2039 CharUpperW((LPWSTR
)lParam
);
2040 return SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2044 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2045 CharLowerA((LPSTR
)lParam
);
2046 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2047 CharUpperA((LPSTR
)lParam
);
2049 return SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2051 case CB_DELETESTRING
:
2052 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2053 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2054 case CB_SELECTSTRING
:
2055 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2057 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2058 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2059 case CB_FINDSTRINGEXACT
:
2060 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2061 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2062 case CB_SETITEMHEIGHT
:
2063 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2064 case CB_GETITEMHEIGHT
:
2065 if( (INT
)wParam
>= 0 ) /* listbox item */
2066 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2067 return CBGetTextAreaHeight(hwnd
, lphc
);
2068 case CB_RESETCONTENT
:
2069 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2070 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2072 static const WCHAR empty_stringW
[] = { 0 };
2073 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2076 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2078 case CB_INITSTORAGE
:
2079 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2080 case CB_GETHORIZONTALEXTENT
:
2081 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2082 case CB_SETHORIZONTALEXTENT
:
2083 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2084 case CB_GETTOPINDEX
:
2085 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2087 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2089 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2090 case CB_SETDROPPEDWIDTH
:
2091 if( (CB_GETTYPE(lphc
) == CBS_SIMPLE
) ||
2092 (INT
)wParam
>= 32768 )
2094 /* new value must be higher than combobox width */
2095 if((INT
)wParam
>= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
)
2096 lphc
->droppedWidth
= wParam
;
2098 lphc
->droppedWidth
= 0;
2100 /* recalculate the combobox area */
2101 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
2104 case CB_GETDROPPEDWIDTH
:
2105 if( lphc
->droppedWidth
)
2106 return lphc
->droppedWidth
;
2107 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2108 case CB_GETDROPPEDCONTROLRECT
:
2109 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2111 case CB_GETDROPPEDSTATE
:
2112 return (lphc
->wState
& CBF_DROPPED
) != 0;
2114 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2115 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2117 case CB_SHOWDROPDOWN
:
2118 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2122 if( !(lphc
->wState
& CBF_DROPPED
) )
2126 if( lphc
->wState
& CBF_DROPPED
)
2127 CBRollUp( lphc
, FALSE
, TRUE
);
2131 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2133 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2135 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2137 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2139 /* no LBN_SELCHANGE in this case, update manually */
2140 CBPaintText( lphc
, NULL
);
2141 lphc
->wState
&= ~CBF_SELCHANGE
;
2144 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2145 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2146 case CB_GETLBTEXTLEN
:
2147 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2148 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2149 case CB_GETITEMDATA
:
2150 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2151 case CB_SETITEMDATA
:
2152 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2154 /* Edit checks passed parameters itself */
2155 if( lphc
->wState
& CBF_EDIT
)
2156 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2159 if( lphc
->wState
& CBF_EDIT
)
2160 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2161 (INT
)(SHORT
)LOWORD(lParam
), (INT
)(SHORT
)HIWORD(lParam
) );
2163 case CB_SETEXTENDEDUI
:
2164 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2167 lphc
->wState
|= CBF_EUI
;
2168 else lphc
->wState
&= ~CBF_EUI
;
2170 case CB_GETEXTENDEDUI
:
2171 return (lphc
->wState
& CBF_EUI
) != 0;
2172 case CB_GETCOMBOBOXINFO
:
2173 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2175 if( lphc
->wState
& CBF_EDIT
)
2176 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2179 if (message
>= WM_USER
)
2180 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2181 message
- WM_USER
, wParam
, lParam
);
2184 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2185 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2188 /*************************************************************************
2189 * GetComboBoxInfo (USER32.@)
2191 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2192 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2194 TRACE("(%p, %p)\n", hwndCombo
, pcbi
);
2195 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);