uiautomationcore: Implement UiaGetPropertyValue.
[wine.git] / dlls / comctl32 / combo.c
blob5876c2acbb8d691efd5e1c464c1ad3b5c395848a
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
23 #include <stdarg.h>
24 #include <string.h>
26 #define OEMRESOURCE
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "uxtheme.h"
33 #include "vssym32.h"
34 #include "commctrl.h"
35 #include "wine/debug.h"
36 #include "wine/heap.h"
38 #include "comctl32.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(combo);
42 /* bits in the dwKeyData */
43 #define KEYDATA_ALT 0x2000
44 #define KEYDATA_PREVSTATE 0x4000
47 * Additional combo box definitions
50 #define CB_NOTIFY( lphc, code ) \
51 (SendMessageW((lphc)->owner, WM_COMMAND, \
52 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
54 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
55 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
56 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
57 #define CB_HWND( lphc ) ((lphc)->self)
58 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
60 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
63 * Drawing globals
65 static HBITMAP hComboBmp = 0;
66 static UINT CBitHeight, CBitWidth;
69 * Look and feel dependent "constants"
72 #define COMBO_YBORDERGAP 5
73 #define COMBO_XBORDERSIZE() 2
74 #define COMBO_YBORDERSIZE() 2
75 #define COMBO_EDITBUTTONSPACE() 0
76 #define EDIT_CONTROL_PADDING() 1
78 #define ID_CB_LISTBOX 1000
79 #define ID_CB_EDIT 1001
81 static void CBCalcPlacement(HEADCOMBO *combo);
82 static void CBResetPos(HEADCOMBO *combo);
84 /***********************************************************************
85 * COMBO_Init
87 * Load combo button bitmap.
89 static BOOL COMBO_Init(void)
91 HDC hDC;
93 if( hComboBmp ) return TRUE;
94 if( (hDC = CreateCompatibleDC(0)) )
96 BOOL bRet = FALSE;
97 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
99 BITMAP bm;
100 HBITMAP hPrevB;
101 RECT r;
103 GetObjectW( hComboBmp, sizeof(bm), &bm );
104 CBitHeight = bm.bmHeight;
105 CBitWidth = bm.bmWidth;
107 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
109 hPrevB = SelectObject( hDC, hComboBmp);
110 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
111 InvertRect( hDC, &r );
112 SelectObject( hDC, hPrevB );
113 bRet = TRUE;
115 DeleteDC( hDC );
116 return bRet;
118 return FALSE;
121 /***********************************************************************
122 * COMBO_NCCreate
124 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
126 HEADCOMBO *lphc;
128 if (COMBO_Init() && (lphc = heap_alloc_zero(sizeof(*lphc))))
130 lphc->self = hwnd;
131 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
133 /* some braindead apps do try to use scrollbar/border flags */
135 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
136 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
139 * We also have to remove the client edge style to make sure
140 * we don't end-up with a non client area.
142 SetWindowLongW( hwnd, GWL_EXSTYLE,
143 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
145 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
146 lphc->dwStyle |= CBS_HASSTRINGS;
147 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
148 lphc->wState |= CBF_NOTIFY;
150 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
151 return TRUE;
153 return FALSE;
156 /***********************************************************************
157 * COMBO_NCDestroy
159 static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc )
161 if (lphc)
163 TRACE("[%p]: freeing storage\n", lphc->self);
165 if ( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
166 DestroyWindow( lphc->hWndLBox );
168 SetWindowLongPtrW( lphc->self, 0, 0 );
169 heap_free( lphc );
172 return 0;
175 static INT combo_get_text_height(const HEADCOMBO *combo)
177 HDC hdc = GetDC(combo->self);
178 HFONT prev_font = 0;
179 TEXTMETRICW tm;
181 if (combo->hFont)
182 prev_font = SelectObject(hdc, combo->hFont);
184 GetTextMetricsW(hdc, &tm);
186 if (prev_font)
187 SelectObject(hdc, prev_font);
189 ReleaseDC(combo->self, hdc);
191 return tm.tmHeight + 4;
194 /***********************************************************************
195 * CBGetTextAreaHeight
197 * This method will calculate the height of the text area of the
198 * combobox.
199 * The height of the text area is set in two ways.
200 * It can be set explicitly through a combobox message or through a
201 * WM_MEASUREITEM callback.
202 * If this is not the case, the height is set to font height + 4px
203 * This height was determined through experimentation.
204 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
206 static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
208 INT item_height, text_height;
210 if (clip_item_height && !CB_OWNERDRAWN(lphc))
212 text_height = combo_get_text_height(lphc);
213 if (lphc->item_height < text_height)
214 lphc->item_height = text_height;
216 item_height = lphc->item_height;
220 * Check the ownerdraw case if we haven't asked the parent the size
221 * of the item yet.
223 if ( CB_OWNERDRAWN(lphc) &&
224 (lphc->wState & CBF_MEASUREITEM) )
226 MEASUREITEMSTRUCT measureItem;
227 RECT clientRect;
228 INT originalItemHeight = item_height;
229 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
232 * We use the client rect for the width of the item.
234 GetClientRect(lphc->self, &clientRect);
236 lphc->wState &= ~CBF_MEASUREITEM;
239 * Send a first one to measure the size of the text area
241 measureItem.CtlType = ODT_COMBOBOX;
242 measureItem.CtlID = id;
243 measureItem.itemID = -1;
244 measureItem.itemWidth = clientRect.right;
245 measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */
246 measureItem.itemData = 0;
247 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
248 item_height = 6 + measureItem.itemHeight;
251 * Send a second one in the case of a fixed ownerdraw list to calculate the
252 * size of the list items. (we basically do this on behalf of the listbox)
254 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
256 measureItem.CtlType = ODT_COMBOBOX;
257 measureItem.CtlID = id;
258 measureItem.itemID = 0;
259 measureItem.itemWidth = clientRect.right;
260 measureItem.itemHeight = originalItemHeight;
261 measureItem.itemData = 0;
262 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
263 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
267 * Keep the size for the next time
269 lphc->item_height = item_height;
272 return item_height;
275 /***********************************************************************
276 * CBForceDummyResize
278 * The dummy resize is used for listboxes that have a popup to trigger
279 * a re-arranging of the contents of the combobox and the recalculation
280 * of the size of the "real" control window.
282 static void CBForceDummyResize(LPHEADCOMBO lphc)
284 RECT windowRect;
285 int newComboHeight;
287 newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE();
289 GetWindowRect(lphc->self, &windowRect);
292 * We have to be careful, resizing a combobox also has the meaning that the
293 * dropped rect will be resized. In this case, we want to trigger a resize
294 * to recalculate layout but we don't want to change the dropped rectangle
295 * So, we pass the height of text area of control as the height.
296 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
297 * message.
299 lphc->wState |= CBF_NORESIZE;
300 SetWindowPos( lphc->self,
301 NULL,
302 0, 0,
303 windowRect.right - windowRect.left,
304 newComboHeight,
305 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
306 lphc->wState &= ~CBF_NORESIZE;
308 CBCalcPlacement(lphc);
309 CBResetPos(lphc);
312 /***********************************************************************
313 * CBCalcPlacement
315 * Set up component coordinates given valid lphc->RectCombo.
317 static void CBCalcPlacement(HEADCOMBO *combo)
319 /* Start with the client rectangle. */
320 GetClientRect(combo->self, &combo->textRect);
322 /* Remove the borders */
323 InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
325 /* Chop off the bottom part to fit with the height of the text area. */
326 combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
328 /* The button starts the same vertical position as the text area. */
329 combo->buttonRect = combo->textRect;
331 /* If the combobox is "simple" there is no button. */
332 if (CB_GETTYPE(combo) == CBS_SIMPLE)
333 combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0;
334 else
337 * Let's assume the combobox button is the same width as the
338 * scrollbar button.
339 * size the button horizontally and cut-off the text area.
341 combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
342 combo->textRect.right = combo->buttonRect.left;
345 /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
346 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
347 combo->textRect.right -= COMBO_EDITBUTTONSPACE();
349 /* If we have an edit control, we space it away from the borders slightly. */
350 if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
351 InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
353 /* Adjust the size of the listbox popup. */
354 if (CB_GETTYPE(combo) == CBS_SIMPLE)
356 GetClientRect(combo->self, &combo->droppedRect);
357 combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
359 else
361 /* Make sure the dropped width is as large as the combobox itself. */
362 if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
364 combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
366 /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
367 with the right side of the combobox */
368 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
369 combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
371 else
372 combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
375 /* Disallow negative window width */
376 if (combo->textRect.right < combo->textRect.left)
377 combo->textRect.right = combo->textRect.left;
379 TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
380 wine_dbgstr_rect(&combo->droppedRect));
383 /***********************************************************************
384 * CBGetDroppedControlRect
386 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
388 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
389 of the combo box and the lower right corner of the listbox */
391 GetWindowRect(lphc->self, lpRect);
393 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
394 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
398 /***********************************************************************
399 * COMBO_Create
401 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style )
403 OpenThemeData( hwnd, WC_COMBOBOXW );
404 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
405 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
407 lphc->owner = hwndParent;
409 lphc->droppedWidth = 0;
411 lphc->item_height = combo_get_text_height(lphc);
414 * The first time we go through, we want to measure the ownerdraw item
416 lphc->wState |= CBF_MEASUREITEM;
419 * Per default the comctl32 version of combo shows up to 30 items
421 lphc->visibleItems = 30;
423 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
425 if( lphc->owner || !(style & WS_VISIBLE) )
427 UINT lbeStyle = 0;
428 UINT lbeExStyle = 0;
431 * Initialize the dropped rect to the size of the client area of the
432 * control and then, force all the areas of the combobox to be
433 * recalculated.
435 GetClientRect( hwnd, &lphc->droppedRect );
436 CBCalcPlacement(lphc);
439 * Adjust the position of the popup listbox if it's necessary
441 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
443 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
446 * If it's a dropdown, the listbox is offset
448 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
449 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
451 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
452 lphc->droppedRect.bottom = lphc->droppedRect.top;
453 if (lphc->droppedRect.right < lphc->droppedRect.left)
454 lphc->droppedRect.right = lphc->droppedRect.left;
455 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
458 /* create listbox popup */
460 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
461 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
463 if( lphc->dwStyle & CBS_SORT )
464 lbeStyle |= LBS_SORT;
465 if( lphc->dwStyle & CBS_HASSTRINGS )
466 lbeStyle |= LBS_HASSTRINGS;
467 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
468 lbeStyle |= LBS_NOINTEGRALHEIGHT;
469 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
470 lbeStyle |= LBS_DISABLENOSCROLL;
472 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
474 lbeStyle |= WS_VISIBLE;
477 * In win 95 look n feel, the listbox in the simple combobox has
478 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
480 lbeStyle &= ~WS_BORDER;
481 lbeExStyle |= WS_EX_CLIENTEDGE;
483 else
485 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
488 lphc->hWndLBox = CreateWindowExW(lbeExStyle, L"ComboLBox", NULL, lbeStyle,
489 lphc->droppedRect.left, lphc->droppedRect.top, lphc->droppedRect.right - lphc->droppedRect.left,
490 lphc->droppedRect.bottom - lphc->droppedRect.top, hwnd, (HMENU)ID_CB_LISTBOX,
491 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
492 if( lphc->hWndLBox )
494 BOOL bEdit = TRUE;
495 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
497 if( lphc->wState & CBF_EDIT )
499 if( lphc->dwStyle & CBS_OEMCONVERT )
500 lbeStyle |= ES_OEMCONVERT;
501 if( lphc->dwStyle & CBS_AUTOHSCROLL )
502 lbeStyle |= ES_AUTOHSCROLL;
503 if( lphc->dwStyle & CBS_LOWERCASE )
504 lbeStyle |= ES_LOWERCASE;
505 else if( lphc->dwStyle & CBS_UPPERCASE )
506 lbeStyle |= ES_UPPERCASE;
508 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
510 lphc->hWndEdit = CreateWindowExW(0, WC_EDITW, NULL, lbeStyle,
511 lphc->textRect.left, lphc->textRect.top,
512 lphc->textRect.right - lphc->textRect.left,
513 lphc->textRect.bottom - lphc->textRect.top,
514 hwnd, (HMENU)ID_CB_EDIT,
515 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
516 if( !lphc->hWndEdit )
517 bEdit = FALSE;
520 if( bEdit )
522 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
524 /* Now do the trick with parent */
525 SetParent(lphc->hWndLBox, HWND_DESKTOP);
527 * If the combo is a dropdown, we must resize the control
528 * to fit only the text area and button. To do this,
529 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
530 * will take care of setting the height for us.
532 CBForceDummyResize(lphc);
535 TRACE("init done\n");
536 return 0;
538 ERR("edit control failure.\n");
539 } else ERR("listbox failure.\n");
540 } else ERR("no owner for visible combo.\n");
542 /* CreateWindow() will send WM_NCDESTROY to cleanup */
544 return -1;
547 /***********************************************************************
548 * CBPaintButton
550 * Paint combo button (normal, pressed, and disabled states).
552 static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
554 UINT buttonState = DFCS_SCROLLCOMBOBOX;
556 if (IsRectEmpty(&lphc->buttonRect))
557 return;
559 if( lphc->wState & CBF_NOREDRAW )
560 return;
563 if (lphc->wState & CBF_BUTTONDOWN)
564 buttonState |= DFCS_PUSHED;
566 if (CB_DISABLED(lphc))
567 buttonState |= DFCS_INACTIVE;
569 DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
572 /***********************************************************************
573 * COMBO_PrepareColors
575 * This method will sent the appropriate WM_CTLCOLOR message to
576 * prepare and setup the colors for the combo's DC.
578 * It also returns the brush to use for the background.
580 static HBRUSH COMBO_PrepareColors(
581 LPHEADCOMBO lphc,
582 HDC hDC)
584 HBRUSH hBkgBrush;
587 * Get the background brush for this control.
589 if (CB_DISABLED(lphc))
591 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
592 (WPARAM)hDC, (LPARAM)lphc->self );
595 * We have to change the text color since WM_CTLCOLORSTATIC will
596 * set it to the "enabled" color. This is the same behavior as the
597 * edit control
599 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
601 else
603 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
604 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
605 (WPARAM)hDC, (LPARAM)lphc->self );
609 * Catch errors.
611 if( !hBkgBrush )
612 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
614 return hBkgBrush;
617 /***********************************************************************
618 * CBPaintText
620 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
622 static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint)
624 RECT rectEdit = lphc->textRect;
625 INT id, size = 0;
626 LPWSTR pText = NULL;
628 TRACE("\n");
630 /* follow Windows combobox that sends a bunch of text
631 * inquiries to its listbox while processing WM_PAINT. */
633 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
635 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
636 if (size == LB_ERR)
637 FIXME("LB_ERR probably not handled yet\n");
638 if ((pText = heap_alloc((size + 1) * sizeof(WCHAR))))
640 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
641 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
642 pText[size] = '\0'; /* just in case */
643 } else return;
646 if( lphc->wState & CBF_EDIT )
648 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : L"" );
649 if( lphc->wState & CBF_FOCUSED )
650 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
652 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
654 /* paint text field ourselves */
655 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
656 UINT itemState = ODS_COMBOBOXEDIT;
657 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
658 HBRUSH hPrevBrush, hBkgBrush;
661 * Give ourselves some space.
663 InflateRect( &rectEdit, -1, -1 );
665 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
666 hPrevBrush = SelectObject( hdc, hBkgBrush );
667 FillRect( hdc, &rectEdit, hBkgBrush );
669 if( CB_OWNERDRAWN(lphc) )
671 DRAWITEMSTRUCT dis;
672 HRGN clipRegion;
673 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
675 /* setup state for DRAWITEM message. Owner will highlight */
676 if ( (lphc->wState & CBF_FOCUSED) &&
677 !(lphc->wState & CBF_DROPPED) )
678 itemState |= ODS_SELECTED | ODS_FOCUS;
680 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
682 dis.CtlType = ODT_COMBOBOX;
683 dis.CtlID = ctlid;
684 dis.hwndItem = lphc->self;
685 dis.itemAction = ODA_DRAWENTIRE;
686 dis.itemID = id;
687 dis.itemState = itemState;
688 dis.hDC = hdc;
689 dis.rcItem = rectEdit;
690 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
693 * Clip the DC and have the parent draw the item.
695 clipRegion = set_control_clipping( hdc, &rectEdit );
697 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
699 SelectClipRgn( hdc, clipRegion );
700 if (clipRegion) DeleteObject( clipRegion );
702 else
704 if ( (lphc->wState & CBF_FOCUSED) &&
705 !(lphc->wState & CBF_DROPPED) ) {
707 /* highlight */
708 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
709 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
710 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
713 ExtTextOutW( hdc,
714 rectEdit.left + 1,
715 rectEdit.top + 1,
716 ETO_OPAQUE | ETO_CLIPPED,
717 &rectEdit,
718 pText ? pText : L"" , size, NULL );
720 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
721 DrawFocusRect( hdc, &rectEdit );
724 if( hPrevFont )
725 SelectObject(hdc, hPrevFont );
727 if( hPrevBrush )
728 SelectObject( hdc, hPrevBrush );
730 if( !hdc_paint )
731 ReleaseDC( lphc->self, hdc );
734 heap_free(pText);
737 /***********************************************************************
738 * CBPaintBorder
740 static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc)
742 RECT clientRect;
744 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
746 GetClientRect(lphc->self, &clientRect);
748 else
750 clientRect = lphc->textRect;
752 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
753 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
756 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
759 static LRESULT COMBO_ThemedPaint(HTHEME theme, HEADCOMBO *lphc, HDC hdc)
761 int button_state;
762 RECT frame;
764 /* paint border */
765 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
766 GetClientRect(lphc->self, &frame);
767 else
769 frame = lphc->textRect;
770 InflateRect(&frame, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
771 InflateRect(&frame, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
774 DrawThemeBackground(theme, hdc, 0, IsWindowEnabled(lphc->self) ? CBXS_NORMAL : CBXS_DISABLED, &frame, NULL);
776 /* Paint button */
777 if (!IsRectEmpty(&lphc->buttonRect))
779 if (!IsWindowEnabled(lphc->self))
780 button_state = CBXS_DISABLED;
781 else if (lphc->wState & CBF_BUTTONDOWN)
782 button_state = CBXS_PRESSED;
783 else if (lphc->wState & CBF_HOT)
784 button_state = CBXS_HOT;
785 else
786 button_state = CBXS_NORMAL;
787 DrawThemeBackground(theme, hdc, CP_DROPDOWNBUTTON, button_state, &lphc->buttonRect, NULL);
790 if ((lphc->dwStyle & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST)
791 CBPaintText(lphc, hdc);
792 else
793 InvalidateRect(lphc->hWndEdit, NULL, TRUE);
795 return 0;
798 /***********************************************************************
799 * COMBO_Paint
801 static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc)
803 HBRUSH hPrevBrush, hBkgBrush;
805 TRACE("hdc=%p\n", hdc);
808 * Retrieve the background brush and select it in the
809 * DC.
811 hBkgBrush = COMBO_PrepareColors(lphc, hdc);
812 hPrevBrush = SelectObject(hdc, hBkgBrush);
813 if (!(lphc->wState & CBF_EDIT))
814 FillRect(hdc, &lphc->textRect, hBkgBrush);
817 * In non 3.1 look, there is a sunken border on the combobox
819 CBPaintBorder(lphc, hdc);
821 CBPaintButton(lphc, hdc);
823 /* paint the edit control padding area */
824 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
826 RECT rPadEdit = lphc->textRect;
828 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
830 FrameRect(hdc, &rPadEdit, GetSysColorBrush(COLOR_WINDOW));
833 if (!(lphc->wState & CBF_EDIT))
834 CBPaintText( lphc, hdc );
836 if (hPrevBrush)
837 SelectObject( hdc, hPrevBrush );
839 return 0;
842 /***********************************************************************
843 * CBUpdateLBox
845 * Select listbox entry according to the contents of the edit control.
847 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
849 INT length, idx;
850 LPWSTR pText = NULL;
852 idx = LB_ERR;
853 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
855 if (length > 0)
856 pText = heap_alloc((length + 1) * sizeof(WCHAR));
858 TRACE("\t edit text length %i\n", length );
860 if( pText )
862 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
863 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
864 heap_free( pText );
867 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
869 /* probably superfluous but Windows sends this too */
870 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
871 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
873 return idx;
876 /***********************************************************************
877 * CBUpdateEdit
879 * Copy a listbox entry to the edit control.
881 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
883 INT length;
884 LPWSTR pText = NULL;
886 TRACE("\t %i\n", index );
888 if( index >= 0 ) /* got an entry */
890 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
891 if( length != LB_ERR)
893 if ((pText = heap_alloc((length + 1) * sizeof(WCHAR))))
894 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
898 if( CB_HASSTRINGS(lphc) )
900 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
901 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)L"");
902 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
905 if( lphc->wState & CBF_FOCUSED )
906 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
908 heap_free( pText );
911 /***********************************************************************
912 * CBDropDown
914 * Show listbox popup.
916 static void CBDropDown( LPHEADCOMBO lphc )
918 HMONITOR monitor;
919 MONITORINFO mon_info;
920 RECT rect,r;
921 int nItems;
922 int nDroppedHeight;
924 TRACE("[%p]: drop down\n", lphc->self);
926 CB_NOTIFY( lphc, CBN_DROPDOWN );
928 /* set selection */
930 lphc->wState |= CBF_DROPPED;
931 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
933 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
935 /* Update edit only if item is in the list */
936 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
937 CBUpdateEdit( lphc, lphc->droppedIndex );
939 else
941 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
943 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
944 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
945 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
948 /* now set popup position */
949 GetWindowRect( lphc->self, &rect );
952 * If it's a dropdown, the listbox is offset
954 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
955 rect.left += COMBO_EDITBUTTONSPACE();
957 /* if the dropped height is greater than the total height of the dropped
958 items list, then force the drop down list height to be the total height
959 of the items in the dropped list */
961 /* And Remove any extra space (Best Fit) */
962 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
963 /* if listbox length has been set directly by its handle */
964 GetWindowRect(lphc->hWndLBox, &r);
965 if (nDroppedHeight < r.bottom - r.top)
966 nDroppedHeight = r.bottom - r.top;
967 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
969 if (nItems > 0)
971 int nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
973 if (lphc->dwStyle & CBS_NOINTEGRALHEIGHT)
975 nDroppedHeight -= 1;
977 else
979 if (nItems > lphc->visibleItems)
980 nItems = lphc->visibleItems;
981 nDroppedHeight = nItems * nIHeight + COMBO_YBORDERSIZE();
985 r.left = rect.left;
986 r.top = rect.bottom;
987 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
988 r.bottom = r.top + nDroppedHeight;
990 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
991 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
992 mon_info.cbSize = sizeof(mon_info);
993 GetMonitorInfoW( monitor, &mon_info );
995 if (r.bottom > mon_info.rcWork.bottom)
997 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
998 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1001 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1002 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1005 if( !(lphc->wState & CBF_NOREDRAW) )
1006 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1008 EnableWindow( lphc->hWndLBox, TRUE );
1009 if (GetCapture() != lphc->self)
1010 SetCapture(lphc->hWndLBox);
1013 /***********************************************************************
1014 * CBRollUp
1016 * Hide listbox popup.
1018 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1020 HWND hWnd = lphc->self;
1022 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1023 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1025 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1027 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1030 if( lphc->wState & CBF_DROPPED )
1032 RECT rect;
1034 lphc->wState &= ~CBF_DROPPED;
1035 ShowWindow( lphc->hWndLBox, SW_HIDE );
1037 if(GetCapture() == lphc->hWndLBox)
1039 ReleaseCapture();
1042 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1044 rect = lphc->buttonRect;
1046 else
1048 if( bButton )
1050 UnionRect( &rect,
1051 &lphc->buttonRect,
1052 &lphc->textRect);
1054 else
1055 rect = lphc->textRect;
1057 bButton = TRUE;
1060 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1061 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1062 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1063 CB_NOTIFY( lphc, CBN_CLOSEUP );
1068 /***********************************************************************
1069 * COMBO_FlipListbox
1071 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1073 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1075 if( lphc->wState & CBF_DROPPED )
1077 CBRollUp( lphc, ok, bRedrawButton );
1078 return FALSE;
1081 CBDropDown( lphc );
1082 return TRUE;
1085 /***********************************************************************
1086 * CBRepaintButton
1088 static void CBRepaintButton( LPHEADCOMBO lphc )
1090 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1091 UpdateWindow(lphc->self);
1094 /***********************************************************************
1095 * COMBO_SetFocus
1097 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1099 if( !(lphc->wState & CBF_FOCUSED) )
1101 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1102 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1104 /* This is wrong. Message sequences seem to indicate that this
1105 is set *after* the notify. */
1106 /* lphc->wState |= CBF_FOCUSED; */
1108 if( !(lphc->wState & CBF_EDIT) )
1109 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1111 CB_NOTIFY( lphc, CBN_SETFOCUS );
1112 lphc->wState |= CBF_FOCUSED;
1116 /***********************************************************************
1117 * COMBO_KillFocus
1119 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1121 HWND hWnd = lphc->self;
1123 if( lphc->wState & CBF_FOCUSED )
1125 CBRollUp( lphc, FALSE, TRUE );
1126 if( IsWindow( hWnd ) )
1128 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1129 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1131 lphc->wState &= ~CBF_FOCUSED;
1133 /* redraw text */
1134 if( !(lphc->wState & CBF_EDIT) )
1135 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1137 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1142 /***********************************************************************
1143 * COMBO_Command
1145 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1147 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1149 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1151 switch( HIWORD(wParam) >> 8 )
1153 case (EN_SETFOCUS >> 8):
1155 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1157 COMBO_SetFocus( lphc );
1158 break;
1160 case (EN_KILLFOCUS >> 8):
1162 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1164 /* NOTE: it seems that Windows' edit control sends an
1165 * undocumented message WM_USER + 0x1B instead of this
1166 * notification (only when it happens to be a part of
1167 * the combo). ?? - AK.
1170 COMBO_KillFocus( lphc );
1171 break;
1174 case (EN_CHANGE >> 8):
1176 * In some circumstances (when the selection of the combobox
1177 * is changed for example) we don't want the EN_CHANGE notification
1178 * to be forwarded to the parent of the combobox. This code
1179 * checks a flag that is set in these occasions and ignores the
1180 * notification.
1182 if (lphc->wState & CBF_NOLBSELECT)
1184 lphc->wState &= ~CBF_NOLBSELECT;
1186 else
1188 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1191 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1192 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1193 break;
1195 case (EN_UPDATE >> 8):
1196 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1197 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1198 break;
1200 case (EN_ERRSPACE >> 8):
1201 CB_NOTIFY( lphc, CBN_ERRSPACE );
1204 else if( lphc->hWndLBox == hWnd )
1206 switch( (short)HIWORD(wParam) )
1208 case LBN_ERRSPACE:
1209 CB_NOTIFY( lphc, CBN_ERRSPACE );
1210 break;
1212 case LBN_DBLCLK:
1213 CB_NOTIFY( lphc, CBN_DBLCLK );
1214 break;
1216 case LBN_SELCHANGE:
1217 case LBN_SELCANCEL:
1219 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1221 /* do not roll up if selection is being tracked
1222 * by arrow keys in the dropdown listbox */
1223 if (!(lphc->wState & CBF_NOROLLUP))
1225 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1227 else lphc->wState &= ~CBF_NOROLLUP;
1229 CB_NOTIFY( lphc, CBN_SELCHANGE );
1231 if( HIWORD(wParam) == LBN_SELCHANGE)
1233 if( lphc->wState & CBF_EDIT )
1234 lphc->wState |= CBF_NOLBSELECT;
1235 CBPaintText( lphc, NULL );
1237 break;
1239 case LBN_SETFOCUS:
1240 case LBN_KILLFOCUS:
1241 /* nothing to do here since ComboLBox always resets the focus to its
1242 * combo/edit counterpart */
1243 break;
1246 return 0;
1249 /***********************************************************************
1250 * COMBO_ItemOp
1252 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1254 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1256 HWND hWnd = lphc->self;
1257 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1259 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1261 switch( msg )
1263 case WM_DELETEITEM:
1265 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1266 lpIS->CtlType = ODT_COMBOBOX;
1267 lpIS->CtlID = id;
1268 lpIS->hwndItem = hWnd;
1269 break;
1271 case WM_DRAWITEM:
1273 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1274 lpIS->CtlType = ODT_COMBOBOX;
1275 lpIS->CtlID = id;
1276 lpIS->hwndItem = hWnd;
1277 break;
1279 case WM_COMPAREITEM:
1281 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1282 lpIS->CtlType = ODT_COMBOBOX;
1283 lpIS->CtlID = id;
1284 lpIS->hwndItem = hWnd;
1285 break;
1287 case WM_MEASUREITEM:
1289 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1290 lpIS->CtlType = ODT_COMBOBOX;
1291 lpIS->CtlID = id;
1292 break;
1295 return SendMessageW(lphc->owner, msg, id, lParam);
1299 /***********************************************************************
1300 * COMBO_GetTextW
1302 static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
1304 INT length;
1306 if( lphc->wState & CBF_EDIT )
1307 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1309 /* get it from the listbox */
1311 if (!count || !buf) return 0;
1312 if( lphc->hWndLBox )
1314 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1315 if (idx == LB_ERR) goto error;
1316 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1317 if (length == LB_ERR) goto error;
1319 /* 'length' is without the terminating character */
1320 if (length >= count)
1322 WCHAR *lpBuffer = heap_alloc((length + 1) * sizeof(WCHAR));
1323 if (!lpBuffer) goto error;
1324 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1326 /* truncate if buffer is too short */
1327 if (length != LB_ERR)
1329 lstrcpynW( buf, lpBuffer, count );
1330 length = count;
1332 heap_free( lpBuffer );
1334 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1336 if (length == LB_ERR) return 0;
1337 return length;
1340 error: /* error - truncate string, return zero */
1341 buf[0] = 0;
1342 return 0;
1345 /***********************************************************************
1346 * CBResetPos
1348 * This function sets window positions according to the updated
1349 * component placement struct.
1351 static void CBResetPos(HEADCOMBO *combo)
1353 BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
1355 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1356 * sizing messages */
1357 if (combo->wState & CBF_EDIT)
1358 SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
1359 combo->textRect.right - combo->textRect.left,
1360 combo->textRect.bottom - combo->textRect.top,
1361 SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0));
1363 SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
1364 combo->droppedRect.right - combo->droppedRect.left,
1365 combo->droppedRect.bottom - combo->droppedRect.top,
1366 SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0));
1368 if (drop)
1370 if (combo->wState & CBF_DROPPED)
1372 combo->wState &= ~CBF_DROPPED;
1373 ShowWindow(combo->hWndLBox, SW_HIDE);
1376 if (!(combo->wState & CBF_NOREDRAW))
1377 RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
1382 /***********************************************************************
1383 * COMBO_Size
1385 static void COMBO_Size( HEADCOMBO *lphc )
1387 if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
1388 return;
1391 * Those controls are always the same height. So we have to make sure
1392 * they are not resized to another value.
1394 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1396 int newComboHeight, curComboHeight, curComboWidth;
1397 RECT rc;
1399 GetWindowRect(lphc->self, &rc);
1400 curComboHeight = rc.bottom - rc.top;
1401 curComboWidth = rc.right - rc.left;
1402 newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1405 * Resizing a combobox has another side effect, it resizes the dropped
1406 * rectangle as well. However, it does it only if the new height for the
1407 * combobox is more than the height it should have. In other words,
1408 * if the application resizing the combobox only had the intention to resize
1409 * the actual control, for example, to do the layout of a dialog that is
1410 * resized, the height of the dropdown is not changed.
1412 if( curComboHeight > newComboHeight )
1414 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%ld, oldDropTop=%ld\n",
1415 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1416 lphc->droppedRect.top);
1417 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1420 * Restore original height
1422 if (curComboHeight != newComboHeight)
1424 lphc->wState |= CBF_NORESIZE;
1425 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1426 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW);
1427 lphc->wState &= ~CBF_NORESIZE;
1431 CBCalcPlacement(lphc);
1433 CBResetPos(lphc);
1437 /***********************************************************************
1438 * COMBO_Font
1440 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1442 lphc->hFont = hFont;
1443 lphc->item_height = combo_get_text_height(lphc);
1446 * Propagate to owned windows.
1448 if( lphc->wState & CBF_EDIT )
1449 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1450 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1453 * Redo the layout of the control.
1455 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1457 CBCalcPlacement(lphc);
1459 CBResetPos(lphc);
1461 else
1463 CBForceDummyResize(lphc);
1468 /***********************************************************************
1469 * COMBO_SetItemHeight
1471 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1473 LRESULT lRet = CB_ERR;
1475 if( index == -1 ) /* set text field height */
1477 if( height < 32768 )
1479 lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1482 * Redo the layout of the control.
1484 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1486 CBCalcPlacement(lphc);
1488 CBResetPos(lphc);
1490 else
1492 CBForceDummyResize(lphc);
1495 lRet = height;
1498 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1499 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1500 return lRet;
1503 /***********************************************************************
1504 * COMBO_SelectString
1506 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText)
1508 INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1509 if( index >= 0 )
1511 if( lphc->wState & CBF_EDIT )
1512 CBUpdateEdit( lphc, index );
1513 else
1515 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1518 return (LRESULT)index;
1521 /***********************************************************************
1522 * COMBO_LButtonDown
1524 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1526 POINT pt;
1527 BOOL bButton;
1528 HWND hWnd = lphc->self;
1530 pt.x = (short)LOWORD(lParam);
1531 pt.y = (short)HIWORD(lParam);
1532 bButton = PtInRect(&lphc->buttonRect, pt);
1534 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1535 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1537 lphc->wState |= CBF_BUTTONDOWN;
1538 if( lphc->wState & CBF_DROPPED )
1540 /* got a click to cancel selection */
1542 lphc->wState &= ~CBF_BUTTONDOWN;
1543 CBRollUp( lphc, TRUE, FALSE );
1544 if( !IsWindow( hWnd ) ) return;
1546 if( lphc->wState & CBF_CAPTURE )
1548 lphc->wState &= ~CBF_CAPTURE;
1549 ReleaseCapture();
1552 else
1554 /* drop down the listbox and start tracking */
1556 lphc->wState |= CBF_CAPTURE;
1557 SetCapture( hWnd );
1558 CBDropDown( lphc );
1560 if( bButton ) CBRepaintButton( lphc );
1564 /***********************************************************************
1565 * COMBO_LButtonUp
1567 * Release capture and stop tracking if needed.
1569 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1571 if( lphc->wState & CBF_CAPTURE )
1573 lphc->wState &= ~CBF_CAPTURE;
1574 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1576 INT index = CBUpdateLBox( lphc, TRUE );
1577 /* Update edit only if item is in the list */
1578 if(index >= 0)
1580 lphc->wState |= CBF_NOLBSELECT;
1581 CBUpdateEdit( lphc, index );
1582 lphc->wState &= ~CBF_NOLBSELECT;
1585 ReleaseCapture();
1586 SetCapture(lphc->hWndLBox);
1589 if( lphc->wState & CBF_BUTTONDOWN )
1591 lphc->wState &= ~CBF_BUTTONDOWN;
1592 CBRepaintButton( lphc );
1596 /***********************************************************************
1597 * COMBO_MouseMove
1599 * Two things to do - track combo button and release capture when
1600 * pointer goes into the listbox.
1602 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1604 POINT pt;
1605 RECT lbRect;
1607 pt.x = (short)LOWORD(lParam);
1608 pt.y = (short)HIWORD(lParam);
1610 if( lphc->wState & CBF_BUTTONDOWN )
1612 BOOL bButton;
1614 bButton = PtInRect(&lphc->buttonRect, pt);
1616 if( !bButton )
1618 lphc->wState &= ~CBF_BUTTONDOWN;
1619 CBRepaintButton( lphc );
1623 GetClientRect( lphc->hWndLBox, &lbRect );
1624 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1625 if( PtInRect(&lbRect, pt) )
1627 lphc->wState &= ~CBF_CAPTURE;
1628 ReleaseCapture();
1629 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1631 /* hand over pointer tracking */
1632 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1636 static LRESULT COMBO_MouseLeave(LPHEADCOMBO lphc)
1638 lphc->wState &= ~CBF_HOT;
1639 RedrawWindow(lphc->self, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1640 return 0;
1643 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1645 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1646 return FALSE;
1648 pcbi->rcItem = lphc->textRect;
1649 pcbi->rcButton = lphc->buttonRect;
1650 pcbi->stateButton = 0;
1651 if (lphc->wState & CBF_BUTTONDOWN)
1652 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1653 if (IsRectEmpty(&lphc->buttonRect))
1654 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1655 pcbi->hwndCombo = lphc->self;
1656 pcbi->hwndItem = lphc->hWndEdit;
1657 pcbi->hwndList = lphc->hWndLBox;
1658 return TRUE;
1661 static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1663 HEADCOMBO *lphc = (HEADCOMBO *)GetWindowLongPtrW( hwnd, 0 );
1664 HTHEME theme;
1666 TRACE("[%p]: msg %#x, wp %Ix, lp %Ix\n", hwnd, message, wParam, lParam );
1668 if (!IsWindow(hwnd)) return 0;
1670 if (lphc || message == WM_NCCREATE)
1671 switch(message)
1673 case WM_NCCREATE:
1675 LONG style = ((CREATESTRUCTW *)lParam)->style;
1676 return COMBO_NCCreate(hwnd, style);
1679 case WM_NCDESTROY:
1680 COMBO_NCDestroy(lphc);
1681 break;/* -> DefWindowProc */
1683 case WM_CREATE:
1685 HWND hwndParent;
1686 LONG style;
1688 hwndParent = ((CREATESTRUCTW *)lParam)->hwndParent;
1689 style = ((CREATESTRUCTW *)lParam)->style;
1690 return COMBO_Create(hwnd, lphc, hwndParent, style);
1693 case WM_DESTROY:
1694 theme = GetWindowTheme( hwnd );
1695 CloseThemeData( theme );
1696 break;
1698 case WM_THEMECHANGED:
1699 theme = GetWindowTheme( hwnd );
1700 CloseThemeData( theme );
1701 OpenThemeData( hwnd, WC_COMBOBOXW );
1702 InvalidateRect( hwnd, NULL, TRUE );
1703 break;
1705 case WM_PRINTCLIENT:
1706 case WM_PAINT:
1708 LRESULT ret = 0;
1709 PAINTSTRUCT ps;
1710 HDC hdc;
1712 hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1714 if (hdc && !(lphc->wState & CBF_NOREDRAW))
1716 HTHEME theme = GetWindowTheme(hwnd);
1718 if (theme)
1719 ret = COMBO_ThemedPaint(theme, lphc, hdc);
1720 else
1721 ret = COMBO_Paint(lphc, hdc);
1724 if (!wParam)
1725 EndPaint(hwnd, &ps);
1727 return ret;
1729 case WM_ERASEBKGND:
1730 /* do all painting in WM_PAINT like Windows does */
1731 return 1;
1733 case WM_GETDLGCODE:
1735 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1736 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1738 int vk = (int)((LPMSG)lParam)->wParam;
1740 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1741 result |= DLGC_WANTMESSAGE;
1743 return result;
1746 case WM_SIZE:
1747 COMBO_Size( lphc );
1748 return TRUE;
1750 case WM_SETFONT:
1751 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1752 return TRUE;
1754 case WM_GETFONT:
1755 return (LRESULT)lphc->hFont;
1757 case WM_SETFOCUS:
1758 if (lphc->wState & CBF_EDIT)
1760 SetFocus( lphc->hWndEdit );
1761 /* The first time focus is received, select all the text */
1762 if (!(lphc->wState & CBF_BEENFOCUSED))
1764 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1765 lphc->wState |= CBF_BEENFOCUSED;
1768 else
1769 COMBO_SetFocus( lphc );
1770 return TRUE;
1772 case WM_KILLFOCUS:
1774 HWND hwndFocus = (HWND)wParam;
1775 if (!hwndFocus || (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox))
1776 COMBO_KillFocus( lphc );
1777 return TRUE;
1780 case WM_COMMAND:
1781 return COMBO_Command( lphc, wParam, (HWND)lParam );
1783 case WM_GETTEXT:
1784 return COMBO_GetText( lphc, wParam, (LPWSTR)lParam );
1786 case WM_SETTEXT:
1787 case WM_GETTEXTLENGTH:
1788 case WM_CLEAR:
1789 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1791 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1792 if (j == -1) return 0;
1793 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1795 else if ( lphc->wState & CBF_EDIT )
1797 LRESULT ret;
1798 lphc->wState |= CBF_NOEDITNOTIFY;
1799 ret = SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1800 lphc->wState &= ~CBF_NOEDITNOTIFY;
1801 return ret;
1803 else
1804 return CB_ERR;
1806 case WM_CUT:
1807 case WM_PASTE:
1808 case WM_COPY:
1809 if (lphc->wState & CBF_EDIT)
1810 return SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1811 else return CB_ERR;
1813 case WM_DRAWITEM:
1814 case WM_DELETEITEM:
1815 case WM_COMPAREITEM:
1816 case WM_MEASUREITEM:
1817 return COMBO_ItemOp(lphc, message, lParam);
1819 case WM_ENABLE:
1820 if (lphc->wState & CBF_EDIT)
1821 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1822 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1824 /* Force the control to repaint when the enabled state changes. */
1825 InvalidateRect(lphc->self, NULL, TRUE);
1826 return TRUE;
1828 case WM_SETREDRAW:
1829 if (wParam)
1830 lphc->wState &= ~CBF_NOREDRAW;
1831 else
1832 lphc->wState |= CBF_NOREDRAW;
1834 if ( lphc->wState & CBF_EDIT )
1835 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1836 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1837 return 0;
1839 case WM_SYSKEYDOWN:
1840 if ( KEYDATA_ALT & HIWORD(lParam) )
1841 if( wParam == VK_UP || wParam == VK_DOWN )
1842 COMBO_FlipListbox( lphc, FALSE, FALSE );
1843 return 0;
1845 case WM_KEYDOWN:
1846 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1847 (lphc->wState & CBF_DROPPED))
1849 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1850 return TRUE;
1852 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1854 COMBO_FlipListbox( lphc, FALSE, FALSE );
1855 return TRUE;
1857 /* fall through */
1858 case WM_CHAR:
1859 case WM_IME_CHAR:
1861 HWND hwndTarget;
1863 if ( lphc->wState & CBF_EDIT )
1864 hwndTarget = lphc->hWndEdit;
1865 else
1866 hwndTarget = lphc->hWndLBox;
1868 return SendMessageW(hwndTarget, message, wParam, lParam);
1871 case WM_LBUTTONDOWN:
1872 if ( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1873 if ( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1874 return TRUE;
1876 case WM_LBUTTONUP:
1877 COMBO_LButtonUp( lphc );
1878 return TRUE;
1880 case WM_MOUSEMOVE:
1881 if (!IsRectEmpty(&lphc->buttonRect))
1883 TRACKMOUSEEVENT event;
1884 POINT pt;
1886 pt.x = (short)LOWORD(lParam);
1887 pt.y = (short)HIWORD(lParam);
1889 if (PtInRect(&lphc->buttonRect, pt))
1891 if (!(lphc->wState & CBF_HOT))
1893 lphc->wState |= CBF_HOT;
1894 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1896 event.cbSize = sizeof(TRACKMOUSEEVENT);
1897 event.dwFlags = TME_QUERY;
1898 if (!TrackMouseEvent(&event) || event.hwndTrack != hwnd || !(event.dwFlags & TME_LEAVE))
1900 event.hwndTrack = hwnd;
1901 event.dwFlags = TME_LEAVE;
1902 TrackMouseEvent(&event);
1906 else if (lphc->wState & CBF_HOT)
1908 lphc->wState &= ~CBF_HOT;
1909 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1913 if ( lphc->wState & CBF_CAPTURE )
1914 COMBO_MouseMove( lphc, wParam, lParam );
1915 return TRUE;
1917 case WM_MOUSELEAVE:
1918 return COMBO_MouseLeave(lphc);
1920 case WM_MOUSEWHEEL:
1921 if (wParam & (MK_SHIFT | MK_CONTROL))
1922 return DefWindowProcW(hwnd, message, wParam, lParam);
1924 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1925 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1926 return TRUE;
1928 case WM_CTLCOLOR:
1929 case WM_CTLCOLORMSGBOX:
1930 case WM_CTLCOLOREDIT:
1931 case WM_CTLCOLORLISTBOX:
1932 case WM_CTLCOLORBTN:
1933 case WM_CTLCOLORDLG:
1934 case WM_CTLCOLORSCROLLBAR:
1935 case WM_CTLCOLORSTATIC:
1936 return SendMessageW(lphc->owner, message, wParam, lParam);
1938 /* Combo messages */
1939 case CB_ADDSTRING:
1940 if (lphc->dwStyle & CBS_LOWERCASE)
1941 CharLowerW((LPWSTR)lParam);
1942 else if (lphc->dwStyle & CBS_UPPERCASE)
1943 CharUpperW((LPWSTR)lParam);
1944 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1946 case CB_INSERTSTRING:
1947 if (lphc->dwStyle & CBS_LOWERCASE)
1948 CharLowerW((LPWSTR)lParam);
1949 else if (lphc->dwStyle & CBS_UPPERCASE)
1950 CharUpperW((LPWSTR)lParam);
1951 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1953 case CB_DELETESTRING:
1954 return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1956 case CB_SELECTSTRING:
1957 return COMBO_SelectString(lphc, (INT)wParam, lParam);
1959 case CB_FINDSTRING:
1960 return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1962 case CB_FINDSTRINGEXACT:
1963 return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1965 case CB_SETITEMHEIGHT:
1966 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1968 case CB_GETITEMHEIGHT:
1969 if ((INT)wParam >= 0) /* listbox item */
1970 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1971 return CBGetTextAreaHeight(lphc, FALSE);
1973 case CB_RESETCONTENT:
1974 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
1976 if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc))
1977 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)L"");
1978 else
1979 InvalidateRect(lphc->self, NULL, TRUE);
1980 return TRUE;
1982 case CB_INITSTORAGE:
1983 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1985 case CB_GETHORIZONTALEXTENT:
1986 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1988 case CB_SETHORIZONTALEXTENT:
1989 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1991 case CB_GETTOPINDEX:
1992 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1994 case CB_GETLOCALE:
1995 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1997 case CB_SETLOCALE:
1998 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2000 case CB_SETDROPPEDWIDTH:
2001 if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768)
2002 return CB_ERR;
2004 /* new value must be higher than combobox width */
2005 if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2006 lphc->droppedWidth = wParam;
2007 else if (wParam)
2008 lphc->droppedWidth = 0;
2010 /* recalculate the combobox area */
2011 CBCalcPlacement(lphc);
2013 /* fall through */
2014 case CB_GETDROPPEDWIDTH:
2015 if (lphc->droppedWidth)
2016 return lphc->droppedWidth;
2017 return lphc->droppedRect.right - lphc->droppedRect.left;
2019 case CB_GETDROPPEDCONTROLRECT:
2020 if (lParam)
2021 CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2022 return CB_OKAY;
2024 case CB_GETDROPPEDSTATE:
2025 return (lphc->wState & CBF_DROPPED) != 0;
2027 case CB_DIR:
2028 return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam);
2030 case CB_SHOWDROPDOWN:
2031 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
2033 if (wParam)
2035 if (!(lphc->wState & CBF_DROPPED))
2036 CBDropDown( lphc );
2038 else if (lphc->wState & CBF_DROPPED)
2039 CBRollUp( lphc, FALSE, TRUE );
2041 return TRUE;
2043 case CB_GETCOUNT:
2044 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2046 case CB_GETCURSEL:
2047 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2049 case CB_SETCURSEL:
2050 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2051 if (lParam >= 0)
2052 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2054 /* no LBN_SELCHANGE in this case, update manually */
2055 CBPaintText(lphc, NULL);
2056 lphc->wState &= ~CBF_SELCHANGE;
2057 return lParam;
2059 case CB_GETLBTEXT:
2060 return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2062 case CB_GETLBTEXTLEN:
2063 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2065 case CB_GETITEMDATA:
2066 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2068 case CB_SETITEMDATA:
2069 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2071 case CB_GETEDITSEL:
2072 /* Edit checks passed parameters itself */
2073 if (lphc->wState & CBF_EDIT)
2074 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2075 return CB_ERR;
2077 case CB_SETEDITSEL:
2078 if (lphc->wState & CBF_EDIT)
2079 return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2080 return CB_ERR;
2082 case CB_SETEXTENDEDUI:
2083 if (CB_GETTYPE(lphc) == CBS_SIMPLE )
2084 return CB_ERR;
2085 if (wParam)
2086 lphc->wState |= CBF_EUI;
2087 else
2088 lphc->wState &= ~CBF_EUI;
2089 return CB_OKAY;
2091 case CB_GETEXTENDEDUI:
2092 return (lphc->wState & CBF_EUI) != 0;
2094 case CB_GETCOMBOBOXINFO:
2095 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2097 case CB_LIMITTEXT:
2098 if (lphc->wState & CBF_EDIT)
2099 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2100 return TRUE;
2102 case CB_GETMINVISIBLE:
2103 return lphc->visibleItems;
2105 case CB_SETMINVISIBLE:
2106 lphc->visibleItems = (INT)wParam;
2107 return TRUE;
2109 default:
2110 if (message >= WM_USER)
2111 WARN("unknown msg WM_USER+%04x, wp %Ix, lp %Ix\n", message - WM_USER, wParam, lParam );
2112 break;
2115 return DefWindowProcW(hwnd, message, wParam, lParam);
2118 void COMBO_Register(void)
2120 WNDCLASSW wndClass;
2122 memset(&wndClass, 0, sizeof(wndClass));
2123 wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2124 wndClass.lpfnWndProc = COMBO_WindowProc;
2125 wndClass.cbClsExtra = 0;
2126 wndClass.cbWndExtra = sizeof(HEADCOMBO *);
2127 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2128 wndClass.hbrBackground = NULL;
2129 wndClass.lpszClassName = WC_COMBOBOXW;
2130 RegisterClassW(&wndClass);