4 * Copyright 1997 Alex Korobka
6 * FIXME: roll up in Netscape 3.01.
15 #include "wine/winuser16.h"
21 #include "debugtools.h"
24 DEFAULT_DEBUG_CHANNEL(combo
);
26 /* bits in the dwKeyData */
27 #define KEYDATA_ALT 0x2000
28 #define KEYDATA_PREVSTATE 0x4000
31 * Additional combo box definitions
34 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
35 #define CB_NOTIFY( lphc, code ) \
36 (SendMessageA( (lphc)->owner, WM_COMMAND, \
37 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
38 #define CB_GETEDITTEXTLENGTH( lphc ) \
39 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
41 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
46 static HBITMAP hComboBmp
= 0;
47 static UINT CBitHeight
, CBitWidth
;
50 * Look and feel dependant "constants"
53 #define COMBO_YBORDERGAP 5
54 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
55 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
56 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
57 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
59 static LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
62 /*********************************************************************
63 * combo class descriptor
65 const struct builtin_class_descr COMBO_builtin_class
=
67 "ComboBox", /* name */
68 CS_GLOBALCLASS
| CS_PARENTDC
| CS_DBLCLKS
, /* style */
69 ComboWndProcA
, /* procA */
70 NULL
, /* procW (FIXME) */
71 sizeof(HEADCOMBO
*), /* extra */
72 IDC_ARROWA
, /* cursor */
77 /***********************************************************************
80 * Load combo button bitmap.
82 static BOOL
COMBO_Init()
86 if( hComboBmp
) return TRUE
;
87 if( (hDC
= CreateCompatibleDC(0)) )
90 if( (hComboBmp
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO
))) )
96 GetObjectA( hComboBmp
, sizeof(bm
), &bm
);
97 CBitHeight
= bm
.bmHeight
;
98 CBitWidth
= bm
.bmWidth
;
100 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
102 hPrevB
= SelectObject( hDC
, hComboBmp
);
103 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
104 InvertRect( hDC
, &r
);
105 SelectObject( hDC
, hPrevB
);
114 /***********************************************************************
117 static LRESULT
COMBO_NCCreate(WND
* wnd
, LPARAM lParam
)
121 if ( wnd
&& COMBO_Init() &&
122 (lphc
= HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO
))) )
124 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
126 memset( lphc
, 0, sizeof(HEADCOMBO
) );
127 *(LPHEADCOMBO
*)wnd
->wExtra
= lphc
;
129 /* some braindead apps do try to use scrollbar/border flags */
131 lphc
->dwStyle
= (lpcs
->style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
));
132 wnd
->dwStyle
&= ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
135 * We also have to remove the client edge style to make sure
136 * we don't end-up with a non client area.
138 wnd
->dwExStyle
&= ~(WS_EX_CLIENTEDGE
);
140 if( !(lpcs
->style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
141 lphc
->dwStyle
|= CBS_HASSTRINGS
;
142 if( !(wnd
->dwExStyle
& WS_EX_NOPARENTNOTIFY
) )
143 lphc
->wState
|= CBF_NOTIFY
;
145 TRACE("[0x%08x], style = %08x\n",
146 (UINT
)lphc
, lphc
->dwStyle
);
148 return (LRESULT
)(UINT
)wnd
->hwndSelf
;
150 return (LRESULT
)FALSE
;
153 /***********************************************************************
156 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
161 WND
* wnd
= lphc
->self
;
163 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc
));
165 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
166 DestroyWindow( lphc
->hWndLBox
);
168 HeapFree( GetProcessHeap(), 0, lphc
);
174 /***********************************************************************
175 * CBGetTextAreaHeight
177 * This method will calculate the height of the text area of the
179 * The height of the text area is set in two ways.
180 * It can be set explicitely through a combobox message or through a
181 * WM_MEASUREITEM callback.
182 * If this is not the case, the height is set to 13 dialog units.
183 * This height was determined through experimentation.
185 static INT
CBGetTextAreaHeight(
191 if( lphc
->editHeight
) /* explicitly set height */
193 iTextItemHeight
= lphc
->editHeight
;
198 HDC hDC
= GetDC(hwnd
);
203 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
205 GetTextMetricsA(hDC
, &tm
);
207 baseUnitY
= tm
.tmHeight
;
210 SelectObject( hDC
, hPrevFont
);
212 ReleaseDC(hwnd
, hDC
);
214 iTextItemHeight
= ((13 * baseUnitY
) / 8);
217 * This "formula" calculates the height of the complete control.
218 * To calculate the height of the text area, we have to remove the
221 iTextItemHeight
-= 2*COMBO_YBORDERSIZE();
225 * Check the ownerdraw case if we haven't asked the parent the size
228 if ( CB_OWNERDRAWN(lphc
) &&
229 (lphc
->wState
& CBF_MEASUREITEM
) )
231 MEASUREITEMSTRUCT measureItem
;
233 INT originalItemHeight
= iTextItemHeight
;
236 * We use the client rect for the width of the item.
238 GetClientRect(hwnd
, &clientRect
);
240 lphc
->wState
&= ~CBF_MEASUREITEM
;
243 * Send a first one to measure the size of the text area
245 measureItem
.CtlType
= ODT_COMBOBOX
;
246 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
247 measureItem
.itemID
= -1;
248 measureItem
.itemWidth
= clientRect
.right
;
249 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
250 measureItem
.itemData
= 0;
251 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
252 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
253 iTextItemHeight
= 6 + measureItem
.itemHeight
;
256 * Send a second one in the case of a fixed ownerdraw list to calculate the
257 * size of the list items. (we basically do this on behalf of the listbox)
259 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
261 measureItem
.CtlType
= ODT_COMBOBOX
;
262 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
263 measureItem
.itemID
= 0;
264 measureItem
.itemWidth
= clientRect
.right
;
265 measureItem
.itemHeight
= originalItemHeight
;
266 measureItem
.itemData
= 0;
267 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
268 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
269 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
273 * Keep the size for the next time
275 lphc
->editHeight
= iTextItemHeight
;
278 return iTextItemHeight
;
281 /***********************************************************************
284 * The dummy resize is used for listboxes that have a popup to trigger
285 * a re-arranging of the contents of the combobox and the recalculation
286 * of the size of the "real" control window.
288 static void CBForceDummyResize(
294 newComboHeight
= CBGetTextAreaHeight(CB_HWND(lphc
),lphc
) + 2*COMBO_YBORDERSIZE();
296 GetWindowRect(CB_HWND(lphc
), &windowRect
);
299 * We have to be careful, resizing a combobox also has the meaning that the
300 * dropped rect will be resized. In this case, we want to trigger a resize
301 * to recalculate layout but we don't want to change the dropped rectangle
302 * So, we pass the height of text area of control as the height.
303 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
306 SetWindowPos( CB_HWND(lphc
),
309 windowRect
.right
- windowRect
.left
,
311 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
314 /***********************************************************************
317 * Set up component coordinates given valid lphc->RectCombo.
319 static void CBCalcPlacement(
327 * Again, start with the client rectangle.
329 GetClientRect(hwnd
, lprEdit
);
334 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
337 * Chop off the bottom part to fit with the height of the text area.
339 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
342 * The button starts the same vertical position as the text area.
344 CopyRect(lprButton
, lprEdit
);
347 * If the combobox is "simple" there is no button.
349 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
350 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
354 * Let's assume the combobox button is the same width as the
356 * size the button horizontally and cut-off the text area.
358 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
359 lprEdit
->right
= lprButton
->left
;
363 * In the case of a dropdown, there is an additional spacing between the
364 * text area and the button.
366 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
368 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
372 * If we have an edit control, we space it away from the borders slightly.
374 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
376 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
380 * Adjust the size of the listbox popup.
382 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
385 * Use the client rectangle to initialize the listbox rectangle
387 GetClientRect(hwnd
, lprLB
);
390 * Then, chop-off the top part.
392 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
397 * Make sure the dropped width is as large as the combobox itself.
399 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
401 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
404 * In the case of a dropdown, the popup listbox is offset to the right.
405 * so, we want to make sure it's flush with the right side of the
408 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
409 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
412 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
415 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
416 lprEdit
->left
, lprEdit
->top
, lprEdit
->right
, lprEdit
->bottom
);
418 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
419 lprButton
->left
, lprButton
->top
, lprButton
->right
, lprButton
->bottom
);
421 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
422 lprLB
->left
, lprLB
->top
, lprLB
->right
, lprLB
->bottom
);
425 /***********************************************************************
426 * CBGetDroppedControlRect
428 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
430 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
431 of the combo box and the lower right corner of the listbox */
433 GetWindowRect(lphc
->self
->hwndSelf
, lpRect
);
435 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
436 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
440 /***********************************************************************
441 * COMBO_WindowPosChanging
443 static LRESULT
COMBO_WindowPosChanging(
446 WINDOWPOS
* posChanging
)
449 * We need to override the WM_WINDOWPOSCHANGING method to handle all
450 * the non-simple comboboxes. The problem is that those controls are
451 * always the same height. We have to make sure they are not resized
454 if ( ( CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
455 ((posChanging
->flags
& SWP_NOSIZE
) == 0) )
459 newComboHeight
= CBGetTextAreaHeight(hwnd
,lphc
) +
460 2*COMBO_YBORDERSIZE();
463 * Resizing a combobox has another side effect, it resizes the dropped
464 * rectangle as well. However, it does it only if the new height for the
465 * combobox is different than the height it should have. In other words,
466 * if the application resizing the combobox only had the intention to resize
467 * the actual control, for example, to do the layout of a dialog that is
468 * resized, the height of the dropdown is not changed.
470 if (posChanging
->cy
!= newComboHeight
)
472 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ posChanging
->cy
- newComboHeight
;
474 posChanging
->cy
= newComboHeight
;
481 /***********************************************************************
484 static LRESULT
COMBO_Create( LPHEADCOMBO lphc
, WND
* wnd
, LPARAM lParam
)
486 static char clbName
[] = "ComboLBox";
487 static char editName
[] = "Edit";
489 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
491 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
492 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
495 lphc
->owner
= lpcs
->hwndParent
;
498 * The item height and dropped width are not set when the control
501 lphc
->droppedWidth
= lphc
->editHeight
= 0;
504 * The first time we go through, we want to measure the ownerdraw item
506 lphc
->wState
|= CBF_MEASUREITEM
;
508 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
510 if( lphc
->owner
|| !(lpcs
->style
& WS_VISIBLE
) )
516 * Initialize the dropped rect to the size of the client area of the
517 * control and then, force all the areas of the combobox to be
520 GetClientRect( wnd
->hwndSelf
, &lphc
->droppedRect
);
522 CBCalcPlacement(wnd
->hwndSelf
,
526 &lphc
->droppedRect
);
529 * Adjust the position of the popup listbox if it's necessary
531 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
533 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
536 * If it's a dropdown, the listbox is offset
538 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
539 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
541 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
);
542 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
.right
);
545 /* create listbox popup */
547 lbeStyle
= (LBS_NOTIFY
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
548 (lpcs
->style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
550 if( lphc
->dwStyle
& CBS_SORT
)
551 lbeStyle
|= LBS_SORT
;
552 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
553 lbeStyle
|= LBS_HASSTRINGS
;
554 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
555 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
556 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
557 lbeStyle
|= LBS_DISABLENOSCROLL
;
559 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
561 lbeStyle
|= WS_VISIBLE
;
564 * In win 95 look n feel, the listbox in the simple combobox has
565 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
567 if (TWEAK_WineLook
> WIN31_LOOK
)
569 lbeStyle
&= ~WS_BORDER
;
570 lbeExStyle
|= WS_EX_CLIENTEDGE
;
574 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
,
578 lphc
->droppedRect
.left
,
579 lphc
->droppedRect
.top
,
580 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
581 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
582 lphc
->self
->hwndSelf
,
583 (HMENU
)ID_CB_LISTBOX
,
584 lphc
->self
->hInstance
,
590 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
593 * In Win95 look, the border fo the edit control is
594 * provided by the combobox
596 if (TWEAK_WineLook
== WIN31_LOOK
)
597 lbeStyle
|= WS_BORDER
;
599 if( lphc
->wState
& CBF_EDIT
)
601 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
602 lbeStyle
|= ES_OEMCONVERT
;
603 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
604 lbeStyle
|= ES_AUTOHSCROLL
;
605 if( lphc
->dwStyle
& CBS_LOWERCASE
)
606 lbeStyle
|= ES_LOWERCASE
;
607 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
608 lbeStyle
|= ES_UPPERCASE
;
610 lphc
->hWndEdit
= CreateWindowExA(0,
614 lphc
->textRect
.left
, lphc
->textRect
.top
,
615 lphc
->textRect
.right
- lphc
->textRect
.left
,
616 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
617 lphc
->self
->hwndSelf
,
619 lphc
->self
->hInstance
,
622 if( !lphc
->hWndEdit
)
628 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
630 /* Now do the trick with parent */
631 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
633 * If the combo is a dropdown, we must resize the control
634 * to fit only the text area and button. To do this,
635 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
636 * will take care of setting the height for us.
638 CBForceDummyResize(lphc
);
641 TRACE("init done\n");
642 return wnd
->hwndSelf
;
644 ERR("edit control failure.\n");
645 } else ERR("listbox failure.\n");
646 } else ERR("no owner for visible combo.\n");
648 /* CreateWindow() will send WM_NCDESTROY to cleanup */
653 /***********************************************************************
656 * Paint combo button (normal, pressed, and disabled states).
658 static void CBPaintButton(
663 if( lphc
->wState
& CBF_NOREDRAW
)
666 if (TWEAK_WineLook
== WIN31_LOOK
)
672 COLORREF oldTextColor
, oldBkColor
;
675 hPrevBrush
= SelectObject(hdc
, GetSysColorBrush(COLOR_BTNFACE
));
678 * Draw the button background
683 rectButton
.right
-rectButton
.left
,
684 rectButton
.bottom
-rectButton
.top
,
687 if( (bBool
= lphc
->wState
& CBF_BUTTONDOWN
) )
689 DrawEdge( hdc
, &rectButton
, EDGE_SUNKEN
, BF_RECT
);
693 DrawEdge( hdc
, &rectButton
, EDGE_RAISED
, BF_RECT
);
697 * Remove the edge of the button from the rectangle
698 * and calculate the position of the bitmap.
700 InflateRect( &rectButton
, -2, -2);
702 x
= (rectButton
.left
+ rectButton
.right
- CBitWidth
) >> 1;
703 y
= (rectButton
.top
+ rectButton
.bottom
- CBitHeight
) >> 1;
706 hMemDC
= CreateCompatibleDC( hdc
);
707 SelectObject( hMemDC
, hComboBmp
);
708 oldTextColor
= SetTextColor( hdc
, GetSysColor(COLOR_BTNFACE
) );
709 oldBkColor
= SetBkColor( hdc
, CB_DISABLED(lphc
) ? RGB(128,128,128) :
711 BitBlt( hdc
, x
, y
, CBitWidth
, CBitHeight
, hMemDC
, 0, 0, SRCCOPY
);
712 SetBkColor( hdc
, oldBkColor
);
713 SetTextColor( hdc
, oldTextColor
);
715 SelectObject( hdc
, hPrevBrush
);
719 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
721 if (lphc
->wState
& CBF_BUTTONDOWN
)
723 buttonState
|= DFCS_PUSHED
;
726 if (CB_DISABLED(lphc
))
728 buttonState
|= DFCS_INACTIVE
;
731 DrawFrameControl(hdc
,
738 /***********************************************************************
741 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
743 static void CBPaintText(
751 if( lphc
->wState
& CBF_NOREDRAW
) return;
753 /* follow Windows combobox that sends a bunch of text
754 * inquiries to its listbox while processing WM_PAINT. */
756 if( (id
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
758 size
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
759 if( (pText
= HeapAlloc( GetProcessHeap(), 0, size
+ 1)) )
761 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
762 pText
[size
] = '\0'; /* just in case */
766 if( lphc
->wState
& CBF_EDIT
)
768 if( CB_HASSTRINGS(lphc
) ) SetWindowTextA( lphc
->hWndEdit
, pText
? pText
: "" );
769 if( lphc
->wState
& CBF_FOCUSED
)
770 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
772 else /* paint text field ourselves */
775 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
778 * Give ourselves some space.
780 InflateRect( &rectEdit
, -1, -1 );
782 if ( (lphc
->wState
& CBF_FOCUSED
) &&
783 !(lphc
->wState
& CBF_DROPPED
) )
787 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
788 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
789 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
790 itemState
= ODS_SELECTED
| ODS_FOCUS
;
795 if( CB_OWNERDRAWN(lphc
) )
801 * Save the current clip region.
802 * To retrieve the clip region, we need to create one "dummy"
805 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
807 if (GetClipRgn(hdc
, clipRegion
)!=1)
809 DeleteObject(clipRegion
);
810 clipRegion
=(HRGN
)NULL
;
813 if ( lphc
->self
->dwStyle
& WS_DISABLED
)
814 itemState
|= ODS_DISABLED
;
816 dis
.CtlType
= ODT_COMBOBOX
;
817 dis
.CtlID
= lphc
->self
->wIDmenu
;
818 dis
.hwndItem
= lphc
->self
->hwndSelf
;
819 dis
.itemAction
= ODA_DRAWENTIRE
;
821 dis
.itemState
= itemState
;
823 dis
.rcItem
= rectEdit
;
824 dis
.itemData
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
,
828 * Clip the DC and have the parent draw the item.
830 IntersectClipRect(hdc
,
831 rectEdit
.left
, rectEdit
.top
,
832 rectEdit
.right
, rectEdit
.bottom
);
834 SendMessageA(lphc
->owner
, WM_DRAWITEM
,
835 lphc
->self
->wIDmenu
, (LPARAM
)&dis
);
838 * Reset the clipping region.
840 SelectClipRgn(hdc
, clipRegion
);
847 ETO_OPAQUE
| ETO_CLIPPED
,
849 pText
? pText
: "" , size
, NULL
);
851 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
852 DrawFocusRect( hdc
, &rectEdit
);
856 SelectObject(hdc
, hPrevFont
);
859 HeapFree( GetProcessHeap(), 0, pText
);
862 /***********************************************************************
865 static void CBPaintBorder(
872 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
874 GetClientRect(hwnd
, &clientRect
);
878 CopyRect(&clientRect
, &lphc
->textRect
);
880 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
881 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
884 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
887 /***********************************************************************
888 * COMBO_PrepareColors
890 * This method will sent the appropriate WM_CTLCOLOR message to
891 * prepare and setup the colors for the combo's DC.
893 * It also returns the brush to use for the background.
895 static HBRUSH
COMBO_PrepareColors(
903 * Get the background brush for this control.
905 if (CB_DISABLED(lphc
))
907 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORSTATIC
,
908 hDC
, lphc
->self
->hwndSelf
);
911 * We have to change the text color since WM_CTLCOLORSTATIC will
912 * set it to the "enabled" color. This is the same behavior as the
915 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
919 if (lphc
->wState
& CBF_EDIT
)
921 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLOREDIT
,
922 hDC
, lphc
->self
->hwndSelf
);
926 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
927 hDC
, lphc
->self
->hwndSelf
);
935 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
940 /***********************************************************************
941 * COMBO_EraseBackground
943 static LRESULT
COMBO_EraseBackground(
952 hDC
= (hParamDC
) ? hParamDC
956 * Calculate the area that we want to erase.
958 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
960 GetClientRect(hwnd
, &clientRect
);
964 CopyRect(&clientRect
, &lphc
->textRect
);
966 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
970 * Retrieve the background brush
972 hBkgBrush
= COMBO_PrepareColors(hwnd
, lphc
, hDC
);
974 FillRect(hDC
, &clientRect
, hBkgBrush
);
977 ReleaseDC(hwnd
, hDC
);
982 /***********************************************************************
985 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
990 hDC
= (hParamDC
) ? hParamDC
991 : BeginPaint( lphc
->self
->hwndSelf
, &ps
);
994 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
996 HBRUSH hPrevBrush
, hBkgBrush
;
999 * Retrieve the background brush and select it in the
1002 hBkgBrush
= COMBO_PrepareColors(lphc
->self
->hwndSelf
, lphc
, hDC
);
1004 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
1007 * In non 3.1 look, there is a sunken border on the combobox
1009 if (TWEAK_WineLook
!= WIN31_LOOK
)
1011 CBPaintBorder(CB_HWND(lphc
), lphc
, hDC
);
1014 if( !IsRectEmpty(&lphc
->buttonRect
) )
1016 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
1019 if( !(lphc
->wState
& CBF_EDIT
) )
1022 * The text area has a border only in Win 3.1 look.
1024 if (TWEAK_WineLook
== WIN31_LOOK
)
1026 HPEN hPrevPen
= SelectObject( hDC
, GetSysColorPen(COLOR_WINDOWFRAME
) );
1029 lphc
->textRect
.left
, lphc
->textRect
.top
,
1030 lphc
->textRect
.right
- 1, lphc
->textRect
.bottom
- 1);
1032 SelectObject( hDC
, hPrevPen
);
1035 CBPaintText( lphc
, hDC
, lphc
->textRect
);
1039 SelectObject( hDC
, hPrevBrush
);
1043 EndPaint(lphc
->self
->hwndSelf
, &ps
);
1048 /***********************************************************************
1051 * Select listbox entry according to the contents of the edit control.
1053 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
1059 length
= CB_GETEDITTEXTLENGTH( lphc
);
1062 pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1);
1064 TRACE("\t edit text length %i\n", length
);
1068 if( length
) GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1);
1069 else pText
[0] = '\0';
1070 idx
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
1071 (WPARAM
)(-1), (LPARAM
)pText
);
1072 HeapFree( GetProcessHeap(), 0, pText
);
1077 SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)(bSelect
? idx
: -1), 0 );
1079 /* probably superfluous but Windows sends this too */
1080 SendMessageA( lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0 );
1081 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0 );
1086 /***********************************************************************
1089 * Copy a listbox entry to the edit control.
1091 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1096 TRACE("\t %i\n", index
);
1098 if( index
>= 0 ) /* got an entry */
1100 length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1103 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1105 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1106 (WPARAM
)index
, (LPARAM
)pText
);
1110 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1111 SendMessageA( lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)"" );
1112 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1115 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1117 if( lphc
->wState
& CBF_FOCUSED
)
1118 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1121 HeapFree( GetProcessHeap(), 0, pText
);
1124 /***********************************************************************
1127 * Show listbox popup.
1129 static void CBDropDown( LPHEADCOMBO lphc
)
1135 TRACE("[%04x]: drop down\n", CB_HWND(lphc
));
1137 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1141 lphc
->wState
|= CBF_DROPPED
;
1142 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1144 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
1146 if( !(lphc
->wState
& CBF_CAPTURE
) )
1147 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1151 lphc
->droppedIndex
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1153 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
,
1154 (WPARAM
)(lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
), 0 );
1155 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1158 /* now set popup position */
1159 GetWindowRect( lphc
->self
->hwndSelf
, &rect
);
1162 * If it's a dropdown, the listbox is offset
1164 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1165 rect
.left
+= COMBO_EDITBUTTONSPACE();
1167 /* if the dropped height is greater than the total height of the dropped
1168 items list, then force the drop down list height to be the total height
1169 of the items in the dropped list */
1171 /* And Remove any extra space (Best Fit) */
1172 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1173 /* if listbox length has been set directly by its handle */
1174 GetWindowRect(lphc
->hWndLBox
, &r
);
1175 if (nDroppedHeight
< r
.bottom
- r
.top
)
1176 nDroppedHeight
= r
.bottom
- r
.top
;
1177 nItems
= (int)SendMessageA (lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1183 nHeight
= (int)SendMessageA (lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1186 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1187 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1190 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1191 if( (rect
.bottom
+ nDroppedHeight
) >= GetSystemMetrics( SM_CYSCREEN
) )
1192 rect
.bottom
= rect
.top
- nDroppedHeight
;
1194 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1195 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1197 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1200 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1201 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0, RDW_INVALIDATE
|
1202 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1204 EnableWindow( lphc
->hWndLBox
, TRUE
);
1205 if (GetCapture() != lphc
->self
->hwndSelf
)
1206 SetCapture(lphc
->hWndLBox
);
1209 /***********************************************************************
1212 * Hide listbox popup.
1214 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1216 HWND hWnd
= lphc
->self
->hwndSelf
;
1218 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1220 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1223 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc
), (INT
)ok
);
1225 if( lphc
->wState
& CBF_DROPPED
)
1229 lphc
->wState
&= ~CBF_DROPPED
;
1230 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1231 if(GetCapture() == lphc
->hWndLBox
)
1236 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1238 rect
= lphc
->buttonRect
;
1249 rect
= lphc
->textRect
;
1254 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1255 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1256 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1257 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1262 /***********************************************************************
1265 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1267 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1269 if( lphc
->wState
& CBF_DROPPED
)
1271 CBRollUp( lphc
, ok
, bRedrawButton
);
1279 /***********************************************************************
1282 static void CBRepaintButton( LPHEADCOMBO lphc
)
1284 InvalidateRect(CB_HWND(lphc
), &lphc
->buttonRect
, TRUE
);
1285 UpdateWindow(CB_HWND(lphc
));
1288 /***********************************************************************
1291 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1293 if( !(lphc
->wState
& CBF_FOCUSED
) )
1295 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1296 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1298 lphc
->wState
|= CBF_FOCUSED
;
1300 if( !(lphc
->wState
& CBF_EDIT
) )
1301 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1303 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1307 /***********************************************************************
1310 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1312 HWND hWnd
= lphc
->self
->hwndSelf
;
1314 if( lphc
->wState
& CBF_FOCUSED
)
1316 CBRollUp( lphc
, FALSE
, TRUE
);
1317 if( IsWindow( hWnd
) )
1319 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1320 SendMessageA( lphc
->hWndLBox
, LB_CARETOFF
, 0, 0 );
1322 lphc
->wState
&= ~CBF_FOCUSED
;
1325 if( !(lphc
->wState
& CBF_EDIT
) )
1326 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1328 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1333 /***********************************************************************
1336 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1338 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1340 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1342 switch( HIWORD(wParam
) >> 8 )
1344 case (EN_SETFOCUS
>> 8):
1346 TRACE("[%04x]: edit [%04x] got focus\n",
1347 CB_HWND(lphc
), lphc
->hWndEdit
);
1349 COMBO_SetFocus( lphc
);
1352 case (EN_KILLFOCUS
>> 8):
1354 TRACE("[%04x]: edit [%04x] lost focus\n",
1355 CB_HWND(lphc
), lphc
->hWndEdit
);
1357 /* NOTE: it seems that Windows' edit control sends an
1358 * undocumented message WM_USER + 0x1B instead of this
1359 * notification (only when it happens to be a part of
1360 * the combo). ?? - AK.
1363 COMBO_KillFocus( lphc
);
1367 case (EN_CHANGE
>> 8):
1369 * In some circumstances (when the selection of the combobox
1370 * is changed for example) we don't wans the EN_CHANGE notification
1371 * to be forwarded to the parent of the combobox. This code
1372 * checks a flag that is set in these occasions and ignores the
1375 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1376 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1378 if (lphc
->wState
& CBF_NOLBSELECT
)
1380 lphc
->wState
&= ~CBF_NOLBSELECT
;
1384 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1388 case (EN_UPDATE
>> 8):
1389 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1390 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1393 case (EN_ERRSPACE
>> 8):
1394 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1397 else if( lphc
->hWndLBox
== hWnd
)
1399 switch( HIWORD(wParam
) )
1402 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1406 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1412 TRACE("[%04x]: lbox selection change [%04x]\n",
1413 CB_HWND(lphc
), lphc
->wState
);
1415 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1417 if( lphc
->wState
& CBF_EDIT
)
1419 INT index
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1420 lphc
->wState
|= CBF_NOLBSELECT
;
1421 CBUpdateEdit( lphc
, index
);
1422 /* select text in edit, as Windows does */
1423 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1426 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1429 /* do not roll up if selection is being tracked
1430 * by arrowkeys in the dropdown listbox */
1432 if( (lphc
->dwStyle
& CBS_SIMPLE
) ||
1433 ((lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
)) )
1435 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1437 else lphc
->wState
&= ~CBF_NOROLLUP
;
1439 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1445 /* nothing to do here since ComboLBox always resets the focus to its
1446 * combo/edit counterpart */
1453 /***********************************************************************
1456 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1458 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
,
1459 WPARAM wParam
, LPARAM lParam
)
1461 HWND hWnd
= lphc
->self
->hwndSelf
;
1463 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc
), msg
);
1465 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1467 /* two first items are the same in all 4 structs */
1468 lpIS
->CtlType
= ODT_COMBOBOX
;
1469 lpIS
->CtlID
= lphc
->self
->wIDmenu
;
1471 switch( msg
) /* patch window handle */
1474 lpIS
->hwndItem
= hWnd
;
1478 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1479 lpIS
->hwndItem
= hWnd
;
1482 case WM_COMPAREITEM
:
1483 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1484 lpIS
->hwndItem
= hWnd
;
1489 return SendMessageA( lphc
->owner
, msg
, lphc
->self
->wIDmenu
, lParam
);
1492 /***********************************************************************
1495 static LRESULT
COMBO_GetText( LPHEADCOMBO lphc
, UINT N
, LPSTR lpText
)
1497 if( lphc
->wState
& CBF_EDIT
)
1498 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
,
1499 (WPARAM
)N
, (LPARAM
)lpText
);
1501 /* get it from the listbox */
1503 if( lphc
->hWndLBox
)
1505 INT idx
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1509 INT length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
,
1512 /* 'length' is without the terminating character */
1514 lpBuffer
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1 );
1520 INT n
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1521 (WPARAM
)idx
, (LPARAM
)lpBuffer
);
1523 /* truncate if buffer is too short */
1528 if( n
!= LB_ERR
) memcpy( lpText
, lpBuffer
, (N
>n
) ? n
+1 : N
-1 );
1529 lpText
[N
- 1] = '\0';
1531 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1541 /***********************************************************************
1544 * This function sets window positions according to the updated
1545 * component placement struct.
1547 static void CBResetPos(
1553 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1555 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1556 * sizing messages */
1558 if( lphc
->wState
& CBF_EDIT
)
1559 SetWindowPos( lphc
->hWndEdit
, 0,
1560 rectEdit
->left
, rectEdit
->top
,
1561 rectEdit
->right
- rectEdit
->left
,
1562 rectEdit
->bottom
- rectEdit
->top
,
1563 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1565 SetWindowPos( lphc
->hWndLBox
, 0,
1566 rectLB
->left
, rectLB
->top
,
1567 rectLB
->right
- rectLB
->left
,
1568 rectLB
->bottom
- rectLB
->top
,
1569 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1573 if( lphc
->wState
& CBF_DROPPED
)
1575 lphc
->wState
&= ~CBF_DROPPED
;
1576 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1579 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1580 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
1581 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1586 /***********************************************************************
1589 static void COMBO_Size( LPHEADCOMBO lphc
)
1591 CBCalcPlacement(lphc
->self
->hwndSelf
,
1595 &lphc
->droppedRect
);
1597 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1601 /***********************************************************************
1604 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1609 lphc
->hFont
= hFont
;
1612 * Propagate to owned windows.
1614 if( lphc
->wState
& CBF_EDIT
)
1615 SendMessageA( lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1616 SendMessageA( lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1619 * Redo the layout of the control.
1621 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1623 CBCalcPlacement(lphc
->self
->hwndSelf
,
1627 &lphc
->droppedRect
);
1629 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1633 CBForceDummyResize(lphc
);
1638 /***********************************************************************
1639 * COMBO_SetItemHeight
1641 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1643 LRESULT lRet
= CB_ERR
;
1645 if( index
== -1 ) /* set text field height */
1647 if( height
< 32768 )
1649 lphc
->editHeight
= height
;
1652 * Redo the layout of the control.
1654 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1656 CBCalcPlacement(lphc
->self
->hwndSelf
,
1660 &lphc
->droppedRect
);
1662 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1666 CBForceDummyResize(lphc
);
1672 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1673 lRet
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1674 (WPARAM
)index
, (LPARAM
)height
);
1678 /***********************************************************************
1679 * COMBO_SelectString
1681 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPCSTR pText
)
1683 INT index
= SendMessageA( lphc
->hWndLBox
, LB_SELECTSTRING
,
1684 (WPARAM
)start
, (LPARAM
)pText
);
1687 if( lphc
->wState
& CBF_EDIT
)
1688 CBUpdateEdit( lphc
, index
);
1691 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1694 return (LRESULT
)index
;
1697 /***********************************************************************
1700 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1704 HWND hWnd
= lphc
->self
->hwndSelf
;
1706 pt
.x
= LOWORD(lParam
);
1707 pt
.y
= HIWORD(lParam
);
1708 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1710 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1711 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1713 lphc
->wState
|= CBF_BUTTONDOWN
;
1714 if( lphc
->wState
& CBF_DROPPED
)
1716 /* got a click to cancel selection */
1718 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1719 CBRollUp( lphc
, TRUE
, FALSE
);
1720 if( !IsWindow( hWnd
) ) return;
1722 if( lphc
->wState
& CBF_CAPTURE
)
1724 lphc
->wState
&= ~CBF_CAPTURE
;
1730 /* drop down the listbox and start tracking */
1732 lphc
->wState
|= CBF_CAPTURE
;
1736 if( bButton
) CBRepaintButton( lphc
);
1740 /***********************************************************************
1743 * Release capture and stop tracking if needed.
1745 static void COMBO_LButtonUp( LPHEADCOMBO lphc
, LPARAM lParam
)
1747 if( lphc
->wState
& CBF_CAPTURE
)
1749 lphc
->wState
&= ~CBF_CAPTURE
;
1750 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1752 INT index
= CBUpdateLBox( lphc
, TRUE
);
1753 lphc
->wState
|= CBF_NOLBSELECT
;
1754 CBUpdateEdit( lphc
, index
);
1755 lphc
->wState
&= ~CBF_NOLBSELECT
;
1758 SetCapture(lphc
->hWndLBox
);
1762 if( lphc
->wState
& CBF_BUTTONDOWN
)
1764 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1765 CBRepaintButton( lphc
);
1769 /***********************************************************************
1772 * Two things to do - track combo button and release capture when
1773 * pointer goes into the listbox.
1775 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1780 pt
.x
= LOWORD(lParam
);
1781 pt
.y
= HIWORD(lParam
);
1783 if( lphc
->wState
& CBF_BUTTONDOWN
)
1787 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1791 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1792 CBRepaintButton( lphc
);
1796 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1797 MapWindowPoints( lphc
->self
->hwndSelf
, lphc
->hWndLBox
, &pt
, 1 );
1798 if( PtInRect(&lbRect
, pt
) )
1800 lphc
->wState
&= ~CBF_CAPTURE
;
1802 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1804 /* hand over pointer tracking */
1805 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1810 /***********************************************************************
1811 * ComboWndProc_locked
1813 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1815 static inline LRESULT WINAPI
ComboWndProc_locked( WND
* pWnd
, UINT message
,
1816 WPARAM wParam
, LPARAM lParam
)
1819 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1820 HWND hwnd
= pWnd
->hwndSelf
;
1822 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1823 pWnd
->hwndSelf
, SPY_GetMsgName(message
), wParam
, lParam
);
1825 if( lphc
|| message
== WM_NCCREATE
)
1829 /* System messages */
1832 return COMBO_NCCreate(pWnd
, lParam
);
1834 COMBO_NCDestroy(lphc
);
1835 break;/* -> DefWindowProc */
1838 return COMBO_Create(lphc
, pWnd
, lParam
);
1840 case WM_PRINTCLIENT
:
1841 if (lParam
& PRF_ERASEBKGND
)
1842 COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1846 /* wParam may contain a valid HDC! */
1847 return COMBO_Paint(lphc
, wParam
);
1849 return COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1852 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1853 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1855 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1857 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1858 result
|= DLGC_WANTMESSAGE
;
1862 case WM_WINDOWPOSCHANGING
:
1863 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1864 case WM_WINDOWPOSCHANGED
:
1865 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1866 * In that case, the Combobox itself will not be resized, so we won't
1867 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1872 if( lphc
->hWndLBox
&&
1873 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1876 COMBO_Font( lphc
, (HFONT16
)wParam
, (BOOL
)lParam
);
1879 return (LRESULT
)lphc
->hFont
;
1881 if( lphc
->wState
& CBF_EDIT
)
1882 SetFocus( lphc
->hWndEdit
);
1884 COMBO_SetFocus( lphc
);
1887 #define hwndFocus ((HWND16)wParam)
1889 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1890 COMBO_KillFocus( lphc
);
1894 return COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1896 return COMBO_GetText( lphc
, (UINT
)wParam
, (LPSTR
)lParam
);
1898 case WM_GETTEXTLENGTH
:
1900 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1902 int j
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1903 if (j
== -1) return 0;
1904 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1906 else if( lphc
->wState
& CBF_EDIT
)
1909 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1910 ret
= SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1911 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1918 if( lphc
->wState
& CBF_EDIT
)
1919 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1924 case WM_COMPAREITEM
:
1925 case WM_MEASUREITEM
:
1926 return COMBO_ItemOp( lphc
, message
, wParam
, lParam
);
1928 if( lphc
->wState
& CBF_EDIT
)
1929 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1930 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1932 /* Force the control to repaint when the enabled state changes. */
1933 InvalidateRect(CB_HWND(lphc
), NULL
, TRUE
);
1937 lphc
->wState
&= ~CBF_NOREDRAW
;
1939 lphc
->wState
|= CBF_NOREDRAW
;
1941 if( lphc
->wState
& CBF_EDIT
)
1942 SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1943 SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1946 if( KEYDATA_ALT
& HIWORD(lParam
) )
1947 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1948 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
1953 if (((CHAR
)wParam
== VK_RETURN
|| (CHAR
)wParam
== VK_ESCAPE
) &&
1954 (lphc
->wState
& CBF_DROPPED
))
1956 CBRollUp( lphc
, (CHAR
)wParam
== VK_RETURN
, FALSE
);
1960 if( lphc
->wState
& CBF_EDIT
)
1961 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1963 return SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1964 case WM_LBUTTONDOWN
:
1965 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
->hwndSelf
);
1966 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1969 COMBO_LButtonUp( lphc
, lParam
);
1972 if( lphc
->wState
& CBF_CAPTURE
)
1973 COMBO_MouseMove( lphc
, wParam
, lParam
);
1977 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
1978 return DefWindowProcA( hwnd
, message
, wParam
, lParam
);
1979 if ((short) HIWORD(wParam
) > 0) return SendMessageA(hwnd
,WM_KEYDOWN
,VK_UP
,0);
1980 if ((short) HIWORD(wParam
) < 0) return SendMessageA(hwnd
,WM_KEYDOWN
,VK_DOWN
,0);
1983 /* Combo messages */
1985 case CB_ADDSTRING16
:
1986 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
1988 return SendMessageA( lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1989 case CB_INSERTSTRING16
:
1990 wParam
= (INT
)(INT16
)wParam
;
1991 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
1992 case CB_INSERTSTRING
:
1993 return SendMessageA( lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1994 case CB_DELETESTRING16
:
1995 case CB_DELETESTRING
:
1996 return SendMessageA( lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1997 case CB_SELECTSTRING16
:
1998 wParam
= (INT
)(INT16
)wParam
;
1999 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2000 case CB_SELECTSTRING
:
2001 return COMBO_SelectString( lphc
, (INT
)wParam
, (LPSTR
)lParam
);
2002 case CB_FINDSTRING16
:
2003 wParam
= (INT
)(INT16
)wParam
;
2004 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2006 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2007 case CB_FINDSTRINGEXACT16
:
2008 wParam
= (INT
)(INT16
)wParam
;
2009 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)MapSL(lParam
);
2010 case CB_FINDSTRINGEXACT
:
2011 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRINGEXACT
,
2013 case CB_SETITEMHEIGHT16
:
2014 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
2015 case CB_SETITEMHEIGHT
:
2016 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2017 case CB_GETITEMHEIGHT16
:
2018 wParam
= (INT
)(INT16
)wParam
;
2019 case CB_GETITEMHEIGHT
:
2020 if( (INT
)wParam
>= 0 ) /* listbox item */
2021 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2022 return CBGetTextAreaHeight(hwnd
, lphc
);
2023 case CB_RESETCONTENT16
:
2024 case CB_RESETCONTENT
:
2025 SendMessageA( lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0 );
2026 if( CB_HASSTRINGS(lphc
) )
2027 SendMessageA( lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)"" );
2029 InvalidateRect(CB_HWND(lphc
), NULL
, TRUE
);
2031 case CB_INITSTORAGE
:
2032 return SendMessageA( lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2033 case CB_GETHORIZONTALEXTENT
:
2034 return SendMessageA( lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2035 case CB_SETHORIZONTALEXTENT
:
2036 return SendMessageA( lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2037 case CB_GETTOPINDEX
:
2038 return SendMessageA( lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2040 return SendMessageA( lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2042 return SendMessageA( lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2043 case CB_GETDROPPEDWIDTH
:
2044 if( lphc
->droppedWidth
)
2045 return lphc
->droppedWidth
;
2046 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2047 case CB_SETDROPPEDWIDTH
:
2048 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
2049 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
2051 case CB_GETDROPPEDCONTROLRECT16
:
2052 lParam
= (LPARAM
)MapSL(lParam
);
2056 CBGetDroppedControlRect( lphc
, &r
);
2057 CONV_RECT32TO16( &r
, (LPRECT16
)lParam
);
2060 case CB_GETDROPPEDCONTROLRECT
:
2061 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2063 case CB_GETDROPPEDSTATE16
:
2064 case CB_GETDROPPEDSTATE
:
2065 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
2067 lParam
= (LPARAM
)MapSL(lParam
);
2070 return COMBO_Directory( lphc
, (UINT
)wParam
,
2071 (LPSTR
)lParam
, (message
== CB_DIR
));
2072 case CB_SHOWDROPDOWN16
:
2073 case CB_SHOWDROPDOWN
:
2074 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2078 if( !(lphc
->wState
& CBF_DROPPED
) )
2082 if( lphc
->wState
& CBF_DROPPED
)
2083 CBRollUp( lphc
, FALSE
, TRUE
);
2088 return SendMessageA( lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2089 case CB_GETCURSEL16
:
2091 return SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2092 case CB_SETCURSEL16
:
2093 wParam
= (INT
)(INT16
)wParam
;
2095 lParam
= SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2097 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2098 if( lphc
->wState
& CBF_SELCHANGE
)
2100 /* no LBN_SELCHANGE in this case, update manually */
2101 if( lphc
->wState
& CBF_EDIT
)
2102 CBUpdateEdit( lphc
, (INT
)wParam
);
2104 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
2105 lphc
->wState
&= ~CBF_SELCHANGE
;
2108 case CB_GETLBTEXT16
:
2109 wParam
= (INT
)(INT16
)wParam
;
2110 lParam
= (LPARAM
)MapSL(lParam
);
2112 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2113 case CB_GETLBTEXTLEN16
:
2114 wParam
= (INT
)(INT16
)wParam
;
2115 case CB_GETLBTEXTLEN
:
2116 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2117 case CB_GETITEMDATA16
:
2118 wParam
= (INT
)(INT16
)wParam
;
2119 case CB_GETITEMDATA
:
2120 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2121 case CB_SETITEMDATA16
:
2122 wParam
= (INT
)(INT16
)wParam
;
2123 case CB_SETITEMDATA
:
2124 return SendMessageA( lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2125 case CB_GETEDITSEL16
:
2126 wParam
= lParam
= 0; /* just in case */
2128 if( lphc
->wState
& CBF_EDIT
)
2132 return SendMessageA( lphc
->hWndEdit
, EM_GETSEL
,
2133 (wParam
) ? wParam
: (WPARAM
)&a
,
2134 (lParam
) ? lParam
: (LPARAM
)&b
);
2137 case CB_SETEDITSEL16
:
2139 if( lphc
->wState
& CBF_EDIT
)
2140 return SendMessageA( lphc
->hWndEdit
, EM_SETSEL
,
2141 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2143 case CB_SETEXTENDEDUI16
:
2144 case CB_SETEXTENDEDUI
:
2145 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2148 lphc
->wState
|= CBF_EUI
;
2149 else lphc
->wState
&= ~CBF_EUI
;
2151 case CB_GETEXTENDEDUI16
:
2152 case CB_GETEXTENDEDUI
:
2153 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2156 if (message
>= WM_USER
)
2157 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2158 message
- WM_USER
, wParam
, lParam
);
2161 return DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2166 /***********************************************************************
2169 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2172 static LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2174 WND
* pWnd
= WIN_FindWndPtr(hwnd
);
2175 LRESULT retvalue
= ComboWndProc_locked(pWnd
,message
,wParam
,lParam
);
2177 WIN_ReleaseWndPtr(pWnd
);