Replaced PTR_SEG_TO_LIN macro by exported MapSL function.
[wine.git] / controls / combo.c
blob2ca664a8e06288c1f8680715e596a91690242073
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 (SendMessageA( (lphc)->owner, WM_COMMAND, \
37 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
38 #define CB_GETEDITTEXTLENGTH( lphc ) \
39 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
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 );
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 NULL, /* procW (FIXME) */
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 = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
92 BITMAP bm;
93 HBITMAP hPrevB;
94 RECT r;
96 GetObjectA( 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, LPARAM lParam)
119 LPHEADCOMBO lphc;
121 if ( wnd && COMBO_Init() &&
122 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
124 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
126 memset( lphc, 0, sizeof(HEADCOMBO) );
127 *(LPHEADCOMBO*)wnd->wExtra = lphc;
129 /* some braindead apps do try to use scrollbar/border flags */
131 lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
132 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
135 * We also have to remove the client edge style to make sure
136 * we don't end-up with a non client area.
138 wnd->dwExStyle &= ~(WS_EX_CLIENTEDGE);
140 if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
141 lphc->dwStyle |= CBS_HASSTRINGS;
142 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
143 lphc->wState |= CBF_NOTIFY;
145 TRACE("[0x%08x], style = %08x\n",
146 (UINT)lphc, lphc->dwStyle );
148 return (LRESULT)(UINT)wnd->hwndSelf;
150 return (LRESULT)FALSE;
153 /***********************************************************************
154 * COMBO_NCDestroy
156 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
159 if( lphc )
161 WND* wnd = lphc->self;
163 TRACE("[%04x]: freeing storage\n", CB_HWND(lphc));
165 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
166 DestroyWindow( lphc->hWndLBox );
168 HeapFree( GetProcessHeap(), 0, lphc );
169 wnd->wExtra[0] = 0;
171 return 0;
174 /***********************************************************************
175 * CBGetTextAreaHeight
177 * This method will calculate the height of the text area of the
178 * combobox.
179 * The height of the text area is set in two ways.
180 * It can be set explicitely through a combobox message or through a
181 * WM_MEASUREITEM callback.
182 * If this is not the case, the height is set to 13 dialog units.
183 * This height was determined through experimentation.
185 static INT CBGetTextAreaHeight(
186 HWND hwnd,
187 LPHEADCOMBO lphc)
189 INT iTextItemHeight;
191 if( lphc->editHeight ) /* explicitly set height */
193 iTextItemHeight = lphc->editHeight;
195 else
197 TEXTMETRICA tm;
198 HDC hDC = GetDC(hwnd);
199 HFONT hPrevFont = 0;
200 INT baseUnitY;
202 if (lphc->hFont)
203 hPrevFont = SelectObject( hDC, lphc->hFont );
205 GetTextMetricsA(hDC, &tm);
207 baseUnitY = tm.tmHeight;
209 if( hPrevFont )
210 SelectObject( hDC, hPrevFont );
212 ReleaseDC(hwnd, hDC);
214 iTextItemHeight = ((13 * baseUnitY) / 8);
217 * This "formula" calculates the height of the complete control.
218 * To calculate the height of the text area, we have to remove the
219 * borders.
221 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
225 * Check the ownerdraw case if we haven't asked the parent the size
226 * of the item yet.
228 if ( CB_OWNERDRAWN(lphc) &&
229 (lphc->wState & CBF_MEASUREITEM) )
231 MEASUREITEMSTRUCT measureItem;
232 RECT clientRect;
233 INT originalItemHeight = iTextItemHeight;
236 * We use the client rect for the width of the item.
238 GetClientRect(hwnd, &clientRect);
240 lphc->wState &= ~CBF_MEASUREITEM;
243 * Send a first one to measure the size of the text area
245 measureItem.CtlType = ODT_COMBOBOX;
246 measureItem.CtlID = lphc->self->wIDmenu;
247 measureItem.itemID = -1;
248 measureItem.itemWidth = clientRect.right;
249 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
250 measureItem.itemData = 0;
251 SendMessageA(lphc->owner, WM_MEASUREITEM,
252 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
253 iTextItemHeight = 6 + measureItem.itemHeight;
256 * Send a second one in the case of a fixed ownerdraw list to calculate the
257 * size of the list items. (we basically do this on behalf of the listbox)
259 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
261 measureItem.CtlType = ODT_COMBOBOX;
262 measureItem.CtlID = lphc->self->wIDmenu;
263 measureItem.itemID = 0;
264 measureItem.itemWidth = clientRect.right;
265 measureItem.itemHeight = originalItemHeight;
266 measureItem.itemData = 0;
267 SendMessageA(lphc->owner, WM_MEASUREITEM,
268 (WPARAM)measureItem.CtlID, (LPARAM)&measureItem);
269 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
273 * Keep the size for the next time
275 lphc->editHeight = iTextItemHeight;
278 return iTextItemHeight;
281 /***********************************************************************
282 * CBForceDummyResize
284 * The dummy resize is used for listboxes that have a popup to trigger
285 * a re-arranging of the contents of the combobox and the recalculation
286 * of the size of the "real" control window.
288 static void CBForceDummyResize(
289 LPHEADCOMBO lphc)
291 RECT windowRect;
292 int newComboHeight;
294 newComboHeight = CBGetTextAreaHeight(CB_HWND(lphc),lphc) + 2*COMBO_YBORDERSIZE();
296 GetWindowRect(CB_HWND(lphc), &windowRect);
299 * We have to be careful, resizing a combobox also has the meaning that the
300 * dropped rect will be resized. In this case, we want to trigger a resize
301 * to recalculate layout but we don't want to change the dropped rectangle
302 * So, we pass the height of text area of control as the height.
303 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
304 * message.
306 SetWindowPos( CB_HWND(lphc),
307 (HWND)NULL,
308 0, 0,
309 windowRect.right - windowRect.left,
310 newComboHeight,
311 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
314 /***********************************************************************
315 * CBCalcPlacement
317 * Set up component coordinates given valid lphc->RectCombo.
319 static void CBCalcPlacement(
320 HWND hwnd,
321 LPHEADCOMBO lphc,
322 LPRECT lprEdit,
323 LPRECT lprButton,
324 LPRECT lprLB)
327 * Again, start with the client rectangle.
329 GetClientRect(hwnd, lprEdit);
332 * Remove the borders
334 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
337 * Chop off the bottom part to fit with the height of the text area.
339 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
342 * The button starts the same vertical position as the text area.
344 CopyRect(lprButton, lprEdit);
347 * If the combobox is "simple" there is no button.
349 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
350 lprButton->left = lprButton->right = lprButton->bottom = 0;
351 else
354 * Let's assume the combobox button is the same width as the
355 * scrollbar button.
356 * size the button horizontally and cut-off the text area.
358 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
359 lprEdit->right = lprButton->left;
363 * In the case of a dropdown, there is an additional spacing between the
364 * text area and the button.
366 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
368 lprEdit->right -= COMBO_EDITBUTTONSPACE();
372 * If we have an edit control, we space it away from the borders slightly.
374 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
376 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
380 * Adjust the size of the listbox popup.
382 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
385 * Use the client rectangle to initialize the listbox rectangle
387 GetClientRect(hwnd, lprLB);
390 * Then, chop-off the top part.
392 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
394 else
397 * Make sure the dropped width is as large as the combobox itself.
399 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
401 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
404 * In the case of a dropdown, the popup listbox is offset to the right.
405 * so, we want to make sure it's flush with the right side of the
406 * combobox
408 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
409 lprLB->right -= COMBO_EDITBUTTONSPACE();
411 else
412 lprLB->right = lprLB->left + lphc->droppedWidth;
415 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
416 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
418 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
419 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
421 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
422 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
425 /***********************************************************************
426 * CBGetDroppedControlRect
428 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
430 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
431 of the combo box and the lower right corner of the listbox */
433 GetWindowRect(lphc->self->hwndSelf, lpRect);
435 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
436 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
440 /***********************************************************************
441 * COMBO_WindowPosChanging
443 static LRESULT COMBO_WindowPosChanging(
444 HWND hwnd,
445 LPHEADCOMBO lphc,
446 WINDOWPOS* posChanging)
449 * We need to override the WM_WINDOWPOSCHANGING method to handle all
450 * the non-simple comboboxes. The problem is that those controls are
451 * always the same height. We have to make sure they are not resized
452 * to another value.
454 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
455 ((posChanging->flags & SWP_NOSIZE) == 0) )
457 int newComboHeight;
459 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
460 2*COMBO_YBORDERSIZE();
463 * Resizing a combobox has another side effect, it resizes the dropped
464 * rectangle as well. However, it does it only if the new height for the
465 * combobox is different than the height it should have. In other words,
466 * if the application resizing the combobox only had the intention to resize
467 * the actual control, for example, to do the layout of a dialog that is
468 * resized, the height of the dropdown is not changed.
470 if (posChanging->cy != newComboHeight)
472 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
474 posChanging->cy = newComboHeight;
478 return 0;
481 /***********************************************************************
482 * COMBO_Create
484 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
486 static char clbName[] = "ComboLBox";
487 static char editName[] = "Edit";
489 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
491 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
492 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
494 lphc->self = wnd;
495 lphc->owner = lpcs->hwndParent;
498 * The item height and dropped width are not set when the control
499 * is created.
501 lphc->droppedWidth = lphc->editHeight = 0;
504 * The first time we go through, we want to measure the ownerdraw item
506 lphc->wState |= CBF_MEASUREITEM;
508 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
510 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
512 UINT lbeStyle = 0;
513 UINT lbeExStyle = 0;
516 * Initialize the dropped rect to the size of the client area of the
517 * control and then, force all the areas of the combobox to be
518 * recalculated.
520 GetClientRect( wnd->hwndSelf, &lphc->droppedRect );
522 CBCalcPlacement(wnd->hwndSelf,
523 lphc,
524 &lphc->textRect,
525 &lphc->buttonRect,
526 &lphc->droppedRect );
529 * Adjust the position of the popup listbox if it's necessary
531 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
533 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
536 * If it's a dropdown, the listbox is offset
538 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
539 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
541 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect);
542 ClientToScreen(wnd->hwndSelf, (LPPOINT)&lphc->droppedRect.right);
545 /* create listbox popup */
547 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
548 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
550 if( lphc->dwStyle & CBS_SORT )
551 lbeStyle |= LBS_SORT;
552 if( lphc->dwStyle & CBS_HASSTRINGS )
553 lbeStyle |= LBS_HASSTRINGS;
554 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
555 lbeStyle |= LBS_NOINTEGRALHEIGHT;
556 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
557 lbeStyle |= LBS_DISABLENOSCROLL;
559 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
561 lbeStyle |= WS_VISIBLE;
564 * In win 95 look n feel, the listbox in the simple combobox has
565 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
567 if (TWEAK_WineLook > WIN31_LOOK)
569 lbeStyle &= ~WS_BORDER;
570 lbeExStyle |= WS_EX_CLIENTEDGE;
574 lphc->hWndLBox = CreateWindowExA(lbeExStyle,
575 clbName,
576 NULL,
577 lbeStyle,
578 lphc->droppedRect.left,
579 lphc->droppedRect.top,
580 lphc->droppedRect.right - lphc->droppedRect.left,
581 lphc->droppedRect.bottom - lphc->droppedRect.top,
582 lphc->self->hwndSelf,
583 (HMENU)ID_CB_LISTBOX,
584 lphc->self->hInstance,
585 (LPVOID)lphc );
587 if( lphc->hWndLBox )
589 BOOL bEdit = TRUE;
590 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
593 * In Win95 look, the border fo the edit control is
594 * provided by the combobox
596 if (TWEAK_WineLook == WIN31_LOOK)
597 lbeStyle |= WS_BORDER;
599 if( lphc->wState & CBF_EDIT )
601 if( lphc->dwStyle & CBS_OEMCONVERT )
602 lbeStyle |= ES_OEMCONVERT;
603 if( lphc->dwStyle & CBS_AUTOHSCROLL )
604 lbeStyle |= ES_AUTOHSCROLL;
605 if( lphc->dwStyle & CBS_LOWERCASE )
606 lbeStyle |= ES_LOWERCASE;
607 else if( lphc->dwStyle & CBS_UPPERCASE )
608 lbeStyle |= ES_UPPERCASE;
610 lphc->hWndEdit = CreateWindowExA(0,
611 editName,
612 NULL,
613 lbeStyle,
614 lphc->textRect.left, lphc->textRect.top,
615 lphc->textRect.right - lphc->textRect.left,
616 lphc->textRect.bottom - lphc->textRect.top,
617 lphc->self->hwndSelf,
618 (HMENU)ID_CB_EDIT,
619 lphc->self->hInstance,
620 NULL );
622 if( !lphc->hWndEdit )
623 bEdit = FALSE;
626 if( bEdit )
628 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
630 /* Now do the trick with parent */
631 SetParent(lphc->hWndLBox, HWND_DESKTOP);
633 * If the combo is a dropdown, we must resize the control
634 * to fit only the text area and button. To do this,
635 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
636 * will take care of setting the height for us.
638 CBForceDummyResize(lphc);
641 TRACE("init done\n");
642 return wnd->hwndSelf;
644 ERR("edit control failure.\n");
645 } else ERR("listbox failure.\n");
646 } else ERR("no owner for visible combo.\n");
648 /* CreateWindow() will send WM_NCDESTROY to cleanup */
650 return -1;
653 /***********************************************************************
654 * CBPaintButton
656 * Paint combo button (normal, pressed, and disabled states).
658 static void CBPaintButton(
659 LPHEADCOMBO lphc,
660 HDC hdc,
661 RECT rectButton)
663 if( lphc->wState & CBF_NOREDRAW )
664 return;
666 if (TWEAK_WineLook == WIN31_LOOK)
668 UINT x, y;
669 BOOL bBool;
670 HDC hMemDC;
671 HBRUSH hPrevBrush;
672 COLORREF oldTextColor, oldBkColor;
675 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
678 * Draw the button background
680 PatBlt( hdc,
681 rectButton.left,
682 rectButton.top,
683 rectButton.right-rectButton.left,
684 rectButton.bottom-rectButton.top,
685 PATCOPY );
687 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
689 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
691 else
693 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
697 * Remove the edge of the button from the rectangle
698 * and calculate the position of the bitmap.
700 InflateRect( &rectButton, -2, -2);
702 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
703 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
706 hMemDC = CreateCompatibleDC( hdc );
707 SelectObject( hMemDC, hComboBmp );
708 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
709 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
710 RGB(0,0,0) );
711 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
712 SetBkColor( hdc, oldBkColor );
713 SetTextColor( hdc, oldTextColor );
714 DeleteDC( hMemDC );
715 SelectObject( hdc, hPrevBrush );
717 else
719 UINT buttonState = DFCS_SCROLLCOMBOBOX;
721 if (lphc->wState & CBF_BUTTONDOWN)
723 buttonState |= DFCS_PUSHED;
726 if (CB_DISABLED(lphc))
728 buttonState |= DFCS_INACTIVE;
731 DrawFrameControl(hdc,
732 &rectButton,
733 DFC_SCROLL,
734 buttonState);
738 /***********************************************************************
739 * CBPaintText
741 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
743 static void CBPaintText(
744 LPHEADCOMBO lphc,
745 HDC hdc,
746 RECT rectEdit)
748 INT id, size = 0;
749 LPSTR pText = NULL;
751 if( lphc->wState & CBF_NOREDRAW ) return;
753 /* follow Windows combobox that sends a bunch of text
754 * inquiries to its listbox while processing WM_PAINT. */
756 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
758 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
759 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
761 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
762 pText[size] = '\0'; /* just in case */
763 } else return;
766 if( lphc->wState & CBF_EDIT )
768 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
769 if( lphc->wState & CBF_FOCUSED )
770 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
772 else /* paint text field ourselves */
774 UINT itemState;
775 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
778 * Give ourselves some space.
780 InflateRect( &rectEdit, -1, -1 );
782 if ( (lphc->wState & CBF_FOCUSED) &&
783 !(lphc->wState & CBF_DROPPED) )
785 /* highlight */
787 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
788 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
789 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
790 itemState = ODS_SELECTED | ODS_FOCUS;
792 else
793 itemState = 0;
795 if( CB_OWNERDRAWN(lphc) )
797 DRAWITEMSTRUCT dis;
798 HRGN clipRegion;
801 * Save the current clip region.
802 * To retrieve the clip region, we need to create one "dummy"
803 * clip region.
805 clipRegion = CreateRectRgnIndirect(&rectEdit);
807 if (GetClipRgn(hdc, clipRegion)!=1)
809 DeleteObject(clipRegion);
810 clipRegion=(HRGN)NULL;
813 if ( lphc->self->dwStyle & WS_DISABLED )
814 itemState |= ODS_DISABLED;
816 dis.CtlType = ODT_COMBOBOX;
817 dis.CtlID = lphc->self->wIDmenu;
818 dis.hwndItem = lphc->self->hwndSelf;
819 dis.itemAction = ODA_DRAWENTIRE;
820 dis.itemID = id;
821 dis.itemState = itemState;
822 dis.hDC = hdc;
823 dis.rcItem = rectEdit;
824 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
825 (WPARAM)id, 0 );
828 * Clip the DC and have the parent draw the item.
830 IntersectClipRect(hdc,
831 rectEdit.left, rectEdit.top,
832 rectEdit.right, rectEdit.bottom);
834 SendMessageA(lphc->owner, WM_DRAWITEM,
835 lphc->self->wIDmenu, (LPARAM)&dis );
838 * Reset the clipping region.
840 SelectClipRgn(hdc, clipRegion);
842 else
844 ExtTextOutA( hdc,
845 rectEdit.left + 1,
846 rectEdit.top + 1,
847 ETO_OPAQUE | ETO_CLIPPED,
848 &rectEdit,
849 pText ? pText : "" , size, NULL );
851 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
852 DrawFocusRect( hdc, &rectEdit );
855 if( hPrevFont )
856 SelectObject(hdc, hPrevFont );
858 if (pText)
859 HeapFree( GetProcessHeap(), 0, pText );
862 /***********************************************************************
863 * CBPaintBorder
865 static void CBPaintBorder(
866 HWND hwnd,
867 LPHEADCOMBO lphc,
868 HDC hdc)
870 RECT clientRect;
872 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
874 GetClientRect(hwnd, &clientRect);
876 else
878 CopyRect(&clientRect, &lphc->textRect);
880 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
881 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
884 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
887 /***********************************************************************
888 * COMBO_PrepareColors
890 * This method will sent the appropriate WM_CTLCOLOR message to
891 * prepare and setup the colors for the combo's DC.
893 * It also returns the brush to use for the background.
895 static HBRUSH COMBO_PrepareColors(
896 HWND hwnd,
897 LPHEADCOMBO lphc,
898 HDC hDC)
900 HBRUSH hBkgBrush;
903 * Get the background brush for this control.
905 if (CB_DISABLED(lphc))
907 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORSTATIC,
908 hDC, lphc->self->hwndSelf );
911 * We have to change the text color since WM_CTLCOLORSTATIC will
912 * set it to the "enabled" color. This is the same behavior as the
913 * edit control
915 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
917 else
919 if (lphc->wState & CBF_EDIT)
921 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLOREDIT,
922 hDC, lphc->self->hwndSelf );
924 else
926 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
927 hDC, lphc->self->hwndSelf );
932 * Catch errors.
934 if( !hBkgBrush )
935 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
937 return hBkgBrush;
940 /***********************************************************************
941 * COMBO_EraseBackground
943 static LRESULT COMBO_EraseBackground(
944 HWND hwnd,
945 LPHEADCOMBO lphc,
946 HDC hParamDC)
948 HBRUSH hBkgBrush;
949 RECT clientRect;
950 HDC hDC;
952 hDC = (hParamDC) ? hParamDC
953 : GetDC(hwnd);
956 * Calculate the area that we want to erase.
958 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
960 GetClientRect(hwnd, &clientRect);
962 else
964 CopyRect(&clientRect, &lphc->textRect);
966 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
970 * Retrieve the background brush
972 hBkgBrush = COMBO_PrepareColors(hwnd, lphc, hDC);
974 FillRect(hDC, &clientRect, hBkgBrush);
976 if (!hParamDC)
977 ReleaseDC(hwnd, hDC);
979 return TRUE;
982 /***********************************************************************
983 * COMBO_Paint
985 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
987 PAINTSTRUCT ps;
988 HDC hDC;
990 hDC = (hParamDC) ? hParamDC
991 : BeginPaint( lphc->self->hwndSelf, &ps);
994 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
996 HBRUSH hPrevBrush, hBkgBrush;
999 * Retrieve the background brush and select it in the
1000 * DC.
1002 hBkgBrush = COMBO_PrepareColors(lphc->self->hwndSelf, lphc, hDC);
1004 hPrevBrush = SelectObject( hDC, hBkgBrush );
1007 * In non 3.1 look, there is a sunken border on the combobox
1009 if (TWEAK_WineLook != WIN31_LOOK)
1011 CBPaintBorder(CB_HWND(lphc), lphc, hDC);
1014 if( !IsRectEmpty(&lphc->buttonRect) )
1016 CBPaintButton(lphc, hDC, lphc->buttonRect);
1019 if( !(lphc->wState & CBF_EDIT) )
1022 * The text area has a border only in Win 3.1 look.
1024 if (TWEAK_WineLook == WIN31_LOOK)
1026 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1028 Rectangle( hDC,
1029 lphc->textRect.left, lphc->textRect.top,
1030 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
1032 SelectObject( hDC, hPrevPen );
1035 CBPaintText( lphc, hDC, lphc->textRect);
1038 if( hPrevBrush )
1039 SelectObject( hDC, hPrevBrush );
1042 if( !hParamDC )
1043 EndPaint(lphc->self->hwndSelf, &ps);
1045 return 0;
1048 /***********************************************************************
1049 * CBUpdateLBox
1051 * Select listbox entry according to the contents of the edit control.
1053 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1055 INT length, idx;
1056 LPSTR pText = NULL;
1058 idx = LB_ERR;
1059 length = CB_GETEDITTEXTLENGTH( lphc );
1061 if( length > 0 )
1062 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
1064 TRACE("\t edit text length %i\n", length );
1066 if( pText )
1068 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
1069 else pText[0] = '\0';
1070 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
1071 (WPARAM)(-1), (LPARAM)pText );
1072 HeapFree( GetProcessHeap(), 0, pText );
1075 if (idx >= 0)
1077 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0 );
1079 /* probably superfluous but Windows sends this too */
1080 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0 );
1081 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0 );
1083 return idx;
1086 /***********************************************************************
1087 * CBUpdateEdit
1089 * Copy a listbox entry to the edit control.
1091 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1093 INT length;
1094 LPSTR pText = NULL;
1096 TRACE("\t %i\n", index );
1098 if( index >= 0 ) /* got an entry */
1100 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1101 if( length )
1103 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
1105 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1106 (WPARAM)index, (LPARAM)pText );
1110 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1111 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)"" );
1112 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1114 else
1115 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1117 if( lphc->wState & CBF_FOCUSED )
1118 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1120 if( pText )
1121 HeapFree( GetProcessHeap(), 0, pText );
1124 /***********************************************************************
1125 * CBDropDown
1127 * Show listbox popup.
1129 static void CBDropDown( LPHEADCOMBO lphc )
1131 RECT rect,r;
1132 int nItems = 0;
1133 int nDroppedHeight;
1135 TRACE("[%04x]: drop down\n", CB_HWND(lphc));
1137 CB_NOTIFY( lphc, CBN_DROPDOWN );
1139 /* set selection */
1141 lphc->wState |= CBF_DROPPED;
1142 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1144 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1146 if( !(lphc->wState & CBF_CAPTURE) )
1147 CBUpdateEdit( lphc, lphc->droppedIndex );
1149 else
1151 lphc->droppedIndex = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1153 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX,
1154 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1155 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1158 /* now set popup position */
1159 GetWindowRect( lphc->self->hwndSelf, &rect );
1162 * If it's a dropdown, the listbox is offset
1164 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1165 rect.left += COMBO_EDITBUTTONSPACE();
1167 /* if the dropped height is greater than the total height of the dropped
1168 items list, then force the drop down list height to be the total height
1169 of the items in the dropped list */
1171 /* And Remove any extra space (Best Fit) */
1172 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1173 /* if listbox length has been set directly by its handle */
1174 GetWindowRect(lphc->hWndLBox, &r);
1175 if (nDroppedHeight < r.bottom - r.top)
1176 nDroppedHeight = r.bottom - r.top;
1177 nItems = (int)SendMessageA (lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1179 if (nItems > 0)
1181 int nHeight;
1183 nHeight = (int)SendMessageA (lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1184 nHeight *= nItems;
1186 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1187 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1190 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1191 if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1192 rect.bottom = rect.top - nDroppedHeight;
1194 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1195 lphc->droppedRect.right - lphc->droppedRect.left,
1196 nDroppedHeight,
1197 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1200 if( !(lphc->wState & CBF_NOREDRAW) )
1201 RedrawWindow( lphc->self->hwndSelf, NULL, 0, RDW_INVALIDATE |
1202 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1204 EnableWindow( lphc->hWndLBox, TRUE );
1205 if (GetCapture() != lphc->self->hwndSelf)
1206 SetCapture(lphc->hWndLBox);
1209 /***********************************************************************
1210 * CBRollUp
1212 * Hide listbox popup.
1214 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1216 HWND hWnd = lphc->self->hwndSelf;
1218 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1220 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1223 TRACE("[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
1225 if( lphc->wState & CBF_DROPPED )
1227 RECT rect;
1229 lphc->wState &= ~CBF_DROPPED;
1230 ShowWindow( lphc->hWndLBox, SW_HIDE );
1231 if(GetCapture() == lphc->hWndLBox)
1233 ReleaseCapture();
1236 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1238 rect = lphc->buttonRect;
1240 else
1242 if( bButton )
1244 UnionRect( &rect,
1245 &lphc->buttonRect,
1246 &lphc->textRect);
1248 else
1249 rect = lphc->textRect;
1251 bButton = TRUE;
1254 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1255 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1256 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1257 CB_NOTIFY( lphc, CBN_CLOSEUP );
1262 /***********************************************************************
1263 * COMBO_FlipListbox
1265 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1267 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1269 if( lphc->wState & CBF_DROPPED )
1271 CBRollUp( lphc, ok, bRedrawButton );
1272 return FALSE;
1275 CBDropDown( lphc );
1276 return TRUE;
1279 /***********************************************************************
1280 * CBRepaintButton
1282 static void CBRepaintButton( LPHEADCOMBO lphc )
1284 InvalidateRect(CB_HWND(lphc), &lphc->buttonRect, TRUE);
1285 UpdateWindow(CB_HWND(lphc));
1288 /***********************************************************************
1289 * COMBO_SetFocus
1291 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1293 if( !(lphc->wState & CBF_FOCUSED) )
1295 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1296 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
1298 lphc->wState |= CBF_FOCUSED;
1300 if( !(lphc->wState & CBF_EDIT) )
1301 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1303 CB_NOTIFY( lphc, CBN_SETFOCUS );
1307 /***********************************************************************
1308 * COMBO_KillFocus
1310 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1312 HWND hWnd = lphc->self->hwndSelf;
1314 if( lphc->wState & CBF_FOCUSED )
1316 CBRollUp( lphc, FALSE, TRUE );
1317 if( IsWindow( hWnd ) )
1319 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1320 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
1322 lphc->wState &= ~CBF_FOCUSED;
1324 /* redraw text */
1325 if( !(lphc->wState & CBF_EDIT) )
1326 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1328 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1333 /***********************************************************************
1334 * COMBO_Command
1336 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1338 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1340 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1342 switch( HIWORD(wParam) >> 8 )
1344 case (EN_SETFOCUS >> 8):
1346 TRACE("[%04x]: edit [%04x] got focus\n",
1347 CB_HWND(lphc), lphc->hWndEdit );
1349 COMBO_SetFocus( lphc );
1350 break;
1352 case (EN_KILLFOCUS >> 8):
1354 TRACE("[%04x]: edit [%04x] lost focus\n",
1355 CB_HWND(lphc), lphc->hWndEdit );
1357 /* NOTE: it seems that Windows' edit control sends an
1358 * undocumented message WM_USER + 0x1B instead of this
1359 * notification (only when it happens to be a part of
1360 * the combo). ?? - AK.
1363 COMBO_KillFocus( lphc );
1364 break;
1367 case (EN_CHANGE >> 8):
1369 * In some circumstances (when the selection of the combobox
1370 * is changed for example) we don't wans the EN_CHANGE notification
1371 * to be forwarded to the parent of the combobox. This code
1372 * checks a flag that is set in these occasions and ignores the
1373 * notification.
1375 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1376 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1378 if (lphc->wState & CBF_NOLBSELECT)
1380 lphc->wState &= ~CBF_NOLBSELECT;
1382 else
1384 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1386 break;
1388 case (EN_UPDATE >> 8):
1389 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1390 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1391 break;
1393 case (EN_ERRSPACE >> 8):
1394 CB_NOTIFY( lphc, CBN_ERRSPACE );
1397 else if( lphc->hWndLBox == hWnd )
1399 switch( HIWORD(wParam) )
1401 case LBN_ERRSPACE:
1402 CB_NOTIFY( lphc, CBN_ERRSPACE );
1403 break;
1405 case LBN_DBLCLK:
1406 CB_NOTIFY( lphc, CBN_DBLCLK );
1407 break;
1409 case LBN_SELCHANGE:
1410 case LBN_SELCANCEL:
1412 TRACE("[%04x]: lbox selection change [%04x]\n",
1413 CB_HWND(lphc), lphc->wState );
1415 if( HIWORD(wParam) == LBN_SELCHANGE)
1417 if( lphc->wState & CBF_EDIT )
1419 INT index = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1420 lphc->wState |= CBF_NOLBSELECT;
1421 CBUpdateEdit( lphc, index );
1422 /* select text in edit, as Windows does */
1423 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
1425 else
1426 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1429 /* do not roll up if selection is being tracked
1430 * by arrowkeys in the dropdown listbox */
1432 if( (lphc->dwStyle & CBS_SIMPLE) ||
1433 ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1435 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1437 else lphc->wState &= ~CBF_NOROLLUP;
1439 CB_NOTIFY( lphc, CBN_SELCHANGE );
1441 /* fall through */
1443 case LBN_SETFOCUS:
1444 case LBN_KILLFOCUS:
1445 /* nothing to do here since ComboLBox always resets the focus to its
1446 * combo/edit counterpart */
1447 break;
1450 return 0;
1453 /***********************************************************************
1454 * COMBO_ItemOp
1456 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1458 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
1459 WPARAM wParam, LPARAM lParam )
1461 HWND hWnd = lphc->self->hwndSelf;
1463 TRACE("[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
1465 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
1467 /* two first items are the same in all 4 structs */
1468 lpIS->CtlType = ODT_COMBOBOX;
1469 lpIS->CtlID = lphc->self->wIDmenu;
1471 switch( msg ) /* patch window handle */
1473 case WM_DELETEITEM:
1474 lpIS->hwndItem = hWnd;
1475 #undef lpIS
1476 break;
1477 case WM_DRAWITEM:
1478 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
1479 lpIS->hwndItem = hWnd;
1480 #undef lpIS
1481 break;
1482 case WM_COMPAREITEM:
1483 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
1484 lpIS->hwndItem = hWnd;
1485 #undef lpIS
1486 break;
1489 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
1492 /***********************************************************************
1493 * COMBO_GetText
1495 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
1497 if( lphc->wState & CBF_EDIT )
1498 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
1499 (WPARAM)N, (LPARAM)lpText );
1501 /* get it from the listbox */
1503 if( lphc->hWndLBox )
1505 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1506 if( idx != LB_ERR )
1508 LPSTR lpBuffer;
1509 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1510 (WPARAM)idx, 0 );
1512 /* 'length' is without the terminating character */
1513 if( length >= N )
1514 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1515 else
1516 lpBuffer = lpText;
1518 if( lpBuffer )
1520 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1521 (WPARAM)idx, (LPARAM)lpBuffer );
1523 /* truncate if buffer is too short */
1525 if( length >= N )
1527 if (N && lpText) {
1528 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1529 lpText[N - 1] = '\0';
1531 HeapFree( GetProcessHeap(), 0, lpBuffer );
1533 return (LRESULT)n;
1537 return 0;
1541 /***********************************************************************
1542 * CBResetPos
1544 * This function sets window positions according to the updated
1545 * component placement struct.
1547 static void CBResetPos(
1548 LPHEADCOMBO lphc,
1549 LPRECT rectEdit,
1550 LPRECT rectLB,
1551 BOOL bRedraw)
1553 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1555 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1556 * sizing messages */
1558 if( lphc->wState & CBF_EDIT )
1559 SetWindowPos( lphc->hWndEdit, 0,
1560 rectEdit->left, rectEdit->top,
1561 rectEdit->right - rectEdit->left,
1562 rectEdit->bottom - rectEdit->top,
1563 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1565 SetWindowPos( lphc->hWndLBox, 0,
1566 rectLB->left, rectLB->top,
1567 rectLB->right - rectLB->left,
1568 rectLB->bottom - rectLB->top,
1569 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1571 if( bDrop )
1573 if( lphc->wState & CBF_DROPPED )
1575 lphc->wState &= ~CBF_DROPPED;
1576 ShowWindow( lphc->hWndLBox, SW_HIDE );
1579 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1580 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1581 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1586 /***********************************************************************
1587 * COMBO_Size
1589 static void COMBO_Size( LPHEADCOMBO lphc )
1591 CBCalcPlacement(lphc->self->hwndSelf,
1592 lphc,
1593 &lphc->textRect,
1594 &lphc->buttonRect,
1595 &lphc->droppedRect);
1597 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1601 /***********************************************************************
1602 * COMBO_Font
1604 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1607 * Set the font
1609 lphc->hFont = hFont;
1612 * Propagate to owned windows.
1614 if( lphc->wState & CBF_EDIT )
1615 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1616 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1619 * Redo the layout of the control.
1621 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1623 CBCalcPlacement(lphc->self->hwndSelf,
1624 lphc,
1625 &lphc->textRect,
1626 &lphc->buttonRect,
1627 &lphc->droppedRect);
1629 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1631 else
1633 CBForceDummyResize(lphc);
1638 /***********************************************************************
1639 * COMBO_SetItemHeight
1641 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1643 LRESULT lRet = CB_ERR;
1645 if( index == -1 ) /* set text field height */
1647 if( height < 32768 )
1649 lphc->editHeight = height;
1652 * Redo the layout of the control.
1654 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1656 CBCalcPlacement(lphc->self->hwndSelf,
1657 lphc,
1658 &lphc->textRect,
1659 &lphc->buttonRect,
1660 &lphc->droppedRect);
1662 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1664 else
1666 CBForceDummyResize(lphc);
1669 lRet = height;
1672 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1673 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1674 (WPARAM)index, (LPARAM)height );
1675 return lRet;
1678 /***********************************************************************
1679 * COMBO_SelectString
1681 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1683 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1684 (WPARAM)start, (LPARAM)pText );
1685 if( index >= 0 )
1687 if( lphc->wState & CBF_EDIT )
1688 CBUpdateEdit( lphc, index );
1689 else
1691 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
1694 return (LRESULT)index;
1697 /***********************************************************************
1698 * COMBO_LButtonDown
1700 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1702 POINT pt;
1703 BOOL bButton;
1704 HWND hWnd = lphc->self->hwndSelf;
1706 pt.x = LOWORD(lParam);
1707 pt.y = HIWORD(lParam);
1708 bButton = PtInRect(&lphc->buttonRect, pt);
1710 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1711 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1713 lphc->wState |= CBF_BUTTONDOWN;
1714 if( lphc->wState & CBF_DROPPED )
1716 /* got a click to cancel selection */
1718 lphc->wState &= ~CBF_BUTTONDOWN;
1719 CBRollUp( lphc, TRUE, FALSE );
1720 if( !IsWindow( hWnd ) ) return;
1722 if( lphc->wState & CBF_CAPTURE )
1724 lphc->wState &= ~CBF_CAPTURE;
1725 ReleaseCapture();
1728 else
1730 /* drop down the listbox and start tracking */
1732 lphc->wState |= CBF_CAPTURE;
1733 SetCapture( hWnd );
1734 CBDropDown( lphc );
1736 if( bButton ) CBRepaintButton( lphc );
1740 /***********************************************************************
1741 * COMBO_LButtonUp
1743 * Release capture and stop tracking if needed.
1745 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1747 if( lphc->wState & CBF_CAPTURE )
1749 lphc->wState &= ~CBF_CAPTURE;
1750 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1752 INT index = CBUpdateLBox( lphc, TRUE );
1753 lphc->wState |= CBF_NOLBSELECT;
1754 CBUpdateEdit( lphc, index );
1755 lphc->wState &= ~CBF_NOLBSELECT;
1757 ReleaseCapture();
1758 SetCapture(lphc->hWndLBox);
1762 if( lphc->wState & CBF_BUTTONDOWN )
1764 lphc->wState &= ~CBF_BUTTONDOWN;
1765 CBRepaintButton( lphc );
1769 /***********************************************************************
1770 * COMBO_MouseMove
1772 * Two things to do - track combo button and release capture when
1773 * pointer goes into the listbox.
1775 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1777 POINT pt;
1778 RECT lbRect;
1780 pt.x = LOWORD(lParam);
1781 pt.y = HIWORD(lParam);
1783 if( lphc->wState & CBF_BUTTONDOWN )
1785 BOOL bButton;
1787 bButton = PtInRect(&lphc->buttonRect, pt);
1789 if( !bButton )
1791 lphc->wState &= ~CBF_BUTTONDOWN;
1792 CBRepaintButton( lphc );
1796 GetClientRect( lphc->hWndLBox, &lbRect );
1797 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1798 if( PtInRect(&lbRect, pt) )
1800 lphc->wState &= ~CBF_CAPTURE;
1801 ReleaseCapture();
1802 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1804 /* hand over pointer tracking */
1805 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1810 /***********************************************************************
1811 * ComboWndProc_locked
1813 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1815 static inline LRESULT WINAPI ComboWndProc_locked( WND* pWnd, UINT message,
1816 WPARAM wParam, LPARAM lParam )
1818 if( pWnd ) {
1819 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1820 HWND hwnd = pWnd->hwndSelf;
1822 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1823 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1825 if( lphc || message == WM_NCCREATE )
1826 switch(message)
1829 /* System messages */
1831 case WM_NCCREATE:
1832 return COMBO_NCCreate(pWnd, lParam);
1833 case WM_NCDESTROY:
1834 COMBO_NCDestroy(lphc);
1835 break;/* -> DefWindowProc */
1837 case WM_CREATE:
1838 return COMBO_Create(lphc, pWnd, lParam);
1840 case WM_PRINTCLIENT:
1841 if (lParam & PRF_ERASEBKGND)
1842 COMBO_EraseBackground(hwnd, lphc, wParam);
1844 /* Fallthrough */
1845 case WM_PAINT:
1846 /* wParam may contain a valid HDC! */
1847 return COMBO_Paint(lphc, wParam);
1848 case WM_ERASEBKGND:
1849 return COMBO_EraseBackground(hwnd, lphc, wParam);
1850 case WM_GETDLGCODE:
1852 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1853 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1855 int vk = (int)((LPMSG)lParam)->wParam;
1857 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1858 result |= DLGC_WANTMESSAGE;
1860 return result;
1862 case WM_WINDOWPOSCHANGING:
1863 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1864 case WM_WINDOWPOSCHANGED:
1865 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1866 * In that case, the Combobox itself will not be resized, so we won't
1867 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1868 * do it here.
1870 /* fall through */
1871 case WM_SIZE:
1872 if( lphc->hWndLBox &&
1873 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1874 return TRUE;
1875 case WM_SETFONT:
1876 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1877 return TRUE;
1878 case WM_GETFONT:
1879 return (LRESULT)lphc->hFont;
1880 case WM_SETFOCUS:
1881 if( lphc->wState & CBF_EDIT )
1882 SetFocus( lphc->hWndEdit );
1883 else
1884 COMBO_SetFocus( lphc );
1885 return TRUE;
1886 case WM_KILLFOCUS:
1887 #define hwndFocus ((HWND16)wParam)
1888 if( !hwndFocus ||
1889 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1890 COMBO_KillFocus( lphc );
1891 #undef hwndFocus
1892 return TRUE;
1893 case WM_COMMAND:
1894 return COMBO_Command( lphc, wParam, (HWND)lParam );
1895 case WM_GETTEXT:
1896 return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1897 case WM_SETTEXT:
1898 case WM_GETTEXTLENGTH:
1899 case WM_CLEAR:
1900 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1902 int j = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
1903 if (j == -1) return 0;
1904 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1906 else if( lphc->wState & CBF_EDIT )
1908 LRESULT ret;
1909 lphc->wState |= CBF_NOEDITNOTIFY;
1910 ret = SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1911 lphc->wState &= ~CBF_NOEDITNOTIFY;
1912 return ret;
1914 else return CB_ERR;
1915 case WM_CUT:
1916 case WM_PASTE:
1917 case WM_COPY:
1918 if( lphc->wState & CBF_EDIT )
1919 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1920 else return CB_ERR;
1922 case WM_DRAWITEM:
1923 case WM_DELETEITEM:
1924 case WM_COMPAREITEM:
1925 case WM_MEASUREITEM:
1926 return COMBO_ItemOp( lphc, message, wParam, lParam );
1927 case WM_ENABLE:
1928 if( lphc->wState & CBF_EDIT )
1929 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1930 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1932 /* Force the control to repaint when the enabled state changes. */
1933 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
1934 return TRUE;
1935 case WM_SETREDRAW:
1936 if( wParam )
1937 lphc->wState &= ~CBF_NOREDRAW;
1938 else
1939 lphc->wState |= CBF_NOREDRAW;
1941 if( lphc->wState & CBF_EDIT )
1942 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1943 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1944 return 0;
1945 case WM_SYSKEYDOWN:
1946 if( KEYDATA_ALT & HIWORD(lParam) )
1947 if( wParam == VK_UP || wParam == VK_DOWN )
1948 COMBO_FlipListbox( lphc, FALSE, FALSE );
1949 return 0;
1951 case WM_CHAR:
1952 case WM_KEYDOWN:
1953 if (((CHAR)wParam == VK_RETURN || (CHAR)wParam == VK_ESCAPE) &&
1954 (lphc->wState & CBF_DROPPED))
1956 CBRollUp( lphc, (CHAR)wParam == VK_RETURN, FALSE );
1957 return TRUE;
1960 if( lphc->wState & CBF_EDIT )
1961 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1962 else
1963 return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1964 case WM_LBUTTONDOWN:
1965 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1966 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1967 return TRUE;
1968 case WM_LBUTTONUP:
1969 COMBO_LButtonUp( lphc, lParam );
1970 return TRUE;
1971 case WM_MOUSEMOVE:
1972 if( lphc->wState & CBF_CAPTURE )
1973 COMBO_MouseMove( lphc, wParam, lParam );
1974 return TRUE;
1976 case WM_MOUSEWHEEL:
1977 if (wParam & (MK_SHIFT | MK_CONTROL))
1978 return DefWindowProcA( hwnd, message, wParam, lParam );
1979 if ((short) HIWORD(wParam) > 0) return SendMessageA(hwnd,WM_KEYDOWN,VK_UP,0);
1980 if ((short) HIWORD(wParam) < 0) return SendMessageA(hwnd,WM_KEYDOWN,VK_DOWN,0);
1981 return TRUE;
1983 /* Combo messages */
1985 case CB_ADDSTRING16:
1986 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
1987 case CB_ADDSTRING:
1988 return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1989 case CB_INSERTSTRING16:
1990 wParam = (INT)(INT16)wParam;
1991 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
1992 case CB_INSERTSTRING:
1993 return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1994 case CB_DELETESTRING16:
1995 case CB_DELETESTRING:
1996 return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1997 case CB_SELECTSTRING16:
1998 wParam = (INT)(INT16)wParam;
1999 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2000 case CB_SELECTSTRING:
2001 return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
2002 case CB_FINDSTRING16:
2003 wParam = (INT)(INT16)wParam;
2004 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2005 case CB_FINDSTRING:
2006 return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2007 case CB_FINDSTRINGEXACT16:
2008 wParam = (INT)(INT16)wParam;
2009 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2010 case CB_FINDSTRINGEXACT:
2011 return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
2012 wParam, lParam );
2013 case CB_SETITEMHEIGHT16:
2014 wParam = (INT)(INT16)wParam; /* signed integer */
2015 case CB_SETITEMHEIGHT:
2016 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2017 case CB_GETITEMHEIGHT16:
2018 wParam = (INT)(INT16)wParam;
2019 case CB_GETITEMHEIGHT:
2020 if( (INT)wParam >= 0 ) /* listbox item */
2021 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2022 return CBGetTextAreaHeight(hwnd, lphc);
2023 case CB_RESETCONTENT16:
2024 case CB_RESETCONTENT:
2025 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
2026 if( CB_HASSTRINGS(lphc) )
2027 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)"" );
2028 else
2029 InvalidateRect(CB_HWND(lphc), NULL, TRUE);
2030 return TRUE;
2031 case CB_INITSTORAGE:
2032 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2033 case CB_GETHORIZONTALEXTENT:
2034 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2035 case CB_SETHORIZONTALEXTENT:
2036 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2037 case CB_GETTOPINDEX:
2038 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2039 case CB_GETLOCALE:
2040 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2041 case CB_SETLOCALE:
2042 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2043 case CB_GETDROPPEDWIDTH:
2044 if( lphc->droppedWidth )
2045 return lphc->droppedWidth;
2046 return lphc->droppedRect.right - lphc->droppedRect.left;
2047 case CB_SETDROPPEDWIDTH:
2048 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2049 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2050 return CB_ERR;
2051 case CB_GETDROPPEDCONTROLRECT16:
2052 lParam = (LPARAM)MapSL(lParam);
2053 if( lParam )
2055 RECT r;
2056 CBGetDroppedControlRect( lphc, &r );
2057 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2059 return CB_OKAY;
2060 case CB_GETDROPPEDCONTROLRECT:
2061 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2062 return CB_OKAY;
2063 case CB_GETDROPPEDSTATE16:
2064 case CB_GETDROPPEDSTATE:
2065 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2066 case CB_DIR16:
2067 lParam = (LPARAM)MapSL(lParam);
2068 /* fall through */
2069 case CB_DIR:
2070 return COMBO_Directory( lphc, (UINT)wParam,
2071 (LPSTR)lParam, (message == CB_DIR));
2072 case CB_SHOWDROPDOWN16:
2073 case CB_SHOWDROPDOWN:
2074 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2076 if( wParam )
2078 if( !(lphc->wState & CBF_DROPPED) )
2079 CBDropDown( lphc );
2081 else
2082 if( lphc->wState & CBF_DROPPED )
2083 CBRollUp( lphc, FALSE, TRUE );
2085 return TRUE;
2086 case CB_GETCOUNT16:
2087 case CB_GETCOUNT:
2088 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2089 case CB_GETCURSEL16:
2090 case CB_GETCURSEL:
2091 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2092 case CB_SETCURSEL16:
2093 wParam = (INT)(INT16)wParam;
2094 case CB_SETCURSEL:
2095 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2096 if( lParam >= 0 )
2097 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2098 if( lphc->wState & CBF_SELCHANGE )
2100 /* no LBN_SELCHANGE in this case, update manually */
2101 if( lphc->wState & CBF_EDIT )
2102 CBUpdateEdit( lphc, (INT)wParam );
2103 else
2104 InvalidateRect(CB_HWND(lphc), &lphc->textRect, TRUE);
2105 lphc->wState &= ~CBF_SELCHANGE;
2107 return lParam;
2108 case CB_GETLBTEXT16:
2109 wParam = (INT)(INT16)wParam;
2110 lParam = (LPARAM)MapSL(lParam);
2111 case CB_GETLBTEXT:
2112 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2113 case CB_GETLBTEXTLEN16:
2114 wParam = (INT)(INT16)wParam;
2115 case CB_GETLBTEXTLEN:
2116 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2117 case CB_GETITEMDATA16:
2118 wParam = (INT)(INT16)wParam;
2119 case CB_GETITEMDATA:
2120 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2121 case CB_SETITEMDATA16:
2122 wParam = (INT)(INT16)wParam;
2123 case CB_SETITEMDATA:
2124 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2125 case CB_GETEDITSEL16:
2126 wParam = lParam = 0; /* just in case */
2127 case CB_GETEDITSEL:
2128 if( lphc->wState & CBF_EDIT )
2130 INT a, b;
2132 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
2133 (wParam) ? wParam : (WPARAM)&a,
2134 (lParam) ? lParam : (LPARAM)&b );
2136 return CB_ERR;
2137 case CB_SETEDITSEL16:
2138 case CB_SETEDITSEL:
2139 if( lphc->wState & CBF_EDIT )
2140 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
2141 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2142 return CB_ERR;
2143 case CB_SETEXTENDEDUI16:
2144 case CB_SETEXTENDEDUI:
2145 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2146 return CB_ERR;
2147 if( wParam )
2148 lphc->wState |= CBF_EUI;
2149 else lphc->wState &= ~CBF_EUI;
2150 return CB_OKAY;
2151 case CB_GETEXTENDEDUI16:
2152 case CB_GETEXTENDEDUI:
2153 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2155 default:
2156 if (message >= WM_USER)
2157 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2158 message - WM_USER, wParam, lParam );
2159 break;
2161 return DefWindowProcA(hwnd, message, wParam, lParam);
2163 return CB_ERR;
2166 /***********************************************************************
2167 * ComboWndProcA
2169 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2170 * window structs.
2172 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2174 WND* pWnd = WIN_FindWndPtr(hwnd);
2175 LRESULT retvalue = ComboWndProc_locked(pWnd,message,wParam,lParam);
2177 WIN_ReleaseWndPtr(pWnd);
2178 return retvalue;