Ensure there is an edit control when we reset contents.
[wine/dcerpc.git] / controls / combo.c
blobb886993752553685ea5863dc6ed63b7603e70af4
1 /*
2 * Combo controls
3 *
4 * Copyright 1997 Alex Korobka
5 *
6 * FIXME: roll up in Netscape 3.01.
7 */
9 #include <string.h>
11 #include "winbase.h"
12 #include "windef.h"
13 #include "wingdi.h"
14 #include "winuser.h"
15 #include "wine/winuser16.h"
16 #include "win.h"
17 #include "spy.h"
18 #include "user.h"
19 #include "heap.h"
20 #include "controls.h"
21 #include "debugtools.h"
22 #include "tweak.h"
24 DEFAULT_DEBUG_CHANNEL(combo);
26 /* bits in the dwKeyData */
27 #define KEYDATA_ALT 0x2000
28 #define KEYDATA_PREVSTATE 0x4000
31 * Additional combo box definitions
34 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
35 #define CB_NOTIFY( lphc, code ) \
36 (SendMessageW((lphc)->owner, WM_COMMAND, \
37 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
38 #define CB_GETEDITTEXTLENGTH( lphc ) \
39 (SendMessageW((lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
41 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
44 * Drawing globals
46 static HBITMAP hComboBmp = 0;
47 static UINT CBitHeight, CBitWidth;
50 * Look and feel dependant "constants"
53 #define COMBO_YBORDERGAP 5
54 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
55 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
56 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
57 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
59 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
60 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
62 /*********************************************************************
63 * combo class descriptor
65 const struct builtin_class_descr COMBO_builtin_class =
67 "ComboBox", /* name */
68 CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS, /* style */
69 ComboWndProcA, /* procA */
70 ComboWndProcW, /* procW */
71 sizeof(HEADCOMBO *), /* extra */
72 IDC_ARROWA, /* cursor */
73 0 /* brush */
77 /***********************************************************************
78 * COMBO_Init
80 * Load combo button bitmap.
82 static BOOL COMBO_Init()
84 HDC hDC;
86 if( hComboBmp ) return TRUE;
87 if( (hDC = CreateCompatibleDC(0)) )
89 BOOL bRet = FALSE;
90 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
92 BITMAP bm;
93 HBITMAP hPrevB;
94 RECT r;
96 GetObjectW( hComboBmp, sizeof(bm), &bm );
97 CBitHeight = bm.bmHeight;
98 CBitWidth = bm.bmWidth;
100 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
102 hPrevB = SelectObject( hDC, hComboBmp);
103 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
104 InvertRect( hDC, &r );
105 SelectObject( hDC, hPrevB );
106 bRet = TRUE;
108 DeleteDC( hDC );
109 return bRet;
111 return FALSE;
114 /***********************************************************************
115 * COMBO_NCCreate
117 static LRESULT COMBO_NCCreate(WND* wnd, LONG style)
119 LPHEADCOMBO lphc;
121 if ( wnd && COMBO_Init() &&
122 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
124 memset( lphc, 0, sizeof(HEADCOMBO) );
125 *(LPHEADCOMBO*)wnd->wExtra = lphc;
127 /* some braindead apps do try to use scrollbar/border flags */
129 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
130 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
133 * We also have to remove the client edge style to make sure
134 * we don't end-up with a non client area.
136 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
138 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
139 lphc->dwStyle |= CBS_HASSTRINGS;
140 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
141 lphc->wState |= CBF_NOTIFY;
143 TRACE("[0x%08x], style = %08x\n",
144 (UINT)lphc, lphc->dwStyle );
146 return (LRESULT)(UINT)wnd->hwndSelf;
148 return (LRESULT)FALSE;
151 /***********************************************************************
152 * COMBO_NCDestroy
154 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
157 if( lphc )
159 WND* wnd = lphc->self;
161 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc));
163 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
164 DestroyWindow( lphc->hWndLBox );
166 HeapFree( GetProcessHeap(), 0, lphc );
167 wnd->wExtra[0] = 0;
169 return 0;
172 /***********************************************************************
173 * CBGetTextAreaHeight
175 * This method will calculate the height of the text area of the
176 * combobox.
177 * The height of the text area is set in two ways.
178 * It can be set explicitely through a combobox message or through a
179 * WM_MEASUREITEM callback.
180 * If this is not the case, the height is set to 13 dialog units.
181 * This height was determined through experimentation.
183 static INT CBGetTextAreaHeight(
184 HWND hwnd,
185 LPHEADCOMBO lphc)
187 INT iTextItemHeight;
189 if( lphc->editHeight ) /* explicitly set height */
191 iTextItemHeight = lphc->editHeight;
193 else
195 TEXTMETRICW tm;
196 HDC hDC = GetDC(hwnd);
197 HFONT hPrevFont = 0;
198 INT baseUnitY;
200 if (lphc->hFont)
201 hPrevFont = SelectObject( hDC, lphc->hFont );
203 GetTextMetricsW(hDC, &tm);
205 baseUnitY = tm.tmHeight;
207 if( hPrevFont )
208 SelectObject( hDC, hPrevFont );
210 ReleaseDC(hwnd, hDC);
212 iTextItemHeight = ((13 * baseUnitY) / 8);
215 * This "formula" calculates the height of the complete control.
216 * To calculate the height of the text area, we have to remove the
217 * borders.
219 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
223 * Check the ownerdraw case if we haven't asked the parent the size
224 * of the item yet.
226 if ( CB_OWNERDRAWN(lphc) &&
227 (lphc->wState & CBF_MEASUREITEM) )
229 MEASUREITEMSTRUCT measureItem;
230 RECT clientRect;
231 INT originalItemHeight = iTextItemHeight;
234 * We use the client rect for the width of the item.
236 GetClientRect(hwnd, &clientRect);
238 lphc->wState &= ~CBF_MEASUREITEM;
241 * Send a first one to measure the size of the text area
243 measureItem.CtlType = ODT_COMBOBOX;
244 measureItem.CtlID = lphc->self->wIDmenu;
245 measureItem.itemID = -1;
246 measureItem.itemWidth = clientRect.right;
247 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
248 measureItem.itemData = 0;
249 SendMessageW(lphc->owner, WM_MEASUREITEM,
250 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
251 iTextItemHeight = 6 + measureItem.itemHeight;
254 * Send a second one in the case of a fixed ownerdraw list to calculate the
255 * size of the list items. (we basically do this on behalf of the listbox)
257 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
259 measureItem.CtlType = ODT_COMBOBOX;
260 measureItem.CtlID = lphc->self->wIDmenu;
261 measureItem.itemID = 0;
262 measureItem.itemWidth = clientRect.right;
263 measureItem.itemHeight = originalItemHeight;
264 measureItem.itemData = 0;
265 SendMessageW(lphc->owner, WM_MEASUREITEM,
266 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
267 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
271 * Keep the size for the next time
273 lphc->editHeight = iTextItemHeight;
276 return iTextItemHeight;
279 /***********************************************************************
280 * CBForceDummyResize
282 * The dummy resize is used for listboxes that have a popup to trigger
283 * a re-arranging of the contents of the combobox and the recalculation
284 * of the size of the "real" control window.
286 static void CBForceDummyResize(
287 LPHEADCOMBO lphc)
289 RECT windowRect;
290 int newComboHeight;
292 newComboHeight = CBGetTextAreaHeight(CB_HWND(lphc),lphc) + 2*COMBO_YBORDERSIZE();
294 GetWindowRect(CB_HWND(lphc), &windowRect);
297 * We have to be careful, resizing a combobox also has the meaning that the
298 * dropped rect will be resized. In this case, we want to trigger a resize
299 * to recalculate layout but we don't want to change the dropped rectangle
300 * So, we pass the height of text area of control as the height.
301 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
302 * message.
304 SetWindowPos( CB_HWND(lphc),
305 (HWND)NULL,
306 0, 0,
307 windowRect.right - windowRect.left,
308 newComboHeight,
309 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
312 /***********************************************************************
313 * CBCalcPlacement
315 * Set up component coordinates given valid lphc->RectCombo.
317 static void CBCalcPlacement(
318 HWND hwnd,
319 LPHEADCOMBO lphc,
320 LPRECT lprEdit,
321 LPRECT lprButton,
322 LPRECT lprLB)
325 * Again, start with the client rectangle.
327 GetClientRect(hwnd, lprEdit);
330 * Remove the borders
332 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
335 * Chop off the bottom part to fit with the height of the text area.
337 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
340 * The button starts the same vertical position as the text area.
342 CopyRect(lprButton, lprEdit);
345 * If the combobox is "simple" there is no button.
347 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
348 lprButton->left = lprButton->right = lprButton->bottom = 0;
349 else
352 * Let's assume the combobox button is the same width as the
353 * scrollbar button.
354 * size the button horizontally and cut-off the text area.
356 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
357 lprEdit->right = lprButton->left;
361 * In the case of a dropdown, there is an additional spacing between the
362 * text area and the button.
364 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
366 lprEdit->right -= COMBO_EDITBUTTONSPACE();
370 * If we have an edit control, we space it away from the borders slightly.
372 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
374 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
378 * Adjust the size of the listbox popup.
380 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
383 * Use the client rectangle to initialize the listbox rectangle
385 GetClientRect(hwnd, lprLB);
388 * Then, chop-off the top part.
390 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
392 else
395 * Make sure the dropped width is as large as the combobox itself.
397 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
399 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
402 * In the case of a dropdown, the popup listbox is offset to the right.
403 * so, we want to make sure it's flush with the right side of the
404 * combobox
406 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
407 lprLB->right -= COMBO_EDITBUTTONSPACE();
409 else
410 lprLB->right = lprLB->left + lphc->droppedWidth;
413 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
414 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
416 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
417 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
419 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
420 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
423 /***********************************************************************
424 * CBGetDroppedControlRect
426 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
428 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
429 of the combo box and the lower right corner of the listbox */
431 GetWindowRect(lphc->self->hwndSelf, lpRect);
433 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
434 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
438 /***********************************************************************
439 * COMBO_WindowPosChanging
441 static LRESULT COMBO_WindowPosChanging(
442 HWND hwnd,
443 LPHEADCOMBO lphc,
444 WINDOWPOS* posChanging)
447 * We need to override the WM_WINDOWPOSCHANGING method to handle all
448 * the non-simple comboboxes. The problem is that those controls are
449 * always the same height. We have to make sure they are not resized
450 * to another value.
452 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
453 ((posChanging->flags & SWP_NOSIZE) == 0) )
455 int newComboHeight;
457 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
458 2*COMBO_YBORDERSIZE();
461 * Resizing a combobox has another side effect, it resizes the dropped
462 * rectangle as well. However, it does it only if the new height for the
463 * combobox is different than the height it should have. In other words,
464 * if the application resizing the combobox only had the intention to resize
465 * the actual control, for example, to do the layout of a dialog that is
466 * resized, the height of the dropdown is not changed.
468 if (posChanging->cy != newComboHeight)
470 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
472 posChanging->cy = newComboHeight;
476 return 0;
479 /***********************************************************************
480 * COMBO_Create
482 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, HWND hwndParent, LONG style )
484 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
485 static const WCHAR editName[] = {'E','d','i','t',0};
487 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
488 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
490 lphc->self = wnd;
491 lphc->owner = hwndParent;
494 * The item height and dropped width are not set when the control
495 * is created.
497 lphc->droppedWidth = lphc->editHeight = 0;
500 * The first time we go through, we want to measure the ownerdraw item
502 lphc->wState |= CBF_MEASUREITEM;
504 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
506 if( lphc->owner || !(style & WS_VISIBLE) )
508 UINT lbeStyle = 0;
509 UINT lbeExStyle = 0;
512 * Initialize the dropped rect to the size of the client area of the
513 * control and then, force all the areas of the combobox to be
514 * recalculated.
516 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
518 CBCalcPlacement(wnd->hwndSelf,
519 lphc,
520 &lphc->textRect,
521 &lphc->buttonRect,
522 &lphc->droppedRect );
525 * Adjust the position of the popup listbox if it's necessary
527 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
529 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
532 * If it's a dropdown, the listbox is offset
534 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
535 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
537 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
538 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
541 /* create listbox popup */
543 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
544 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
546 if( lphc->dwStyle & CBS_SORT )
547 lbeStyle |= LBS_SORT;
548 if( lphc->dwStyle & CBS_HASSTRINGS )
549 lbeStyle |= LBS_HASSTRINGS;
550 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
551 lbeStyle |= LBS_NOINTEGRALHEIGHT;
552 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
553 lbeStyle |= LBS_DISABLENOSCROLL;
555 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
557 lbeStyle |= WS_VISIBLE;
560 * In win 95 look n feel, the listbox in the simple combobox has
561 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
563 if (TWEAK_WineLook > WIN31_LOOK)
565 lbeStyle &= ~WS_BORDER;
566 lbeExStyle |= WS_EX_CLIENTEDGE;
570 lphc->hWndLBox = CreateWindowExW(lbeExStyle,
571 clbName,
572 NULL,
573 lbeStyle,
574 lphc->droppedRect.left,
575 lphc->droppedRect.top,
576 lphc->droppedRect.right - lphc->droppedRect.left,
577 lphc->droppedRect.bottom - lphc->droppedRect.top,
578 lphc->self->hwndSelf,
579 (HMENU)ID_CB_LISTBOX,
580 lphc->self->hInstance,
581 (LPVOID)lphc );
583 if( lphc->hWndLBox )
585 BOOL bEdit = TRUE;
586 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
589 * In Win95 look, the border fo the edit control is
590 * provided by the combobox
592 if (TWEAK_WineLook == WIN31_LOOK)
593 lbeStyle |= WS_BORDER;
595 if( lphc->wState & CBF_EDIT )
597 if( lphc->dwStyle & CBS_OEMCONVERT )
598 lbeStyle |= ES_OEMCONVERT;
599 if( lphc->dwStyle & CBS_AUTOHSCROLL )
600 lbeStyle |= ES_AUTOHSCROLL;
601 if( lphc->dwStyle & CBS_LOWERCASE )
602 lbeStyle |= ES_LOWERCASE;
603 else if( lphc->dwStyle & CBS_UPPERCASE )
604 lbeStyle |= ES_UPPERCASE;
606 lphc->hWndEdit = CreateWindowExW(0,
607 editName,
608 NULL,
609 lbeStyle,
610 lphc->textRect.left, lphc->textRect.top,
611 lphc->textRect.right - lphc->textRect.left,
612 lphc->textRect.bottom - lphc->textRect.top,
613 lphc->self->hwndSelf,
614 (HMENU)ID_CB_EDIT,
615 lphc->self->hInstance,
616 NULL );
618 if( !lphc->hWndEdit )
619 bEdit = FALSE;
622 if( bEdit )
624 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
626 /* Now do the trick with parent */
627 SetParent(lphc->hWndLBox, HWND_DESKTOP);
629 * If the combo is a dropdown, we must resize the control
630 * to fit only the text area and button. To do this,
631 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
632 * will take care of setting the height for us.
634 CBForceDummyResize(lphc);
637 TRACE("init done\n");
638 return wnd->hwndSelf;
640 ERR("edit control failure.\n");
641 } else ERR("listbox failure.\n");
642 } else ERR("no owner for visible combo.\n");
644 /* CreateWindow() will send WM_NCDESTROY to cleanup */
646 return -1;
649 /***********************************************************************
650 * CBPaintButton
652 * Paint combo button (normal, pressed, and disabled states).
654 static void CBPaintButton(
655 LPHEADCOMBO lphc,
656 HDC hdc,
657 RECT rectButton)
659 if( lphc->wState & CBF_NOREDRAW )
660 return;
662 if (TWEAK_WineLook == WIN31_LOOK)
664 UINT x, y;
665 BOOL bBool;
666 HDC hMemDC;
667 HBRUSH hPrevBrush;
668 COLORREF oldTextColor, oldBkColor;
671 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
674 * Draw the button background
676 PatBlt( hdc,
677 rectButton.left,
678 rectButton.top,
679 rectButton.right-rectButton.left,
680 rectButton.bottom-rectButton.top,
681 PATCOPY );
683 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
685 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
687 else
689 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
693 * Remove the edge of the button from the rectangle
694 * and calculate the position of the bitmap.
696 InflateRect( &rectButton, -2, -2);
698 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
699 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
702 hMemDC = CreateCompatibleDC( hdc );
703 SelectObject( hMemDC, hComboBmp );
704 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
705 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
706 RGB(0,0,0) );
707 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
708 SetBkColor( hdc, oldBkColor );
709 SetTextColor( hdc, oldTextColor );
710 DeleteDC( hMemDC );
711 SelectObject( hdc, hPrevBrush );
713 else
715 UINT buttonState = DFCS_SCROLLCOMBOBOX;
717 if (lphc->wState & CBF_BUTTONDOWN)
719 buttonState |= DFCS_PUSHED;
722 if (CB_DISABLED(lphc))
724 buttonState |= DFCS_INACTIVE;
727 DrawFrameControl(hdc,
728 &rectButton,
729 DFC_SCROLL,
730 buttonState);
734 /***********************************************************************
735 * CBPaintText
737 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
739 static void CBPaintText(
740 LPHEADCOMBO lphc,
741 HDC hdc,
742 RECT rectEdit)
744 INT id, size = 0;
745 LPWSTR pText = NULL;
747 if( lphc->wState & CBF_NOREDRAW ) return;
749 /* follow Windows combobox that sends a bunch of text
750 * inquiries to its listbox while processing WM_PAINT. */
752 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
754 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
755 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
757 SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
758 pText[size] = '\0'; /* just in case */
759 } else return;
762 if( lphc->wState & CBF_EDIT )
764 static const WCHAR empty_stringW[] = { 0 };
765 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
766 if( lphc->wState & CBF_FOCUSED )
767 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
769 else /* paint text field ourselves */
771 UINT itemState;
772 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
775 * Give ourselves some space.
777 InflateRect( &rectEdit, -1, -1 );
779 if ( (lphc->wState & CBF_FOCUSED) &&
780 !(lphc->wState & CBF_DROPPED) )
782 /* highlight */
784 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
785 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
786 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
787 itemState = ODS_SELECTED | ODS_FOCUS;
789 else
790 itemState = 0;
792 if( CB_OWNERDRAWN(lphc) )
794 DRAWITEMSTRUCT dis;
795 HRGN clipRegion;
798 * Save the current clip region.
799 * To retrieve the clip region, we need to create one "dummy"
800 * clip region.
802 clipRegion = CreateRectRgnIndirect(&rectEdit);
804 if (GetClipRgn(hdc, clipRegion)!=1)
806 DeleteObject(clipRegion);
807 clipRegion=(HRGN)NULL;
810 if ( lphc->self->dwStyle & WS_DISABLED )
811 itemState |= ODS_DISABLED;
813 dis.CtlType = ODT_COMBOBOX;
814 dis.CtlID = lphc->self->wIDmenu;
815 dis.hwndItem = lphc->self->hwndSelf;
816 dis.itemAction = ODA_DRAWENTIRE;
817 dis.itemID = id;
818 dis.itemState = itemState;
819 dis.hDC = hdc;
820 dis.rcItem = rectEdit;
821 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
822 (WPARAM)id, 0 );
825 * Clip the DC and have the parent draw the item.
827 IntersectClipRect(hdc,
828 rectEdit.left, rectEdit.top,
829 rectEdit.right, rectEdit.bottom);
831 SendMessageW(lphc->owner, WM_DRAWITEM,
832 lphc->self->wIDmenu, (LPARAM)&dis );
835 * Reset the clipping region.
837 SelectClipRgn(hdc, clipRegion);
839 else
841 static const WCHAR empty_stringW[] = { 0 };
842 ExtTextOutW( hdc,
843 rectEdit.left + 1,
844 rectEdit.top + 1,
845 ETO_OPAQUE | ETO_CLIPPED,
846 &rectEdit,
847 pText ? pText : empty_stringW , size, NULL );
849 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
850 DrawFocusRect( hdc, &rectEdit );
853 if( hPrevFont )
854 SelectObject(hdc, hPrevFont );
856 if (pText)
857 HeapFree( GetProcessHeap(), 0, pText );
860 /***********************************************************************
861 * CBPaintBorder
863 static void CBPaintBorder(
864 HWND hwnd,
865 LPHEADCOMBO lphc,
866 HDC hdc)
868 RECT clientRect;
870 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
872 GetClientRect(hwnd, &clientRect);
874 else
876 CopyRect(&clientRect, &lphc->textRect);
878 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
879 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
882 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
885 /***********************************************************************
886 * COMBO_PrepareColors
888 * This method will sent the appropriate WM_CTLCOLOR message to
889 * prepare and setup the colors for the combo's DC.
891 * It also returns the brush to use for the background.
893 static HBRUSH COMBO_PrepareColors(
894 LPHEADCOMBO lphc,
895 HDC hDC)
897 HBRUSH hBkgBrush;
900 * Get the background brush for this control.
902 if (CB_DISABLED(lphc))
904 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
905 hDC, lphc->self->hwndSelf );
908 * We have to change the text color since WM_CTLCOLORSTATIC will
909 * set it to the "enabled" color. This is the same behavior as the
910 * edit control
912 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
914 else
916 if (lphc->wState & CBF_EDIT)
918 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
919 hDC, lphc->self->hwndSelf );
921 else
923 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX,
924 hDC, lphc->self->hwndSelf );
929 * Catch errors.
931 if( !hBkgBrush )
932 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
934 return hBkgBrush;
937 /***********************************************************************
938 * COMBO_EraseBackground
940 static LRESULT COMBO_EraseBackground(
941 HWND hwnd,
942 LPHEADCOMBO lphc,
943 HDC hParamDC)
945 HBRUSH hBkgBrush;
946 RECT clientRect;
947 HDC hDC;
949 hDC = (hParamDC) ? hParamDC
950 : GetDC(hwnd);
953 * Calculate the area that we want to erase.
955 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
957 GetClientRect(hwnd, &clientRect);
959 else
961 CopyRect(&clientRect, &lphc->textRect);
963 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
967 * Retrieve the background brush
969 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
971 FillRect(hDC, &clientRect, hBkgBrush);
973 if (!hParamDC)
974 ReleaseDC(hwnd, hDC);
976 return TRUE;
979 /***********************************************************************
980 * COMBO_Paint
982 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
984 PAINTSTRUCT ps;
985 HDC hDC;
987 hDC = (hParamDC) ? hParamDC
988 : BeginPaint( lphc->self->hwndSelf, &ps);
991 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
993 HBRUSH hPrevBrush, hBkgBrush;
996 * Retrieve the background brush and select it in the
997 * DC.
999 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
1001 hPrevBrush = SelectObject( hDC, hBkgBrush );
1004 * In non 3.1 look, there is a sunken border on the combobox
1006 if (TWEAK_WineLook != WIN31_LOOK)
1008 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
1011 if( !IsRectEmpty(&lphc->buttonRect) )
1013 CBPaintButton(lphc, hDC, lphc->buttonRect);
1016 if( !(lphc->wState & CBF_EDIT) )
1019 * The text area has a border only in Win 3.1 look.
1021 if (TWEAK_WineLook == WIN31_LOOK)
1023 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1025 Rectangle( hDC,
1026 lphc->textRect.left, lphc->textRect.top,
1027 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
1029 SelectObject( hDC, hPrevPen );
1032 CBPaintText( lphc, hDC, lphc->textRect);
1035 if( hPrevBrush )
1036 SelectObject( hDC, hPrevBrush );
1039 if( !hParamDC )
1040 EndPaint(lphc->self->hwndSelf, &ps);
1042 return 0;
1045 /***********************************************************************
1046 * CBUpdateLBox
1048 * Select listbox entry according to the contents of the edit control.
1050 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1052 INT length, idx;
1053 LPWSTR pText = NULL;
1055 idx = LB_ERR;
1056 length = CB_GETEDITTEXTLENGTH( lphc );
1058 if( length > 0 )
1059 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1061 TRACE("\t edit text length %i\n", length );
1063 if( pText )
1065 if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
1066 else pText[0] = '\0';
1067 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1068 (WPARAM)(-1), (LPARAM)pText );
1069 HeapFree( GetProcessHeap(), 0, pText );
1072 if (idx >= 0)
1074 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
1076 /* probably superfluous but Windows sends this too */
1077 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1078 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1080 return idx;
1083 /***********************************************************************
1084 * CBUpdateEdit
1086 * Copy a listbox entry to the edit control.
1088 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1090 INT length;
1091 LPWSTR pText = NULL;
1092 static const WCHAR empty_stringW[] = { 0 };
1094 TRACE("\t %i\n", index );
1096 if( index >= 0 ) /* got an entry */
1098 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1099 if( length )
1101 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1103 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1104 (WPARAM)index, (LPARAM)pText );
1109 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1110 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1111 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1113 if( lphc->wState & CBF_FOCUSED )
1114 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1116 if( pText )
1117 HeapFree( GetProcessHeap(), 0, pText );
1120 /***********************************************************************
1121 * CBDropDown
1123 * Show listbox popup.
1125 static void CBDropDown( LPHEADCOMBO lphc )
1127 RECT rect,r;
1128 int nItems = 0;
1129 int nDroppedHeight;
1131 TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1133 CB_NOTIFY( lphc, CBN_DROPDOWN );
1135 /* set selection */
1137 lphc->wState |= CBF_DROPPED;
1138 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1140 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1142 if( !(lphc->wState & CBF_CAPTURE) )
1143 CBUpdateEdit( lphc, lphc->droppedIndex );
1145 else
1147 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1149 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1150 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1151 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1154 /* now set popup position */
1155 GetWindowRect( lphc->self->hwndSelf, &rect );
1158 * If it's a dropdown, the listbox is offset
1160 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1161 rect.left += COMBO_EDITBUTTONSPACE();
1163 /* if the dropped height is greater than the total height of the dropped
1164 items list, then force the drop down list height to be the total height
1165 of the items in the dropped list */
1167 /* And Remove any extra space (Best Fit) */
1168 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1169 /* if listbox length has been set directly by its handle */
1170 GetWindowRect(lphc->hWndLBox, &r);
1171 if (nDroppedHeight < r.bottom - r.top)
1172 nDroppedHeight = r.bottom - r.top;
1173 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1175 if (nItems > 0)
1177 int nHeight;
1179 nHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1180 nHeight *= nItems;
1182 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1183 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1186 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1187 if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1188 rect.bottom = rect.top - nDroppedHeight;
1190 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1191 lphc->droppedRect.right - lphc->droppedRect.left,
1192 nDroppedHeight,
1193 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1196 if( !(lphc->wState & CBF_NOREDRAW) )
1197 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1198 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1200 EnableWindow( lphc->hWndLBox, TRUE );
1201 if (GetCapture() != lphc->self->hwndSelf)
1202 SetCapture(lphc->hWndLBox);
1205 /***********************************************************************
1206 * CBRollUp
1208 * Hide listbox popup.
1210 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1212 HWND hWnd = lphc->self->hwndSelf;
1214 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1216 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1219 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1221 if( lphc->wState & CBF_DROPPED )
1223 RECT rect;
1225 lphc->wState &= ~CBF_DROPPED;
1226 ShowWindow( lphc->hWndLBox, SW_HIDE );
1227 if(GetCapture() == lphc->hWndLBox)
1229 ReleaseCapture();
1232 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1234 rect = lphc->buttonRect;
1236 else
1238 if( bButton )
1240 UnionRect( &rect,
1241 &lphc->buttonRect,
1242 &lphc->textRect);
1244 else
1245 rect = lphc->textRect;
1247 bButton = TRUE;
1250 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1251 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1252 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1253 CB_NOTIFY( lphc, CBN_CLOSEUP );
1258 /***********************************************************************
1259 * COMBO_FlipListbox
1261 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1263 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1265 if( lphc->wState & CBF_DROPPED )
1267 CBRollUp( lphc, ok, bRedrawButton );
1268 return FALSE;
1271 CBDropDown( lphc );
1272 return TRUE;
1275 /***********************************************************************
1276 * CBRepaintButton
1278 static void CBRepaintButton( LPHEADCOMBO lphc )
1280 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1281 UpdateWindow(CB_HWND(lphc));
1284 /***********************************************************************
1285 * COMBO_SetFocus
1287 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1289 if( !(lphc->wState & CBF_FOCUSED) )
1291 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1292 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1294 lphc->wState |= CBF_FOCUSED;
1296 if( !(lphc->wState & CBF_EDIT) )
1297 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1299 CB_NOTIFY( lphc, CBN_SETFOCUS );
1303 /***********************************************************************
1304 * COMBO_KillFocus
1306 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1308 HWND hWnd = lphc->self->hwndSelf;
1310 if( lphc->wState & CBF_FOCUSED )
1312 CBRollUp( lphc, FALSE, TRUE );
1313 if( IsWindow( hWnd ) )
1315 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1316 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1318 lphc->wState &= ~CBF_FOCUSED;
1320 /* redraw text */
1321 if( !(lphc->wState & CBF_EDIT) )
1322 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1324 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1329 /***********************************************************************
1330 * COMBO_Command
1332 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1334 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1336 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1338 switch( HIWORD(wParam) >> 8 )
1340 case (EN_SETFOCUS >> 8):
1342 TRACE("[%04x]: edit [%04x] got focus\n",
1343 CB_HWND(lphc), lphc->hWndEdit );
1345 COMBO_SetFocus( lphc );
1346 break;
1348 case (EN_KILLFOCUS >> 8):
1350 TRACE("[%04x]: edit [%04x] lost focus\n",
1351 CB_HWND(lphc), lphc->hWndEdit );
1353 /* NOTE: it seems that Windows' edit control sends an
1354 * undocumented message WM_USER + 0x1B instead of this
1355 * notification (only when it happens to be a part of
1356 * the combo). ?? - AK.
1359 COMBO_KillFocus( lphc );
1360 break;
1363 case (EN_CHANGE >> 8):
1365 * In some circumstances (when the selection of the combobox
1366 * is changed for example) we don't wans the EN_CHANGE notification
1367 * to be forwarded to the parent of the combobox. This code
1368 * checks a flag that is set in these occasions and ignores the
1369 * notification.
1371 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1372 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1374 if (lphc->wState & CBF_NOLBSELECT)
1376 lphc->wState &= ~CBF_NOLBSELECT;
1378 else
1380 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1382 break;
1384 case (EN_UPDATE >> 8):
1385 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1386 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1387 break;
1389 case (EN_ERRSPACE >> 8):
1390 CB_NOTIFY( lphc, CBN_ERRSPACE );
1393 else if( lphc->hWndLBox == hWnd )
1395 switch( HIWORD(wParam) )
1397 case LBN_ERRSPACE:
1398 CB_NOTIFY( lphc, CBN_ERRSPACE );
1399 break;
1401 case LBN_DBLCLK:
1402 CB_NOTIFY( lphc, CBN_DBLCLK );
1403 break;
1405 case LBN_SELCHANGE:
1406 case LBN_SELCANCEL:
1408 TRACE("[%04x]: lbox selection change [%04x]\n",
1409 CB_HWND(lphc), lphc->wState );
1411 if( HIWORD(wParam) == LBN_SELCHANGE)
1413 if( lphc->wState & CBF_EDIT )
1415 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1416 lphc->wState |= CBF_NOLBSELECT;
1417 CBUpdateEdit( lphc, index );
1418 /* select text in edit, as Windows does */
1419 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1421 else
1422 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1425 /* do not roll up if selection is being tracked
1426 * by arrowkeys in the dropdown listbox */
1428 if( (lphc->dwStyle & CBS_SIMPLE) ||
1429 ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1431 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1433 else lphc->wState &= ~CBF_NOROLLUP;
1435 CB_NOTIFY( lphc, CBN_SELCHANGE );
1437 /* fall through */
1439 case LBN_SETFOCUS:
1440 case LBN_KILLFOCUS:
1441 /* nothing to do here since ComboLBox always resets the focus to its
1442 * combo/edit counterpart */
1443 break;
1446 return 0;
1449 /***********************************************************************
1450 * COMBO_ItemOp
1452 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1454 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1456 HWND hWnd = lphc->self->hwndSelf;
1458 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1460 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1462 /* two first items are the same in all 4 structs */
1463 lpIS->CtlType = ODT_COMBOBOX;
1464 lpIS->CtlID = lphc->self->wIDmenu;
1466 switch( msg ) /* patch window handle */
1468 case WM_DELETEITEM:
1469 lpIS->hwndItem = hWnd;
1470 #undef lpIS
1471 break;
1472 case WM_DRAWITEM:
1473 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1474 lpIS->hwndItem = hWnd;
1475 #undef lpIS
1476 break;
1477 case WM_COMPAREITEM:
1478 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1479 lpIS->hwndItem = hWnd;
1480 #undef lpIS
1481 break;
1484 return SendMessageW(lphc->owner, msg, lphc->self->wIDmenu, lParam);
1487 /***********************************************************************
1488 * COMBO_GetText
1490 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, INT N, LPARAM lParam, BOOL unicode)
1492 if( lphc->wState & CBF_EDIT )
1493 return unicode ? SendMessageW(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam) :
1494 SendMessageA(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam);
1496 /* get it from the listbox */
1498 if( lphc->hWndLBox )
1500 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1501 if( idx != LB_ERR )
1503 INT n = 0;
1504 INT length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN,
1505 (WPARAM)idx, 0 );
1507 if(unicode)
1509 LPWSTR lpBuffer, lpText = (LPWSTR)lParam;
1511 /* 'length' is without the terminating character */
1512 if(length >= N)
1513 lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1514 else
1515 lpBuffer = lpText;
1517 if(lpBuffer)
1519 n = SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1521 /* truncate if buffer is too short */
1522 if(length >= N)
1524 if(N && lpText)
1526 if(n != LB_ERR)
1527 strncpyW(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1528 lpText[N - 1] = '\0';
1531 HeapFree( GetProcessHeap(), 0, lpBuffer );
1534 else
1536 LPSTR lpBuffer, lpText = (LPSTR)lParam;
1538 /* 'length' is without the terminating character */
1539 if(length >= N)
1540 lpBuffer = HeapAlloc(GetProcessHeap(), 0, length + 1);
1541 else
1542 lpBuffer = lpText;
1544 if(lpBuffer)
1546 n = SendMessageA(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1548 /* truncate if buffer is too short */
1549 if(length >= N)
1551 if(N && lpText)
1553 if(n != LB_ERR)
1554 strncpy(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1555 lpText[N - 1] = '\0';
1558 HeapFree( GetProcessHeap(), 0, lpBuffer );
1561 return (LRESULT)n;
1564 return 0;
1568 /***********************************************************************
1569 * CBResetPos
1571 * This function sets window positions according to the updated
1572 * component placement struct.
1574 static void CBResetPos(
1575 LPHEADCOMBO lphc,
1576 LPRECT rectEdit,
1577 LPRECT rectLB,
1578 BOOL bRedraw)
1580 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1582 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1583 * sizing messages */
1585 if( lphc->wState & CBF_EDIT )
1586 SetWindowPos( lphc->hWndEdit, 0,
1587 rectEdit->left, rectEdit->top,
1588 rectEdit->right - rectEdit->left,
1589 rectEdit->bottom - rectEdit->top,
1590 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1592 SetWindowPos( lphc->hWndLBox, 0,
1593 rectLB->left, rectLB->top,
1594 rectLB->right - rectLB->left,
1595 rectLB->bottom - rectLB->top,
1596 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1598 if( bDrop )
1600 if( lphc->wState & CBF_DROPPED )
1602 lphc->wState &= ~CBF_DROPPED;
1603 ShowWindow( lphc->hWndLBox, SW_HIDE );
1606 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1607 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1608 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1613 /***********************************************************************
1614 * COMBO_Size
1616 static void COMBO_Size( LPHEADCOMBO lphc )
1618 CBCalcPlacement(lphc->self->hwndSelf,
1619 lphc,
1620 &lphc->textRect,
1621 &lphc->buttonRect,
1622 &lphc->droppedRect);
1624 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1628 /***********************************************************************
1629 * COMBO_Font
1631 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1634 * Set the font
1636 lphc->hFont = hFont;
1639 * Propagate to owned windows.
1641 if( lphc->wState & CBF_EDIT )
1642 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1643 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1646 * Redo the layout of the control.
1648 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1650 CBCalcPlacement(lphc->self->hwndSelf,
1651 lphc,
1652 &lphc->textRect,
1653 &lphc->buttonRect,
1654 &lphc->droppedRect);
1656 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1658 else
1660 CBForceDummyResize(lphc);
1665 /***********************************************************************
1666 * COMBO_SetItemHeight
1668 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1670 LRESULT lRet = CB_ERR;
1672 if( index == -1 ) /* set text field height */
1674 if( height < 32768 )
1676 lphc->editHeight = height;
1679 * Redo the layout of the control.
1681 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1683 CBCalcPlacement(lphc->self->hwndSelf,
1684 lphc,
1685 &lphc->textRect,
1686 &lphc->buttonRect,
1687 &lphc->droppedRect);
1689 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1691 else
1693 CBForceDummyResize(lphc);
1696 lRet = height;
1699 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1700 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1701 (WPARAM)index, (LPARAM)height );
1702 return lRet;
1705 /***********************************************************************
1706 * COMBO_SelectString
1708 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1710 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1711 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1712 if( index >= 0 )
1714 if( lphc->wState & CBF_EDIT )
1715 CBUpdateEdit( lphc, index );
1716 else
1718 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1721 return (LRESULT)index;
1724 /***********************************************************************
1725 * COMBO_LButtonDown
1727 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1729 POINT pt;
1730 BOOL bButton;
1731 HWND hWnd = lphc->self->hwndSelf;
1733 pt.x = LOWORD(lParam);
1734 pt.y = HIWORD(lParam);
1735 bButton = PtInRect(&lphc->buttonRect, pt);
1737 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1738 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1740 lphc->wState |= CBF_BUTTONDOWN;
1741 if( lphc->wState & CBF_DROPPED )
1743 /* got a click to cancel selection */
1745 lphc->wState &= ~CBF_BUTTONDOWN;
1746 CBRollUp( lphc, TRUE, FALSE );
1747 if( !IsWindow( hWnd ) ) return;
1749 if( lphc->wState & CBF_CAPTURE )
1751 lphc->wState &= ~CBF_CAPTURE;
1752 ReleaseCapture();
1755 else
1757 /* drop down the listbox and start tracking */
1759 lphc->wState |= CBF_CAPTURE;
1760 SetCapture( hWnd );
1761 CBDropDown( lphc );
1763 if( bButton ) CBRepaintButton( lphc );
1767 /***********************************************************************
1768 * COMBO_LButtonUp
1770 * Release capture and stop tracking if needed.
1772 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1774 if( lphc->wState & CBF_CAPTURE )
1776 lphc->wState &= ~CBF_CAPTURE;
1777 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1779 INT index = CBUpdateLBox( lphc, TRUE );
1780 lphc->wState |= CBF_NOLBSELECT;
1781 CBUpdateEdit( lphc, index );
1782 lphc->wState &= ~CBF_NOLBSELECT;
1784 ReleaseCapture();
1785 SetCapture(lphc->hWndLBox);
1789 if( lphc->wState & CBF_BUTTONDOWN )
1791 lphc->wState &= ~CBF_BUTTONDOWN;
1792 CBRepaintButton( lphc );
1796 /***********************************************************************
1797 * COMBO_MouseMove
1799 * Two things to do - track combo button and release capture when
1800 * pointer goes into the listbox.
1802 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1804 POINT pt;
1805 RECT lbRect;
1807 pt.x = LOWORD(lParam);
1808 pt.y = HIWORD(lParam);
1810 if( lphc->wState & CBF_BUTTONDOWN )
1812 BOOL bButton;
1814 bButton = PtInRect(&lphc->buttonRect, pt);
1816 if( !bButton )
1818 lphc->wState &= ~CBF_BUTTONDOWN;
1819 CBRepaintButton( lphc );
1823 GetClientRect( lphc->hWndLBox, &lbRect );
1824 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1825 if( PtInRect(&lbRect, pt) )
1827 lphc->wState &= ~CBF_CAPTURE;
1828 ReleaseCapture();
1829 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1831 /* hand over pointer tracking */
1832 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1837 /***********************************************************************
1838 * ComboWndProc_locked
1840 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1842 static LRESULT ComboWndProc_locked( WND* pWnd, UINT message,
1843 WPARAM wParam, LPARAM lParam, BOOL unicode )
1845 if( pWnd ) {
1846 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1847 HWND hwnd = pWnd->hwndSelf;
1849 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1850 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1852 if( lphc || message == WM_NCCREATE )
1853 switch(message)
1856 /* System messages */
1858 case WM_NCCREATE:
1860 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1861 ((LPCREATESTRUCTA)lParam)->style;
1862 return COMBO_NCCreate(pWnd, style);
1864 case WM_NCDESTROY:
1865 COMBO_NCDestroy(lphc);
1866 break;/* -> DefWindowProc */
1868 case WM_CREATE:
1870 HWND hwndParent;
1871 LONG style;
1872 if(unicode)
1874 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1875 style = ((LPCREATESTRUCTW)lParam)->style;
1877 else
1879 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1880 style = ((LPCREATESTRUCTA)lParam)->style;
1882 return COMBO_Create(lphc, pWnd, hwndParent, style);
1885 case WM_PRINTCLIENT:
1886 if (lParam & PRF_ERASEBKGND)
1887 COMBO_EraseBackground(hwnd, lphc, wParam);
1889 /* Fallthrough */
1890 case WM_PAINT:
1891 /* wParam may contain a valid HDC! */
1892 return COMBO_Paint(lphc, wParam);
1893 case WM_ERASEBKGND:
1894 return COMBO_EraseBackground(hwnd, lphc, wParam);
1895 case WM_GETDLGCODE:
1897 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1898 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1900 int vk = (int)((LPMSG)lParam)->wParam;
1902 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1903 result |= DLGC_WANTMESSAGE;
1905 return result;
1907 case WM_WINDOWPOSCHANGING:
1908 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1909 case WM_WINDOWPOSCHANGED:
1910 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1911 * In that case, the Combobox itself will not be resized, so we won't
1912 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1913 * do it here.
1915 /* fall through */
1916 case WM_SIZE:
1917 if( lphc->hWndLBox &&
1918 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1919 return TRUE;
1920 case WM_SETFONT:
1921 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1922 return TRUE;
1923 case WM_GETFONT:
1924 return (LRESULT)lphc->hFont;
1925 case WM_SETFOCUS:
1926 if( lphc->wState & CBF_EDIT )
1927 SetFocus( lphc->hWndEdit );
1928 else
1929 COMBO_SetFocus( lphc );
1930 return TRUE;
1931 case WM_KILLFOCUS:
1932 #define hwndFocus ((HWND16)wParam)
1933 if( !hwndFocus ||
1934 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1935 COMBO_KillFocus( lphc );
1936 #undef hwndFocus
1937 return TRUE;
1938 case WM_COMMAND:
1939 return COMBO_Command( lphc, wParam, (HWND)lParam );
1940 case WM_GETTEXT:
1941 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1942 case WM_SETTEXT:
1943 case WM_GETTEXTLENGTH:
1944 case WM_CLEAR:
1945 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1947 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1948 if (j == -1) return 0;
1949 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1951 else if( lphc->wState & CBF_EDIT )
1953 LRESULT ret;
1954 lphc->wState |= CBF_NOEDITNOTIFY;
1955 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1956 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1957 lphc->wState &= ~CBF_NOEDITNOTIFY;
1958 return ret;
1960 else return CB_ERR;
1961 case WM_CUT:
1962 case WM_PASTE:
1963 case WM_COPY:
1964 if( lphc->wState & CBF_EDIT )
1966 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1967 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1969 else return CB_ERR;
1971 case WM_DRAWITEM:
1972 case WM_DELETEITEM:
1973 case WM_COMPAREITEM:
1974 case WM_MEASUREITEM:
1975 return COMBO_ItemOp(lphc, message, lParam);
1976 case WM_ENABLE:
1977 if( lphc->wState & CBF_EDIT )
1978 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1979 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1981 /* Force the control to repaint when the enabled state changes. */
1982 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1983 return TRUE;
1984 case WM_SETREDRAW:
1985 if( wParam )
1986 lphc->wState &= ~CBF_NOREDRAW;
1987 else
1988 lphc->wState |= CBF_NOREDRAW;
1990 if( lphc->wState & CBF_EDIT )
1991 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1992 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1993 return 0;
1994 case WM_SYSKEYDOWN:
1995 if( KEYDATA_ALT & HIWORD(lParam) )
1996 if( wParam == VK_UP || wParam == VK_DOWN )
1997 COMBO_FlipListbox( lphc, FALSE, FALSE );
1998 return 0;
2000 case WM_CHAR:
2001 case WM_KEYDOWN:
2003 HWND hwndTarget;
2005 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2006 (lphc->wState & CBF_DROPPED))
2008 CBRollUp( lphc, (CHAR)wParam == VK_RETURN, FALSE );
2009 return TRUE;
2012 if( lphc->wState & CBF_EDIT )
2013 hwndTarget = lphc->hWndEdit;
2014 else
2015 hwndTarget = lphc->hWndLBox;
2017 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2018 SendMessageA(hwndTarget, message, wParam, lParam);
2020 case WM_LBUTTONDOWN:
2021 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
2022 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2023 return TRUE;
2024 case WM_LBUTTONUP:
2025 COMBO_LButtonUp( lphc );
2026 return TRUE;
2027 case WM_MOUSEMOVE:
2028 if( lphc->wState & CBF_CAPTURE )
2029 COMBO_MouseMove( lphc, wParam, lParam );
2030 return TRUE;
2032 case WM_MOUSEWHEEL:
2033 if (wParam & (MK_SHIFT | MK_CONTROL))
2034 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2035 DefWindowProcA(hwnd, message, wParam, lParam);
2036 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2037 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2038 return TRUE;
2040 /* Combo messages */
2042 case CB_ADDSTRING16:
2043 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2044 /* fall through */
2045 case CB_ADDSTRING:
2046 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2047 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2048 case CB_INSERTSTRING16:
2049 wParam = (INT)(INT16)wParam;
2050 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2051 /* fall through */
2052 case CB_INSERTSTRING:
2053 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2054 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2055 case CB_DELETESTRING16:
2056 case CB_DELETESTRING:
2057 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2058 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2059 case CB_SELECTSTRING16:
2060 wParam = (INT)(INT16)wParam;
2061 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2062 /* fall through */
2063 case CB_SELECTSTRING:
2064 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2065 case CB_FINDSTRING16:
2066 wParam = (INT)(INT16)wParam;
2067 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2068 /* fall through */
2069 case CB_FINDSTRING:
2070 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2071 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2072 case CB_FINDSTRINGEXACT16:
2073 wParam = (INT)(INT16)wParam;
2074 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2075 /* fall through */
2076 case CB_FINDSTRINGEXACT:
2077 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2078 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2079 case CB_SETITEMHEIGHT16:
2080 wParam = (INT)(INT16)wParam; /* signed integer */
2081 /* fall through */
2082 case CB_SETITEMHEIGHT:
2083 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2084 case CB_GETITEMHEIGHT16:
2085 wParam = (INT)(INT16)wParam;
2086 /* fall through */
2087 case CB_GETITEMHEIGHT:
2088 if( (INT)wParam >= 0 ) /* listbox item */
2089 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2090 return CBGetTextAreaHeight(hwnd, lphc);
2091 case CB_RESETCONTENT16:
2092 case CB_RESETCONTENT:
2093 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2094 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2096 static const WCHAR empty_stringW[] = { 0 };
2097 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2099 else
2100 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
2101 return TRUE;
2102 case CB_INITSTORAGE:
2103 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2104 case CB_GETHORIZONTALEXTENT:
2105 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2106 case CB_SETHORIZONTALEXTENT:
2107 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2108 case CB_GETTOPINDEX:
2109 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2110 case CB_GETLOCALE:
2111 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2112 case CB_SETLOCALE:
2113 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2114 case CB_GETDROPPEDWIDTH:
2115 if( lphc->droppedWidth )
2116 return lphc->droppedWidth;
2117 return lphc->droppedRect.right - lphc->droppedRect.left;
2118 case CB_SETDROPPEDWIDTH:
2119 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2120 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2121 return CB_ERR;
2122 case CB_GETDROPPEDCONTROLRECT16:
2123 lParam = (LPARAM)MapSL(lParam);
2124 if( lParam )
2126 RECT r;
2127 CBGetDroppedControlRect( lphc, &r );
2128 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2130 return CB_OKAY;
2131 case CB_GETDROPPEDCONTROLRECT:
2132 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2133 return CB_OKAY;
2134 case CB_GETDROPPEDSTATE16:
2135 case CB_GETDROPPEDSTATE:
2136 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2137 case CB_DIR16:
2138 lParam = (LPARAM)MapSL(lParam);
2139 /* fall through */
2140 case CB_DIR:
2142 LRESULT res;
2143 LPSTR dir; /* temporary, until listbox is unicoded */
2144 if(unicode)
2146 LPWSTR textW = (LPWSTR)lParam;
2147 INT countA = WideCharToMultiByte(CP_ACP, 0, textW, -1, NULL, 0, NULL, NULL);
2148 dir = HeapAlloc(GetProcessHeap(), 0, countA);
2149 WideCharToMultiByte(CP_ACP, 0, textW, -1, dir, countA, NULL, NULL);
2151 else
2152 dir = (LPSTR)lParam;
2153 res = COMBO_Directory(lphc, (UINT)wParam, dir, (message == CB_DIR));
2154 if(unicode)
2155 HeapFree(GetProcessHeap(), 0, dir);
2156 return res;
2158 case CB_SHOWDROPDOWN16:
2159 case CB_SHOWDROPDOWN:
2160 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2162 if( wParam )
2164 if( !(lphc->wState & CBF_DROPPED) )
2165 CBDropDown( lphc );
2167 else
2168 if( lphc->wState & CBF_DROPPED )
2169 CBRollUp( lphc, FALSE, TRUE );
2171 return TRUE;
2172 case CB_GETCOUNT16:
2173 case CB_GETCOUNT:
2174 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2175 case CB_GETCURSEL16:
2176 case CB_GETCURSEL:
2177 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2178 case CB_SETCURSEL16:
2179 wParam = (INT)(INT16)wParam;
2180 /* fall through */
2181 case CB_SETCURSEL:
2182 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2183 if( lParam >= 0 )
2184 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2185 if( lphc->wState & CBF_SELCHANGE )
2187 /* no LBN_SELCHANGE in this case, update manually */
2188 if( lphc->wState & CBF_EDIT )
2189 CBUpdateEdit( lphc, (INT)wParam );
2190 else
2191 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2192 lphc->wState &= ~CBF_SELCHANGE;
2194 return lParam;
2195 case CB_GETLBTEXT16:
2196 wParam = (INT)(INT16)wParam;
2197 lParam = (LPARAM)MapSL(lParam);
2198 /* fall through */
2199 case CB_GETLBTEXT:
2200 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2201 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2202 case CB_GETLBTEXTLEN16:
2203 wParam = (INT)(INT16)wParam;
2204 /* fall through */
2205 case CB_GETLBTEXTLEN:
2206 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2207 case CB_GETITEMDATA16:
2208 wParam = (INT)(INT16)wParam;
2209 /* fall through */
2210 case CB_GETITEMDATA:
2211 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2212 case CB_SETITEMDATA16:
2213 wParam = (INT)(INT16)wParam;
2214 /* fall through */
2215 case CB_SETITEMDATA:
2216 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2217 case CB_GETEDITSEL16:
2218 wParam = lParam = 0; /* just in case */
2219 /* fall through */
2220 case CB_GETEDITSEL:
2221 /* Edit checks passed parameters itself */
2222 if( lphc->wState & CBF_EDIT )
2223 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2224 return CB_ERR;
2225 case CB_SETEDITSEL16:
2226 case CB_SETEDITSEL:
2227 if( lphc->wState & CBF_EDIT )
2228 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2229 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2230 return CB_ERR;
2231 case CB_SETEXTENDEDUI16:
2232 case CB_SETEXTENDEDUI:
2233 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2234 return CB_ERR;
2235 if( wParam )
2236 lphc->wState |= CBF_EUI;
2237 else lphc->wState &= ~CBF_EUI;
2238 return CB_OKAY;
2239 case CB_GETEXTENDEDUI16:
2240 case CB_GETEXTENDEDUI:
2241 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2243 default:
2244 if (message >= WM_USER)
2245 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2246 message - WM_USER, wParam, lParam );
2247 break;
2249 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2250 DefWindowProcA(hwnd, message, wParam, lParam);
2252 return CB_ERR;
2255 /***********************************************************************
2256 * ComboWndProcA
2258 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2259 * window structs.
2261 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2263 WND* pWnd = WIN_FindWndPtr(hwnd);
2264 LRESULT retvalue = ComboWndProc_locked(pWnd, message, wParam, lParam, FALSE);
2266 WIN_ReleaseWndPtr(pWnd);
2267 return retvalue;
2270 /***********************************************************************
2271 * ComboWndProcW
2273 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2275 WND* pWnd = WIN_FindWndPtr(hwnd);
2276 LRESULT retvalue = ComboWndProc_locked(pWnd, message, wParam, lParam, TRUE);
2278 WIN_ReleaseWndPtr(pWnd);
2279 return retvalue;