d3d8: Stop setting the device state in d3d8_device_SetPixelShaderConstant().
[wine.git] / dlls / comctl32 / combo.c
blob765fc631e0c6405cf26e9e2a3d22512608b4ed54
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 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
404 static const WCHAR editName[] = {'E','d','i','t',0};
406 OpenThemeData( hwnd, WC_COMBOBOXW );
407 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
408 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
410 lphc->owner = hwndParent;
412 lphc->droppedWidth = 0;
414 lphc->item_height = combo_get_text_height(lphc);
417 * The first time we go through, we want to measure the ownerdraw item
419 lphc->wState |= CBF_MEASUREITEM;
422 * Per default the comctl32 version of combo shows up to 30 items
424 lphc->visibleItems = 30;
426 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
428 if( lphc->owner || !(style & WS_VISIBLE) )
430 UINT lbeStyle = 0;
431 UINT lbeExStyle = 0;
434 * Initialize the dropped rect to the size of the client area of the
435 * control and then, force all the areas of the combobox to be
436 * recalculated.
438 GetClientRect( hwnd, &lphc->droppedRect );
439 CBCalcPlacement(lphc);
442 * Adjust the position of the popup listbox if it's necessary
444 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
446 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
449 * If it's a dropdown, the listbox is offset
451 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
452 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
454 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
455 lphc->droppedRect.bottom = lphc->droppedRect.top;
456 if (lphc->droppedRect.right < lphc->droppedRect.left)
457 lphc->droppedRect.right = lphc->droppedRect.left;
458 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
461 /* create listbox popup */
463 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
464 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
466 if( lphc->dwStyle & CBS_SORT )
467 lbeStyle |= LBS_SORT;
468 if( lphc->dwStyle & CBS_HASSTRINGS )
469 lbeStyle |= LBS_HASSTRINGS;
470 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
471 lbeStyle |= LBS_NOINTEGRALHEIGHT;
472 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
473 lbeStyle |= LBS_DISABLENOSCROLL;
475 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
477 lbeStyle |= WS_VISIBLE;
480 * In win 95 look n feel, the listbox in the simple combobox has
481 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
483 lbeStyle &= ~WS_BORDER;
484 lbeExStyle |= WS_EX_CLIENTEDGE;
486 else
488 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
491 lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
492 lphc->droppedRect.left, lphc->droppedRect.top, lphc->droppedRect.right - lphc->droppedRect.left,
493 lphc->droppedRect.bottom - lphc->droppedRect.top, hwnd, (HMENU)ID_CB_LISTBOX,
494 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
495 if( lphc->hWndLBox )
497 BOOL bEdit = TRUE;
498 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
500 if( lphc->wState & CBF_EDIT )
502 if( lphc->dwStyle & CBS_OEMCONVERT )
503 lbeStyle |= ES_OEMCONVERT;
504 if( lphc->dwStyle & CBS_AUTOHSCROLL )
505 lbeStyle |= ES_AUTOHSCROLL;
506 if( lphc->dwStyle & CBS_LOWERCASE )
507 lbeStyle |= ES_LOWERCASE;
508 else if( lphc->dwStyle & CBS_UPPERCASE )
509 lbeStyle |= ES_UPPERCASE;
511 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
513 lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
514 lphc->textRect.left, lphc->textRect.top,
515 lphc->textRect.right - lphc->textRect.left,
516 lphc->textRect.bottom - lphc->textRect.top,
517 hwnd, (HMENU)ID_CB_EDIT,
518 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
519 if( !lphc->hWndEdit )
520 bEdit = FALSE;
523 if( bEdit )
525 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
527 /* Now do the trick with parent */
528 SetParent(lphc->hWndLBox, HWND_DESKTOP);
530 * If the combo is a dropdown, we must resize the control
531 * to fit only the text area and button. To do this,
532 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
533 * will take care of setting the height for us.
535 CBForceDummyResize(lphc);
538 TRACE("init done\n");
539 return 0;
541 ERR("edit control failure.\n");
542 } else ERR("listbox failure.\n");
543 } else ERR("no owner for visible combo.\n");
545 /* CreateWindow() will send WM_NCDESTROY to cleanup */
547 return -1;
550 /***********************************************************************
551 * CBPaintButton
553 * Paint combo button (normal, pressed, and disabled states).
555 static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
557 UINT buttonState = DFCS_SCROLLCOMBOBOX;
559 if (IsRectEmpty(&lphc->buttonRect))
560 return;
562 if( lphc->wState & CBF_NOREDRAW )
563 return;
566 if (lphc->wState & CBF_BUTTONDOWN)
567 buttonState |= DFCS_PUSHED;
569 if (CB_DISABLED(lphc))
570 buttonState |= DFCS_INACTIVE;
572 DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
575 /***********************************************************************
576 * COMBO_PrepareColors
578 * This method will sent the appropriate WM_CTLCOLOR message to
579 * prepare and setup the colors for the combo's DC.
581 * It also returns the brush to use for the background.
583 static HBRUSH COMBO_PrepareColors(
584 LPHEADCOMBO lphc,
585 HDC hDC)
587 HBRUSH hBkgBrush;
590 * Get the background brush for this control.
592 if (CB_DISABLED(lphc))
594 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
595 (WPARAM)hDC, (LPARAM)lphc->self );
598 * We have to change the text color since WM_CTLCOLORSTATIC will
599 * set it to the "enabled" color. This is the same behavior as the
600 * edit control
602 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
604 else
606 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
607 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
608 (WPARAM)hDC, (LPARAM)lphc->self );
612 * Catch errors.
614 if( !hBkgBrush )
615 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
617 return hBkgBrush;
620 /***********************************************************************
621 * CBPaintText
623 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
625 static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint)
627 RECT rectEdit = lphc->textRect;
628 INT id, size = 0;
629 LPWSTR pText = NULL;
631 TRACE("\n");
633 /* follow Windows combobox that sends a bunch of text
634 * inquiries to its listbox while processing WM_PAINT. */
636 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
638 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
639 if (size == LB_ERR)
640 FIXME("LB_ERR probably not handled yet\n");
641 if ((pText = heap_alloc((size + 1) * sizeof(WCHAR))))
643 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
644 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
645 pText[size] = '\0'; /* just in case */
646 } else return;
649 if( lphc->wState & CBF_EDIT )
651 static const WCHAR empty_stringW[] = { 0 };
652 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
653 if( lphc->wState & CBF_FOCUSED )
654 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
656 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
658 /* paint text field ourselves */
659 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
660 UINT itemState = ODS_COMBOBOXEDIT;
661 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
662 HBRUSH hPrevBrush, hBkgBrush;
665 * Give ourselves some space.
667 InflateRect( &rectEdit, -1, -1 );
669 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
670 hPrevBrush = SelectObject( hdc, hBkgBrush );
671 FillRect( hdc, &rectEdit, hBkgBrush );
673 if( CB_OWNERDRAWN(lphc) )
675 DRAWITEMSTRUCT dis;
676 HRGN clipRegion;
677 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
679 /* setup state for DRAWITEM message. Owner will highlight */
680 if ( (lphc->wState & CBF_FOCUSED) &&
681 !(lphc->wState & CBF_DROPPED) )
682 itemState |= ODS_SELECTED | ODS_FOCUS;
684 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
686 dis.CtlType = ODT_COMBOBOX;
687 dis.CtlID = ctlid;
688 dis.hwndItem = lphc->self;
689 dis.itemAction = ODA_DRAWENTIRE;
690 dis.itemID = id;
691 dis.itemState = itemState;
692 dis.hDC = hdc;
693 dis.rcItem = rectEdit;
694 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
697 * Clip the DC and have the parent draw the item.
699 clipRegion = set_control_clipping( hdc, &rectEdit );
701 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
703 SelectClipRgn( hdc, clipRegion );
704 if (clipRegion) DeleteObject( clipRegion );
706 else
708 static const WCHAR empty_stringW[] = { 0 };
710 if ( (lphc->wState & CBF_FOCUSED) &&
711 !(lphc->wState & CBF_DROPPED) ) {
713 /* highlight */
714 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
715 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
716 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
719 ExtTextOutW( hdc,
720 rectEdit.left + 1,
721 rectEdit.top + 1,
722 ETO_OPAQUE | ETO_CLIPPED,
723 &rectEdit,
724 pText ? pText : empty_stringW , size, NULL );
726 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
727 DrawFocusRect( hdc, &rectEdit );
730 if( hPrevFont )
731 SelectObject(hdc, hPrevFont );
733 if( hPrevBrush )
734 SelectObject( hdc, hPrevBrush );
736 if( !hdc_paint )
737 ReleaseDC( lphc->self, hdc );
740 heap_free(pText);
743 /***********************************************************************
744 * CBPaintBorder
746 static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc)
748 RECT clientRect;
750 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
752 GetClientRect(lphc->self, &clientRect);
754 else
756 clientRect = lphc->textRect;
758 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
759 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
762 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
765 static LRESULT COMBO_ThemedPaint(HTHEME theme, HEADCOMBO *lphc, HDC hdc)
767 int button_state;
768 RECT frame;
770 /* paint border */
771 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
772 GetClientRect(lphc->self, &frame);
773 else
775 frame = lphc->textRect;
776 InflateRect(&frame, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
777 InflateRect(&frame, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
780 DrawThemeBackground(theme, hdc, 0, IsWindowEnabled(lphc->self) ? CBXS_NORMAL : CBXS_DISABLED, &frame, NULL);
782 /* Paint button */
783 if (!IsRectEmpty(&lphc->buttonRect))
785 if (!IsWindowEnabled(lphc->self))
786 button_state = CBXS_DISABLED;
787 else if (lphc->wState & CBF_BUTTONDOWN)
788 button_state = CBXS_PRESSED;
789 else if (lphc->wState & CBF_HOT)
790 button_state = CBXS_HOT;
791 else
792 button_state = CBXS_NORMAL;
793 DrawThemeBackground(theme, hdc, CP_DROPDOWNBUTTON, button_state, &lphc->buttonRect, NULL);
796 if ((lphc->dwStyle & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST)
797 CBPaintText(lphc, hdc);
799 return 0;
802 /***********************************************************************
803 * COMBO_Paint
805 static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc)
807 HBRUSH hPrevBrush, hBkgBrush;
809 TRACE("hdc=%p\n", hdc);
812 * Retrieve the background brush and select it in the
813 * DC.
815 hBkgBrush = COMBO_PrepareColors(lphc, hdc);
816 hPrevBrush = SelectObject(hdc, hBkgBrush);
817 if (!(lphc->wState & CBF_EDIT))
818 FillRect(hdc, &lphc->textRect, hBkgBrush);
821 * In non 3.1 look, there is a sunken border on the combobox
823 CBPaintBorder(lphc, hdc);
825 CBPaintButton(lphc, hdc);
827 /* paint the edit control padding area */
828 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
830 RECT rPadEdit = lphc->textRect;
832 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
834 FrameRect(hdc, &rPadEdit, GetSysColorBrush(COLOR_WINDOW));
837 if (!(lphc->wState & CBF_EDIT))
838 CBPaintText( lphc, hdc );
840 if (hPrevBrush)
841 SelectObject( hdc, hPrevBrush );
843 return 0;
846 /***********************************************************************
847 * CBUpdateLBox
849 * Select listbox entry according to the contents of the edit control.
851 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
853 INT length, idx;
854 LPWSTR pText = NULL;
856 idx = LB_ERR;
857 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
859 if (length > 0)
860 pText = heap_alloc((length + 1) * sizeof(WCHAR));
862 TRACE("\t edit text length %i\n", length );
864 if( pText )
866 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
867 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
868 heap_free( pText );
871 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
873 /* probably superfluous but Windows sends this too */
874 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
875 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
877 return idx;
880 /***********************************************************************
881 * CBUpdateEdit
883 * Copy a listbox entry to the edit control.
885 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
887 INT length;
888 LPWSTR pText = NULL;
889 static const WCHAR empty_stringW[] = { 0 };
891 TRACE("\t %i\n", index );
893 if( index >= 0 ) /* got an entry */
895 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
896 if( length != LB_ERR)
898 if ((pText = heap_alloc((length + 1) * sizeof(WCHAR))))
899 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
903 if( CB_HASSTRINGS(lphc) )
905 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
906 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
907 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
910 if( lphc->wState & CBF_FOCUSED )
911 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
913 heap_free( pText );
916 /***********************************************************************
917 * CBDropDown
919 * Show listbox popup.
921 static void CBDropDown( LPHEADCOMBO lphc )
923 HMONITOR monitor;
924 MONITORINFO mon_info;
925 RECT rect,r;
926 int nItems;
927 int nDroppedHeight;
929 TRACE("[%p]: drop down\n", lphc->self);
931 CB_NOTIFY( lphc, CBN_DROPDOWN );
933 /* set selection */
935 lphc->wState |= CBF_DROPPED;
936 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
938 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
940 /* Update edit only if item is in the list */
941 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
942 CBUpdateEdit( lphc, lphc->droppedIndex );
944 else
946 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
948 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
949 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
950 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
953 /* now set popup position */
954 GetWindowRect( lphc->self, &rect );
957 * If it's a dropdown, the listbox is offset
959 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
960 rect.left += COMBO_EDITBUTTONSPACE();
962 /* if the dropped height is greater than the total height of the dropped
963 items list, then force the drop down list height to be the total height
964 of the items in the dropped list */
966 /* And Remove any extra space (Best Fit) */
967 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
968 /* if listbox length has been set directly by its handle */
969 GetWindowRect(lphc->hWndLBox, &r);
970 if (nDroppedHeight < r.bottom - r.top)
971 nDroppedHeight = r.bottom - r.top;
972 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
974 if (nItems > 0)
976 int nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
978 if (lphc->dwStyle & CBS_NOINTEGRALHEIGHT)
980 nDroppedHeight -= 1;
982 else
984 if (nItems > lphc->visibleItems)
985 nItems = lphc->visibleItems;
986 nDroppedHeight = nItems * nIHeight + COMBO_YBORDERSIZE();
990 r.left = rect.left;
991 r.top = rect.bottom;
992 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
993 r.bottom = r.top + nDroppedHeight;
995 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
996 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
997 mon_info.cbSize = sizeof(mon_info);
998 GetMonitorInfoW( monitor, &mon_info );
1000 if (r.bottom > mon_info.rcWork.bottom)
1002 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
1003 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1006 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1007 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1010 if( !(lphc->wState & CBF_NOREDRAW) )
1011 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1012 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1014 EnableWindow( lphc->hWndLBox, TRUE );
1015 if (GetCapture() != lphc->self)
1016 SetCapture(lphc->hWndLBox);
1019 /***********************************************************************
1020 * CBRollUp
1022 * Hide listbox popup.
1024 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1026 HWND hWnd = lphc->self;
1028 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1029 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1031 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1033 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1036 if( lphc->wState & CBF_DROPPED )
1038 RECT rect;
1040 lphc->wState &= ~CBF_DROPPED;
1041 ShowWindow( lphc->hWndLBox, SW_HIDE );
1043 if(GetCapture() == lphc->hWndLBox)
1045 ReleaseCapture();
1048 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1050 rect = lphc->buttonRect;
1052 else
1054 if( bButton )
1056 UnionRect( &rect,
1057 &lphc->buttonRect,
1058 &lphc->textRect);
1060 else
1061 rect = lphc->textRect;
1063 bButton = TRUE;
1066 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1067 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1068 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1069 CB_NOTIFY( lphc, CBN_CLOSEUP );
1074 /***********************************************************************
1075 * COMBO_FlipListbox
1077 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1079 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1081 if( lphc->wState & CBF_DROPPED )
1083 CBRollUp( lphc, ok, bRedrawButton );
1084 return FALSE;
1087 CBDropDown( lphc );
1088 return TRUE;
1091 /***********************************************************************
1092 * CBRepaintButton
1094 static void CBRepaintButton( LPHEADCOMBO lphc )
1096 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1097 UpdateWindow(lphc->self);
1100 /***********************************************************************
1101 * COMBO_SetFocus
1103 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1105 if( !(lphc->wState & CBF_FOCUSED) )
1107 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1108 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1110 /* This is wrong. Message sequences seem to indicate that this
1111 is set *after* the notify. */
1112 /* lphc->wState |= CBF_FOCUSED; */
1114 if( !(lphc->wState & CBF_EDIT) )
1115 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1117 CB_NOTIFY( lphc, CBN_SETFOCUS );
1118 lphc->wState |= CBF_FOCUSED;
1122 /***********************************************************************
1123 * COMBO_KillFocus
1125 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1127 HWND hWnd = lphc->self;
1129 if( lphc->wState & CBF_FOCUSED )
1131 CBRollUp( lphc, FALSE, TRUE );
1132 if( IsWindow( hWnd ) )
1134 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1135 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1137 lphc->wState &= ~CBF_FOCUSED;
1139 /* redraw text */
1140 if( !(lphc->wState & CBF_EDIT) )
1141 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1143 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1148 /***********************************************************************
1149 * COMBO_Command
1151 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1153 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1155 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1157 switch( HIWORD(wParam) >> 8 )
1159 case (EN_SETFOCUS >> 8):
1161 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1163 COMBO_SetFocus( lphc );
1164 break;
1166 case (EN_KILLFOCUS >> 8):
1168 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1170 /* NOTE: it seems that Windows' edit control sends an
1171 * undocumented message WM_USER + 0x1B instead of this
1172 * notification (only when it happens to be a part of
1173 * the combo). ?? - AK.
1176 COMBO_KillFocus( lphc );
1177 break;
1180 case (EN_CHANGE >> 8):
1182 * In some circumstances (when the selection of the combobox
1183 * is changed for example) we don't want the EN_CHANGE notification
1184 * to be forwarded to the parent of the combobox. This code
1185 * checks a flag that is set in these occasions and ignores the
1186 * notification.
1188 if (lphc->wState & CBF_NOLBSELECT)
1190 lphc->wState &= ~CBF_NOLBSELECT;
1192 else
1194 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1197 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1198 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1199 break;
1201 case (EN_UPDATE >> 8):
1202 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1203 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1204 break;
1206 case (EN_ERRSPACE >> 8):
1207 CB_NOTIFY( lphc, CBN_ERRSPACE );
1210 else if( lphc->hWndLBox == hWnd )
1212 switch( (short)HIWORD(wParam) )
1214 case LBN_ERRSPACE:
1215 CB_NOTIFY( lphc, CBN_ERRSPACE );
1216 break;
1218 case LBN_DBLCLK:
1219 CB_NOTIFY( lphc, CBN_DBLCLK );
1220 break;
1222 case LBN_SELCHANGE:
1223 case LBN_SELCANCEL:
1225 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1227 /* do not roll up if selection is being tracked
1228 * by arrow keys in the dropdown listbox */
1229 if (!(lphc->wState & CBF_NOROLLUP))
1231 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1233 else lphc->wState &= ~CBF_NOROLLUP;
1235 CB_NOTIFY( lphc, CBN_SELCHANGE );
1237 if( HIWORD(wParam) == LBN_SELCHANGE)
1239 if( lphc->wState & CBF_EDIT )
1240 lphc->wState |= CBF_NOLBSELECT;
1241 CBPaintText( lphc, NULL );
1243 break;
1245 case LBN_SETFOCUS:
1246 case LBN_KILLFOCUS:
1247 /* nothing to do here since ComboLBox always resets the focus to its
1248 * combo/edit counterpart */
1249 break;
1252 return 0;
1255 /***********************************************************************
1256 * COMBO_ItemOp
1258 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1260 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1262 HWND hWnd = lphc->self;
1263 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1265 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1267 switch( msg )
1269 case WM_DELETEITEM:
1271 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1272 lpIS->CtlType = ODT_COMBOBOX;
1273 lpIS->CtlID = id;
1274 lpIS->hwndItem = hWnd;
1275 break;
1277 case WM_DRAWITEM:
1279 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1280 lpIS->CtlType = ODT_COMBOBOX;
1281 lpIS->CtlID = id;
1282 lpIS->hwndItem = hWnd;
1283 break;
1285 case WM_COMPAREITEM:
1287 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1288 lpIS->CtlType = ODT_COMBOBOX;
1289 lpIS->CtlID = id;
1290 lpIS->hwndItem = hWnd;
1291 break;
1293 case WM_MEASUREITEM:
1295 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1296 lpIS->CtlType = ODT_COMBOBOX;
1297 lpIS->CtlID = id;
1298 break;
1301 return SendMessageW(lphc->owner, msg, id, lParam);
1305 /***********************************************************************
1306 * COMBO_GetTextW
1308 static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
1310 INT length;
1312 if( lphc->wState & CBF_EDIT )
1313 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1315 /* get it from the listbox */
1317 if (!count || !buf) return 0;
1318 if( lphc->hWndLBox )
1320 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1321 if (idx == LB_ERR) goto error;
1322 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1323 if (length == LB_ERR) goto error;
1325 /* 'length' is without the terminating character */
1326 if (length >= count)
1328 WCHAR *lpBuffer = heap_alloc((length + 1) * sizeof(WCHAR));
1329 if (!lpBuffer) goto error;
1330 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1332 /* truncate if buffer is too short */
1333 if (length != LB_ERR)
1335 lstrcpynW( buf, lpBuffer, count );
1336 length = count;
1338 heap_free( lpBuffer );
1340 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1342 if (length == LB_ERR) return 0;
1343 return length;
1346 error: /* error - truncate string, return zero */
1347 buf[0] = 0;
1348 return 0;
1351 /***********************************************************************
1352 * CBResetPos
1354 * This function sets window positions according to the updated
1355 * component placement struct.
1357 static void CBResetPos(HEADCOMBO *combo)
1359 BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
1361 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1362 * sizing messages */
1363 if (combo->wState & CBF_EDIT)
1364 SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
1365 combo->textRect.right - combo->textRect.left,
1366 combo->textRect.bottom - combo->textRect.top,
1367 SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0));
1369 SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
1370 combo->droppedRect.right - combo->droppedRect.left,
1371 combo->droppedRect.bottom - combo->droppedRect.top,
1372 SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0));
1374 if (drop)
1376 if (combo->wState & CBF_DROPPED)
1378 combo->wState &= ~CBF_DROPPED;
1379 ShowWindow(combo->hWndLBox, SW_HIDE);
1382 if (!(combo->wState & CBF_NOREDRAW))
1383 RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
1388 /***********************************************************************
1389 * COMBO_Size
1391 static void COMBO_Size( HEADCOMBO *lphc )
1393 if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
1394 return;
1397 * Those controls are always the same height. So we have to make sure
1398 * they are not resized to another value.
1400 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1402 int newComboHeight, curComboHeight, curComboWidth;
1403 RECT rc;
1405 GetWindowRect(lphc->self, &rc);
1406 curComboHeight = rc.bottom - rc.top;
1407 curComboWidth = rc.right - rc.left;
1408 newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1411 * Resizing a combobox has another side effect, it resizes the dropped
1412 * rectangle as well. However, it does it only if the new height for the
1413 * combobox is more than the height it should have. In other words,
1414 * if the application resizing the combobox only had the intention to resize
1415 * the actual control, for example, to do the layout of a dialog that is
1416 * resized, the height of the dropdown is not changed.
1418 if( curComboHeight > newComboHeight )
1420 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1421 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1422 lphc->droppedRect.top);
1423 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1426 * Restore original height
1428 if (curComboHeight != newComboHeight)
1430 lphc->wState |= CBF_NORESIZE;
1431 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1432 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW);
1433 lphc->wState &= ~CBF_NORESIZE;
1437 CBCalcPlacement(lphc);
1439 CBResetPos(lphc);
1443 /***********************************************************************
1444 * COMBO_Font
1446 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1448 lphc->hFont = hFont;
1449 lphc->item_height = combo_get_text_height(lphc);
1452 * Propagate to owned windows.
1454 if( lphc->wState & CBF_EDIT )
1455 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1456 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1459 * Redo the layout of the control.
1461 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1463 CBCalcPlacement(lphc);
1465 CBResetPos(lphc);
1467 else
1469 CBForceDummyResize(lphc);
1474 /***********************************************************************
1475 * COMBO_SetItemHeight
1477 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1479 LRESULT lRet = CB_ERR;
1481 if( index == -1 ) /* set text field height */
1483 if( height < 32768 )
1485 lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1488 * Redo the layout of the control.
1490 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1492 CBCalcPlacement(lphc);
1494 CBResetPos(lphc);
1496 else
1498 CBForceDummyResize(lphc);
1501 lRet = height;
1504 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1505 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1506 return lRet;
1509 /***********************************************************************
1510 * COMBO_SelectString
1512 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText)
1514 INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1515 if( index >= 0 )
1517 if( lphc->wState & CBF_EDIT )
1518 CBUpdateEdit( lphc, index );
1519 else
1521 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1524 return (LRESULT)index;
1527 /***********************************************************************
1528 * COMBO_LButtonDown
1530 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1532 POINT pt;
1533 BOOL bButton;
1534 HWND hWnd = lphc->self;
1536 pt.x = (short)LOWORD(lParam);
1537 pt.y = (short)HIWORD(lParam);
1538 bButton = PtInRect(&lphc->buttonRect, pt);
1540 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1541 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1543 lphc->wState |= CBF_BUTTONDOWN;
1544 if( lphc->wState & CBF_DROPPED )
1546 /* got a click to cancel selection */
1548 lphc->wState &= ~CBF_BUTTONDOWN;
1549 CBRollUp( lphc, TRUE, FALSE );
1550 if( !IsWindow( hWnd ) ) return;
1552 if( lphc->wState & CBF_CAPTURE )
1554 lphc->wState &= ~CBF_CAPTURE;
1555 ReleaseCapture();
1558 else
1560 /* drop down the listbox and start tracking */
1562 lphc->wState |= CBF_CAPTURE;
1563 SetCapture( hWnd );
1564 CBDropDown( lphc );
1566 if( bButton ) CBRepaintButton( lphc );
1570 /***********************************************************************
1571 * COMBO_LButtonUp
1573 * Release capture and stop tracking if needed.
1575 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1577 if( lphc->wState & CBF_CAPTURE )
1579 lphc->wState &= ~CBF_CAPTURE;
1580 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1582 INT index = CBUpdateLBox( lphc, TRUE );
1583 /* Update edit only if item is in the list */
1584 if(index >= 0)
1586 lphc->wState |= CBF_NOLBSELECT;
1587 CBUpdateEdit( lphc, index );
1588 lphc->wState &= ~CBF_NOLBSELECT;
1591 ReleaseCapture();
1592 SetCapture(lphc->hWndLBox);
1595 if( lphc->wState & CBF_BUTTONDOWN )
1597 lphc->wState &= ~CBF_BUTTONDOWN;
1598 CBRepaintButton( lphc );
1602 /***********************************************************************
1603 * COMBO_MouseMove
1605 * Two things to do - track combo button and release capture when
1606 * pointer goes into the listbox.
1608 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1610 POINT pt;
1611 RECT lbRect;
1613 pt.x = (short)LOWORD(lParam);
1614 pt.y = (short)HIWORD(lParam);
1616 if( lphc->wState & CBF_BUTTONDOWN )
1618 BOOL bButton;
1620 bButton = PtInRect(&lphc->buttonRect, pt);
1622 if( !bButton )
1624 lphc->wState &= ~CBF_BUTTONDOWN;
1625 CBRepaintButton( lphc );
1629 GetClientRect( lphc->hWndLBox, &lbRect );
1630 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1631 if( PtInRect(&lbRect, pt) )
1633 lphc->wState &= ~CBF_CAPTURE;
1634 ReleaseCapture();
1635 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1637 /* hand over pointer tracking */
1638 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
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 POINT pt;
1883 pt.x = (short)LOWORD(lParam);
1884 pt.y = (short)HIWORD(lParam);
1886 if (PtInRect(&lphc->buttonRect, pt))
1888 if (!(lphc->wState & CBF_HOT))
1890 lphc->wState |= CBF_HOT;
1891 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1894 else if (lphc->wState & CBF_HOT)
1896 lphc->wState &= ~CBF_HOT;
1897 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1901 if ( lphc->wState & CBF_CAPTURE )
1902 COMBO_MouseMove( lphc, wParam, lParam );
1903 return TRUE;
1905 case WM_MOUSEWHEEL:
1906 if (wParam & (MK_SHIFT | MK_CONTROL))
1907 return DefWindowProcW(hwnd, message, wParam, lParam);
1909 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1910 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1911 return TRUE;
1913 case WM_CTLCOLOR:
1914 case WM_CTLCOLORMSGBOX:
1915 case WM_CTLCOLOREDIT:
1916 case WM_CTLCOLORLISTBOX:
1917 case WM_CTLCOLORBTN:
1918 case WM_CTLCOLORDLG:
1919 case WM_CTLCOLORSCROLLBAR:
1920 case WM_CTLCOLORSTATIC:
1921 return SendMessageW(lphc->owner, message, wParam, lParam);
1923 /* Combo messages */
1924 case CB_ADDSTRING:
1925 if (lphc->dwStyle & CBS_LOWERCASE)
1926 CharLowerW((LPWSTR)lParam);
1927 else if (lphc->dwStyle & CBS_UPPERCASE)
1928 CharUpperW((LPWSTR)lParam);
1929 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1931 case CB_INSERTSTRING:
1932 if (lphc->dwStyle & CBS_LOWERCASE)
1933 CharLowerW((LPWSTR)lParam);
1934 else if (lphc->dwStyle & CBS_UPPERCASE)
1935 CharUpperW((LPWSTR)lParam);
1936 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1938 case CB_DELETESTRING:
1939 return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1941 case CB_SELECTSTRING:
1942 return COMBO_SelectString(lphc, (INT)wParam, lParam);
1944 case CB_FINDSTRING:
1945 return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1947 case CB_FINDSTRINGEXACT:
1948 return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1950 case CB_SETITEMHEIGHT:
1951 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1953 case CB_GETITEMHEIGHT:
1954 if ((INT)wParam >= 0) /* listbox item */
1955 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1956 return CBGetTextAreaHeight(lphc, FALSE);
1958 case CB_RESETCONTENT:
1959 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
1961 if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc))
1963 static const WCHAR empty_stringW[] = { 0 };
1964 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
1966 else
1967 InvalidateRect(lphc->self, NULL, TRUE);
1968 return TRUE;
1970 case CB_INITSTORAGE:
1971 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1973 case CB_GETHORIZONTALEXTENT:
1974 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1976 case CB_SETHORIZONTALEXTENT:
1977 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1979 case CB_GETTOPINDEX:
1980 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1982 case CB_GETLOCALE:
1983 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1985 case CB_SETLOCALE:
1986 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1988 case CB_SETDROPPEDWIDTH:
1989 if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768)
1990 return CB_ERR;
1992 /* new value must be higher than combobox width */
1993 if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
1994 lphc->droppedWidth = wParam;
1995 else if (wParam)
1996 lphc->droppedWidth = 0;
1998 /* recalculate the combobox area */
1999 CBCalcPlacement(lphc);
2001 /* fall through */
2002 case CB_GETDROPPEDWIDTH:
2003 if (lphc->droppedWidth)
2004 return lphc->droppedWidth;
2005 return lphc->droppedRect.right - lphc->droppedRect.left;
2007 case CB_GETDROPPEDCONTROLRECT:
2008 if (lParam)
2009 CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2010 return CB_OKAY;
2012 case CB_GETDROPPEDSTATE:
2013 return (lphc->wState & CBF_DROPPED) != 0;
2015 case CB_DIR:
2016 return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam);
2018 case CB_SHOWDROPDOWN:
2019 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
2021 if (wParam)
2023 if (!(lphc->wState & CBF_DROPPED))
2024 CBDropDown( lphc );
2026 else if (lphc->wState & CBF_DROPPED)
2027 CBRollUp( lphc, FALSE, TRUE );
2029 return TRUE;
2031 case CB_GETCOUNT:
2032 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2034 case CB_GETCURSEL:
2035 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2037 case CB_SETCURSEL:
2038 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2039 if (lParam >= 0)
2040 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2042 /* no LBN_SELCHANGE in this case, update manually */
2043 CBPaintText(lphc, NULL);
2044 lphc->wState &= ~CBF_SELCHANGE;
2045 return lParam;
2047 case CB_GETLBTEXT:
2048 return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2050 case CB_GETLBTEXTLEN:
2051 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2053 case CB_GETITEMDATA:
2054 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2056 case CB_SETITEMDATA:
2057 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2059 case CB_GETEDITSEL:
2060 /* Edit checks passed parameters itself */
2061 if (lphc->wState & CBF_EDIT)
2062 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2063 return CB_ERR;
2065 case CB_SETEDITSEL:
2066 if (lphc->wState & CBF_EDIT)
2067 return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2068 return CB_ERR;
2070 case CB_SETEXTENDEDUI:
2071 if (CB_GETTYPE(lphc) == CBS_SIMPLE )
2072 return CB_ERR;
2073 if (wParam)
2074 lphc->wState |= CBF_EUI;
2075 else
2076 lphc->wState &= ~CBF_EUI;
2077 return CB_OKAY;
2079 case CB_GETEXTENDEDUI:
2080 return (lphc->wState & CBF_EUI) != 0;
2082 case CB_GETCOMBOBOXINFO:
2083 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2085 case CB_LIMITTEXT:
2086 if (lphc->wState & CBF_EDIT)
2087 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2088 return TRUE;
2090 case CB_GETMINVISIBLE:
2091 return lphc->visibleItems;
2093 case CB_SETMINVISIBLE:
2094 lphc->visibleItems = (INT)wParam;
2095 return TRUE;
2097 default:
2098 if (message >= WM_USER)
2099 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n", message - WM_USER, wParam, lParam );
2100 break;
2103 return DefWindowProcW(hwnd, message, wParam, lParam);
2106 void COMBO_Register(void)
2108 WNDCLASSW wndClass;
2110 memset(&wndClass, 0, sizeof(wndClass));
2111 wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2112 wndClass.lpfnWndProc = COMBO_WindowProc;
2113 wndClass.cbClsExtra = 0;
2114 wndClass.cbWndExtra = sizeof(HEADCOMBO *);
2115 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2116 wndClass.hbrBackground = NULL;
2117 wndClass.lpszClassName = WC_COMBOBOXW;
2118 RegisterClassW(&wndClass);