4 * Copyright 1997 Alex Korobka
6 * FIXME: roll up in Netscape 3.01.
14 #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 ))
44 static HBITMAP hComboBmp
= 0;
45 static UINT CBitHeight
, CBitWidth
;
48 * Look and feel dependant "constants"
50 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
51 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
52 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
53 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
55 /***********************************************************************
58 * Load combo button bitmap.
60 static BOOL
COMBO_Init()
64 if( hComboBmp
) return TRUE
;
65 if( (hDC
= CreateCompatibleDC(0)) )
68 if( (hComboBmp
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO
))) )
74 GetObjectA( hComboBmp
, sizeof(bm
), &bm
);
75 CBitHeight
= bm
.bmHeight
;
76 CBitWidth
= bm
.bmWidth
;
78 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
80 hPrevB
= SelectObject16( hDC
, hComboBmp
);
81 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
82 InvertRect( hDC
, &r
);
83 SelectObject( hDC
, hPrevB
);
92 /***********************************************************************
95 static LRESULT
COMBO_NCCreate(WND
* wnd
, LPARAM lParam
)
99 if ( wnd
&& COMBO_Init() &&
100 (lphc
= HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO
))) )
102 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
104 memset( lphc
, 0, sizeof(HEADCOMBO
) );
105 *(LPHEADCOMBO
*)wnd
->wExtra
= lphc
;
107 /* some braindead apps do try to use scrollbar/border flags */
109 lphc
->dwStyle
= (lpcs
->style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
));
110 wnd
->dwStyle
&= ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
113 * We also have to remove the client edge style to make sure
114 * we don't end-up with a non client area.
116 wnd
->dwExStyle
&= ~(WS_EX_CLIENTEDGE
);
118 if( !(lpcs
->style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
119 lphc
->dwStyle
|= CBS_HASSTRINGS
;
120 if( !(wnd
->dwExStyle
& WS_EX_NOPARENTNOTIFY
) )
121 lphc
->wState
|= CBF_NOTIFY
;
123 TRACE("[0x%08x], style = %08x\n",
124 (UINT
)lphc
, lphc
->dwStyle
);
126 return (LRESULT
)(UINT
)wnd
->hwndSelf
;
128 return (LRESULT
)FALSE
;
131 /***********************************************************************
134 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
139 WND
* wnd
= lphc
->self
;
141 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc
));
143 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
144 DestroyWindow( lphc
->hWndLBox
);
146 HeapFree( GetProcessHeap(), 0, lphc
);
152 /***********************************************************************
155 * The dummy resize is used for listboxes that have a popup to trigger
156 * a re-arranging of the contents of the combobox and the recalculation
157 * of the size of the "real" control window.
159 static void CBForceDummyResize(
164 GetWindowRect(CB_HWND(lphc
), &windowRect
);
167 * We have to be careful, resizing a combobox also has the meaning that the
168 * dropped rect will be resized. In this case, we want to trigger a resize
169 * to recalculate layout but we don't want to change the dropped rectangle
170 * So, we add the size of the dropped rectangle to the size of the control.
171 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
174 SetWindowPos( CB_HWND(lphc
),
177 windowRect
.right
- windowRect
.left
,
178 windowRect
.bottom
- windowRect
.top
+
179 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
180 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
183 /***********************************************************************
184 * CBGetTextAreaHeight
186 * This method will calculate the height of the text area of the
188 * The height of the text area is set in two ways.
189 * It can be set explicitely through a combobox message of through a
190 * WM_MEASUREITEM callback.
191 * If this is not the case, the height is set to 13 dialog units.
192 * This height was determined through experimentation.
194 static INT
CBGetTextAreaHeight(
200 if( lphc
->editHeight
) /* explicitly set height */
202 iTextItemHeight
= lphc
->editHeight
;
207 HDC hDC
= GetDC(hwnd
);
212 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
214 GetTextMetricsA(hDC
, &tm
);
216 baseUnitY
= tm
.tmHeight
;
219 SelectObject( hDC
, hPrevFont
);
221 ReleaseDC(hwnd
, hDC
);
223 iTextItemHeight
= ((13 * baseUnitY
) / 8);
226 * This "formula" calculates the height of the complete control.
227 * To calculate the height of the text area, we have to remove the
230 iTextItemHeight
-= 2*COMBO_YBORDERSIZE();
234 * Check the ownerdraw case if we haven't asked the parent the size
237 if ( CB_OWNERDRAWN(lphc
) &&
238 (lphc
->wState
& CBF_MEASUREITEM
) )
240 MEASUREITEMSTRUCT measureItem
;
242 INT originalItemHeight
= iTextItemHeight
;
245 * We use the client rect for the width of the item.
247 GetClientRect(hwnd
, &clientRect
);
249 lphc
->wState
&= ~CBF_MEASUREITEM
;
252 * Send a first one to measure the size of the text area
254 measureItem
.CtlType
= ODT_COMBOBOX
;
255 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
256 measureItem
.itemID
= -1;
257 measureItem
.itemWidth
= clientRect
.right
;
258 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
259 measureItem
.itemData
= 0;
260 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
261 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
262 iTextItemHeight
= 6 + measureItem
.itemHeight
;
265 * Send a second one in the case of a fixed ownerdraw list to calculate the
266 * size of the list items. (we basically do this on behalf of the listbox)
268 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
270 measureItem
.CtlType
= ODT_COMBOBOX
;
271 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
272 measureItem
.itemID
= 0;
273 measureItem
.itemWidth
= clientRect
.right
;
274 measureItem
.itemHeight
= originalItemHeight
;
275 measureItem
.itemData
= 0;
276 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
277 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
278 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
282 * Keep the size for the next time
284 lphc
->editHeight
= iTextItemHeight
;
287 return iTextItemHeight
;
291 /***********************************************************************
294 * Set up component coordinates given valid lphc->RectCombo.
296 static void CBCalcPlacement(
304 * Again, start with the client rectangle.
306 GetClientRect(hwnd
, lprEdit
);
311 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
314 * Chop off the bottom part to fit with the height of the text area.
316 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
319 * The button starts the same vertical position as the text area.
321 CopyRect(lprButton
, lprEdit
);
324 * If the combobox is "simple" there is no button.
326 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
327 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
331 * Let's assume the combobox button is the same width as the
333 * size the button horizontally and cut-off the text area.
335 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
336 lprEdit
->right
= lprButton
->left
;
340 * In the case of a dropdown, there is an additional spacing between the
341 * text area and the button.
343 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
345 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
349 * If we have an edit control, we space it away from the borders slightly.
351 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
353 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
357 * Adjust the size of the listbox popup.
359 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
362 * Use the client rectangle to initialize the listbox rectangle
364 GetClientRect(hwnd
, lprLB
);
367 * Then, chop-off the top part.
369 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
374 * Make sure the dropped width is as large as the combobox itself.
376 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
378 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
381 * In the case of a dropdown, the popup listbox is offset to the right.
382 * so, we want to make sure it's flush with the right side of the
385 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
386 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
389 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
392 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
393 lprEdit
->left
, lprEdit
->top
, lprEdit
->right
, lprEdit
->bottom
);
395 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
396 lprButton
->left
, lprButton
->top
, lprButton
->right
, lprButton
->bottom
);
398 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
399 lprLB
->left
, lprLB
->top
, lprLB
->right
, lprLB
->bottom
);
402 /***********************************************************************
403 * CBGetDroppedControlRect
405 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
407 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
408 of the combo box and the lower right corner of the listbox */
410 GetWindowRect(lphc
->self
->hwndSelf
, lpRect
);
412 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
413 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
417 /***********************************************************************
418 * COMBO_WindowPosChanging
420 static LRESULT
COMBO_WindowPosChanging(
423 WINDOWPOS
* posChanging
)
426 * We need to override the WM_WINDOWPOSCHANGING method to handle all
427 * the non-simple comboboxes. The problem is that those controls are
428 * always the same height. We have to make sure they are not resized
431 if ( ( CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
432 ((posChanging
->flags
& SWP_NOSIZE
) == 0) )
436 newComboHeight
= CBGetTextAreaHeight(hwnd
,lphc
) +
437 2*COMBO_YBORDERSIZE();
440 * Resizing a combobox has another side effect, it resizes the dropped
441 * rectangle as well. However, it does it only if the new height for the
442 * combobox is different than the height it should have. In other words,
443 * if the application resizing the combobox only had the intention to resize
444 * the actual control, for example, to do the layout of a dialog that is
445 * resized, the height of the dropdown is not changed.
447 if (posChanging
->cy
!= newComboHeight
)
449 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ posChanging
->cy
- newComboHeight
;
451 posChanging
->cy
= newComboHeight
;
458 /***********************************************************************
461 static LRESULT
COMBO_Create( LPHEADCOMBO lphc
, WND
* wnd
, LPARAM lParam
)
463 static char clbName
[] = "ComboLBox";
464 static char editName
[] = "Edit";
466 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
468 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
469 else if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
472 lphc
->owner
= lpcs
->hwndParent
;
475 * The item height and dropped width are not set when the control
478 lphc
->droppedWidth
= lphc
->editHeight
= 0;
481 * The first time we go through, we want to measure the ownerdraw item
483 lphc
->wState
|= CBF_MEASUREITEM
;
485 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
487 if( lphc
->owner
|| !(lpcs
->style
& WS_VISIBLE
) )
492 * Initialize the dropped rect to the size of the client area of the
493 * control and then, force all the areas of the combobox to be
496 GetClientRect( wnd
->hwndSelf
, &lphc
->droppedRect
);
498 CBCalcPlacement(wnd
->hwndSelf
,
502 &lphc
->droppedRect
);
505 * Adjust the position of the popup listbox if it's necessary
507 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
509 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
512 * If it's a dropdown, the listbox is offset
514 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
515 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
517 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
);
518 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
.right
);
521 /* create listbox popup */
523 lbeStyle
= (LBS_NOTIFY
| WS_BORDER
| WS_CLIPSIBLINGS
) |
524 (lpcs
->style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
526 if( lphc
->dwStyle
& CBS_SORT
)
527 lbeStyle
|= LBS_SORT
;
528 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
529 lbeStyle
|= LBS_HASSTRINGS
;
530 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
531 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
532 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
533 lbeStyle
|= LBS_DISABLENOSCROLL
;
535 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
536 lbeStyle
|= WS_CHILD
| WS_VISIBLE
;
537 else /* popup listbox */
538 lbeStyle
|= WS_POPUP
;
540 /* Dropdown ComboLBox is not a child window and we cannot pass
541 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
543 lphc
->hWndLBox
= CreateWindowExA(0,
547 lphc
->droppedRect
.left
,
548 lphc
->droppedRect
.top
,
549 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
550 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
551 lphc
->self
->hwndSelf
,
552 (lphc
->dwStyle
& CBS_DROPDOWN
)? (HMENU
)0 : (HMENU
)ID_CB_LISTBOX
,
553 lphc
->self
->hInstance
,
557 * The ComboLBox is a strange little beast (when it's not a CBS_SIMPLE)...
558 * It's a popup window but, when you get the window style, you get WS_CHILD.
559 * When created, it's parent is the combobox but, when you ask for it's parent
560 * after that, you're supposed to get the desktop. (see MFC code function
562 * To achieve this in Wine, we have to create it as a popup and change
563 * it's style to child after the creation.
565 if ( (lphc
->hWndLBox
!= 0) &&
566 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
568 SetWindowLongA(lphc
->hWndLBox
,
570 (GetWindowLongA(lphc
->hWndLBox
, GWL_STYLE
) | WS_CHILD
) & ~WS_POPUP
);
576 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
;
579 * In Win95 look, the border fo the edit control is
580 * provided by the combobox
582 if (TWEAK_WineLook
== WIN31_LOOK
)
583 lbeStyle
|= WS_BORDER
;
585 if( lphc
->wState
& CBF_EDIT
)
587 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
588 lbeStyle
|= ES_OEMCONVERT
;
589 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
590 lbeStyle
|= ES_AUTOHSCROLL
;
591 if( lphc
->dwStyle
& CBS_LOWERCASE
)
592 lbeStyle
|= ES_LOWERCASE
;
593 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
594 lbeStyle
|= ES_UPPERCASE
;
596 lphc
->hWndEdit
= CreateWindowExA(0,
600 lphc
->textRect
.left
, lphc
->textRect
.top
,
601 lphc
->textRect
.right
- lphc
->textRect
.left
,
602 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
603 lphc
->self
->hwndSelf
,
605 lphc
->self
->hInstance
,
608 if( !lphc
->hWndEdit
)
615 * If the combo is a dropdown, we must resize the control to fit only
616 * the text area and button. To do this, we send a dummy resize and the
617 * WM_WINDOWPOSCHANGING message will take care of setting the height for
620 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
622 CBForceDummyResize(lphc
);
625 TRACE("init done\n");
626 return wnd
->hwndSelf
;
628 ERR("edit control failure.\n");
629 } else ERR("listbox failure.\n");
630 } else ERR("no owner for visible combo.\n");
632 /* CreateWindow() will send WM_NCDESTROY to cleanup */
637 /***********************************************************************
640 * Paint combo button (normal, pressed, and disabled states).
642 static void CBPaintButton(
651 COLORREF oldTextColor
, oldBkColor
;
653 if( lphc
->wState
& CBF_NOREDRAW
)
656 hPrevBrush
= SelectObject(hdc
, GetSysColorBrush(COLOR_BTNFACE
));
659 * Draw the button background
664 rectButton
.right
-rectButton
.left
,
665 rectButton
.bottom
-rectButton
.top
,
668 if( (bBool
= lphc
->wState
& CBF_BUTTONDOWN
) )
670 DrawEdge( hdc
, &rectButton
, EDGE_SUNKEN
, BF_RECT
);
674 DrawEdge( hdc
, &rectButton
, EDGE_RAISED
, BF_RECT
);
678 * Remove the edge of the button from the rectangle
679 * and calculate the position of the bitmap.
681 InflateRect( &rectButton
, -2, -2);
683 x
= (rectButton
.left
+ rectButton
.right
- CBitWidth
) >> 1;
684 y
= (rectButton
.top
+ rectButton
.bottom
- CBitHeight
) >> 1;
687 hMemDC
= CreateCompatibleDC( hdc
);
688 SelectObject( hMemDC
, hComboBmp
);
689 oldTextColor
= SetTextColor( hdc
, GetSysColor(COLOR_BTNFACE
) );
690 oldBkColor
= SetBkColor( hdc
, CB_DISABLED(lphc
) ? RGB(128,128,128) :
692 BitBlt( hdc
, x
, y
, CBitWidth
, CBitHeight
, hMemDC
, 0, 0, SRCCOPY
);
693 SetBkColor( hdc
, oldBkColor
);
694 SetTextColor( hdc
, oldTextColor
);
696 SelectObject( hdc
, hPrevBrush
);
699 /***********************************************************************
702 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
704 static void CBPaintText(
712 if( lphc
->wState
& CBF_NOREDRAW
) return;
714 /* follow Windows combobox that sends a bunch of text
715 * inquiries to its listbox while processing WM_PAINT. */
717 if( (id
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
719 size
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
720 if( (pText
= HeapAlloc( GetProcessHeap(), 0, size
+ 1)) )
722 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
723 pText
[size
] = '\0'; /* just in case */
727 if( lphc
->wState
& CBF_EDIT
)
729 if( CB_HASSTRINGS(lphc
) ) SetWindowTextA( lphc
->hWndEdit
, pText
? pText
: "" );
730 if( lphc
->wState
& CBF_FOCUSED
)
731 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
733 else /* paint text field ourselves */
735 HBRUSH hPrevBrush
= 0;
740 if ((hDC
= GetDC(lphc
->self
->hwndSelf
)))
742 HBRUSH hBrush
= SendMessageA( lphc
->owner
,
744 hDC
, lphc
->self
->hwndSelf
);
745 hPrevBrush
= SelectObject( hDC
,
746 (hBrush
) ? hBrush
: GetStockObject(WHITE_BRUSH
) );
752 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hDC
, lphc
->hFont
) : 0;
755 * Give ourselves some space.
757 InflateRect( &rectEdit
, -1, -1 );
759 if ( (lphc
->wState
& CBF_FOCUSED
) &&
760 !(lphc
->wState
& CBF_DROPPED
) )
764 FillRect( hDC
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
765 SetBkColor( hDC
, GetSysColor( COLOR_HIGHLIGHT
) );
766 SetTextColor( hDC
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
767 itemState
= ODS_SELECTED
| ODS_FOCUS
;
772 if( CB_OWNERDRAWN(lphc
) )
778 * Save the current clip region.
779 * To retrieve the clip region, we need to create one "dummy"
782 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
784 if (GetClipRgn(hDC
, clipRegion
)!=1)
786 DeleteObject(clipRegion
);
787 clipRegion
=(HRGN
)NULL
;
790 if ( lphc
->self
->dwStyle
& WS_DISABLED
)
791 itemState
|= ODS_DISABLED
;
793 dis
.CtlType
= ODT_COMBOBOX
;
794 dis
.CtlID
= lphc
->self
->wIDmenu
;
795 dis
.hwndItem
= lphc
->self
->hwndSelf
;
796 dis
.itemAction
= ODA_DRAWENTIRE
;
798 dis
.itemState
= itemState
;
800 dis
.rcItem
= rectEdit
;
801 dis
.itemData
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
,
805 * Clip the DC and have the parent draw the item.
807 IntersectClipRect(hDC
,
808 rectEdit
.left
, rectEdit
.top
,
809 rectEdit
.right
, rectEdit
.bottom
);
811 SendMessageA(lphc
->owner
, WM_DRAWITEM
,
812 lphc
->self
->wIDmenu
, (LPARAM
)&dis
);
815 * Reset the clipping region.
817 SelectClipRgn(hDC
, clipRegion
);
824 ETO_OPAQUE
| ETO_CLIPPED
,
826 pText
? pText
: "" , size
, NULL
);
828 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
829 DrawFocusRect( hDC
, &rectEdit
);
833 SelectObject(hDC
, hPrevFont
);
838 SelectObject( hDC
, hPrevBrush
);
840 ReleaseDC( lphc
->self
->hwndSelf
, hDC
);
845 HeapFree( GetProcessHeap(), 0, pText
);
848 /***********************************************************************
851 static void CBPaintBorder(
858 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
860 GetClientRect(hwnd
, &clientRect
);
864 CopyRect(&clientRect
, &lphc
->textRect
);
866 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
867 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
870 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
873 /***********************************************************************
874 * COMBO_EraseBackground
876 static LRESULT
COMBO_EraseBackground(
885 hDC
= (hParamDC
) ? hParamDC
889 * Calculate the area that we want to erase.
891 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
893 GetClientRect(hwnd
, &clientRect
);
897 CopyRect(&clientRect
, &lphc
->textRect
);
899 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
902 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
906 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
908 FillRect(hDC
, &clientRect
, hBkgBrush
);
911 ReleaseDC(hwnd
, hDC
);
916 /***********************************************************************
919 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
924 hDC
= (hParamDC
) ? hParamDC
925 : BeginPaint( lphc
->self
->hwndSelf
, &ps
);
928 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
930 HBRUSH hPrevBrush
, hBkgBrush
;
932 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
933 hDC
, lphc
->self
->hwndSelf
);
936 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
938 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
941 * In non 3.1 look, there is a sunken border on the combobox
943 if (TWEAK_WineLook
!= WIN31_LOOK
)
945 CBPaintBorder(CB_HWND(lphc
), lphc
, hDC
);
948 if( !IsRectEmpty(&lphc
->buttonRect
) )
950 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
953 if( !(lphc
->wState
& CBF_EDIT
) )
956 * The text area has a border only in Win 3.1 look.
958 if (TWEAK_WineLook
== WIN31_LOOK
)
960 HPEN hPrevPen
= SelectObject( hDC
, GetSysColorPen(COLOR_WINDOWFRAME
) );
963 lphc
->textRect
.left
, lphc
->textRect
.top
,
964 lphc
->textRect
.right
- 1, lphc
->textRect
.bottom
- 1);
966 SelectObject( hDC
, hPrevPen
);
969 CBPaintText( lphc
, hDC
, lphc
->textRect
);
973 SelectObject( hDC
, hPrevBrush
);
977 EndPaint(lphc
->self
->hwndSelf
, &ps
);
982 /***********************************************************************
985 * Select listbox entry according to the contents of the edit control.
987 static INT
CBUpdateLBox( LPHEADCOMBO lphc
)
989 INT length
, idx
, ret
;
993 length
= CB_GETEDITTEXTLENGTH( lphc
);
996 pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1);
998 TRACE("\t edit text length %i\n", length
);
1002 if( length
) GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1);
1003 else pText
[0] = '\0';
1004 idx
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
1005 (WPARAM
)(-1), (LPARAM
)pText
);
1006 if( idx
== LB_ERR
) idx
= 0; /* select first item */
1008 HeapFree( GetProcessHeap(), 0, pText
);
1013 SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)idx
, 0 );
1017 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)idx
, 0 );
1018 /* probably superfluous but Windows sends this too */
1019 SendMessageA( lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)idx
, 0 );
1024 /***********************************************************************
1027 * Copy a listbox entry to the edit control.
1029 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1034 TRACE("\t %i\n", index
);
1038 length
= CB_GETEDITTEXTLENGTH( lphc
);
1041 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1043 GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1 );
1044 index
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
1045 (WPARAM
)(-1), (LPARAM
)pText
);
1046 HeapFree( GetProcessHeap(), 0, pText
);
1051 if( index
>= 0 ) /* got an entry */
1053 length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1056 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1058 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1059 (WPARAM
)index
, (LPARAM
)pText
);
1061 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1063 SendMessageA( lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)pText
);
1064 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1065 HeapFree( GetProcessHeap(), 0, pText
);
1071 /***********************************************************************
1074 * Show listbox popup.
1076 static void CBDropDown( LPHEADCOMBO lphc
)
1080 TRACE("[%04x]: drop down\n", CB_HWND(lphc
));
1082 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1086 lphc
->wState
|= CBF_DROPPED
;
1087 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1089 lphc
->droppedIndex
= CBUpdateLBox( lphc
);
1091 if( !(lphc
->wState
& CBF_CAPTURE
) )
1092 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1096 lphc
->droppedIndex
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1098 if( lphc
->droppedIndex
== LB_ERR
)
1099 lphc
->droppedIndex
= 0;
1101 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)lphc
->droppedIndex
, 0 );
1102 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1105 /* now set popup position */
1106 GetWindowRect( lphc
->self
->hwndSelf
, &rect
);
1110 * If it's a dropdown, the listbox is offset
1112 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1113 rect
.left
+= COMBO_EDITBUTTONSPACE();
1115 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1116 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1117 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
1118 SWP_NOACTIVATE
| SWP_NOREDRAW
);
1120 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1121 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0, RDW_INVALIDATE
|
1122 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1124 ShowWindow( lphc
->hWndLBox
, SW_SHOWNA
);
1127 /***********************************************************************
1130 * Hide listbox popup.
1132 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1134 HWND hWnd
= lphc
->self
->hwndSelf
;
1136 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1138 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1141 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc
), (INT
)ok
);
1144 * It seems useful to send the WM_LBUTTONUP with (-1,-1) when cancelling
1145 * and with (0,0) (anywhere in the listbox) when Oking.
1147 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONUP
, 0, ok
? (LPARAM
)0 : (LPARAM
)(-1) );
1149 if( lphc
->wState
& CBF_DROPPED
)
1153 lphc
->wState
&= ~CBF_DROPPED
;
1154 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1156 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1158 INT index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1159 CBUpdateEdit( lphc
, index
);
1160 rect
= lphc
->buttonRect
;
1171 rect
= lphc
->textRect
;
1176 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1177 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1178 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1179 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1184 /***********************************************************************
1187 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1189 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL bRedrawButton
)
1191 if( lphc
->wState
& CBF_DROPPED
)
1193 CBRollUp( lphc
, TRUE
, bRedrawButton
);
1201 /***********************************************************************
1204 * Edit control helper.
1206 HWND
COMBO_GetLBWindow( WND
* pWnd
)
1208 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1209 if( lphc
) return lphc
->hWndLBox
;
1214 /***********************************************************************
1217 static void CBRepaintButton( LPHEADCOMBO lphc
)
1219 InvalidateRect(CB_HWND(lphc
), &lphc
->buttonRect
, TRUE
);
1220 UpdateWindow(CB_HWND(lphc
));
1223 /***********************************************************************
1226 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1228 if( !(lphc
->wState
& CBF_FOCUSED
) )
1230 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1231 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1233 if( lphc
->wState
& CBF_EDIT
)
1234 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1235 lphc
->wState
|= CBF_FOCUSED
;
1236 if( !(lphc
->wState
& CBF_EDIT
) )
1238 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1241 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1245 /***********************************************************************
1248 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1250 HWND hWnd
= lphc
->self
->hwndSelf
;
1252 if( lphc
->wState
& CBF_FOCUSED
)
1254 SendMessageA( hWnd
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1256 CBRollUp( lphc
, FALSE
, TRUE
);
1257 if( IsWindow( hWnd
) )
1259 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1260 SendMessageA( lphc
->hWndLBox
, LB_CARETOFF
, 0, 0 );
1262 lphc
->wState
&= ~CBF_FOCUSED
;
1265 if( lphc
->wState
& CBF_EDIT
)
1266 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, (WPARAM
)(-1), 0 );
1269 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1272 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1277 /***********************************************************************
1280 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1282 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1284 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1286 switch( HIWORD(wParam
) >> 8 )
1288 case (EN_SETFOCUS
>> 8):
1290 TRACE("[%04x]: edit [%04x] got focus\n",
1291 CB_HWND(lphc
), lphc
->hWndEdit
);
1293 if( !(lphc
->wState
& CBF_FOCUSED
) ) COMBO_SetFocus( lphc
);
1296 case (EN_KILLFOCUS
>> 8):
1298 TRACE("[%04x]: edit [%04x] lost focus\n",
1299 CB_HWND(lphc
), lphc
->hWndEdit
);
1301 /* NOTE: it seems that Windows' edit control sends an
1302 * undocumented message WM_USER + 0x1B instead of this
1303 * notification (only when it happens to be a part of
1304 * the combo). ?? - AK.
1307 COMBO_KillFocus( lphc
);
1311 case (EN_CHANGE
>> 8):
1313 * In some circumstances (when the selection of the combobox
1314 * is changed for example) we don't wans the EN_CHANGE notification
1315 * to be forwarded to the parent of the combobox. This code
1316 * checks a flag that is set in these occasions and ignores the
1319 if (lphc
->wState
& CBF_NOEDITNOTIFY
)
1321 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1325 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1328 CBUpdateLBox( lphc
);
1331 case (EN_UPDATE
>> 8):
1332 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1335 case (EN_ERRSPACE
>> 8):
1336 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1339 else if( lphc
->hWndLBox
== hWnd
)
1341 switch( HIWORD(wParam
) )
1344 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1348 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1354 TRACE("[%04x]: lbox selection change [%04x]\n",
1355 CB_HWND(lphc
), lphc
->wState
);
1357 /* do not roll up if selection is being tracked
1358 * by arrowkeys in the dropdown listbox */
1360 if( (lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
) )
1361 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1362 else lphc
->wState
&= ~CBF_NOROLLUP
;
1364 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1365 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1370 /* nothing to do here since ComboLBox always resets the focus to its
1371 * combo/edit counterpart */
1378 /***********************************************************************
1381 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1383 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
,
1384 WPARAM wParam
, LPARAM lParam
)
1386 HWND hWnd
= lphc
->self
->hwndSelf
;
1388 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc
), msg
);
1390 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1392 /* two first items are the same in all 4 structs */
1393 lpIS
->CtlType
= ODT_COMBOBOX
;
1394 lpIS
->CtlID
= lphc
->self
->wIDmenu
;
1396 switch( msg
) /* patch window handle */
1399 lpIS
->hwndItem
= hWnd
;
1403 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1404 lpIS
->hwndItem
= hWnd
;
1407 case WM_COMPAREITEM
:
1408 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1409 lpIS
->hwndItem
= hWnd
;
1414 return SendMessageA( lphc
->owner
, msg
, lphc
->self
->wIDmenu
, lParam
);
1417 /***********************************************************************
1420 static LRESULT
COMBO_GetText( LPHEADCOMBO lphc
, UINT N
, LPSTR lpText
)
1422 if( lphc
->wState
& CBF_EDIT
)
1423 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
,
1424 (WPARAM
)N
, (LPARAM
)lpText
);
1426 /* get it from the listbox */
1428 if( lphc
->hWndLBox
)
1430 INT idx
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1434 INT length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
,
1437 /* 'length' is without the terminating character */
1439 lpBuffer
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1 );
1445 INT n
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1446 (WPARAM
)idx
, (LPARAM
)lpBuffer
);
1448 /* truncate if buffer is too short */
1453 if( n
!= LB_ERR
) memcpy( lpText
, lpBuffer
, (N
>n
) ? n
+1 : N
-1 );
1454 lpText
[N
- 1] = '\0';
1456 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1466 /***********************************************************************
1469 * This function sets window positions according to the updated
1470 * component placement struct.
1472 static void CBResetPos(
1478 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1480 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1481 * sizing messages */
1483 if( lphc
->wState
& CBF_EDIT
)
1484 SetWindowPos( lphc
->hWndEdit
, 0,
1485 rectEdit
->left
, rectEdit
->top
,
1486 rectEdit
->right
- rectEdit
->left
,
1487 rectEdit
->bottom
- rectEdit
->top
,
1488 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1490 SetWindowPos( lphc
->hWndLBox
, 0,
1491 rectLB
->left
, rectLB
->top
,
1492 rectLB
->right
- rectLB
->left
,
1493 rectLB
->bottom
- rectLB
->top
,
1494 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1498 if( lphc
->wState
& CBF_DROPPED
)
1500 lphc
->wState
&= ~CBF_DROPPED
;
1501 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1504 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1505 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
1506 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1511 /***********************************************************************
1514 static void COMBO_Size( LPHEADCOMBO lphc
)
1516 CBCalcPlacement(lphc
->self
->hwndSelf
,
1520 &lphc
->droppedRect
);
1522 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1526 /***********************************************************************
1529 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1534 lphc
->hFont
= hFont
;
1537 * Propagate to owned windows.
1539 if( lphc
->wState
& CBF_EDIT
)
1540 SendMessageA( lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1541 SendMessageA( lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1544 * Redo the layout of the control.
1546 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1548 CBCalcPlacement(lphc
->self
->hwndSelf
,
1552 &lphc
->droppedRect
);
1554 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1558 CBForceDummyResize(lphc
);
1563 /***********************************************************************
1564 * COMBO_SetItemHeight
1566 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1568 LRESULT lRet
= CB_ERR
;
1570 if( index
== -1 ) /* set text field height */
1572 if( height
< 32768 )
1574 lphc
->editHeight
= height
;
1577 * Redo the layout of the control.
1579 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1581 CBCalcPlacement(lphc
->self
->hwndSelf
,
1585 &lphc
->droppedRect
);
1587 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1591 CBForceDummyResize(lphc
);
1597 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1598 lRet
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1599 (WPARAM
)index
, (LPARAM
)height
);
1603 /***********************************************************************
1604 * COMBO_SelectString
1606 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPCSTR pText
)
1608 INT index
= SendMessageA( lphc
->hWndLBox
, LB_SELECTSTRING
,
1609 (WPARAM
)start
, (LPARAM
)pText
);
1612 if( lphc
->wState
& CBF_EDIT
)
1613 CBUpdateEdit( lphc
, index
);
1616 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1619 return (LRESULT
)index
;
1622 /***********************************************************************
1625 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1629 HWND hWnd
= lphc
->self
->hwndSelf
;
1631 pt
.x
= LOWORD(lParam
);
1632 pt
.y
= HIWORD(lParam
);
1633 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1635 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1636 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1638 lphc
->wState
|= CBF_BUTTONDOWN
;
1639 if( lphc
->wState
& CBF_DROPPED
)
1641 /* got a click to cancel selection */
1643 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1644 CBRollUp( lphc
, TRUE
, FALSE
);
1645 if( !IsWindow( hWnd
) ) return;
1647 if( lphc
->wState
& CBF_CAPTURE
)
1649 lphc
->wState
&= ~CBF_CAPTURE
;
1655 /* drop down the listbox and start tracking */
1657 lphc
->wState
|= CBF_CAPTURE
;
1661 if( bButton
) CBRepaintButton( lphc
);
1665 /***********************************************************************
1668 * Release capture and stop tracking if needed.
1670 static void COMBO_LButtonUp( LPHEADCOMBO lphc
, LPARAM lParam
)
1672 if( lphc
->wState
& CBF_CAPTURE
)
1674 lphc
->wState
&= ~CBF_CAPTURE
;
1675 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1677 INT index
= CBUpdateLBox( lphc
);
1678 CBUpdateEdit( lphc
, index
);
1683 if( lphc
->wState
& CBF_BUTTONDOWN
)
1685 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1686 CBRepaintButton( lphc
);
1690 /***********************************************************************
1693 * Two things to do - track combo button and release capture when
1694 * pointer goes into the listbox.
1696 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1701 pt
.x
= LOWORD(lParam
);
1702 pt
.y
= HIWORD(lParam
);
1704 if( lphc
->wState
& CBF_BUTTONDOWN
)
1708 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1712 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1713 CBRepaintButton( lphc
);
1717 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1718 MapWindowPoints( lphc
->self
->hwndSelf
, lphc
->hWndLBox
, &pt
, 1 );
1719 if( PtInRect(&lbRect
, pt
) )
1721 lphc
->wState
&= ~CBF_CAPTURE
;
1723 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
);
1725 /* hand over pointer tracking */
1726 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1731 /***********************************************************************
1732 * ComboWndProc_locked
1734 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1736 static inline LRESULT WINAPI
ComboWndProc_locked( WND
* pWnd
, UINT message
,
1737 WPARAM wParam
, LPARAM lParam
)
1740 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1741 HWND hwnd
= pWnd
->hwndSelf
;
1743 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1744 pWnd
->hwndSelf
, SPY_GetMsgName(message
), wParam
, lParam
);
1746 if( lphc
|| message
== WM_NCCREATE
)
1750 /* System messages */
1753 return COMBO_NCCreate(pWnd
, lParam
);
1755 COMBO_NCDestroy(lphc
);
1756 break;/* -> DefWindowProc */
1759 return COMBO_Create(lphc
, pWnd
, lParam
);
1761 case WM_PRINTCLIENT
:
1762 if (lParam
& PRF_ERASEBKGND
)
1763 COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1767 /* wParam may contain a valid HDC! */
1768 return COMBO_Paint(lphc
, wParam
);
1770 return COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1772 return (LRESULT
)(DLGC_WANTARROWS
| DLGC_WANTCHARS
);
1773 case WM_WINDOWPOSCHANGING
:
1774 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1776 if( lphc
->hWndLBox
&&
1777 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1780 COMBO_Font( lphc
, (HFONT16
)wParam
, (BOOL
)lParam
);
1783 return (LRESULT
)lphc
->hFont
;
1785 if( lphc
->wState
& CBF_EDIT
)
1786 SetFocus( lphc
->hWndEdit
);
1788 COMBO_SetFocus( lphc
);
1791 #define hwndFocus ((HWND16)wParam)
1793 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1794 COMBO_KillFocus( lphc
);
1798 return COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1800 return COMBO_GetText( lphc
, (UINT
)wParam
, (LPSTR
)lParam
);
1802 case WM_GETTEXTLENGTH
:
1807 if( lphc
->wState
& CBF_EDIT
)
1809 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1811 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1816 case WM_COMPAREITEM
:
1817 case WM_MEASUREITEM
:
1818 return COMBO_ItemOp( lphc
, message
, wParam
, lParam
);
1820 if( lphc
->wState
& CBF_EDIT
)
1821 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1822 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1826 lphc
->wState
&= ~CBF_NOREDRAW
;
1828 lphc
->wState
|= CBF_NOREDRAW
;
1830 if( lphc
->wState
& CBF_EDIT
)
1831 SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1832 SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1835 if( KEYDATA_ALT
& HIWORD(lParam
) )
1836 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1837 COMBO_FlipListbox( lphc
, TRUE
);
1838 break;/* -> DefWindowProc */
1842 if( lphc
->wState
& CBF_EDIT
)
1843 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1845 return SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1846 case WM_LBUTTONDOWN
:
1847 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
->hwndSelf
);
1848 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1851 COMBO_LButtonUp( lphc
, lParam
);
1854 if( lphc
->wState
& CBF_CAPTURE
)
1855 COMBO_MouseMove( lphc
, wParam
, lParam
);
1857 /* Combo messages */
1859 case CB_ADDSTRING16
:
1860 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1862 return SendMessageA( lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1863 case CB_INSERTSTRING16
:
1864 wParam
= (INT
)(INT16
)wParam
;
1865 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1866 case CB_INSERTSTRING
:
1867 return SendMessageA( lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1868 case CB_DELETESTRING16
:
1869 case CB_DELETESTRING
:
1870 return SendMessageA( lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1871 case CB_SELECTSTRING16
:
1872 wParam
= (INT
)(INT16
)wParam
;
1873 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1874 case CB_SELECTSTRING
:
1875 return COMBO_SelectString( lphc
, (INT
)wParam
, (LPSTR
)lParam
);
1876 case CB_FINDSTRING16
:
1877 wParam
= (INT
)(INT16
)wParam
;
1878 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1880 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
1881 case CB_FINDSTRINGEXACT16
:
1882 wParam
= (INT
)(INT16
)wParam
;
1883 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1884 case CB_FINDSTRINGEXACT
:
1885 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRINGEXACT
,
1887 case CB_SETITEMHEIGHT16
:
1888 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
1889 case CB_SETITEMHEIGHT
:
1890 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
1891 case CB_GETITEMHEIGHT16
:
1892 wParam
= (INT
)(INT16
)wParam
;
1893 case CB_GETITEMHEIGHT
:
1894 if( (INT
)wParam
>= 0 ) /* listbox item */
1895 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
1896 return CBGetTextAreaHeight(hwnd
, lphc
);
1897 case CB_RESETCONTENT16
:
1898 case CB_RESETCONTENT
:
1899 SendMessageA( lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0 );
1900 InvalidateRect(CB_HWND(lphc
), NULL
, TRUE
);
1902 case CB_INITSTORAGE
:
1903 return SendMessageA( lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
1904 case CB_GETHORIZONTALEXTENT
:
1905 return SendMessageA( lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
1906 case CB_SETHORIZONTALEXTENT
:
1907 return SendMessageA( lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
1908 case CB_GETTOPINDEX
:
1909 return SendMessageA( lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
1911 return SendMessageA( lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
1913 return SendMessageA( lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
1914 case CB_GETDROPPEDWIDTH
:
1915 if( lphc
->droppedWidth
)
1916 return lphc
->droppedWidth
;
1917 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1918 case CB_SETDROPPEDWIDTH
:
1919 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
1920 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
1922 case CB_GETDROPPEDCONTROLRECT16
:
1923 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1927 CBGetDroppedControlRect( lphc
, &r
);
1928 CONV_RECT32TO16( &r
, (LPRECT16
)lParam
);
1931 case CB_GETDROPPEDCONTROLRECT
:
1932 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
1934 case CB_GETDROPPEDSTATE16
:
1935 case CB_GETDROPPEDSTATE
:
1936 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
1938 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1941 return COMBO_Directory( lphc
, (UINT
)wParam
,
1942 (LPSTR
)lParam
, (message
== CB_DIR
));
1943 case CB_SHOWDROPDOWN16
:
1944 case CB_SHOWDROPDOWN
:
1945 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1949 if( !(lphc
->wState
& CBF_DROPPED
) )
1953 if( lphc
->wState
& CBF_DROPPED
)
1954 CBRollUp( lphc
, FALSE
, TRUE
);
1959 return SendMessageA( lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1960 case CB_GETCURSEL16
:
1962 return SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1963 case CB_SETCURSEL16
:
1964 wParam
= (INT
)(INT16
)wParam
;
1966 lParam
= SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
1967 if( lphc
->wState
& CBF_SELCHANGE
)
1969 /* no LBN_SELCHANGE in this case, update manually */
1970 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1971 lphc
->wState
&= ~CBF_SELCHANGE
;
1974 case CB_GETLBTEXT16
:
1975 wParam
= (INT
)(INT16
)wParam
;
1976 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1978 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
1979 case CB_GETLBTEXTLEN16
:
1980 wParam
= (INT
)(INT16
)wParam
;
1981 case CB_GETLBTEXTLEN
:
1982 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
1983 case CB_GETITEMDATA16
:
1984 wParam
= (INT
)(INT16
)wParam
;
1985 case CB_GETITEMDATA
:
1986 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
1987 case CB_SETITEMDATA16
:
1988 wParam
= (INT
)(INT16
)wParam
;
1989 case CB_SETITEMDATA
:
1990 return SendMessageA( lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
1991 case CB_GETEDITSEL16
:
1992 wParam
= lParam
= 0; /* just in case */
1994 if( lphc
->wState
& CBF_EDIT
)
1998 return SendMessageA( lphc
->hWndEdit
, EM_GETSEL
,
1999 (wParam
) ? wParam
: (WPARAM
)&a
,
2000 (lParam
) ? lParam
: (LPARAM
)&b
);
2003 case CB_SETEDITSEL16
:
2005 if( lphc
->wState
& CBF_EDIT
)
2006 return SendMessageA( lphc
->hWndEdit
, EM_SETSEL
,
2007 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2009 case CB_SETEXTENDEDUI16
:
2010 case CB_SETEXTENDEDUI
:
2011 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2014 lphc
->wState
|= CBF_EUI
;
2015 else lphc
->wState
&= ~CBF_EUI
;
2017 case CB_GETEXTENDEDUI16
:
2018 case CB_GETEXTENDEDUI
:
2019 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2020 case (WM_USER
+ 0x1B):
2021 WARN("[%04x]: undocumented msg!\n", hwnd
);
2023 return DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2028 /***********************************************************************
2031 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2034 LRESULT WINAPI
ComboWndProc( HWND hwnd
, UINT message
,
2035 WPARAM wParam
, LPARAM lParam
)
2037 WND
* pWnd
= WIN_FindWndPtr(hwnd
);
2038 LRESULT retvalue
= ComboWndProc_locked(pWnd
,message
,wParam
,lParam
);
2041 WIN_ReleaseWndPtr(pWnd
);