4 * Copyright 1997 Alex Korobka
6 * FIXME: roll up in Netscape 3.01.
12 #include "wine/winuser16.h"
13 #include "sysmetrics.h"
23 DEFAULT_DEBUG_CHANNEL(combo
)
25 /* bits in the dwKeyData */
26 #define KEYDATA_ALT 0x2000
27 #define KEYDATA_PREVSTATE 0x4000
30 * Additional combo box definitions
33 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
34 #define CB_NOTIFY( lphc, code ) \
35 (SendMessageA( (lphc)->owner, WM_COMMAND, \
36 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
37 #define CB_GETEDITTEXTLENGTH( lphc ) \
38 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
43 static HBITMAP hComboBmp
= 0;
44 static UINT CBitHeight
, CBitWidth
;
47 * Look and feel dependant "constants"
49 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
50 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
51 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
52 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
54 /***********************************************************************
57 * Load combo button bitmap.
59 static BOOL
COMBO_Init()
63 if( hComboBmp
) return TRUE
;
64 if( (hDC
= CreateCompatibleDC(0)) )
67 if( (hComboBmp
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO
))) )
73 GetObjectA( hComboBmp
, sizeof(bm
), &bm
);
74 CBitHeight
= bm
.bmHeight
;
75 CBitWidth
= bm
.bmWidth
;
77 TRACE(combo
, "combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
79 hPrevB
= SelectObject16( hDC
, hComboBmp
);
80 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
81 InvertRect( hDC
, &r
);
82 SelectObject( hDC
, hPrevB
);
91 /***********************************************************************
94 static LRESULT
COMBO_NCCreate(WND
* wnd
, LPARAM lParam
)
98 if ( wnd
&& COMBO_Init() &&
99 (lphc
= HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO
))) )
101 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
103 memset( lphc
, 0, sizeof(HEADCOMBO
) );
104 *(LPHEADCOMBO
*)wnd
->wExtra
= lphc
;
106 /* some braindead apps do try to use scrollbar/border flags */
108 lphc
->dwStyle
= (lpcs
->style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
));
109 wnd
->dwStyle
&= ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
112 * We also have to remove the client edge style to make sure
113 * we don't end-up with a non client area.
115 wnd
->dwExStyle
&= ~(WS_EX_CLIENTEDGE
);
117 if( !(lpcs
->style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
118 lphc
->dwStyle
|= CBS_HASSTRINGS
;
119 if( !(wnd
->dwExStyle
& WS_EX_NOPARENTNOTIFY
) )
120 lphc
->wState
|= CBF_NOTIFY
;
122 TRACE(combo
, "[0x%08x], style = %08x\n",
123 (UINT
)lphc
, lphc
->dwStyle
);
125 return (LRESULT
)(UINT
)wnd
->hwndSelf
;
127 return (LRESULT
)FALSE
;
130 /***********************************************************************
133 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
138 WND
* wnd
= lphc
->self
;
140 TRACE(combo
,"[%04x]: freeing storage\n", CB_HWND(lphc
));
142 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
143 DestroyWindow( lphc
->hWndLBox
);
145 HeapFree( GetProcessHeap(), 0, lphc
);
151 /***********************************************************************
154 * The dummy resize is used for listboxes that have a popup to trigger
155 * a re-arranging of the contents of the combobox and the recalculation
156 * of the size of the "real" control window.
158 static void CBForceDummyResize(
163 GetWindowRect(hwnd
, &windowRect
);
168 windowRect
.right
- windowRect
.left
,
169 windowRect
.bottom
- windowRect
.top
+ 1, /* dummy value adjusted later */
170 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
173 /***********************************************************************
174 * CBGetTextAreaHeight
176 * This method will calculate the height of the text area of the
178 * The height of the text area is set in two ways.
179 * It can be set explicitely through a combobox message of through a
180 * WM_MEASUREITEM callback.
181 * If this is not the case, the height is set to 13 dialog units.
182 * This height was determined through experimentation.
184 static INT
CBGetTextAreaHeight(
190 if( lphc
->editHeight
) /* explicitly set height */
192 iTextItemHeight
= lphc
->editHeight
;
197 HDC hDC
= GetDC(hwnd
);
202 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
204 GetTextMetricsA(hDC
, &tm
);
206 baseUnitY
= tm
.tmHeight
;
209 SelectObject( hDC
, hPrevFont
);
211 ReleaseDC(hwnd
, hDC
);
213 iTextItemHeight
= ((13 * baseUnitY
) / 8);
216 * This "formula" calculates the height of the complete control.
217 * To calculate the height of the text area, we have to remove the
220 iTextItemHeight
-= 2*COMBO_YBORDERSIZE();
224 * Check the ownerdraw case if we haven't asked the parent the size
227 if ( CB_OWNERDRAWN(lphc
) &&
228 (lphc
->wState
& CBF_MEASUREITEM
) )
230 MEASUREITEMSTRUCT measureItem
;
232 INT originalItemHeight
= iTextItemHeight
;
235 * We use the client rect for the width of the item.
237 GetClientRect(hwnd
, &clientRect
);
239 lphc
->wState
&= ~CBF_MEASUREITEM
;
242 * Send a first one to measure the size of the text area
244 measureItem
.CtlType
= ODT_COMBOBOX
;
245 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
246 measureItem
.itemID
= -1;
247 measureItem
.itemWidth
= clientRect
.right
;
248 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
249 measureItem
.itemData
= 0;
250 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
251 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
252 iTextItemHeight
= 6 + measureItem
.itemHeight
;
255 * Send a second one in the case of a fixed ownerdraw list to calculate the
256 * size of the list items. (we basically do this on behalf of the listbox)
258 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
260 measureItem
.CtlType
= ODT_COMBOBOX
;
261 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
262 measureItem
.itemID
= 0;
263 measureItem
.itemWidth
= clientRect
.right
;
264 measureItem
.itemHeight
= originalItemHeight
;
265 measureItem
.itemData
= 0;
266 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
267 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
268 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
272 * Keep the size for the next time
274 lphc
->editHeight
= iTextItemHeight
;
277 return iTextItemHeight
;
281 /***********************************************************************
284 * Set up component coordinates given valid lphc->RectCombo.
286 static void CBCalcPlacement(
294 * Again, start with the client rectangle.
296 GetClientRect(hwnd
, lprEdit
);
301 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
304 * Chop off the bottom part to fit with the height of the text area.
306 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
309 * The button starts the same vertical position as the text area.
311 CopyRect(lprButton
, lprEdit
);
314 * If the combobox is "simple" there is no button.
316 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
317 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
321 * Let's assume the combobox button is the same width as the
323 * size the button horizontally and cut-off the text area.
325 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
326 lprEdit
->right
= lprButton
->left
;
330 * In the case of a dropdown, there is an additional spacing between the
331 * text area and the button.
333 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
335 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
339 * If we have an edit control, we space it away from the borders slightly.
341 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
343 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
347 * Adjust the size of the listbox popup.
349 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
352 * Use the client rectangle to initialize the listbox rectangle
354 GetClientRect(hwnd
, lprLB
);
357 * Then, chop-off the top part.
359 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
364 * Make sure the dropped width is as large as the combobox itself.
366 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
368 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
371 * In the case of a dropdown, the popup listbox is offset to the right.
372 * so, we want to make sure it's flush with the right side of the
375 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
376 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
379 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
382 TRACE(combo
,"\ttext\t= (%i,%i-%i,%i)\n",
383 lprEdit
->left
, lprEdit
->top
, lprEdit
->right
, lprEdit
->bottom
);
385 TRACE(combo
,"\tbutton\t= (%i,%i-%i,%i)\n",
386 lprButton
->left
, lprButton
->top
, lprButton
->right
, lprButton
->bottom
);
388 TRACE(combo
,"\tlbox\t= (%i,%i-%i,%i)\n",
389 lprLB
->left
, lprLB
->top
, lprLB
->right
, lprLB
->bottom
);
392 /***********************************************************************
393 * CBGetDroppedControlRect
395 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
397 CopyRect(lpRect
, &lphc
->droppedRect
);
400 /***********************************************************************
401 * COMBO_WindowPosChanging
403 static LRESULT
COMBO_WindowPosChanging(
406 WINDOWPOS
* posChanging
)
409 * We need to override the WM_WINDOWPOSCHANGING method to handle all
410 * the non-simple comboboxes. The problem is that those controls are
411 * always the same height. We have to make sure they are not resized
414 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
416 posChanging
->cy
= CBGetTextAreaHeight(hwnd
,lphc
) +
417 2*COMBO_YBORDERSIZE();
423 /***********************************************************************
426 static LRESULT
COMBO_Create( LPHEADCOMBO lphc
, WND
* wnd
, LPARAM lParam
)
428 static char clbName
[] = "ComboLBox";
429 static char editName
[] = "Edit";
431 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
433 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
434 else if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
437 lphc
->owner
= lpcs
->hwndParent
;
440 * The item height and dropped width are not set when the control
443 lphc
->droppedWidth
= lphc
->editHeight
= 0;
446 * The first time we go through, we want to measure the ownerdraw item
448 lphc
->wState
|= CBF_MEASUREITEM
;
450 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
452 if( lphc
->owner
|| !(lpcs
->style
& WS_VISIBLE
) )
457 * Initialize the dropped rect to the size of the client area of the
458 * control and then, force all the areas of the combobox to be
461 GetClientRect( wnd
->hwndSelf
, &lphc
->droppedRect
);
463 CBCalcPlacement(wnd
->hwndSelf
,
467 &lphc
->droppedRect
);
470 * Adjust the position of the popup listbox if it's necessary
472 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
474 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
477 * If it's a dropdown, the listbox is offset
479 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
480 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
482 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
);
483 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
.right
);
486 /* create listbox popup */
488 lbeStyle
= (LBS_NOTIFY
| WS_BORDER
| WS_CLIPSIBLINGS
) |
489 (lpcs
->style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
491 if( lphc
->dwStyle
& CBS_SORT
)
492 lbeStyle
|= LBS_SORT
;
493 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
494 lbeStyle
|= LBS_HASSTRINGS
;
495 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
496 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
497 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
498 lbeStyle
|= LBS_DISABLENOSCROLL
;
500 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
501 lbeStyle
|= WS_CHILD
| WS_VISIBLE
;
502 else /* popup listbox */
503 lbeStyle
|= WS_POPUP
;
505 /* Dropdown ComboLBox is not a child window and we cannot pass
506 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
508 lphc
->hWndLBox
= CreateWindowExA(0,
512 lphc
->droppedRect
.left
,
513 lphc
->droppedRect
.top
,
514 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
515 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
516 lphc
->self
->hwndSelf
,
517 (lphc
->dwStyle
& CBS_DROPDOWN
)? (HMENU
)0 : (HMENU
)ID_CB_LISTBOX
,
518 lphc
->self
->hInstance
,
524 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
;
527 * In Win95 look, the border fo the edit control is
528 * provided by the combobox
530 if (TWEAK_WineLook
== WIN31_LOOK
)
531 lbeStyle
|= WS_BORDER
;
533 if( lphc
->wState
& CBF_EDIT
)
535 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
536 lbeStyle
|= ES_OEMCONVERT
;
537 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
538 lbeStyle
|= ES_AUTOHSCROLL
;
539 if( lphc
->dwStyle
& CBS_LOWERCASE
)
540 lbeStyle
|= ES_LOWERCASE
;
541 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
542 lbeStyle
|= ES_UPPERCASE
;
544 lphc
->hWndEdit
= CreateWindowExA(0,
548 lphc
->textRect
.left
, lphc
->textRect
.top
,
549 lphc
->textRect
.right
- lphc
->textRect
.left
,
550 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
551 lphc
->self
->hwndSelf
,
553 lphc
->self
->hInstance
,
556 if( !lphc
->hWndEdit
)
563 * If the combo is a dropdown, we must resize the control to fit only
564 * the text area and button. To do this, we send a dummy resize and the
565 * WM_WINDOWPOSCHANGING message will take care of setting the height for
568 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
570 CBForceDummyResize(CB_HWND(lphc
));
573 TRACE(combo
,"init done\n");
574 return wnd
->hwndSelf
;
576 ERR(combo
, "edit control failure.\n");
577 } else ERR(combo
, "listbox failure.\n");
578 } else ERR(combo
, "no owner for visible combo.\n");
580 /* CreateWindow() will send WM_NCDESTROY to cleanup */
585 /***********************************************************************
588 * Paint combo button (normal, pressed, and disabled states).
590 static void CBPaintButton(
599 COLORREF oldTextColor
, oldBkColor
;
601 if( lphc
->wState
& CBF_NOREDRAW
)
604 hPrevBrush
= SelectObject(hdc
, GetSysColorBrush(COLOR_BTNFACE
));
607 * Draw the button background
612 rectButton
.right
-rectButton
.left
,
613 rectButton
.bottom
-rectButton
.top
,
616 if( (bBool
= lphc
->wState
& CBF_BUTTONDOWN
) )
618 DrawEdge( hdc
, &rectButton
, EDGE_SUNKEN
, BF_RECT
);
622 DrawEdge( hdc
, &rectButton
, EDGE_RAISED
, BF_RECT
);
626 * Remove the edge of the button from the rectangle
627 * and calculate the position of the bitmap.
629 InflateRect( &rectButton
, -2, -2);
631 x
= (rectButton
.left
+ rectButton
.right
- CBitWidth
) >> 1;
632 y
= (rectButton
.top
+ rectButton
.bottom
- CBitHeight
) >> 1;
635 hMemDC
= CreateCompatibleDC( hdc
);
636 SelectObject( hMemDC
, hComboBmp
);
637 oldTextColor
= SetTextColor( hdc
, GetSysColor(COLOR_BTNFACE
) );
638 oldBkColor
= SetBkColor( hdc
, CB_DISABLED(lphc
) ? RGB(128,128,128) :
640 BitBlt( hdc
, x
, y
, CBitWidth
, CBitHeight
, hMemDC
, 0, 0, SRCCOPY
);
641 SetBkColor( hdc
, oldBkColor
);
642 SetTextColor( hdc
, oldTextColor
);
644 SelectObject( hdc
, hPrevBrush
);
647 /***********************************************************************
650 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
652 static void CBPaintText(
660 if( lphc
->wState
& CBF_NOREDRAW
) return;
662 /* follow Windows combobox that sends a bunch of text
663 * inquiries to its listbox while processing WM_PAINT. */
665 if( (id
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
667 size
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
668 if( (pText
= HeapAlloc( GetProcessHeap(), 0, size
+ 1)) )
670 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
671 pText
[size
] = '\0'; /* just in case */
675 if( lphc
->wState
& CBF_EDIT
)
677 if( CB_HASSTRINGS(lphc
) ) SetWindowTextA( lphc
->hWndEdit
, pText
? pText
: "" );
678 if( lphc
->wState
& CBF_FOCUSED
)
679 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
681 else /* paint text field ourselves */
683 HBRUSH hPrevBrush
= 0;
688 if ((hDC
= GetDC(lphc
->self
->hwndSelf
)))
690 HBRUSH hBrush
= SendMessageA( lphc
->owner
,
692 hDC
, lphc
->self
->hwndSelf
);
693 hPrevBrush
= SelectObject( hDC
,
694 (hBrush
) ? hBrush
: GetStockObject(WHITE_BRUSH
) );
700 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hDC
, lphc
->hFont
) : 0;
703 * Give ourselves some space.
705 InflateRect( &rectEdit
, -1, -1 );
707 if ( (lphc
->wState
& CBF_FOCUSED
) &&
708 !(lphc
->wState
& CBF_DROPPED
) )
712 FillRect( hDC
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
713 SetBkColor( hDC
, GetSysColor( COLOR_HIGHLIGHT
) );
714 SetTextColor( hDC
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
715 itemState
= ODS_SELECTED
| ODS_FOCUS
;
720 if( CB_OWNERDRAWN(lphc
) )
726 * Save the current clip region.
727 * To retrieve the clip region, we need to create one "dummy"
730 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
732 if (GetClipRgn(hDC
, clipRegion
)!=1)
734 DeleteObject(clipRegion
);
735 clipRegion
=(HRGN
)NULL
;
738 if ( lphc
->self
->dwStyle
& WS_DISABLED
)
739 itemState
|= ODS_DISABLED
;
741 dis
.CtlType
= ODT_COMBOBOX
;
742 dis
.CtlID
= lphc
->self
->wIDmenu
;
743 dis
.hwndItem
= lphc
->self
->hwndSelf
;
744 dis
.itemAction
= ODA_DRAWENTIRE
;
746 dis
.itemState
= itemState
;
748 dis
.rcItem
= rectEdit
;
749 dis
.itemData
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
,
753 * Clip the DC and have the parent draw the item.
755 IntersectClipRect(hDC
,
756 rectEdit
.left
, rectEdit
.top
,
757 rectEdit
.right
, rectEdit
.bottom
);
759 SendMessageA(lphc
->owner
, WM_DRAWITEM
,
760 lphc
->self
->wIDmenu
, (LPARAM
)&dis
);
763 * Reset the clipping region.
765 SelectClipRgn(hDC
, clipRegion
);
772 ETO_OPAQUE
| ETO_CLIPPED
,
774 pText
? pText
: "" , size
, NULL
);
776 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
777 DrawFocusRect( hDC
, &rectEdit
);
781 SelectObject(hDC
, hPrevFont
);
786 SelectObject( hDC
, hPrevBrush
);
788 ReleaseDC( lphc
->self
->hwndSelf
, hDC
);
793 HeapFree( GetProcessHeap(), 0, pText
);
796 /***********************************************************************
799 static void CBPaintBorder(
806 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
808 GetClientRect(hwnd
, &clientRect
);
812 CopyRect(&clientRect
, &lphc
->textRect
);
814 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
815 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
818 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
821 /***********************************************************************
822 * COMBO_EraseBackground
824 static LRESULT
COMBO_EraseBackground(
833 hDC
= (hParamDC
) ? hParamDC
837 * Calculate the area that we want to erase.
839 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
841 GetClientRect(hwnd
, &clientRect
);
845 CopyRect(&clientRect
, &lphc
->textRect
);
847 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
850 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
854 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
856 FillRect(hDC
, &clientRect
, hBkgBrush
);
859 ReleaseDC(hwnd
, hDC
);
864 /***********************************************************************
867 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
872 hDC
= (hParamDC
) ? hParamDC
873 : BeginPaint( lphc
->self
->hwndSelf
, &ps
);
876 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
878 HBRUSH hPrevBrush
, hBkgBrush
;
880 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
881 hDC
, lphc
->self
->hwndSelf
);
884 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
886 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
889 * In non 3.1 look, there is a sunken border on the combobox
891 if (TWEAK_WineLook
!= WIN31_LOOK
)
893 CBPaintBorder(CB_HWND(lphc
), lphc
, hDC
);
896 if( !IsRectEmpty(&lphc
->buttonRect
) )
898 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
901 if( !(lphc
->wState
& CBF_EDIT
) )
904 * The text area has a border only in Win 3.1 look.
906 if (TWEAK_WineLook
== WIN31_LOOK
)
908 HPEN hPrevPen
= SelectObject( hDC
, GetSysColorPen(COLOR_WINDOWFRAME
) );
911 lphc
->textRect
.left
, lphc
->textRect
.top
,
912 lphc
->textRect
.right
- 1, lphc
->textRect
.bottom
- 1);
914 SelectObject( hDC
, hPrevPen
);
917 CBPaintText( lphc
, hDC
, lphc
->textRect
);
921 SelectObject( hDC
, hPrevBrush
);
925 EndPaint(lphc
->self
->hwndSelf
, &ps
);
930 /***********************************************************************
933 * Select listbox entry according to the contents of the edit control.
935 static INT
CBUpdateLBox( LPHEADCOMBO lphc
)
937 INT length
, idx
, ret
;
941 length
= CB_GETEDITTEXTLENGTH( lphc
);
944 pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1);
946 TRACE(combo
,"\t edit text length %i\n", length
);
950 if( length
) GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1);
951 else pText
[0] = '\0';
952 idx
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
953 (WPARAM
)(-1), (LPARAM
)pText
);
954 if( idx
== LB_ERR
) idx
= 0; /* select first item */
956 HeapFree( GetProcessHeap(), 0, pText
);
961 SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)idx
, 0 );
965 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)idx
, 0 );
966 /* probably superfluous but Windows sends this too */
967 SendMessageA( lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)idx
, 0 );
972 /***********************************************************************
975 * Copy a listbox entry to the edit control.
977 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
982 TRACE(combo
,"\t %i\n", index
);
986 length
= CB_GETEDITTEXTLENGTH( lphc
);
989 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
991 GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1 );
992 index
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
993 (WPARAM
)(-1), (LPARAM
)pText
);
994 HeapFree( GetProcessHeap(), 0, pText
);
999 if( index
>= 0 ) /* got an entry */
1001 length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1004 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1006 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1007 (WPARAM
)index
, (LPARAM
)pText
);
1008 SendMessageA( lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)pText
);
1009 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1010 HeapFree( GetProcessHeap(), 0, pText
);
1016 /***********************************************************************
1019 * Show listbox popup.
1021 static void CBDropDown( LPHEADCOMBO lphc
)
1026 TRACE(combo
,"[%04x]: drop down\n", CB_HWND(lphc
));
1028 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1032 lphc
->wState
|= CBF_DROPPED
;
1033 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1035 index
= CBUpdateLBox( lphc
);
1036 if( !(lphc
->wState
& CBF_CAPTURE
) ) CBUpdateEdit( lphc
, index
);
1040 index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1041 if( index
== LB_ERR
) index
= 0;
1042 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)index
, 0 );
1043 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1046 /* now set popup position */
1047 GetWindowRect( lphc
->self
->hwndSelf
, &rect
);
1051 * If it's a dropdown, the listbox is offset
1053 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1054 rect
.left
+= COMBO_EDITBUTTONSPACE();
1056 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1057 rect
.right
- rect
.left
, rect
.bottom
- rect
.top
,
1058 SWP_NOACTIVATE
| SWP_NOSIZE
| SWP_NOREDRAW
);
1060 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1061 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0, RDW_INVALIDATE
|
1062 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1064 ShowWindow( lphc
->hWndLBox
, SW_SHOWNA
);
1067 /***********************************************************************
1070 * Hide listbox popup.
1072 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1074 HWND hWnd
= lphc
->self
->hwndSelf
;
1076 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1078 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1081 TRACE(combo
,"[%04x]: roll up [%i]\n", CB_HWND(lphc
), (INT
)ok
);
1083 /* always send WM_LBUTTONUP? */
1084 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1086 if( lphc
->wState
& CBF_DROPPED
)
1090 lphc
->wState
&= ~CBF_DROPPED
;
1091 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1093 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1095 INT index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1096 CBUpdateEdit( lphc
, index
);
1097 rect
= lphc
->buttonRect
;
1108 rect
= lphc
->textRect
;
1113 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1114 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1115 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1116 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1121 /***********************************************************************
1124 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1126 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL bRedrawButton
)
1128 if( lphc
->wState
& CBF_DROPPED
)
1130 CBRollUp( lphc
, TRUE
, bRedrawButton
);
1138 /***********************************************************************
1141 * Edit control helper.
1143 HWND
COMBO_GetLBWindow( WND
* pWnd
)
1145 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1146 if( lphc
) return lphc
->hWndLBox
;
1151 /***********************************************************************
1154 static void CBRepaintButton( LPHEADCOMBO lphc
)
1156 InvalidateRect(CB_HWND(lphc
), &lphc
->buttonRect
, TRUE
);
1157 UpdateWindow(CB_HWND(lphc
));
1160 /***********************************************************************
1163 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1165 if( !(lphc
->wState
& CBF_FOCUSED
) )
1167 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1168 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1170 if( lphc
->wState
& CBF_EDIT
)
1171 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1172 lphc
->wState
|= CBF_FOCUSED
;
1173 if( !(lphc
->wState
& CBF_EDIT
) )
1175 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1178 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1182 /***********************************************************************
1185 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1187 HWND hWnd
= lphc
->self
->hwndSelf
;
1189 if( lphc
->wState
& CBF_FOCUSED
)
1191 SendMessageA( hWnd
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1193 CBRollUp( lphc
, FALSE
, TRUE
);
1194 if( IsWindow( hWnd
) )
1196 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1197 SendMessageA( lphc
->hWndLBox
, LB_CARETOFF
, 0, 0 );
1199 lphc
->wState
&= ~CBF_FOCUSED
;
1202 if( lphc
->wState
& CBF_EDIT
)
1203 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, (WPARAM
)(-1), 0 );
1206 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1209 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1214 /***********************************************************************
1217 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1219 if( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1221 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1223 switch( HIWORD(wParam
) >> 8 )
1225 case (EN_SETFOCUS
>> 8):
1227 TRACE(combo
,"[%04x]: edit [%04x] got focus\n",
1228 CB_HWND(lphc
), lphc
->hWndEdit
);
1230 if( !(lphc
->wState
& CBF_FOCUSED
) ) COMBO_SetFocus( lphc
);
1233 case (EN_KILLFOCUS
>> 8):
1235 TRACE(combo
,"[%04x]: edit [%04x] lost focus\n",
1236 CB_HWND(lphc
), lphc
->hWndEdit
);
1238 /* NOTE: it seems that Windows' edit control sends an
1239 * undocumented message WM_USER + 0x1B instead of this
1240 * notification (only when it happens to be a part of
1241 * the combo). ?? - AK.
1244 COMBO_KillFocus( lphc
);
1248 case (EN_CHANGE
>> 8):
1249 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1250 CBUpdateLBox( lphc
);
1253 case (EN_UPDATE
>> 8):
1254 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1257 case (EN_ERRSPACE
>> 8):
1258 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1261 else if( lphc
->hWndLBox
== hWnd
)
1263 switch( HIWORD(wParam
) )
1266 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1270 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1276 TRACE(combo
,"[%04x]: lbox selection change [%04x]\n",
1277 CB_HWND(lphc
), lphc
->wState
);
1279 /* do not roll up if selection is being tracked
1280 * by arrowkeys in the dropdown listbox */
1282 if( (lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
) )
1283 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1284 else lphc
->wState
&= ~CBF_NOROLLUP
;
1286 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1287 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1292 /* nothing to do here since ComboLBox always resets the focus to its
1293 * combo/edit counterpart */
1300 /***********************************************************************
1303 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1305 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
,
1306 WPARAM wParam
, LPARAM lParam
)
1308 HWND hWnd
= lphc
->self
->hwndSelf
;
1310 TRACE(combo
,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc
), msg
);
1312 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1314 /* two first items are the same in all 4 structs */
1315 lpIS
->CtlType
= ODT_COMBOBOX
;
1316 lpIS
->CtlID
= lphc
->self
->wIDmenu
;
1318 switch( msg
) /* patch window handle */
1321 lpIS
->hwndItem
= hWnd
;
1325 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1326 lpIS
->hwndItem
= hWnd
;
1329 case WM_COMPAREITEM
:
1330 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1331 lpIS
->hwndItem
= hWnd
;
1336 return SendMessageA( lphc
->owner
, msg
, lphc
->self
->wIDmenu
, lParam
);
1339 /***********************************************************************
1342 static LRESULT
COMBO_GetText( LPHEADCOMBO lphc
, UINT N
, LPSTR lpText
)
1344 if( lphc
->wState
& CBF_EDIT
)
1345 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
,
1346 (WPARAM
)N
, (LPARAM
)lpText
);
1348 /* get it from the listbox */
1350 if( lphc
->hWndLBox
)
1352 INT idx
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1356 INT length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
,
1359 /* 'length' is without the terminating character */
1361 lpBuffer
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1 );
1367 INT n
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1368 (WPARAM
)idx
, (LPARAM
)lpBuffer
);
1370 /* truncate if buffer is too short */
1375 if( n
!= LB_ERR
) memcpy( lpText
, lpBuffer
, (N
>n
) ? n
+1 : N
-1 );
1376 lpText
[N
- 1] = '\0';
1378 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1388 /***********************************************************************
1391 * This function sets window positions according to the updated
1392 * component placement struct.
1394 static void CBResetPos(
1400 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1402 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1403 * sizing messages */
1405 if( lphc
->wState
& CBF_EDIT
)
1406 SetWindowPos( lphc
->hWndEdit
, 0,
1407 rectEdit
->left
, rectEdit
->top
,
1408 rectEdit
->right
- rectEdit
->left
,
1409 rectEdit
->bottom
- rectEdit
->top
,
1410 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1412 SetWindowPos( lphc
->hWndLBox
, 0,
1413 rectLB
->left
, rectLB
->top
,
1414 rectLB
->right
- rectLB
->left
,
1415 rectLB
->bottom
- rectLB
->top
,
1416 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1420 if( lphc
->wState
& CBF_DROPPED
)
1422 lphc
->wState
&= ~CBF_DROPPED
;
1423 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1426 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1427 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
1428 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1433 /***********************************************************************
1436 static void COMBO_Size( LPHEADCOMBO lphc
)
1438 CBCalcPlacement(lphc
->self
->hwndSelf
,
1442 &lphc
->droppedRect
);
1444 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1448 /***********************************************************************
1451 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1456 lphc
->hFont
= hFont
;
1459 * Propagate to owned windows.
1461 if( lphc
->wState
& CBF_EDIT
)
1462 SendMessageA( lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1463 SendMessageA( lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1466 * Redo the layout of the control.
1468 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1470 CBCalcPlacement(lphc
->self
->hwndSelf
,
1474 &lphc
->droppedRect
);
1476 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1480 CBForceDummyResize(CB_HWND(lphc
));
1485 /***********************************************************************
1486 * COMBO_SetItemHeight
1488 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1490 LRESULT lRet
= CB_ERR
;
1492 if( index
== -1 ) /* set text field height */
1494 if( height
< 32768 )
1496 lphc
->editHeight
= height
;
1499 * Redo the layout of the control.
1501 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1503 CBCalcPlacement(lphc
->self
->hwndSelf
,
1507 &lphc
->droppedRect
);
1509 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1513 CBForceDummyResize(CB_HWND(lphc
));
1519 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1520 lRet
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1521 (WPARAM
)index
, (LPARAM
)height
);
1525 /***********************************************************************
1526 * COMBO_SelectString
1528 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPCSTR pText
)
1530 INT index
= SendMessageA( lphc
->hWndLBox
, LB_SELECTSTRING
,
1531 (WPARAM
)start
, (LPARAM
)pText
);
1534 if( lphc
->wState
& CBF_EDIT
)
1535 CBUpdateEdit( lphc
, index
);
1538 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1541 return (LRESULT
)index
;
1544 /***********************************************************************
1547 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1549 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
1551 HWND hWnd
= lphc
->self
->hwndSelf
;
1553 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1555 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1556 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1558 lphc
->wState
|= CBF_BUTTONDOWN
;
1559 if( lphc
->wState
& CBF_DROPPED
)
1561 /* got a click to cancel selection */
1563 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1564 CBRollUp( lphc
, TRUE
, FALSE
);
1565 if( !IsWindow( hWnd
) ) return;
1567 if( lphc
->wState
& CBF_CAPTURE
)
1569 lphc
->wState
&= ~CBF_CAPTURE
;
1575 /* drop down the listbox and start tracking */
1577 lphc
->wState
|= CBF_CAPTURE
;
1581 if( bButton
) CBRepaintButton( lphc
);
1585 /***********************************************************************
1588 * Release capture and stop tracking if needed.
1590 static void COMBO_LButtonUp( LPHEADCOMBO lphc
, LPARAM lParam
)
1592 if( lphc
->wState
& CBF_CAPTURE
)
1594 lphc
->wState
&= ~CBF_CAPTURE
;
1595 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1597 INT index
= CBUpdateLBox( lphc
);
1598 CBUpdateEdit( lphc
, index
);
1603 if( lphc
->wState
& CBF_BUTTONDOWN
)
1605 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1606 CBRepaintButton( lphc
);
1610 /***********************************************************************
1613 * Two things to do - track combo button and release capture when
1614 * pointer goes into the listbox.
1616 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1618 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
1621 if( lphc
->wState
& CBF_BUTTONDOWN
)
1625 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1629 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1630 CBRepaintButton( lphc
);
1634 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1635 MapWindowPoints( lphc
->self
->hwndSelf
, lphc
->hWndLBox
, &pt
, 1 );
1636 if( PtInRect(&lbRect
, pt
) )
1638 lphc
->wState
&= ~CBF_CAPTURE
;
1640 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
);
1642 /* hand over pointer tracking */
1643 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1648 /***********************************************************************
1651 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1653 LRESULT WINAPI
ComboWndProc( HWND hwnd
, UINT message
,
1654 WPARAM wParam
, LPARAM lParam
)
1657 WND
* pWnd
= WIN_FindWndPtr(hwnd
);
1661 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1663 TRACE(combo
, "[%04x]: msg %s wp %08x lp %08lx\n",
1664 pWnd
->hwndSelf
, SPY_GetMsgName(message
), wParam
, lParam
);
1666 if( lphc
|| message
== WM_NCCREATE
)
1670 /* System messages */
1673 retvalue
= COMBO_NCCreate(pWnd
, lParam
);
1676 COMBO_NCDestroy(lphc
);
1680 retvalue
= COMBO_Create(lphc
, pWnd
, lParam
);
1683 case WM_PRINTCLIENT
:
1684 if (lParam
& PRF_ERASEBKGND
)
1685 COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1689 /* wParam may contain a valid HDC! */
1690 retvalue
= COMBO_Paint(lphc
, wParam
);
1693 retvalue
= COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1696 retvalue
= (LRESULT
)(DLGC_WANTARROWS
| DLGC_WANTCHARS
);
1698 case WM_WINDOWPOSCHANGING
:
1699 retvalue
= COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1702 if( lphc
->hWndLBox
&&
1703 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1707 COMBO_Font( lphc
, (HFONT16
)wParam
, (BOOL
)lParam
);
1711 retvalue
= (LRESULT
)lphc
->hFont
;
1714 if( lphc
->wState
& CBF_EDIT
)
1715 SetFocus( lphc
->hWndEdit
);
1717 COMBO_SetFocus( lphc
);
1721 #define hwndFocus ((HWND16)wParam)
1723 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1724 COMBO_KillFocus( lphc
);
1729 retvalue
= COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1732 retvalue
= COMBO_GetText( lphc
, (UINT
)wParam
, (LPSTR
)lParam
);
1735 case WM_GETTEXTLENGTH
:
1740 if( lphc
->wState
& CBF_EDIT
)
1742 retvalue
= SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1749 case WM_COMPAREITEM
:
1750 case WM_MEASUREITEM
:
1751 retvalue
= COMBO_ItemOp( lphc
, message
, wParam
, lParam
);
1754 if( lphc
->wState
& CBF_EDIT
)
1755 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1756 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1761 lphc
->wState
&= ~CBF_NOREDRAW
;
1763 lphc
->wState
|= CBF_NOREDRAW
;
1765 if( lphc
->wState
& CBF_EDIT
)
1766 SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1767 SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1771 if( KEYDATA_ALT
& HIWORD(lParam
) )
1772 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1773 COMBO_FlipListbox( lphc
, TRUE
);
1778 if( lphc
->wState
& CBF_EDIT
)
1779 retvalue
= SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1781 retvalue
= SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1783 case WM_LBUTTONDOWN
:
1784 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
->hwndSelf
);
1785 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1789 COMBO_LButtonUp( lphc
, lParam
);
1793 if( lphc
->wState
& CBF_CAPTURE
)
1794 COMBO_MouseMove( lphc
, wParam
, lParam
);
1797 /* Combo messages */
1799 case CB_ADDSTRING16
:
1800 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1802 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1804 case CB_INSERTSTRING16
:
1805 wParam
= (INT
)(INT16
)wParam
;
1806 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1807 case CB_INSERTSTRING
:
1808 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1810 case CB_DELETESTRING16
:
1811 case CB_DELETESTRING
:
1812 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1814 case CB_SELECTSTRING16
:
1815 wParam
= (INT
)(INT16
)wParam
;
1816 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1817 case CB_SELECTSTRING
:
1818 retvalue
= COMBO_SelectString( lphc
, (INT
)wParam
, (LPSTR
)lParam
);
1820 case CB_FINDSTRING16
:
1821 wParam
= (INT
)(INT16
)wParam
;
1822 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1824 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
1826 case CB_FINDSTRINGEXACT16
:
1827 wParam
= (INT
)(INT16
)wParam
;
1828 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1829 case CB_FINDSTRINGEXACT
:
1830 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRINGEXACT
,
1833 case CB_SETITEMHEIGHT16
:
1834 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
1835 case CB_SETITEMHEIGHT
:
1836 retvalue
= COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
1838 case CB_GETITEMHEIGHT16
:
1839 wParam
= (INT
)(INT16
)wParam
;
1840 case CB_GETITEMHEIGHT
:
1841 if( (INT
)wParam
>= 0 ) /* listbox item */
1843 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
1846 retvalue
= CBGetTextAreaHeight(hwnd
, lphc
);
1848 case CB_RESETCONTENT16
:
1849 case CB_RESETCONTENT
:
1850 SendMessageA( lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0 );
1851 InvalidateRect(CB_HWND(lphc
), NULL
, TRUE
);
1854 case CB_INITSTORAGE
:
1855 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
1857 case CB_GETHORIZONTALEXTENT
:
1858 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
1860 case CB_SETHORIZONTALEXTENT
:
1861 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
1863 case CB_GETTOPINDEX
:
1864 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
1867 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
1870 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
1872 case CB_GETDROPPEDWIDTH
:
1873 if( lphc
->droppedWidth
)
1875 retvalue
= lphc
->droppedWidth
;
1878 retvalue
= lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1880 case CB_SETDROPPEDWIDTH
:
1881 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
1882 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
1885 case CB_GETDROPPEDCONTROLRECT16
:
1886 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1890 CBGetDroppedControlRect( lphc
, &r
);
1891 CONV_RECT32TO16( &r
, (LPRECT16
)lParam
);
1895 case CB_GETDROPPEDCONTROLRECT
:
1896 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
1899 case CB_GETDROPPEDSTATE16
:
1900 case CB_GETDROPPEDSTATE
:
1901 retvalue
= (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
1904 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1907 retvalue
= COMBO_Directory( lphc
, (UINT
)wParam
,
1908 (LPSTR
)lParam
, (message
== CB_DIR
));
1910 case CB_SHOWDROPDOWN16
:
1911 case CB_SHOWDROPDOWN
:
1912 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1916 if( !(lphc
->wState
& CBF_DROPPED
) )
1920 if( lphc
->wState
& CBF_DROPPED
)
1921 CBRollUp( lphc
, FALSE
, TRUE
);
1927 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1929 case CB_GETCURSEL16
:
1931 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1933 case CB_SETCURSEL16
:
1934 wParam
= (INT
)(INT16
)wParam
;
1936 lParam
= SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
1937 if( lphc
->wState
& CBF_SELCHANGE
)
1939 /* no LBN_SELCHANGE in this case, update manually */
1940 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1941 lphc
->wState
&= ~CBF_SELCHANGE
;
1945 case CB_GETLBTEXT16
:
1946 wParam
= (INT
)(INT16
)wParam
;
1947 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1949 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
1951 case CB_GETLBTEXTLEN16
:
1952 wParam
= (INT
)(INT16
)wParam
;
1953 case CB_GETLBTEXTLEN
:
1954 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
1956 case CB_GETITEMDATA16
:
1957 wParam
= (INT
)(INT16
)wParam
;
1958 case CB_GETITEMDATA
:
1959 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
1961 case CB_SETITEMDATA16
:
1962 wParam
= (INT
)(INT16
)wParam
;
1963 case CB_SETITEMDATA
:
1964 retvalue
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
1966 case CB_GETEDITSEL16
:
1967 wParam
= lParam
= 0; /* just in case */
1969 if( lphc
->wState
& CBF_EDIT
)
1973 retvalue
= SendMessageA( lphc
->hWndEdit
, EM_GETSEL
,
1974 (wParam
) ? wParam
: (WPARAM
)&a
,
1975 (lParam
) ? lParam
: (LPARAM
)&b
);
1980 case CB_SETEDITSEL16
:
1982 if( lphc
->wState
& CBF_EDIT
)
1984 retvalue
= SendMessageA( lphc
->hWndEdit
, EM_SETSEL
,
1985 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
1990 case CB_SETEXTENDEDUI16
:
1991 case CB_SETEXTENDEDUI
:
1992 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1998 lphc
->wState
|= CBF_EUI
;
1999 else lphc
->wState
&= ~CBF_EUI
;
2002 case CB_GETEXTENDEDUI16
:
2003 case CB_GETEXTENDEDUI
:
2004 retvalue
= (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2006 case (WM_USER
+ 0x1B):
2007 WARN(combo
, "[%04x]: undocumented msg!\n", hwnd
);
2009 retvalue
= DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2014 WIN_ReleaseWndPtr(pWnd
);