4 * Copyright 1997 Alex Korobka
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * FIXME: roll up in Netscape 3.01.
23 * COPYRIGHT: See COPYING in the top level directory
24 * PROJECT: ReactOS User32
25 * PURPOSE: combobox control
26 * FILE: lib/user32/controls/combo.c
27 * PROGRAMER: Steven Edwards
28 * REVISION HISTORY: 2003/06/25 SAE Created
29 * NOTES: Adapted from Wine
39 #include "user32/regcontrol.h"
42 /* bits in the dwKeyData */
43 #define KEYDATA_ALT 0x2000
44 #define KEYDATA_PREVSTATE 0x4000
47 * Additional combo box definitions
50 #define CB_NOTIFY( lphc, code ) \
51 (SendMessageW((lphc)->owner, WM_COMMAND, \
52 MAKEWPARAM(GetWindowLongA((lphc)->self,GWL_ID), (code)), (LPARAM)(lphc)->self))
54 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
55 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
56 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
57 #define CB_HWND( lphc ) ((lphc)->self)
59 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
64 static HBITMAP hComboBmp
= 0;
65 static UINT CBitHeight
, CBitWidth
;
68 * Look and feel dependant "constants"
71 #define COMBO_YBORDERGAP 5
72 #define COMBO_XBORDERSIZE() ( 2 )
73 #define COMBO_YBORDERSIZE() ( 2 )
74 #define COMBO_EDITBUTTONSPACE() ( 0 )
75 #define EDIT_CONTROL_PADDING() ( 1 )
77 static LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
78 static LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
);
80 /*********************************************************************
81 * combo class descriptor
83 const struct builtin_class_descr COMBO_builtin_class
=
85 "ComboBox", /* name */
86 CS_GLOBALCLASS
| CS_PARENTDC
| CS_DBLCLKS
, /* style */
87 (WNDPROC
) ComboWndProcA
, /* procA */
88 (WNDPROC
) ComboWndProcW
, /* procW */
89 sizeof(HEADCOMBO
*), /* extra */
90 (LPCSTR
) IDC_ARROW
, /* cursor */
95 /***********************************************************************
98 * Load combo button bitmap.
100 static BOOL
COMBO_Init()
104 if( hComboBmp
) return TRUE
;
105 if( (hDC
= CreateCompatibleDC(0)) )
108 if( (hComboBmp
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO
))) )
114 GetObjectW( hComboBmp
, sizeof(bm
), &bm
);
115 CBitHeight
= bm
.bmHeight
;
116 CBitWidth
= bm
.bmWidth
;
118 DbgPrint("combo bitmap [%i,%i]\n", CBitWidth
, CBitHeight
);
120 hPrevB
= SelectObject( hDC
, hComboBmp
);
121 SetRect( &r
, 0, 0, CBitWidth
, CBitHeight
);
122 InvertRect( hDC
, &r
);
123 SelectObject( hDC
, hPrevB
);
132 /***********************************************************************
135 static LRESULT
COMBO_NCCreate(HWND hwnd
, LONG style
)
139 if (COMBO_Init() && (lphc
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(HEADCOMBO
))) )
142 SetWindowLongA( hwnd
, 0, (LONG
)lphc
);
144 /* some braindead apps do try to use scrollbar/border flags */
146 lphc
->dwStyle
= style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
);
147 SetWindowLongA( hwnd
, GWL_STYLE
, style
& ~(WS_BORDER
| WS_HSCROLL
| WS_VSCROLL
) );
150 * We also have to remove the client edge style to make sure
151 * we don't end-up with a non client area.
153 SetWindowLongA( hwnd
, GWL_EXSTYLE
,
154 GetWindowLongA( hwnd
, GWL_EXSTYLE
) & ~WS_EX_CLIENTEDGE
);
156 if( !(style
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
157 lphc
->dwStyle
|= CBS_HASSTRINGS
;
158 if( !(GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_NOPARENTNOTIFY
) )
159 lphc
->wState
|= CBF_NOTIFY
;
161 DbgPrint("[%p], style = %08x\n", lphc
, lphc
->dwStyle
);
167 /***********************************************************************
170 static LRESULT
COMBO_NCDestroy( LPHEADCOMBO lphc
)
175 DbgPrint("[%p]: freeing storage\n", lphc
->self
);
177 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) && lphc
->hWndLBox
)
178 DestroyWindow( lphc
->hWndLBox
);
180 SetWindowLongA( lphc
->self
, 0, 0 );
181 HeapFree( GetProcessHeap(), 0, lphc
);
186 /***********************************************************************
187 * CBGetTextAreaHeight
189 * This method will calculate the height of the text area of the
191 * The height of the text area is set in two ways.
192 * It can be set explicitly through a combobox message or through a
193 * WM_MEASUREITEM callback.
194 * If this is not the case, the height is set to 13 dialog units.
195 * This height was determined through experimentation.
197 static INT
CBGetTextAreaHeight(
203 if( lphc
->editHeight
) /* explicitly set height */
205 iTextItemHeight
= lphc
->editHeight
;
210 HDC hDC
= GetDC(hwnd
);
215 hPrevFont
= SelectObject( hDC
, lphc
->hFont
);
217 GetTextMetricsW(hDC
, &tm
);
219 baseUnitY
= tm
.tmHeight
;
222 SelectObject( hDC
, hPrevFont
);
224 ReleaseDC(hwnd
, hDC
);
227 iTextItemHeight
= ((13 * baseUnitY
) / 8);
230 * This "formula" calculates the height of the complete control.
231 * To calculate the height of the text area, we have to remove the
234 iTextItemHeight
-= 2*COMBO_YBORDERSIZE();
237 /* Joakim: This seems to work better, the old formula caused the combo box
238 to be waaay to big with big font sizes */
239 iTextItemHeight
= baseUnitY
+2*COMBO_YBORDERSIZE();
243 * Check the ownerdraw case if we haven't asked the parent the size
246 if ( CB_OWNERDRAWN(lphc
) &&
247 (lphc
->wState
& CBF_MEASUREITEM
) )
249 MEASUREITEMSTRUCT measureItem
;
251 INT originalItemHeight
= iTextItemHeight
;
252 UINT id
= GetWindowLongA( lphc
->self
, GWL_ID
);
255 * We use the client rect for the width of the item.
257 GetClientRect(hwnd
, &clientRect
);
259 lphc
->wState
&= ~CBF_MEASUREITEM
;
262 * Send a first one to measure the size of the text area
264 measureItem
.CtlType
= ODT_COMBOBOX
;
265 measureItem
.CtlID
= id
;
266 measureItem
.itemID
= -1;
267 measureItem
.itemWidth
= clientRect
.right
;
268 measureItem
.itemHeight
= iTextItemHeight
- 6; /* ownerdrawn cb is taller */
269 measureItem
.itemData
= 0;
270 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
271 iTextItemHeight
= 6 + measureItem
.itemHeight
;
274 * Send a second one in the case of a fixed ownerdraw list to calculate the
275 * size of the list items. (we basically do this on behalf of the listbox)
277 if (lphc
->dwStyle
& CBS_OWNERDRAWFIXED
)
279 measureItem
.CtlType
= ODT_COMBOBOX
;
280 measureItem
.CtlID
= id
;
281 measureItem
.itemID
= 0;
282 measureItem
.itemWidth
= clientRect
.right
;
283 measureItem
.itemHeight
= originalItemHeight
;
284 measureItem
.itemData
= 0;
285 SendMessageW(lphc
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&measureItem
);
286 lphc
->fixedOwnerDrawHeight
= measureItem
.itemHeight
;
290 * Keep the size for the next time
292 lphc
->editHeight
= iTextItemHeight
;
295 return iTextItemHeight
;
298 /***********************************************************************
301 * The dummy resize is used for listboxes that have a popup to trigger
302 * a re-arranging of the contents of the combobox and the recalculation
303 * of the size of the "real" control window.
305 static void CBForceDummyResize(
311 newComboHeight
= CBGetTextAreaHeight(lphc
->self
,lphc
) + 2*COMBO_YBORDERSIZE();
313 GetWindowRect(lphc
->self
, &windowRect
);
316 * We have to be careful, resizing a combobox also has the meaning that the
317 * dropped rect will be resized. In this case, we want to trigger a resize
318 * to recalculate layout but we don't want to change the dropped rectangle
319 * So, we pass the height of text area of control as the height.
320 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
323 SetWindowPos( lphc
->self
,
326 windowRect
.right
- windowRect
.left
,
328 SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
331 /***********************************************************************
334 * Set up component coordinates given valid lphc->RectCombo.
336 static void CBCalcPlacement(
344 * Again, start with the client rectangle.
346 GetClientRect(hwnd
, lprEdit
);
351 InflateRect(lprEdit
, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
354 * Chop off the bottom part to fit with the height of the text area.
356 lprEdit
->bottom
= lprEdit
->top
+ CBGetTextAreaHeight(hwnd
, lphc
);
359 * The button starts the same vertical position as the text area.
361 CopyRect(lprButton
, lprEdit
);
364 * If the combobox is "simple" there is no button.
366 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
367 lprButton
->left
= lprButton
->right
= lprButton
->bottom
= 0;
371 * Let's assume the combobox button is the same width as the
373 * size the button horizontally and cut-off the text area.
375 lprButton
->left
= lprButton
->right
- GetSystemMetrics(SM_CXVSCROLL
);
376 lprEdit
->right
= lprButton
->left
;
380 * In the case of a dropdown, there is an additional spacing between the
381 * text area and the button.
383 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
385 lprEdit
->right
-= COMBO_EDITBUTTONSPACE();
389 * If we have an edit control, we space it away from the borders slightly.
391 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
393 InflateRect(lprEdit
, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
397 * Adjust the size of the listbox popup.
399 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
402 * Use the client rectangle to initialize the listbox rectangle
404 GetClientRect(hwnd
, lprLB
);
407 * Then, chop-off the top part.
409 lprLB
->top
= lprEdit
->bottom
+ COMBO_YBORDERSIZE();
414 * Make sure the dropped width is as large as the combobox itself.
416 if (lphc
->droppedWidth
< (lprButton
->right
+ COMBO_XBORDERSIZE()))
418 lprLB
->right
= lprLB
->left
+ (lprButton
->right
+ COMBO_XBORDERSIZE());
421 * In the case of a dropdown, the popup listbox is offset to the right.
422 * so, we want to make sure it's flush with the right side of the
425 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
426 lprLB
->right
-= COMBO_EDITBUTTONSPACE();
429 lprLB
->right
= lprLB
->left
+ lphc
->droppedWidth
;
432 DbgPrint("\ttext\t= (%ld,%ld-%ld,%ld)\n",
433 lprEdit
->left
, lprEdit
->top
, lprEdit
->right
, lprEdit
->bottom
);
435 DbgPrint("\tbutton\t= (%ld,%ld-%ld,%ld)\n",
436 lprButton
->left
, lprButton
->top
, lprButton
->right
, lprButton
->bottom
);
438 DbgPrint("\tlbox\t= (%ld,%ld-%ld,%ld)\n",
439 lprLB
->left
, lprLB
->top
, lprLB
->right
, lprLB
->bottom
);
442 /***********************************************************************
443 * CBGetDroppedControlRect
445 static void CBGetDroppedControlRect( LPHEADCOMBO lphc
, LPRECT lpRect
)
447 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
448 of the combo box and the lower right corner of the listbox */
450 GetWindowRect(lphc
->self
, lpRect
);
452 lpRect
->right
= lpRect
->left
+ lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
453 lpRect
->bottom
= lpRect
->top
+ lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
457 /***********************************************************************
458 * COMBO_WindowPosChanging
460 static LRESULT
COMBO_WindowPosChanging(
463 WINDOWPOS
* posChanging
)
466 * We need to override the WM_WINDOWPOSCHANGING method to handle all
467 * the non-simple comboboxes. The problem is that those controls are
468 * always the same height. We have to make sure they are not resized
471 if ( ( CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
472 ((posChanging
->flags
& SWP_NOSIZE
) == 0) )
476 newComboHeight
= CBGetTextAreaHeight(hwnd
,lphc
) +
477 2*COMBO_YBORDERSIZE();
480 * Resizing a combobox has another side effect, it resizes the dropped
481 * rectangle as well. However, it does it only if the new height for the
482 * combobox is different from the height it should have. In other words,
483 * if the application resizing the combobox only had the intention to resize
484 * the actual control, for example, to do the layout of a dialog that is
485 * resized, the height of the dropdown is not changed.
487 if (posChanging
->cy
!= newComboHeight
)
489 DbgPrint("posChanging->cy=%d, newComboHeight=%d, oldbot=%ld, oldtop=%ld\n",
490 posChanging
->cy
, newComboHeight
, lphc
->droppedRect
.bottom
,
491 lphc
->droppedRect
.top
);
492 lphc
->droppedRect
.bottom
= lphc
->droppedRect
.top
+ posChanging
->cy
- newComboHeight
;
494 posChanging
->cy
= newComboHeight
;
501 /***********************************************************************
504 static LRESULT
COMBO_Create( HWND hwnd
, LPHEADCOMBO lphc
, HWND hwndParent
, LONG style
,
507 static const WCHAR clbName
[] = {'C','o','m','b','o','L','B','o','x',0};
508 static const WCHAR editName
[] = {'E','d','i','t',0};
510 if( !CB_GETTYPE(lphc
) ) lphc
->dwStyle
|= CBS_SIMPLE
;
511 if( CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
) lphc
->wState
|= CBF_EDIT
;
513 lphc
->owner
= hwndParent
;
516 * The item height and dropped width are not set when the control
519 lphc
->droppedWidth
= lphc
->editHeight
= 0;
522 * The first time we go through, we want to measure the ownerdraw item
524 lphc
->wState
|= CBF_MEASUREITEM
;
526 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
528 if( lphc
->owner
|| !(style
& WS_VISIBLE
) )
534 * Initialize the dropped rect to the size of the client area of the
535 * control and then, force all the areas of the combobox to be
538 GetClientRect( hwnd
, &lphc
->droppedRect
);
539 CBCalcPlacement(hwnd
, lphc
, &lphc
->textRect
, &lphc
->buttonRect
, &lphc
->droppedRect
);
542 * Adjust the position of the popup listbox if it's necessary
544 if ( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
546 lphc
->droppedRect
.top
= lphc
->textRect
.bottom
+ COMBO_YBORDERSIZE();
549 * If it's a dropdown, the listbox is offset
551 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
552 lphc
->droppedRect
.left
+= COMBO_EDITBUTTONSPACE();
554 ClientToScreen(hwnd
, (LPPOINT
)&lphc
->droppedRect
);
555 ClientToScreen(hwnd
, (LPPOINT
)&lphc
->droppedRect
.right
);
558 /* create listbox popup */
560 lbeStyle
= (LBS_NOTIFY
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CHILD
) |
561 (style
& (WS_VSCROLL
| CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
));
563 if( lphc
->dwStyle
& CBS_SORT
)
564 lbeStyle
|= LBS_SORT
;
565 if( lphc
->dwStyle
& CBS_HASSTRINGS
)
566 lbeStyle
|= LBS_HASSTRINGS
;
567 if( lphc
->dwStyle
& CBS_NOINTEGRALHEIGHT
)
568 lbeStyle
|= LBS_NOINTEGRALHEIGHT
;
569 if( lphc
->dwStyle
& CBS_DISABLENOSCROLL
)
570 lbeStyle
|= LBS_DISABLENOSCROLL
;
572 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
) /* child listbox */
574 lbeStyle
|= WS_VISIBLE
;
577 * In win 95 look n feel, the listbox in the simple combobox has
578 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
580 //if (TWEAK_WineLook > WIN31_LOOK)
582 lbeStyle
&= ~WS_BORDER
;
583 lbeExStyle
|= WS_EX_CLIENTEDGE
;
588 lphc
->hWndLBox
= CreateWindowExW(lbeExStyle
, clbName
, NULL
, lbeStyle
,
589 lphc
->droppedRect
.left
,
590 lphc
->droppedRect
.top
,
591 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
592 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
593 hwnd
, (HMENU
)ID_CB_LISTBOX
,
594 (HINSTANCE
)GetWindowLongA( hwnd
, GWL_HINSTANCE
), lphc
);
596 lphc
->hWndLBox
= CreateWindowExA(lbeExStyle
, "ComboLBox", NULL
, lbeStyle
,
597 lphc
->droppedRect
.left
,
598 lphc
->droppedRect
.top
,
599 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
600 lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
,
601 hwnd
, (HMENU
)ID_CB_LISTBOX
,
602 (HINSTANCE
)GetWindowLongA( hwnd
, GWL_HINSTANCE
), lphc
);
607 lbeStyle
= WS_CHILD
| WS_VISIBLE
| ES_NOHIDESEL
| ES_LEFT
| ES_COMBO
;
610 * In Win95 look, the border fo the edit control is
611 * provided by the combobox
613 //if (TWEAK_WineLook == WIN31_LOOK)
614 // lbeStyle |= WS_BORDER;
616 if( lphc
->wState
& CBF_EDIT
)
618 if( lphc
->dwStyle
& CBS_OEMCONVERT
)
619 lbeStyle
|= ES_OEMCONVERT
;
620 if( lphc
->dwStyle
& CBS_AUTOHSCROLL
)
621 lbeStyle
|= ES_AUTOHSCROLL
;
622 if( lphc
->dwStyle
& CBS_LOWERCASE
)
623 lbeStyle
|= ES_LOWERCASE
;
624 else if( lphc
->dwStyle
& CBS_UPPERCASE
)
625 lbeStyle
|= ES_UPPERCASE
;
627 if (!IsWindowEnabled(hwnd
)) lbeStyle
|= WS_DISABLED
;
630 lphc
->hWndEdit
= CreateWindowExW(0, editName
, NULL
, lbeStyle
,
631 lphc
->textRect
.left
, lphc
->textRect
.top
,
632 lphc
->textRect
.right
- lphc
->textRect
.left
,
633 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
634 hwnd
, (HMENU
)ID_CB_EDIT
,
635 (HINSTANCE
)GetWindowLongA( hwnd
, GWL_HINSTANCE
), NULL
);
637 lphc
->hWndEdit
= CreateWindowExA(0, "Edit", NULL
, lbeStyle
,
638 lphc
->textRect
.left
, lphc
->textRect
.top
,
639 lphc
->textRect
.right
- lphc
->textRect
.left
,
640 lphc
->textRect
.bottom
- lphc
->textRect
.top
,
641 hwnd
, (HMENU
)ID_CB_EDIT
,
642 (HINSTANCE
)GetWindowLongA( hwnd
, GWL_HINSTANCE
), NULL
);
644 if( !lphc
->hWndEdit
)
650 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
652 /* Now do the trick with parent */
653 SetParent(lphc
->hWndLBox
, HWND_DESKTOP
);
655 * If the combo is a dropdown, we must resize the control
656 * to fit only the text area and button. To do this,
657 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
658 * will take care of setting the height for us.
660 CBForceDummyResize(lphc
);
663 OutputDebugStringA("init done\n");
666 OutputDebugStringA("edit control failure.\n");
667 } else OutputDebugStringA("listbox failure.\n");
668 } else OutputDebugStringA("no owner for visible combo.\n");
670 /* CreateWindow() will send WM_NCDESTROY to cleanup */
675 /***********************************************************************
678 * Paint combo button (normal, pressed, and disabled states).
680 static void CBPaintButton(
685 if( lphc
->wState
& CBF_NOREDRAW
)
688 UINT buttonState
= DFCS_SCROLLCOMBOBOX
;
690 if (lphc
->wState
& CBF_BUTTONDOWN
)
692 buttonState
|= DFCS_PUSHED
;
695 if (CB_DISABLED(lphc
))
697 buttonState
|= DFCS_INACTIVE
;
700 DrawFrameControl(hdc
,
706 /***********************************************************************
709 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
711 static void CBPaintText(
719 if( lphc
->wState
& CBF_NOREDRAW
) return;
721 OutputDebugStringA("\n");
723 /* follow Windows combobox that sends a bunch of text
724 * inquiries to its listbox while processing WM_PAINT. */
726 if( (id
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
728 size
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, id
, 0);
730 FIXME("LB_ERR probably not handled yet\n");
731 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
733 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
734 size
=SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
735 pText
[size
] = '\0'; /* just in case */
739 if( !CB_OWNERDRAWN(lphc
) )
742 if( lphc
->wState
& CBF_EDIT
)
744 static const WCHAR empty_stringW
[] = { 0 };
745 if( CB_HASSTRINGS(lphc
) ) SetWindowTextW( lphc
->hWndEdit
, pText
? pText
: empty_stringW
);
746 if( lphc
->wState
& CBF_FOCUSED
)
747 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
749 else /* paint text field ourselves */
751 UINT itemState
= ODS_COMBOBOXEDIT
;
752 HFONT hPrevFont
= (lphc
->hFont
) ? SelectObject(hdc
, lphc
->hFont
) : 0;
755 * Give ourselves some space.
757 InflateRect( &rectEdit
, -1, -1 );
759 if( CB_OWNERDRAWN(lphc
) )
763 UINT ctlid
= GetWindowLongA( lphc
->self
, GWL_ID
);
765 /* setup state for DRAWITEM message. Owner will highlight */
766 if ( (lphc
->wState
& CBF_FOCUSED
) &&
767 !(lphc
->wState
& CBF_DROPPED
) )
768 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
771 * Save the current clip region.
772 * To retrieve the clip region, we need to create one "dummy"
775 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
777 if (GetClipRgn(hdc
, clipRegion
)!=1)
779 DeleteObject(clipRegion
);
783 if (!IsWindowEnabled(lphc
->self
) & WS_DISABLED
) itemState
|= ODS_DISABLED
;
785 dis
.CtlType
= ODT_COMBOBOX
;
787 dis
.hwndItem
= lphc
->self
;
788 dis
.itemAction
= ODA_DRAWENTIRE
;
790 dis
.itemState
= itemState
;
792 dis
.rcItem
= rectEdit
;
793 dis
.itemData
= SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
,
797 * Clip the DC and have the parent draw the item.
799 IntersectClipRect(hdc
,
800 rectEdit
.left
, rectEdit
.top
,
801 rectEdit
.right
, rectEdit
.bottom
);
803 SendMessageW(lphc
->owner
, WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
806 * Reset the clipping region.
808 SelectClipRgn(hdc
, clipRegion
);
812 static const WCHAR empty_stringW
[] = { 0 };
814 if ( (lphc
->wState
& CBF_FOCUSED
) &&
815 !(lphc
->wState
& CBF_DROPPED
) ) {
818 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
819 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
820 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
826 ETO_OPAQUE
| ETO_CLIPPED
,
828 pText
? pText
: empty_stringW
, size
, NULL
);
830 if(lphc
->wState
& CBF_FOCUSED
&& !(lphc
->wState
& CBF_DROPPED
))
831 DrawFocusRect( hdc
, &rectEdit
);
835 SelectObject(hdc
, hPrevFont
);
838 HeapFree( GetProcessHeap(), 0, pText
);
841 /***********************************************************************
844 static void CBPaintBorder(
851 if (CB_GETTYPE(lphc
) != CBS_SIMPLE
)
853 GetClientRect(hwnd
, &clientRect
);
857 CopyRect(&clientRect
, &lphc
->textRect
);
859 InflateRect(&clientRect
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
860 InflateRect(&clientRect
, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
863 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_RECT
);
866 /***********************************************************************
867 * COMBO_PrepareColors
869 * This method will sent the appropriate WM_CTLCOLOR message to
870 * prepare and setup the colors for the combo's DC.
872 * It also returns the brush to use for the background.
874 static HBRUSH
COMBO_PrepareColors(
881 * Get the background brush for this control.
883 if (CB_DISABLED(lphc
))
885 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORSTATIC
,
886 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
889 * We have to change the text color since WM_CTLCOLORSTATIC will
890 * set it to the "enabled" color. This is the same behavior as the
893 SetTextColor(hDC
, GetSysColor(COLOR_GRAYTEXT
));
897 if (lphc
->wState
& CBF_EDIT
)
899 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLOREDIT
,
900 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
904 hBkgBrush
= (HBRUSH
)SendMessageW(lphc
->owner
, WM_CTLCOLORLISTBOX
,
905 (WPARAM
)hDC
, (LPARAM
)lphc
->self
);
913 hBkgBrush
= GetSysColorBrush(COLOR_WINDOW
);
918 /***********************************************************************
919 * COMBO_EraseBackground
921 static LRESULT
COMBO_EraseBackground(
929 if(lphc
->wState
& CBF_EDIT
)
932 hDC
= (hParamDC
) ? hParamDC
935 * Retrieve the background brush
937 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
939 FillRect(hDC
, &lphc
->textRect
, hBkgBrush
);
942 ReleaseDC(hwnd
, hDC
);
947 /***********************************************************************
950 static LRESULT
COMBO_Paint(LPHEADCOMBO lphc
, HDC hParamDC
)
955 hDC
= (hParamDC
) ? hParamDC
956 : BeginPaint( lphc
->self
, &ps
);
958 DbgPrint("hdc=%p\n", hDC
);
960 if( hDC
&& !(lphc
->wState
& CBF_NOREDRAW
) )
962 HBRUSH hPrevBrush
, hBkgBrush
;
965 * Retrieve the background brush and select it in the
968 hBkgBrush
= COMBO_PrepareColors(lphc
, hDC
);
970 hPrevBrush
= SelectObject( hDC
, hBkgBrush
);
973 * In non 3.1 look, there is a sunken border on the combobox
975 //if (TWEAK_WineLook != WIN31_LOOK)
977 CBPaintBorder(lphc
->self
, lphc
, hDC
);
980 if( !IsRectEmpty(&lphc
->buttonRect
) )
982 CBPaintButton(lphc
, hDC
, lphc
->buttonRect
);
985 /* paint the edit control padding area */
986 if (CB_GETTYPE(lphc
) != CBS_DROPDOWNLIST
)
988 RECT rPadEdit
= lphc
->textRect
;
990 InflateRect(&rPadEdit
, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
992 FrameRect( hDC
, &rPadEdit
, GetSysColorBrush(COLOR_WINDOW
) );
995 if( !(lphc
->wState
& CBF_EDIT
) )
997 CBPaintText( lphc
, hDC
, lphc
->textRect
);
1001 SelectObject( hDC
, hPrevBrush
);
1005 EndPaint(lphc
->self
, &ps
);
1010 /***********************************************************************
1013 * Select listbox entry according to the contents of the edit control.
1015 static INT
CBUpdateLBox( LPHEADCOMBO lphc
, BOOL bSelect
)
1018 LPWSTR pText
= NULL
;
1021 length
= SendMessageW( lphc
->hWndEdit
, WM_GETTEXTLENGTH
, 0, 0 );
1024 pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1026 DbgPrint("\t edit text length %i\n", length
);
1030 if( length
) GetWindowTextW( lphc
->hWndEdit
, pText
, length
+ 1);
1031 else pText
[0] = '\0';
1032 idx
= SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
,
1033 (WPARAM
)(-1), (LPARAM
)pText
);
1034 HeapFree( GetProcessHeap(), 0, pText
);
1037 SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, (WPARAM
)(bSelect
? idx
: -1), 0);
1039 /* probably superfluous but Windows sends this too */
1040 SendMessageW(lphc
->hWndLBox
, LB_SETCARETINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
1041 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, (WPARAM
)(idx
< 0 ? 0 : idx
), 0);
1046 /***********************************************************************
1049 * Copy a listbox entry to the edit control.
1051 static void CBUpdateEdit( LPHEADCOMBO lphc
, INT index
)
1054 LPWSTR pText
= NULL
;
1055 static const WCHAR empty_stringW
[] = { 0 };
1057 DbgPrint("\t %i\n", index
);
1059 if( index
>= 0 ) /* got an entry */
1061 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, (WPARAM
)index
, 0);
1062 if( length
!= LB_ERR
)
1064 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
))) )
1066 SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
,
1067 (WPARAM
)index
, (LPARAM
)pText
);
1072 lphc
->wState
|= (CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1073 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, pText
? (LPARAM
)pText
: (LPARAM
)empty_stringW
);
1074 lphc
->wState
&= ~(CBF_NOEDITNOTIFY
| CBF_NOLBSELECT
);
1076 if( lphc
->wState
& CBF_FOCUSED
)
1077 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1080 HeapFree( GetProcessHeap(), 0, pText
);
1083 /***********************************************************************
1086 * Show listbox popup.
1088 static void CBDropDown( LPHEADCOMBO lphc
)
1094 DbgPrint("[%p]: drop down\n", lphc
->self
);
1096 CB_NOTIFY( lphc
, CBN_DROPDOWN
);
1100 lphc
->wState
|= CBF_DROPPED
;
1101 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1103 lphc
->droppedIndex
= CBUpdateLBox( lphc
, TRUE
);
1105 /* Update edit only if item is in the list */
1106 if( !(lphc
->wState
& CBF_CAPTURE
) && lphc
->droppedIndex
>= 0)
1107 CBUpdateEdit( lphc
, lphc
->droppedIndex
);
1111 lphc
->droppedIndex
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1113 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
,
1114 (WPARAM
)(lphc
->droppedIndex
== LB_ERR
? 0 : lphc
->droppedIndex
), 0 );
1115 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1118 /* now set popup position */
1119 GetWindowRect( lphc
->self
, &rect
);
1122 * If it's a dropdown, the listbox is offset
1124 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1125 rect
.left
+= COMBO_EDITBUTTONSPACE();
1127 /* if the dropped height is greater than the total height of the dropped
1128 items list, then force the drop down list height to be the total height
1129 of the items in the dropped list */
1131 /* And Remove any extra space (Best Fit) */
1132 nDroppedHeight
= lphc
->droppedRect
.bottom
- lphc
->droppedRect
.top
;
1133 /* if listbox length has been set directly by its handle */
1134 GetWindowRect(lphc
->hWndLBox
, &r
);
1135 if (nDroppedHeight
< r
.bottom
- r
.top
)
1136 nDroppedHeight
= r
.bottom
- r
.top
;
1137 nItems
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
1144 nIHeight
= (int)SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, 0, 0);
1146 nHeight
= nIHeight
*nItems
;
1148 if (nHeight
< nDroppedHeight
- COMBO_YBORDERSIZE())
1149 nDroppedHeight
= nHeight
+ COMBO_YBORDERSIZE();
1151 if (nDroppedHeight
< nIHeight
)
1154 nDroppedHeight
= (nItems
+1)*nIHeight
;
1156 nDroppedHeight
= 6*nIHeight
;
1160 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1161 if( (rect
.bottom
+ nDroppedHeight
) >= GetSystemMetrics( SM_CYSCREEN
) )
1162 rect
.bottom
= rect
.top
- nDroppedHeight
;
1164 SetWindowPos( lphc
->hWndLBox
, HWND_TOP
, rect
.left
, rect
.bottom
,
1165 lphc
->droppedRect
.right
- lphc
->droppedRect
.left
,
1167 SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
1170 if( !(lphc
->wState
& CBF_NOREDRAW
) )
1171 RedrawWindow( lphc
->self
, NULL
, 0, RDW_INVALIDATE
|
1172 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1174 EnableWindow( lphc
->hWndLBox
, TRUE
);
1175 if (GetCapture() != lphc
->self
)
1176 SetCapture(lphc
->hWndLBox
);
1179 /***********************************************************************
1182 * Hide listbox popup.
1184 static void CBRollUp( LPHEADCOMBO lphc
, BOOL ok
, BOOL bButton
)
1186 HWND hWnd
= lphc
->self
;
1188 DbgPrint("[%p]: sel ok? [%i] dropped? [%i]\n",
1189 lphc
->self
, (INT
)ok
, (INT
)(lphc
->wState
& CBF_DROPPED
));
1191 CB_NOTIFY( lphc
, (ok
) ? CBN_SELENDOK
: CBN_SELENDCANCEL
);
1193 if( IsWindow( hWnd
) && CB_GETTYPE(lphc
) != CBS_SIMPLE
)
1196 if( lphc
->wState
& CBF_DROPPED
)
1200 lphc
->wState
&= ~CBF_DROPPED
;
1201 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1203 if(GetCapture() == lphc
->hWndLBox
)
1208 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1210 rect
= lphc
->buttonRect
;
1221 rect
= lphc
->textRect
;
1226 if( bButton
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1227 RedrawWindow( hWnd
, &rect
, 0, RDW_INVALIDATE
|
1228 RDW_ERASE
| RDW_UPDATENOW
| RDW_NOCHILDREN
);
1229 CB_NOTIFY( lphc
, CBN_CLOSEUP
);
1234 /***********************************************************************
1237 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1239 BOOL
COMBO_FlipListbox( LPHEADCOMBO lphc
, BOOL ok
, BOOL bRedrawButton
)
1241 if( lphc
->wState
& CBF_DROPPED
)
1243 CBRollUp( lphc
, ok
, bRedrawButton
);
1251 /***********************************************************************
1254 static void CBRepaintButton( LPHEADCOMBO lphc
)
1256 InvalidateRect(lphc
->self
, &lphc
->buttonRect
, TRUE
);
1257 UpdateWindow(lphc
->self
);
1260 /***********************************************************************
1263 static void COMBO_SetFocus( LPHEADCOMBO lphc
)
1265 if( !(lphc
->wState
& CBF_FOCUSED
) )
1267 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1268 SendMessageW(lphc
->hWndLBox
, LB_CARETON
, 0, 0);
1270 /* This is wrong. Message sequences seem to indicate that this
1271 is set *after* the notify. */
1272 /* lphc->wState |= CBF_FOCUSED; */
1274 if( !(lphc
->wState
& CBF_EDIT
) )
1275 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1277 CB_NOTIFY( lphc
, CBN_SETFOCUS
);
1278 lphc
->wState
|= CBF_FOCUSED
;
1282 /***********************************************************************
1285 static void COMBO_KillFocus( LPHEADCOMBO lphc
)
1287 HWND hWnd
= lphc
->self
;
1289 if( lphc
->wState
& CBF_FOCUSED
)
1291 CBRollUp( lphc
, FALSE
, TRUE
);
1292 if( IsWindow( hWnd
) )
1294 if( CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
)
1295 SendMessageW(lphc
->hWndLBox
, LB_CARETOFF
, 0, 0);
1297 lphc
->wState
&= ~CBF_FOCUSED
;
1300 if( !(lphc
->wState
& CBF_EDIT
) )
1301 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1303 CB_NOTIFY( lphc
, CBN_KILLFOCUS
);
1308 /***********************************************************************
1311 static LRESULT
COMBO_Command( LPHEADCOMBO lphc
, WPARAM wParam
, HWND hWnd
)
1313 if ( lphc
->wState
& CBF_EDIT
&& lphc
->hWndEdit
== hWnd
)
1315 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1317 switch( HIWORD(wParam
) >> 8 )
1319 case (EN_SETFOCUS
>> 8):
1321 DbgPrint("[%p]: edit [%p] got focus\n", lphc
->self
, lphc
->hWndEdit
);
1323 COMBO_SetFocus( lphc
);
1326 case (EN_KILLFOCUS
>> 8):
1328 DbgPrint("[%p]: edit [%p] lost focus\n", lphc
->self
, lphc
->hWndEdit
);
1330 /* NOTE: it seems that Windows' edit control sends an
1331 * undocumented message WM_USER + 0x1B instead of this
1332 * notification (only when it happens to be a part of
1333 * the combo). ?? - AK.
1336 COMBO_KillFocus( lphc
);
1340 case (EN_CHANGE
>> 8):
1342 * In some circumstances (when the selection of the combobox
1343 * is changed for example) we don't wans the EN_CHANGE notification
1344 * to be forwarded to the parent of the combobox. This code
1345 * checks a flag that is set in these occasions and ignores the
1348 if (lphc
->wState
& CBF_NOLBSELECT
)
1350 lphc
->wState
&= ~CBF_NOLBSELECT
;
1354 CBUpdateLBox( lphc
, lphc
->wState
& CBF_DROPPED
);
1357 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1358 CB_NOTIFY( lphc
, CBN_EDITCHANGE
);
1361 case (EN_UPDATE
>> 8):
1362 if (!(lphc
->wState
& CBF_NOEDITNOTIFY
))
1363 CB_NOTIFY( lphc
, CBN_EDITUPDATE
);
1366 case (EN_ERRSPACE
>> 8):
1367 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1370 else if( lphc
->hWndLBox
== hWnd
)
1372 switch( HIWORD(wParam
) )
1375 CB_NOTIFY( lphc
, CBN_ERRSPACE
);
1379 CB_NOTIFY( lphc
, CBN_DBLCLK
);
1385 DbgPrint("[%p]: lbox selection change [%x]\n", lphc
->self
, lphc
->wState
);
1387 if( HIWORD(wParam
) == LBN_SELCHANGE
)
1389 if( lphc
->wState
& CBF_EDIT
)
1391 INT index
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1392 lphc
->wState
|= CBF_NOLBSELECT
;
1393 CBUpdateEdit( lphc
, index
);
1394 /* select text in edit, as Windows does */
1395 SendMessageW(lphc
->hWndEdit
, EM_SETSEL
, 0, (LPARAM
)(-1));
1398 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1401 /* do not roll up if selection is being tracked
1402 * by arrowkeys in the dropdown listbox */
1403 if( ((lphc
->wState
& CBF_DROPPED
) && !(lphc
->wState
& CBF_NOROLLUP
)) )
1405 CBRollUp( lphc
, (HIWORD(wParam
) == LBN_SELCHANGE
), TRUE
);
1407 else lphc
->wState
&= ~CBF_NOROLLUP
;
1409 CB_NOTIFY( lphc
, CBN_SELCHANGE
);
1415 /* nothing to do here since ComboLBox always resets the focus to its
1416 * combo/edit counterpart */
1423 /***********************************************************************
1426 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1428 static LRESULT
COMBO_ItemOp( LPHEADCOMBO lphc
, UINT msg
, LPARAM lParam
)
1430 HWND hWnd
= lphc
->self
;
1431 UINT id
= GetWindowLongA( hWnd
, GWL_ID
);
1433 DbgPrint("[%p]: ownerdraw op %04x\n", lphc
->self
, msg
);
1439 DELETEITEMSTRUCT
*lpIS
= (DELETEITEMSTRUCT
*)lParam
;
1440 lpIS
->CtlType
= ODT_COMBOBOX
;
1442 lpIS
->hwndItem
= hWnd
;
1447 DRAWITEMSTRUCT
*lpIS
= (DRAWITEMSTRUCT
*)lParam
;
1448 lpIS
->CtlType
= ODT_COMBOBOX
;
1450 lpIS
->hwndItem
= hWnd
;
1453 case WM_COMPAREITEM
:
1455 COMPAREITEMSTRUCT
*lpIS
= (COMPAREITEMSTRUCT
*)lParam
;
1456 lpIS
->CtlType
= ODT_COMBOBOX
;
1458 lpIS
->hwndItem
= hWnd
;
1461 case WM_MEASUREITEM
:
1463 MEASUREITEMSTRUCT
*lpIS
= (MEASUREITEMSTRUCT
*)lParam
;
1464 lpIS
->CtlType
= ODT_COMBOBOX
;
1469 return SendMessageW(lphc
->owner
, msg
, id
, lParam
);
1473 /***********************************************************************
1476 static LRESULT
COMBO_GetTextW( LPHEADCOMBO lphc
, INT count
, LPWSTR buf
)
1480 if( lphc
->wState
& CBF_EDIT
)
1481 return SendMessageW( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1483 /* get it from the listbox */
1485 if (!count
|| !buf
) return 0;
1486 if( lphc
->hWndLBox
)
1488 INT idx
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1489 if (idx
== LB_ERR
) goto error
;
1490 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1491 if (length
== LB_ERR
) goto error
;
1493 /* 'length' is without the terminating character */
1494 if (length
>= count
)
1496 LPWSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) * sizeof(WCHAR
));
1497 if (!lpBuffer
) goto error
;
1498 length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1500 /* truncate if buffer is too short */
1501 if (length
!= LB_ERR
)
1503 lstrcpynW( buf
, lpBuffer
, count
);
1506 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1508 else length
= SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1510 if (length
== LB_ERR
) return 0;
1514 error
: /* error - truncate string, return zero */
1520 /***********************************************************************
1523 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1524 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1526 static LRESULT
COMBO_GetTextA( LPHEADCOMBO lphc
, INT count
, LPSTR buf
)
1530 if( lphc
->wState
& CBF_EDIT
)
1531 return SendMessageA( lphc
->hWndEdit
, WM_GETTEXT
, count
, (LPARAM
)buf
);
1533 /* get it from the listbox */
1535 if (!count
|| !buf
) return 0;
1536 if( lphc
->hWndLBox
)
1538 INT idx
= SendMessageA(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1539 if (idx
== LB_ERR
) goto error
;
1540 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, idx
, 0 );
1541 if (length
== LB_ERR
) goto error
;
1543 /* 'length' is without the terminating character */
1544 if (length
>= count
)
1546 LPSTR lpBuffer
= HeapAlloc(GetProcessHeap(), 0, (length
+ 1) );
1547 if (!lpBuffer
) goto error
;
1548 length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)lpBuffer
);
1550 /* truncate if buffer is too short */
1551 if (length
!= LB_ERR
)
1553 lstrcpynA( buf
, lpBuffer
, count
);
1556 HeapFree( GetProcessHeap(), 0, lpBuffer
);
1558 else length
= SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, idx
, (LPARAM
)buf
);
1560 if (length
== LB_ERR
) return 0;
1564 error
: /* error - truncate string, return zero */
1570 /***********************************************************************
1573 * This function sets window positions according to the updated
1574 * component placement struct.
1576 static void CBResetPos(
1582 BOOL bDrop
= (CB_GETTYPE(lphc
) != CBS_SIMPLE
);
1584 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1585 * sizing messages */
1587 if( lphc
->wState
& CBF_EDIT
)
1588 SetWindowPos( lphc
->hWndEdit
, 0,
1589 rectEdit
->left
, rectEdit
->top
,
1590 rectEdit
->right
- rectEdit
->left
,
1591 rectEdit
->bottom
- rectEdit
->top
,
1592 SWP_NOZORDER
| SWP_NOACTIVATE
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1594 SetWindowPos( lphc
->hWndLBox
, 0,
1595 rectLB
->left
, rectLB
->top
,
1596 rectLB
->right
- rectLB
->left
,
1597 rectLB
->bottom
- rectLB
->top
,
1598 SWP_NOACTIVATE
| SWP_NOZORDER
| ((bDrop
) ? SWP_NOREDRAW
: 0) );
1602 if( lphc
->wState
& CBF_DROPPED
)
1604 lphc
->wState
&= ~CBF_DROPPED
;
1605 ShowWindow( lphc
->hWndLBox
, SW_HIDE
);
1608 if( bRedraw
&& !(lphc
->wState
& CBF_NOREDRAW
) )
1609 RedrawWindow( lphc
->self
, NULL
, 0,
1610 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
1615 /***********************************************************************
1618 static void COMBO_Size( LPHEADCOMBO lphc
)
1620 CBCalcPlacement(lphc
->self
,
1624 &lphc
->droppedRect
);
1626 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1630 /***********************************************************************
1633 static void COMBO_Font( LPHEADCOMBO lphc
, HFONT hFont
, BOOL bRedraw
)
1638 lphc
->hFont
= hFont
;
1641 * Propagate to owned windows.
1643 if( lphc
->wState
& CBF_EDIT
)
1644 SendMessageW(lphc
->hWndEdit
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1645 SendMessageW(lphc
->hWndLBox
, WM_SETFONT
, (WPARAM
)hFont
, bRedraw
);
1648 * Redo the layout of the control.
1650 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1652 CBCalcPlacement(lphc
->self
,
1656 &lphc
->droppedRect
);
1658 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1662 CBForceDummyResize(lphc
);
1667 /***********************************************************************
1668 * COMBO_SetItemHeight
1670 static LRESULT
COMBO_SetItemHeight( LPHEADCOMBO lphc
, INT index
, INT height
)
1672 LRESULT lRet
= CB_ERR
;
1674 if( index
== -1 ) /* set text field height */
1676 if( height
< 32768 )
1678 lphc
->editHeight
= height
;
1681 * Redo the layout of the control.
1683 if ( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
1685 CBCalcPlacement(lphc
->self
,
1689 &lphc
->droppedRect
);
1691 CBResetPos( lphc
, &lphc
->textRect
, &lphc
->droppedRect
, TRUE
);
1695 CBForceDummyResize(lphc
);
1701 else if ( CB_OWNERDRAWN(lphc
) ) /* set listbox item height */
1702 lRet
= SendMessageW(lphc
->hWndLBox
, LB_SETITEMHEIGHT
,
1703 (WPARAM
)index
, (LPARAM
)height
);
1707 /***********************************************************************
1708 * COMBO_SelectString
1710 static LRESULT
COMBO_SelectString( LPHEADCOMBO lphc
, INT start
, LPARAM pText
, BOOL unicode
)
1712 INT index
= unicode
? SendMessageW(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
) :
1713 SendMessageA(lphc
->hWndLBox
, LB_SELECTSTRING
, (WPARAM
)start
, pText
);
1716 if( lphc
->wState
& CBF_EDIT
)
1717 CBUpdateEdit( lphc
, index
);
1720 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
1723 return (LRESULT
)index
;
1726 /***********************************************************************
1729 static void COMBO_LButtonDown( LPHEADCOMBO lphc
, LPARAM lParam
)
1733 HWND hWnd
= lphc
->self
;
1735 pt
.x
= LOWORD(lParam
);
1736 pt
.y
= HIWORD(lParam
);
1737 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1739 if( (CB_GETTYPE(lphc
) == CBS_DROPDOWNLIST
) ||
1740 (bButton
&& (CB_GETTYPE(lphc
) == CBS_DROPDOWN
)) )
1742 lphc
->wState
|= CBF_BUTTONDOWN
;
1743 if( lphc
->wState
& CBF_DROPPED
)
1745 /* got a click to cancel selection */
1747 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1748 CBRollUp( lphc
, TRUE
, FALSE
);
1749 if( !IsWindow( hWnd
) ) return;
1751 if( lphc
->wState
& CBF_CAPTURE
)
1753 lphc
->wState
&= ~CBF_CAPTURE
;
1759 /* drop down the listbox and start tracking */
1761 lphc
->wState
|= CBF_CAPTURE
;
1765 if( bButton
) CBRepaintButton( lphc
);
1769 /***********************************************************************
1772 * Release capture and stop tracking if needed.
1774 static void COMBO_LButtonUp( LPHEADCOMBO lphc
)
1776 if( lphc
->wState
& CBF_CAPTURE
)
1778 lphc
->wState
&= ~CBF_CAPTURE
;
1779 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
)
1781 INT index
= CBUpdateLBox( lphc
, TRUE
);
1782 /* Update edit only if item is in the list */
1785 lphc
->wState
|= CBF_NOLBSELECT
;
1786 CBUpdateEdit( lphc
, index
);
1787 lphc
->wState
&= ~CBF_NOLBSELECT
;
1791 SetCapture(lphc
->hWndLBox
);
1794 if( lphc
->wState
& CBF_BUTTONDOWN
)
1796 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1797 CBRepaintButton( lphc
);
1801 /***********************************************************************
1804 * Two things to do - track combo button and release capture when
1805 * pointer goes into the listbox.
1807 static void COMBO_MouseMove( LPHEADCOMBO lphc
, WPARAM wParam
, LPARAM lParam
)
1812 pt
.x
= LOWORD(lParam
);
1813 pt
.y
= HIWORD(lParam
);
1815 if( lphc
->wState
& CBF_BUTTONDOWN
)
1819 bButton
= PtInRect(&lphc
->buttonRect
, pt
);
1823 lphc
->wState
&= ~CBF_BUTTONDOWN
;
1824 CBRepaintButton( lphc
);
1828 GetClientRect( lphc
->hWndLBox
, &lbRect
);
1829 MapWindowPoints( lphc
->self
, lphc
->hWndLBox
, &pt
, 1 );
1830 if( PtInRect(&lbRect
, pt
) )
1832 lphc
->wState
&= ~CBF_CAPTURE
;
1834 if( CB_GETTYPE(lphc
) == CBS_DROPDOWN
) CBUpdateLBox( lphc
, TRUE
);
1836 /* hand over pointer tracking */
1837 SendMessageW(lphc
->hWndLBox
, WM_LBUTTONDOWN
, wParam
, lParam
);
1842 /***********************************************************************
1843 * ComboWndProc_common
1845 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1847 static LRESULT
ComboWndProc_common( HWND hwnd
, UINT message
,
1848 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
1850 LPHEADCOMBO lphc
= (LPHEADCOMBO
)GetWindowLongA( hwnd
, 0 );
1852 //TRACE("[%p]: msg %s wp %08x lp %08lx\n",
1853 // hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1855 if( lphc
|| message
== WM_NCCREATE
)
1859 /* System messages */
1863 LONG style
= unicode
? ((LPCREATESTRUCTW
)lParam
)->style
:
1864 ((LPCREATESTRUCTA
)lParam
)->style
;
1865 return COMBO_NCCreate(hwnd
, style
);
1868 COMBO_NCDestroy(lphc
);
1869 break;/* -> DefWindowProc */
1877 hwndParent
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1878 style
= ((LPCREATESTRUCTW
)lParam
)->style
;
1882 hwndParent
= ((LPCREATESTRUCTA
)lParam
)->hwndParent
;
1883 style
= ((LPCREATESTRUCTA
)lParam
)->style
;
1885 return COMBO_Create(hwnd
, lphc
, hwndParent
, style
, unicode
);
1888 case WM_PRINTCLIENT
:
1889 if (lParam
& PRF_ERASEBKGND
)
1890 COMBO_EraseBackground(hwnd
, lphc
, (HDC
)wParam
);
1894 /* wParam may contain a valid HDC! */
1895 return COMBO_Paint(lphc
, (HDC
)wParam
);
1897 return COMBO_EraseBackground(hwnd
, lphc
, (HDC
)wParam
);
1900 LRESULT result
= DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1901 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
1903 int vk
= (int)((LPMSG
)lParam
)->wParam
;
1905 if ((vk
== VK_RETURN
|| vk
== VK_ESCAPE
) && (lphc
->wState
& CBF_DROPPED
))
1906 result
|= DLGC_WANTMESSAGE
;
1910 case WM_WINDOWPOSCHANGING
:
1911 return COMBO_WindowPosChanging(hwnd
, lphc
, (LPWINDOWPOS
)lParam
);
1912 case WM_WINDOWPOSCHANGED
:
1913 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1914 * In that case, the Combobox itself will not be resized, so we won't
1915 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1920 if( lphc
->hWndLBox
&&
1921 !(lphc
->wState
& CBF_NORESIZE
) ) COMBO_Size( lphc
);
1924 COMBO_Font( lphc
, (HFONT
)wParam
, (BOOL
)lParam
);
1927 return (LRESULT
)lphc
->hFont
;
1929 if( lphc
->wState
& CBF_EDIT
)
1930 SetFocus( lphc
->hWndEdit
);
1932 COMBO_SetFocus( lphc
);
1936 //HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1938 // (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1939 // COMBO_KillFocus( lphc );
1943 //return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1944 return COMBO_Command( lphc
, wParam
, (HWND
)lParam
);
1947 return unicode
? COMBO_GetTextW( lphc
, wParam
, (LPWSTR
)lParam
)
1948 : COMBO_GetTextA( lphc
, wParam
, (LPSTR
)lParam
);
1950 case WM_GETTEXTLENGTH
:
1952 if ((message
== WM_GETTEXTLENGTH
) && !ISWIN31
&& !(lphc
->wState
& CBF_EDIT
))
1954 int j
= SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
1955 if (j
== -1) return 0;
1956 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0) :
1957 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, j
, 0);
1959 else if( lphc
->wState
& CBF_EDIT
)
1962 lphc
->wState
|= CBF_NOEDITNOTIFY
;
1963 ret
= unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1964 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1965 lphc
->wState
&= ~CBF_NOEDITNOTIFY
;
1972 if( lphc
->wState
& CBF_EDIT
)
1974 return unicode
? SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
) :
1975 SendMessageA(lphc
->hWndEdit
, message
, wParam
, lParam
);
1981 case WM_COMPAREITEM
:
1982 case WM_MEASUREITEM
:
1983 return COMBO_ItemOp(lphc
, message
, lParam
);
1985 if( lphc
->wState
& CBF_EDIT
)
1986 EnableWindow( lphc
->hWndEdit
, (BOOL
)wParam
);
1987 EnableWindow( lphc
->hWndLBox
, (BOOL
)wParam
);
1989 /* Force the control to repaint when the enabled state changes. */
1990 InvalidateRect(lphc
->self
, NULL
, TRUE
);
1994 lphc
->wState
&= ~CBF_NOREDRAW
;
1996 lphc
->wState
|= CBF_NOREDRAW
;
1998 if( lphc
->wState
& CBF_EDIT
)
1999 SendMessageW(lphc
->hWndEdit
, message
, wParam
, lParam
);
2000 SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
);
2003 if( KEYDATA_ALT
& HIWORD(lParam
) )
2004 if( wParam
== VK_UP
|| wParam
== VK_DOWN
)
2005 COMBO_FlipListbox( lphc
, FALSE
, FALSE
);
2014 if ((wParam
== VK_RETURN
|| wParam
== VK_ESCAPE
) &&
2015 (lphc
->wState
& CBF_DROPPED
))
2017 CBRollUp( lphc
, wParam
== VK_RETURN
, FALSE
);
2021 if( lphc
->wState
& CBF_EDIT
)
2022 hwndTarget
= lphc
->hWndEdit
;
2024 hwndTarget
= lphc
->hWndLBox
;
2026 return unicode
? SendMessageW(hwndTarget
, message
, wParam
, lParam
) :
2027 SendMessageA(hwndTarget
, message
, wParam
, lParam
);
2029 case WM_LBUTTONDOWN
:
2030 if( !(lphc
->wState
& CBF_FOCUSED
) ) SetFocus( lphc
->self
);
2031 if( lphc
->wState
& CBF_FOCUSED
) COMBO_LButtonDown( lphc
, lParam
);
2034 COMBO_LButtonUp( lphc
);
2037 if( lphc
->wState
& CBF_CAPTURE
)
2038 COMBO_MouseMove( lphc
, wParam
, lParam
);
2042 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
2043 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2044 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2045 if (SHIWORD(wParam
) > 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_UP
, 0);
2046 if (SHIWORD(wParam
) < 0) return SendMessageW(hwnd
, WM_KEYDOWN
, VK_DOWN
, 0);
2049 /* Combo messages */
2052 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
) :
2053 SendMessageA(lphc
->hWndLBox
, LB_ADDSTRING
, 0, lParam
);
2054 case CB_INSERTSTRING
:
2055 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
) :
2056 SendMessageA(lphc
->hWndLBox
, LB_INSERTSTRING
, wParam
, lParam
);
2057 case CB_DELETESTRING
:
2058 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0) :
2059 SendMessageA(lphc
->hWndLBox
, LB_DELETESTRING
, wParam
, 0);
2060 case CB_SELECTSTRING
:
2061 return COMBO_SelectString(lphc
, (INT
)wParam
, lParam
, unicode
);
2063 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
) :
2064 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRING
, wParam
, lParam
);
2065 case CB_FINDSTRINGEXACT
:
2066 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
) :
2067 SendMessageA(lphc
->hWndLBox
, LB_FINDSTRINGEXACT
, wParam
, lParam
);
2068 case CB_SETITEMHEIGHT
:
2069 return COMBO_SetItemHeight( lphc
, (INT
)wParam
, (INT
)lParam
);
2070 case CB_GETITEMHEIGHT
:
2071 if( (INT
)wParam
>= 0 ) /* listbox item */
2072 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMHEIGHT
, wParam
, 0);
2073 return CBGetTextAreaHeight(hwnd
, lphc
);
2074 case CB_RESETCONTENT
:
2075 SendMessageW(lphc
->hWndLBox
, LB_RESETCONTENT
, 0, 0);
2076 if( (lphc
->wState
& CBF_EDIT
) && CB_HASSTRINGS(lphc
) )
2078 static const WCHAR empty_stringW
[] = { 0 };
2079 SendMessageW(lphc
->hWndEdit
, WM_SETTEXT
, 0, (LPARAM
)empty_stringW
);
2082 InvalidateRect(lphc
->self
, NULL
, TRUE
);
2084 case CB_INITSTORAGE
:
2085 return SendMessageW(lphc
->hWndLBox
, LB_INITSTORAGE
, wParam
, lParam
);
2086 case CB_GETHORIZONTALEXTENT
:
2087 return SendMessageW(lphc
->hWndLBox
, LB_GETHORIZONTALEXTENT
, 0, 0);
2088 case CB_SETHORIZONTALEXTENT
:
2089 return SendMessageW(lphc
->hWndLBox
, LB_SETHORIZONTALEXTENT
, wParam
, 0);
2090 case CB_GETTOPINDEX
:
2091 return SendMessageW(lphc
->hWndLBox
, LB_GETTOPINDEX
, 0, 0);
2093 return SendMessageW(lphc
->hWndLBox
, LB_GETLOCALE
, 0, 0);
2095 return SendMessageW(lphc
->hWndLBox
, LB_SETLOCALE
, wParam
, 0);
2096 case CB_GETDROPPEDWIDTH
:
2097 if( lphc
->droppedWidth
)
2098 return lphc
->droppedWidth
;
2099 return lphc
->droppedRect
.right
- lphc
->droppedRect
.left
;
2100 case CB_SETDROPPEDWIDTH
:
2101 if( (CB_GETTYPE(lphc
) != CBS_SIMPLE
) &&
2102 (INT
)wParam
< 32768 ) lphc
->droppedWidth
= (INT
)wParam
;
2104 case CB_GETDROPPEDCONTROLRECT
:
2105 if( lParam
) CBGetDroppedControlRect(lphc
, (LPRECT
)lParam
);
2107 case CB_GETDROPPEDSTATE
:
2108 return (lphc
->wState
& CBF_DROPPED
) ? TRUE
: FALSE
;
2110 if(message
== CB_DIR
) message
= LB_DIR
;
2111 return unicode
? SendMessageW(lphc
->hWndLBox
, message
, wParam
, lParam
) :
2112 SendMessageA(lphc
->hWndLBox
, message
, wParam
, lParam
);
2114 case CB_SHOWDROPDOWN
:
2115 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2119 if( !(lphc
->wState
& CBF_DROPPED
) )
2123 if( lphc
->wState
& CBF_DROPPED
)
2124 CBRollUp( lphc
, FALSE
, TRUE
);
2128 return SendMessageW(lphc
->hWndLBox
, LB_GETCOUNT
, 0, 0);
2130 return SendMessageW(lphc
->hWndLBox
, LB_GETCURSEL
, 0, 0);
2132 lParam
= SendMessageW(lphc
->hWndLBox
, LB_SETCURSEL
, wParam
, 0);
2134 SendMessageW(lphc
->hWndLBox
, LB_SETTOPINDEX
, wParam
, 0);
2136 /* no LBN_SELCHANGE in this case, update manually */
2137 if( lphc
->wState
& CBF_EDIT
)
2138 CBUpdateEdit( lphc
, (INT
)wParam
);
2140 InvalidateRect(lphc
->self
, &lphc
->textRect
, TRUE
);
2141 lphc
->wState
&= ~CBF_SELCHANGE
;
2144 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
) :
2145 SendMessageA(lphc
->hWndLBox
, LB_GETTEXT
, wParam
, lParam
);
2146 case CB_GETLBTEXTLEN
:
2147 return unicode
? SendMessageW(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0) :
2148 SendMessageA(lphc
->hWndLBox
, LB_GETTEXTLEN
, wParam
, 0);
2149 case CB_GETITEMDATA
:
2150 return SendMessageW(lphc
->hWndLBox
, LB_GETITEMDATA
, wParam
, 0);
2151 case CB_SETITEMDATA
:
2152 return SendMessageW(lphc
->hWndLBox
, LB_SETITEMDATA
, wParam
, lParam
);
2154 /* Edit checks passed parameters itself */
2155 if( lphc
->wState
& CBF_EDIT
)
2156 return SendMessageW(lphc
->hWndEdit
, EM_GETSEL
, wParam
, lParam
);
2159 if( lphc
->wState
& CBF_EDIT
)
2160 return SendMessageW(lphc
->hWndEdit
, EM_SETSEL
,
2161 (INT
)(INT16
)LOWORD(lParam
), (INT
)(INT16
)HIWORD(lParam
) );
2163 case CB_SETEXTENDEDUI
:
2164 if( CB_GETTYPE(lphc
) == CBS_SIMPLE
)
2167 lphc
->wState
|= CBF_EUI
;
2168 else lphc
->wState
&= ~CBF_EUI
;
2170 case CB_GETEXTENDEDUI
:
2171 return (lphc
->wState
& CBF_EUI
) ? TRUE
: FALSE
;
2174 if (message
>= WM_USER
)
2175 DbgPrint("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2176 message
- WM_USER
, wParam
, lParam
);
2179 return unicode
? DefWindowProcW(hwnd
, message
, wParam
, lParam
) :
2180 DefWindowProcA(hwnd
, message
, wParam
, lParam
);
2183 /***********************************************************************
2186 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2189 static LRESULT WINAPI
ComboWndProcA( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2191 if (!IsWindow(hwnd
)) return 0;
2192 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, FALSE
);
2195 /***********************************************************************
2198 static LRESULT WINAPI
ComboWndProcW( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
2200 if (!IsWindow(hwnd
)) return 0;
2201 return ComboWndProc_common( hwnd
, message
, wParam
, lParam
, TRUE
);
2204 /*************************************************************************
2205 * GetComboBoxInfo (USER32.@)
2207 BOOL WINAPI
GetComboBoxInfo(HWND hwndCombo
, /* [in] handle to combo box */
2208 PCOMBOBOXINFO pcbi
/* [in/out] combo box information */)