user32: Don't wait for other threads to process WM_NCDESTROY.
[wine.git] / dlls / user32 / combo.c
blobdcfa661f97fd15c806f92c3c89af005ac41630f2
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 #include <stdarg.h>
25 #include <string.h>
27 #define OEMRESOURCE
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "wine/unicode.h"
34 #include "user_private.h"
35 #include "win.h"
36 #include "controls.h"
37 #include "winternl.h"
38 #include "wine/debug.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 /*********************************************************************
79 * combo class descriptor
81 static const WCHAR comboboxW[] = {'C','o','m','b','o','B','o','x',0};
82 const struct builtin_class_descr COMBO_builtin_class =
84 comboboxW, /* name */
85 CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */
86 WINPROC_COMBO, /* proc */
87 sizeof(HEADCOMBO *), /* extra */
88 IDC_ARROW, /* cursor */
89 0 /* brush */
93 /***********************************************************************
94 * COMBO_Init
96 * Load combo button bitmap.
98 static BOOL COMBO_Init(void)
100 HDC hDC;
102 if( hComboBmp ) return TRUE;
103 if( (hDC = CreateCompatibleDC(0)) )
105 BOOL bRet = FALSE;
106 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
108 BITMAP bm;
109 HBITMAP hPrevB;
110 RECT r;
112 GetObjectW( hComboBmp, sizeof(bm), &bm );
113 CBitHeight = bm.bmHeight;
114 CBitWidth = bm.bmWidth;
116 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
118 hPrevB = SelectObject( hDC, hComboBmp);
119 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
120 InvertRect( hDC, &r );
121 SelectObject( hDC, hPrevB );
122 bRet = TRUE;
124 DeleteDC( hDC );
125 return bRet;
127 return FALSE;
130 /***********************************************************************
131 * COMBO_NCCreate
133 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
135 LPHEADCOMBO lphc;
137 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
139 lphc->self = hwnd;
140 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
142 /* some braindead apps do try to use scrollbar/border flags */
144 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
145 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
148 * We also have to remove the client edge style to make sure
149 * we don't end-up with a non client area.
151 SetWindowLongW( hwnd, GWL_EXSTYLE,
152 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
154 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
155 lphc->dwStyle |= CBS_HASSTRINGS;
156 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
157 lphc->wState |= CBF_NOTIFY;
159 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
160 return TRUE;
162 return FALSE;
165 /***********************************************************************
166 * COMBO_NCDestroy
168 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
171 if( lphc )
173 TRACE("[%p]: freeing storage\n", lphc->self);
175 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
176 DestroyWindow( lphc->hWndLBox );
178 SetWindowLongPtrW( lphc->self, 0, 0 );
179 HeapFree( GetProcessHeap(), 0, lphc );
181 return 0;
184 /***********************************************************************
185 * CBGetTextAreaHeight
187 * This method will calculate the height of the text area of the
188 * combobox.
189 * The height of the text area is set in two ways.
190 * It can be set explicitly through a combobox message or through a
191 * WM_MEASUREITEM callback.
192 * If this is not the case, the height is set to font height + 4px
193 * This height was determined through experimentation.
194 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
196 static INT CBGetTextAreaHeight(
197 HWND hwnd,
198 LPHEADCOMBO lphc)
200 INT iTextItemHeight;
202 if( lphc->editHeight ) /* explicitly set height */
204 iTextItemHeight = lphc->editHeight;
206 else
208 TEXTMETRICW tm;
209 HDC hDC = GetDC(hwnd);
210 HFONT hPrevFont = 0;
211 INT baseUnitY;
213 if (lphc->hFont)
214 hPrevFont = SelectObject( hDC, lphc->hFont );
216 GetTextMetricsW(hDC, &tm);
218 baseUnitY = tm.tmHeight;
220 if( hPrevFont )
221 SelectObject( hDC, hPrevFont );
223 ReleaseDC(hwnd, hDC);
225 iTextItemHeight = baseUnitY + 4;
229 * Check the ownerdraw case if we haven't asked the parent the size
230 * of the item yet.
232 if ( CB_OWNERDRAWN(lphc) &&
233 (lphc->wState & CBF_MEASUREITEM) )
235 MEASUREITEMSTRUCT measureItem;
236 RECT clientRect;
237 INT originalItemHeight = iTextItemHeight;
238 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
241 * We use the client rect for the width of the item.
243 GetClientRect(hwnd, &clientRect);
245 lphc->wState &= ~CBF_MEASUREITEM;
248 * Send a first one to measure the size of the text area
250 measureItem.CtlType = ODT_COMBOBOX;
251 measureItem.CtlID = id;
252 measureItem.itemID = -1;
253 measureItem.itemWidth = clientRect.right;
254 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
255 measureItem.itemData = 0;
256 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
257 iTextItemHeight = 6 + measureItem.itemHeight;
260 * Send a second one in the case of a fixed ownerdraw list to calculate the
261 * size of the list items. (we basically do this on behalf of the listbox)
263 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
265 measureItem.CtlType = ODT_COMBOBOX;
266 measureItem.CtlID = id;
267 measureItem.itemID = 0;
268 measureItem.itemWidth = clientRect.right;
269 measureItem.itemHeight = originalItemHeight;
270 measureItem.itemData = 0;
271 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
272 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
276 * Keep the size for the next time
278 lphc->editHeight = iTextItemHeight;
281 return iTextItemHeight;
284 /***********************************************************************
285 * CBForceDummyResize
287 * The dummy resize is used for listboxes that have a popup to trigger
288 * a re-arranging of the contents of the combobox and the recalculation
289 * of the size of the "real" control window.
291 static void CBForceDummyResize(
292 LPHEADCOMBO lphc)
294 RECT windowRect;
295 int newComboHeight;
297 newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
299 GetWindowRect(lphc->self, &windowRect);
302 * We have to be careful, resizing a combobox also has the meaning that the
303 * dropped rect will be resized. In this case, we want to trigger a resize
304 * to recalculate layout but we don't want to change the dropped rectangle
305 * So, we pass the height of text area of control as the height.
306 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
307 * message.
309 SetWindowPos( lphc->self,
310 NULL,
311 0, 0,
312 windowRect.right - windowRect.left,
313 newComboHeight,
314 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
317 /***********************************************************************
318 * CBCalcPlacement
320 * Set up component coordinates given valid lphc->RectCombo.
322 static void CBCalcPlacement(
323 HWND hwnd,
324 LPHEADCOMBO lphc,
325 LPRECT lprEdit,
326 LPRECT lprButton,
327 LPRECT lprLB)
330 * Again, start with the client rectangle.
332 GetClientRect(hwnd, lprEdit);
335 * Remove the borders
337 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
340 * Chop off the bottom part to fit with the height of the text area.
342 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
345 * The button starts the same vertical position as the text area.
347 CopyRect(lprButton, lprEdit);
350 * If the combobox is "simple" there is no button.
352 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
353 lprButton->left = lprButton->right = lprButton->bottom = 0;
354 else
357 * Let's assume the combobox button is the same width as the
358 * scrollbar button.
359 * size the button horizontally and cut-off the text area.
361 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
362 lprEdit->right = lprButton->left;
366 * In the case of a dropdown, there is an additional spacing between the
367 * text area and the button.
369 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
371 lprEdit->right -= COMBO_EDITBUTTONSPACE();
375 * If we have an edit control, we space it away from the borders slightly.
377 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
379 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
383 * Adjust the size of the listbox popup.
385 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
388 * Use the client rectangle to initialize the listbox rectangle
390 GetClientRect(hwnd, lprLB);
393 * Then, chop-off the top part.
395 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
397 else
400 * Make sure the dropped width is as large as the combobox itself.
402 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
404 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
407 * In the case of a dropdown, the popup listbox is offset to the right.
408 * so, we want to make sure it's flush with the right side of the
409 * combobox
411 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
412 lprLB->right -= COMBO_EDITBUTTONSPACE();
414 else
415 lprLB->right = lprLB->left + lphc->droppedWidth;
418 /* don't allow negative window width */
419 if (lprEdit->right < lprEdit->left)
420 lprEdit->right = lprEdit->left;
422 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
424 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
426 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
429 /***********************************************************************
430 * CBGetDroppedControlRect
432 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
434 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
435 of the combo box and the lower right corner of the listbox */
437 GetWindowRect(lphc->self, lpRect);
439 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
440 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
444 /***********************************************************************
445 * COMBO_Create
447 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
448 BOOL unicode )
450 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
451 static const WCHAR editName[] = {'E','d','i','t',0};
453 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
454 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
456 lphc->owner = hwndParent;
459 * The item height and dropped width are not set when the control
460 * is created.
462 lphc->droppedWidth = lphc->editHeight = 0;
465 * The first time we go through, we want to measure the ownerdraw item
467 lphc->wState |= CBF_MEASUREITEM;
469 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
471 if( lphc->owner || !(style & WS_VISIBLE) )
473 UINT lbeStyle = 0;
474 UINT lbeExStyle = 0;
477 * Initialize the dropped rect to the size of the client area of the
478 * control and then, force all the areas of the combobox to be
479 * recalculated.
481 GetClientRect( hwnd, &lphc->droppedRect );
482 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
485 * Adjust the position of the popup listbox if it's necessary
487 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
489 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
492 * If it's a dropdown, the listbox is offset
494 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
495 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
497 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
498 lphc->droppedRect.bottom = lphc->droppedRect.top;
499 if (lphc->droppedRect.right < lphc->droppedRect.left)
500 lphc->droppedRect.right = lphc->droppedRect.left;
501 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
504 /* create listbox popup */
506 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
507 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
509 if( lphc->dwStyle & CBS_SORT )
510 lbeStyle |= LBS_SORT;
511 if( lphc->dwStyle & CBS_HASSTRINGS )
512 lbeStyle |= LBS_HASSTRINGS;
513 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
514 lbeStyle |= LBS_NOINTEGRALHEIGHT;
515 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
516 lbeStyle |= LBS_DISABLENOSCROLL;
518 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
520 lbeStyle |= WS_VISIBLE;
523 * In win 95 look n feel, the listbox in the simple combobox has
524 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
526 lbeStyle &= ~WS_BORDER;
527 lbeExStyle |= WS_EX_CLIENTEDGE;
529 else
531 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
534 if (unicode)
535 lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
536 lphc->droppedRect.left,
537 lphc->droppedRect.top,
538 lphc->droppedRect.right - lphc->droppedRect.left,
539 lphc->droppedRect.bottom - lphc->droppedRect.top,
540 hwnd, (HMENU)ID_CB_LISTBOX,
541 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
542 else
543 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
544 lphc->droppedRect.left,
545 lphc->droppedRect.top,
546 lphc->droppedRect.right - lphc->droppedRect.left,
547 lphc->droppedRect.bottom - lphc->droppedRect.top,
548 hwnd, (HMENU)ID_CB_LISTBOX,
549 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
551 if( lphc->hWndLBox )
553 BOOL bEdit = TRUE;
554 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
556 if( lphc->wState & CBF_EDIT )
558 if( lphc->dwStyle & CBS_OEMCONVERT )
559 lbeStyle |= ES_OEMCONVERT;
560 if( lphc->dwStyle & CBS_AUTOHSCROLL )
561 lbeStyle |= ES_AUTOHSCROLL;
562 if( lphc->dwStyle & CBS_LOWERCASE )
563 lbeStyle |= ES_LOWERCASE;
564 else if( lphc->dwStyle & CBS_UPPERCASE )
565 lbeStyle |= ES_UPPERCASE;
567 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
569 if (unicode)
570 lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
571 lphc->textRect.left, lphc->textRect.top,
572 lphc->textRect.right - lphc->textRect.left,
573 lphc->textRect.bottom - lphc->textRect.top,
574 hwnd, (HMENU)ID_CB_EDIT,
575 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
576 else
577 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
578 lphc->textRect.left, lphc->textRect.top,
579 lphc->textRect.right - lphc->textRect.left,
580 lphc->textRect.bottom - lphc->textRect.top,
581 hwnd, (HMENU)ID_CB_EDIT,
582 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
584 if( !lphc->hWndEdit )
585 bEdit = FALSE;
588 if( bEdit )
590 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
592 /* Now do the trick with parent */
593 SetParent(lphc->hWndLBox, HWND_DESKTOP);
595 * If the combo is a dropdown, we must resize the control
596 * to fit only the text area and button. To do this,
597 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
598 * will take care of setting the height for us.
600 CBForceDummyResize(lphc);
603 TRACE("init done\n");
604 return 0;
606 ERR("edit control failure.\n");
607 } else ERR("listbox failure.\n");
608 } else ERR("no owner for visible combo.\n");
610 /* CreateWindow() will send WM_NCDESTROY to cleanup */
612 return -1;
615 /***********************************************************************
616 * CBPaintButton
618 * Paint combo button (normal, pressed, and disabled states).
620 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
622 UINT buttonState = DFCS_SCROLLCOMBOBOX;
624 if( lphc->wState & CBF_NOREDRAW )
625 return;
628 if (lphc->wState & CBF_BUTTONDOWN)
629 buttonState |= DFCS_PUSHED;
631 if (CB_DISABLED(lphc))
632 buttonState |= DFCS_INACTIVE;
634 DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
637 /***********************************************************************
638 * COMBO_PrepareColors
640 * This method will sent the appropriate WM_CTLCOLOR message to
641 * prepare and setup the colors for the combo's DC.
643 * It also returns the brush to use for the background.
645 static HBRUSH COMBO_PrepareColors(
646 LPHEADCOMBO lphc,
647 HDC hDC)
649 HBRUSH hBkgBrush;
652 * Get the background brush for this control.
654 if (CB_DISABLED(lphc))
656 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
657 (WPARAM)hDC, (LPARAM)lphc->self );
660 * We have to change the text color since WM_CTLCOLORSTATIC will
661 * set it to the "enabled" color. This is the same behavior as the
662 * edit control
664 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
666 else
668 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
669 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
670 (WPARAM)hDC, (LPARAM)lphc->self );
674 * Catch errors.
676 if( !hBkgBrush )
677 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
679 return hBkgBrush;
682 /***********************************************************************
683 * CBPaintText
685 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
687 static void CBPaintText(
688 LPHEADCOMBO lphc,
689 HDC hdc_paint)
691 RECT rectEdit = lphc->textRect;
692 INT id, size = 0;
693 LPWSTR pText = NULL;
695 TRACE("\n");
697 /* follow Windows combobox that sends a bunch of text
698 * inquiries to its listbox while processing WM_PAINT. */
700 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
702 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
703 if (size == LB_ERR)
704 FIXME("LB_ERR probably not handled yet\n");
705 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
707 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
708 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
709 pText[size] = '\0'; /* just in case */
710 } else return;
713 if( lphc->wState & CBF_EDIT )
715 static const WCHAR empty_stringW[] = { 0 };
716 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
717 if( lphc->wState & CBF_FOCUSED )
718 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
720 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
722 /* paint text field ourselves */
723 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
724 UINT itemState = ODS_COMBOBOXEDIT;
725 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
726 HBRUSH hPrevBrush, hBkgBrush;
729 * Give ourselves some space.
731 InflateRect( &rectEdit, -1, -1 );
733 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
734 hPrevBrush = SelectObject( hdc, hBkgBrush );
735 FillRect( hdc, &rectEdit, hBkgBrush );
737 if( CB_OWNERDRAWN(lphc) )
739 DRAWITEMSTRUCT dis;
740 HRGN clipRegion;
741 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
743 /* setup state for DRAWITEM message. Owner will highlight */
744 if ( (lphc->wState & CBF_FOCUSED) &&
745 !(lphc->wState & CBF_DROPPED) )
746 itemState |= ODS_SELECTED | ODS_FOCUS;
748 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
750 dis.CtlType = ODT_COMBOBOX;
751 dis.CtlID = ctlid;
752 dis.hwndItem = lphc->self;
753 dis.itemAction = ODA_DRAWENTIRE;
754 dis.itemID = id;
755 dis.itemState = itemState;
756 dis.hDC = hdc;
757 dis.rcItem = rectEdit;
758 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
761 * Clip the DC and have the parent draw the item.
763 clipRegion = set_control_clipping( hdc, &rectEdit );
765 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
767 SelectClipRgn( hdc, clipRegion );
768 if (clipRegion) DeleteObject( clipRegion );
770 else
772 static const WCHAR empty_stringW[] = { 0 };
774 if ( (lphc->wState & CBF_FOCUSED) &&
775 !(lphc->wState & CBF_DROPPED) ) {
777 /* highlight */
778 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
779 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
780 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
783 ExtTextOutW( hdc,
784 rectEdit.left + 1,
785 rectEdit.top + 1,
786 ETO_OPAQUE | ETO_CLIPPED,
787 &rectEdit,
788 pText ? pText : empty_stringW , size, NULL );
790 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
791 DrawFocusRect( hdc, &rectEdit );
794 if( hPrevFont )
795 SelectObject(hdc, hPrevFont );
797 if( hPrevBrush )
798 SelectObject( hdc, hPrevBrush );
800 if( !hdc_paint )
801 ReleaseDC( lphc->self, hdc );
803 HeapFree( GetProcessHeap(), 0, pText );
806 /***********************************************************************
807 * CBPaintBorder
809 static void CBPaintBorder(
810 HWND hwnd,
811 const HEADCOMBO *lphc,
812 HDC hdc)
814 RECT clientRect;
816 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
818 GetClientRect(hwnd, &clientRect);
820 else
822 clientRect = lphc->textRect;
824 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
825 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
828 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
831 /***********************************************************************
832 * COMBO_Paint
834 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
836 PAINTSTRUCT ps;
837 HDC hDC;
839 hDC = (hParamDC) ? hParamDC
840 : BeginPaint( lphc->self, &ps);
842 TRACE("hdc=%p\n", hDC);
844 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
846 HBRUSH hPrevBrush, hBkgBrush;
849 * Retrieve the background brush and select it in the
850 * DC.
852 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
854 hPrevBrush = SelectObject( hDC, hBkgBrush );
855 if (!(lphc->wState & CBF_EDIT))
856 FillRect(hDC, &lphc->textRect, hBkgBrush);
859 * In non 3.1 look, there is a sunken border on the combobox
861 CBPaintBorder(lphc->self, lphc, hDC);
863 if( !IsRectEmpty(&lphc->buttonRect) )
865 CBPaintButton(lphc, hDC, lphc->buttonRect);
868 /* paint the edit control padding area */
869 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
871 RECT rPadEdit = lphc->textRect;
873 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
875 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
878 if( !(lphc->wState & CBF_EDIT) )
879 CBPaintText( lphc, hDC );
881 if( hPrevBrush )
882 SelectObject( hDC, hPrevBrush );
885 if( !hParamDC )
886 EndPaint(lphc->self, &ps);
888 return 0;
891 /***********************************************************************
892 * CBUpdateLBox
894 * Select listbox entry according to the contents of the edit control.
896 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
898 INT length, idx;
899 LPWSTR pText = NULL;
901 idx = LB_ERR;
902 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
904 if( length > 0 )
905 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
907 TRACE("\t edit text length %i\n", length );
909 if( pText )
911 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
912 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
913 HeapFree( GetProcessHeap(), 0, pText );
916 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
918 /* probably superfluous but Windows sends this too */
919 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
920 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
922 return idx;
925 /***********************************************************************
926 * CBUpdateEdit
928 * Copy a listbox entry to the edit control.
930 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
932 INT length;
933 LPWSTR pText = NULL;
934 static const WCHAR empty_stringW[] = { 0 };
936 TRACE("\t %i\n", index );
938 if( index >= 0 ) /* got an entry */
940 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
941 if( length != LB_ERR)
943 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
945 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
950 if( CB_HASSTRINGS(lphc) )
952 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
953 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
954 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
957 if( lphc->wState & CBF_FOCUSED )
958 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
960 HeapFree( GetProcessHeap(), 0, pText );
963 /***********************************************************************
964 * CBDropDown
966 * Show listbox popup.
968 static void CBDropDown( LPHEADCOMBO lphc )
970 HMONITOR monitor;
971 MONITORINFO mon_info;
972 RECT rect,r;
973 int nItems;
974 int nDroppedHeight;
976 TRACE("[%p]: drop down\n", lphc->self);
978 CB_NOTIFY( lphc, CBN_DROPDOWN );
980 /* set selection */
982 lphc->wState |= CBF_DROPPED;
983 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
985 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
987 /* Update edit only if item is in the list */
988 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
989 CBUpdateEdit( lphc, lphc->droppedIndex );
991 else
993 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
995 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
996 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
997 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1000 /* now set popup position */
1001 GetWindowRect( lphc->self, &rect );
1004 * If it's a dropdown, the listbox is offset
1006 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1007 rect.left += COMBO_EDITBUTTONSPACE();
1009 /* if the dropped height is greater than the total height of the dropped
1010 items list, then force the drop down list height to be the total height
1011 of the items in the dropped list */
1013 /* And Remove any extra space (Best Fit) */
1014 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1015 /* if listbox length has been set directly by its handle */
1016 GetWindowRect(lphc->hWndLBox, &r);
1017 if (nDroppedHeight < r.bottom - r.top)
1018 nDroppedHeight = r.bottom - r.top;
1019 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1021 if (nItems > 0)
1023 int nHeight;
1024 int nIHeight;
1026 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1028 nHeight = nIHeight*nItems;
1030 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1031 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1033 if (nDroppedHeight < nHeight)
1035 if (nItems < 5)
1036 nDroppedHeight = (nItems+1)*nIHeight;
1037 else if (nDroppedHeight < 6*nIHeight)
1038 nDroppedHeight = 6*nIHeight;
1042 r.left = rect.left;
1043 r.top = rect.bottom;
1044 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
1045 r.bottom = r.top + nDroppedHeight;
1047 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1048 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1049 mon_info.cbSize = sizeof(mon_info);
1050 GetMonitorInfoW( monitor, &mon_info );
1052 if (r.bottom > mon_info.rcWork.bottom)
1054 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
1055 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1058 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1059 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1062 if( !(lphc->wState & CBF_NOREDRAW) )
1063 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1064 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1066 EnableWindow( lphc->hWndLBox, TRUE );
1067 if (GetCapture() != lphc->self)
1068 SetCapture(lphc->hWndLBox);
1071 /***********************************************************************
1072 * CBRollUp
1074 * Hide listbox popup.
1076 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1078 HWND hWnd = lphc->self;
1080 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1081 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1083 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1085 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1088 if( lphc->wState & CBF_DROPPED )
1090 RECT rect;
1092 lphc->wState &= ~CBF_DROPPED;
1093 ShowWindow( lphc->hWndLBox, SW_HIDE );
1095 if(GetCapture() == lphc->hWndLBox)
1097 ReleaseCapture();
1100 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1102 rect = lphc->buttonRect;
1104 else
1106 if( bButton )
1108 UnionRect( &rect,
1109 &lphc->buttonRect,
1110 &lphc->textRect);
1112 else
1113 rect = lphc->textRect;
1115 bButton = TRUE;
1118 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1119 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1120 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1121 CB_NOTIFY( lphc, CBN_CLOSEUP );
1126 /***********************************************************************
1127 * COMBO_FlipListbox
1129 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1131 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1133 if( lphc->wState & CBF_DROPPED )
1135 CBRollUp( lphc, ok, bRedrawButton );
1136 return FALSE;
1139 CBDropDown( lphc );
1140 return TRUE;
1143 /***********************************************************************
1144 * CBRepaintButton
1146 static void CBRepaintButton( LPHEADCOMBO lphc )
1148 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1149 UpdateWindow(lphc->self);
1152 /***********************************************************************
1153 * COMBO_SetFocus
1155 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1157 if( !(lphc->wState & CBF_FOCUSED) )
1159 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1160 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1162 /* This is wrong. Message sequences seem to indicate that this
1163 is set *after* the notify. */
1164 /* lphc->wState |= CBF_FOCUSED; */
1166 if( !(lphc->wState & CBF_EDIT) )
1167 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1169 CB_NOTIFY( lphc, CBN_SETFOCUS );
1170 lphc->wState |= CBF_FOCUSED;
1174 /***********************************************************************
1175 * COMBO_KillFocus
1177 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1179 HWND hWnd = lphc->self;
1181 if( lphc->wState & CBF_FOCUSED )
1183 CBRollUp( lphc, FALSE, TRUE );
1184 if( IsWindow( hWnd ) )
1186 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1187 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1189 lphc->wState &= ~CBF_FOCUSED;
1191 /* redraw text */
1192 if( !(lphc->wState & CBF_EDIT) )
1193 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1195 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1200 /***********************************************************************
1201 * COMBO_Command
1203 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1205 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1207 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1209 switch( HIWORD(wParam) >> 8 )
1211 case (EN_SETFOCUS >> 8):
1213 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1215 COMBO_SetFocus( lphc );
1216 break;
1218 case (EN_KILLFOCUS >> 8):
1220 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1222 /* NOTE: it seems that Windows' edit control sends an
1223 * undocumented message WM_USER + 0x1B instead of this
1224 * notification (only when it happens to be a part of
1225 * the combo). ?? - AK.
1228 COMBO_KillFocus( lphc );
1229 break;
1232 case (EN_CHANGE >> 8):
1234 * In some circumstances (when the selection of the combobox
1235 * is changed for example) we don't want the EN_CHANGE notification
1236 * to be forwarded to the parent of the combobox. This code
1237 * checks a flag that is set in these occasions and ignores the
1238 * notification.
1240 if (lphc->wState & CBF_NOLBSELECT)
1242 lphc->wState &= ~CBF_NOLBSELECT;
1244 else
1246 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1249 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1250 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1251 break;
1253 case (EN_UPDATE >> 8):
1254 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1255 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1256 break;
1258 case (EN_ERRSPACE >> 8):
1259 CB_NOTIFY( lphc, CBN_ERRSPACE );
1262 else if( lphc->hWndLBox == hWnd )
1264 switch( (short)HIWORD(wParam) )
1266 case LBN_ERRSPACE:
1267 CB_NOTIFY( lphc, CBN_ERRSPACE );
1268 break;
1270 case LBN_DBLCLK:
1271 CB_NOTIFY( lphc, CBN_DBLCLK );
1272 break;
1274 case LBN_SELCHANGE:
1275 case LBN_SELCANCEL:
1277 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1279 /* do not roll up if selection is being tracked
1280 * by arrow keys in the dropdown listbox */
1281 if (!(lphc->wState & CBF_NOROLLUP))
1283 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1285 else lphc->wState &= ~CBF_NOROLLUP;
1287 CB_NOTIFY( lphc, CBN_SELCHANGE );
1289 if( HIWORD(wParam) == LBN_SELCHANGE)
1291 if( lphc->wState & CBF_EDIT )
1292 lphc->wState |= CBF_NOLBSELECT;
1293 CBPaintText( lphc, NULL );
1295 break;
1297 case LBN_SETFOCUS:
1298 case LBN_KILLFOCUS:
1299 /* nothing to do here since ComboLBox always resets the focus to its
1300 * combo/edit counterpart */
1301 break;
1304 return 0;
1307 /***********************************************************************
1308 * COMBO_ItemOp
1310 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1312 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1314 HWND hWnd = lphc->self;
1315 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1317 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1319 switch( msg )
1321 case WM_DELETEITEM:
1323 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1324 lpIS->CtlType = ODT_COMBOBOX;
1325 lpIS->CtlID = id;
1326 lpIS->hwndItem = hWnd;
1327 break;
1329 case WM_DRAWITEM:
1331 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1332 lpIS->CtlType = ODT_COMBOBOX;
1333 lpIS->CtlID = id;
1334 lpIS->hwndItem = hWnd;
1335 break;
1337 case WM_COMPAREITEM:
1339 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1340 lpIS->CtlType = ODT_COMBOBOX;
1341 lpIS->CtlID = id;
1342 lpIS->hwndItem = hWnd;
1343 break;
1345 case WM_MEASUREITEM:
1347 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1348 lpIS->CtlType = ODT_COMBOBOX;
1349 lpIS->CtlID = id;
1350 break;
1353 return SendMessageW(lphc->owner, msg, id, lParam);
1357 /***********************************************************************
1358 * COMBO_GetTextW
1360 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1362 INT length;
1364 if( lphc->wState & CBF_EDIT )
1365 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1367 /* get it from the listbox */
1369 if (!count || !buf) return 0;
1370 if( lphc->hWndLBox )
1372 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1373 if (idx == LB_ERR) goto error;
1374 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1375 if (length == LB_ERR) goto error;
1377 /* 'length' is without the terminating character */
1378 if (length >= count)
1380 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1381 if (!lpBuffer) goto error;
1382 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1384 /* truncate if buffer is too short */
1385 if (length != LB_ERR)
1387 lstrcpynW( buf, lpBuffer, count );
1388 length = count;
1390 HeapFree( GetProcessHeap(), 0, lpBuffer );
1392 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1394 if (length == LB_ERR) return 0;
1395 return length;
1398 error: /* error - truncate string, return zero */
1399 buf[0] = 0;
1400 return 0;
1404 /***********************************************************************
1405 * COMBO_GetTextA
1407 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1408 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1410 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1412 INT length;
1414 if( lphc->wState & CBF_EDIT )
1415 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1417 /* get it from the listbox */
1419 if (!count || !buf) return 0;
1420 if( lphc->hWndLBox )
1422 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1423 if (idx == LB_ERR) goto error;
1424 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1425 if (length == LB_ERR) goto error;
1427 /* 'length' is without the terminating character */
1428 if (length >= count)
1430 LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1431 if (!lpBuffer) goto error;
1432 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1434 /* truncate if buffer is too short */
1435 if (length != LB_ERR)
1437 lstrcpynA( buf, lpBuffer, count );
1438 length = count;
1440 HeapFree( GetProcessHeap(), 0, lpBuffer );
1442 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1444 if (length == LB_ERR) return 0;
1445 return length;
1448 error: /* error - truncate string, return zero */
1449 buf[0] = 0;
1450 return 0;
1454 /***********************************************************************
1455 * CBResetPos
1457 * This function sets window positions according to the updated
1458 * component placement struct.
1460 static void CBResetPos(
1461 LPHEADCOMBO lphc,
1462 const RECT *rectEdit,
1463 const RECT *rectLB,
1464 BOOL bRedraw)
1466 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1468 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1469 * sizing messages */
1471 if( lphc->wState & CBF_EDIT )
1472 SetWindowPos( lphc->hWndEdit, 0,
1473 rectEdit->left, rectEdit->top,
1474 rectEdit->right - rectEdit->left,
1475 rectEdit->bottom - rectEdit->top,
1476 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1478 SetWindowPos( lphc->hWndLBox, 0,
1479 rectLB->left, rectLB->top,
1480 rectLB->right - rectLB->left,
1481 rectLB->bottom - rectLB->top,
1482 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1484 if( bDrop )
1486 if( lphc->wState & CBF_DROPPED )
1488 lphc->wState &= ~CBF_DROPPED;
1489 ShowWindow( lphc->hWndLBox, SW_HIDE );
1492 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1493 RedrawWindow( lphc->self, NULL, 0,
1494 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1499 /***********************************************************************
1500 * COMBO_Size
1502 static void COMBO_Size( LPHEADCOMBO lphc )
1505 * Those controls are always the same height. So we have to make sure
1506 * they are not resized to another value.
1508 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1510 int newComboHeight, curComboHeight, curComboWidth;
1511 RECT rc;
1513 GetWindowRect(lphc->self, &rc);
1514 curComboHeight = rc.bottom - rc.top;
1515 curComboWidth = rc.right - rc.left;
1516 newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE();
1519 * Resizing a combobox has another side effect, it resizes the dropped
1520 * rectangle as well. However, it does it only if the new height for the
1521 * combobox is more than the height it should have. In other words,
1522 * if the application resizing the combobox only had the intention to resize
1523 * the actual control, for example, to do the layout of a dialog that is
1524 * resized, the height of the dropdown is not changed.
1526 if( curComboHeight > newComboHeight )
1528 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1529 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1530 lphc->droppedRect.top);
1531 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1534 * Restore original height
1536 if( curComboHeight != newComboHeight )
1537 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1538 SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW);
1541 CBCalcPlacement(lphc->self,
1542 lphc,
1543 &lphc->textRect,
1544 &lphc->buttonRect,
1545 &lphc->droppedRect);
1547 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1551 /***********************************************************************
1552 * COMBO_Font
1554 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1557 * Set the font
1559 lphc->hFont = hFont;
1562 * Propagate to owned windows.
1564 if( lphc->wState & CBF_EDIT )
1565 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1566 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1569 * Redo the layout of the control.
1571 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1573 CBCalcPlacement(lphc->self,
1574 lphc,
1575 &lphc->textRect,
1576 &lphc->buttonRect,
1577 &lphc->droppedRect);
1579 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1581 else
1583 CBForceDummyResize(lphc);
1588 /***********************************************************************
1589 * COMBO_SetItemHeight
1591 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1593 LRESULT lRet = CB_ERR;
1595 if( index == -1 ) /* set text field height */
1597 if( height < 32768 )
1599 lphc->editHeight = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1602 * Redo the layout of the control.
1604 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1606 CBCalcPlacement(lphc->self,
1607 lphc,
1608 &lphc->textRect,
1609 &lphc->buttonRect,
1610 &lphc->droppedRect);
1612 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1614 else
1616 CBForceDummyResize(lphc);
1619 lRet = height;
1622 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1623 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1624 return lRet;
1627 /***********************************************************************
1628 * COMBO_SelectString
1630 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1632 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText) :
1633 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1634 if( index >= 0 )
1636 if( lphc->wState & CBF_EDIT )
1637 CBUpdateEdit( lphc, index );
1638 else
1640 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1643 return (LRESULT)index;
1646 /***********************************************************************
1647 * COMBO_LButtonDown
1649 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1651 POINT pt;
1652 BOOL bButton;
1653 HWND hWnd = lphc->self;
1655 pt.x = (short)LOWORD(lParam);
1656 pt.y = (short)HIWORD(lParam);
1657 bButton = PtInRect(&lphc->buttonRect, pt);
1659 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1660 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1662 lphc->wState |= CBF_BUTTONDOWN;
1663 if( lphc->wState & CBF_DROPPED )
1665 /* got a click to cancel selection */
1667 lphc->wState &= ~CBF_BUTTONDOWN;
1668 CBRollUp( lphc, TRUE, FALSE );
1669 if( !IsWindow( hWnd ) ) return;
1671 if( lphc->wState & CBF_CAPTURE )
1673 lphc->wState &= ~CBF_CAPTURE;
1674 ReleaseCapture();
1677 else
1679 /* drop down the listbox and start tracking */
1681 lphc->wState |= CBF_CAPTURE;
1682 SetCapture( hWnd );
1683 CBDropDown( lphc );
1685 if( bButton ) CBRepaintButton( lphc );
1689 /***********************************************************************
1690 * COMBO_LButtonUp
1692 * Release capture and stop tracking if needed.
1694 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1696 if( lphc->wState & CBF_CAPTURE )
1698 lphc->wState &= ~CBF_CAPTURE;
1699 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1701 INT index = CBUpdateLBox( lphc, TRUE );
1702 /* Update edit only if item is in the list */
1703 if(index >= 0)
1705 lphc->wState |= CBF_NOLBSELECT;
1706 CBUpdateEdit( lphc, index );
1707 lphc->wState &= ~CBF_NOLBSELECT;
1710 ReleaseCapture();
1711 SetCapture(lphc->hWndLBox);
1714 if( lphc->wState & CBF_BUTTONDOWN )
1716 lphc->wState &= ~CBF_BUTTONDOWN;
1717 CBRepaintButton( lphc );
1721 /***********************************************************************
1722 * COMBO_MouseMove
1724 * Two things to do - track combo button and release capture when
1725 * pointer goes into the listbox.
1727 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1729 POINT pt;
1730 RECT lbRect;
1732 pt.x = (short)LOWORD(lParam);
1733 pt.y = (short)HIWORD(lParam);
1735 if( lphc->wState & CBF_BUTTONDOWN )
1737 BOOL bButton;
1739 bButton = PtInRect(&lphc->buttonRect, pt);
1741 if( !bButton )
1743 lphc->wState &= ~CBF_BUTTONDOWN;
1744 CBRepaintButton( lphc );
1748 GetClientRect( lphc->hWndLBox, &lbRect );
1749 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1750 if( PtInRect(&lbRect, pt) )
1752 lphc->wState &= ~CBF_CAPTURE;
1753 ReleaseCapture();
1754 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1756 /* hand over pointer tracking */
1757 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1761 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1763 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1764 return FALSE;
1766 pcbi->rcItem = lphc->textRect;
1767 pcbi->rcButton = lphc->buttonRect;
1768 pcbi->stateButton = 0;
1769 if (lphc->wState & CBF_BUTTONDOWN)
1770 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1771 if (IsRectEmpty(&lphc->buttonRect))
1772 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1773 pcbi->hwndCombo = lphc->self;
1774 pcbi->hwndItem = lphc->hWndEdit;
1775 pcbi->hwndList = lphc->hWndLBox;
1776 return TRUE;
1779 static char *strdupA(LPCSTR str)
1781 char *ret;
1782 DWORD len;
1784 if(!str) return NULL;
1786 len = strlen(str);
1787 ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1788 memcpy(ret, str, len + 1);
1789 return ret;
1792 /***********************************************************************
1793 * ComboWndProc_common
1795 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1797 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1799 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1800 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1802 if (!IsWindow(hwnd)) return 0;
1804 if( lphc || message == WM_NCCREATE )
1805 switch(message)
1808 /* System messages */
1810 case WM_NCCREATE:
1812 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1813 ((LPCREATESTRUCTA)lParam)->style;
1814 return COMBO_NCCreate(hwnd, style);
1816 case WM_NCDESTROY:
1817 COMBO_NCDestroy(lphc);
1818 break;/* -> DefWindowProc */
1820 case WM_CREATE:
1822 HWND hwndParent;
1823 LONG style;
1824 if(unicode)
1826 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1827 style = ((LPCREATESTRUCTW)lParam)->style;
1829 else
1831 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1832 style = ((LPCREATESTRUCTA)lParam)->style;
1834 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1837 case WM_PRINTCLIENT:
1838 /* Fallthrough */
1839 case WM_PAINT:
1840 /* wParam may contain a valid HDC! */
1841 return COMBO_Paint(lphc, (HDC)wParam);
1843 case WM_ERASEBKGND:
1844 /* do all painting in WM_PAINT like Windows does */
1845 return 1;
1847 case WM_GETDLGCODE:
1849 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1850 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1852 int vk = (int)((LPMSG)lParam)->wParam;
1854 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1855 result |= DLGC_WANTMESSAGE;
1857 return result;
1859 case WM_SIZE:
1860 if( lphc->hWndLBox &&
1861 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1862 return TRUE;
1863 case WM_SETFONT:
1864 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1865 return TRUE;
1866 case WM_GETFONT:
1867 return (LRESULT)lphc->hFont;
1868 case WM_SETFOCUS:
1869 if( lphc->wState & CBF_EDIT ) {
1870 SetFocus( lphc->hWndEdit );
1871 /* The first time focus is received, select all the text */
1872 if( !(lphc->wState & CBF_BEENFOCUSED) ) {
1873 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1874 lphc->wState |= CBF_BEENFOCUSED;
1877 else
1878 COMBO_SetFocus( lphc );
1879 return TRUE;
1880 case WM_KILLFOCUS:
1882 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1883 if( !hwndFocus ||
1884 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1885 COMBO_KillFocus( lphc );
1886 return TRUE;
1888 case WM_COMMAND:
1889 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1890 case WM_GETTEXT:
1891 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1892 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1893 case WM_SETTEXT:
1894 case WM_GETTEXTLENGTH:
1895 case WM_CLEAR:
1896 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1898 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1899 if (j == -1) return 0;
1900 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1901 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1903 else if( lphc->wState & CBF_EDIT )
1905 LRESULT ret;
1906 lphc->wState |= CBF_NOEDITNOTIFY;
1907 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1908 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1909 lphc->wState &= ~CBF_NOEDITNOTIFY;
1910 return ret;
1912 else return CB_ERR;
1913 case WM_CUT:
1914 case WM_PASTE:
1915 case WM_COPY:
1916 if( lphc->wState & CBF_EDIT )
1918 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1919 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1921 else return CB_ERR;
1923 case WM_DRAWITEM:
1924 case WM_DELETEITEM:
1925 case WM_COMPAREITEM:
1926 case WM_MEASUREITEM:
1927 return COMBO_ItemOp(lphc, message, lParam);
1928 case WM_ENABLE:
1929 if( lphc->wState & CBF_EDIT )
1930 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1931 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1933 /* Force the control to repaint when the enabled state changes. */
1934 InvalidateRect(lphc->self, NULL, TRUE);
1935 return TRUE;
1936 case WM_SETREDRAW:
1937 if( wParam )
1938 lphc->wState &= ~CBF_NOREDRAW;
1939 else
1940 lphc->wState |= CBF_NOREDRAW;
1942 if( lphc->wState & CBF_EDIT )
1943 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1944 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1945 return 0;
1946 case WM_SYSKEYDOWN:
1947 if( KEYDATA_ALT & HIWORD(lParam) )
1948 if( wParam == VK_UP || wParam == VK_DOWN )
1949 COMBO_FlipListbox( lphc, FALSE, FALSE );
1950 return 0;
1952 case WM_KEYDOWN:
1953 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1954 (lphc->wState & CBF_DROPPED))
1956 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1957 return TRUE;
1959 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1961 COMBO_FlipListbox( lphc, FALSE, FALSE );
1962 return TRUE;
1964 /* fall through */
1965 case WM_CHAR:
1966 case WM_IME_CHAR:
1968 HWND hwndTarget;
1970 if( lphc->wState & CBF_EDIT )
1971 hwndTarget = lphc->hWndEdit;
1972 else
1973 hwndTarget = lphc->hWndLBox;
1975 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
1976 SendMessageA(hwndTarget, message, wParam, lParam);
1978 case WM_LBUTTONDOWN:
1979 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1980 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1981 return TRUE;
1982 case WM_LBUTTONUP:
1983 COMBO_LButtonUp( lphc );
1984 return TRUE;
1985 case WM_MOUSEMOVE:
1986 if( lphc->wState & CBF_CAPTURE )
1987 COMBO_MouseMove( lphc, wParam, lParam );
1988 return TRUE;
1990 case WM_MOUSEWHEEL:
1991 if (wParam & (MK_SHIFT | MK_CONTROL))
1992 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
1993 DefWindowProcA(hwnd, message, wParam, lParam);
1995 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1996 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1997 return TRUE;
1999 /* Combo messages */
2001 case CB_ADDSTRING:
2002 if( unicode )
2004 if( lphc->dwStyle & CBS_LOWERCASE )
2005 CharLowerW((LPWSTR)lParam);
2006 else if( lphc->dwStyle & CBS_UPPERCASE )
2007 CharUpperW((LPWSTR)lParam);
2008 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2010 else /* unlike the unicode version, the ansi version does not overwrite
2011 the string if converting case */
2013 char *string = NULL;
2014 LRESULT ret;
2015 if( lphc->dwStyle & CBS_LOWERCASE )
2017 string = strdupA((LPSTR)lParam);
2018 CharLowerA(string);
2021 else if( lphc->dwStyle & CBS_UPPERCASE )
2023 string = strdupA((LPSTR)lParam);
2024 CharUpperA(string);
2027 ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
2028 HeapFree(GetProcessHeap(), 0, string);
2029 return ret;
2031 case CB_INSERTSTRING:
2032 if( unicode )
2034 if( lphc->dwStyle & CBS_LOWERCASE )
2035 CharLowerW((LPWSTR)lParam);
2036 else if( lphc->dwStyle & CBS_UPPERCASE )
2037 CharUpperW((LPWSTR)lParam);
2038 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2040 else
2042 if( lphc->dwStyle & CBS_LOWERCASE )
2043 CharLowerA((LPSTR)lParam);
2044 else if( lphc->dwStyle & CBS_UPPERCASE )
2045 CharUpperA((LPSTR)lParam);
2047 return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2049 case CB_DELETESTRING:
2050 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2051 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2052 case CB_SELECTSTRING:
2053 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2054 case CB_FINDSTRING:
2055 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2056 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2057 case CB_FINDSTRINGEXACT:
2058 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2059 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2060 case CB_SETITEMHEIGHT:
2061 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2062 case CB_GETITEMHEIGHT:
2063 if( (INT)wParam >= 0 ) /* listbox item */
2064 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2065 return CBGetTextAreaHeight(hwnd, lphc);
2066 case CB_RESETCONTENT:
2067 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2068 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2070 static const WCHAR empty_stringW[] = { 0 };
2071 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2073 else
2074 InvalidateRect(lphc->self, NULL, TRUE);
2075 return TRUE;
2076 case CB_INITSTORAGE:
2077 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2078 case CB_GETHORIZONTALEXTENT:
2079 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2080 case CB_SETHORIZONTALEXTENT:
2081 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2082 case CB_GETTOPINDEX:
2083 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2084 case CB_GETLOCALE:
2085 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2086 case CB_SETLOCALE:
2087 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2088 case CB_SETDROPPEDWIDTH:
2089 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
2090 (INT)wParam >= 32768 )
2091 return CB_ERR;
2092 /* new value must be higher than combobox width */
2093 if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2094 lphc->droppedWidth = wParam;
2095 else if(wParam)
2096 lphc->droppedWidth = 0;
2098 /* recalculate the combobox area */
2099 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
2101 /* fall through */
2102 case CB_GETDROPPEDWIDTH:
2103 if( lphc->droppedWidth )
2104 return lphc->droppedWidth;
2105 return lphc->droppedRect.right - lphc->droppedRect.left;
2106 case CB_GETDROPPEDCONTROLRECT:
2107 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2108 return CB_OKAY;
2109 case CB_GETDROPPEDSTATE:
2110 return (lphc->wState & CBF_DROPPED) != 0;
2111 case CB_DIR:
2112 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2113 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2115 case CB_SHOWDROPDOWN:
2116 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2118 if( wParam )
2120 if( !(lphc->wState & CBF_DROPPED) )
2121 CBDropDown( lphc );
2123 else
2124 if( lphc->wState & CBF_DROPPED )
2125 CBRollUp( lphc, FALSE, TRUE );
2127 return TRUE;
2128 case CB_GETCOUNT:
2129 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2130 case CB_GETCURSEL:
2131 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2132 case CB_SETCURSEL:
2133 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2134 if( lParam >= 0 )
2135 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2137 /* no LBN_SELCHANGE in this case, update manually */
2138 CBPaintText( lphc, NULL );
2139 lphc->wState &= ~CBF_SELCHANGE;
2140 return lParam;
2141 case CB_GETLBTEXT:
2142 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2143 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2144 case CB_GETLBTEXTLEN:
2145 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2146 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2147 case CB_GETITEMDATA:
2148 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2149 case CB_SETITEMDATA:
2150 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2151 case CB_GETEDITSEL:
2152 /* Edit checks passed parameters itself */
2153 if( lphc->wState & CBF_EDIT )
2154 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2155 return CB_ERR;
2156 case CB_SETEDITSEL:
2157 if( lphc->wState & CBF_EDIT )
2158 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2159 (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2160 return CB_ERR;
2161 case CB_SETEXTENDEDUI:
2162 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2163 return CB_ERR;
2164 if( wParam )
2165 lphc->wState |= CBF_EUI;
2166 else lphc->wState &= ~CBF_EUI;
2167 return CB_OKAY;
2168 case CB_GETEXTENDEDUI:
2169 return (lphc->wState & CBF_EUI) != 0;
2170 case CB_GETCOMBOBOXINFO:
2171 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2172 case CB_LIMITTEXT:
2173 if( lphc->wState & CBF_EDIT )
2174 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2175 return TRUE;
2176 default:
2177 if (message >= WM_USER)
2178 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2179 message - WM_USER, wParam, lParam );
2180 break;
2182 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2183 DefWindowProcA(hwnd, message, wParam, lParam);
2186 /*************************************************************************
2187 * GetComboBoxInfo (USER32.@)
2189 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2190 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2192 TRACE("(%p, %p)\n", hwndCombo, pcbi);
2193 return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);