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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 4, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
30 * - ComboBox_[GS]etMinVisible()
31 * - CB_GETMINVISIBLE, CB_SETMINVISIBLE
39 WINE_DEFAULT_DEBUG_CHANNEL(combo
);
41 /* bits in the dwKeyData */
42 #define KEYDATA_ALT 0x2000
43 #define KEYDATA_PREVSTATE 0x4000
46 * Additional combo box definitions
49 #define CB_NOTIFY( lphc, code ) \
50 (SendMessageW((lphc)->owner, WM_COMMAND, \
51 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
53 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
54 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
55 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
56 #define CB_HWND( lphc ) ((lphc)->self)
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 LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
77 static LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
79 /*********************************************************************
80 * combo class descriptor
82 const struct builtin_class_descr COMBO_builtin_class
=
85 L
"ComboBox", /* name */
86 CS_PARENTDC
| CS_VREDRAW
| CS_HREDRAW
| CS_DBLCLKS
, /* style */
87 (WNDPROC
) ComboWndProcW
, /* procW */
88 (WNDPROC
) ComboWndProcA
, /* procA */
89 sizeof(HEADCOMBO
*), /* extra */
90 (LPCWSTR
) IDC_ARROW
, /* cursor */
93 "ComboBox", /* name */
94 CS_PARENTDC
| CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, /* style */
95 ComboWndProcA
, /* procA */
96 ComboWndProcW
, /* procW */
97 sizeof(HEADCOMBO
*), /* extra */
98 IDC_ARROW
, /* cursor */
104 /***********************************************************************
107 * Load combo button bitmap.
109 static BOOL
COMBO_Init()
113 if( hComboBmp
) return TRUE
;
114 if( (hDC
= CreateCompatibleDC(0)) )
117 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
123 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
124 CBitHeight
= bm
.bmHeight
;
125 CBitWidth
= bm
.bmWidth
;
127 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
129 hPrevB
= SelectObject( hDC
, hComboBmp
);
130 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
131 InvertRect( hDC
, &r
);
132 SelectObject( hDC
, hPrevB
);
141 /***********************************************************************
144 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
148 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
151 SetWindowLongW( hwnd
, 0, (LONG
)lphc
);
153 /* some braindead apps do try to use scrollbar/border flags */
155 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
156 SetWindowLongW( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
159 * We also have to remove the client edge style to make sure
160 * we don't end-up with a non client area.
162 SetWindowLongW( hwnd
, GWL_EXSTYLE
,
163 GetWindowLongW( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
165 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
166 lphc
->dwStyle
|= CBS_HASSTRINGS
;
167 if( !(GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
168 lphc
->wState
|= CBF_NOTIFY
;
170 TRACE("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
176 /***********************************************************************
179 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
184 TRACE("[%p]: freeing storage\n", lphc
->self
);
186 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
187 DestroyWindow( lphc
->hWndLBox
);
189 SetWindowLongPtrW( lphc
->self
, 0, 0 );
190 HeapFree( GetProcessHeap(), 0, lphc
);
195 /***********************************************************************
196 * CBGetTextAreaHeight
198 * This method will calculate the height of the text area of the
200 * The height of the text area is set in two ways.
201 * It can be set explicitly through a combobox message or through a
202 * WM_MEASUREITEM callback.
203 * If this is not the case, the height is set to 13 dialog units.
204 * This height was determined through experimentation.
206 static INT
CBGetTextAreaHeight(
212 if( lphc
->editHeight
) /* explicitly set height */
214 iTextItemHeight
= lphc
->editHeight
;
219 HDC hDC
= GetDC(hwnd
);
224 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
226 GetTextMetricsW(hDC
, &tm
);
228 baseUnitY
= tm
.tmHeight
;
231 SelectObject( hDC
, hPrevFont
);
233 ReleaseDC(hwnd
, hDC
);
235 iTextItemHeight
= ((13 * baseUnitY
) / 8);
238 * This "formula" calculates the height of the complete control.
239 * To calculate the height of the text area, we have to remove the
242 iTextItemHeight
-= 2*COMBO_YBORDERSIZE();
246 * Check the ownerdraw case if we haven't asked the parent the size
249 if ( CB_OWNERDRAWN(lphc
) &&
250 (lphc
->wState
& CBF_MEASUREITEM
) )
252 MEASUREITEMSTRUCT measureItem
;
254 INT originalItemHeight
= iTextItemHeight
;
255 UINT id
= GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
258 * We use the client rect for the width of the item.
260 GetClientRect(hwnd
, &clientRect
);
262 lphc
->wState
&= ~CBF_MEASUREITEM
;
265 * Send a first one to measure the size of the text area
267 measureItem
.CtlType
= ODT_COMBOBOX
;
268 measureItem
.CtlID
= id
;
269 measureItem
.itemID
= -1;
270 measureItem
.itemWidth
= clientRect
.right
;
271 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
272 measureItem
.itemData
= 0;
273 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
274 iTextItemHeight
= 6 + measureItem
.itemHeight
;
277 * Send a second one in the case of a fixed ownerdraw list to calculate the
278 * size of the list items. (we basically do this on behalf of the listbox)
280 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
282 measureItem
.CtlType
= ODT_COMBOBOX
;
283 measureItem
.CtlID
= id
;
284 measureItem
.itemID
= 0;
285 measureItem
.itemWidth
= clientRect
.right
;
286 measureItem
.itemHeight
= originalItemHeight
;
287 measureItem
.itemData
= 0;
288 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
289 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
293 * Keep the size for the next time
295 lphc
->editHeight
= iTextItemHeight
;
298 return iTextItemHeight
;
301 /***********************************************************************
304 * The dummy resize is used for listboxes that have a popup to trigger
305 * a re-arranging of the contents of the combobox and the recalculation
306 * of the size of the "real" control window.
308 static void CBForceDummyResize(
314 newComboHeight
= CBGetTextAreaHeight(lphc
->self
,lphc
) + 2*COMBO_YBORDERSIZE();
316 GetWindowRect(lphc
->self
, &windowRect
);
319 * We have to be careful, resizing a combobox also has the meaning that the
320 * dropped rect will be resized. In this case, we want to trigger a resize
321 * to recalculate layout but we don't want to change the dropped rectangle
322 * So, we pass the height of text area of control as the height.
323 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
326 SetWindowPos( lphc
->self
,
329 windowRect
.right
- windowRect
.left
,
331 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
334 /***********************************************************************
337 * Set up component coordinates given valid lphc->RectCombo.
339 static void CBCalcPlacement(
347 * Again, start with the client rectangle.
349 GetClientRect(hwnd
, lprEdit
);
354 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
357 * Chop off the bottom part to fit with the height of the text area.
359 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
362 * The button starts the same vertical position as the text area.
364 CopyRect(lprButton
, lprEdit
);
367 * If the combobox is "simple" there is no button.
369 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
370 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
374 * Let's assume the combobox button is the same width as the
376 * size the button horizontally and cut-off the text area.
378 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
379 lprEdit
->right
= lprButton
->left
;
383 * In the case of a dropdown, there is an additional spacing between the
384 * text area and the button.
386 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
388 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
392 * If we have an edit control, we space it away from the borders slightly.
394 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
396 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
400 * Adjust the size of the listbox popup.
402 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
405 * Use the client rectangle to initialize the listbox rectangle
407 GetClientRect(hwnd
, lprLB
);
410 * Then, chop-off the top part.
412 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
417 * Make sure the dropped width is as large as the combobox itself.
419 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
421 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
424 * In the case of a dropdown, the popup listbox is offset to the right.
425 * so, we want to make sure it's flush with the right side of the
428 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
429 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
432 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
435 TRACE("\ttext\t= (%ld,%ld-%ld,%ld)\n",
436 lprEdit
->left
, lprEdit
->top
, lprEdit
->right
, lprEdit
->bottom
);
438 TRACE("\tbutton\t= (%ld,%ld-%ld,%ld)\n",
439 lprButton
->left
, lprButton
->top
, lprButton
->right
, lprButton
->bottom
);
441 TRACE("\tlbox\t= (%ld,%ld-%ld,%ld)\n",
442 lprLB
->left
, lprLB
->top
, lprLB
->right
, lprLB
->bottom
);
445 /***********************************************************************
446 * CBGetDroppedControlRect
448 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
450 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
451 of the combo box and the lower right corner of the listbox */
453 GetWindowRect(lphc
->self
, lpRect
);
455 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
456 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
460 /***********************************************************************
461 * COMBO_WindowPosChanging
463 static LRESULT
COMBO_WindowPosChanging(
466 WINDOWPOS
* posChanging
)
469 * We need to override the WM_WINDOWPOSCHANGING method to handle all
470 * the non-simple comboboxes. The problem is that those controls are
471 * always the same height. We have to make sure they are not resized
474 if ( ( CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
475 ((posChanging
->flags
& SWP_NOSIZE
) == 0) )
479 newComboHeight
= CBGetTextAreaHeight(hwnd
,lphc
) +
480 2*COMBO_YBORDERSIZE();
483 * Resizing a combobox has another side effect, it resizes the dropped
484 * rectangle as well. However, it does it only if the new height for the
485 * combobox is different from the height it should have. In other words,
486 * if the application resizing the combobox only had the intention to resize
487 * the actual control, for example, to do the layout of a dialog that is
488 * resized, the height of the dropdown is not changed.
490 if (posChanging
->cy
!= newComboHeight
)
492 TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%ld, oldtop=%ld\n",
493 posChanging
->cy
, newComboHeight
, lphc
->droppedRect
.bottom
,
494 lphc
->droppedRect
.top
);
495 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ posChanging
->cy
- newComboHeight
;
497 posChanging
->cy
= newComboHeight
;
504 /***********************************************************************
507 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
510 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
511 static const WCHAR editName
[] = {'E','d','i','t',0};
513 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
514 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
516 lphc
->owner
= hwndParent
;
519 * The item height and dropped width are not set when the control
522 lphc
->droppedWidth
= lphc
->editHeight
= 0;
525 * The first time we go through, we want to measure the ownerdraw item
527 lphc
->wState
|= CBF_MEASUREITEM
;
529 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
531 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
537 * Initialize the dropped rect to the size of the client area of the
538 * control and then, force all the areas of the combobox to be
541 GetClientRect( hwnd
, &lphc
->droppedRect
);
542 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
545 * Adjust the position of the popup listbox if it's necessary
547 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
549 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
552 * If it's a dropdown, the listbox is offset
554 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
555 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
557 if (lphc
->droppedRect
.bottom
< lphc
->droppedRect
.top
)
558 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
;
559 if (lphc
->droppedRect
.right
< lphc
->droppedRect
.left
)
560 lphc
->droppedRect
.right
= lphc
->droppedRect
.left
;
561 MapWindowPoints( hwnd
, 0, (LPPOINT
)&lphc
->droppedRect
, 2 );
564 /* create listbox popup */
566 lbeStyle
= (LBS_NOTIFY
| LBS_COMBOBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
567 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
569 if( lphc
->dwStyle
& CBS_SORT
)
570 lbeStyle
|= LBS_SORT
;
571 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
572 lbeStyle
|= LBS_HASSTRINGS
;
573 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
574 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
575 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
576 lbeStyle
|= LBS_DISABLENOSCROLL
;
578 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
580 lbeStyle
|= WS_VISIBLE
;
583 * In win 95 look n feel, the listbox in the simple combobox has
584 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
586 lbeStyle
&= ~WS_BORDER
;
587 lbeExStyle
|= WS_EX_CLIENTEDGE
;
591 lbeExStyle
|= (WS_EX_TOPMOST
| WS_EX_TOOLWINDOW
);
595 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
596 lphc
->droppedRect
.left
,
597 lphc
->droppedRect
.top
,
598 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
599 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
600 hwnd
, (HMENU
)ID_CB_LISTBOX
,
601 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
603 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
604 lphc
->droppedRect
.left
,
605 lphc
->droppedRect
.top
,
606 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
607 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
608 hwnd
, (HMENU
)ID_CB_LISTBOX
,
609 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), lphc
);
614 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
616 if( lphc
->wState
& CBF_EDIT
)
618 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
619 lbeStyle
|= ES_OEMCONVERT
;
620 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
621 lbeStyle
|= ES_AUTOHSCROLL
;
622 if( lphc
->dwStyle
& CBS_LOWERCASE
)
623 lbeStyle
|= ES_LOWERCASE
;
624 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
625 lbeStyle
|= ES_UPPERCASE
;
627 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
630 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
631 lphc
->textRect
.left
, lphc
->textRect
.top
,
632 lphc
->textRect
.right
- lphc
->textRect
.left
,
633 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
634 hwnd
, (HMENU
)ID_CB_EDIT
,
635 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
637 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
638 lphc
->textRect
.left
, lphc
->textRect
.top
,
639 lphc
->textRect
.right
- lphc
->textRect
.left
,
640 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
641 hwnd
, (HMENU
)ID_CB_EDIT
,
642 (HINSTANCE
)GetWindowLongPtrW( hwnd
, GWLP_HINSTANCE
), NULL
);
644 if( !lphc
->hWndEdit
)
650 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
652 /* Now do the trick with parent */
653 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
655 * If the combo is a dropdown, we must resize the control
656 * to fit only the text area and button. To do this,
657 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
658 * will take care of setting the height for us.
660 CBForceDummyResize(lphc
);
663 TRACE("init done\n");
666 ERR("edit control failure.\n");
667 } else ERR("listbox failure.\n");
668 } else ERR("no owner for visible combo.\n");
670 /* CreateWindow() will send WM_NCDESTROY to cleanup */
675 /***********************************************************************
678 * Paint combo button (normal, pressed, and disabled states).
680 static void CBPaintButton( LPHEADCOMBO lphc
, HDC hdc
, RECT rectButton
)
682 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
684 if( lphc
->wState
& CBF_NOREDRAW
)
688 if (lphc
->wState
& CBF_BUTTONDOWN
)
689 buttonState
|= DFCS_PUSHED
;
691 if (CB_DISABLED(lphc
))
692 buttonState
|= DFCS_INACTIVE
;
694 DrawFrameControl(hdc
, &rectButton
, DFC_SCROLL
, buttonState
);
697 /***********************************************************************
700 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
702 static void CBPaintText(
710 if( lphc
->wState
& CBF_NOREDRAW
) return;
714 /* follow Windows combobox that sends a bunch of text
715 * inquiries to its listbox while processing WM_PAINT. */
717 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
719 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
721 FIXME("LB_ERR probably not handled yet\n");
722 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
724 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
725 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
726 pText
[size
] = '\0'; /* just in case */
730 if( !CB_OWNERDRAWN(lphc
) )
733 if( lphc
->wState
& CBF_EDIT
)
735 static const WCHAR empty_stringW
[] = { 0 };
736 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
737 if( lphc
->wState
& CBF_FOCUSED
)
738 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
740 else /* paint text field ourselves */
742 UINT itemState
= ODS_COMBOBOXEDIT
;
743 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
746 * Give ourselves some space.
748 InflateRect( &rectEdit
, -1, -1 );
750 if( CB_OWNERDRAWN(lphc
) )
754 UINT ctlid
= (UINT
)GetWindowLongPtrW( lphc
->self
, GWLP_ID
);
756 /* setup state for DRAWITEM message. Owner will highlight */
757 if ( (lphc
->wState
& CBF_FOCUSED
) &&
758 !(lphc
->wState
& CBF_DROPPED
) )
759 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
762 * Save the current clip region.
763 * To retrieve the clip region, we need to create one "dummy"
766 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
768 if (GetClipRgn(hdc
, clipRegion
)!=1)
770 DeleteObject(clipRegion
);
774 if (!IsWindowEnabled(lphc
->self
) & WS_DISABLED
) itemState
|= ODS_DISABLED
;
776 dis
.CtlType
= ODT_COMBOBOX
;
778 dis
.hwndItem
= lphc
->self
;
779 dis
.itemAction
= ODA_DRAWENTIRE
;
781 dis
.itemState
= itemState
;
783 dis
.rcItem
= rectEdit
;
784 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
,
788 * Clip the DC and have the parent draw the item.
790 IntersectClipRect(hdc
,
791 rectEdit
.left
, rectEdit
.top
,
792 rectEdit
.right
, rectEdit
.bottom
);
794 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
797 * Reset the clipping region.
799 SelectClipRgn(hdc
, clipRegion
);
803 static const WCHAR empty_stringW
[] = { 0 };
805 if ( (lphc
->wState
& CBF_FOCUSED
) &&
806 !(lphc
->wState
& CBF_DROPPED
) ) {
809 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
810 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
811 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
817 ETO_OPAQUE
| ETO_CLIPPED
,
819 pText
? pText
: empty_stringW
, size
, NULL
);
821 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
822 DrawFocusRect( hdc
, &rectEdit
);
826 SelectObject(hdc
, hPrevFont
);
829 HeapFree( GetProcessHeap(), 0, pText
);
832 /***********************************************************************
835 static void CBPaintBorder(
842 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
844 GetClientRect(hwnd
, &clientRect
);
848 CopyRect(&clientRect
, &lphc
->textRect
);
850 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
851 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
854 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
857 /***********************************************************************
858 * COMBO_PrepareColors
860 * This method will sent the appropriate WM_CTLCOLOR message to
861 * prepare and setup the colors for the combo's DC.
863 * It also returns the brush to use for the background.
865 static HBRUSH
COMBO_PrepareColors(
872 * Get the background brush for this control.
874 if (CB_DISABLED(lphc
))
876 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
877 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
880 * We have to change the text color since WM_CTLCOLORSTATIC will
881 * set it to the "enabled" color. This is the same behavior as the
884 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
888 if (lphc
->wState
& CBF_EDIT
)
890 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
891 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
895 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORLISTBOX
,
896 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
904 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
909 /***********************************************************************
910 * COMBO_EraseBackground
912 static LRESULT
COMBO_EraseBackground(
920 if(lphc
->wState
& CBF_EDIT
)
923 hDC
= (hParamDC
) ? hParamDC
926 * Retrieve the background brush
928 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
930 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
933 ReleaseDC(hwnd
, hDC
);
938 /***********************************************************************
941 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
946 hDC
= (hParamDC
) ? hParamDC
947 : BeginPaint( lphc
->self
, &ps
);
949 TRACE("hdc=%p\n", hDC
);
951 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
953 HBRUSH hPrevBrush
, hBkgBrush
;
956 * Retrieve the background brush and select it in the
959 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
961 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
964 * In non 3.1 look, there is a sunken border on the combobox
966 CBPaintBorder(lphc
->self
, lphc
, hDC
);
968 if( !IsRectEmpty(&lphc
->buttonRect
) )
970 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
973 /* paint the edit control padding area */
974 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
976 RECT rPadEdit
= lphc
->textRect
;
978 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
980 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
983 if( !(lphc
->wState
& CBF_EDIT
) )
984 CBPaintText( lphc
, hDC
, lphc
->textRect
);
987 SelectObject( hDC
, hPrevBrush
);
991 EndPaint(lphc
->self
, &ps
);
996 /***********************************************************************
999 * Select listbox entry according to the contents of the edit control.
1001 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
1004 LPWSTR pText
= NULL
;
1007 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
1010 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1012 TRACE("\t edit text length %i\n", length
);
1016 if( length
) GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
1017 else pText
[0] = '\0';
1018 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
,
1019 (WPARAM
)(-1), (LPARAM
)pText
);
1020 HeapFree( GetProcessHeap(), 0, pText
);
1023 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)(bSelect
? idx
: -1), 0);
1025 /* probably superfluous but Windows sends this too */
1026 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
1027 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
1032 /***********************************************************************
1035 * Copy a listbox entry to the edit control.
1037 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1040 LPWSTR pText
= NULL
;
1041 static const WCHAR empty_stringW
[] = { 0 };
1043 TRACE("\t %i\n", index
);
1045 if( index
>= 0 ) /* got an entry */
1047 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1048 if( length
!= LB_ERR
)
1050 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
1052 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
,
1053 (WPARAM
)index
, (LPARAM
)pText
);
1058 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1059 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
1060 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1062 if( lphc
->wState
& CBF_FOCUSED
)
1063 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1065 HeapFree( GetProcessHeap(), 0, pText
);
1068 /***********************************************************************
1071 * Show listbox popup.
1073 static void CBDropDown( LPHEADCOMBO lphc
)
1079 TRACE("[%p]: drop down\n", lphc
->self
);
1081 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1085 lphc
->wState
|= CBF_DROPPED
;
1086 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1088 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
1090 /* Update edit only if item is in the list */
1091 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
1092 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1096 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1098 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
1099 (WPARAM
)(lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
), 0 );
1100 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1103 /* now set popup position */
1104 GetWindowRect( lphc
->self
, &rect
);
1107 * If it's a dropdown, the listbox is offset
1109 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1110 rect
.left
+= COMBO_EDITBUTTONSPACE();
1112 /* if the dropped height is greater than the total height of the dropped
1113 items list, then force the drop down list height to be the total height
1114 of the items in the dropped list */
1116 /* And Remove any extra space (Best Fit) */
1117 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1118 /* if listbox length has been set directly by its handle */
1119 GetWindowRect(lphc
->hWndLBox
, &r
);
1120 if (nDroppedHeight
< r
.bottom
- r
.top
)
1121 nDroppedHeight
= r
.bottom
- r
.top
;
1122 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1129 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1131 nHeight
= nIHeight
*nItems
;
1133 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1134 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1136 if (nDroppedHeight
< nIHeight
)
1139 nDroppedHeight
= (nItems
+1)*nIHeight
;
1141 nDroppedHeight
= 6*nIHeight
;
1145 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1146 if( (rect
.bottom
+ nDroppedHeight
) >= GetSystemMetrics( SM_CYSCREEN
) )
1147 rect
.bottom
= rect
.top
- nDroppedHeight
;
1149 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1150 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1152 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1155 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1156 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1157 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1159 EnableWindow( lphc
->hWndLBox
, TRUE
);
1160 if (GetCapture() != lphc
->self
)
1161 SetCapture(lphc
->hWndLBox
);
1164 /***********************************************************************
1167 * Hide listbox popup.
1169 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1171 HWND hWnd
= lphc
->self
;
1173 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1174 lphc
->self
, (INT
)ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1176 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1178 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1181 if( lphc
->wState
& CBF_DROPPED
)
1185 lphc
->wState
&= ~CBF_DROPPED
;
1186 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1188 if(GetCapture() == lphc
->hWndLBox
)
1193 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1195 rect
= lphc
->buttonRect
;
1206 rect
= lphc
->textRect
;
1211 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1212 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1213 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1214 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1219 /***********************************************************************
1222 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1224 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1226 if( lphc
->wState
& CBF_DROPPED
)
1228 CBRollUp( lphc
, ok
, bRedrawButton
);
1236 /***********************************************************************
1239 static void CBRepaintButton( LPHEADCOMBO lphc
)
1241 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1242 UpdateWindow(lphc
->self
);
1245 /***********************************************************************
1248 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1250 if( !(lphc
->wState
& CBF_FOCUSED
) )
1252 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1253 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1255 /* This is wrong. Message sequences seem to indicate that this
1256 is set *after* the notify. */
1257 /* lphc->wState |= CBF_FOCUSED; */
1259 if( !(lphc
->wState
& CBF_EDIT
) )
1260 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1262 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1263 lphc
->wState
|= CBF_FOCUSED
;
1267 /***********************************************************************
1270 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1272 HWND hWnd
= lphc
->self
;
1274 if( lphc
->wState
& CBF_FOCUSED
)
1276 CBRollUp( lphc
, FALSE
, TRUE
);
1277 if( IsWindow( hWnd
) )
1279 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1280 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1282 lphc
->wState
&= ~CBF_FOCUSED
;
1285 if( !(lphc
->wState
& CBF_EDIT
) )
1286 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1288 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1293 /***********************************************************************
1296 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1298 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1300 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1302 switch( HIWORD(wParam
) >> 8 )
1304 case (EN_SETFOCUS
>> 8):
1306 TRACE("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1308 COMBO_SetFocus( lphc
);
1311 case (EN_KILLFOCUS
>> 8):
1313 TRACE("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1315 /* NOTE: it seems that Windows' edit control sends an
1316 * undocumented message WM_USER + 0x1B instead of this
1317 * notification (only when it happens to be a part of
1318 * the combo). ?? - AK.
1321 COMBO_KillFocus( lphc
);
1325 case (EN_CHANGE
>> 8):
1327 * In some circumstances (when the selection of the combobox
1328 * is changed for example) we don't wans the EN_CHANGE notification
1329 * to be forwarded to the parent of the combobox. This code
1330 * checks a flag that is set in these occasions and ignores the
1333 if (lphc
->wState
& CBF_NOLBSELECT
)
1335 lphc
->wState
&= ~CBF_NOLBSELECT
;
1339 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1342 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1343 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1346 case (EN_UPDATE
>> 8):
1347 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1348 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1351 case (EN_ERRSPACE
>> 8):
1352 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1355 else if( lphc
->hWndLBox
== hWnd
)
1357 switch( (short)HIWORD(wParam
) )
1360 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1364 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1370 TRACE("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1372 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1374 if( lphc
->wState
& CBF_EDIT
)
1376 INT index
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1377 lphc
->wState
|= CBF_NOLBSELECT
;
1378 CBUpdateEdit( lphc
, index
);
1379 /* select text in edit, as Windows does */
1380 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1383 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1386 /* do not roll up if selection is being tracked
1387 * by arrowkeys in the dropdown listbox */
1388 if( ((lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
)) )
1390 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1392 else lphc
->wState
&= ~CBF_NOROLLUP
;
1394 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1400 /* nothing to do here since ComboLBox always resets the focus to its
1401 * combo/edit counterpart */
1408 /***********************************************************************
1411 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1413 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1415 HWND hWnd
= lphc
->self
;
1416 UINT id
= (UINT
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
1418 TRACE("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1424 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1425 lpIS
->CtlType
= ODT_COMBOBOX
;
1427 lpIS
->hwndItem
= hWnd
;
1432 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1433 lpIS
->CtlType
= ODT_COMBOBOX
;
1435 lpIS
->hwndItem
= hWnd
;
1438 case WM_COMPAREITEM
:
1440 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1441 lpIS
->CtlType
= ODT_COMBOBOX
;
1443 lpIS
->hwndItem
= hWnd
;
1446 case WM_MEASUREITEM
:
1448 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1449 lpIS
->CtlType
= ODT_COMBOBOX
;
1454 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1458 /***********************************************************************
1461 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1465 if( lphc
->wState
& CBF_EDIT
)
1466 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1468 /* get it from the listbox */
1470 if (!count
|| !buf
) return 0;
1471 if( lphc
->hWndLBox
)
1473 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1474 if (idx
== LB_ERR
) goto error
;
1475 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1476 if (length
== LB_ERR
) goto error
;
1478 /* 'length' is without the terminating character */
1479 if (length
>= count
)
1481 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1482 if (!lpBuffer
) goto error
;
1483 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1485 /* truncate if buffer is too short */
1486 if (length
!= LB_ERR
)
1488 lstrcpynW( buf
, lpBuffer
, count
);
1491 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1493 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1495 if (length
== LB_ERR
) return 0;
1499 error
: /* error - truncate string, return zero */
1505 /***********************************************************************
1508 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1509 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1511 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1515 if( lphc
->wState
& CBF_EDIT
)
1516 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1518 /* get it from the listbox */
1520 if (!count
|| !buf
) return 0;
1521 if( lphc
->hWndLBox
)
1523 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1524 if (idx
== LB_ERR
) goto error
;
1525 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1526 if (length
== LB_ERR
) goto error
;
1528 /* 'length' is without the terminating character */
1529 if (length
>= count
)
1531 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1532 if (!lpBuffer
) goto error
;
1533 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1535 /* truncate if buffer is too short */
1536 if (length
!= LB_ERR
)
1538 lstrcpynA( buf
, lpBuffer
, count
);
1541 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1543 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1545 if (length
== LB_ERR
) return 0;
1549 error
: /* error - truncate string, return zero */
1555 /***********************************************************************
1558 * This function sets window positions according to the updated
1559 * component placement struct.
1561 static void CBResetPos(
1567 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1569 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1570 * sizing messages */
1572 if( lphc
->wState
& CBF_EDIT
)
1573 SetWindowPos( lphc
->hWndEdit
, 0,
1574 rectEdit
->left
, rectEdit
->top
,
1575 rectEdit
->right
- rectEdit
->left
,
1576 rectEdit
->bottom
- rectEdit
->top
,
1577 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1579 SetWindowPos( lphc
->hWndLBox
, 0,
1580 rectLB
->left
, rectLB
->top
,
1581 rectLB
->right
- rectLB
->left
,
1582 rectLB
->bottom
- rectLB
->top
,
1583 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1587 if( lphc
->wState
& CBF_DROPPED
)
1589 lphc
->wState
&= ~CBF_DROPPED
;
1590 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1593 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1594 RedrawWindow( lphc
->self
, NULL
, 0,
1595 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1600 /***********************************************************************
1603 static void COMBO_Size( LPHEADCOMBO lphc
)
1605 CBCalcPlacement(lphc
->self
,
1609 &lphc
->droppedRect
);
1611 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1615 /***********************************************************************
1618 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1623 lphc
->hFont
= hFont
;
1626 * Propagate to owned windows.
1628 if( lphc
->wState
& CBF_EDIT
)
1629 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1630 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1633 * Redo the layout of the control.
1635 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1637 CBCalcPlacement(lphc
->self
,
1641 &lphc
->droppedRect
);
1643 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1647 CBForceDummyResize(lphc
);
1652 /***********************************************************************
1653 * COMBO_SetItemHeight
1655 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1657 LRESULT lRet
= CB_ERR
;
1659 if( index
== -1 ) /* set text field height */
1661 if( height
< 32768 )
1663 lphc
->editHeight
= height
;
1666 * Redo the layout of the control.
1668 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1670 CBCalcPlacement(lphc
->self
,
1674 &lphc
->droppedRect
);
1676 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1680 CBForceDummyResize(lphc
);
1686 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1687 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1688 (WPARAM
)index
, (LPARAM
)height
);
1692 /***********************************************************************
1693 * COMBO_SelectString
1695 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1697 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
) :
1698 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
);
1701 if( lphc
->wState
& CBF_EDIT
)
1702 CBUpdateEdit( lphc
, index
);
1705 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1708 return (LRESULT
)index
;
1711 /***********************************************************************
1714 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1718 HWND hWnd
= lphc
->self
;
1720 pt
.x
= LOWORD(lParam
);
1721 pt
.y
= HIWORD(lParam
);
1722 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1724 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1725 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1727 lphc
->wState
|= CBF_BUTTONDOWN
;
1728 if( lphc
->wState
& CBF_DROPPED
)
1730 /* got a click to cancel selection */
1732 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1733 CBRollUp( lphc
, TRUE
, FALSE
);
1734 if( !IsWindow( hWnd
) ) return;
1736 if( lphc
->wState
& CBF_CAPTURE
)
1738 lphc
->wState
&= ~CBF_CAPTURE
;
1744 /* drop down the listbox and start tracking */
1746 lphc
->wState
|= CBF_CAPTURE
;
1750 if( bButton
) CBRepaintButton( lphc
);
1754 /***********************************************************************
1757 * Release capture and stop tracking if needed.
1759 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1761 if( lphc
->wState
& CBF_CAPTURE
)
1763 lphc
->wState
&= ~CBF_CAPTURE
;
1764 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1766 INT index
= CBUpdateLBox( lphc
, TRUE
);
1767 /* Update edit only if item is in the list */
1770 lphc
->wState
|= CBF_NOLBSELECT
;
1771 CBUpdateEdit( lphc
, index
);
1772 lphc
->wState
&= ~CBF_NOLBSELECT
;
1776 SetCapture(lphc
->hWndLBox
);
1779 if( lphc
->wState
& CBF_BUTTONDOWN
)
1781 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1782 CBRepaintButton( lphc
);
1786 /***********************************************************************
1789 * Two things to do - track combo button and release capture when
1790 * pointer goes into the listbox.
1792 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1797 pt
.x
= LOWORD(lParam
);
1798 pt
.y
= HIWORD(lParam
);
1800 if( lphc
->wState
& CBF_BUTTONDOWN
)
1804 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1808 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1809 CBRepaintButton( lphc
);
1813 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1814 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1815 if( PtInRect(&lbRect
, pt
) )
1817 lphc
->wState
&= ~CBF_CAPTURE
;
1819 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1821 /* hand over pointer tracking */
1822 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1826 static LRESULT
COMBO_GetComboBoxInfo(LPHEADCOMBO lphc
, COMBOBOXINFO
*pcbi
)
1828 if (!pcbi
|| (pcbi
->cbSize
< sizeof(COMBOBOXINFO
)))
1831 pcbi
->rcItem
= lphc
->textRect
;
1832 pcbi
->rcButton
= lphc
->buttonRect
;
1833 pcbi
->stateButton
= 0;
1834 if (lphc
->wState
& CBF_BUTTONDOWN
)
1835 pcbi
->stateButton
|= STATE_SYSTEM_PRESSED
;
1836 if (IsRectEmpty(&lphc
->buttonRect
))
1837 pcbi
->stateButton
|= STATE_SYSTEM_INVISIBLE
;
1838 pcbi
->hwndCombo
= lphc
->self
;
1839 pcbi
->hwndItem
= lphc
->hWndEdit
;
1840 pcbi
->hwndList
= lphc
->hWndLBox
;
1844 /***********************************************************************
1845 * ComboWndProc_common
1847 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1849 static LRESULT
ComboWndProc_common( HWND hwnd
, UINT message
,
1850 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1852 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongPtrW( hwnd
, 0 );
1854 //TRACE("[%p]: msg %s wp %08x lp %08lx\n",
1855 // hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1857 if( lphc
|| message
== WM_NCCREATE
)
1861 /* System messages */
1865 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1866 ((LPCREATESTRUCTA
)lParam
)->style
;
1867 return COMBO_NCCreate(hwnd
, style
);
1870 COMBO_NCDestroy(lphc
);
1871 break;/* -> DefWindowProc */
1879 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1880 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1884 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1885 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1887 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1890 case WM_PRINTCLIENT
:
1891 if (lParam
& PRF_ERASEBKGND
)
1892 COMBO_EraseBackground(hwnd
, lphc
, (HDC
)wParam
);
1896 /* wParam may contain a valid HDC! */
1897 return COMBO_Paint(lphc
, (HDC
)wParam
);
1899 return COMBO_EraseBackground(hwnd
, lphc
, (HDC
)wParam
);
1902 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1903 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1905 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1907 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1908 result
|= DLGC_WANTMESSAGE
;
1912 case WM_WINDOWPOSCHANGING
:
1913 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1914 case WM_WINDOWPOSCHANGED
:
1915 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1916 * In that case, the Combobox itself will not be resized, so we won't
1917 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1922 if( lphc
->hWndLBox
&&
1923 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1926 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1929 return (LRESULT
)lphc
->hFont
;
1931 if( lphc
->wState
& CBF_EDIT
)
1932 SetFocus( lphc
->hWndEdit
);
1934 COMBO_SetFocus( lphc
);
1939 HWND hwndFocus
= (HWND
)wParam
;
1941 HWND hwndFocus
= WIN_GetFullHandle( (HWND
)wParam
);
1944 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1945 COMBO_KillFocus( lphc
);
1950 return COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1952 return COMBO_Command( lphc
, wParam
, WIN_GetFullHandle( (HWND
)lParam
) );
1955 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1956 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1958 case WM_GETTEXTLENGTH
:
1960 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1962 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1963 if (j
== -1) return 0;
1964 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1965 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1967 else if( lphc
->wState
& CBF_EDIT
)
1970 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1971 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1972 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1973 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1980 if( lphc
->wState
& CBF_EDIT
)
1982 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1983 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1989 case WM_COMPAREITEM
:
1990 case WM_MEASUREITEM
:
1991 return COMBO_ItemOp(lphc
, message
, lParam
);
1993 if( lphc
->wState
& CBF_EDIT
)
1994 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1995 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1997 /* Force the control to repaint when the enabled state changes. */
1998 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2002 lphc
->wState
&= ~CBF_NOREDRAW
;
2004 lphc
->wState
|= CBF_NOREDRAW
;
2006 if( lphc
->wState
& CBF_EDIT
)
2007 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
2008 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
2011 if( KEYDATA_ALT
& HIWORD(lParam
) )
2012 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
2013 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2022 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
2023 (lphc
->wState
& CBF_DROPPED
))
2025 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
2028 else if ((wParam
== VK_F4
) && !(lphc
->wState
& CBF_EUI
))
2030 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2034 if( lphc
->wState
& CBF_EDIT
)
2035 hwndTarget
= lphc
->hWndEdit
;
2037 hwndTarget
= lphc
->hWndLBox
;
2039 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
2040 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
2042 case WM_LBUTTONDOWN
:
2043 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
2044 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
2047 COMBO_LButtonUp( lphc
);
2050 if( lphc
->wState
& CBF_CAPTURE
)
2051 COMBO_MouseMove( lphc
, wParam
, lParam
);
2055 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2056 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2057 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2059 if (GET_WHEEL_DELTA_WPARAM(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
2060 if (GET_WHEEL_DELTA_WPARAM(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2063 /* Combo messages */
2066 case CB_ADDSTRING16
:
2067 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2072 UINT msg
= LB_ADDSTRING
;
2073 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2074 msg
= LB_ADDSTRING_LOWER
;
2075 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2076 msg
= LB_ADDSTRING_UPPER
;
2078 return SendMessageW(lphc
->hWndLBox
, msg
, 0, lParam
);
2080 return SendMessageA(lphc
->hWndLBox
, msg
, 0, lParam
);
2083 case CB_INSERTSTRING16
:
2084 wParam
= (INT
)(INT16
)wParam
;
2085 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2088 case CB_INSERTSTRING
:
2090 UINT msg
= LB_INSERTSTRING
;
2091 if( lphc
->dwStyle
& CBS_LOWERCASE
)
2092 msg
= LB_INSERTSTRING_LOWER
;
2093 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
2094 msg
= LB_INSERTSTRING_UPPER
;
2096 return SendMessageW(lphc
->hWndLBox
, msg
, 0, lParam
);
2098 return SendMessageA(lphc
->hWndLBox
, msg
, 0, lParam
);
2101 case CB_DELETESTRING16
:
2103 case CB_DELETESTRING
:
2104 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2105 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2107 case CB_SELECTSTRING16
:
2108 wParam
= (INT
)(INT16
)wParam
;
2109 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2112 case CB_SELECTSTRING
:
2113 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2115 case CB_FINDSTRING16
:
2116 wParam
= (INT
)(INT16
)wParam
;
2117 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2121 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2122 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2124 case CB_FINDSTRINGEXACT16
:
2125 wParam
= (INT
)(INT16
)wParam
;
2126 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2129 case CB_FINDSTRINGEXACT
:
2130 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2131 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2133 case CB_SETITEMHEIGHT16
:
2134 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
2137 case CB_SETITEMHEIGHT
:
2138 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2140 case CB_GETITEMHEIGHT16
:
2141 wParam
= (INT
)(INT16
)wParam
;
2144 case CB_GETITEMHEIGHT
:
2145 if( (INT
)wParam
>= 0 ) /* listbox item */
2146 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2147 return CBGetTextAreaHeight(hwnd
, lphc
);
2149 case CB_RESETCONTENT16
:
2151 case CB_RESETCONTENT
:
2152 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2153 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2155 static const WCHAR empty_stringW
[] = { 0 };
2156 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2159 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2161 case CB_INITSTORAGE
:
2162 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2163 case CB_GETHORIZONTALEXTENT
:
2164 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2165 case CB_SETHORIZONTALEXTENT
:
2166 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2167 case CB_GETTOPINDEX
:
2168 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2170 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2172 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2173 case CB_GETDROPPEDWIDTH
:
2174 if( lphc
->droppedWidth
)
2175 return lphc
->droppedWidth
;
2176 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2177 case CB_SETDROPPEDWIDTH
:
2178 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
2179 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
2182 case CB_GETDROPPEDCONTROLRECT16
:
2183 lParam
= (LPARAM
)MapSL(lParam
);
2187 CBGetDroppedControlRect( lphc
, &r
);
2188 CONV_RECT32TO16( &r
, (LPRECT16
)lParam
);
2192 case CB_GETDROPPEDCONTROLRECT
:
2193 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2196 case CB_GETDROPPEDSTATE16
:
2198 case CB_GETDROPPEDSTATE
:
2199 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
2202 return SendMessageA(lphc
->hWndLBox
, LB_DIR16
, wParam
, lParam
);
2205 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
) :
2206 SendMessageA(lphc
->hWndLBox
, LB_DIR
, wParam
, lParam
);
2209 case CB_SHOWDROPDOWN16
:
2211 case CB_SHOWDROPDOWN
:
2212 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2216 if( !(lphc
->wState
& CBF_DROPPED
) )
2220 if( lphc
->wState
& CBF_DROPPED
)
2221 CBRollUp( lphc
, FALSE
, TRUE
);
2228 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2230 case CB_GETCURSEL16
:
2233 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2235 case CB_SETCURSEL16
:
2236 wParam
= (INT
)(INT16
)wParam
;
2240 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2242 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2244 /* no LBN_SELCHANGE in this case, update manually */
2245 if( lphc
->wState
& CBF_EDIT
)
2246 CBUpdateEdit( lphc
, (INT
)wParam
);
2248 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2249 lphc
->wState
&= ~CBF_SELCHANGE
;
2252 case CB_GETLBTEXT16
:
2253 wParam
= (INT
)(INT16
)wParam
;
2254 lParam
= (LPARAM
)MapSL(lParam
);
2258 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2259 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2261 case CB_GETLBTEXTLEN16
:
2262 wParam
= (INT
)(INT16
)wParam
;
2265 case CB_GETLBTEXTLEN
:
2266 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2267 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2269 case CB_GETITEMDATA16
:
2270 wParam
= (INT
)(INT16
)wParam
;
2273 case CB_GETITEMDATA
:
2274 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2276 case CB_SETITEMDATA16
:
2277 wParam
= (INT
)(INT16
)wParam
;
2280 case CB_SETITEMDATA
:
2281 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2283 case CB_GETEDITSEL16
:
2284 wParam
= lParam
= 0; /* just in case */
2288 /* Edit checks passed parameters itself */
2289 if( lphc
->wState
& CBF_EDIT
)
2290 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2293 case CB_SETEDITSEL16
:
2296 if( lphc
->wState
& CBF_EDIT
)
2297 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2298 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2301 case CB_SETEXTENDEDUI16
:
2303 case CB_SETEXTENDEDUI
:
2304 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2307 lphc
->wState
|= CBF_EUI
;
2308 else lphc
->wState
&= ~CBF_EUI
;
2311 case CB_GETEXTENDEDUI16
:
2313 case CB_GETEXTENDEDUI
:
2314 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2315 case CB_GETCOMBOBOXINFO
:
2316 return COMBO_GetComboBoxInfo(lphc
, (COMBOBOXINFO
*)lParam
);
2318 if( lphc
->wState
& CBF_EDIT
)
2319 return SendMessageW(lphc
->hWndEdit
, EM_LIMITTEXT
, wParam
, lParam
);
2322 if (message
>= WM_USER
)
2323 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2324 message
- WM_USER
, wParam
, lParam
);
2327 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2328 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2331 /***********************************************************************
2334 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2337 static LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2339 if (!IsWindow(hwnd
)) return 0;
2340 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, FALSE
);
2343 /***********************************************************************
2346 static LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2348 if (!IsWindow(hwnd
)) return 0;
2349 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, TRUE
);
2352 /*************************************************************************
2353 * GetComboBoxInfo (USER32.@)
2355 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2356 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)
2358 return SendMessageW(hwndCombo
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)pcbi
);