comctl32: Introduce ListBox control.
[wine.git] / dlls / comctl32 / combo.c
blob43b4cb40f1ab11ab22953f34538dbdc20d216440
1 /*
2 * Combo controls
4 * Copyright 1997 Alex Korobka
5 * Copyright (c) 2005 by Frank Richter
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * TODO:
22 * - ComboBox_[GS]etMinVisible()
23 * - CB_GETMINVISIBLE, CB_SETMINVISIBLE
24 * - CB_SETTOPINDEX
27 #include <stdarg.h>
28 #include <string.h>
30 #define OEMRESOURCE
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "uxtheme.h"
37 #include "vssym32.h"
38 #include "commctrl.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 #include "comctl32.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(combo);
46 /* bits in the dwKeyData */
47 #define KEYDATA_ALT 0x2000
48 #define KEYDATA_PREVSTATE 0x4000
51 * Additional combo box definitions
54 #define CB_NOTIFY( lphc, code ) \
55 (SendMessageW((lphc)->owner, WM_COMMAND, \
56 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
58 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
59 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
60 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
61 #define CB_HWND( lphc ) ((lphc)->self)
62 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
64 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
67 * Drawing globals
69 static HBITMAP hComboBmp = 0;
70 static UINT CBitHeight, CBitWidth;
73 * Look and feel dependent "constants"
76 #define COMBO_YBORDERGAP 5
77 #define COMBO_XBORDERSIZE() 2
78 #define COMBO_YBORDERSIZE() 2
79 #define COMBO_EDITBUTTONSPACE() 0
80 #define EDIT_CONTROL_PADDING() 1
82 #define ID_CB_LISTBOX 1000
83 #define ID_CB_EDIT 1001
85 /***********************************************************************
86 * COMBO_Init
88 * Load combo button bitmap.
90 static BOOL COMBO_Init(void)
92 HDC hDC;
94 if( hComboBmp ) return TRUE;
95 if( (hDC = CreateCompatibleDC(0)) )
97 BOOL bRet = FALSE;
98 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
100 BITMAP bm;
101 HBITMAP hPrevB;
102 RECT r;
104 GetObjectW( hComboBmp, sizeof(bm), &bm );
105 CBitHeight = bm.bmHeight;
106 CBitWidth = bm.bmWidth;
108 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
110 hPrevB = SelectObject( hDC, hComboBmp);
111 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
112 InvertRect( hDC, &r );
113 SelectObject( hDC, hPrevB );
114 bRet = TRUE;
116 DeleteDC( hDC );
117 return bRet;
119 return FALSE;
122 /***********************************************************************
123 * COMBO_NCCreate
125 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
127 LPHEADCOMBO lphc;
129 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
131 lphc->self = hwnd;
132 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
134 /* some braindead apps do try to use scrollbar/border flags */
136 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
137 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
140 * We also have to remove the client edge style to make sure
141 * we don't end-up with a non client area.
143 SetWindowLongW( hwnd, GWL_EXSTYLE,
144 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
146 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
147 lphc->dwStyle |= CBS_HASSTRINGS;
148 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
149 lphc->wState |= CBF_NOTIFY;
151 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
152 return TRUE;
154 return FALSE;
157 /***********************************************************************
158 * COMBO_NCDestroy
160 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
163 if( lphc )
165 TRACE("[%p]: freeing storage\n", lphc->self);
167 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
168 DestroyWindow( lphc->hWndLBox );
170 SetWindowLongPtrW( lphc->self, 0, 0 );
171 HeapFree( GetProcessHeap(), 0, lphc );
173 return 0;
176 /***********************************************************************
177 * CBGetTextAreaHeight
179 * This method will calculate the height of the text area of the
180 * combobox.
181 * The height of the text area is set in two ways.
182 * It can be set explicitly through a combobox message or through a
183 * WM_MEASUREITEM callback.
184 * If this is not the case, the height is set to font height + 4px
185 * This height was determined through experimentation.
186 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
188 static INT CBGetTextAreaHeight(
189 HWND hwnd,
190 LPHEADCOMBO lphc)
192 INT iTextItemHeight;
194 if( lphc->editHeight ) /* explicitly set height */
196 iTextItemHeight = lphc->editHeight;
198 else
200 TEXTMETRICW tm;
201 HDC hDC = GetDC(hwnd);
202 HFONT hPrevFont = 0;
203 INT baseUnitY;
205 if (lphc->hFont)
206 hPrevFont = SelectObject( hDC, lphc->hFont );
208 GetTextMetricsW(hDC, &tm);
210 baseUnitY = tm.tmHeight;
212 if( hPrevFont )
213 SelectObject( hDC, hPrevFont );
215 ReleaseDC(hwnd, hDC);
217 iTextItemHeight = baseUnitY + 4;
221 * Check the ownerdraw case if we haven't asked the parent the size
222 * of the item yet.
224 if ( CB_OWNERDRAWN(lphc) &&
225 (lphc->wState & CBF_MEASUREITEM) )
227 MEASUREITEMSTRUCT measureItem;
228 RECT clientRect;
229 INT originalItemHeight = iTextItemHeight;
230 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
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 = id;
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, id, (LPARAM)&measureItem);
249 iTextItemHeight = 6 + measureItem.itemHeight;
252 * Send a second one in the case of a fixed ownerdraw list to calculate the
253 * size of the list items. (we basically do this on behalf of the listbox)
255 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
257 measureItem.CtlType = ODT_COMBOBOX;
258 measureItem.CtlID = id;
259 measureItem.itemID = 0;
260 measureItem.itemWidth = clientRect.right;
261 measureItem.itemHeight = originalItemHeight;
262 measureItem.itemData = 0;
263 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
264 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
268 * Keep the size for the next time
270 lphc->editHeight = iTextItemHeight;
273 return iTextItemHeight;
276 /***********************************************************************
277 * CBForceDummyResize
279 * The dummy resize is used for listboxes that have a popup to trigger
280 * a re-arranging of the contents of the combobox and the recalculation
281 * of the size of the "real" control window.
283 static void CBForceDummyResize(
284 LPHEADCOMBO lphc)
286 RECT windowRect;
287 int newComboHeight;
289 newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
291 GetWindowRect(lphc->self, &windowRect);
294 * We have to be careful, resizing a combobox also has the meaning that the
295 * dropped rect will be resized. In this case, we want to trigger a resize
296 * to recalculate layout but we don't want to change the dropped rectangle
297 * So, we pass the height of text area of control as the height.
298 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
299 * message.
301 SetWindowPos( lphc->self,
302 NULL,
303 0, 0,
304 windowRect.right - windowRect.left,
305 newComboHeight,
306 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
309 /***********************************************************************
310 * CBCalcPlacement
312 * Set up component coordinates given valid lphc->RectCombo.
314 static void CBCalcPlacement(
315 HWND hwnd,
316 LPHEADCOMBO lphc,
317 LPRECT lprEdit,
318 LPRECT lprButton,
319 LPRECT lprLB)
322 * Again, start with the client rectangle.
324 GetClientRect(hwnd, lprEdit);
327 * Remove the borders
329 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
332 * Chop off the bottom part to fit with the height of the text area.
334 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
337 * The button starts the same vertical position as the text area.
339 CopyRect(lprButton, lprEdit);
342 * If the combobox is "simple" there is no button.
344 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
345 lprButton->left = lprButton->right = lprButton->bottom = 0;
346 else
349 * Let's assume the combobox button is the same width as the
350 * scrollbar button.
351 * size the button horizontally and cut-off the text area.
353 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
354 lprEdit->right = lprButton->left;
358 * In the case of a dropdown, there is an additional spacing between the
359 * text area and the button.
361 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
363 lprEdit->right -= COMBO_EDITBUTTONSPACE();
367 * If we have an edit control, we space it away from the borders slightly.
369 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
371 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
375 * Adjust the size of the listbox popup.
377 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
380 * Use the client rectangle to initialize the listbox rectangle
382 GetClientRect(hwnd, lprLB);
385 * Then, chop-off the top part.
387 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
389 else
392 * Make sure the dropped width is as large as the combobox itself.
394 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
396 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
399 * In the case of a dropdown, the popup listbox is offset to the right.
400 * so, we want to make sure it's flush with the right side of the
401 * combobox
403 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
404 lprLB->right -= COMBO_EDITBUTTONSPACE();
406 else
407 lprLB->right = lprLB->left + lphc->droppedWidth;
410 /* don't allow negative window width */
411 if (lprEdit->right < lprEdit->left)
412 lprEdit->right = lprEdit->left;
414 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
416 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
418 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
421 /***********************************************************************
422 * CBGetDroppedControlRect
424 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
426 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
427 of the combo box and the lower right corner of the listbox */
429 GetWindowRect(lphc->self, lpRect);
431 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
432 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
436 /***********************************************************************
437 * COMBO_Create
439 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style )
441 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
442 static const WCHAR editName[] = {'E','d','i','t',0};
444 OpenThemeData( hwnd, WC_COMBOBOXW );
445 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
446 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
448 lphc->owner = hwndParent;
451 * The item height and dropped width are not set when the control
452 * is created.
454 lphc->droppedWidth = lphc->editHeight = 0;
457 * The first time we go through, we want to measure the ownerdraw item
459 lphc->wState |= CBF_MEASUREITEM;
461 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
463 if( lphc->owner || !(style & WS_VISIBLE) )
465 UINT lbeStyle = 0;
466 UINT lbeExStyle = 0;
469 * Initialize the dropped rect to the size of the client area of the
470 * control and then, force all the areas of the combobox to be
471 * recalculated.
473 GetClientRect( hwnd, &lphc->droppedRect );
474 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
477 * Adjust the position of the popup listbox if it's necessary
479 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
481 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
484 * If it's a dropdown, the listbox is offset
486 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
487 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
489 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
490 lphc->droppedRect.bottom = lphc->droppedRect.top;
491 if (lphc->droppedRect.right < lphc->droppedRect.left)
492 lphc->droppedRect.right = lphc->droppedRect.left;
493 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
496 /* create listbox popup */
498 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
499 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
501 if( lphc->dwStyle & CBS_SORT )
502 lbeStyle |= LBS_SORT;
503 if( lphc->dwStyle & CBS_HASSTRINGS )
504 lbeStyle |= LBS_HASSTRINGS;
505 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
506 lbeStyle |= LBS_NOINTEGRALHEIGHT;
507 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
508 lbeStyle |= LBS_DISABLENOSCROLL;
510 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
512 lbeStyle |= WS_VISIBLE;
515 * In win 95 look n feel, the listbox in the simple combobox has
516 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
518 lbeStyle &= ~WS_BORDER;
519 lbeExStyle |= WS_EX_CLIENTEDGE;
521 else
523 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
526 lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
527 lphc->droppedRect.left, lphc->droppedRect.top, lphc->droppedRect.right - lphc->droppedRect.left,
528 lphc->droppedRect.bottom - lphc->droppedRect.top, hwnd, (HMENU)ID_CB_LISTBOX,
529 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
530 if( lphc->hWndLBox )
532 BOOL bEdit = TRUE;
533 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
535 if( lphc->wState & CBF_EDIT )
537 if( lphc->dwStyle & CBS_OEMCONVERT )
538 lbeStyle |= ES_OEMCONVERT;
539 if( lphc->dwStyle & CBS_AUTOHSCROLL )
540 lbeStyle |= ES_AUTOHSCROLL;
541 if( lphc->dwStyle & CBS_LOWERCASE )
542 lbeStyle |= ES_LOWERCASE;
543 else if( lphc->dwStyle & CBS_UPPERCASE )
544 lbeStyle |= ES_UPPERCASE;
546 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
548 lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
549 lphc->textRect.left, lphc->textRect.top,
550 lphc->textRect.right - lphc->textRect.left,
551 lphc->textRect.bottom - lphc->textRect.top,
552 hwnd, (HMENU)ID_CB_EDIT,
553 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
554 if( !lphc->hWndEdit )
555 bEdit = FALSE;
558 if( bEdit )
560 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
562 /* Now do the trick with parent */
563 SetParent(lphc->hWndLBox, HWND_DESKTOP);
565 * If the combo is a dropdown, we must resize the control
566 * to fit only the text area and button. To do this,
567 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
568 * will take care of setting the height for us.
570 CBForceDummyResize(lphc);
573 TRACE("init done\n");
574 return 0;
576 ERR("edit control failure.\n");
577 } else ERR("listbox failure.\n");
578 } else ERR("no owner for visible combo.\n");
580 /* CreateWindow() will send WM_NCDESTROY to cleanup */
582 return -1;
585 /***********************************************************************
586 * CBPaintButton
588 * Paint combo button (normal, pressed, and disabled states).
590 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
592 UINT buttonState = DFCS_SCROLLCOMBOBOX;
594 if( lphc->wState & CBF_NOREDRAW )
595 return;
598 if (lphc->wState & CBF_BUTTONDOWN)
599 buttonState |= DFCS_PUSHED;
601 if (CB_DISABLED(lphc))
602 buttonState |= DFCS_INACTIVE;
604 DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
607 /***********************************************************************
608 * COMBO_PrepareColors
610 * This method will sent the appropriate WM_CTLCOLOR message to
611 * prepare and setup the colors for the combo's DC.
613 * It also returns the brush to use for the background.
615 static HBRUSH COMBO_PrepareColors(
616 LPHEADCOMBO lphc,
617 HDC hDC)
619 HBRUSH hBkgBrush;
622 * Get the background brush for this control.
624 if (CB_DISABLED(lphc))
626 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
627 (WPARAM)hDC, (LPARAM)lphc->self );
630 * We have to change the text color since WM_CTLCOLORSTATIC will
631 * set it to the "enabled" color. This is the same behavior as the
632 * edit control
634 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
636 else
638 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
639 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
640 (WPARAM)hDC, (LPARAM)lphc->self );
644 * Catch errors.
646 if( !hBkgBrush )
647 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
649 return hBkgBrush;
652 /***********************************************************************
653 * CBPaintText
655 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
657 static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint)
659 RECT rectEdit = lphc->textRect;
660 INT id, size = 0;
661 LPWSTR pText = NULL;
663 TRACE("\n");
665 /* follow Windows combobox that sends a bunch of text
666 * inquiries to its listbox while processing WM_PAINT. */
668 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
670 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
671 if (size == LB_ERR)
672 FIXME("LB_ERR probably not handled yet\n");
673 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
675 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
676 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
677 pText[size] = '\0'; /* just in case */
678 } else return;
681 if( lphc->wState & CBF_EDIT )
683 static const WCHAR empty_stringW[] = { 0 };
684 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
685 if( lphc->wState & CBF_FOCUSED )
686 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
688 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
690 /* paint text field ourselves */
691 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
692 UINT itemState = ODS_COMBOBOXEDIT;
693 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
694 HBRUSH hPrevBrush, hBkgBrush;
697 * Give ourselves some space.
699 InflateRect( &rectEdit, -1, -1 );
701 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
702 hPrevBrush = SelectObject( hdc, hBkgBrush );
703 FillRect( hdc, &rectEdit, hBkgBrush );
705 if( CB_OWNERDRAWN(lphc) )
707 DRAWITEMSTRUCT dis;
708 HRGN clipRegion;
709 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
711 /* setup state for DRAWITEM message. Owner will highlight */
712 if ( (lphc->wState & CBF_FOCUSED) &&
713 !(lphc->wState & CBF_DROPPED) )
714 itemState |= ODS_SELECTED | ODS_FOCUS;
716 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
718 dis.CtlType = ODT_COMBOBOX;
719 dis.CtlID = ctlid;
720 dis.hwndItem = lphc->self;
721 dis.itemAction = ODA_DRAWENTIRE;
722 dis.itemID = id;
723 dis.itemState = itemState;
724 dis.hDC = hdc;
725 dis.rcItem = rectEdit;
726 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
729 * Clip the DC and have the parent draw the item.
731 clipRegion = set_control_clipping( hdc, &rectEdit );
733 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
735 SelectClipRgn( hdc, clipRegion );
736 if (clipRegion) DeleteObject( clipRegion );
738 else
740 static const WCHAR empty_stringW[] = { 0 };
742 if ( (lphc->wState & CBF_FOCUSED) &&
743 !(lphc->wState & CBF_DROPPED) ) {
745 /* highlight */
746 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
747 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
748 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
751 ExtTextOutW( hdc,
752 rectEdit.left + 1,
753 rectEdit.top + 1,
754 ETO_OPAQUE | ETO_CLIPPED,
755 &rectEdit,
756 pText ? pText : empty_stringW , size, NULL );
758 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
759 DrawFocusRect( hdc, &rectEdit );
762 if( hPrevFont )
763 SelectObject(hdc, hPrevFont );
765 if( hPrevBrush )
766 SelectObject( hdc, hPrevBrush );
768 if( !hdc_paint )
769 ReleaseDC( lphc->self, hdc );
771 HeapFree( GetProcessHeap(), 0, pText );
774 /***********************************************************************
775 * CBPaintBorder
777 static void CBPaintBorder(
778 HWND hwnd,
779 const HEADCOMBO *lphc,
780 HDC hdc)
782 RECT clientRect;
784 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
786 GetClientRect(hwnd, &clientRect);
788 else
790 clientRect = lphc->textRect;
792 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
793 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
796 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
799 static LRESULT COMBO_ThemedPaint(HTHEME theme, HEADCOMBO *lphc, HDC hdc)
801 int button_state;
802 RECT frame;
804 /* paint border */
805 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
806 GetClientRect(lphc->self, &frame);
807 else
809 frame = lphc->textRect;
810 InflateRect(&frame, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
811 InflateRect(&frame, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
814 DrawThemeBackground(theme, hdc, 0, IsWindowEnabled(lphc->self) ? CBXS_NORMAL : CBXS_DISABLED, &frame, NULL);
816 /* Paint button */
817 if (!IsRectEmpty(&lphc->buttonRect))
819 if (!IsWindowEnabled(lphc->self))
820 button_state = CBXS_DISABLED;
821 else if (lphc->wState & CBF_BUTTONDOWN)
822 button_state = CBXS_PRESSED;
823 else if (lphc->wState & CBF_HOT)
824 button_state = CBXS_HOT;
825 else
826 button_state = CBXS_NORMAL;
827 DrawThemeBackground(theme, hdc, CP_DROPDOWNBUTTON, button_state, &lphc->buttonRect, NULL);
830 if ((lphc->dwStyle & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST)
831 CBPaintText(lphc, hdc);
833 return 0;
836 /***********************************************************************
837 * COMBO_Paint
839 static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc)
841 HBRUSH hPrevBrush, hBkgBrush;
843 TRACE("hdc=%p\n", hdc);
846 * Retrieve the background brush and select it in the
847 * DC.
849 hBkgBrush = COMBO_PrepareColors(lphc, hdc);
850 hPrevBrush = SelectObject(hdc, hBkgBrush);
851 if (!(lphc->wState & CBF_EDIT))
852 FillRect(hdc, &lphc->textRect, hBkgBrush);
855 * In non 3.1 look, there is a sunken border on the combobox
857 CBPaintBorder(lphc->self, lphc, hdc);
859 if (!IsRectEmpty(&lphc->buttonRect))
860 CBPaintButton(lphc, hdc, lphc->buttonRect);
862 /* paint the edit control padding area */
863 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
865 RECT rPadEdit = lphc->textRect;
867 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
869 FrameRect(hdc, &rPadEdit, GetSysColorBrush(COLOR_WINDOW));
872 if (!(lphc->wState & CBF_EDIT))
873 CBPaintText( lphc, hdc );
875 if (hPrevBrush)
876 SelectObject( hdc, hPrevBrush );
878 return 0;
881 /***********************************************************************
882 * CBUpdateLBox
884 * Select listbox entry according to the contents of the edit control.
886 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
888 INT length, idx;
889 LPWSTR pText = NULL;
891 idx = LB_ERR;
892 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
894 if( length > 0 )
895 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
897 TRACE("\t edit text length %i\n", length );
899 if( pText )
901 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
902 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
903 HeapFree( GetProcessHeap(), 0, pText );
906 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
908 /* probably superfluous but Windows sends this too */
909 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
910 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
912 return idx;
915 /***********************************************************************
916 * CBUpdateEdit
918 * Copy a listbox entry to the edit control.
920 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
922 INT length;
923 LPWSTR pText = NULL;
924 static const WCHAR empty_stringW[] = { 0 };
926 TRACE("\t %i\n", index );
928 if( index >= 0 ) /* got an entry */
930 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
931 if( length != LB_ERR)
933 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
935 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
940 if( CB_HASSTRINGS(lphc) )
942 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
943 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
944 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
947 if( lphc->wState & CBF_FOCUSED )
948 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
950 HeapFree( GetProcessHeap(), 0, pText );
953 /***********************************************************************
954 * CBDropDown
956 * Show listbox popup.
958 static void CBDropDown( LPHEADCOMBO lphc )
960 HMONITOR monitor;
961 MONITORINFO mon_info;
962 RECT rect,r;
963 int nItems;
964 int nDroppedHeight;
966 TRACE("[%p]: drop down\n", lphc->self);
968 CB_NOTIFY( lphc, CBN_DROPDOWN );
970 /* set selection */
972 lphc->wState |= CBF_DROPPED;
973 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
975 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
977 /* Update edit only if item is in the list */
978 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
979 CBUpdateEdit( lphc, lphc->droppedIndex );
981 else
983 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
985 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
986 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
987 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
990 /* now set popup position */
991 GetWindowRect( lphc->self, &rect );
994 * If it's a dropdown, the listbox is offset
996 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
997 rect.left += COMBO_EDITBUTTONSPACE();
999 /* if the dropped height is greater than the total height of the dropped
1000 items list, then force the drop down list height to be the total height
1001 of the items in the dropped list */
1003 /* And Remove any extra space (Best Fit) */
1004 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1005 /* if listbox length has been set directly by its handle */
1006 GetWindowRect(lphc->hWndLBox, &r);
1007 if (nDroppedHeight < r.bottom - r.top)
1008 nDroppedHeight = r.bottom - r.top;
1009 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1011 if (nItems > 0)
1013 int nHeight;
1014 int nIHeight;
1016 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1018 nHeight = nIHeight*nItems;
1020 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1021 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1023 if (nDroppedHeight < nHeight)
1025 if (nItems < 5)
1026 nDroppedHeight = (nItems+1)*nIHeight;
1027 else if (nDroppedHeight < 6*nIHeight)
1028 nDroppedHeight = 6*nIHeight;
1032 r.left = rect.left;
1033 r.top = rect.bottom;
1034 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
1035 r.bottom = r.top + nDroppedHeight;
1037 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1038 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1039 mon_info.cbSize = sizeof(mon_info);
1040 GetMonitorInfoW( monitor, &mon_info );
1042 if (r.bottom > mon_info.rcWork.bottom)
1044 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
1045 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1048 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1049 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1052 if( !(lphc->wState & CBF_NOREDRAW) )
1053 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1054 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1056 EnableWindow( lphc->hWndLBox, TRUE );
1057 if (GetCapture() != lphc->self)
1058 SetCapture(lphc->hWndLBox);
1061 /***********************************************************************
1062 * CBRollUp
1064 * Hide listbox popup.
1066 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1068 HWND hWnd = lphc->self;
1070 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1071 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1073 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1075 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1078 if( lphc->wState & CBF_DROPPED )
1080 RECT rect;
1082 lphc->wState &= ~CBF_DROPPED;
1083 ShowWindow( lphc->hWndLBox, SW_HIDE );
1085 if(GetCapture() == lphc->hWndLBox)
1087 ReleaseCapture();
1090 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1092 rect = lphc->buttonRect;
1094 else
1096 if( bButton )
1098 UnionRect( &rect,
1099 &lphc->buttonRect,
1100 &lphc->textRect);
1102 else
1103 rect = lphc->textRect;
1105 bButton = TRUE;
1108 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1109 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1110 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1111 CB_NOTIFY( lphc, CBN_CLOSEUP );
1116 /***********************************************************************
1117 * COMBO_FlipListbox
1119 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1121 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1123 if( lphc->wState & CBF_DROPPED )
1125 CBRollUp( lphc, ok, bRedrawButton );
1126 return FALSE;
1129 CBDropDown( lphc );
1130 return TRUE;
1133 /***********************************************************************
1134 * CBRepaintButton
1136 static void CBRepaintButton( LPHEADCOMBO lphc )
1138 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1139 UpdateWindow(lphc->self);
1142 /***********************************************************************
1143 * COMBO_SetFocus
1145 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1147 if( !(lphc->wState & CBF_FOCUSED) )
1149 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1150 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1152 /* This is wrong. Message sequences seem to indicate that this
1153 is set *after* the notify. */
1154 /* lphc->wState |= CBF_FOCUSED; */
1156 if( !(lphc->wState & CBF_EDIT) )
1157 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1159 CB_NOTIFY( lphc, CBN_SETFOCUS );
1160 lphc->wState |= CBF_FOCUSED;
1164 /***********************************************************************
1165 * COMBO_KillFocus
1167 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1169 HWND hWnd = lphc->self;
1171 if( lphc->wState & CBF_FOCUSED )
1173 CBRollUp( lphc, FALSE, TRUE );
1174 if( IsWindow( hWnd ) )
1176 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1177 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1179 lphc->wState &= ~CBF_FOCUSED;
1181 /* redraw text */
1182 if( !(lphc->wState & CBF_EDIT) )
1183 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1185 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1190 /***********************************************************************
1191 * COMBO_Command
1193 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1195 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1197 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1199 switch( HIWORD(wParam) >> 8 )
1201 case (EN_SETFOCUS >> 8):
1203 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1205 COMBO_SetFocus( lphc );
1206 break;
1208 case (EN_KILLFOCUS >> 8):
1210 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1212 /* NOTE: it seems that Windows' edit control sends an
1213 * undocumented message WM_USER + 0x1B instead of this
1214 * notification (only when it happens to be a part of
1215 * the combo). ?? - AK.
1218 COMBO_KillFocus( lphc );
1219 break;
1222 case (EN_CHANGE >> 8):
1224 * In some circumstances (when the selection of the combobox
1225 * is changed for example) we don't want the EN_CHANGE notification
1226 * to be forwarded to the parent of the combobox. This code
1227 * checks a flag that is set in these occasions and ignores the
1228 * notification.
1230 if (lphc->wState & CBF_NOLBSELECT)
1232 lphc->wState &= ~CBF_NOLBSELECT;
1234 else
1236 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1239 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1240 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1241 break;
1243 case (EN_UPDATE >> 8):
1244 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1245 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1246 break;
1248 case (EN_ERRSPACE >> 8):
1249 CB_NOTIFY( lphc, CBN_ERRSPACE );
1252 else if( lphc->hWndLBox == hWnd )
1254 switch( (short)HIWORD(wParam) )
1256 case LBN_ERRSPACE:
1257 CB_NOTIFY( lphc, CBN_ERRSPACE );
1258 break;
1260 case LBN_DBLCLK:
1261 CB_NOTIFY( lphc, CBN_DBLCLK );
1262 break;
1264 case LBN_SELCHANGE:
1265 case LBN_SELCANCEL:
1267 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1269 /* do not roll up if selection is being tracked
1270 * by arrow keys in the dropdown listbox */
1271 if (!(lphc->wState & CBF_NOROLLUP))
1273 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1275 else lphc->wState &= ~CBF_NOROLLUP;
1277 CB_NOTIFY( lphc, CBN_SELCHANGE );
1279 if( HIWORD(wParam) == LBN_SELCHANGE)
1281 if( lphc->wState & CBF_EDIT )
1282 lphc->wState |= CBF_NOLBSELECT;
1283 CBPaintText( lphc, NULL );
1285 break;
1287 case LBN_SETFOCUS:
1288 case LBN_KILLFOCUS:
1289 /* nothing to do here since ComboLBox always resets the focus to its
1290 * combo/edit counterpart */
1291 break;
1294 return 0;
1297 /***********************************************************************
1298 * COMBO_ItemOp
1300 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1302 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1304 HWND hWnd = lphc->self;
1305 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1307 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1309 switch( msg )
1311 case WM_DELETEITEM:
1313 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1314 lpIS->CtlType = ODT_COMBOBOX;
1315 lpIS->CtlID = id;
1316 lpIS->hwndItem = hWnd;
1317 break;
1319 case WM_DRAWITEM:
1321 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1322 lpIS->CtlType = ODT_COMBOBOX;
1323 lpIS->CtlID = id;
1324 lpIS->hwndItem = hWnd;
1325 break;
1327 case WM_COMPAREITEM:
1329 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1330 lpIS->CtlType = ODT_COMBOBOX;
1331 lpIS->CtlID = id;
1332 lpIS->hwndItem = hWnd;
1333 break;
1335 case WM_MEASUREITEM:
1337 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1338 lpIS->CtlType = ODT_COMBOBOX;
1339 lpIS->CtlID = id;
1340 break;
1343 return SendMessageW(lphc->owner, msg, id, lParam);
1347 /***********************************************************************
1348 * COMBO_GetTextW
1350 static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
1352 INT length;
1354 if( lphc->wState & CBF_EDIT )
1355 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1357 /* get it from the listbox */
1359 if (!count || !buf) return 0;
1360 if( lphc->hWndLBox )
1362 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1363 if (idx == LB_ERR) goto error;
1364 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1365 if (length == LB_ERR) goto error;
1367 /* 'length' is without the terminating character */
1368 if (length >= count)
1370 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1371 if (!lpBuffer) goto error;
1372 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1374 /* truncate if buffer is too short */
1375 if (length != LB_ERR)
1377 lstrcpynW( buf, lpBuffer, count );
1378 length = count;
1380 HeapFree( GetProcessHeap(), 0, lpBuffer );
1382 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1384 if (length == LB_ERR) return 0;
1385 return length;
1388 error: /* error - truncate string, return zero */
1389 buf[0] = 0;
1390 return 0;
1393 /***********************************************************************
1394 * CBResetPos
1396 * This function sets window positions according to the updated
1397 * component placement struct.
1399 static void CBResetPos(
1400 LPHEADCOMBO lphc,
1401 const RECT *rectEdit,
1402 const RECT *rectLB,
1403 BOOL bRedraw)
1405 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1407 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1408 * sizing messages */
1410 if( lphc->wState & CBF_EDIT )
1411 SetWindowPos( lphc->hWndEdit, 0,
1412 rectEdit->left, rectEdit->top,
1413 rectEdit->right - rectEdit->left,
1414 rectEdit->bottom - rectEdit->top,
1415 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1417 SetWindowPos( lphc->hWndLBox, 0,
1418 rectLB->left, rectLB->top,
1419 rectLB->right - rectLB->left,
1420 rectLB->bottom - rectLB->top,
1421 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1423 if( bDrop )
1425 if( lphc->wState & CBF_DROPPED )
1427 lphc->wState &= ~CBF_DROPPED;
1428 ShowWindow( lphc->hWndLBox, SW_HIDE );
1431 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1432 RedrawWindow( lphc->self, NULL, 0,
1433 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1438 /***********************************************************************
1439 * COMBO_Size
1441 static void COMBO_Size( LPHEADCOMBO lphc )
1444 * Those controls are always the same height. So we have to make sure
1445 * they are not resized to another value.
1447 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1449 int newComboHeight, curComboHeight, curComboWidth;
1450 RECT rc;
1452 GetWindowRect(lphc->self, &rc);
1453 curComboHeight = rc.bottom - rc.top;
1454 curComboWidth = rc.right - rc.left;
1455 newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE();
1458 * Resizing a combobox has another side effect, it resizes the dropped
1459 * rectangle as well. However, it does it only if the new height for the
1460 * combobox is more than the height it should have. In other words,
1461 * if the application resizing the combobox only had the intention to resize
1462 * the actual control, for example, to do the layout of a dialog that is
1463 * resized, the height of the dropdown is not changed.
1465 if( curComboHeight > newComboHeight )
1467 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1468 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1469 lphc->droppedRect.top);
1470 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1473 * Restore original height
1475 if( curComboHeight != newComboHeight )
1476 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1477 SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW);
1480 CBCalcPlacement(lphc->self,
1481 lphc,
1482 &lphc->textRect,
1483 &lphc->buttonRect,
1484 &lphc->droppedRect);
1486 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1490 /***********************************************************************
1491 * COMBO_Font
1493 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1496 * Set the font
1498 lphc->hFont = hFont;
1501 * Propagate to owned windows.
1503 if( lphc->wState & CBF_EDIT )
1504 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1505 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1508 * Redo the layout of the control.
1510 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1512 CBCalcPlacement(lphc->self,
1513 lphc,
1514 &lphc->textRect,
1515 &lphc->buttonRect,
1516 &lphc->droppedRect);
1518 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1520 else
1522 CBForceDummyResize(lphc);
1527 /***********************************************************************
1528 * COMBO_SetItemHeight
1530 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1532 LRESULT lRet = CB_ERR;
1534 if( index == -1 ) /* set text field height */
1536 if( height < 32768 )
1538 lphc->editHeight = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1541 * Redo the layout of the control.
1543 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1545 CBCalcPlacement(lphc->self,
1546 lphc,
1547 &lphc->textRect,
1548 &lphc->buttonRect,
1549 &lphc->droppedRect);
1551 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1553 else
1555 CBForceDummyResize(lphc);
1558 lRet = height;
1561 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1562 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1563 return lRet;
1566 /***********************************************************************
1567 * COMBO_SelectString
1569 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText)
1571 INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1572 if( index >= 0 )
1574 if( lphc->wState & CBF_EDIT )
1575 CBUpdateEdit( lphc, index );
1576 else
1578 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1581 return (LRESULT)index;
1584 /***********************************************************************
1585 * COMBO_LButtonDown
1587 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1589 POINT pt;
1590 BOOL bButton;
1591 HWND hWnd = lphc->self;
1593 pt.x = (short)LOWORD(lParam);
1594 pt.y = (short)HIWORD(lParam);
1595 bButton = PtInRect(&lphc->buttonRect, pt);
1597 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1598 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1600 lphc->wState |= CBF_BUTTONDOWN;
1601 if( lphc->wState & CBF_DROPPED )
1603 /* got a click to cancel selection */
1605 lphc->wState &= ~CBF_BUTTONDOWN;
1606 CBRollUp( lphc, TRUE, FALSE );
1607 if( !IsWindow( hWnd ) ) return;
1609 if( lphc->wState & CBF_CAPTURE )
1611 lphc->wState &= ~CBF_CAPTURE;
1612 ReleaseCapture();
1615 else
1617 /* drop down the listbox and start tracking */
1619 lphc->wState |= CBF_CAPTURE;
1620 SetCapture( hWnd );
1621 CBDropDown( lphc );
1623 if( bButton ) CBRepaintButton( lphc );
1627 /***********************************************************************
1628 * COMBO_LButtonUp
1630 * Release capture and stop tracking if needed.
1632 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1634 if( lphc->wState & CBF_CAPTURE )
1636 lphc->wState &= ~CBF_CAPTURE;
1637 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1639 INT index = CBUpdateLBox( lphc, TRUE );
1640 /* Update edit only if item is in the list */
1641 if(index >= 0)
1643 lphc->wState |= CBF_NOLBSELECT;
1644 CBUpdateEdit( lphc, index );
1645 lphc->wState &= ~CBF_NOLBSELECT;
1648 ReleaseCapture();
1649 SetCapture(lphc->hWndLBox);
1652 if( lphc->wState & CBF_BUTTONDOWN )
1654 lphc->wState &= ~CBF_BUTTONDOWN;
1655 CBRepaintButton( lphc );
1659 /***********************************************************************
1660 * COMBO_MouseMove
1662 * Two things to do - track combo button and release capture when
1663 * pointer goes into the listbox.
1665 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1667 POINT pt;
1668 RECT lbRect;
1670 pt.x = (short)LOWORD(lParam);
1671 pt.y = (short)HIWORD(lParam);
1673 if( lphc->wState & CBF_BUTTONDOWN )
1675 BOOL bButton;
1677 bButton = PtInRect(&lphc->buttonRect, pt);
1679 if( !bButton )
1681 lphc->wState &= ~CBF_BUTTONDOWN;
1682 CBRepaintButton( lphc );
1686 GetClientRect( lphc->hWndLBox, &lbRect );
1687 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1688 if( PtInRect(&lbRect, pt) )
1690 lphc->wState &= ~CBF_CAPTURE;
1691 ReleaseCapture();
1692 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1694 /* hand over pointer tracking */
1695 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1699 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1701 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1702 return FALSE;
1704 pcbi->rcItem = lphc->textRect;
1705 pcbi->rcButton = lphc->buttonRect;
1706 pcbi->stateButton = 0;
1707 if (lphc->wState & CBF_BUTTONDOWN)
1708 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1709 if (IsRectEmpty(&lphc->buttonRect))
1710 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1711 pcbi->hwndCombo = lphc->self;
1712 pcbi->hwndItem = lphc->hWndEdit;
1713 pcbi->hwndList = lphc->hWndLBox;
1714 return TRUE;
1717 LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1719 HEADCOMBO *lphc = (HEADCOMBO *)GetWindowLongPtrW( hwnd, 0 );
1720 HTHEME theme;
1722 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", hwnd, message, wParam, lParam );
1724 if (!IsWindow(hwnd)) return 0;
1726 if (lphc || message == WM_NCCREATE)
1727 switch(message)
1729 case WM_NCCREATE:
1731 LONG style = ((CREATESTRUCTW *)lParam)->style;
1732 return COMBO_NCCreate(hwnd, style);
1735 case WM_NCDESTROY:
1736 COMBO_NCDestroy(lphc);
1737 break;/* -> DefWindowProc */
1739 case WM_CREATE:
1741 HWND hwndParent;
1742 LONG style;
1744 hwndParent = ((CREATESTRUCTW *)lParam)->hwndParent;
1745 style = ((CREATESTRUCTW *)lParam)->style;
1746 return COMBO_Create(hwnd, lphc, hwndParent, style);
1749 case WM_DESTROY:
1750 theme = GetWindowTheme( hwnd );
1751 CloseThemeData( theme );
1752 break;
1754 case WM_THEMECHANGED:
1755 theme = GetWindowTheme( hwnd );
1756 CloseThemeData( theme );
1757 OpenThemeData( hwnd, WC_COMBOBOXW );
1758 break;
1760 case WM_PRINTCLIENT:
1761 case WM_PAINT:
1763 LRESULT ret = 0;
1764 PAINTSTRUCT ps;
1765 HDC hdc;
1767 hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1769 if (hdc && !(lphc->wState & CBF_NOREDRAW))
1771 HTHEME theme = GetWindowTheme(hwnd);
1773 if (theme)
1774 ret = COMBO_ThemedPaint(theme, lphc, hdc);
1775 else
1776 ret = COMBO_Paint(lphc, hdc);
1779 if (!wParam)
1780 EndPaint(hwnd, &ps);
1782 return ret;
1784 case WM_ERASEBKGND:
1785 /* do all painting in WM_PAINT like Windows does */
1786 return 1;
1788 case WM_GETDLGCODE:
1790 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1791 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1793 int vk = (int)((LPMSG)lParam)->wParam;
1795 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1796 result |= DLGC_WANTMESSAGE;
1798 return result;
1801 case WM_SIZE:
1802 if (lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE))
1803 COMBO_Size( lphc );
1804 return TRUE;
1806 case WM_SETFONT:
1807 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1808 return TRUE;
1810 case WM_GETFONT:
1811 return (LRESULT)lphc->hFont;
1813 case WM_SETFOCUS:
1814 if (lphc->wState & CBF_EDIT)
1816 SetFocus( lphc->hWndEdit );
1817 /* The first time focus is received, select all the text */
1818 if (!(lphc->wState & CBF_BEENFOCUSED))
1820 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1821 lphc->wState |= CBF_BEENFOCUSED;
1824 else
1825 COMBO_SetFocus( lphc );
1826 return TRUE;
1828 case WM_KILLFOCUS:
1830 HWND hwndFocus = (HWND)wParam;
1831 if (!hwndFocus || (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox))
1832 COMBO_KillFocus( lphc );
1833 return TRUE;
1836 case WM_COMMAND:
1837 return COMBO_Command( lphc, wParam, (HWND)lParam );
1839 case WM_GETTEXT:
1840 return COMBO_GetText( lphc, wParam, (LPWSTR)lParam );
1842 case WM_SETTEXT:
1843 case WM_GETTEXTLENGTH:
1844 case WM_CLEAR:
1845 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1847 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1848 if (j == -1) return 0;
1849 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1851 else if ( lphc->wState & CBF_EDIT )
1853 LRESULT ret;
1854 lphc->wState |= CBF_NOEDITNOTIFY;
1855 ret = SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1856 lphc->wState &= ~CBF_NOEDITNOTIFY;
1857 return ret;
1859 else
1860 return CB_ERR;
1862 case WM_CUT:
1863 case WM_PASTE:
1864 case WM_COPY:
1865 if (lphc->wState & CBF_EDIT)
1866 return SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1867 else return CB_ERR;
1869 case WM_DRAWITEM:
1870 case WM_DELETEITEM:
1871 case WM_COMPAREITEM:
1872 case WM_MEASUREITEM:
1873 return COMBO_ItemOp(lphc, message, lParam);
1875 case WM_ENABLE:
1876 if (lphc->wState & CBF_EDIT)
1877 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1878 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1880 /* Force the control to repaint when the enabled state changes. */
1881 InvalidateRect(lphc->self, NULL, TRUE);
1882 return TRUE;
1884 case WM_SETREDRAW:
1885 if (wParam)
1886 lphc->wState &= ~CBF_NOREDRAW;
1887 else
1888 lphc->wState |= CBF_NOREDRAW;
1890 if ( lphc->wState & CBF_EDIT )
1891 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1892 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1893 return 0;
1895 case WM_SYSKEYDOWN:
1896 if ( KEYDATA_ALT & HIWORD(lParam) )
1897 if( wParam == VK_UP || wParam == VK_DOWN )
1898 COMBO_FlipListbox( lphc, FALSE, FALSE );
1899 return 0;
1901 case WM_KEYDOWN:
1902 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1903 (lphc->wState & CBF_DROPPED))
1905 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1906 return TRUE;
1908 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1910 COMBO_FlipListbox( lphc, FALSE, FALSE );
1911 return TRUE;
1913 /* fall through */
1914 case WM_CHAR:
1915 case WM_IME_CHAR:
1917 HWND hwndTarget;
1919 if ( lphc->wState & CBF_EDIT )
1920 hwndTarget = lphc->hWndEdit;
1921 else
1922 hwndTarget = lphc->hWndLBox;
1924 return SendMessageW(hwndTarget, message, wParam, lParam);
1927 case WM_LBUTTONDOWN:
1928 if ( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1929 if ( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1930 return TRUE;
1932 case WM_LBUTTONUP:
1933 COMBO_LButtonUp( lphc );
1934 return TRUE;
1936 case WM_MOUSEMOVE:
1937 if (!IsRectEmpty(&lphc->buttonRect))
1939 POINT pt;
1941 pt.x = (short)LOWORD(lParam);
1942 pt.y = (short)HIWORD(lParam);
1944 if (PtInRect(&lphc->buttonRect, pt))
1946 if (!(lphc->wState & CBF_HOT))
1948 lphc->wState |= CBF_HOT;
1949 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1952 else if (lphc->wState & CBF_HOT)
1954 lphc->wState &= ~CBF_HOT;
1955 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1959 if ( lphc->wState & CBF_CAPTURE )
1960 COMBO_MouseMove( lphc, wParam, lParam );
1961 return TRUE;
1963 case WM_MOUSEWHEEL:
1964 if (wParam & (MK_SHIFT | MK_CONTROL))
1965 return DefWindowProcW(hwnd, message, wParam, lParam);
1967 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1968 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1969 return TRUE;
1971 /* Combo messages */
1972 case CB_ADDSTRING:
1973 if (lphc->dwStyle & CBS_LOWERCASE)
1974 CharLowerW((LPWSTR)lParam);
1975 else if (lphc->dwStyle & CBS_UPPERCASE)
1976 CharUpperW((LPWSTR)lParam);
1977 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1979 case CB_INSERTSTRING:
1980 if (lphc->dwStyle & CBS_LOWERCASE)
1981 CharLowerW((LPWSTR)lParam);
1982 else if (lphc->dwStyle & CBS_UPPERCASE)
1983 CharUpperW((LPWSTR)lParam);
1984 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1986 case CB_DELETESTRING:
1987 return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1989 case CB_SELECTSTRING:
1990 return COMBO_SelectString(lphc, (INT)wParam, lParam);
1992 case CB_FINDSTRING:
1993 return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1995 case CB_FINDSTRINGEXACT:
1996 return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1998 case CB_SETITEMHEIGHT:
1999 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2001 case CB_GETITEMHEIGHT:
2002 if ((INT)wParam >= 0) /* listbox item */
2003 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2004 return CBGetTextAreaHeight(hwnd, lphc);
2006 case CB_RESETCONTENT:
2007 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2009 if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc))
2011 static const WCHAR empty_stringW[] = { 0 };
2012 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2014 else
2015 InvalidateRect(lphc->self, NULL, TRUE);
2016 return TRUE;
2018 case CB_INITSTORAGE:
2019 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2021 case CB_GETHORIZONTALEXTENT:
2022 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2024 case CB_SETHORIZONTALEXTENT:
2025 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2027 case CB_GETTOPINDEX:
2028 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2030 case CB_GETLOCALE:
2031 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2033 case CB_SETLOCALE:
2034 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2036 case CB_SETDROPPEDWIDTH:
2037 if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768)
2038 return CB_ERR;
2040 /* new value must be higher than combobox width */
2041 if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2042 lphc->droppedWidth = wParam;
2043 else if (wParam)
2044 lphc->droppedWidth = 0;
2046 /* recalculate the combobox area */
2047 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
2049 /* fall through */
2050 case CB_GETDROPPEDWIDTH:
2051 if (lphc->droppedWidth)
2052 return lphc->droppedWidth;
2053 return lphc->droppedRect.right - lphc->droppedRect.left;
2055 case CB_GETDROPPEDCONTROLRECT:
2056 if (lParam)
2057 CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2058 return CB_OKAY;
2060 case CB_GETDROPPEDSTATE:
2061 return (lphc->wState & CBF_DROPPED) != 0;
2063 case CB_DIR:
2064 return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam);
2066 case CB_SHOWDROPDOWN:
2067 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
2069 if (wParam)
2071 if (!(lphc->wState & CBF_DROPPED))
2072 CBDropDown( lphc );
2074 else if (lphc->wState & CBF_DROPPED)
2075 CBRollUp( lphc, FALSE, TRUE );
2077 return TRUE;
2079 case CB_GETCOUNT:
2080 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2082 case CB_GETCURSEL:
2083 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2085 case CB_SETCURSEL:
2086 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2087 if (lParam >= 0)
2088 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2090 /* no LBN_SELCHANGE in this case, update manually */
2091 CBPaintText(lphc, NULL);
2092 lphc->wState &= ~CBF_SELCHANGE;
2093 return lParam;
2095 case CB_GETLBTEXT:
2096 return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2098 case CB_GETLBTEXTLEN:
2099 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2101 case CB_GETITEMDATA:
2102 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2104 case CB_SETITEMDATA:
2105 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2107 case CB_GETEDITSEL:
2108 /* Edit checks passed parameters itself */
2109 if (lphc->wState & CBF_EDIT)
2110 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2111 return CB_ERR;
2113 case CB_SETEDITSEL:
2114 if (lphc->wState & CBF_EDIT)
2115 return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2116 return CB_ERR;
2118 case CB_SETEXTENDEDUI:
2119 if (CB_GETTYPE(lphc) == CBS_SIMPLE )
2120 return CB_ERR;
2121 if (wParam)
2122 lphc->wState |= CBF_EUI;
2123 else
2124 lphc->wState &= ~CBF_EUI;
2125 return CB_OKAY;
2127 case CB_GETEXTENDEDUI:
2128 return (lphc->wState & CBF_EUI) != 0;
2130 case CB_GETCOMBOBOXINFO:
2131 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2133 case CB_LIMITTEXT:
2134 if (lphc->wState & CBF_EDIT)
2135 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2136 return TRUE;
2138 default:
2139 if (message >= WM_USER)
2140 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n", message - WM_USER, wParam, lParam );
2141 break;
2144 return DefWindowProcW(hwnd, message, wParam, lParam);
2147 void COMBO_Register(void)
2149 WNDCLASSW wndClass;
2151 memset(&wndClass, 0, sizeof(wndClass));
2152 wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2153 wndClass.lpfnWndProc = COMBO_WindowProc;
2154 wndClass.cbClsExtra = 0;
2155 wndClass.cbWndExtra = sizeof(HEADCOMBO *);
2156 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2157 wndClass.hbrBackground = NULL;
2158 wndClass.lpszClassName = WC_COMBOBOXW;
2159 RegisterClassW(&wndClass);