Release 20010216.
[wine/multimedia.git] / controls / combo.c
blobaa92f41298adca1e717d160d85eb234f3d2448d6
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"
23 DEFAULT_DEBUG_CHANNEL(combo);
25 /* bits in the dwKeyData */
26 #define KEYDATA_ALT 0x2000
27 #define KEYDATA_PREVSTATE 0x4000
30 * Additional combo box definitions
33 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
34 #define CB_NOTIFY( lphc, code ) \
35 (SendMessageW((lphc)->owner, WM_COMMAND, \
36 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
37 #define CB_GETEDITTEXTLENGTH( lphc ) \
38 (SendMessageW((lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
40 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
43 * Drawing globals
45 static HBITMAP hComboBmp = 0;
46 static UINT CBitHeight, CBitWidth;
49 * Look and feel dependant "constants"
52 #define COMBO_YBORDERGAP 5
53 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
54 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
55 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
56 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
58 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
59 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
61 /*********************************************************************
62 * combo class descriptor
64 const struct builtin_class_descr COMBO_builtin_class =
66 "ComboBox", /* name */
67 CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS, /* style */
68 ComboWndProcA, /* procA */
69 ComboWndProcW, /* procW */
70 sizeof(HEADCOMBO *), /* extra */
71 IDC_ARROWA, /* cursor */
72 0 /* brush */
76 /***********************************************************************
77 * COMBO_Init
79 * Load combo button bitmap.
81 static BOOL COMBO_Init()
83 HDC hDC;
85 if( hComboBmp ) return TRUE;
86 if( (hDC = CreateCompatibleDC(0)) )
88 BOOL bRet = FALSE;
89 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
91 BITMAP bm;
92 HBITMAP hPrevB;
93 RECT r;
95 GetObjectW( hComboBmp, sizeof(bm), &bm );
96 CBitHeight = bm.bmHeight;
97 CBitWidth = bm.bmWidth;
99 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
101 hPrevB = SelectObject( hDC, hComboBmp);
102 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
103 InvertRect( hDC, &r );
104 SelectObject( hDC, hPrevB );
105 bRet = TRUE;
107 DeleteDC( hDC );
108 return bRet;
110 return FALSE;
113 /***********************************************************************
114 * COMBO_NCCreate
116 static LRESULT COMBO_NCCreate(WND* wnd, LONG style)
118 LPHEADCOMBO lphc;
120 if ( wnd && COMBO_Init() &&
121 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
123 memset( lphc, 0, sizeof(HEADCOMBO) );
124 *(LPHEADCOMBO*)wnd->wExtra = lphc;
126 /* some braindead apps do try to use scrollbar/border flags */
128 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
129 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
132 * We also have to remove the client edge style to make sure
133 * we don't end-up with a non client area.
135 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
137 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
138 lphc->dwStyle |= CBS_HASSTRINGS;
139 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
140 lphc->wState |= CBF_NOTIFY;
142 TRACE("[0x%08x], style = %08x\n",
143 (UINT)lphc, lphc->dwStyle );
145 return (LRESULT)(UINT)wnd->hwndSelf;
147 return (LRESULT)FALSE;
150 /***********************************************************************
151 * COMBO_NCDestroy
153 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
156 if( lphc )
158 WND* wnd = lphc->self;
160 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc));
162 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
163 DestroyWindow( lphc->hWndLBox );
165 HeapFree( GetProcessHeap(), 0, lphc );
166 wnd->wExtra[0] = 0;
168 return 0;
171 /***********************************************************************
172 * CBGetTextAreaHeight
174 * This method will calculate the height of the text area of the
175 * combobox.
176 * The height of the text area is set in two ways.
177 * It can be set explicitely through a combobox message or through a
178 * WM_MEASUREITEM callback.
179 * If this is not the case, the height is set to 13 dialog units.
180 * This height was determined through experimentation.
182 static INT CBGetTextAreaHeight(
183 HWND hwnd,
184 LPHEADCOMBO lphc)
186 INT iTextItemHeight;
188 if( lphc->editHeight ) /* explicitly set height */
190 iTextItemHeight = lphc->editHeight;
192 else
194 TEXTMETRICW tm;
195 HDC hDC = GetDC(hwnd);
196 HFONT hPrevFont = 0;
197 INT baseUnitY;
199 if (lphc->hFont)
200 hPrevFont = SelectObject( hDC, lphc->hFont );
202 GetTextMetricsW(hDC, &tm);
204 baseUnitY = tm.tmHeight;
206 if( hPrevFont )
207 SelectObject( hDC, hPrevFont );
209 ReleaseDC(hwnd, hDC);
211 iTextItemHeight = ((13 * baseUnitY) / 8);
214 * This "formula" calculates the height of the complete control.
215 * To calculate the height of the text area, we have to remove the
216 * borders.
218 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
222 * Check the ownerdraw case if we haven't asked the parent the size
223 * of the item yet.
225 if ( CB_OWNERDRAWN(lphc) &&
226 (lphc->wState & CBF_MEASUREITEM) )
228 MEASUREITEMSTRUCT measureItem;
229 RECT clientRect;
230 INT originalItemHeight = iTextItemHeight;
233 * We use the client rect for the width of the item.
235 GetClientRect(hwnd, &clientRect);
237 lphc->wState &= ~CBF_MEASUREITEM;
240 * Send a first one to measure the size of the text area
242 measureItem.CtlType = ODT_COMBOBOX;
243 measureItem.CtlID = lphc->self->wIDmenu;
244 measureItem.itemID = -1;
245 measureItem.itemWidth = clientRect.right;
246 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
247 measureItem.itemData = 0;
248 SendMessageW(lphc->owner, WM_MEASUREITEM,
249 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
250 iTextItemHeight = 6 + measureItem.itemHeight;
253 * Send a second one in the case of a fixed ownerdraw list to calculate the
254 * size of the list items. (we basically do this on behalf of the listbox)
256 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
258 measureItem.CtlType = ODT_COMBOBOX;
259 measureItem.CtlID = lphc->self->wIDmenu;
260 measureItem.itemID = 0;
261 measureItem.itemWidth = clientRect.right;
262 measureItem.itemHeight = originalItemHeight;
263 measureItem.itemData = 0;
264 SendMessageW(lphc->owner, WM_MEASUREITEM,
265 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
266 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
270 * Keep the size for the next time
272 lphc->editHeight = iTextItemHeight;
275 return iTextItemHeight;
278 /***********************************************************************
279 * CBForceDummyResize
281 * The dummy resize is used for listboxes that have a popup to trigger
282 * a re-arranging of the contents of the combobox and the recalculation
283 * of the size of the "real" control window.
285 static void CBForceDummyResize(
286 LPHEADCOMBO lphc)
288 RECT windowRect;
289 int newComboHeight;
291 newComboHeight = CBGetTextAreaHeight(CB_HWND(lphc),lphc) + 2*COMBO_YBORDERSIZE();
293 GetWindowRect(CB_HWND(lphc), &windowRect);
296 * We have to be careful, resizing a combobox also has the meaning that the
297 * dropped rect will be resized. In this case, we want to trigger a resize
298 * to recalculate layout but we don't want to change the dropped rectangle
299 * So, we pass the height of text area of control as the height.
300 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
301 * message.
303 SetWindowPos( CB_HWND(lphc),
304 (HWND)NULL,
305 0, 0,
306 windowRect.right - windowRect.left,
307 newComboHeight,
308 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
311 /***********************************************************************
312 * CBCalcPlacement
314 * Set up component coordinates given valid lphc->RectCombo.
316 static void CBCalcPlacement(
317 HWND hwnd,
318 LPHEADCOMBO lphc,
319 LPRECT lprEdit,
320 LPRECT lprButton,
321 LPRECT lprLB)
324 * Again, start with the client rectangle.
326 GetClientRect(hwnd, lprEdit);
329 * Remove the borders
331 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
334 * Chop off the bottom part to fit with the height of the text area.
336 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
339 * The button starts the same vertical position as the text area.
341 CopyRect(lprButton, lprEdit);
344 * If the combobox is "simple" there is no button.
346 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
347 lprButton->left = lprButton->right = lprButton->bottom = 0;
348 else
351 * Let's assume the combobox button is the same width as the
352 * scrollbar button.
353 * size the button horizontally and cut-off the text area.
355 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
356 lprEdit->right = lprButton->left;
360 * In the case of a dropdown, there is an additional spacing between the
361 * text area and the button.
363 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
365 lprEdit->right -= COMBO_EDITBUTTONSPACE();
369 * If we have an edit control, we space it away from the borders slightly.
371 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
373 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
377 * Adjust the size of the listbox popup.
379 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
382 * Use the client rectangle to initialize the listbox rectangle
384 GetClientRect(hwnd, lprLB);
387 * Then, chop-off the top part.
389 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
391 else
394 * Make sure the dropped width is as large as the combobox itself.
396 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
398 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
401 * In the case of a dropdown, the popup listbox is offset to the right.
402 * so, we want to make sure it's flush with the right side of the
403 * combobox
405 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
406 lprLB->right -= COMBO_EDITBUTTONSPACE();
408 else
409 lprLB->right = lprLB->left + lphc->droppedWidth;
412 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
413 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
415 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
416 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
418 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
419 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
422 /***********************************************************************
423 * CBGetDroppedControlRect
425 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
427 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
428 of the combo box and the lower right corner of the listbox */
430 GetWindowRect(lphc->self->hwndSelf, lpRect);
432 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
433 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
437 /***********************************************************************
438 * COMBO_WindowPosChanging
440 static LRESULT COMBO_WindowPosChanging(
441 HWND hwnd,
442 LPHEADCOMBO lphc,
443 WINDOWPOS* posChanging)
446 * We need to override the WM_WINDOWPOSCHANGING method to handle all
447 * the non-simple comboboxes. The problem is that those controls are
448 * always the same height. We have to make sure they are not resized
449 * to another value.
451 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
452 ((posChanging->flags & SWP_NOSIZE) == 0) )
454 int newComboHeight;
456 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
457 2*COMBO_YBORDERSIZE();
460 * Resizing a combobox has another side effect, it resizes the dropped
461 * rectangle as well. However, it does it only if the new height for the
462 * combobox is different from the height it should have. In other words,
463 * if the application resizing the combobox only had the intention to resize
464 * the actual control, for example, to do the layout of a dialog that is
465 * resized, the height of the dropdown is not changed.
467 if (posChanging->cy != newComboHeight)
469 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
471 posChanging->cy = newComboHeight;
475 return 0;
478 /***********************************************************************
479 * COMBO_Create
481 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, HWND hwndParent, LONG style )
483 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
484 static const WCHAR editName[] = {'E','d','i','t',0};
486 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
487 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
489 lphc->self = wnd;
490 lphc->owner = hwndParent;
493 * The item height and dropped width are not set when the control
494 * is created.
496 lphc->droppedWidth = lphc->editHeight = 0;
499 * The first time we go through, we want to measure the ownerdraw item
501 lphc->wState |= CBF_MEASUREITEM;
503 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
505 if( lphc->owner || !(style & WS_VISIBLE) )
507 UINT lbeStyle = 0;
508 UINT lbeExStyle = 0;
511 * Initialize the dropped rect to the size of the client area of the
512 * control and then, force all the areas of the combobox to be
513 * recalculated.
515 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
517 CBCalcPlacement(wnd->hwndSelf,
518 lphc,
519 &lphc->textRect,
520 &lphc->buttonRect,
521 &lphc->droppedRect );
524 * Adjust the position of the popup listbox if it's necessary
526 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
528 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
531 * If it's a dropdown, the listbox is offset
533 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
534 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
536 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
537 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
540 /* create listbox popup */
542 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
543 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
545 if( lphc->dwStyle & CBS_SORT )
546 lbeStyle |= LBS_SORT;
547 if( lphc->dwStyle & CBS_HASSTRINGS )
548 lbeStyle |= LBS_HASSTRINGS;
549 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
550 lbeStyle |= LBS_NOINTEGRALHEIGHT;
551 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
552 lbeStyle |= LBS_DISABLENOSCROLL;
554 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
556 lbeStyle |= WS_VISIBLE;
559 * In win 95 look n feel, the listbox in the simple combobox has
560 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
562 if (TWEAK_WineLook > WIN31_LOOK)
564 lbeStyle &= ~WS_BORDER;
565 lbeExStyle |= WS_EX_CLIENTEDGE;
569 lphc->hWndLBox = CreateWindowExW(lbeExStyle,
570 clbName,
571 NULL,
572 lbeStyle,
573 lphc->droppedRect.left,
574 lphc->droppedRect.top,
575 lphc->droppedRect.right - lphc->droppedRect.left,
576 lphc->droppedRect.bottom - lphc->droppedRect.top,
577 lphc->self->hwndSelf,
578 (HMENU)ID_CB_LISTBOX,
579 lphc->self->hInstance,
580 (LPVOID)lphc );
582 if( lphc->hWndLBox )
584 BOOL bEdit = TRUE;
585 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
588 * In Win95 look, the border fo the edit control is
589 * provided by the combobox
591 if (TWEAK_WineLook == WIN31_LOOK)
592 lbeStyle |= WS_BORDER;
594 if( lphc->wState & CBF_EDIT )
596 if( lphc->dwStyle & CBS_OEMCONVERT )
597 lbeStyle |= ES_OEMCONVERT;
598 if( lphc->dwStyle & CBS_AUTOHSCROLL )
599 lbeStyle |= ES_AUTOHSCROLL;
600 if( lphc->dwStyle & CBS_LOWERCASE )
601 lbeStyle |= ES_LOWERCASE;
602 else if( lphc->dwStyle & CBS_UPPERCASE )
603 lbeStyle |= ES_UPPERCASE;
605 lphc->hWndEdit = CreateWindowExW(0,
606 editName,
607 NULL,
608 lbeStyle,
609 lphc->textRect.left, lphc->textRect.top,
610 lphc->textRect.right - lphc->textRect.left,
611 lphc->textRect.bottom - lphc->textRect.top,
612 lphc->self->hwndSelf,
613 (HMENU)ID_CB_EDIT,
614 lphc->self->hInstance,
615 NULL );
617 if( !lphc->hWndEdit )
618 bEdit = FALSE;
621 if( bEdit )
623 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
625 /* Now do the trick with parent */
626 SetParent(lphc->hWndLBox, HWND_DESKTOP);
628 * If the combo is a dropdown, we must resize the control
629 * to fit only the text area and button. To do this,
630 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
631 * will take care of setting the height for us.
633 CBForceDummyResize(lphc);
636 TRACE("init done\n");
637 return wnd->hwndSelf;
639 ERR("edit control failure.\n");
640 } else ERR("listbox failure.\n");
641 } else ERR("no owner for visible combo.\n");
643 /* CreateWindow() will send WM_NCDESTROY to cleanup */
645 return -1;
648 /***********************************************************************
649 * CBPaintButton
651 * Paint combo button (normal, pressed, and disabled states).
653 static void CBPaintButton(
654 LPHEADCOMBO lphc,
655 HDC hdc,
656 RECT rectButton)
658 if( lphc->wState & CBF_NOREDRAW )
659 return;
661 if (TWEAK_WineLook == WIN31_LOOK)
663 UINT x, y;
664 BOOL bBool;
665 HDC hMemDC;
666 HBRUSH hPrevBrush;
667 COLORREF oldTextColor, oldBkColor;
670 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
673 * Draw the button background
675 PatBlt( hdc,
676 rectButton.left,
677 rectButton.top,
678 rectButton.right-rectButton.left,
679 rectButton.bottom-rectButton.top,
680 PATCOPY );
682 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
684 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
686 else
688 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
692 * Remove the edge of the button from the rectangle
693 * and calculate the position of the bitmap.
695 InflateRect( &rectButton, -2, -2);
697 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
698 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
701 hMemDC = CreateCompatibleDC( hdc );
702 SelectObject( hMemDC, hComboBmp );
703 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
704 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
705 RGB(0,0,0) );
706 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
707 SetBkColor( hdc, oldBkColor );
708 SetTextColor( hdc, oldTextColor );
709 DeleteDC( hMemDC );
710 SelectObject( hdc, hPrevBrush );
712 else
714 UINT buttonState = DFCS_SCROLLCOMBOBOX;
716 if (lphc->wState & CBF_BUTTONDOWN)
718 buttonState |= DFCS_PUSHED;
721 if (CB_DISABLED(lphc))
723 buttonState |= DFCS_INACTIVE;
726 DrawFrameControl(hdc,
727 &rectButton,
728 DFC_SCROLL,
729 buttonState);
733 /***********************************************************************
734 * CBPaintText
736 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
738 static void CBPaintText(
739 LPHEADCOMBO lphc,
740 HDC hdc,
741 RECT rectEdit)
743 INT id, size = 0;
744 LPWSTR pText = NULL;
746 if( lphc->wState & CBF_NOREDRAW ) return;
748 /* follow Windows combobox that sends a bunch of text
749 * inquiries to its listbox while processing WM_PAINT. */
751 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
753 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
754 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
756 SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
757 pText[size] = '\0'; /* just in case */
758 } else return;
760 else
761 if( !CB_OWNERDRAWN(lphc) )
762 return;
764 if( lphc->wState & CBF_EDIT )
766 static const WCHAR empty_stringW[] = { 0 };
767 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
768 if( lphc->wState & CBF_FOCUSED )
769 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
771 else /* paint text field ourselves */
773 UINT itemState = ODS_COMBOBOXEDIT;
774 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
777 * Give ourselves some space.
779 InflateRect( &rectEdit, -1, -1 );
781 if ( (lphc->wState & CBF_FOCUSED) &&
782 !(lphc->wState & CBF_DROPPED) )
784 /* highlight */
786 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
787 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
788 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
789 itemState |= ODS_SELECTED | ODS_FOCUS;
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 HDC hDC;
948 if(lphc->wState & CBF_EDIT)
949 return TRUE;
951 hDC = (hParamDC) ? hParamDC
952 : GetDC(hwnd);
954 * Retrieve the background brush
956 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
958 FillRect(hDC, &lphc->textRect, hBkgBrush);
960 if (!hParamDC)
961 ReleaseDC(hwnd, hDC);
963 return TRUE;
966 /***********************************************************************
967 * COMBO_Paint
969 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
971 PAINTSTRUCT ps;
972 HDC hDC;
974 hDC = (hParamDC) ? hParamDC
975 : BeginPaint( lphc->self->hwndSelf, &ps);
978 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
980 HBRUSH hPrevBrush, hBkgBrush;
983 * Retrieve the background brush and select it in the
984 * DC.
986 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
988 hPrevBrush = SelectObject( hDC, hBkgBrush );
991 * In non 3.1 look, there is a sunken border on the combobox
993 if (TWEAK_WineLook != WIN31_LOOK)
995 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
998 if( !IsRectEmpty(&lphc->buttonRect) )
1000 CBPaintButton(lphc, hDC, lphc->buttonRect);
1003 if( !(lphc->wState & CBF_EDIT) )
1006 * The text area has a border only in Win 3.1 look.
1008 if (TWEAK_WineLook == WIN31_LOOK)
1010 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1012 Rectangle( hDC,
1013 lphc->textRect.left, lphc->textRect.top,
1014 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
1016 SelectObject( hDC, hPrevPen );
1019 CBPaintText( lphc, hDC, lphc->textRect);
1022 if( hPrevBrush )
1023 SelectObject( hDC, hPrevBrush );
1026 if( !hParamDC )
1027 EndPaint(lphc->self->hwndSelf, &ps);
1029 return 0;
1032 /***********************************************************************
1033 * CBUpdateLBox
1035 * Select listbox entry according to the contents of the edit control.
1037 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1039 INT length, idx;
1040 LPWSTR pText = NULL;
1042 idx = LB_ERR;
1043 length = CB_GETEDITTEXTLENGTH( lphc );
1045 if( length > 0 )
1046 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1048 TRACE("\t edit text length %i\n", length );
1050 if( pText )
1052 if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
1053 else pText[0] = '\0';
1054 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1055 (WPARAM)(-1), (LPARAM)pText );
1056 HeapFree( GetProcessHeap(), 0, pText );
1059 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
1061 /* probably superfluous but Windows sends this too */
1062 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1063 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1065 return idx;
1068 /***********************************************************************
1069 * CBUpdateEdit
1071 * Copy a listbox entry to the edit control.
1073 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1075 INT length;
1076 LPWSTR pText = NULL;
1077 static const WCHAR empty_stringW[] = { 0 };
1079 TRACE("\t %i\n", index );
1081 if( index >= 0 ) /* got an entry */
1083 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1084 if( length )
1086 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1088 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1089 (WPARAM)index, (LPARAM)pText );
1094 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1095 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1096 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1098 if( lphc->wState & CBF_FOCUSED )
1099 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1101 if( pText )
1102 HeapFree( GetProcessHeap(), 0, pText );
1105 /***********************************************************************
1106 * CBDropDown
1108 * Show listbox popup.
1110 static void CBDropDown( LPHEADCOMBO lphc )
1112 RECT rect,r;
1113 int nItems = 0;
1114 int nDroppedHeight;
1116 TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1118 CB_NOTIFY( lphc, CBN_DROPDOWN );
1120 /* set selection */
1122 lphc->wState |= CBF_DROPPED;
1123 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1125 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1127 /* Update edit only if item is in the list */
1128 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1129 CBUpdateEdit( lphc, lphc->droppedIndex );
1131 else
1133 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1135 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1136 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1137 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1140 /* now set popup position */
1141 GetWindowRect( lphc->self->hwndSelf, &rect );
1144 * If it's a dropdown, the listbox is offset
1146 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1147 rect.left += COMBO_EDITBUTTONSPACE();
1149 /* if the dropped height is greater than the total height of the dropped
1150 items list, then force the drop down list height to be the total height
1151 of the items in the dropped list */
1153 /* And Remove any extra space (Best Fit) */
1154 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1155 /* if listbox length has been set directly by its handle */
1156 GetWindowRect(lphc->hWndLBox, &r);
1157 if (nDroppedHeight < r.bottom - r.top)
1158 nDroppedHeight = r.bottom - r.top;
1159 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1161 if (nItems > 0)
1163 int nHeight;
1165 nHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1166 nHeight *= nItems;
1168 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1169 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1172 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1173 if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1174 rect.bottom = rect.top - nDroppedHeight;
1176 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1177 lphc->droppedRect.right - lphc->droppedRect.left,
1178 nDroppedHeight,
1179 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1182 if( !(lphc->wState & CBF_NOREDRAW) )
1183 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1184 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1186 EnableWindow( lphc->hWndLBox, TRUE );
1187 if (GetCapture() != lphc->self->hwndSelf)
1188 SetCapture(lphc->hWndLBox);
1191 /***********************************************************************
1192 * CBRollUp
1194 * Hide listbox popup.
1196 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1198 HWND hWnd = lphc->self->hwndSelf;
1200 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1202 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1205 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1207 if( lphc->wState & CBF_DROPPED )
1209 RECT rect;
1211 lphc->wState &= ~CBF_DROPPED;
1212 ShowWindow( lphc->hWndLBox, SW_HIDE );
1213 if(GetCapture() == lphc->hWndLBox)
1215 ReleaseCapture();
1218 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1220 rect = lphc->buttonRect;
1222 else
1224 if( bButton )
1226 UnionRect( &rect,
1227 &lphc->buttonRect,
1228 &lphc->textRect);
1230 else
1231 rect = lphc->textRect;
1233 bButton = TRUE;
1236 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1237 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1238 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1239 CB_NOTIFY( lphc, CBN_CLOSEUP );
1244 /***********************************************************************
1245 * COMBO_FlipListbox
1247 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1249 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1251 if( lphc->wState & CBF_DROPPED )
1253 CBRollUp( lphc, ok, bRedrawButton );
1254 return FALSE;
1257 CBDropDown( lphc );
1258 return TRUE;
1261 /***********************************************************************
1262 * CBRepaintButton
1264 static void CBRepaintButton( LPHEADCOMBO lphc )
1266 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1267 UpdateWindow(CB_HWND(lphc));
1270 /***********************************************************************
1271 * COMBO_SetFocus
1273 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1275 if( !(lphc->wState & CBF_FOCUSED) )
1277 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1278 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1280 lphc->wState |= CBF_FOCUSED;
1282 if( !(lphc->wState & CBF_EDIT) )
1283 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1285 CB_NOTIFY( lphc, CBN_SETFOCUS );
1286 lphc->wState |= CBF_FOCUSED;
1290 /***********************************************************************
1291 * COMBO_KillFocus
1293 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1295 HWND hWnd = lphc->self->hwndSelf;
1297 if( lphc->wState & CBF_FOCUSED )
1299 CBRollUp( lphc, FALSE, TRUE );
1300 if( IsWindow( hWnd ) )
1302 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1303 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1305 lphc->wState &= ~CBF_FOCUSED;
1307 /* redraw text */
1308 if( !(lphc->wState & CBF_EDIT) )
1309 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1311 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1316 /***********************************************************************
1317 * COMBO_Command
1319 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1321 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1323 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1325 switch( HIWORD(wParam) >> 8 )
1327 case (EN_SETFOCUS >> 8):
1329 TRACE("[%04x]: edit [%04x] got focus\n",
1330 CB_HWND(lphc), lphc->hWndEdit );
1332 COMBO_SetFocus( lphc );
1333 break;
1335 case (EN_KILLFOCUS >> 8):
1337 TRACE("[%04x]: edit [%04x] lost focus\n",
1338 CB_HWND(lphc), lphc->hWndEdit );
1340 /* NOTE: it seems that Windows' edit control sends an
1341 * undocumented message WM_USER + 0x1B instead of this
1342 * notification (only when it happens to be a part of
1343 * the combo). ?? - AK.
1346 COMBO_KillFocus( lphc );
1347 break;
1350 case (EN_CHANGE >> 8):
1352 * In some circumstances (when the selection of the combobox
1353 * is changed for example) we don't wans the EN_CHANGE notification
1354 * to be forwarded to the parent of the combobox. This code
1355 * checks a flag that is set in these occasions and ignores the
1356 * notification.
1358 if (lphc->wState & CBF_NOLBSELECT)
1360 lphc->wState &= ~CBF_NOLBSELECT;
1362 else
1364 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1367 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1368 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1369 break;
1371 case (EN_UPDATE >> 8):
1372 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1373 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1374 break;
1376 case (EN_ERRSPACE >> 8):
1377 CB_NOTIFY( lphc, CBN_ERRSPACE );
1380 else if( lphc->hWndLBox == hWnd )
1382 switch( HIWORD(wParam) )
1384 case LBN_ERRSPACE:
1385 CB_NOTIFY( lphc, CBN_ERRSPACE );
1386 break;
1388 case LBN_DBLCLK:
1389 CB_NOTIFY( lphc, CBN_DBLCLK );
1390 break;
1392 case LBN_SELCHANGE:
1393 case LBN_SELCANCEL:
1395 TRACE("[%04x]: lbox selection change [%04x]\n",
1396 CB_HWND(lphc), lphc->wState );
1398 if( HIWORD(wParam) == LBN_SELCHANGE)
1400 if( lphc->wState & CBF_EDIT )
1402 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1403 lphc->wState |= CBF_NOLBSELECT;
1404 CBUpdateEdit( lphc, index );
1405 /* select text in edit, as Windows does */
1406 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1408 else
1409 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1412 /* do not roll up if selection is being tracked
1413 * by arrowkeys in the dropdown listbox */
1415 if( (lphc->dwStyle & CBS_SIMPLE) ||
1416 ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1418 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1420 else lphc->wState &= ~CBF_NOROLLUP;
1422 CB_NOTIFY( lphc, CBN_SELCHANGE );
1424 /* fall through */
1426 case LBN_SETFOCUS:
1427 case LBN_KILLFOCUS:
1428 /* nothing to do here since ComboLBox always resets the focus to its
1429 * combo/edit counterpart */
1430 break;
1433 return 0;
1436 /***********************************************************************
1437 * COMBO_ItemOp
1439 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1441 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1443 HWND hWnd = lphc->self->hwndSelf;
1445 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1447 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1449 /* two first items are the same in all 4 structs */
1450 lpIS->CtlType = ODT_COMBOBOX;
1451 lpIS->CtlID = lphc->self->wIDmenu;
1453 switch( msg ) /* patch window handle */
1455 case WM_DELETEITEM:
1456 lpIS->hwndItem = hWnd;
1457 #undef lpIS
1458 break;
1459 case WM_DRAWITEM:
1460 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1461 lpIS->hwndItem = hWnd;
1462 #undef lpIS
1463 break;
1464 case WM_COMPAREITEM:
1465 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1466 lpIS->hwndItem = hWnd;
1467 #undef lpIS
1468 break;
1471 return SendMessageW(lphc->owner, msg, lphc->self->wIDmenu, lParam);
1474 /***********************************************************************
1475 * COMBO_GetText
1477 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, INT N, LPARAM lParam, BOOL unicode)
1479 if( lphc->wState & CBF_EDIT )
1480 return unicode ? SendMessageW(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam) :
1481 SendMessageA(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam);
1483 /* get it from the listbox */
1485 if( lphc->hWndLBox )
1487 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1488 if( idx != LB_ERR )
1490 INT n = 0;
1491 INT length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN,
1492 (WPARAM)idx, 0 );
1494 if(unicode)
1496 LPWSTR lpBuffer, lpText = (LPWSTR)lParam;
1498 /* 'length' is without the terminating character */
1499 if(length >= N)
1500 lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1501 else
1502 lpBuffer = lpText;
1504 if(lpBuffer)
1506 n = SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1508 /* truncate if buffer is too short */
1509 if(length >= N)
1511 if(N && lpText)
1513 if(n != LB_ERR)
1514 strncpyW(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1515 lpText[N - 1] = '\0';
1518 HeapFree( GetProcessHeap(), 0, lpBuffer );
1521 else
1523 LPSTR lpBuffer, lpText = (LPSTR)lParam;
1525 /* 'length' is without the terminating character */
1526 if(length >= N)
1527 lpBuffer = HeapAlloc(GetProcessHeap(), 0, length + 1);
1528 else
1529 lpBuffer = lpText;
1531 if(lpBuffer)
1533 n = SendMessageA(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1535 /* truncate if buffer is too short */
1536 if(length >= N)
1538 if(N && lpText)
1540 if(n != LB_ERR)
1541 strncpy(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1542 lpText[N - 1] = '\0';
1545 HeapFree( GetProcessHeap(), 0, lpBuffer );
1548 return (LRESULT)n;
1551 return 0;
1555 /***********************************************************************
1556 * CBResetPos
1558 * This function sets window positions according to the updated
1559 * component placement struct.
1561 static void CBResetPos(
1562 LPHEADCOMBO lphc,
1563 LPRECT rectEdit,
1564 LPRECT rectLB,
1565 BOOL bRedraw)
1567 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1569 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1570 * sizing messages */
1572 if( lphc->wState & CBF_EDIT )
1573 SetWindowPos( lphc->hWndEdit, 0,
1574 rectEdit->left, rectEdit->top,
1575 rectEdit->right - rectEdit->left,
1576 rectEdit->bottom - rectEdit->top,
1577 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1579 SetWindowPos( lphc->hWndLBox, 0,
1580 rectLB->left, rectLB->top,
1581 rectLB->right - rectLB->left,
1582 rectLB->bottom - rectLB->top,
1583 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1585 if( bDrop )
1587 if( lphc->wState & CBF_DROPPED )
1589 lphc->wState &= ~CBF_DROPPED;
1590 ShowWindow( lphc->hWndLBox, SW_HIDE );
1593 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1594 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1595 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1600 /***********************************************************************
1601 * COMBO_Size
1603 static void COMBO_Size( LPHEADCOMBO lphc )
1605 CBCalcPlacement(lphc->self->hwndSelf,
1606 lphc,
1607 &lphc->textRect,
1608 &lphc->buttonRect,
1609 &lphc->droppedRect);
1611 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1615 /***********************************************************************
1616 * COMBO_Font
1618 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1621 * Set the font
1623 lphc->hFont = hFont;
1626 * Propagate to owned windows.
1628 if( lphc->wState & CBF_EDIT )
1629 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1630 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1633 * Redo the layout of the control.
1635 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1637 CBCalcPlacement(lphc->self->hwndSelf,
1638 lphc,
1639 &lphc->textRect,
1640 &lphc->buttonRect,
1641 &lphc->droppedRect);
1643 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1645 else
1647 CBForceDummyResize(lphc);
1652 /***********************************************************************
1653 * COMBO_SetItemHeight
1655 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1657 LRESULT lRet = CB_ERR;
1659 if( index == -1 ) /* set text field height */
1661 if( height < 32768 )
1663 lphc->editHeight = height;
1666 * Redo the layout of the control.
1668 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1670 CBCalcPlacement(lphc->self->hwndSelf,
1671 lphc,
1672 &lphc->textRect,
1673 &lphc->buttonRect,
1674 &lphc->droppedRect);
1676 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1678 else
1680 CBForceDummyResize(lphc);
1683 lRet = height;
1686 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1687 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1688 (WPARAM)index, (LPARAM)height );
1689 return lRet;
1692 /***********************************************************************
1693 * COMBO_SelectString
1695 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1697 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1698 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1699 if( index >= 0 )
1701 if( lphc->wState & CBF_EDIT )
1702 CBUpdateEdit( lphc, index );
1703 else
1705 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1708 return (LRESULT)index;
1711 /***********************************************************************
1712 * COMBO_LButtonDown
1714 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1716 POINT pt;
1717 BOOL bButton;
1718 HWND hWnd = lphc->self->hwndSelf;
1720 pt.x = LOWORD(lParam);
1721 pt.y = HIWORD(lParam);
1722 bButton = PtInRect(&lphc->buttonRect, pt);
1724 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1725 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1727 lphc->wState |= CBF_BUTTONDOWN;
1728 if( lphc->wState & CBF_DROPPED )
1730 /* got a click to cancel selection */
1732 lphc->wState &= ~CBF_BUTTONDOWN;
1733 CBRollUp( lphc, TRUE, FALSE );
1734 if( !IsWindow( hWnd ) ) return;
1736 if( lphc->wState & CBF_CAPTURE )
1738 lphc->wState &= ~CBF_CAPTURE;
1739 ReleaseCapture();
1742 else
1744 /* drop down the listbox and start tracking */
1746 lphc->wState |= CBF_CAPTURE;
1747 SetCapture( hWnd );
1748 CBDropDown( lphc );
1750 if( bButton ) CBRepaintButton( lphc );
1754 /***********************************************************************
1755 * COMBO_LButtonUp
1757 * Release capture and stop tracking if needed.
1759 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1761 if( lphc->wState & CBF_CAPTURE )
1763 lphc->wState &= ~CBF_CAPTURE;
1764 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1766 INT index = CBUpdateLBox( lphc, TRUE );
1767 /* Update edit only if item is in the list */
1768 if(index >= 0)
1770 lphc->wState |= CBF_NOLBSELECT;
1771 CBUpdateEdit( lphc, index );
1772 lphc->wState &= ~CBF_NOLBSELECT;
1775 ReleaseCapture();
1776 SetCapture(lphc->hWndLBox);
1779 if( lphc->wState & CBF_BUTTONDOWN )
1781 lphc->wState &= ~CBF_BUTTONDOWN;
1782 CBRepaintButton( lphc );
1786 /***********************************************************************
1787 * COMBO_MouseMove
1789 * Two things to do - track combo button and release capture when
1790 * pointer goes into the listbox.
1792 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1794 POINT pt;
1795 RECT lbRect;
1797 pt.x = LOWORD(lParam);
1798 pt.y = HIWORD(lParam);
1800 if( lphc->wState & CBF_BUTTONDOWN )
1802 BOOL bButton;
1804 bButton = PtInRect(&lphc->buttonRect, pt);
1806 if( !bButton )
1808 lphc->wState &= ~CBF_BUTTONDOWN;
1809 CBRepaintButton( lphc );
1813 GetClientRect( lphc->hWndLBox, &lbRect );
1814 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1815 if( PtInRect(&lbRect, pt) )
1817 lphc->wState &= ~CBF_CAPTURE;
1818 ReleaseCapture();
1819 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1821 /* hand over pointer tracking */
1822 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1827 /***********************************************************************
1828 * ComboWndProc_locked
1830 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1832 static LRESULT ComboWndProc_locked( WND* pWnd, UINT message,
1833 WPARAM wParam, LPARAM lParam, BOOL unicode )
1835 if( pWnd ) {
1836 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1837 HWND hwnd = pWnd->hwndSelf;
1839 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1840 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1842 if( lphc || message == WM_NCCREATE )
1843 switch(message)
1846 /* System messages */
1848 case WM_NCCREATE:
1850 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1851 ((LPCREATESTRUCTA)lParam)->style;
1852 return COMBO_NCCreate(pWnd, style);
1854 case WM_NCDESTROY:
1855 COMBO_NCDestroy(lphc);
1856 break;/* -> DefWindowProc */
1858 case WM_CREATE:
1860 HWND hwndParent;
1861 LONG style;
1862 if(unicode)
1864 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1865 style = ((LPCREATESTRUCTW)lParam)->style;
1867 else
1869 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1870 style = ((LPCREATESTRUCTA)lParam)->style;
1872 return COMBO_Create(lphc, pWnd, hwndParent, style);
1875 case WM_PRINTCLIENT:
1876 if (lParam & PRF_ERASEBKGND)
1877 COMBO_EraseBackground(hwnd, lphc, wParam);
1879 /* Fallthrough */
1880 case WM_PAINT:
1881 /* wParam may contain a valid HDC! */
1882 return COMBO_Paint(lphc, wParam);
1883 case WM_ERASEBKGND:
1884 return COMBO_EraseBackground(hwnd, lphc, wParam);
1885 case WM_GETDLGCODE:
1887 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1888 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1890 int vk = (int)((LPMSG)lParam)->wParam;
1892 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1893 result |= DLGC_WANTMESSAGE;
1895 return result;
1897 case WM_WINDOWPOSCHANGING:
1898 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1899 case WM_WINDOWPOSCHANGED:
1900 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1901 * In that case, the Combobox itself will not be resized, so we won't
1902 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1903 * do it here.
1905 /* fall through */
1906 case WM_SIZE:
1907 if( lphc->hWndLBox &&
1908 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1909 return TRUE;
1910 case WM_SETFONT:
1911 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1912 return TRUE;
1913 case WM_GETFONT:
1914 return (LRESULT)lphc->hFont;
1915 case WM_SETFOCUS:
1916 if( lphc->wState & CBF_EDIT )
1917 SetFocus( lphc->hWndEdit );
1918 else
1919 COMBO_SetFocus( lphc );
1920 return TRUE;
1921 case WM_KILLFOCUS:
1922 #define hwndFocus ((HWND16)wParam)
1923 if( !hwndFocus ||
1924 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1925 COMBO_KillFocus( lphc );
1926 #undef hwndFocus
1927 return TRUE;
1928 case WM_COMMAND:
1929 return COMBO_Command( lphc, wParam, (HWND)lParam );
1930 case WM_GETTEXT:
1931 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1932 case WM_SETTEXT:
1933 case WM_GETTEXTLENGTH:
1934 case WM_CLEAR:
1935 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1937 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1938 if (j == -1) return 0;
1939 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1941 else if( lphc->wState & CBF_EDIT )
1943 LRESULT ret;
1944 lphc->wState |= CBF_NOEDITNOTIFY;
1945 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1946 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1947 lphc->wState &= ~CBF_NOEDITNOTIFY;
1948 return ret;
1950 else return CB_ERR;
1951 case WM_CUT:
1952 case WM_PASTE:
1953 case WM_COPY:
1954 if( lphc->wState & CBF_EDIT )
1956 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1957 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1959 else return CB_ERR;
1961 case WM_DRAWITEM:
1962 case WM_DELETEITEM:
1963 case WM_COMPAREITEM:
1964 case WM_MEASUREITEM:
1965 return COMBO_ItemOp(lphc, message, lParam);
1966 case WM_ENABLE:
1967 if( lphc->wState & CBF_EDIT )
1968 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1969 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1971 /* Force the control to repaint when the enabled state changes. */
1972 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1973 return TRUE;
1974 case WM_SETREDRAW:
1975 if( wParam )
1976 lphc->wState &= ~CBF_NOREDRAW;
1977 else
1978 lphc->wState |= CBF_NOREDRAW;
1980 if( lphc->wState & CBF_EDIT )
1981 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1982 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1983 return 0;
1984 case WM_SYSKEYDOWN:
1985 if( KEYDATA_ALT & HIWORD(lParam) )
1986 if( wParam == VK_UP || wParam == VK_DOWN )
1987 COMBO_FlipListbox( lphc, FALSE, FALSE );
1988 return 0;
1990 case WM_CHAR:
1991 case WM_KEYDOWN:
1993 HWND hwndTarget;
1995 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1996 (lphc->wState & CBF_DROPPED))
1998 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1999 return TRUE;
2002 if( lphc->wState & CBF_EDIT )
2003 hwndTarget = lphc->hWndEdit;
2004 else
2005 hwndTarget = lphc->hWndLBox;
2007 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2008 SendMessageA(hwndTarget, message, wParam, lParam);
2010 case WM_LBUTTONDOWN:
2011 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
2012 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2013 return TRUE;
2014 case WM_LBUTTONUP:
2015 COMBO_LButtonUp( lphc );
2016 return TRUE;
2017 case WM_MOUSEMOVE:
2018 if( lphc->wState & CBF_CAPTURE )
2019 COMBO_MouseMove( lphc, wParam, lParam );
2020 return TRUE;
2022 case WM_MOUSEWHEEL:
2023 if (wParam & (MK_SHIFT | MK_CONTROL))
2024 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2025 DefWindowProcA(hwnd, message, wParam, lParam);
2026 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2027 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2028 return TRUE;
2030 /* Combo messages */
2032 case CB_ADDSTRING16:
2033 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2034 /* fall through */
2035 case CB_ADDSTRING:
2036 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2037 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2038 case CB_INSERTSTRING16:
2039 wParam = (INT)(INT16)wParam;
2040 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2041 /* fall through */
2042 case CB_INSERTSTRING:
2043 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2044 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2045 case CB_DELETESTRING16:
2046 case CB_DELETESTRING:
2047 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2048 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2049 case CB_SELECTSTRING16:
2050 wParam = (INT)(INT16)wParam;
2051 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2052 /* fall through */
2053 case CB_SELECTSTRING:
2054 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2055 case CB_FINDSTRING16:
2056 wParam = (INT)(INT16)wParam;
2057 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2058 /* fall through */
2059 case CB_FINDSTRING:
2060 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2061 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2062 case CB_FINDSTRINGEXACT16:
2063 wParam = (INT)(INT16)wParam;
2064 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2065 /* fall through */
2066 case CB_FINDSTRINGEXACT:
2067 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2068 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2069 case CB_SETITEMHEIGHT16:
2070 wParam = (INT)(INT16)wParam; /* signed integer */
2071 /* fall through */
2072 case CB_SETITEMHEIGHT:
2073 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2074 case CB_GETITEMHEIGHT16:
2075 wParam = (INT)(INT16)wParam;
2076 /* fall through */
2077 case CB_GETITEMHEIGHT:
2078 if( (INT)wParam >= 0 ) /* listbox item */
2079 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2080 return CBGetTextAreaHeight(hwnd, lphc);
2081 case CB_RESETCONTENT16:
2082 case CB_RESETCONTENT:
2083 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2084 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2086 static const WCHAR empty_stringW[] = { 0 };
2087 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2089 else
2090 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
2091 return TRUE;
2092 case CB_INITSTORAGE:
2093 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2094 case CB_GETHORIZONTALEXTENT:
2095 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2096 case CB_SETHORIZONTALEXTENT:
2097 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2098 case CB_GETTOPINDEX:
2099 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2100 case CB_GETLOCALE:
2101 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2102 case CB_SETLOCALE:
2103 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2104 case CB_GETDROPPEDWIDTH:
2105 if( lphc->droppedWidth )
2106 return lphc->droppedWidth;
2107 return lphc->droppedRect.right - lphc->droppedRect.left;
2108 case CB_SETDROPPEDWIDTH:
2109 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2110 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2111 return CB_ERR;
2112 case CB_GETDROPPEDCONTROLRECT16:
2113 lParam = (LPARAM)MapSL(lParam);
2114 if( lParam )
2116 RECT r;
2117 CBGetDroppedControlRect( lphc, &r );
2118 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2120 return CB_OKAY;
2121 case CB_GETDROPPEDCONTROLRECT:
2122 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2123 return CB_OKAY;
2124 case CB_GETDROPPEDSTATE16:
2125 case CB_GETDROPPEDSTATE:
2126 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2127 case CB_DIR16:
2128 lParam = (LPARAM)MapSL(lParam);
2129 message = LB_DIR16;
2130 /* fall through */
2131 case CB_DIR:
2132 if(message == CB_DIR) message = LB_DIR;
2133 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2134 SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2136 case CB_SHOWDROPDOWN16:
2137 case CB_SHOWDROPDOWN:
2138 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2140 if( wParam )
2142 if( !(lphc->wState & CBF_DROPPED) )
2143 CBDropDown( lphc );
2145 else
2146 if( lphc->wState & CBF_DROPPED )
2147 CBRollUp( lphc, FALSE, TRUE );
2149 return TRUE;
2150 case CB_GETCOUNT16:
2151 case CB_GETCOUNT:
2152 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2153 case CB_GETCURSEL16:
2154 case CB_GETCURSEL:
2155 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2156 case CB_SETCURSEL16:
2157 wParam = (INT)(INT16)wParam;
2158 /* fall through */
2159 case CB_SETCURSEL:
2160 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2161 if( lParam >= 0 )
2162 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2164 /* no LBN_SELCHANGE in this case, update manually */
2165 if( lphc->wState & CBF_EDIT )
2166 CBUpdateEdit( lphc, (INT)wParam );
2167 else
2168 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2169 lphc->wState &= ~CBF_SELCHANGE;
2170 return lParam;
2171 case CB_GETLBTEXT16:
2172 wParam = (INT)(INT16)wParam;
2173 lParam = (LPARAM)MapSL(lParam);
2174 /* fall through */
2175 case CB_GETLBTEXT:
2176 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2177 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2178 case CB_GETLBTEXTLEN16:
2179 wParam = (INT)(INT16)wParam;
2180 /* fall through */
2181 case CB_GETLBTEXTLEN:
2182 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2183 case CB_GETITEMDATA16:
2184 wParam = (INT)(INT16)wParam;
2185 /* fall through */
2186 case CB_GETITEMDATA:
2187 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2188 case CB_SETITEMDATA16:
2189 wParam = (INT)(INT16)wParam;
2190 /* fall through */
2191 case CB_SETITEMDATA:
2192 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2193 case CB_GETEDITSEL16:
2194 wParam = lParam = 0; /* just in case */
2195 /* fall through */
2196 case CB_GETEDITSEL:
2197 /* Edit checks passed parameters itself */
2198 if( lphc->wState & CBF_EDIT )
2199 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2200 return CB_ERR;
2201 case CB_SETEDITSEL16:
2202 case CB_SETEDITSEL:
2203 if( lphc->wState & CBF_EDIT )
2204 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2205 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2206 return CB_ERR;
2207 case CB_SETEXTENDEDUI16:
2208 case CB_SETEXTENDEDUI:
2209 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2210 return CB_ERR;
2211 if( wParam )
2212 lphc->wState |= CBF_EUI;
2213 else lphc->wState &= ~CBF_EUI;
2214 return CB_OKAY;
2215 case CB_GETEXTENDEDUI16:
2216 case CB_GETEXTENDEDUI:
2217 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2219 default:
2220 if (message >= WM_USER)
2221 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2222 message - WM_USER, wParam, lParam );
2223 break;
2225 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2226 DefWindowProcA(hwnd, message, wParam, lParam);
2228 return CB_ERR;
2231 /***********************************************************************
2232 * ComboWndProcA
2234 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2235 * window structs.
2237 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2239 WND* pWnd = WIN_FindWndPtr(hwnd);
2240 LRESULT retvalue = ComboWndProc_locked(pWnd, message, wParam, lParam, FALSE);
2242 WIN_ReleaseWndPtr(pWnd);
2243 return retvalue;
2246 /***********************************************************************
2247 * ComboWndProcW
2249 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2251 WND* pWnd = WIN_FindWndPtr(hwnd);
2252 LRESULT retvalue = ComboWndProc_locked(pWnd, message, wParam, lParam, TRUE);
2254 WIN_ReleaseWndPtr(pWnd);
2255 return retvalue;