4 * Copyright 1997 Alex Korobka
6 * FIXME: roll up in Netscape 3.01.
12 #include "wine/winuser16.h"
13 #include "sysmetrics.h"
20 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(combo
)
25 /* bits in the dwKeyData */
26 #define KEYDATA_ALT 0x2000
27 #define KEYDATA_PREVSTATE 0x4000
30 * Additional combo box definitions
33 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
34 #define CB_NOTIFY( lphc, code ) \
35 (SendMessageA( (lphc)->owner, WM_COMMAND, \
36 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
37 #define CB_GETEDITTEXTLENGTH( lphc ) \
38 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
43 static HBITMAP hComboBmp
= 0;
44 static UINT CBitHeight
, CBitWidth
;
47 * Look and feel dependant "constants"
49 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
50 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
51 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
52 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
54 /***********************************************************************
57 * Load combo button bitmap.
59 static BOOL
COMBO_Init()
63 if( hComboBmp
) return TRUE
;
64 if( (hDC
= CreateCompatibleDC(0)) )
67 if( (hComboBmp
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO
))) )
73 GetObjectA( hComboBmp
, sizeof(bm
), &bm
);
74 CBitHeight
= bm
.bmHeight
;
75 CBitWidth
= bm
.bmWidth
;
77 TRACE("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
79 hPrevB
= SelectObject16( hDC
, hComboBmp
);
80 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
81 InvertRect( hDC
, &r
);
82 SelectObject( hDC
, hPrevB
);
91 /***********************************************************************
94 static LRESULT
COMBO_NCCreate(WND
* wnd
, LPARAM lParam
)
98 if ( wnd
&& COMBO_Init() &&
99 (lphc
= HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO
))) )
101 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
103 memset( lphc
, 0, sizeof(HEADCOMBO
) );
104 *(LPHEADCOMBO
*)wnd
->wExtra
= lphc
;
106 /* some braindead apps do try to use scrollbar/border flags */
108 lphc
->dwStyle
= (lpcs
->style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
));
109 wnd
->dwStyle
&= ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
112 * We also have to remove the client edge style to make sure
113 * we don't end-up with a non client area.
115 wnd
->dwExStyle
&= ~(WS_EX_CLIENTEDGE
);
117 if( !(lpcs
->style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
118 lphc
->dwStyle
|= CBS_HASSTRINGS
;
119 if( !(wnd
->dwExStyle
& WS_EX_NOPARENTNOTIFY
) )
120 lphc
->wState
|= CBF_NOTIFY
;
122 TRACE("[0x%08x], style = %08x\n",
123 (UINT
)lphc
, lphc
->dwStyle
);
125 return (LRESULT
)(UINT
)wnd
->hwndSelf
;
127 return (LRESULT
)FALSE
;
130 /***********************************************************************
133 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
138 WND
* wnd
= lphc
->self
;
140 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc
));
142 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
143 DestroyWindow( lphc
->hWndLBox
);
145 HeapFree( GetProcessHeap(), 0, lphc
);
151 /***********************************************************************
154 * The dummy resize is used for listboxes that have a popup to trigger
155 * a re-arranging of the contents of the combobox and the recalculation
156 * of the size of the "real" control window.
158 static void CBForceDummyResize(
163 GetWindowRect(CB_HWND(lphc
), &windowRect
);
166 * We have to be careful, resizing a combobox also has the meaning that the
167 * dropped rect will be resized. In this case, we want to trigger a resize
168 * to recalculate layout but we don't want to change the dropped rectangle
169 * So, we add the size of the dropped rectangle to the size of the control.
170 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
173 SetWindowPos( CB_HWND(lphc
),
176 windowRect
.right
- windowRect
.left
,
177 windowRect
.bottom
- windowRect
.top
+
178 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
179 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
182 /***********************************************************************
183 * CBGetTextAreaHeight
185 * This method will calculate the height of the text area of the
187 * The height of the text area is set in two ways.
188 * It can be set explicitely through a combobox message of through a
189 * WM_MEASUREITEM callback.
190 * If this is not the case, the height is set to 13 dialog units.
191 * This height was determined through experimentation.
193 static INT
CBGetTextAreaHeight(
199 if( lphc
->editHeight
) /* explicitly set height */
201 iTextItemHeight
= lphc
->editHeight
;
206 HDC hDC
= GetDC(hwnd
);
211 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
213 GetTextMetricsA(hDC
, &tm
);
215 baseUnitY
= tm
.tmHeight
;
218 SelectObject( hDC
, hPrevFont
);
220 ReleaseDC(hwnd
, hDC
);
222 iTextItemHeight
= ((13 * baseUnitY
) / 8);
225 * This "formula" calculates the height of the complete control.
226 * To calculate the height of the text area, we have to remove the
229 iTextItemHeight
-= 2*COMBO_YBORDERSIZE();
233 * Check the ownerdraw case if we haven't asked the parent the size
236 if ( CB_OWNERDRAWN(lphc
) &&
237 (lphc
->wState
& CBF_MEASUREITEM
) )
239 MEASUREITEMSTRUCT measureItem
;
241 INT originalItemHeight
= iTextItemHeight
;
244 * We use the client rect for the width of the item.
246 GetClientRect(hwnd
, &clientRect
);
248 lphc
->wState
&= ~CBF_MEASUREITEM
;
251 * Send a first one to measure the size of the text area
253 measureItem
.CtlType
= ODT_COMBOBOX
;
254 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
255 measureItem
.itemID
= -1;
256 measureItem
.itemWidth
= clientRect
.right
;
257 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
258 measureItem
.itemData
= 0;
259 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
260 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
261 iTextItemHeight
= 6 + measureItem
.itemHeight
;
264 * Send a second one in the case of a fixed ownerdraw list to calculate the
265 * size of the list items. (we basically do this on behalf of the listbox)
267 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
269 measureItem
.CtlType
= ODT_COMBOBOX
;
270 measureItem
.CtlID
= lphc
->self
->wIDmenu
;
271 measureItem
.itemID
= 0;
272 measureItem
.itemWidth
= clientRect
.right
;
273 measureItem
.itemHeight
= originalItemHeight
;
274 measureItem
.itemData
= 0;
275 SendMessageA(lphc
->owner
, WM_MEASUREITEM
,
276 (WPARAM
)measureItem
.CtlID
, (LPARAM
)&measureItem
);
277 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
281 * Keep the size for the next time
283 lphc
->editHeight
= iTextItemHeight
;
286 return iTextItemHeight
;
290 /***********************************************************************
293 * Set up component coordinates given valid lphc->RectCombo.
295 static void CBCalcPlacement(
303 * Again, start with the client rectangle.
305 GetClientRect(hwnd
, lprEdit
);
310 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
313 * Chop off the bottom part to fit with the height of the text area.
315 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
318 * The button starts the same vertical position as the text area.
320 CopyRect(lprButton
, lprEdit
);
323 * If the combobox is "simple" there is no button.
325 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
326 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
330 * Let's assume the combobox button is the same width as the
332 * size the button horizontally and cut-off the text area.
334 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
335 lprEdit
->right
= lprButton
->left
;
339 * In the case of a dropdown, there is an additional spacing between the
340 * text area and the button.
342 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
344 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
348 * If we have an edit control, we space it away from the borders slightly.
350 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
352 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
356 * Adjust the size of the listbox popup.
358 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
361 * Use the client rectangle to initialize the listbox rectangle
363 GetClientRect(hwnd
, lprLB
);
366 * Then, chop-off the top part.
368 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
373 * Make sure the dropped width is as large as the combobox itself.
375 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
377 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
380 * In the case of a dropdown, the popup listbox is offset to the right.
381 * so, we want to make sure it's flush with the right side of the
384 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
385 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
388 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
391 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
392 lprEdit
->left
, lprEdit
->top
, lprEdit
->right
, lprEdit
->bottom
);
394 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
395 lprButton
->left
, lprButton
->top
, lprButton
->right
, lprButton
->bottom
);
397 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
398 lprLB
->left
, lprLB
->top
, lprLB
->right
, lprLB
->bottom
);
401 /***********************************************************************
402 * CBGetDroppedControlRect
404 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
406 CopyRect(lpRect
, &lphc
->droppedRect
);
409 /***********************************************************************
410 * COMBO_WindowPosChanging
412 static LRESULT
COMBO_WindowPosChanging(
415 WINDOWPOS
* posChanging
)
418 * We need to override the WM_WINDOWPOSCHANGING method to handle all
419 * the non-simple comboboxes. The problem is that those controls are
420 * always the same height. We have to make sure they are not resized
423 if ( ( CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
424 ((posChanging
->flags
& SWP_NOSIZE
) == 0) )
428 newComboHeight
= CBGetTextAreaHeight(hwnd
,lphc
) +
429 2*COMBO_YBORDERSIZE();
432 * Resizing a combobox has another side effect, it resizes the dropped
433 * rectangle as well. However, it does it only if the new height for the
434 * combobox is different than the height it should have. In other words,
435 * if the application resizing the combobox only had the intention to resize
436 * the actual control, for example, to do the layout of a dialog that is
437 * resized, the height of the dropdown is not changed.
439 if (posChanging
->cy
!= newComboHeight
)
441 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ posChanging
->cy
- newComboHeight
;
443 posChanging
->cy
= newComboHeight
;
450 /***********************************************************************
453 static LRESULT
COMBO_Create( LPHEADCOMBO lphc
, WND
* wnd
, LPARAM lParam
)
455 static char clbName
[] = "ComboLBox";
456 static char editName
[] = "Edit";
458 LPCREATESTRUCTA lpcs
= (CREATESTRUCTA
*)lParam
;
460 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
461 else if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
464 lphc
->owner
= lpcs
->hwndParent
;
467 * The item height and dropped width are not set when the control
470 lphc
->droppedWidth
= lphc
->editHeight
= 0;
473 * The first time we go through, we want to measure the ownerdraw item
475 lphc
->wState
|= CBF_MEASUREITEM
;
477 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
479 if( lphc
->owner
|| !(lpcs
->style
& WS_VISIBLE
) )
484 * Initialize the dropped rect to the size of the client area of the
485 * control and then, force all the areas of the combobox to be
488 GetClientRect( wnd
->hwndSelf
, &lphc
->droppedRect
);
490 CBCalcPlacement(wnd
->hwndSelf
,
494 &lphc
->droppedRect
);
497 * Adjust the position of the popup listbox if it's necessary
499 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
501 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
504 * If it's a dropdown, the listbox is offset
506 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
507 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
509 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
);
510 ClientToScreen(wnd
->hwndSelf
, (LPPOINT
)&lphc
->droppedRect
.right
);
513 /* create listbox popup */
515 lbeStyle
= (LBS_NOTIFY
| WS_BORDER
| WS_CLIPSIBLINGS
) |
516 (lpcs
->style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
518 if( lphc
->dwStyle
& CBS_SORT
)
519 lbeStyle
|= LBS_SORT
;
520 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
521 lbeStyle
|= LBS_HASSTRINGS
;
522 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
523 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
524 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
525 lbeStyle
|= LBS_DISABLENOSCROLL
;
527 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
528 lbeStyle
|= WS_CHILD
| WS_VISIBLE
;
529 else /* popup listbox */
530 lbeStyle
|= WS_POPUP
;
532 /* Dropdown ComboLBox is not a child window and we cannot pass
533 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
535 lphc
->hWndLBox
= CreateWindowExA(0,
539 lphc
->droppedRect
.left
,
540 lphc
->droppedRect
.top
,
541 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
542 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
543 lphc
->self
->hwndSelf
,
544 (lphc
->dwStyle
& CBS_DROPDOWN
)? (HMENU
)0 : (HMENU
)ID_CB_LISTBOX
,
545 lphc
->self
->hInstance
,
551 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
;
554 * In Win95 look, the border fo the edit control is
555 * provided by the combobox
557 if (TWEAK_WineLook
== WIN31_LOOK
)
558 lbeStyle
|= WS_BORDER
;
560 if( lphc
->wState
& CBF_EDIT
)
562 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
563 lbeStyle
|= ES_OEMCONVERT
;
564 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
565 lbeStyle
|= ES_AUTOHSCROLL
;
566 if( lphc
->dwStyle
& CBS_LOWERCASE
)
567 lbeStyle
|= ES_LOWERCASE
;
568 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
569 lbeStyle
|= ES_UPPERCASE
;
571 lphc
->hWndEdit
= CreateWindowExA(0,
575 lphc
->textRect
.left
, lphc
->textRect
.top
,
576 lphc
->textRect
.right
- lphc
->textRect
.left
,
577 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
578 lphc
->self
->hwndSelf
,
580 lphc
->self
->hInstance
,
583 if( !lphc
->hWndEdit
)
590 * If the combo is a dropdown, we must resize the control to fit only
591 * the text area and button. To do this, we send a dummy resize and the
592 * WM_WINDOWPOSCHANGING message will take care of setting the height for
595 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
597 CBForceDummyResize(lphc
);
600 TRACE("init done\n");
601 return wnd
->hwndSelf
;
603 ERR("edit control failure.\n");
604 } else ERR("listbox failure.\n");
605 } else ERR("no owner for visible combo.\n");
607 /* CreateWindow() will send WM_NCDESTROY to cleanup */
612 /***********************************************************************
615 * Paint combo button (normal, pressed, and disabled states).
617 static void CBPaintButton(
626 COLORREF oldTextColor
, oldBkColor
;
628 if( lphc
->wState
& CBF_NOREDRAW
)
631 hPrevBrush
= SelectObject(hdc
, GetSysColorBrush(COLOR_BTNFACE
));
634 * Draw the button background
639 rectButton
.right
-rectButton
.left
,
640 rectButton
.bottom
-rectButton
.top
,
643 if( (bBool
= lphc
->wState
& CBF_BUTTONDOWN
) )
645 DrawEdge( hdc
, &rectButton
, EDGE_SUNKEN
, BF_RECT
);
649 DrawEdge( hdc
, &rectButton
, EDGE_RAISED
, BF_RECT
);
653 * Remove the edge of the button from the rectangle
654 * and calculate the position of the bitmap.
656 InflateRect( &rectButton
, -2, -2);
658 x
= (rectButton
.left
+ rectButton
.right
- CBitWidth
) >> 1;
659 y
= (rectButton
.top
+ rectButton
.bottom
- CBitHeight
) >> 1;
662 hMemDC
= CreateCompatibleDC( hdc
);
663 SelectObject( hMemDC
, hComboBmp
);
664 oldTextColor
= SetTextColor( hdc
, GetSysColor(COLOR_BTNFACE
) );
665 oldBkColor
= SetBkColor( hdc
, CB_DISABLED(lphc
) ? RGB(128,128,128) :
667 BitBlt( hdc
, x
, y
, CBitWidth
, CBitHeight
, hMemDC
, 0, 0, SRCCOPY
);
668 SetBkColor( hdc
, oldBkColor
);
669 SetTextColor( hdc
, oldTextColor
);
671 SelectObject( hdc
, hPrevBrush
);
674 /***********************************************************************
677 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
679 static void CBPaintText(
687 if( lphc
->wState
& CBF_NOREDRAW
) return;
689 /* follow Windows combobox that sends a bunch of text
690 * inquiries to its listbox while processing WM_PAINT. */
692 if( (id
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
694 size
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
695 if( (pText
= HeapAlloc( GetProcessHeap(), 0, size
+ 1)) )
697 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
698 pText
[size
] = '\0'; /* just in case */
702 if( lphc
->wState
& CBF_EDIT
)
704 if( CB_HASSTRINGS(lphc
) ) SetWindowTextA( lphc
->hWndEdit
, pText
? pText
: "" );
705 if( lphc
->wState
& CBF_FOCUSED
)
706 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
708 else /* paint text field ourselves */
710 HBRUSH hPrevBrush
= 0;
715 if ((hDC
= GetDC(lphc
->self
->hwndSelf
)))
717 HBRUSH hBrush
= SendMessageA( lphc
->owner
,
719 hDC
, lphc
->self
->hwndSelf
);
720 hPrevBrush
= SelectObject( hDC
,
721 (hBrush
) ? hBrush
: GetStockObject(WHITE_BRUSH
) );
727 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hDC
, lphc
->hFont
) : 0;
730 * Give ourselves some space.
732 InflateRect( &rectEdit
, -1, -1 );
734 if ( (lphc
->wState
& CBF_FOCUSED
) &&
735 !(lphc
->wState
& CBF_DROPPED
) )
739 FillRect( hDC
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
740 SetBkColor( hDC
, GetSysColor( COLOR_HIGHLIGHT
) );
741 SetTextColor( hDC
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
742 itemState
= ODS_SELECTED
| ODS_FOCUS
;
747 if( CB_OWNERDRAWN(lphc
) )
753 * Save the current clip region.
754 * To retrieve the clip region, we need to create one "dummy"
757 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
759 if (GetClipRgn(hDC
, clipRegion
)!=1)
761 DeleteObject(clipRegion
);
762 clipRegion
=(HRGN
)NULL
;
765 if ( lphc
->self
->dwStyle
& WS_DISABLED
)
766 itemState
|= ODS_DISABLED
;
768 dis
.CtlType
= ODT_COMBOBOX
;
769 dis
.CtlID
= lphc
->self
->wIDmenu
;
770 dis
.hwndItem
= lphc
->self
->hwndSelf
;
771 dis
.itemAction
= ODA_DRAWENTIRE
;
773 dis
.itemState
= itemState
;
775 dis
.rcItem
= rectEdit
;
776 dis
.itemData
= SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
,
780 * Clip the DC and have the parent draw the item.
782 IntersectClipRect(hDC
,
783 rectEdit
.left
, rectEdit
.top
,
784 rectEdit
.right
, rectEdit
.bottom
);
786 SendMessageA(lphc
->owner
, WM_DRAWITEM
,
787 lphc
->self
->wIDmenu
, (LPARAM
)&dis
);
790 * Reset the clipping region.
792 SelectClipRgn(hDC
, clipRegion
);
799 ETO_OPAQUE
| ETO_CLIPPED
,
801 pText
? pText
: "" , size
, NULL
);
803 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
804 DrawFocusRect( hDC
, &rectEdit
);
808 SelectObject(hDC
, hPrevFont
);
813 SelectObject( hDC
, hPrevBrush
);
815 ReleaseDC( lphc
->self
->hwndSelf
, hDC
);
820 HeapFree( GetProcessHeap(), 0, pText
);
823 /***********************************************************************
826 static void CBPaintBorder(
833 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
835 GetClientRect(hwnd
, &clientRect
);
839 CopyRect(&clientRect
, &lphc
->textRect
);
841 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
842 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
845 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
848 /***********************************************************************
849 * COMBO_EraseBackground
851 static LRESULT
COMBO_EraseBackground(
860 hDC
= (hParamDC
) ? hParamDC
864 * Calculate the area that we want to erase.
866 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
868 GetClientRect(hwnd
, &clientRect
);
872 CopyRect(&clientRect
, &lphc
->textRect
);
874 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
877 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
881 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
883 FillRect(hDC
, &clientRect
, hBkgBrush
);
886 ReleaseDC(hwnd
, hDC
);
891 /***********************************************************************
894 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
899 hDC
= (hParamDC
) ? hParamDC
900 : BeginPaint( lphc
->self
->hwndSelf
, &ps
);
903 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
905 HBRUSH hPrevBrush
, hBkgBrush
;
907 hBkgBrush
= SendMessageA( lphc
->owner
, WM_CTLCOLORLISTBOX
,
908 hDC
, lphc
->self
->hwndSelf
);
911 hBkgBrush
= GetStockObject(WHITE_BRUSH
);
913 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
916 * In non 3.1 look, there is a sunken border on the combobox
918 if (TWEAK_WineLook
!= WIN31_LOOK
)
920 CBPaintBorder(CB_HWND(lphc
), lphc
, hDC
);
923 if( !IsRectEmpty(&lphc
->buttonRect
) )
925 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
928 if( !(lphc
->wState
& CBF_EDIT
) )
931 * The text area has a border only in Win 3.1 look.
933 if (TWEAK_WineLook
== WIN31_LOOK
)
935 HPEN hPrevPen
= SelectObject( hDC
, GetSysColorPen(COLOR_WINDOWFRAME
) );
938 lphc
->textRect
.left
, lphc
->textRect
.top
,
939 lphc
->textRect
.right
- 1, lphc
->textRect
.bottom
- 1);
941 SelectObject( hDC
, hPrevPen
);
944 CBPaintText( lphc
, hDC
, lphc
->textRect
);
948 SelectObject( hDC
, hPrevBrush
);
952 EndPaint(lphc
->self
->hwndSelf
, &ps
);
957 /***********************************************************************
960 * Select listbox entry according to the contents of the edit control.
962 static INT
CBUpdateLBox( LPHEADCOMBO lphc
)
964 INT length
, idx
, ret
;
968 length
= CB_GETEDITTEXTLENGTH( lphc
);
971 pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1);
973 TRACE("\t edit text length %i\n", length
);
977 if( length
) GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1);
978 else pText
[0] = '\0';
979 idx
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
980 (WPARAM
)(-1), (LPARAM
)pText
);
981 if( idx
== LB_ERR
) idx
= 0; /* select first item */
983 HeapFree( GetProcessHeap(), 0, pText
);
988 SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)idx
, 0 );
992 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)idx
, 0 );
993 /* probably superfluous but Windows sends this too */
994 SendMessageA( lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)idx
, 0 );
999 /***********************************************************************
1002 * Copy a listbox entry to the edit control.
1004 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1009 TRACE("\t %i\n", index
);
1013 length
= CB_GETEDITTEXTLENGTH( lphc
);
1016 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1018 GetWindowTextA( lphc
->hWndEdit
, pText
, length
+ 1 );
1019 index
= SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
,
1020 (WPARAM
)(-1), (LPARAM
)pText
);
1021 HeapFree( GetProcessHeap(), 0, pText
);
1026 if( index
>= 0 ) /* got an entry */
1028 length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1031 if( (pText
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1)) )
1033 SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1034 (WPARAM
)index
, (LPARAM
)pText
);
1035 SendMessageA( lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)pText
);
1036 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1037 HeapFree( GetProcessHeap(), 0, pText
);
1043 /***********************************************************************
1046 * Show listbox popup.
1048 static void CBDropDown( LPHEADCOMBO lphc
)
1053 TRACE("[%04x]: drop down\n", CB_HWND(lphc
));
1055 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1059 lphc
->wState
|= CBF_DROPPED
;
1060 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1062 index
= CBUpdateLBox( lphc
);
1063 if( !(lphc
->wState
& CBF_CAPTURE
) ) CBUpdateEdit( lphc
, index
);
1067 index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1068 if( index
== LB_ERR
) index
= 0;
1069 SendMessageA( lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)index
, 0 );
1070 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1073 /* now set popup position */
1074 GetWindowRect( lphc
->self
->hwndSelf
, &rect
);
1078 * If it's a dropdown, the listbox is offset
1080 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1081 rect
.left
+= COMBO_EDITBUTTONSPACE();
1083 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1084 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1085 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
1086 SWP_NOACTIVATE
| SWP_NOREDRAW
);
1088 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1089 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0, RDW_INVALIDATE
|
1090 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1092 ShowWindow( lphc
->hWndLBox
, SW_SHOWNA
);
1095 /***********************************************************************
1098 * Hide listbox popup.
1100 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1102 HWND hWnd
= lphc
->self
->hwndSelf
;
1104 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1106 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1109 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc
), (INT
)ok
);
1111 /* always send WM_LBUTTONUP? */
1112 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1114 if( lphc
->wState
& CBF_DROPPED
)
1118 lphc
->wState
&= ~CBF_DROPPED
;
1119 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1121 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1123 INT index
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1124 CBUpdateEdit( lphc
, index
);
1125 rect
= lphc
->buttonRect
;
1136 rect
= lphc
->textRect
;
1141 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1142 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1143 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1144 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1149 /***********************************************************************
1152 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1154 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL bRedrawButton
)
1156 if( lphc
->wState
& CBF_DROPPED
)
1158 CBRollUp( lphc
, TRUE
, bRedrawButton
);
1166 /***********************************************************************
1169 * Edit control helper.
1171 HWND
COMBO_GetLBWindow( WND
* pWnd
)
1173 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1174 if( lphc
) return lphc
->hWndLBox
;
1179 /***********************************************************************
1182 static void CBRepaintButton( LPHEADCOMBO lphc
)
1184 InvalidateRect(CB_HWND(lphc
), &lphc
->buttonRect
, TRUE
);
1185 UpdateWindow(CB_HWND(lphc
));
1188 /***********************************************************************
1191 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1193 if( !(lphc
->wState
& CBF_FOCUSED
) )
1195 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1196 SendMessageA( lphc
->hWndLBox
, LB_CARETON
, 0, 0 );
1198 if( lphc
->wState
& CBF_EDIT
)
1199 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1) );
1200 lphc
->wState
|= CBF_FOCUSED
;
1201 if( !(lphc
->wState
& CBF_EDIT
) )
1203 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1206 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1210 /***********************************************************************
1213 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1215 HWND hWnd
= lphc
->self
->hwndSelf
;
1217 if( lphc
->wState
& CBF_FOCUSED
)
1219 SendMessageA( hWnd
, WM_LBUTTONUP
, 0, (LPARAM
)(-1) );
1221 CBRollUp( lphc
, FALSE
, TRUE
);
1222 if( IsWindow( hWnd
) )
1224 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1225 SendMessageA( lphc
->hWndLBox
, LB_CARETOFF
, 0, 0 );
1227 lphc
->wState
&= ~CBF_FOCUSED
;
1230 if( lphc
->wState
& CBF_EDIT
)
1231 SendMessageA( lphc
->hWndEdit
, EM_SETSEL
, (WPARAM
)(-1), 0 );
1234 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1237 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1242 /***********************************************************************
1245 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1247 if( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1249 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1251 switch( HIWORD(wParam
) >> 8 )
1253 case (EN_SETFOCUS
>> 8):
1255 TRACE("[%04x]: edit [%04x] got focus\n",
1256 CB_HWND(lphc
), lphc
->hWndEdit
);
1258 if( !(lphc
->wState
& CBF_FOCUSED
) ) COMBO_SetFocus( lphc
);
1261 case (EN_KILLFOCUS
>> 8):
1263 TRACE("[%04x]: edit [%04x] lost focus\n",
1264 CB_HWND(lphc
), lphc
->hWndEdit
);
1266 /* NOTE: it seems that Windows' edit control sends an
1267 * undocumented message WM_USER + 0x1B instead of this
1268 * notification (only when it happens to be a part of
1269 * the combo). ?? - AK.
1272 COMBO_KillFocus( lphc
);
1276 case (EN_CHANGE
>> 8):
1277 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1278 CBUpdateLBox( lphc
);
1281 case (EN_UPDATE
>> 8):
1282 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1285 case (EN_ERRSPACE
>> 8):
1286 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1289 else if( lphc
->hWndLBox
== hWnd
)
1291 switch( HIWORD(wParam
) )
1294 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1298 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1304 TRACE("[%04x]: lbox selection change [%04x]\n",
1305 CB_HWND(lphc
), lphc
->wState
);
1307 /* do not roll up if selection is being tracked
1308 * by arrowkeys in the dropdown listbox */
1310 if( (lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
) )
1311 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1312 else lphc
->wState
&= ~CBF_NOROLLUP
;
1314 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1315 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1320 /* nothing to do here since ComboLBox always resets the focus to its
1321 * combo/edit counterpart */
1328 /***********************************************************************
1331 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1333 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
,
1334 WPARAM wParam
, LPARAM lParam
)
1336 HWND hWnd
= lphc
->self
->hwndSelf
;
1338 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc
), msg
);
1340 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1342 /* two first items are the same in all 4 structs */
1343 lpIS
->CtlType
= ODT_COMBOBOX
;
1344 lpIS
->CtlID
= lphc
->self
->wIDmenu
;
1346 switch( msg
) /* patch window handle */
1349 lpIS
->hwndItem
= hWnd
;
1353 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1354 lpIS
->hwndItem
= hWnd
;
1357 case WM_COMPAREITEM
:
1358 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1359 lpIS
->hwndItem
= hWnd
;
1364 return SendMessageA( lphc
->owner
, msg
, lphc
->self
->wIDmenu
, lParam
);
1367 /***********************************************************************
1370 static LRESULT
COMBO_GetText( LPHEADCOMBO lphc
, UINT N
, LPSTR lpText
)
1372 if( lphc
->wState
& CBF_EDIT
)
1373 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
,
1374 (WPARAM
)N
, (LPARAM
)lpText
);
1376 /* get it from the listbox */
1378 if( lphc
->hWndLBox
)
1380 INT idx
= SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0 );
1384 INT length
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
,
1387 /* 'length' is without the terminating character */
1389 lpBuffer
= (LPSTR
) HeapAlloc( GetProcessHeap(), 0, length
+ 1 );
1395 INT n
= SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
,
1396 (WPARAM
)idx
, (LPARAM
)lpBuffer
);
1398 /* truncate if buffer is too short */
1403 if( n
!= LB_ERR
) memcpy( lpText
, lpBuffer
, (N
>n
) ? n
+1 : N
-1 );
1404 lpText
[N
- 1] = '\0';
1406 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1416 /***********************************************************************
1419 * This function sets window positions according to the updated
1420 * component placement struct.
1422 static void CBResetPos(
1428 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1430 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1431 * sizing messages */
1433 if( lphc
->wState
& CBF_EDIT
)
1434 SetWindowPos( lphc
->hWndEdit
, 0,
1435 rectEdit
->left
, rectEdit
->top
,
1436 rectEdit
->right
- rectEdit
->left
,
1437 rectEdit
->bottom
- rectEdit
->top
,
1438 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1440 SetWindowPos( lphc
->hWndLBox
, 0,
1441 rectLB
->left
, rectLB
->top
,
1442 rectLB
->right
- rectLB
->left
,
1443 rectLB
->bottom
- rectLB
->top
,
1444 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1448 if( lphc
->wState
& CBF_DROPPED
)
1450 lphc
->wState
&= ~CBF_DROPPED
;
1451 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1454 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1455 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
1456 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1461 /***********************************************************************
1464 static void COMBO_Size( LPHEADCOMBO lphc
)
1466 CBCalcPlacement(lphc
->self
->hwndSelf
,
1470 &lphc
->droppedRect
);
1472 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1476 /***********************************************************************
1479 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1484 lphc
->hFont
= hFont
;
1487 * Propagate to owned windows.
1489 if( lphc
->wState
& CBF_EDIT
)
1490 SendMessageA( lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1491 SendMessageA( lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1494 * Redo the layout of the control.
1496 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1498 CBCalcPlacement(lphc
->self
->hwndSelf
,
1502 &lphc
->droppedRect
);
1504 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1508 CBForceDummyResize(lphc
);
1513 /***********************************************************************
1514 * COMBO_SetItemHeight
1516 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1518 LRESULT lRet
= CB_ERR
;
1520 if( index
== -1 ) /* set text field height */
1522 if( height
< 32768 )
1524 lphc
->editHeight
= height
;
1527 * Redo the layout of the control.
1529 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1531 CBCalcPlacement(lphc
->self
->hwndSelf
,
1535 &lphc
->droppedRect
);
1537 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1541 CBForceDummyResize(lphc
);
1547 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1548 lRet
= SendMessageA( lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1549 (WPARAM
)index
, (LPARAM
)height
);
1553 /***********************************************************************
1554 * COMBO_SelectString
1556 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPCSTR pText
)
1558 INT index
= SendMessageA( lphc
->hWndLBox
, LB_SELECTSTRING
,
1559 (WPARAM
)start
, (LPARAM
)pText
);
1562 if( lphc
->wState
& CBF_EDIT
)
1563 CBUpdateEdit( lphc
, index
);
1566 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1569 return (LRESULT
)index
;
1572 /***********************************************************************
1575 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1579 HWND hWnd
= lphc
->self
->hwndSelf
;
1581 pt
.x
= LOWORD(lParam
);
1582 pt
.y
= HIWORD(lParam
);
1583 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1585 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1586 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1588 lphc
->wState
|= CBF_BUTTONDOWN
;
1589 if( lphc
->wState
& CBF_DROPPED
)
1591 /* got a click to cancel selection */
1593 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1594 CBRollUp( lphc
, TRUE
, FALSE
);
1595 if( !IsWindow( hWnd
) ) return;
1597 if( lphc
->wState
& CBF_CAPTURE
)
1599 lphc
->wState
&= ~CBF_CAPTURE
;
1605 /* drop down the listbox and start tracking */
1607 lphc
->wState
|= CBF_CAPTURE
;
1611 if( bButton
) CBRepaintButton( lphc
);
1615 /***********************************************************************
1618 * Release capture and stop tracking if needed.
1620 static void COMBO_LButtonUp( LPHEADCOMBO lphc
, LPARAM lParam
)
1622 if( lphc
->wState
& CBF_CAPTURE
)
1624 lphc
->wState
&= ~CBF_CAPTURE
;
1625 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1627 INT index
= CBUpdateLBox( lphc
);
1628 CBUpdateEdit( lphc
, index
);
1633 if( lphc
->wState
& CBF_BUTTONDOWN
)
1635 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1636 CBRepaintButton( lphc
);
1640 /***********************************************************************
1643 * Two things to do - track combo button and release capture when
1644 * pointer goes into the listbox.
1646 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1651 pt
.x
= LOWORD(lParam
);
1652 pt
.y
= HIWORD(lParam
);
1654 if( lphc
->wState
& CBF_BUTTONDOWN
)
1658 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1662 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1663 CBRepaintButton( lphc
);
1667 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1668 MapWindowPoints( lphc
->self
->hwndSelf
, lphc
->hWndLBox
, &pt
, 1 );
1669 if( PtInRect(&lbRect
, pt
) )
1671 lphc
->wState
&= ~CBF_CAPTURE
;
1673 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
);
1675 /* hand over pointer tracking */
1676 SendMessageA( lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1681 /***********************************************************************
1682 * ComboWndProc_locked
1684 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1686 static inline LRESULT WINAPI
ComboWndProc_locked( WND
* pWnd
, UINT message
,
1687 WPARAM wParam
, LPARAM lParam
)
1690 LPHEADCOMBO lphc
= CB_GETPTR(pWnd
);
1691 HWND hwnd
= pWnd
->hwndSelf
;
1693 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1694 pWnd
->hwndSelf
, SPY_GetMsgName(message
), wParam
, lParam
);
1696 if( lphc
|| message
== WM_NCCREATE
)
1700 /* System messages */
1703 return COMBO_NCCreate(pWnd
, lParam
);
1705 COMBO_NCDestroy(lphc
);
1706 break;/* -> DefWindowProc */
1709 return COMBO_Create(lphc
, pWnd
, lParam
);
1711 case WM_PRINTCLIENT
:
1712 if (lParam
& PRF_ERASEBKGND
)
1713 COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1717 /* wParam may contain a valid HDC! */
1718 return COMBO_Paint(lphc
, wParam
);
1720 return COMBO_EraseBackground(hwnd
, lphc
, wParam
);
1722 return (LRESULT
)(DLGC_WANTARROWS
| DLGC_WANTCHARS
);
1723 case WM_WINDOWPOSCHANGING
:
1724 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1726 if( lphc
->hWndLBox
&&
1727 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1730 COMBO_Font( lphc
, (HFONT16
)wParam
, (BOOL
)lParam
);
1733 return (LRESULT
)lphc
->hFont
;
1735 if( lphc
->wState
& CBF_EDIT
)
1736 SetFocus( lphc
->hWndEdit
);
1738 COMBO_SetFocus( lphc
);
1741 #define hwndFocus ((HWND16)wParam)
1743 (hwndFocus
!= lphc
->hWndEdit
&& hwndFocus
!= lphc
->hWndLBox
))
1744 COMBO_KillFocus( lphc
);
1748 return COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1750 return COMBO_GetText( lphc
, (UINT
)wParam
, (LPSTR
)lParam
);
1752 case WM_GETTEXTLENGTH
:
1757 if( lphc
->wState
& CBF_EDIT
)
1758 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1762 case WM_COMPAREITEM
:
1763 case WM_MEASUREITEM
:
1764 return COMBO_ItemOp( lphc
, message
, wParam
, lParam
);
1766 if( lphc
->wState
& CBF_EDIT
)
1767 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1768 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1772 lphc
->wState
&= ~CBF_NOREDRAW
;
1774 lphc
->wState
|= CBF_NOREDRAW
;
1776 if( lphc
->wState
& CBF_EDIT
)
1777 SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1778 SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1781 if( KEYDATA_ALT
& HIWORD(lParam
) )
1782 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
1783 COMBO_FlipListbox( lphc
, TRUE
);
1784 break;/* -> DefWindowProc */
1788 if( lphc
->wState
& CBF_EDIT
)
1789 return SendMessageA( lphc
->hWndEdit
, message
, wParam
, lParam
);
1791 return SendMessageA( lphc
->hWndLBox
, message
, wParam
, lParam
);
1792 case WM_LBUTTONDOWN
:
1793 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
->hwndSelf
);
1794 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
1797 COMBO_LButtonUp( lphc
, lParam
);
1800 if( lphc
->wState
& CBF_CAPTURE
)
1801 COMBO_MouseMove( lphc
, wParam
, lParam
);
1803 /* Combo messages */
1805 case CB_ADDSTRING16
:
1806 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1808 return SendMessageA( lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
1809 case CB_INSERTSTRING16
:
1810 wParam
= (INT
)(INT16
)wParam
;
1811 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1812 case CB_INSERTSTRING
:
1813 return SendMessageA( lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
1814 case CB_DELETESTRING16
:
1815 case CB_DELETESTRING
:
1816 return SendMessageA( lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
1817 case CB_SELECTSTRING16
:
1818 wParam
= (INT
)(INT16
)wParam
;
1819 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1820 case CB_SELECTSTRING
:
1821 return COMBO_SelectString( lphc
, (INT
)wParam
, (LPSTR
)lParam
);
1822 case CB_FINDSTRING16
:
1823 wParam
= (INT
)(INT16
)wParam
;
1824 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1826 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
1827 case CB_FINDSTRINGEXACT16
:
1828 wParam
= (INT
)(INT16
)wParam
;
1829 if( CB_HASSTRINGS(lphc
) ) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1830 case CB_FINDSTRINGEXACT
:
1831 return SendMessageA( lphc
->hWndLBox
, LB_FINDSTRINGEXACT
,
1833 case CB_SETITEMHEIGHT16
:
1834 wParam
= (INT
)(INT16
)wParam
; /* signed integer */
1835 case CB_SETITEMHEIGHT
:
1836 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
1837 case CB_GETITEMHEIGHT16
:
1838 wParam
= (INT
)(INT16
)wParam
;
1839 case CB_GETITEMHEIGHT
:
1840 if( (INT
)wParam
>= 0 ) /* listbox item */
1841 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
1842 return CBGetTextAreaHeight(hwnd
, lphc
);
1843 case CB_RESETCONTENT16
:
1844 case CB_RESETCONTENT
:
1845 SendMessageA( lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0 );
1846 InvalidateRect(CB_HWND(lphc
), NULL
, TRUE
);
1848 case CB_INITSTORAGE
:
1849 return SendMessageA( lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
1850 case CB_GETHORIZONTALEXTENT
:
1851 return SendMessageA( lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
1852 case CB_SETHORIZONTALEXTENT
:
1853 return SendMessageA( lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
1854 case CB_GETTOPINDEX
:
1855 return SendMessageA( lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
1857 return SendMessageA( lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
1859 return SendMessageA( lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
1860 case CB_GETDROPPEDWIDTH
:
1861 if( lphc
->droppedWidth
)
1862 return lphc
->droppedWidth
;
1863 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
1864 case CB_SETDROPPEDWIDTH
:
1865 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
1866 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
1868 case CB_GETDROPPEDCONTROLRECT16
:
1869 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1873 CBGetDroppedControlRect( lphc
, &r
);
1874 CONV_RECT32TO16( &r
, (LPRECT16
)lParam
);
1877 case CB_GETDROPPEDCONTROLRECT
:
1878 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
1880 case CB_GETDROPPEDSTATE16
:
1881 case CB_GETDROPPEDSTATE
:
1882 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
1884 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1887 return COMBO_Directory( lphc
, (UINT
)wParam
,
1888 (LPSTR
)lParam
, (message
== CB_DIR
));
1889 case CB_SHOWDROPDOWN16
:
1890 case CB_SHOWDROPDOWN
:
1891 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1895 if( !(lphc
->wState
& CBF_DROPPED
) )
1899 if( lphc
->wState
& CBF_DROPPED
)
1900 CBRollUp( lphc
, FALSE
, TRUE
);
1905 return SendMessageA( lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1906 case CB_GETCURSEL16
:
1908 return SendMessageA( lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1909 case CB_SETCURSEL16
:
1910 wParam
= (INT
)(INT16
)wParam
;
1912 lParam
= SendMessageA( lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
1913 if( lphc
->wState
& CBF_SELCHANGE
)
1915 /* no LBN_SELCHANGE in this case, update manually */
1916 InvalidateRect(CB_HWND(lphc
), &lphc
->textRect
, TRUE
);
1917 lphc
->wState
&= ~CBF_SELCHANGE
;
1920 case CB_GETLBTEXT16
:
1921 wParam
= (INT
)(INT16
)wParam
;
1922 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
1924 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
1925 case CB_GETLBTEXTLEN16
:
1926 wParam
= (INT
)(INT16
)wParam
;
1927 case CB_GETLBTEXTLEN
:
1928 return SendMessageA( lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
1929 case CB_GETITEMDATA16
:
1930 wParam
= (INT
)(INT16
)wParam
;
1931 case CB_GETITEMDATA
:
1932 return SendMessageA( lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
1933 case CB_SETITEMDATA16
:
1934 wParam
= (INT
)(INT16
)wParam
;
1935 case CB_SETITEMDATA
:
1936 return SendMessageA( lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
1937 case CB_GETEDITSEL16
:
1938 wParam
= lParam
= 0; /* just in case */
1940 if( lphc
->wState
& CBF_EDIT
)
1944 return SendMessageA( lphc
->hWndEdit
, EM_GETSEL
,
1945 (wParam
) ? wParam
: (WPARAM
)&a
,
1946 (lParam
) ? lParam
: (LPARAM
)&b
);
1949 case CB_SETEDITSEL16
:
1951 if( lphc
->wState
& CBF_EDIT
)
1952 return SendMessageA( lphc
->hWndEdit
, EM_SETSEL
,
1953 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
1955 case CB_SETEXTENDEDUI16
:
1956 case CB_SETEXTENDEDUI
:
1957 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1960 lphc
->wState
|= CBF_EUI
;
1961 else lphc
->wState
&= ~CBF_EUI
;
1963 case CB_GETEXTENDEDUI16
:
1964 case CB_GETEXTENDEDUI
:
1965 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
1966 case (WM_USER
+ 0x1B):
1967 WARN("[%04x]: undocumented msg!\n", hwnd
);
1969 return DefWindowProcA(hwnd
, message
, wParam
, lParam
);
1974 /***********************************************************************
1977 * This is just a wrapper for the real ComboWndProc which locks/unlocks
1980 LRESULT WINAPI
ComboWndProc( HWND hwnd
, UINT message
,
1981 WPARAM wParam
, LPARAM lParam
)
1983 WND
* pWnd
= WIN_FindWndPtr(hwnd
);
1984 LRESULT retvalue
= ComboWndProc_locked(pWnd
,message
,wParam
,lParam
);
1987 WIN_ReleaseWndPtr(pWnd
);