comctl32/tests: Use CRT allocation functions.
[wine.git] / dlls / user32 / combo.c
blob6afd841f82a8589fc10e78683112f0987d4ca50d
1 /*
2 * Combo controls
4 * Copyright 1997 Alex Korobka
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * TODO:
21 * - CB_SETTOPINDEX
24 #define OEMRESOURCE
26 #include <stdlib.h>
27 #include "user_private.h"
28 #include "controls.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(combo);
33 /* bits in the dwKeyData */
34 #define KEYDATA_ALT 0x2000
35 #define KEYDATA_PREVSTATE 0x4000
38 * Additional combo box definitions
41 #define CB_NOTIFY( lphc, code ) \
42 (SendMessageW((lphc)->owner, WM_COMMAND, \
43 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
45 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
46 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
47 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
48 #define CB_HWND( lphc ) ((lphc)->self)
49 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
51 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
54 * Drawing globals
56 static HBITMAP hComboBmp = 0;
57 static UINT CBitHeight, CBitWidth;
60 * Look and feel dependent "constants"
63 #define COMBO_YBORDERGAP 5
64 #define COMBO_XBORDERSIZE() 2
65 #define COMBO_YBORDERSIZE() 2
66 #define COMBO_EDITBUTTONSPACE() 0
67 #define EDIT_CONTROL_PADDING() 1
69 static void CBCalcPlacement(HEADCOMBO *combo);
70 static void CBResetPos(HEADCOMBO *combo, BOOL redraw);
72 /***********************************************************************
73 * COMBO_Init
75 * Load combo button bitmap.
77 static BOOL COMBO_Init(void)
79 HDC hDC;
81 if( hComboBmp ) return TRUE;
82 if( (hDC = CreateCompatibleDC(0)) )
84 BOOL bRet = FALSE;
85 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
87 BITMAP bm;
88 HBITMAP hPrevB;
89 RECT r;
91 GetObjectW( hComboBmp, sizeof(bm), &bm );
92 CBitHeight = bm.bmHeight;
93 CBitWidth = bm.bmWidth;
95 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
97 hPrevB = SelectObject( hDC, hComboBmp);
98 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
99 InvertRect( hDC, &r );
100 SelectObject( hDC, hPrevB );
101 bRet = TRUE;
103 DeleteDC( hDC );
104 return bRet;
106 return FALSE;
109 /***********************************************************************
110 * COMBO_NCCreate
112 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
114 LPHEADCOMBO lphc;
116 if( COMBO_Init() && (lphc = calloc( 1, sizeof(HEADCOMBO) )) )
118 lphc->self = hwnd;
119 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
121 /* some braindead apps do try to use scrollbar/border flags */
123 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
124 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
127 * We also have to remove the client edge style to make sure
128 * we don't end-up with a non client area.
130 SetWindowLongW( hwnd, GWL_EXSTYLE,
131 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
133 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
134 lphc->dwStyle |= CBS_HASSTRINGS;
135 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
136 lphc->wState |= CBF_NOTIFY;
138 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
139 return TRUE;
141 return FALSE;
144 /***********************************************************************
145 * COMBO_NCDestroy
147 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
150 if( lphc )
152 TRACE("[%p]: freeing storage\n", lphc->self);
154 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
155 NtUserDestroyWindow( lphc->hWndLBox );
157 SetWindowLongPtrW( lphc->self, 0, 0 );
158 free( lphc );
160 return 0;
163 static INT combo_get_text_height(const HEADCOMBO *combo)
165 HDC hdc = NtUserGetDC(combo->self);
166 HFONT prev_font = 0;
167 TEXTMETRICW tm;
169 if (combo->hFont)
170 prev_font = SelectObject(hdc, combo->hFont);
172 GetTextMetricsW(hdc, &tm);
174 if (prev_font)
175 SelectObject(hdc, prev_font);
177 NtUserReleaseDC( combo->self, hdc );
179 return tm.tmHeight + 4;
182 /***********************************************************************
183 * CBGetTextAreaHeight
185 * This method will calculate the height of the text area of the
186 * combobox.
187 * The height of the text area is set in two ways.
188 * It can be set explicitly through a combobox message or through a
189 * WM_MEASUREITEM callback.
190 * If this is not the case, the height is set to font height + 4px
191 * This height was determined through experimentation.
192 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
194 static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
196 INT item_height, text_height;
198 if (clip_item_height && !CB_OWNERDRAWN(lphc))
200 text_height = combo_get_text_height(lphc);
201 if (lphc->item_height < text_height)
202 lphc->item_height = text_height;
205 item_height = lphc->item_height;
208 * Check the ownerdraw case if we haven't asked the parent the size
209 * of the item yet.
211 if ( CB_OWNERDRAWN(lphc) &&
212 (lphc->wState & CBF_MEASUREITEM) )
214 MEASUREITEMSTRUCT measureItem;
215 RECT clientRect;
216 INT originalItemHeight = item_height;
217 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
220 * We use the client rect for the width of the item.
222 GetClientRect(lphc->self, &clientRect);
224 lphc->wState &= ~CBF_MEASUREITEM;
227 * Send a first one to measure the size of the text area
229 measureItem.CtlType = ODT_COMBOBOX;
230 measureItem.CtlID = id;
231 measureItem.itemID = -1;
232 measureItem.itemWidth = clientRect.right;
233 measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */
234 measureItem.itemData = 0;
235 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
236 item_height = 6 + measureItem.itemHeight;
239 * Send a second one in the case of a fixed ownerdraw list to calculate the
240 * size of the list items. (we basically do this on behalf of the listbox)
242 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
244 measureItem.CtlType = ODT_COMBOBOX;
245 measureItem.CtlID = id;
246 measureItem.itemID = 0;
247 measureItem.itemWidth = clientRect.right;
248 measureItem.itemHeight = originalItemHeight;
249 measureItem.itemData = 0;
250 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
251 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
255 * Keep the size for the next time
257 lphc->item_height = item_height;
260 return item_height;
263 /***********************************************************************
264 * CBForceDummyResize
266 * The dummy resize is used for listboxes that have a popup to trigger
267 * a re-arranging of the contents of the combobox and the recalculation
268 * of the size of the "real" control window.
270 static void CBForceDummyResize(LPHEADCOMBO lphc)
272 RECT windowRect;
273 int newComboHeight;
275 newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE();
277 GetWindowRect(lphc->self, &windowRect);
280 * We have to be careful, resizing a combobox also has the meaning that the
281 * dropped rect will be resized. In this case, we want to trigger a resize
282 * to recalculate layout but we don't want to change the dropped rectangle
283 * So, we pass the height of text area of control as the height.
284 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
285 * message.
287 lphc->wState |= CBF_NORESIZE;
288 NtUserSetWindowPos( lphc->self,
289 NULL,
290 0, 0,
291 windowRect.right - windowRect.left,
292 newComboHeight,
293 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
294 lphc->wState &= ~CBF_NORESIZE;
296 CBCalcPlacement(lphc);
297 CBResetPos(lphc, FALSE);
300 /***********************************************************************
301 * CBCalcPlacement
303 * Set up component coordinates given valid lphc->RectCombo.
305 static void CBCalcPlacement(HEADCOMBO *combo)
307 /* Start with the client rectangle. */
308 GetClientRect(combo->self, &combo->textRect);
310 /* Remove the borders */
311 InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
313 /* Chop off the bottom part to fit with the height of the text area. */
314 combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
316 /* The button starts the same vertical position as the text area. */
317 combo->buttonRect = combo->textRect;
319 /* If the combobox is "simple" there is no button. */
320 if (CB_GETTYPE(combo) == CBS_SIMPLE)
321 combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0;
322 else
325 * Let's assume the combobox button is the same width as the
326 * scrollbar button.
327 * size the button horizontally and cut-off the text area.
329 combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
330 combo->textRect.right = combo->buttonRect.left;
333 /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
334 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
335 combo->textRect.right -= COMBO_EDITBUTTONSPACE();
337 /* If we have an edit control, we space it away from the borders slightly. */
338 if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
339 InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
341 /* Adjust the size of the listbox popup. */
342 if (CB_GETTYPE(combo) == CBS_SIMPLE)
344 GetClientRect(combo->self, &combo->droppedRect);
345 combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
347 else
349 /* Make sure the dropped width is as large as the combobox itself. */
350 if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
352 combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
354 /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
355 with the right side of the combobox. */
356 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
357 combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
359 else
360 combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
363 /* Disallow negative window width */
364 if (combo->textRect.right < combo->textRect.left)
365 combo->textRect.right = combo->textRect.left;
367 TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
368 wine_dbgstr_rect(&combo->droppedRect));
371 /***********************************************************************
372 * CBGetDroppedControlRect
374 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
376 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
377 of the combo box and the lower right corner of the listbox */
379 GetWindowRect(lphc->self, lpRect);
381 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
382 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
386 /***********************************************************************
387 * COMBO_Create
389 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
390 BOOL unicode )
392 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
393 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
395 lphc->owner = hwndParent;
397 lphc->droppedWidth = 0;
399 lphc->item_height = combo_get_text_height(lphc);
402 * The first time we go through, we want to measure the ownerdraw item
404 lphc->wState |= CBF_MEASUREITEM;
406 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
408 if( lphc->owner || !(style & WS_VISIBLE) )
410 UINT lbeStyle = 0;
411 UINT lbeExStyle = 0;
414 * Initialize the dropped rect to the size of the client area of the
415 * control and then, force all the areas of the combobox to be
416 * recalculated.
418 GetClientRect( hwnd, &lphc->droppedRect );
419 CBCalcPlacement(lphc);
422 * Adjust the position of the popup listbox if it's necessary
424 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
426 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
429 * If it's a dropdown, the listbox is offset
431 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
432 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
434 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
435 lphc->droppedRect.bottom = lphc->droppedRect.top;
436 if (lphc->droppedRect.right < lphc->droppedRect.left)
437 lphc->droppedRect.right = lphc->droppedRect.left;
438 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
441 /* create listbox popup */
443 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
444 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
446 if( lphc->dwStyle & CBS_SORT )
447 lbeStyle |= LBS_SORT;
448 if( lphc->dwStyle & CBS_HASSTRINGS )
449 lbeStyle |= LBS_HASSTRINGS;
450 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
451 lbeStyle |= LBS_NOINTEGRALHEIGHT;
452 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
453 lbeStyle |= LBS_DISABLENOSCROLL;
455 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
457 lbeStyle |= WS_VISIBLE;
460 * In win 95 look n feel, the listbox in the simple combobox has
461 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
463 lbeStyle &= ~WS_BORDER;
464 lbeExStyle |= WS_EX_CLIENTEDGE;
466 else
468 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
471 if (unicode)
472 lphc->hWndLBox = CreateWindowExW(lbeExStyle, L"ComboLBox", NULL, lbeStyle,
473 lphc->droppedRect.left,
474 lphc->droppedRect.top,
475 lphc->droppedRect.right - lphc->droppedRect.left,
476 lphc->droppedRect.bottom - lphc->droppedRect.top,
477 hwnd, (HMENU)ID_CB_LISTBOX,
478 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
479 else
480 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
481 lphc->droppedRect.left,
482 lphc->droppedRect.top,
483 lphc->droppedRect.right - lphc->droppedRect.left,
484 lphc->droppedRect.bottom - lphc->droppedRect.top,
485 hwnd, (HMENU)ID_CB_LISTBOX,
486 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
488 if( lphc->hWndLBox )
490 BOOL bEdit = TRUE;
491 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
493 if( lphc->wState & CBF_EDIT )
495 if( lphc->dwStyle & CBS_OEMCONVERT )
496 lbeStyle |= ES_OEMCONVERT;
497 if( lphc->dwStyle & CBS_AUTOHSCROLL )
498 lbeStyle |= ES_AUTOHSCROLL;
499 if( lphc->dwStyle & CBS_LOWERCASE )
500 lbeStyle |= ES_LOWERCASE;
501 else if( lphc->dwStyle & CBS_UPPERCASE )
502 lbeStyle |= ES_UPPERCASE;
504 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
506 if (unicode)
507 lphc->hWndEdit = CreateWindowExW(0, L"Edit", NULL, lbeStyle,
508 lphc->textRect.left, lphc->textRect.top,
509 lphc->textRect.right - lphc->textRect.left,
510 lphc->textRect.bottom - lphc->textRect.top,
511 hwnd, (HMENU)ID_CB_EDIT,
512 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
513 else
514 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
515 lphc->textRect.left, lphc->textRect.top,
516 lphc->textRect.right - lphc->textRect.left,
517 lphc->textRect.bottom - lphc->textRect.top,
518 hwnd, (HMENU)ID_CB_EDIT,
519 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
521 if( !lphc->hWndEdit )
522 bEdit = FALSE;
525 if( bEdit )
527 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
529 /* Now do the trick with parent */
530 NtUserSetParent( lphc->hWndLBox, HWND_DESKTOP );
532 * If the combo is a dropdown, we must resize the control
533 * to fit only the text area and button. To do this,
534 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
535 * will take care of setting the height for us.
537 CBForceDummyResize(lphc);
540 TRACE("init done\n");
541 return 0;
543 ERR("edit control failure.\n");
544 } else ERR("listbox failure.\n");
545 } else ERR("no owner for visible combo.\n");
547 /* CreateWindow() will send WM_NCDESTROY to cleanup */
549 return -1;
552 /***********************************************************************
553 * CBPaintButton
555 * Paint combo button (normal, pressed, and disabled states).
557 static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
559 UINT buttonState = DFCS_SCROLLCOMBOBOX;
561 if (IsRectEmpty(&lphc->buttonRect))
562 return;
564 if( lphc->wState & CBF_NOREDRAW )
565 return;
568 if (lphc->wState & CBF_BUTTONDOWN)
569 buttonState |= DFCS_PUSHED;
571 if (CB_DISABLED(lphc))
572 buttonState |= DFCS_INACTIVE;
574 DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
577 /***********************************************************************
578 * COMBO_PrepareColors
580 * This method will sent the appropriate WM_CTLCOLOR message to
581 * prepare and setup the colors for the combo's DC.
583 * It also returns the brush to use for the background.
585 static HBRUSH COMBO_PrepareColors(
586 LPHEADCOMBO lphc,
587 HDC hDC)
589 HBRUSH hBkgBrush;
592 * Get the background brush for this control.
594 if (CB_DISABLED(lphc))
596 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
597 (WPARAM)hDC, (LPARAM)lphc->self );
600 * We have to change the text color since WM_CTLCOLORSTATIC will
601 * set it to the "enabled" color. This is the same behavior as the
602 * edit control
604 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
606 else
608 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
609 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
610 (WPARAM)hDC, (LPARAM)lphc->self );
614 * Catch errors.
616 if( !hBkgBrush )
617 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
619 return hBkgBrush;
622 /***********************************************************************
623 * CBPaintText
625 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
627 static void CBPaintText(
628 LPHEADCOMBO lphc,
629 HDC hdc_paint)
631 RECT rectEdit = lphc->textRect;
632 INT id, size = 0;
633 LPWSTR pText = NULL;
635 TRACE("\n");
637 /* follow Windows combobox that sends a bunch of text
638 * inquiries to its listbox while processing WM_PAINT. */
640 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
642 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
643 if (size == LB_ERR)
644 FIXME("LB_ERR probably not handled yet\n");
645 if( (pText = malloc((size + 1) * sizeof(WCHAR))) )
647 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
648 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
649 pText[size] = '\0'; /* just in case */
650 } else return;
653 if( lphc->wState & CBF_EDIT )
655 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : L"" );
656 if( lphc->wState & CBF_FOCUSED )
657 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
659 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
661 /* paint text field ourselves */
662 HDC hdc = hdc_paint ? hdc_paint : NtUserGetDC(lphc->self);
663 UINT itemState = ODS_COMBOBOXEDIT;
664 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
665 HBRUSH hPrevBrush, hBkgBrush;
668 * Give ourselves some space.
670 InflateRect( &rectEdit, -1, -1 );
672 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
673 hPrevBrush = SelectObject( hdc, hBkgBrush );
674 FillRect( hdc, &rectEdit, hBkgBrush );
676 if( CB_OWNERDRAWN(lphc) )
678 DRAWITEMSTRUCT dis;
679 HRGN clipRegion;
680 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
682 /* setup state for DRAWITEM message. Owner will highlight */
683 if ( (lphc->wState & CBF_FOCUSED) &&
684 !(lphc->wState & CBF_DROPPED) )
685 itemState |= ODS_SELECTED | ODS_FOCUS;
687 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
689 dis.CtlType = ODT_COMBOBOX;
690 dis.CtlID = ctlid;
691 dis.hwndItem = lphc->self;
692 dis.itemAction = ODA_DRAWENTIRE;
693 dis.itemID = id;
694 dis.itemState = itemState;
695 dis.hDC = hdc;
696 dis.rcItem = rectEdit;
697 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
700 * Clip the DC and have the parent draw the item.
702 clipRegion = set_control_clipping( hdc, &rectEdit );
704 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
706 SelectClipRgn( hdc, clipRegion );
707 if (clipRegion) DeleteObject( clipRegion );
709 else
711 if ( (lphc->wState & CBF_FOCUSED) &&
712 !(lphc->wState & CBF_DROPPED) ) {
714 /* highlight */
715 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
716 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
717 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
720 ExtTextOutW( hdc,
721 rectEdit.left + 1,
722 rectEdit.top + 1,
723 ETO_OPAQUE | ETO_CLIPPED,
724 &rectEdit,
725 pText ? pText : L"" , size, NULL );
727 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
728 DrawFocusRect( hdc, &rectEdit );
731 if( hPrevFont )
732 SelectObject(hdc, hPrevFont );
734 if( hPrevBrush )
735 SelectObject( hdc, hPrevBrush );
737 if (!hdc_paint)
738 NtUserReleaseDC( lphc->self, hdc );
740 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 /***********************************************************************
766 * COMBO_Paint
768 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
770 PAINTSTRUCT ps;
771 HDC hDC;
773 hDC = hParamDC ? hParamDC : NtUserBeginPaint( lphc->self, &ps );
775 TRACE("hdc=%p\n", hDC);
777 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
779 HBRUSH hPrevBrush, hBkgBrush;
782 * Retrieve the background brush and select it in the
783 * DC.
785 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
787 hPrevBrush = SelectObject( hDC, hBkgBrush );
788 if (!(lphc->wState & CBF_EDIT))
789 FillRect(hDC, &lphc->textRect, hBkgBrush);
792 * In non 3.1 look, there is a sunken border on the combobox
794 CBPaintBorder(lphc, hDC);
795 CBPaintButton(lphc, hDC);
797 /* paint the edit control padding area */
798 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
800 RECT rPadEdit = lphc->textRect;
802 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
804 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
807 if( !(lphc->wState & CBF_EDIT) )
808 CBPaintText( lphc, hDC );
810 if( hPrevBrush )
811 SelectObject( hDC, hPrevBrush );
814 if (!hParamDC)
815 NtUserEndPaint( lphc->self, &ps );
817 return 0;
820 /***********************************************************************
821 * CBUpdateLBox
823 * Select listbox entry according to the contents of the edit control.
825 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
827 INT length, idx;
828 LPWSTR pText = NULL;
830 idx = LB_ERR;
831 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
833 if( length > 0 )
834 pText = malloc((length + 1) * sizeof(WCHAR));
836 TRACE("\t edit text length %i\n", length );
838 if( pText )
840 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
841 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
842 free(pText);
845 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
847 /* probably superfluous but Windows sends this too */
848 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
849 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
851 return idx;
854 /***********************************************************************
855 * CBUpdateEdit
857 * Copy a listbox entry to the edit control.
859 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
861 INT length;
862 LPWSTR pText = NULL;
864 TRACE("\t %i\n", index );
866 if( index >= 0 ) /* got an entry */
868 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
869 if( length != LB_ERR)
871 if( (pText = malloc((length + 1) * sizeof(WCHAR))) )
873 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
878 if( CB_HASSTRINGS(lphc) )
880 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
881 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)L"");
882 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
885 if( lphc->wState & CBF_FOCUSED )
886 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
888 free(pText);
891 /***********************************************************************
892 * CBDropDown
894 * Show listbox popup.
896 static void CBDropDown( LPHEADCOMBO lphc )
898 HMONITOR monitor;
899 MONITORINFO mon_info;
900 RECT rect,r;
901 int nItems;
902 int nDroppedHeight;
904 TRACE("[%p]: drop down\n", lphc->self);
906 CB_NOTIFY( lphc, CBN_DROPDOWN );
908 /* set selection */
910 lphc->wState |= CBF_DROPPED;
911 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
913 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
915 /* Update edit only if item is in the list */
916 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
917 CBUpdateEdit( lphc, lphc->droppedIndex );
919 else
921 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
923 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
924 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
925 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
928 /* now set popup position */
929 GetWindowRect( lphc->self, &rect );
932 * If it's a dropdown, the listbox is offset
934 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
935 rect.left += COMBO_EDITBUTTONSPACE();
937 /* if the dropped height is greater than the total height of the dropped
938 items list, then force the drop down list height to be the total height
939 of the items in the dropped list */
941 /* And Remove any extra space (Best Fit) */
942 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
943 /* if listbox length has been set directly by its handle */
944 GetWindowRect(lphc->hWndLBox, &r);
945 if (nDroppedHeight < r.bottom - r.top)
946 nDroppedHeight = r.bottom - r.top;
947 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
949 if (nItems > 0)
951 int nHeight;
952 int nIHeight;
954 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
956 nHeight = nIHeight*nItems;
958 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
959 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
962 r.left = rect.left;
963 r.top = rect.bottom;
964 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
965 r.bottom = r.top + nDroppedHeight;
967 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
968 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
969 mon_info.cbSize = sizeof(mon_info);
970 GetMonitorInfoW( monitor, &mon_info );
972 if (r.bottom > mon_info.rcWork.bottom)
974 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
975 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
978 NtUserSetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
979 SWP_NOACTIVATE | SWP_SHOWWINDOW );
982 if( !(lphc->wState & CBF_NOREDRAW) )
983 NtUserRedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
985 EnableWindow( lphc->hWndLBox, TRUE );
986 if (GetCapture() != lphc->self)
987 NtUserSetCapture(lphc->hWndLBox);
990 /***********************************************************************
991 * CBRollUp
993 * Hide listbox popup.
995 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
997 HWND hWnd = lphc->self;
999 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1000 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1002 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1004 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1007 if( lphc->wState & CBF_DROPPED )
1009 RECT rect;
1011 lphc->wState &= ~CBF_DROPPED;
1012 NtUserShowWindow( lphc->hWndLBox, SW_HIDE );
1014 if(GetCapture() == lphc->hWndLBox)
1016 ReleaseCapture();
1019 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1021 rect = lphc->buttonRect;
1023 else
1025 if( bButton )
1027 UnionRect( &rect,
1028 &lphc->buttonRect,
1029 &lphc->textRect);
1031 else
1032 rect = lphc->textRect;
1034 bButton = TRUE;
1037 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1038 NtUserRedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1039 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1040 CB_NOTIFY( lphc, CBN_CLOSEUP );
1045 /***********************************************************************
1046 * COMBO_FlipListbox
1048 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1050 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1052 if( lphc->wState & CBF_DROPPED )
1054 CBRollUp( lphc, ok, bRedrawButton );
1055 return FALSE;
1058 CBDropDown( lphc );
1059 return TRUE;
1062 /***********************************************************************
1063 * CBRepaintButton
1065 static void CBRepaintButton( LPHEADCOMBO lphc )
1067 NtUserInvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1068 UpdateWindow(lphc->self);
1071 /***********************************************************************
1072 * COMBO_SetFocus
1074 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1076 if( !(lphc->wState & CBF_FOCUSED) )
1078 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1079 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1081 /* This is wrong. Message sequences seem to indicate that this
1082 is set *after* the notify. */
1083 /* lphc->wState |= CBF_FOCUSED; */
1085 if( !(lphc->wState & CBF_EDIT) )
1086 NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
1088 CB_NOTIFY( lphc, CBN_SETFOCUS );
1089 lphc->wState |= CBF_FOCUSED;
1093 /***********************************************************************
1094 * COMBO_KillFocus
1096 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1098 HWND hWnd = lphc->self;
1100 if( lphc->wState & CBF_FOCUSED )
1102 CBRollUp( lphc, FALSE, TRUE );
1103 if( IsWindow( hWnd ) )
1105 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1106 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1108 lphc->wState &= ~CBF_FOCUSED;
1110 /* redraw text */
1111 if( !(lphc->wState & CBF_EDIT) )
1112 NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
1114 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1119 /***********************************************************************
1120 * COMBO_Command
1122 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1124 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1126 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1128 switch( HIWORD(wParam) >> 8 )
1130 case (EN_SETFOCUS >> 8):
1132 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1134 COMBO_SetFocus( lphc );
1135 break;
1137 case (EN_KILLFOCUS >> 8):
1139 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1141 /* NOTE: it seems that Windows' edit control sends an
1142 * undocumented message WM_USER + 0x1B instead of this
1143 * notification (only when it happens to be a part of
1144 * the combo). ?? - AK.
1147 COMBO_KillFocus( lphc );
1148 break;
1151 case (EN_CHANGE >> 8):
1153 * In some circumstances (when the selection of the combobox
1154 * is changed for example) we don't want the EN_CHANGE notification
1155 * to be forwarded to the parent of the combobox. This code
1156 * checks a flag that is set in these occasions and ignores the
1157 * notification.
1159 if (lphc->wState & CBF_NOLBSELECT)
1161 lphc->wState &= ~CBF_NOLBSELECT;
1163 else
1165 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1168 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1169 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1170 break;
1172 case (EN_UPDATE >> 8):
1173 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1174 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1175 break;
1177 case (EN_ERRSPACE >> 8):
1178 CB_NOTIFY( lphc, CBN_ERRSPACE );
1181 else if( lphc->hWndLBox == hWnd )
1183 switch( (short)HIWORD(wParam) )
1185 case LBN_ERRSPACE:
1186 CB_NOTIFY( lphc, CBN_ERRSPACE );
1187 break;
1189 case LBN_DBLCLK:
1190 CB_NOTIFY( lphc, CBN_DBLCLK );
1191 break;
1193 case LBN_SELCHANGE:
1194 case LBN_SELCANCEL:
1196 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1198 /* do not roll up if selection is being tracked
1199 * by arrow keys in the dropdown listbox */
1200 if (!(lphc->wState & CBF_NOROLLUP))
1202 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1204 else lphc->wState &= ~CBF_NOROLLUP;
1206 CB_NOTIFY( lphc, CBN_SELCHANGE );
1208 if( HIWORD(wParam) == LBN_SELCHANGE)
1210 if( lphc->wState & CBF_EDIT )
1211 lphc->wState |= CBF_NOLBSELECT;
1212 CBPaintText( lphc, NULL );
1214 break;
1216 case LBN_SETFOCUS:
1217 case LBN_KILLFOCUS:
1218 /* nothing to do here since ComboLBox always resets the focus to its
1219 * combo/edit counterpart */
1220 break;
1223 return 0;
1226 /***********************************************************************
1227 * COMBO_ItemOp
1229 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1231 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1233 HWND hWnd = lphc->self;
1234 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1236 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1238 switch( msg )
1240 case WM_DELETEITEM:
1242 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1243 lpIS->CtlType = ODT_COMBOBOX;
1244 lpIS->CtlID = id;
1245 lpIS->hwndItem = hWnd;
1246 break;
1248 case WM_DRAWITEM:
1250 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1251 lpIS->CtlType = ODT_COMBOBOX;
1252 lpIS->CtlID = id;
1253 lpIS->hwndItem = hWnd;
1254 break;
1256 case WM_COMPAREITEM:
1258 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1259 lpIS->CtlType = ODT_COMBOBOX;
1260 lpIS->CtlID = id;
1261 lpIS->hwndItem = hWnd;
1262 break;
1264 case WM_MEASUREITEM:
1266 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1267 lpIS->CtlType = ODT_COMBOBOX;
1268 lpIS->CtlID = id;
1269 break;
1272 return SendMessageW(lphc->owner, msg, id, lParam);
1276 /***********************************************************************
1277 * COMBO_GetTextW
1279 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1281 INT length;
1283 if( lphc->wState & CBF_EDIT )
1284 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1286 /* get it from the listbox */
1288 if (!count || !buf) return 0;
1289 if( lphc->hWndLBox )
1291 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1292 if (idx == LB_ERR) goto error;
1293 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1294 if (length == LB_ERR) goto error;
1296 /* 'length' is without the terminating character */
1297 if (length >= count)
1299 WCHAR *lpBuffer = malloc((length + 1) * sizeof(WCHAR));
1300 if (!lpBuffer) goto error;
1301 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1303 /* truncate if buffer is too short */
1304 if (length != LB_ERR)
1306 lstrcpynW( buf, lpBuffer, count );
1307 length = count;
1309 free(lpBuffer);
1311 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1313 if (length == LB_ERR) return 0;
1314 return length;
1317 error: /* error - truncate string, return zero */
1318 buf[0] = 0;
1319 return 0;
1323 /***********************************************************************
1324 * COMBO_GetTextA
1326 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1327 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1329 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1331 INT length;
1333 if( lphc->wState & CBF_EDIT )
1334 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1336 /* get it from the listbox */
1338 if (!count || !buf) return 0;
1339 if( lphc->hWndLBox )
1341 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1342 if (idx == LB_ERR) goto error;
1343 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1344 if (length == LB_ERR) goto error;
1346 /* 'length' is without the terminating character */
1347 if (length >= count)
1349 char *lpBuffer = malloc(length + 1);
1350 if (!lpBuffer) goto error;
1351 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1353 /* truncate if buffer is too short */
1354 if (length != LB_ERR)
1356 lstrcpynA( buf, lpBuffer, count );
1357 length = count;
1359 free(lpBuffer);
1361 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1363 if (length == LB_ERR) return 0;
1364 return length;
1367 error: /* error - truncate string, return zero */
1368 buf[0] = 0;
1369 return 0;
1373 /***********************************************************************
1374 * CBResetPos
1376 * This function sets window positions according to the updated
1377 * component placement struct.
1379 static void CBResetPos(HEADCOMBO *combo, BOOL redraw)
1381 BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
1383 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1384 * sizing messages */
1385 if (combo->wState & CBF_EDIT)
1386 NtUserSetWindowPos( combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
1387 combo->textRect.right - combo->textRect.left,
1388 combo->textRect.bottom - combo->textRect.top,
1389 SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0) );
1391 NtUserSetWindowPos( combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
1392 combo->droppedRect.right - combo->droppedRect.left,
1393 combo->droppedRect.bottom - combo->droppedRect.top,
1394 SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0) );
1396 if (drop)
1398 if (combo->wState & CBF_DROPPED)
1400 combo->wState &= ~CBF_DROPPED;
1401 NtUserShowWindow( combo->hWndLBox, SW_HIDE );
1404 if (redraw && !(combo->wState & CBF_NOREDRAW))
1405 NtUserRedrawWindow( combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1409 /***********************************************************************
1410 * COMBO_Size
1412 static void COMBO_Size( HEADCOMBO *lphc )
1414 if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
1415 return;
1418 * Those controls are always the same height. So we have to make sure
1419 * they are not resized to another value.
1421 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1423 int newComboHeight, curComboHeight, curComboWidth;
1424 RECT rc;
1426 GetWindowRect(lphc->self, &rc);
1427 curComboHeight = rc.bottom - rc.top;
1428 curComboWidth = rc.right - rc.left;
1429 newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1432 * Resizing a combobox has another side effect, it resizes the dropped
1433 * rectangle as well. However, it does it only if the new height for the
1434 * combobox is more than the height it should have. In other words,
1435 * if the application resizing the combobox only had the intention to resize
1436 * the actual control, for example, to do the layout of a dialog that is
1437 * resized, the height of the dropdown is not changed.
1439 if( curComboHeight > newComboHeight )
1441 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%ld, oldDropTop=%ld\n",
1442 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1443 lphc->droppedRect.top);
1444 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1447 * Restore original height
1449 if (curComboHeight != newComboHeight)
1451 lphc->wState |= CBF_NORESIZE;
1452 NtUserSetWindowPos( lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1453 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW );
1454 lphc->wState &= ~CBF_NORESIZE;
1458 CBCalcPlacement(lphc);
1460 CBResetPos(lphc, FALSE);
1464 /***********************************************************************
1465 * COMBO_Font
1467 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1469 lphc->hFont = hFont;
1470 lphc->item_height = combo_get_text_height(lphc);
1473 * Propagate to owned windows.
1475 if( lphc->wState & CBF_EDIT )
1476 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1477 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1480 * Redo the layout of the control.
1482 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1484 CBCalcPlacement(lphc);
1486 CBResetPos(lphc, TRUE);
1488 else
1490 CBForceDummyResize(lphc);
1495 /***********************************************************************
1496 * COMBO_SetItemHeight
1498 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1500 LRESULT lRet = CB_ERR;
1502 if( index == -1 ) /* set text field height */
1504 if( height < 32768 )
1506 lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1509 * Redo the layout of the control.
1511 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1513 CBCalcPlacement(lphc);
1515 CBResetPos(lphc, TRUE);
1517 else
1519 CBForceDummyResize(lphc);
1522 lRet = height;
1525 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1526 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1527 return lRet;
1530 /***********************************************************************
1531 * COMBO_SelectString
1533 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1535 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText) :
1536 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1537 if( index >= 0 )
1539 if( lphc->wState & CBF_EDIT )
1540 CBUpdateEdit( lphc, index );
1541 else
1543 NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
1546 return (LRESULT)index;
1549 /***********************************************************************
1550 * COMBO_LButtonDown
1552 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1554 POINT pt;
1555 BOOL bButton;
1556 HWND hWnd = lphc->self;
1558 pt.x = (short)LOWORD(lParam);
1559 pt.y = (short)HIWORD(lParam);
1560 bButton = PtInRect(&lphc->buttonRect, pt);
1562 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1563 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1565 lphc->wState |= CBF_BUTTONDOWN;
1566 if( lphc->wState & CBF_DROPPED )
1568 /* got a click to cancel selection */
1570 lphc->wState &= ~CBF_BUTTONDOWN;
1571 CBRollUp( lphc, TRUE, FALSE );
1572 if( !IsWindow( hWnd ) ) return;
1574 if( lphc->wState & CBF_CAPTURE )
1576 lphc->wState &= ~CBF_CAPTURE;
1577 ReleaseCapture();
1580 else
1582 /* drop down the listbox and start tracking */
1584 lphc->wState |= CBF_CAPTURE;
1585 NtUserSetCapture( hWnd );
1586 CBDropDown( lphc );
1588 if( bButton ) CBRepaintButton( lphc );
1592 /***********************************************************************
1593 * COMBO_LButtonUp
1595 * Release capture and stop tracking if needed.
1597 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1599 if( lphc->wState & CBF_CAPTURE )
1601 lphc->wState &= ~CBF_CAPTURE;
1602 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1604 INT index = CBUpdateLBox( lphc, TRUE );
1605 /* Update edit only if item is in the list */
1606 if(index >= 0)
1608 lphc->wState |= CBF_NOLBSELECT;
1609 CBUpdateEdit( lphc, index );
1610 lphc->wState &= ~CBF_NOLBSELECT;
1613 ReleaseCapture();
1614 NtUserSetCapture(lphc->hWndLBox);
1617 if( lphc->wState & CBF_BUTTONDOWN )
1619 lphc->wState &= ~CBF_BUTTONDOWN;
1620 CBRepaintButton( lphc );
1624 /***********************************************************************
1625 * COMBO_MouseMove
1627 * Two things to do - track combo button and release capture when
1628 * pointer goes into the listbox.
1630 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1632 POINT pt;
1633 RECT lbRect;
1635 pt.x = (short)LOWORD(lParam);
1636 pt.y = (short)HIWORD(lParam);
1638 if( lphc->wState & CBF_BUTTONDOWN )
1640 BOOL bButton;
1642 bButton = PtInRect(&lphc->buttonRect, pt);
1644 if( !bButton )
1646 lphc->wState &= ~CBF_BUTTONDOWN;
1647 CBRepaintButton( lphc );
1651 GetClientRect( lphc->hWndLBox, &lbRect );
1652 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1653 if( PtInRect(&lbRect, pt) )
1655 lphc->wState &= ~CBF_CAPTURE;
1656 ReleaseCapture();
1657 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1659 /* hand over pointer tracking */
1660 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1664 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1666 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1667 return FALSE;
1669 pcbi->rcItem = lphc->textRect;
1670 pcbi->rcButton = lphc->buttonRect;
1671 pcbi->stateButton = 0;
1672 if (lphc->wState & CBF_BUTTONDOWN)
1673 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1674 if (IsRectEmpty(&lphc->buttonRect))
1675 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1676 pcbi->hwndCombo = lphc->self;
1677 pcbi->hwndItem = lphc->hWndEdit;
1678 pcbi->hwndList = lphc->hWndLBox;
1679 return TRUE;
1682 /***********************************************************************
1683 * ComboWndProc_common
1685 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1687 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1689 TRACE("[%p]: msg %s wp %08Ix lp %08Ix\n",
1690 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1692 if (!IsWindow(hwnd)) return 0;
1694 if( lphc || message == WM_NCCREATE )
1695 switch(message)
1698 /* System messages */
1700 case WM_NCCREATE:
1702 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1703 ((LPCREATESTRUCTA)lParam)->style;
1704 return COMBO_NCCreate(hwnd, style);
1706 case WM_NCDESTROY:
1707 COMBO_NCDestroy(lphc);
1708 break;/* -> DefWindowProc */
1710 case WM_CREATE:
1712 HWND hwndParent;
1713 LONG style;
1714 if(unicode)
1716 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1717 style = ((LPCREATESTRUCTW)lParam)->style;
1719 else
1721 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1722 style = ((LPCREATESTRUCTA)lParam)->style;
1724 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1727 case WM_PRINTCLIENT:
1728 /* Fallthrough */
1729 case WM_PAINT:
1730 /* wParam may contain a valid HDC! */
1731 return COMBO_Paint(lphc, (HDC)wParam);
1733 case WM_ERASEBKGND:
1734 /* do all painting in WM_PAINT like Windows does */
1735 return 1;
1737 case WM_GETDLGCODE:
1739 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1740 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1742 int vk = (int)((LPMSG)lParam)->wParam;
1744 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1745 result |= DLGC_WANTMESSAGE;
1747 return result;
1749 case WM_SIZE:
1750 COMBO_Size( lphc );
1751 return TRUE;
1752 case WM_SETFONT:
1753 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1754 return TRUE;
1755 case WM_GETFONT:
1756 return (LRESULT)lphc->hFont;
1757 case WM_SETFOCUS:
1758 if( lphc->wState & CBF_EDIT ) {
1759 NtUserSetFocus( lphc->hWndEdit );
1760 /* The first time focus is received, select all the text */
1761 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;
1769 case WM_KILLFOCUS:
1771 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1772 if( !hwndFocus ||
1773 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1774 COMBO_KillFocus( lphc );
1775 return TRUE;
1777 case WM_COMMAND:
1778 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1779 case WM_GETTEXT:
1780 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1781 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1782 case WM_SETTEXT:
1783 case WM_GETTEXTLENGTH:
1784 case WM_CLEAR:
1785 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1787 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1788 if (j == -1) return 0;
1789 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1790 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1792 else if( lphc->wState & CBF_EDIT )
1794 LRESULT ret;
1795 lphc->wState |= CBF_NOEDITNOTIFY;
1796 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1797 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1798 lphc->wState &= ~CBF_NOEDITNOTIFY;
1799 return ret;
1801 else return CB_ERR;
1802 case WM_CUT:
1803 case WM_PASTE:
1804 case WM_COPY:
1805 if( lphc->wState & CBF_EDIT )
1807 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1808 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1810 else return CB_ERR;
1812 case WM_DRAWITEM:
1813 case WM_DELETEITEM:
1814 case WM_COMPAREITEM:
1815 case WM_MEASUREITEM:
1816 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 NtUserInvalidateRect(lphc->self, NULL, TRUE);
1824 return TRUE;
1825 case WM_SETREDRAW:
1826 if( wParam )
1827 lphc->wState &= ~CBF_NOREDRAW;
1828 else
1829 lphc->wState |= CBF_NOREDRAW;
1831 if( lphc->wState & CBF_EDIT )
1832 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1833 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1834 return 0;
1835 case WM_SYSKEYDOWN:
1836 if( KEYDATA_ALT & HIWORD(lParam) )
1837 if( wParam == VK_UP || wParam == VK_DOWN )
1838 COMBO_FlipListbox( lphc, FALSE, FALSE );
1839 return 0;
1841 case WM_KEYDOWN:
1842 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1843 (lphc->wState & CBF_DROPPED))
1845 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1846 return TRUE;
1848 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1850 COMBO_FlipListbox( lphc, FALSE, FALSE );
1851 return TRUE;
1853 /* fall through */
1854 case WM_CHAR:
1855 case WM_IME_CHAR:
1857 HWND hwndTarget;
1859 if( lphc->wState & CBF_EDIT )
1860 hwndTarget = lphc->hWndEdit;
1861 else
1862 hwndTarget = lphc->hWndLBox;
1864 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
1865 SendMessageA(hwndTarget, message, wParam, lParam);
1867 case WM_LBUTTONDOWN:
1868 if (!(lphc->wState & CBF_FOCUSED)) NtUserSetFocus( lphc->self );
1869 if (lphc->wState & CBF_FOCUSED) COMBO_LButtonDown( lphc, lParam );
1870 return TRUE;
1871 case WM_LBUTTONUP:
1872 COMBO_LButtonUp( lphc );
1873 return TRUE;
1874 case WM_MOUSEMOVE:
1875 if( lphc->wState & CBF_CAPTURE )
1876 COMBO_MouseMove( lphc, wParam, lParam );
1877 return TRUE;
1879 case WM_MOUSEWHEEL:
1880 if (wParam & (MK_SHIFT | MK_CONTROL))
1881 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
1882 DefWindowProcA(hwnd, message, wParam, lParam);
1884 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1885 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1886 return TRUE;
1888 case WM_CTLCOLOR:
1889 case WM_CTLCOLORMSGBOX:
1890 case WM_CTLCOLOREDIT:
1891 case WM_CTLCOLORLISTBOX:
1892 case WM_CTLCOLORBTN:
1893 case WM_CTLCOLORDLG:
1894 case WM_CTLCOLORSCROLLBAR:
1895 case WM_CTLCOLORSTATIC:
1896 if (lphc->owner)
1897 return SendMessageW(lphc->owner, message, wParam, lParam);
1898 break;
1900 /* Combo messages */
1902 case CB_ADDSTRING:
1903 if( unicode )
1905 if( lphc->dwStyle & CBS_LOWERCASE )
1906 CharLowerW((LPWSTR)lParam);
1907 else if( lphc->dwStyle & CBS_UPPERCASE )
1908 CharUpperW((LPWSTR)lParam);
1909 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1911 else /* unlike the unicode version, the ansi version does not overwrite
1912 the string if converting case */
1914 char *string = NULL;
1915 LRESULT ret;
1916 if( lphc->dwStyle & CBS_LOWERCASE )
1918 string = strdup((char *)lParam);
1919 CharLowerA(string);
1922 else if( lphc->dwStyle & CBS_UPPERCASE )
1924 string = strdup((char *)lParam);
1925 CharUpperA(string);
1928 ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
1929 free(string);
1930 return ret;
1932 case CB_INSERTSTRING:
1933 if( unicode )
1935 if( lphc->dwStyle & CBS_LOWERCASE )
1936 CharLowerW((LPWSTR)lParam);
1937 else if( lphc->dwStyle & CBS_UPPERCASE )
1938 CharUpperW((LPWSTR)lParam);
1939 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1941 else
1943 if( lphc->dwStyle & CBS_LOWERCASE )
1944 CharLowerA((LPSTR)lParam);
1945 else if( lphc->dwStyle & CBS_UPPERCASE )
1946 CharUpperA((LPSTR)lParam);
1948 return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1950 case CB_DELETESTRING:
1951 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
1952 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1953 case CB_SELECTSTRING:
1954 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
1955 case CB_FINDSTRING:
1956 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
1957 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1958 case CB_FINDSTRINGEXACT:
1959 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
1960 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1961 case CB_SETITEMHEIGHT:
1962 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1963 case CB_GETITEMHEIGHT:
1964 if( (INT)wParam >= 0 ) /* listbox item */
1965 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1966 return CBGetTextAreaHeight(lphc, FALSE);
1967 case CB_RESETCONTENT:
1968 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
1969 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
1970 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)L"");
1971 else
1972 NtUserInvalidateRect(lphc->self, NULL, TRUE);
1973 return TRUE;
1974 case CB_INITSTORAGE:
1975 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1976 case CB_GETHORIZONTALEXTENT:
1977 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1978 case CB_SETHORIZONTALEXTENT:
1979 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1980 case CB_GETTOPINDEX:
1981 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1982 case CB_GETLOCALE:
1983 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1984 case CB_SETLOCALE:
1985 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1986 case CB_SETDROPPEDWIDTH:
1987 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
1988 (INT)wParam >= 32768 )
1989 return CB_ERR;
1990 /* new value must be higher than combobox width */
1991 if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
1992 lphc->droppedWidth = wParam;
1993 else if(wParam)
1994 lphc->droppedWidth = 0;
1996 /* recalculate the combobox area */
1997 CBCalcPlacement(lphc);
1999 /* fall through */
2000 case CB_GETDROPPEDWIDTH:
2001 if( lphc->droppedWidth )
2002 return lphc->droppedWidth;
2003 return lphc->droppedRect.right - lphc->droppedRect.left;
2004 case CB_GETDROPPEDCONTROLRECT:
2005 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2006 return CB_OKAY;
2007 case CB_GETDROPPEDSTATE:
2008 return (lphc->wState & CBF_DROPPED) != 0;
2009 case CB_DIR:
2010 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2011 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2013 case CB_SHOWDROPDOWN:
2014 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2016 if( wParam )
2018 if( !(lphc->wState & CBF_DROPPED) )
2019 CBDropDown( lphc );
2021 else
2022 if( lphc->wState & CBF_DROPPED )
2023 CBRollUp( lphc, FALSE, TRUE );
2025 return TRUE;
2026 case CB_GETCOUNT:
2027 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2028 case CB_GETCURSEL:
2029 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2030 case CB_SETCURSEL:
2031 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2032 if( lParam >= 0 )
2033 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2035 /* no LBN_SELCHANGE in this case, update manually */
2036 CBPaintText( lphc, NULL );
2037 lphc->wState &= ~CBF_SELCHANGE;
2038 return lParam;
2039 case CB_GETLBTEXT:
2040 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2041 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2042 case CB_GETLBTEXTLEN:
2043 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2044 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2045 case CB_GETITEMDATA:
2046 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2047 case CB_SETITEMDATA:
2048 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2049 case CB_GETEDITSEL:
2050 /* Edit checks passed parameters itself */
2051 if( lphc->wState & CBF_EDIT )
2052 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2053 return CB_ERR;
2054 case CB_SETEDITSEL:
2055 if( lphc->wState & CBF_EDIT )
2056 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2057 (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2058 return CB_ERR;
2059 case CB_SETEXTENDEDUI:
2060 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2061 return CB_ERR;
2062 if( wParam )
2063 lphc->wState |= CBF_EUI;
2064 else lphc->wState &= ~CBF_EUI;
2065 return CB_OKAY;
2066 case CB_GETEXTENDEDUI:
2067 return (lphc->wState & CBF_EUI) != 0;
2068 case CB_GETCOMBOBOXINFO:
2069 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2070 case CB_LIMITTEXT:
2071 if( lphc->wState & CBF_EDIT )
2072 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2073 return TRUE;
2074 default:
2075 if (message >= WM_USER)
2076 WARN("unknown msg WM_USER+%04x wp=%04Ix lp=%08Ix\n",
2077 message - WM_USER, wParam, lParam );
2078 break;
2080 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2081 DefWindowProcA(hwnd, message, wParam, lParam);
2084 /*************************************************************************
2085 * GetComboBoxInfo (USER32.@)
2087 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2088 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2090 TRACE("(%p, %p)\n", hwndCombo, pcbi);
2091 return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);