comctl32/combo: Subscribe to WM_MOUSELEAVE events.
[wine.git] / dlls / comctl32 / combo.c
blob887587a053bd641b90fe3fb78aaa27e840d3c4dd
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);
793 return 0;
796 /***********************************************************************
797 * COMBO_Paint
799 static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc)
801 HBRUSH hPrevBrush, hBkgBrush;
803 TRACE("hdc=%p\n", hdc);
806 * Retrieve the background brush and select it in the
807 * DC.
809 hBkgBrush = COMBO_PrepareColors(lphc, hdc);
810 hPrevBrush = SelectObject(hdc, hBkgBrush);
811 if (!(lphc->wState & CBF_EDIT))
812 FillRect(hdc, &lphc->textRect, hBkgBrush);
815 * In non 3.1 look, there is a sunken border on the combobox
817 CBPaintBorder(lphc, hdc);
819 CBPaintButton(lphc, hdc);
821 /* paint the edit control padding area */
822 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
824 RECT rPadEdit = lphc->textRect;
826 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
828 FrameRect(hdc, &rPadEdit, GetSysColorBrush(COLOR_WINDOW));
831 if (!(lphc->wState & CBF_EDIT))
832 CBPaintText( lphc, hdc );
834 if (hPrevBrush)
835 SelectObject( hdc, hPrevBrush );
837 return 0;
840 /***********************************************************************
841 * CBUpdateLBox
843 * Select listbox entry according to the contents of the edit control.
845 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
847 INT length, idx;
848 LPWSTR pText = NULL;
850 idx = LB_ERR;
851 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
853 if (length > 0)
854 pText = heap_alloc((length + 1) * sizeof(WCHAR));
856 TRACE("\t edit text length %i\n", length );
858 if( pText )
860 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
861 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
862 heap_free( pText );
865 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
867 /* probably superfluous but Windows sends this too */
868 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
869 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
871 return idx;
874 /***********************************************************************
875 * CBUpdateEdit
877 * Copy a listbox entry to the edit control.
879 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
881 INT length;
882 LPWSTR pText = NULL;
884 TRACE("\t %i\n", index );
886 if( index >= 0 ) /* got an entry */
888 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
889 if( length != LB_ERR)
891 if ((pText = heap_alloc((length + 1) * sizeof(WCHAR))))
892 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
896 if( CB_HASSTRINGS(lphc) )
898 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
899 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)L"");
900 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
903 if( lphc->wState & CBF_FOCUSED )
904 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
906 heap_free( pText );
909 /***********************************************************************
910 * CBDropDown
912 * Show listbox popup.
914 static void CBDropDown( LPHEADCOMBO lphc )
916 HMONITOR monitor;
917 MONITORINFO mon_info;
918 RECT rect,r;
919 int nItems;
920 int nDroppedHeight;
922 TRACE("[%p]: drop down\n", lphc->self);
924 CB_NOTIFY( lphc, CBN_DROPDOWN );
926 /* set selection */
928 lphc->wState |= CBF_DROPPED;
929 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
931 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
933 /* Update edit only if item is in the list */
934 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
935 CBUpdateEdit( lphc, lphc->droppedIndex );
937 else
939 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
941 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
942 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
943 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
946 /* now set popup position */
947 GetWindowRect( lphc->self, &rect );
950 * If it's a dropdown, the listbox is offset
952 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
953 rect.left += COMBO_EDITBUTTONSPACE();
955 /* if the dropped height is greater than the total height of the dropped
956 items list, then force the drop down list height to be the total height
957 of the items in the dropped list */
959 /* And Remove any extra space (Best Fit) */
960 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
961 /* if listbox length has been set directly by its handle */
962 GetWindowRect(lphc->hWndLBox, &r);
963 if (nDroppedHeight < r.bottom - r.top)
964 nDroppedHeight = r.bottom - r.top;
965 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
967 if (nItems > 0)
969 int nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
971 if (lphc->dwStyle & CBS_NOINTEGRALHEIGHT)
973 nDroppedHeight -= 1;
975 else
977 if (nItems > lphc->visibleItems)
978 nItems = lphc->visibleItems;
979 nDroppedHeight = nItems * nIHeight + COMBO_YBORDERSIZE();
983 r.left = rect.left;
984 r.top = rect.bottom;
985 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
986 r.bottom = r.top + nDroppedHeight;
988 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
989 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
990 mon_info.cbSize = sizeof(mon_info);
991 GetMonitorInfoW( monitor, &mon_info );
993 if (r.bottom > mon_info.rcWork.bottom)
995 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
996 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
999 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1000 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1003 if( !(lphc->wState & CBF_NOREDRAW) )
1004 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1005 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1007 EnableWindow( lphc->hWndLBox, TRUE );
1008 if (GetCapture() != lphc->self)
1009 SetCapture(lphc->hWndLBox);
1012 /***********************************************************************
1013 * CBRollUp
1015 * Hide listbox popup.
1017 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1019 HWND hWnd = lphc->self;
1021 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1022 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1024 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1026 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1029 if( lphc->wState & CBF_DROPPED )
1031 RECT rect;
1033 lphc->wState &= ~CBF_DROPPED;
1034 ShowWindow( lphc->hWndLBox, SW_HIDE );
1036 if(GetCapture() == lphc->hWndLBox)
1038 ReleaseCapture();
1041 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1043 rect = lphc->buttonRect;
1045 else
1047 if( bButton )
1049 UnionRect( &rect,
1050 &lphc->buttonRect,
1051 &lphc->textRect);
1053 else
1054 rect = lphc->textRect;
1056 bButton = TRUE;
1059 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1060 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1061 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1062 CB_NOTIFY( lphc, CBN_CLOSEUP );
1067 /***********************************************************************
1068 * COMBO_FlipListbox
1070 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1072 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1074 if( lphc->wState & CBF_DROPPED )
1076 CBRollUp( lphc, ok, bRedrawButton );
1077 return FALSE;
1080 CBDropDown( lphc );
1081 return TRUE;
1084 /***********************************************************************
1085 * CBRepaintButton
1087 static void CBRepaintButton( LPHEADCOMBO lphc )
1089 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1090 UpdateWindow(lphc->self);
1093 /***********************************************************************
1094 * COMBO_SetFocus
1096 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1098 if( !(lphc->wState & CBF_FOCUSED) )
1100 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1101 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1103 /* This is wrong. Message sequences seem to indicate that this
1104 is set *after* the notify. */
1105 /* lphc->wState |= CBF_FOCUSED; */
1107 if( !(lphc->wState & CBF_EDIT) )
1108 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1110 CB_NOTIFY( lphc, CBN_SETFOCUS );
1111 lphc->wState |= CBF_FOCUSED;
1115 /***********************************************************************
1116 * COMBO_KillFocus
1118 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1120 HWND hWnd = lphc->self;
1122 if( lphc->wState & CBF_FOCUSED )
1124 CBRollUp( lphc, FALSE, TRUE );
1125 if( IsWindow( hWnd ) )
1127 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1128 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1130 lphc->wState &= ~CBF_FOCUSED;
1132 /* redraw text */
1133 if( !(lphc->wState & CBF_EDIT) )
1134 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1136 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1141 /***********************************************************************
1142 * COMBO_Command
1144 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1146 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1148 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1150 switch( HIWORD(wParam) >> 8 )
1152 case (EN_SETFOCUS >> 8):
1154 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1156 COMBO_SetFocus( lphc );
1157 break;
1159 case (EN_KILLFOCUS >> 8):
1161 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1163 /* NOTE: it seems that Windows' edit control sends an
1164 * undocumented message WM_USER + 0x1B instead of this
1165 * notification (only when it happens to be a part of
1166 * the combo). ?? - AK.
1169 COMBO_KillFocus( lphc );
1170 break;
1173 case (EN_CHANGE >> 8):
1175 * In some circumstances (when the selection of the combobox
1176 * is changed for example) we don't want the EN_CHANGE notification
1177 * to be forwarded to the parent of the combobox. This code
1178 * checks a flag that is set in these occasions and ignores the
1179 * notification.
1181 if (lphc->wState & CBF_NOLBSELECT)
1183 lphc->wState &= ~CBF_NOLBSELECT;
1185 else
1187 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1190 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1191 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1192 break;
1194 case (EN_UPDATE >> 8):
1195 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1196 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1197 break;
1199 case (EN_ERRSPACE >> 8):
1200 CB_NOTIFY( lphc, CBN_ERRSPACE );
1203 else if( lphc->hWndLBox == hWnd )
1205 switch( (short)HIWORD(wParam) )
1207 case LBN_ERRSPACE:
1208 CB_NOTIFY( lphc, CBN_ERRSPACE );
1209 break;
1211 case LBN_DBLCLK:
1212 CB_NOTIFY( lphc, CBN_DBLCLK );
1213 break;
1215 case LBN_SELCHANGE:
1216 case LBN_SELCANCEL:
1218 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1220 /* do not roll up if selection is being tracked
1221 * by arrow keys in the dropdown listbox */
1222 if (!(lphc->wState & CBF_NOROLLUP))
1224 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1226 else lphc->wState &= ~CBF_NOROLLUP;
1228 CB_NOTIFY( lphc, CBN_SELCHANGE );
1230 if( HIWORD(wParam) == LBN_SELCHANGE)
1232 if( lphc->wState & CBF_EDIT )
1233 lphc->wState |= CBF_NOLBSELECT;
1234 CBPaintText( lphc, NULL );
1236 break;
1238 case LBN_SETFOCUS:
1239 case LBN_KILLFOCUS:
1240 /* nothing to do here since ComboLBox always resets the focus to its
1241 * combo/edit counterpart */
1242 break;
1245 return 0;
1248 /***********************************************************************
1249 * COMBO_ItemOp
1251 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1253 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1255 HWND hWnd = lphc->self;
1256 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1258 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1260 switch( msg )
1262 case WM_DELETEITEM:
1264 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1265 lpIS->CtlType = ODT_COMBOBOX;
1266 lpIS->CtlID = id;
1267 lpIS->hwndItem = hWnd;
1268 break;
1270 case WM_DRAWITEM:
1272 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1273 lpIS->CtlType = ODT_COMBOBOX;
1274 lpIS->CtlID = id;
1275 lpIS->hwndItem = hWnd;
1276 break;
1278 case WM_COMPAREITEM:
1280 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1281 lpIS->CtlType = ODT_COMBOBOX;
1282 lpIS->CtlID = id;
1283 lpIS->hwndItem = hWnd;
1284 break;
1286 case WM_MEASUREITEM:
1288 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1289 lpIS->CtlType = ODT_COMBOBOX;
1290 lpIS->CtlID = id;
1291 break;
1294 return SendMessageW(lphc->owner, msg, id, lParam);
1298 /***********************************************************************
1299 * COMBO_GetTextW
1301 static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
1303 INT length;
1305 if( lphc->wState & CBF_EDIT )
1306 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1308 /* get it from the listbox */
1310 if (!count || !buf) return 0;
1311 if( lphc->hWndLBox )
1313 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1314 if (idx == LB_ERR) goto error;
1315 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1316 if (length == LB_ERR) goto error;
1318 /* 'length' is without the terminating character */
1319 if (length >= count)
1321 WCHAR *lpBuffer = heap_alloc((length + 1) * sizeof(WCHAR));
1322 if (!lpBuffer) goto error;
1323 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1325 /* truncate if buffer is too short */
1326 if (length != LB_ERR)
1328 lstrcpynW( buf, lpBuffer, count );
1329 length = count;
1331 heap_free( lpBuffer );
1333 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1335 if (length == LB_ERR) return 0;
1336 return length;
1339 error: /* error - truncate string, return zero */
1340 buf[0] = 0;
1341 return 0;
1344 /***********************************************************************
1345 * CBResetPos
1347 * This function sets window positions according to the updated
1348 * component placement struct.
1350 static void CBResetPos(HEADCOMBO *combo)
1352 BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
1354 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1355 * sizing messages */
1356 if (combo->wState & CBF_EDIT)
1357 SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
1358 combo->textRect.right - combo->textRect.left,
1359 combo->textRect.bottom - combo->textRect.top,
1360 SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0));
1362 SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
1363 combo->droppedRect.right - combo->droppedRect.left,
1364 combo->droppedRect.bottom - combo->droppedRect.top,
1365 SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0));
1367 if (drop)
1369 if (combo->wState & CBF_DROPPED)
1371 combo->wState &= ~CBF_DROPPED;
1372 ShowWindow(combo->hWndLBox, SW_HIDE);
1375 if (!(combo->wState & CBF_NOREDRAW))
1376 RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
1381 /***********************************************************************
1382 * COMBO_Size
1384 static void COMBO_Size( HEADCOMBO *lphc )
1386 if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
1387 return;
1390 * Those controls are always the same height. So we have to make sure
1391 * they are not resized to another value.
1393 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1395 int newComboHeight, curComboHeight, curComboWidth;
1396 RECT rc;
1398 GetWindowRect(lphc->self, &rc);
1399 curComboHeight = rc.bottom - rc.top;
1400 curComboWidth = rc.right - rc.left;
1401 newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1404 * Resizing a combobox has another side effect, it resizes the dropped
1405 * rectangle as well. However, it does it only if the new height for the
1406 * combobox is more than the height it should have. In other words,
1407 * if the application resizing the combobox only had the intention to resize
1408 * the actual control, for example, to do the layout of a dialog that is
1409 * resized, the height of the dropdown is not changed.
1411 if( curComboHeight > newComboHeight )
1413 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1414 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1415 lphc->droppedRect.top);
1416 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1419 * Restore original height
1421 if (curComboHeight != newComboHeight)
1423 lphc->wState |= CBF_NORESIZE;
1424 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1425 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW);
1426 lphc->wState &= ~CBF_NORESIZE;
1430 CBCalcPlacement(lphc);
1432 CBResetPos(lphc);
1436 /***********************************************************************
1437 * COMBO_Font
1439 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1441 lphc->hFont = hFont;
1442 lphc->item_height = combo_get_text_height(lphc);
1445 * Propagate to owned windows.
1447 if( lphc->wState & CBF_EDIT )
1448 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1449 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1452 * Redo the layout of the control.
1454 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1456 CBCalcPlacement(lphc);
1458 CBResetPos(lphc);
1460 else
1462 CBForceDummyResize(lphc);
1467 /***********************************************************************
1468 * COMBO_SetItemHeight
1470 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1472 LRESULT lRet = CB_ERR;
1474 if( index == -1 ) /* set text field height */
1476 if( height < 32768 )
1478 lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1481 * Redo the layout of the control.
1483 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1485 CBCalcPlacement(lphc);
1487 CBResetPos(lphc);
1489 else
1491 CBForceDummyResize(lphc);
1494 lRet = height;
1497 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1498 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1499 return lRet;
1502 /***********************************************************************
1503 * COMBO_SelectString
1505 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText)
1507 INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1508 if( index >= 0 )
1510 if( lphc->wState & CBF_EDIT )
1511 CBUpdateEdit( lphc, index );
1512 else
1514 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1517 return (LRESULT)index;
1520 /***********************************************************************
1521 * COMBO_LButtonDown
1523 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1525 POINT pt;
1526 BOOL bButton;
1527 HWND hWnd = lphc->self;
1529 pt.x = (short)LOWORD(lParam);
1530 pt.y = (short)HIWORD(lParam);
1531 bButton = PtInRect(&lphc->buttonRect, pt);
1533 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1534 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1536 lphc->wState |= CBF_BUTTONDOWN;
1537 if( lphc->wState & CBF_DROPPED )
1539 /* got a click to cancel selection */
1541 lphc->wState &= ~CBF_BUTTONDOWN;
1542 CBRollUp( lphc, TRUE, FALSE );
1543 if( !IsWindow( hWnd ) ) return;
1545 if( lphc->wState & CBF_CAPTURE )
1547 lphc->wState &= ~CBF_CAPTURE;
1548 ReleaseCapture();
1551 else
1553 /* drop down the listbox and start tracking */
1555 lphc->wState |= CBF_CAPTURE;
1556 SetCapture( hWnd );
1557 CBDropDown( lphc );
1559 if( bButton ) CBRepaintButton( lphc );
1563 /***********************************************************************
1564 * COMBO_LButtonUp
1566 * Release capture and stop tracking if needed.
1568 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1570 if( lphc->wState & CBF_CAPTURE )
1572 lphc->wState &= ~CBF_CAPTURE;
1573 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1575 INT index = CBUpdateLBox( lphc, TRUE );
1576 /* Update edit only if item is in the list */
1577 if(index >= 0)
1579 lphc->wState |= CBF_NOLBSELECT;
1580 CBUpdateEdit( lphc, index );
1581 lphc->wState &= ~CBF_NOLBSELECT;
1584 ReleaseCapture();
1585 SetCapture(lphc->hWndLBox);
1588 if( lphc->wState & CBF_BUTTONDOWN )
1590 lphc->wState &= ~CBF_BUTTONDOWN;
1591 CBRepaintButton( lphc );
1595 /***********************************************************************
1596 * COMBO_MouseMove
1598 * Two things to do - track combo button and release capture when
1599 * pointer goes into the listbox.
1601 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1603 POINT pt;
1604 RECT lbRect;
1606 pt.x = (short)LOWORD(lParam);
1607 pt.y = (short)HIWORD(lParam);
1609 if( lphc->wState & CBF_BUTTONDOWN )
1611 BOOL bButton;
1613 bButton = PtInRect(&lphc->buttonRect, pt);
1615 if( !bButton )
1617 lphc->wState &= ~CBF_BUTTONDOWN;
1618 CBRepaintButton( lphc );
1622 GetClientRect( lphc->hWndLBox, &lbRect );
1623 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1624 if( PtInRect(&lbRect, pt) )
1626 lphc->wState &= ~CBF_CAPTURE;
1627 ReleaseCapture();
1628 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1630 /* hand over pointer tracking */
1631 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1635 static LRESULT COMBO_MouseLeave(LPHEADCOMBO lphc)
1637 lphc->wState &= ~CBF_HOT;
1638 RedrawWindow(lphc->self, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1639 return 0;
1642 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1644 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1645 return FALSE;
1647 pcbi->rcItem = lphc->textRect;
1648 pcbi->rcButton = lphc->buttonRect;
1649 pcbi->stateButton = 0;
1650 if (lphc->wState & CBF_BUTTONDOWN)
1651 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1652 if (IsRectEmpty(&lphc->buttonRect))
1653 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1654 pcbi->hwndCombo = lphc->self;
1655 pcbi->hwndItem = lphc->hWndEdit;
1656 pcbi->hwndList = lphc->hWndLBox;
1657 return TRUE;
1660 static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1662 HEADCOMBO *lphc = (HEADCOMBO *)GetWindowLongPtrW( hwnd, 0 );
1663 HTHEME theme;
1665 TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", hwnd, message, wParam, lParam );
1667 if (!IsWindow(hwnd)) return 0;
1669 if (lphc || message == WM_NCCREATE)
1670 switch(message)
1672 case WM_NCCREATE:
1674 LONG style = ((CREATESTRUCTW *)lParam)->style;
1675 return COMBO_NCCreate(hwnd, style);
1678 case WM_NCDESTROY:
1679 COMBO_NCDestroy(lphc);
1680 break;/* -> DefWindowProc */
1682 case WM_CREATE:
1684 HWND hwndParent;
1685 LONG style;
1687 hwndParent = ((CREATESTRUCTW *)lParam)->hwndParent;
1688 style = ((CREATESTRUCTW *)lParam)->style;
1689 return COMBO_Create(hwnd, lphc, hwndParent, style);
1692 case WM_DESTROY:
1693 theme = GetWindowTheme( hwnd );
1694 CloseThemeData( theme );
1695 break;
1697 case WM_THEMECHANGED:
1698 theme = GetWindowTheme( hwnd );
1699 CloseThemeData( theme );
1700 OpenThemeData( hwnd, WC_COMBOBOXW );
1701 break;
1703 case WM_PRINTCLIENT:
1704 case WM_PAINT:
1706 LRESULT ret = 0;
1707 PAINTSTRUCT ps;
1708 HDC hdc;
1710 hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1712 if (hdc && !(lphc->wState & CBF_NOREDRAW))
1714 HTHEME theme = GetWindowTheme(hwnd);
1716 if (theme)
1717 ret = COMBO_ThemedPaint(theme, lphc, hdc);
1718 else
1719 ret = COMBO_Paint(lphc, hdc);
1722 if (!wParam)
1723 EndPaint(hwnd, &ps);
1725 return ret;
1727 case WM_ERASEBKGND:
1728 /* do all painting in WM_PAINT like Windows does */
1729 return 1;
1731 case WM_GETDLGCODE:
1733 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1734 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1736 int vk = (int)((LPMSG)lParam)->wParam;
1738 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1739 result |= DLGC_WANTMESSAGE;
1741 return result;
1744 case WM_SIZE:
1745 COMBO_Size( lphc );
1746 return TRUE;
1748 case WM_SETFONT:
1749 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1750 return TRUE;
1752 case WM_GETFONT:
1753 return (LRESULT)lphc->hFont;
1755 case WM_SETFOCUS:
1756 if (lphc->wState & CBF_EDIT)
1758 SetFocus( lphc->hWndEdit );
1759 /* The first time focus is received, select all the text */
1760 if (!(lphc->wState & CBF_BEENFOCUSED))
1762 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1763 lphc->wState |= CBF_BEENFOCUSED;
1766 else
1767 COMBO_SetFocus( lphc );
1768 return TRUE;
1770 case WM_KILLFOCUS:
1772 HWND hwndFocus = (HWND)wParam;
1773 if (!hwndFocus || (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox))
1774 COMBO_KillFocus( lphc );
1775 return TRUE;
1778 case WM_COMMAND:
1779 return COMBO_Command( lphc, wParam, (HWND)lParam );
1781 case WM_GETTEXT:
1782 return COMBO_GetText( lphc, wParam, (LPWSTR)lParam );
1784 case WM_SETTEXT:
1785 case WM_GETTEXTLENGTH:
1786 case WM_CLEAR:
1787 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1789 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1790 if (j == -1) return 0;
1791 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1793 else if ( lphc->wState & CBF_EDIT )
1795 LRESULT ret;
1796 lphc->wState |= CBF_NOEDITNOTIFY;
1797 ret = SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1798 lphc->wState &= ~CBF_NOEDITNOTIFY;
1799 return ret;
1801 else
1802 return CB_ERR;
1804 case WM_CUT:
1805 case WM_PASTE:
1806 case WM_COPY:
1807 if (lphc->wState & CBF_EDIT)
1808 return SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1809 else return CB_ERR;
1811 case WM_DRAWITEM:
1812 case WM_DELETEITEM:
1813 case WM_COMPAREITEM:
1814 case WM_MEASUREITEM:
1815 return COMBO_ItemOp(lphc, message, lParam);
1817 case WM_ENABLE:
1818 if (lphc->wState & CBF_EDIT)
1819 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1820 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1822 /* Force the control to repaint when the enabled state changes. */
1823 InvalidateRect(lphc->self, NULL, TRUE);
1824 return TRUE;
1826 case WM_SETREDRAW:
1827 if (wParam)
1828 lphc->wState &= ~CBF_NOREDRAW;
1829 else
1830 lphc->wState |= CBF_NOREDRAW;
1832 if ( lphc->wState & CBF_EDIT )
1833 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1834 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1835 return 0;
1837 case WM_SYSKEYDOWN:
1838 if ( KEYDATA_ALT & HIWORD(lParam) )
1839 if( wParam == VK_UP || wParam == VK_DOWN )
1840 COMBO_FlipListbox( lphc, FALSE, FALSE );
1841 return 0;
1843 case WM_KEYDOWN:
1844 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1845 (lphc->wState & CBF_DROPPED))
1847 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1848 return TRUE;
1850 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1852 COMBO_FlipListbox( lphc, FALSE, FALSE );
1853 return TRUE;
1855 /* fall through */
1856 case WM_CHAR:
1857 case WM_IME_CHAR:
1859 HWND hwndTarget;
1861 if ( lphc->wState & CBF_EDIT )
1862 hwndTarget = lphc->hWndEdit;
1863 else
1864 hwndTarget = lphc->hWndLBox;
1866 return SendMessageW(hwndTarget, message, wParam, lParam);
1869 case WM_LBUTTONDOWN:
1870 if ( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1871 if ( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1872 return TRUE;
1874 case WM_LBUTTONUP:
1875 COMBO_LButtonUp( lphc );
1876 return TRUE;
1878 case WM_MOUSEMOVE:
1879 if (!IsRectEmpty(&lphc->buttonRect))
1881 TRACKMOUSEEVENT event;
1882 POINT pt;
1884 pt.x = (short)LOWORD(lParam);
1885 pt.y = (short)HIWORD(lParam);
1887 if (PtInRect(&lphc->buttonRect, pt))
1889 if (!(lphc->wState & CBF_HOT))
1891 lphc->wState |= CBF_HOT;
1892 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1894 event.cbSize = sizeof(TRACKMOUSEEVENT);
1895 event.dwFlags = TME_QUERY;
1896 if (!TrackMouseEvent(&event) || event.hwndTrack != hwnd || !(event.dwFlags & TME_LEAVE))
1898 event.hwndTrack = hwnd;
1899 event.dwFlags = TME_LEAVE;
1900 TrackMouseEvent(&event);
1904 else if (lphc->wState & CBF_HOT)
1906 lphc->wState &= ~CBF_HOT;
1907 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1911 if ( lphc->wState & CBF_CAPTURE )
1912 COMBO_MouseMove( lphc, wParam, lParam );
1913 return TRUE;
1915 case WM_MOUSELEAVE:
1916 return COMBO_MouseLeave(lphc);
1918 case WM_MOUSEWHEEL:
1919 if (wParam & (MK_SHIFT | MK_CONTROL))
1920 return DefWindowProcW(hwnd, message, wParam, lParam);
1922 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1923 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1924 return TRUE;
1926 case WM_CTLCOLOR:
1927 case WM_CTLCOLORMSGBOX:
1928 case WM_CTLCOLOREDIT:
1929 case WM_CTLCOLORLISTBOX:
1930 case WM_CTLCOLORBTN:
1931 case WM_CTLCOLORDLG:
1932 case WM_CTLCOLORSCROLLBAR:
1933 case WM_CTLCOLORSTATIC:
1934 return SendMessageW(lphc->owner, message, wParam, lParam);
1936 /* Combo messages */
1937 case CB_ADDSTRING:
1938 if (lphc->dwStyle & CBS_LOWERCASE)
1939 CharLowerW((LPWSTR)lParam);
1940 else if (lphc->dwStyle & CBS_UPPERCASE)
1941 CharUpperW((LPWSTR)lParam);
1942 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1944 case CB_INSERTSTRING:
1945 if (lphc->dwStyle & CBS_LOWERCASE)
1946 CharLowerW((LPWSTR)lParam);
1947 else if (lphc->dwStyle & CBS_UPPERCASE)
1948 CharUpperW((LPWSTR)lParam);
1949 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1951 case CB_DELETESTRING:
1952 return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1954 case CB_SELECTSTRING:
1955 return COMBO_SelectString(lphc, (INT)wParam, lParam);
1957 case CB_FINDSTRING:
1958 return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1960 case CB_FINDSTRINGEXACT:
1961 return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1963 case CB_SETITEMHEIGHT:
1964 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1966 case CB_GETITEMHEIGHT:
1967 if ((INT)wParam >= 0) /* listbox item */
1968 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1969 return CBGetTextAreaHeight(lphc, FALSE);
1971 case CB_RESETCONTENT:
1972 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
1974 if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc))
1975 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)L"");
1976 else
1977 InvalidateRect(lphc->self, NULL, TRUE);
1978 return TRUE;
1980 case CB_INITSTORAGE:
1981 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1983 case CB_GETHORIZONTALEXTENT:
1984 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1986 case CB_SETHORIZONTALEXTENT:
1987 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1989 case CB_GETTOPINDEX:
1990 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1992 case CB_GETLOCALE:
1993 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1995 case CB_SETLOCALE:
1996 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1998 case CB_SETDROPPEDWIDTH:
1999 if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768)
2000 return CB_ERR;
2002 /* new value must be higher than combobox width */
2003 if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2004 lphc->droppedWidth = wParam;
2005 else if (wParam)
2006 lphc->droppedWidth = 0;
2008 /* recalculate the combobox area */
2009 CBCalcPlacement(lphc);
2011 /* fall through */
2012 case CB_GETDROPPEDWIDTH:
2013 if (lphc->droppedWidth)
2014 return lphc->droppedWidth;
2015 return lphc->droppedRect.right - lphc->droppedRect.left;
2017 case CB_GETDROPPEDCONTROLRECT:
2018 if (lParam)
2019 CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2020 return CB_OKAY;
2022 case CB_GETDROPPEDSTATE:
2023 return (lphc->wState & CBF_DROPPED) != 0;
2025 case CB_DIR:
2026 return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam);
2028 case CB_SHOWDROPDOWN:
2029 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
2031 if (wParam)
2033 if (!(lphc->wState & CBF_DROPPED))
2034 CBDropDown( lphc );
2036 else if (lphc->wState & CBF_DROPPED)
2037 CBRollUp( lphc, FALSE, TRUE );
2039 return TRUE;
2041 case CB_GETCOUNT:
2042 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2044 case CB_GETCURSEL:
2045 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2047 case CB_SETCURSEL:
2048 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2049 if (lParam >= 0)
2050 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2052 /* no LBN_SELCHANGE in this case, update manually */
2053 CBPaintText(lphc, NULL);
2054 lphc->wState &= ~CBF_SELCHANGE;
2055 return lParam;
2057 case CB_GETLBTEXT:
2058 return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2060 case CB_GETLBTEXTLEN:
2061 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2063 case CB_GETITEMDATA:
2064 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2066 case CB_SETITEMDATA:
2067 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2069 case CB_GETEDITSEL:
2070 /* Edit checks passed parameters itself */
2071 if (lphc->wState & CBF_EDIT)
2072 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2073 return CB_ERR;
2075 case CB_SETEDITSEL:
2076 if (lphc->wState & CBF_EDIT)
2077 return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2078 return CB_ERR;
2080 case CB_SETEXTENDEDUI:
2081 if (CB_GETTYPE(lphc) == CBS_SIMPLE )
2082 return CB_ERR;
2083 if (wParam)
2084 lphc->wState |= CBF_EUI;
2085 else
2086 lphc->wState &= ~CBF_EUI;
2087 return CB_OKAY;
2089 case CB_GETEXTENDEDUI:
2090 return (lphc->wState & CBF_EUI) != 0;
2092 case CB_GETCOMBOBOXINFO:
2093 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2095 case CB_LIMITTEXT:
2096 if (lphc->wState & CBF_EDIT)
2097 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2098 return TRUE;
2100 case CB_GETMINVISIBLE:
2101 return lphc->visibleItems;
2103 case CB_SETMINVISIBLE:
2104 lphc->visibleItems = (INT)wParam;
2105 return TRUE;
2107 default:
2108 if (message >= WM_USER)
2109 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n", message - WM_USER, wParam, lParam );
2110 break;
2113 return DefWindowProcW(hwnd, message, wParam, lParam);
2116 void COMBO_Register(void)
2118 WNDCLASSW wndClass;
2120 memset(&wndClass, 0, sizeof(wndClass));
2121 wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2122 wndClass.lpfnWndProc = COMBO_WindowProc;
2123 wndClass.cbClsExtra = 0;
2124 wndClass.cbWndExtra = sizeof(HEADCOMBO *);
2125 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2126 wndClass.hbrBackground = NULL;
2127 wndClass.lpszClassName = WC_COMBOBOXW;
2128 RegisterClassW(&wndClass);