4 * Copyright 1997 Alex Korobka
6 * FIXME: roll up in Netscape 3.01.
12 #include "wine/winuser16.h"
13 #include "sysmetrics.h"
23 /* bits in the dwKeyData */
24 #define KEYDATA_ALT 0x2000
25 #define KEYDATA_PREVSTATE 0x4000
28 * Additional combo box definitions
31 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
32 #define CB_NOTIFY( lphc, code ) \
33 (SendMessageA( (lphc)->owner, WM_COMMAND, \
34 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
35 #define CB_GETEDITTEXTLENGTH( lphc ) \
36 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
41 static HBITMAP hComboBmp
= 0;
42 static UINT CBitHeight
, CBitWidth
;
45 * Look and feel dependant "constants"
47 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
48 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
49 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
50 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
52 /***********************************************************************
55 * Load combo button bitmap.
57 static BOOL
COMBO_Init()
61 if( hComboBmp
) return TRUE
;
62 if( (hDC
= CreateCompatibleDC(0)) )
65 if( (hComboBmp
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO
))) )
71 GetObjectA( hComboBmp
, sizeof(bm
), &bm
);
72 CBitHeight
= bm
.bmHeight
;
73 CBitWidth
= bm
.bmWidth
;
75 TRACE(combo
, "combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
77 hPrevB
= SelectObject16( hDC
, hComboBmp
);
78 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
79 InvertRect( hDC
, &r
);
80 SelectObject( hDC
, hPrevB
);
89 /***********************************************************************
92 static LRESULT
COMBO_NCCreate(WND
* wnd
, LPARAM lParam
)
96 if ( wnd
&& COMBO_Init() &&
97 (lphc
= HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO
))) )
99 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
101 memset( lphc
, 0, sizeof(HEADCOMBO
) );
102 *(LPHEADCOMBO
*)wnd
->wExtra
= lphc
;
104 /* some braindead apps do try to use scrollbar/border flags */
106 lphc
->dwStyle
= (lpcs
->style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
));
107 wnd
->dwStyle
&= ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
110 * We also have to remove the client edge style to make sure
111 * we don't end-up with a non client area.
113 wnd
->dwExStyle
&= ~(WS_EX_CLIENTEDGE
);
115 if( !(lpcs
->style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
116 lphc
->dwStyle
|= CBS_HASSTRINGS
;
117 if( !(wnd
->dwExStyle
& WS_EX_NOPARENTNOTIFY
) )
118 lphc
->wState
|= CBF_NOTIFY
;
120 TRACE(combo
, "[0x%08x], style = %08x\n",
121 (UINT
)lphc
, lphc
->dwStyle
);
123 return (LRESULT
)(UINT
)wnd
->hwndSelf
;
125 return (LRESULT
)FALSE
;
128 /***********************************************************************
131 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
136 WND
* wnd
= lphc
->self
;
138 TRACE(combo
,"[%04x]: freeing storage\n", CB_HWND(lphc
));
140 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
141 DestroyWindow( lphc
->hWndLBox
);
143 HeapFree( GetProcessHeap(), 0, lphc
);
149 /***********************************************************************
152 * The dummy resize is used for listboxes that have a popup to trigger
153 * a re-arranging of the contents of the combobox and the recalculation
154 * of the size of the "real" control window.
156 static void CBForceDummyResize(
161 GetWindowRect(hwnd
, &windowRect
);
166 windowRect
.right
- windowRect
.left
,
167 windowRect
.bottom
- windowRect
.top
+ 1, /* dummy value adjusted later */
168 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
171 /***********************************************************************
172 * CBGetTextAreaHeight
174 * This method will calculate the height of the text area of the
176 * The height of the text area is set in two ways.
177 * It can be set explicitely through a combobox message of through a
178 * WM_MEASUREITEM callback.
179 * If this is not the case, the height is set to 13 dialog units.
180 * This height was determined through experimentation.
182 static INT
CBGetTextAreaHeight(
188 if( lphc
->editHeight
) /* explicitly set height */
190 iTextItemHeight
= lphc
->editHeight
;
195 HDC hDC
= GetDC(hwnd
);
200 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
202 GetTextMetricsA(hDC
, &tm
);
204 baseUnitY
= tm
.tmHeight
;
207 SelectObject( hDC
, hPrevFont
);
209 ReleaseDC(hwnd
, hDC
);
211 iTextItemHeight
= ((13 * baseUnitY
) / 8);
214 * This "formula" calculates the height of the complete control.
215 * To calculate the height of the text area, we have to remove the
218 iTextItemHeight
-= 2*COMBO_YBORDERSIZE();
222 * Check the ownerdraw case if we haven't asked the parent the size
225 if ( CB_OWNERDRAWN(lphc
) &&
226 (lphc
->wState
& CBF_MEASUREITEM
) )
228 MEASUREITEMSTRUCT measureItem
;
230 INT originalItemHeight
= iTextItemHeight
;
233 * We use the client rect for the width of the item.
235 GetClientRect(hwnd
, &clientRect
);
237 lphc
->wState
&= ~CBF_MEASUREITEM
;
240 * Send a first one to measure the size of the text area
242 measureItem
.CtlType
= ODT_COMBOBOX
;
243 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
244 measureItem
.itemID
= -1;
245 measureItem
.itemWidth
= clientRect
.right
;
246 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
247 measureItem
.itemData
= 0;
248 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
249 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
250 iTextItemHeight
= 6 + measureItem
.itemHeight
;
253 * Send a second one in the case of a fixed ownerdraw list to calculate the
254 * size of the list items. (we basically do this on behalf of the listbox)
256 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
258 measureItem
.CtlType
= ODT_COMBOBOX
;
259 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
260 measureItem
.itemID
= 0;
261 measureItem
.itemWidth
= clientRect
.right
;
262 measureItem
.itemHeight
= originalItemHeight
;
263 measureItem
.itemData
= 0;
264 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
265 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
266 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
270 * Keep the size for the next time
272 lphc
->editHeight
= iTextItemHeight
;
275 return iTextItemHeight
;
279 /***********************************************************************
282 * Set up component coordinates given valid lphc->RectCombo.
284 static void CBCalcPlacement(
292 * Again, start with the client rectangle.
294 GetClientRect(hwnd
, lprEdit
);
299 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
302 * Chop off the bottom part to fit with the height of the text area.
304 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
307 * The button starts the same vertical position as the text area.
309 CopyRect(lprButton
, lprEdit
);
312 * If the combobox is "simple" there is no button.
314 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
315 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
319 * Let's assume the combobox button is the same width as the
321 * size the button horizontally and cut-off the text area.
323 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
324 lprEdit
->right
= lprButton
->left
;
328 * In the case of a dropdown, there is an additional spacing between the
329 * text area and the button.
331 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
333 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
337 * If we have an edit control, we space it away from the borders slightly.
339 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
341 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
345 * Adjust the size of the listbox popup.
347 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
350 * Use the client rectangle to initialize the listbox rectangle
352 GetClientRect(hwnd
, lprLB
);
355 * Then, chop-off the top part.
357 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
362 * Make sure the dropped width is as large as the combobox itself.
364 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
366 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
369 * In the case of a dropdown, the popup listbox is offset to the right.
370 * so, we want to make sure it's flush with the right side of the
373 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
374 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
377 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
380 TRACE(combo
,"\ttext\t= (%i,%i-%i,%i)\n",
381 lprEdit
->left
, lprEdit
->top
, lprEdit
->right
, lprEdit
->bottom
);
383 TRACE(combo
,"\tbutton\t= (%i,%i-%i,%i)\n",
384 lprButton
->left
, lprButton
->top
, lprButton
->right
, lprButton
->bottom
);
386 TRACE(combo
,"\tlbox\t= (%i,%i-%i,%i)\n",
387 lprLB
->left
, lprLB
->top
, lprLB
->right
, lprLB
->bottom
);
390 /***********************************************************************
391 * CBGetDroppedControlRect
393 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
395 CopyRect(lpRect
, &lphc
->droppedRect
);
398 /***********************************************************************
399 * COMBO_WindowPosChanging
401 static LRESULT
COMBO_WindowPosChanging(
404 WINDOWPOS
* posChanging
)
407 * We need to override the WM_WINDOWPOSCHANGING method to handle all
408 * the non-simple comboboxes. The problem is that those controls are
409 * always the same height. We have to make sure they are not resized
412 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
414 posChanging
->cy
= CBGetTextAreaHeight(hwnd
,lphc
) +
415 2*COMBO_YBORDERSIZE();
421 /***********************************************************************
424 static LRESULT
COMBO_Create( LPHEADCOMBO lphc
, WND
* wnd
, LPARAM lParam
)
426 static char clbName
[] = "ComboLBox";
427 static char editName
[] = "Edit";
429 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
431 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
432 else if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
435 lphc
->owner
= lpcs
->hwndParent
;
438 * The item height and dropped width are not set when the control
441 lphc
->droppedWidth
= lphc
->editHeight
= 0;
444 * The first time we go through, we want to measure the ownerdraw item
446 lphc
->wState
|= CBF_MEASUREITEM
;
448 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
450 if( lphc
->owner
|| !(lpcs
->style
& WS_VISIBLE
) )
455 * Initialize the dropped rect to the size of the client area of the
456 * control and then, force all the areas of the combobox to be
459 GetClientRect( wnd
->hwndSelf
, &lphc
->droppedRect
);
461 CBCalcPlacement(wnd
->hwndSelf
,
465 &lphc
->droppedRect
);
468 * Adjust the position of the popup listbox if it's necessary
470 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
472 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
475 * If it's a dropdown, the listbox is offset
477 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
478 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
480 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
);
481 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
.right
);
484 /* create listbox popup */
486 lbeStyle
= (LBS_NOTIFY
| WS_BORDER
| WS_CLIPSIBLINGS
) |
487 (lpcs
->style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
489 if( lphc
->dwStyle
& CBS_SORT
)
490 lbeStyle
|= LBS_SORT
;
491 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
492 lbeStyle
|= LBS_HASSTRINGS
;
493 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
494 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
495 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
496 lbeStyle
|= LBS_DISABLENOSCROLL
;
498 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
499 lbeStyle
|= WS_CHILD
| WS_VISIBLE
;
500 else /* popup listbox */
501 lbeStyle
|= WS_POPUP
;
503 /* Dropdown ComboLBox is not a child window and we cannot pass
504 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
506 lphc
->hWndLBox
= CreateWindowExA(0,
510 lphc
->droppedRect
.left
,
511 lphc
->droppedRect
.top
,
512 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
513 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
514 lphc
->self
->hwndSelf
,
515 (lphc
->dwStyle
& CBS_DROPDOWN
)? (HMENU
)0 : (HMENU
)ID_CB_LISTBOX
,
516 lphc
->self
->hInstance
,
522 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
;
525 * In Win95 look, the border fo the edit control is
526 * provided by the combobox
528 if (TWEAK_WineLook
== WIN31_LOOK
)
529 lbeStyle
|= WS_BORDER
;
531 if( lphc
->wState
& CBF_EDIT
)
533 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
534 lbeStyle
|= ES_OEMCONVERT
;
535 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
536 lbeStyle
|= ES_AUTOHSCROLL
;
537 if( lphc
->dwStyle
& CBS_LOWERCASE
)
538 lbeStyle
|= ES_LOWERCASE
;
539 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
540 lbeStyle
|= ES_UPPERCASE
;
542 lphc
->hWndEdit
= CreateWindowExA(0,
546 lphc
->textRect
.left
, lphc
->textRect
.top
,
547 lphc
->textRect
.right
- lphc
->textRect
.left
,
548 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
549 lphc
->self
->hwndSelf
,
551 lphc
->self
->hInstance
,
554 if( !lphc
->hWndEdit
)
561 * If the combo is a dropdown, we must resize the control to fit only
562 * the text area and button. To do this, we send a dummy resize and the
563 * WM_WINDOWPOSCHANGING message will take care of setting the height for
566 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
568 CBForceDummyResize(CB_HWND(lphc
));
571 TRACE(combo
,"init done\n");
572 return wnd
->hwndSelf
;
574 ERR(combo
, "edit control failure.\n");
575 } else ERR(combo
, "listbox failure.\n");
576 } else ERR(combo
, "no owner for visible combo.\n");
578 /* CreateWindow() will send WM_NCDESTROY to cleanup */
583 /***********************************************************************
586 * Paint combo button (normal, pressed, and disabled states).
588 static void CBPaintButton(
597 COLORREF oldTextColor
, oldBkColor
;
599 if( lphc
->wState
& CBF_NOREDRAW
)
602 hPrevBrush
= SelectObject(hdc
, GetSysColorBrush(COLOR_BTNFACE
));
605 * Draw the button background
610 rectButton
.right
-rectButton
.left
,
611 rectButton
.bottom
-rectButton
.top
,
614 if( (bBool
= lphc
->wState
& CBF_BUTTONDOWN
) )
616 DrawEdge( hdc
, &rectButton
, EDGE_SUNKEN
, BF_RECT
);
620 DrawEdge( hdc
, &rectButton
, EDGE_RAISED
, BF_RECT
);
624 * Remove the edge of the button from the rectangle
625 * and calculate the position of the bitmap.
627 InflateRect( &rectButton
, -2, -2);
629 x
= (rectButton
.left
+ rectButton
.right
- CBitWidth
) >> 1;
630 y
= (rectButton
.top
+ rectButton
.bottom
- CBitHeight
) >> 1;
633 hMemDC
= CreateCompatibleDC( hdc
);
634 SelectObject( hMemDC
, hComboBmp
);
635 oldTextColor
= SetTextColor( hdc
, GetSysColor(COLOR_BTNFACE
) );
636 oldBkColor
= SetBkColor( hdc
, CB_DISABLED(lphc
) ? RGB(128,128,128) :
638 BitBlt( hdc
, x
, y
, CBitWidth
, CBitHeight
, hMemDC
, 0, 0, SRCCOPY
);
639 SetBkColor( hdc
, oldBkColor
);
640 SetTextColor( hdc
, oldTextColor
);
642 SelectObject( hdc
, hPrevBrush
);
645 /***********************************************************************
648 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
650 static void CBPaintText(
658 if( lphc
->wState
& CBF_NOREDRAW
) return;
660 /* follow Windows combobox that sends a bunch of text
661 * inquiries to its listbox while processing WM_PAINT. */
663 if( (id
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
665 size
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
666 if( (pText
= HeapAlloc( GetProcessHeap(), 0, size
+ 1)) )
668 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
669 pText
[size
] = '\0'; /* just in case */
673 if( lphc
->wState
& CBF_EDIT
)
675 if( CB_HASSTRINGS(lphc
) ) SetWindowTextA( lphc
->hWndEdit
, pText
? pText
: "" );
676 if( lphc
->wState
& CBF_FOCUSED
)
677 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
679 else /* paint text field ourselves */
681 HBRUSH hPrevBrush
= 0;
686 if ((hDC
= GetDC(lphc
->self
->hwndSelf
)))
688 HBRUSH hBrush
= SendMessageA( lphc
->owner
,
690 hDC
, lphc
->self
->hwndSelf
);
691 hPrevBrush
= SelectObject( hDC
,
692 (hBrush
) ? hBrush
: GetStockObject(WHITE_BRUSH
) );
698 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hDC
, lphc
->hFont
) : 0;
701 * Give ourselves some space.
703 InflateRect( &rectEdit
, -1, -1 );
705 if ( (lphc
->wState
& CBF_FOCUSED
) &&
706 !(lphc
->wState
& CBF_DROPPED
) )
710 FillRect( hDC
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
711 SetBkColor( hDC
, GetSysColor( COLOR_HIGHLIGHT
) );
712 SetTextColor( hDC
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
713 itemState
= ODS_SELECTED
| ODS_FOCUS
;
718 if( CB_OWNERDRAWN(lphc
) )
724 * Save the current clip region.
725 * To retrieve the clip region, we need to create one "dummy"
728 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
730 if (GetClipRgn(hDC
, clipRegion
)!=1)
732 DeleteObject(clipRegion
);
733 clipRegion
=(HRGN
)NULL
;
736 if ( lphc
->self
->dwStyle
& WS_DISABLED
)
737 itemState
|= ODS_DISABLED
;
739 dis
.CtlType
= ODT_COMBOBOX
;
740 dis
.CtlID
= lphc
->self
->wIDmenu
;
741 dis
.hwndItem
= lphc
->self
->hwndSelf
;
742 dis
.itemAction
= ODA_DRAWENTIRE
;
744 dis
.itemState
= itemState
;
746 dis
.rcItem
= rectEdit
;
747 dis
.itemData
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
,
751 * Clip the DC and have the parent draw the item.
753 IntersectClipRect(hDC
,
754 rectEdit
.left
, rectEdit
.top
,
755 rectEdit
.right
, rectEdit
.bottom
);
757 SendMessageA(lphc
->owner
, WM_DRAWITEM
,
758 lphc
->self
->wIDmenu
, (LPARAM
)&dis
);
761 * Reset the clipping region.
763 SelectClipRgn(hDC
, clipRegion
);
770 ETO_OPAQUE
| ETO_CLIPPED
,
772 pText
? pText
: "" , size
, NULL
);
774 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
775 DrawFocusRect( hDC
, &rectEdit
);
779 SelectObject(hDC
, hPrevFont
);
784 SelectObject( hDC
, hPrevBrush
);
786 ReleaseDC( lphc
->self
->hwndSelf
, hDC
);
791 HeapFree( GetProcessHeap(), 0, pText
);
794 /***********************************************************************
797 static void CBPaintBorder(
804 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
806 GetClientRect(hwnd
, &clientRect
);
810 CopyRect(&clientRect
, &lphc
->textRect
);
812 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
813 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
816 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
819 /***********************************************************************
820 * COMBO_EraseBackground
822 static LRESULT
COMBO_EraseBackground(
831 hDC
= (hParamDC
) ? hParamDC
835 * Calculate the area that we want to erase.
837 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
839 GetClientRect(hwnd
, &clientRect
);
843 CopyRect(&clientRect
, &lphc
->textRect
);
845 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
848 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
852 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
854 FillRect(hDC
, &clientRect
, hBkgBrush
);
857 ReleaseDC(hwnd
, hDC
);
862 /***********************************************************************
865 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
870 hDC
= (hParamDC
) ? hParamDC
871 : BeginPaint( lphc
->self
->hwndSelf
, &ps
);
874 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
876 HBRUSH hPrevBrush
, hBkgBrush
;
878 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
879 hDC
, lphc
->self
->hwndSelf
);
882 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
884 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
887 * In non 3.1 look, there is a sunken border on the combobox
889 if (TWEAK_WineLook
!= WIN31_LOOK
)
891 CBPaintBorder(CB_HWND(lphc
), lphc
, hDC
);
894 if( !IsRectEmpty(&lphc
->buttonRect
) )
896 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
899 if( !(lphc
->wState
& CBF_EDIT
) )
902 * The text area has a border only in Win 3.1 look.
904 if (TWEAK_WineLook
== WIN31_LOOK
)
906 HPEN hPrevPen
= SelectObject( hDC
, GetSysColorPen(COLOR_WINDOWFRAME
) );
909 lphc
->textRect
.left
, lphc
->textRect
.top
,
910 lphc
->textRect
.right
- 1, lphc
->textRect
.bottom
- 1);
912 SelectObject( hDC
, hPrevPen
);
915 CBPaintText( lphc
, hDC
, lphc
->textRect
);
919 SelectObject( hDC
, hPrevBrush
);
923 EndPaint(lphc
->self
->hwndSelf
, &ps
);
928 /***********************************************************************
931 * Select listbox entry according to the contents of the edit control.
933 static INT
CBUpdateLBox( LPHEADCOMBO lphc
)
935 INT length
, idx
, ret
;
939 length
= CB_GETEDITTEXTLENGTH( lphc
);
942 pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1);
944 TRACE(combo
,"\t edit text length %i\n", length
);
948 if( length
) GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1);
949 else pText
[0] = '\0';
950 idx
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
951 (WPARAM
)(-1), (LPARAM
)pText
);
952 if( idx
== LB_ERR
) idx
= 0; /* select first item */
954 HeapFree( GetProcessHeap(), 0, pText
);
959 SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)idx
, 0 );
963 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)idx
, 0 );
964 /* probably superfluous but Windows sends this too */
965 SendMessageA( lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)idx
, 0 );
970 /***********************************************************************
973 * Copy a listbox entry to the edit control.
975 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
980 TRACE(combo
,"\t %i\n", index
);
984 length
= CB_GETEDITTEXTLENGTH( lphc
);
987 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
989 GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1 );
990 index
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
991 (WPARAM
)(-1), (LPARAM
)pText
);
992 HeapFree( GetProcessHeap(), 0, pText
);
997 if( index
>= 0 ) /* got an entry */
999 length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1002 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1004 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1005 (WPARAM
)index
, (LPARAM
)pText
);
1006 SendMessageA( lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)pText
);
1007 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1008 HeapFree( GetProcessHeap(), 0, pText
);
1014 /***********************************************************************
1017 * Show listbox popup.
1019 static void CBDropDown( LPHEADCOMBO lphc
)
1024 TRACE(combo
,"[%04x]: drop down\n", CB_HWND(lphc
));
1026 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1030 lphc
->wState
|= CBF_DROPPED
;
1031 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1033 index
= CBUpdateLBox( lphc
);
1034 if( !(lphc
->wState
& CBF_CAPTURE
) ) CBUpdateEdit( lphc
, index
);
1038 index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1039 if( index
== LB_ERR
) index
= 0;
1040 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)index
, 0 );
1041 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1044 /* now set popup position */
1045 GetWindowRect( lphc
->self
->hwndSelf
, &rect
);
1049 * If it's a dropdown, the listbox is offset
1051 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1052 rect
.left
+= COMBO_EDITBUTTONSPACE();
1054 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1055 rect
.right
- rect
.left
, rect
.bottom
- rect
.top
,
1056 SWP_NOACTIVATE
| SWP_NOSIZE
| SWP_NOREDRAW
);
1058 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1059 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0, RDW_INVALIDATE
|
1060 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1062 ShowWindow( lphc
->hWndLBox
, SW_SHOWNA
);
1065 /***********************************************************************
1068 * Hide listbox popup.
1070 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1072 HWND hWnd
= lphc
->self
->hwndSelf
;
1074 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1076 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1079 TRACE(combo
,"[%04x]: roll up [%i]\n", CB_HWND(lphc
), (INT
)ok
);
1081 /* always send WM_LBUTTONUP? */
1082 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1084 if( lphc
->wState
& CBF_DROPPED
)
1088 lphc
->wState
&= ~CBF_DROPPED
;
1089 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1091 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1093 INT index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1094 CBUpdateEdit( lphc
, index
);
1095 rect
= lphc
->buttonRect
;
1106 rect
= lphc
->textRect
;
1111 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1112 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1113 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1114 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1119 /***********************************************************************
1122 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1124 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL bRedrawButton
)
1126 if( lphc
->wState
& CBF_DROPPED
)
1128 CBRollUp( lphc
, TRUE
, bRedrawButton
);
1136 /***********************************************************************
1139 * Edit control helper.
1141 HWND
COMBO_GetLBWindow( WND
* pWnd
)
1143 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1144 if( lphc
) return lphc
->hWndLBox
;
1149 /***********************************************************************
1152 static void CBRepaintButton( LPHEADCOMBO lphc
)
1154 InvalidateRect(CB_HWND(lphc
), &lphc
->buttonRect
, TRUE
);
1155 UpdateWindow(CB_HWND(lphc
));
1158 /***********************************************************************
1161 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1163 if( !(lphc
->wState
& CBF_FOCUSED
) )
1165 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1166 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1168 if( lphc
->wState
& CBF_EDIT
)
1169 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1170 lphc
->wState
|= CBF_FOCUSED
;
1171 if( !(lphc
->wState
& CBF_EDIT
) )
1173 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1176 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1180 /***********************************************************************
1183 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1185 HWND hWnd
= lphc
->self
->hwndSelf
;
1187 if( lphc
->wState
& CBF_FOCUSED
)
1189 SendMessageA( hWnd
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1191 CBRollUp( lphc
, FALSE
, TRUE
);
1192 if( IsWindow( hWnd
) )
1194 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1195 SendMessageA( lphc
->hWndLBox
, LB_CARETOFF
, 0, 0 );
1197 lphc
->wState
&= ~CBF_FOCUSED
;
1200 if( lphc
->wState
& CBF_EDIT
)
1201 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, (WPARAM
)(-1), 0 );
1204 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1207 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1212 /***********************************************************************
1215 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1217 if( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1219 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1221 switch( HIWORD(wParam
) >> 8 )
1223 case (EN_SETFOCUS
>> 8):
1225 TRACE(combo
,"[%04x]: edit [%04x] got focus\n",
1226 CB_HWND(lphc
), lphc
->hWndEdit
);
1228 if( !(lphc
->wState
& CBF_FOCUSED
) ) COMBO_SetFocus( lphc
);
1231 case (EN_KILLFOCUS
>> 8):
1233 TRACE(combo
,"[%04x]: edit [%04x] lost focus\n",
1234 CB_HWND(lphc
), lphc
->hWndEdit
);
1236 /* NOTE: it seems that Windows' edit control sends an
1237 * undocumented message WM_USER + 0x1B instead of this
1238 * notification (only when it happens to be a part of
1239 * the combo). ?? - AK.
1242 COMBO_KillFocus( lphc
);
1246 case (EN_CHANGE
>> 8):
1247 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1248 CBUpdateLBox( lphc
);
1251 case (EN_UPDATE
>> 8):
1252 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1255 case (EN_ERRSPACE
>> 8):
1256 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1259 else if( lphc
->hWndLBox
== hWnd
)
1261 switch( HIWORD(wParam
) )
1264 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1268 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1274 TRACE(combo
,"[%04x]: lbox selection change [%04x]\n",
1275 CB_HWND(lphc
), lphc
->wState
);
1277 /* do not roll up if selection is being tracked
1278 * by arrowkeys in the dropdown listbox */
1280 if( (lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
) )
1281 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1282 else lphc
->wState
&= ~CBF_NOROLLUP
;
1284 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1285 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1290 /* nothing to do here since ComboLBox always resets the focus to its
1291 * combo/edit counterpart */
1298 /***********************************************************************
1301 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1303 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
,
1304 WPARAM wParam
, LPARAM lParam
)
1306 HWND hWnd
= lphc
->self
->hwndSelf
;
1308 TRACE(combo
,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc
), msg
);
1310 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1312 /* two first items are the same in all 4 structs */
1313 lpIS
->CtlType
= ODT_COMBOBOX
;
1314 lpIS
->CtlID
= lphc
->self
->wIDmenu
;
1316 switch( msg
) /* patch window handle */
1319 lpIS
->hwndItem
= hWnd
;
1323 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1324 lpIS
->hwndItem
= hWnd
;
1327 case WM_COMPAREITEM
:
1328 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1329 lpIS
->hwndItem
= hWnd
;
1334 return SendMessageA( lphc
->owner
, msg
, lphc
->self
->wIDmenu
, lParam
);
1337 /***********************************************************************
1340 static LRESULT
COMBO_GetText( LPHEADCOMBO lphc
, UINT N
, LPSTR lpText
)
1342 if( lphc
->wState
& CBF_EDIT
)
1343 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
,
1344 (WPARAM
)N
, (LPARAM
)lpText
);
1346 /* get it from the listbox */
1348 if( lphc
->hWndLBox
)
1350 INT idx
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1354 INT length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
,
1357 /* 'length' is without the terminating character */
1359 lpBuffer
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1 );
1365 INT n
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1366 (WPARAM
)idx
, (LPARAM
)lpBuffer
);
1368 /* truncate if buffer is too short */
1373 if( n
!= LB_ERR
) memcpy( lpText
, lpBuffer
, (N
>n
) ? n
+1 : N
-1 );
1374 lpText
[N
- 1] = '\0';
1376 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1386 /***********************************************************************
1389 * This function sets window positions according to the updated
1390 * component placement struct.
1392 static void CBResetPos(
1398 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1400 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1401 * sizing messages */
1403 if( lphc
->wState
& CBF_EDIT
)
1404 SetWindowPos( lphc
->hWndEdit
, 0,
1405 rectEdit
->left
, rectEdit
->top
,
1406 rectEdit
->right
- rectEdit
->left
,
1407 rectEdit
->bottom
- rectEdit
->top
,
1408 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1410 SetWindowPos( lphc
->hWndLBox
, 0,
1411 rectLB
->left
, rectLB
->top
,
1412 rectLB
->right
- rectLB
->left
,
1413 rectLB
->bottom
- rectLB
->top
,
1414 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1418 if( lphc
->wState
& CBF_DROPPED
)
1420 lphc
->wState
&= ~CBF_DROPPED
;
1421 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1424 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1425 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
1426 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1431 /***********************************************************************
1434 static void COMBO_Size( LPHEADCOMBO lphc
)
1436 CBCalcPlacement(lphc
->self
->hwndSelf
,
1440 &lphc
->droppedRect
);
1442 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1446 /***********************************************************************
1449 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1454 lphc
->hFont
= hFont
;
1457 * Propagate to owned windows.
1459 if( lphc
->wState
& CBF_EDIT
)
1460 SendMessageA( lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1461 SendMessageA( lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1464 * Redo the layout of the control.
1466 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1468 CBCalcPlacement(lphc
->self
->hwndSelf
,
1472 &lphc
->droppedRect
);
1474 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1478 CBForceDummyResize(CB_HWND(lphc
));
1483 /***********************************************************************
1484 * COMBO_SetItemHeight
1486 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1488 LRESULT lRet
= CB_ERR
;
1490 if( index
== -1 ) /* set text field height */
1492 if( height
< 32768 )
1494 lphc
->editHeight
= height
;
1497 * Redo the layout of the control.
1499 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1501 CBCalcPlacement(lphc
->self
->hwndSelf
,
1505 &lphc
->droppedRect
);
1507 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1511 CBForceDummyResize(CB_HWND(lphc
));
1517 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1518 lRet
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1519 (WPARAM
)index
, (LPARAM
)height
);
1523 /***********************************************************************
1524 * COMBO_SelectString
1526 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPCSTR pText
)
1528 INT index
= SendMessageA( lphc
->hWndLBox
, LB_SELECTSTRING
,
1529 (WPARAM
)start
, (LPARAM
)pText
);
1532 if( lphc
->wState
& CBF_EDIT
)
1533 CBUpdateEdit( lphc
, index
);
1536 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1539 return (LRESULT
)index
;
1542 /***********************************************************************
1545 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1547 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
1549 HWND hWnd
= lphc
->self
->hwndSelf
;
1551 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1553 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1554 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1556 lphc
->wState
|= CBF_BUTTONDOWN
;
1557 if( lphc
->wState
& CBF_DROPPED
)
1559 /* got a click to cancel selection */
1561 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1562 CBRollUp( lphc
, TRUE
, FALSE
);
1563 if( !IsWindow( hWnd
) ) return;
1565 if( lphc
->wState
& CBF_CAPTURE
)
1567 lphc
->wState
&= ~CBF_CAPTURE
;
1573 /* drop down the listbox and start tracking */
1575 lphc
->wState
|= CBF_CAPTURE
;
1579 if( bButton
) CBRepaintButton( lphc
);
1583 /***********************************************************************
1586 * Release capture and stop tracking if needed.
1588 static void COMBO_LButtonUp( LPHEADCOMBO lphc
, LPARAM lParam
)
1590 if( lphc
->wState
& CBF_CAPTURE
)
1592 lphc
->wState
&= ~CBF_CAPTURE
;
1593 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1595 INT index
= CBUpdateLBox( lphc
);
1596 CBUpdateEdit( lphc
, index
);
1601 if( lphc
->wState
& CBF_BUTTONDOWN
)
1603 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1604 CBRepaintButton( lphc
);
1608 /***********************************************************************
1611 * Two things to do - track combo button and release capture when
1612 * pointer goes into the listbox.
1614 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1616 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
1619 if( lphc
->wState
& CBF_BUTTONDOWN
)
1623 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1627 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1628 CBRepaintButton( lphc
);
1632 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1633 MapWindowPoints( lphc
->self
->hwndSelf
, lphc
->hWndLBox
, &pt
, 1 );
1634 if( PtInRect(&lbRect
, pt
) )
1636 lphc
->wState
&= ~CBF_CAPTURE
;
1638 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
);
1640 /* hand over pointer tracking */
1641 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1646 /***********************************************************************
1649 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1651 LRESULT WINAPI
ComboWndProc( HWND hwnd
, UINT message
,
1652 WPARAM wParam
, LPARAM lParam
)
1655 WND
* pWnd
= WIN_FindWndPtr(hwnd
);
1659 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1661 TRACE(combo
, "[%04x]: msg %s wp %08x lp %08lx\n",
1662 pWnd
->hwndSelf
, SPY_GetMsgName(message
), wParam
, lParam
);
1664 if( lphc
|| message
== WM_NCCREATE
)
1668 /* System messages */
1671 retvalue
= COMBO_NCCreate(pWnd
, lParam
);
1674 COMBO_NCDestroy(lphc
);
1678 retvalue
= COMBO_Create(lphc
, pWnd
, lParam
);
1681 case WM_PRINTCLIENT
:
1682 if (lParam
& PRF_ERASEBKGND
)
1683 COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1687 /* wParam may contain a valid HDC! */
1688 retvalue
= COMBO_Paint(lphc
, wParam
);
1691 retvalue
= COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1694 retvalue
= (LRESULT
)(DLGC_WANTARROWS
| DLGC_WANTCHARS
);
1696 case WM_WINDOWPOSCHANGING
:
1697 retvalue
= COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1700 if( lphc
->hWndLBox
&&
1701 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1705 COMBO_Font( lphc
, (HFONT16
)wParam
, (BOOL
)lParam
);
1709 retvalue
= (LRESULT
)lphc
->hFont
;
1712 if( lphc
->wState
& CBF_EDIT
)
1713 SetFocus( lphc
->hWndEdit
);
1715 COMBO_SetFocus( lphc
);
1719 #define hwndFocus ((HWND16)wParam)
1721 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1722 COMBO_KillFocus( lphc
);
1727 retvalue
= COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1730 retvalue
= COMBO_GetText( lphc
, (UINT
)wParam
, (LPSTR
)lParam
);
1733 case WM_GETTEXTLENGTH
:
1738 if( lphc
->wState
& CBF_EDIT
)
1740 retvalue
= SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1747 case WM_COMPAREITEM
:
1748 case WM_MEASUREITEM
:
1749 retvalue
= COMBO_ItemOp( lphc
, message
, wParam
, lParam
);
1752 if( lphc
->wState
& CBF_EDIT
)
1753 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1754 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1759 lphc
->wState
&= ~CBF_NOREDRAW
;
1761 lphc
->wState
|= CBF_NOREDRAW
;
1763 if( lphc
->wState
& CBF_EDIT
)
1764 SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1765 SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1769 if( KEYDATA_ALT
& HIWORD(lParam
) )
1770 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1771 COMBO_FlipListbox( lphc
, TRUE
);
1776 if( lphc
->wState
& CBF_EDIT
)
1777 retvalue
= SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1779 retvalue
= SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1781 case WM_LBUTTONDOWN
:
1782 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
->hwndSelf
);
1783 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1787 COMBO_LButtonUp( lphc
, lParam
);
1791 if( lphc
->wState
& CBF_CAPTURE
)
1792 COMBO_MouseMove( lphc
, wParam
, lParam
);
1795 /* Combo messages */
1797 case CB_ADDSTRING16
:
1798 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1800 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1802 case CB_INSERTSTRING16
:
1803 wParam
= (INT
)(INT16
)wParam
;
1804 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1805 case CB_INSERTSTRING
:
1806 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1808 case CB_DELETESTRING16
:
1809 case CB_DELETESTRING
:
1810 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1812 case CB_SELECTSTRING16
:
1813 wParam
= (INT
)(INT16
)wParam
;
1814 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1815 case CB_SELECTSTRING
:
1816 retvalue
= COMBO_SelectString( lphc
, (INT
)wParam
, (LPSTR
)lParam
);
1818 case CB_FINDSTRING16
:
1819 wParam
= (INT
)(INT16
)wParam
;
1820 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1822 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
1824 case CB_FINDSTRINGEXACT16
:
1825 wParam
= (INT
)(INT16
)wParam
;
1826 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1827 case CB_FINDSTRINGEXACT
:
1828 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRINGEXACT
,
1831 case CB_SETITEMHEIGHT16
:
1832 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
1833 case CB_SETITEMHEIGHT
:
1834 retvalue
= COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
1836 case CB_GETITEMHEIGHT16
:
1837 wParam
= (INT
)(INT16
)wParam
;
1838 case CB_GETITEMHEIGHT
:
1839 if( (INT
)wParam
>= 0 ) /* listbox item */
1841 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
1844 retvalue
= CBGetTextAreaHeight(hwnd
, lphc
);
1846 case CB_RESETCONTENT16
:
1847 case CB_RESETCONTENT
:
1848 SendMessageA( lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0 );
1849 InvalidateRect(CB_HWND(lphc
), NULL
, TRUE
);
1852 case CB_INITSTORAGE
:
1853 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
1855 case CB_GETHORIZONTALEXTENT
:
1856 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
1858 case CB_SETHORIZONTALEXTENT
:
1859 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
1861 case CB_GETTOPINDEX
:
1862 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
1865 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
1868 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
1870 case CB_GETDROPPEDWIDTH
:
1871 if( lphc
->droppedWidth
)
1873 retvalue
= lphc
->droppedWidth
;
1876 retvalue
= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1878 case CB_SETDROPPEDWIDTH
:
1879 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
1880 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
1883 case CB_GETDROPPEDCONTROLRECT16
:
1884 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1888 CBGetDroppedControlRect( lphc
, &r
);
1889 CONV_RECT32TO16( &r
, (LPRECT16
)lParam
);
1893 case CB_GETDROPPEDCONTROLRECT
:
1894 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
1897 case CB_GETDROPPEDSTATE16
:
1898 case CB_GETDROPPEDSTATE
:
1899 retvalue
= (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
1902 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1905 retvalue
= COMBO_Directory( lphc
, (UINT
)wParam
,
1906 (LPSTR
)lParam
, (message
== CB_DIR
));
1908 case CB_SHOWDROPDOWN16
:
1909 case CB_SHOWDROPDOWN
:
1910 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1914 if( !(lphc
->wState
& CBF_DROPPED
) )
1918 if( lphc
->wState
& CBF_DROPPED
)
1919 CBRollUp( lphc
, FALSE
, TRUE
);
1925 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1927 case CB_GETCURSEL16
:
1929 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1931 case CB_SETCURSEL16
:
1932 wParam
= (INT
)(INT16
)wParam
;
1934 lParam
= SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
1935 if( lphc
->wState
& CBF_SELCHANGE
)
1937 /* no LBN_SELCHANGE in this case, update manually */
1938 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1939 lphc
->wState
&= ~CBF_SELCHANGE
;
1943 case CB_GETLBTEXT16
:
1944 wParam
= (INT
)(INT16
)wParam
;
1945 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1947 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
1949 case CB_GETLBTEXTLEN16
:
1950 wParam
= (INT
)(INT16
)wParam
;
1951 case CB_GETLBTEXTLEN
:
1952 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
1954 case CB_GETITEMDATA16
:
1955 wParam
= (INT
)(INT16
)wParam
;
1956 case CB_GETITEMDATA
:
1957 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
1959 case CB_SETITEMDATA16
:
1960 wParam
= (INT
)(INT16
)wParam
;
1961 case CB_SETITEMDATA
:
1962 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
1964 case CB_GETEDITSEL16
:
1965 wParam
= lParam
= 0; /* just in case */
1967 if( lphc
->wState
& CBF_EDIT
)
1971 retvalue
= SendMessageA( lphc
->hWndEdit
, EM_GETSEL
,
1972 (wParam
) ? wParam
: (WPARAM
)&a
,
1973 (lParam
) ? lParam
: (LPARAM
)&b
);
1978 case CB_SETEDITSEL16
:
1980 if( lphc
->wState
& CBF_EDIT
)
1982 retvalue
= SendMessageA( lphc
->hWndEdit
, EM_SETSEL
,
1983 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
1988 case CB_SETEXTENDEDUI16
:
1989 case CB_SETEXTENDEDUI
:
1990 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1996 lphc
->wState
|= CBF_EUI
;
1997 else lphc
->wState
&= ~CBF_EUI
;
2000 case CB_GETEXTENDEDUI16
:
2001 case CB_GETEXTENDEDUI
:
2002 retvalue
= (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2004 case (WM_USER
+ 0x1B):
2005 WARN(combo
, "[%04x]: undocumented msg!\n", hwnd
);
2007 retvalue
= DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2012 WIN_ReleaseWndPtr(pWnd
);