user32: Remove confusing comments.
[wine.git] / dlls / user32 / combo.c
blobef75c7637e6d9719a54c998d82d3b73a68536ddf
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 * - ComboBox_[GS]etMinVisible()
22 * - CB_GETMINVISIBLE, CB_SETMINVISIBLE
23 * - CB_SETTOPINDEX
26 #include <stdarg.h>
27 #include <string.h>
29 #define OEMRESOURCE
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "wine/unicode.h"
36 #include "user_private.h"
37 #include "win.h"
38 #include "controls.h"
39 #include "winternl.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(combo);
44 /* bits in the dwKeyData */
45 #define KEYDATA_ALT 0x2000
46 #define KEYDATA_PREVSTATE 0x4000
49 * Additional combo box definitions
52 #define CB_NOTIFY( lphc, code ) \
53 (SendMessageW((lphc)->owner, WM_COMMAND, \
54 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
56 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
57 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
58 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
59 #define CB_HWND( lphc ) ((lphc)->self)
60 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
62 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
65 * Drawing globals
67 static HBITMAP hComboBmp = 0;
68 static UINT CBitHeight, CBitWidth;
71 * Look and feel dependent "constants"
74 #define COMBO_YBORDERGAP 5
75 #define COMBO_XBORDERSIZE() 2
76 #define COMBO_YBORDERSIZE() 2
77 #define COMBO_EDITBUTTONSPACE() 0
78 #define EDIT_CONTROL_PADDING() 1
80 /*********************************************************************
81 * combo class descriptor
83 static const WCHAR comboboxW[] = {'C','o','m','b','o','B','o','x',0};
84 const struct builtin_class_descr COMBO_builtin_class =
86 comboboxW, /* name */
87 CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */
88 WINPROC_COMBO, /* proc */
89 sizeof(HEADCOMBO *), /* extra */
90 IDC_ARROW, /* cursor */
91 0 /* brush */
95 /***********************************************************************
96 * COMBO_Init
98 * Load combo button bitmap.
100 static BOOL COMBO_Init(void)
102 HDC hDC;
104 if( hComboBmp ) return TRUE;
105 if( (hDC = CreateCompatibleDC(0)) )
107 BOOL bRet = FALSE;
108 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
110 BITMAP bm;
111 HBITMAP hPrevB;
112 RECT r;
114 GetObjectW( hComboBmp, sizeof(bm), &bm );
115 CBitHeight = bm.bmHeight;
116 CBitWidth = bm.bmWidth;
118 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
120 hPrevB = SelectObject( hDC, hComboBmp);
121 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
122 InvertRect( hDC, &r );
123 SelectObject( hDC, hPrevB );
124 bRet = TRUE;
126 DeleteDC( hDC );
127 return bRet;
129 return FALSE;
132 /***********************************************************************
133 * COMBO_NCCreate
135 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
137 LPHEADCOMBO lphc;
139 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
141 lphc->self = hwnd;
142 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
144 /* some braindead apps do try to use scrollbar/border flags */
146 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
147 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
150 * We also have to remove the client edge style to make sure
151 * we don't end-up with a non client area.
153 SetWindowLongW( hwnd, GWL_EXSTYLE,
154 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
156 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
157 lphc->dwStyle |= CBS_HASSTRINGS;
158 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
159 lphc->wState |= CBF_NOTIFY;
161 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
162 return TRUE;
164 return FALSE;
167 /***********************************************************************
168 * COMBO_NCDestroy
170 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
173 if( lphc )
175 TRACE("[%p]: freeing storage\n", lphc->self);
177 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
178 DestroyWindow( lphc->hWndLBox );
180 SetWindowLongPtrW( lphc->self, 0, 0 );
181 HeapFree( GetProcessHeap(), 0, lphc );
183 return 0;
186 /***********************************************************************
187 * CBGetTextAreaHeight
189 * This method will calculate the height of the text area of the
190 * combobox.
191 * The height of the text area is set in two ways.
192 * It can be set explicitly through a combobox message or through a
193 * WM_MEASUREITEM callback.
194 * If this is not the case, the height is set to font height + 4px
195 * This height was determined through experimentation.
196 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
198 static INT CBGetTextAreaHeight(
199 HWND hwnd,
200 LPHEADCOMBO lphc)
202 INT iTextItemHeight;
204 if( lphc->editHeight ) /* explicitly set height */
206 iTextItemHeight = lphc->editHeight;
208 else
210 TEXTMETRICW tm;
211 HDC hDC = GetDC(hwnd);
212 HFONT hPrevFont = 0;
213 INT baseUnitY;
215 if (lphc->hFont)
216 hPrevFont = SelectObject( hDC, lphc->hFont );
218 GetTextMetricsW(hDC, &tm);
220 baseUnitY = tm.tmHeight;
222 if( hPrevFont )
223 SelectObject( hDC, hPrevFont );
225 ReleaseDC(hwnd, hDC);
227 iTextItemHeight = baseUnitY + 4;
231 * Check the ownerdraw case if we haven't asked the parent the size
232 * of the item yet.
234 if ( CB_OWNERDRAWN(lphc) &&
235 (lphc->wState & CBF_MEASUREITEM) )
237 MEASUREITEMSTRUCT measureItem;
238 RECT clientRect;
239 INT originalItemHeight = iTextItemHeight;
240 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
243 * We use the client rect for the width of the item.
245 GetClientRect(hwnd, &clientRect);
247 lphc->wState &= ~CBF_MEASUREITEM;
250 * Send a first one to measure the size of the text area
252 measureItem.CtlType = ODT_COMBOBOX;
253 measureItem.CtlID = id;
254 measureItem.itemID = -1;
255 measureItem.itemWidth = clientRect.right;
256 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
257 measureItem.itemData = 0;
258 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
259 iTextItemHeight = 6 + measureItem.itemHeight;
262 * Send a second one in the case of a fixed ownerdraw list to calculate the
263 * size of the list items. (we basically do this on behalf of the listbox)
265 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
267 measureItem.CtlType = ODT_COMBOBOX;
268 measureItem.CtlID = id;
269 measureItem.itemID = 0;
270 measureItem.itemWidth = clientRect.right;
271 measureItem.itemHeight = originalItemHeight;
272 measureItem.itemData = 0;
273 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
274 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
278 * Keep the size for the next time
280 lphc->editHeight = iTextItemHeight;
283 return iTextItemHeight;
286 /***********************************************************************
287 * CBForceDummyResize
289 * The dummy resize is used for listboxes that have a popup to trigger
290 * a re-arranging of the contents of the combobox and the recalculation
291 * of the size of the "real" control window.
293 static void CBForceDummyResize(
294 LPHEADCOMBO lphc)
296 RECT windowRect;
297 int newComboHeight;
299 newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
301 GetWindowRect(lphc->self, &windowRect);
304 * We have to be careful, resizing a combobox also has the meaning that the
305 * dropped rect will be resized. In this case, we want to trigger a resize
306 * to recalculate layout but we don't want to change the dropped rectangle
307 * So, we pass the height of text area of control as the height.
308 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
309 * message.
311 SetWindowPos( lphc->self,
312 NULL,
313 0, 0,
314 windowRect.right - windowRect.left,
315 newComboHeight,
316 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
319 /***********************************************************************
320 * CBCalcPlacement
322 * Set up component coordinates given valid lphc->RectCombo.
324 static void CBCalcPlacement(
325 HWND hwnd,
326 LPHEADCOMBO lphc,
327 LPRECT lprEdit,
328 LPRECT lprButton,
329 LPRECT lprLB)
332 * Again, start with the client rectangle.
334 GetClientRect(hwnd, lprEdit);
337 * Remove the borders
339 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
342 * Chop off the bottom part to fit with the height of the text area.
344 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
347 * The button starts the same vertical position as the text area.
349 CopyRect(lprButton, lprEdit);
352 * If the combobox is "simple" there is no button.
354 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
355 lprButton->left = lprButton->right = lprButton->bottom = 0;
356 else
359 * Let's assume the combobox button is the same width as the
360 * scrollbar button.
361 * size the button horizontally and cut-off the text area.
363 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
364 lprEdit->right = lprButton->left;
368 * In the case of a dropdown, there is an additional spacing between the
369 * text area and the button.
371 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
373 lprEdit->right -= COMBO_EDITBUTTONSPACE();
377 * If we have an edit control, we space it away from the borders slightly.
379 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
381 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
385 * Adjust the size of the listbox popup.
387 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
390 * Use the client rectangle to initialize the listbox rectangle
392 GetClientRect(hwnd, lprLB);
395 * Then, chop-off the top part.
397 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
399 else
402 * Make sure the dropped width is as large as the combobox itself.
404 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
406 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
409 * In the case of a dropdown, the popup listbox is offset to the right.
410 * so, we want to make sure it's flush with the right side of the
411 * combobox
413 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
414 lprLB->right -= COMBO_EDITBUTTONSPACE();
416 else
417 lprLB->right = lprLB->left + lphc->droppedWidth;
420 /* don't allow negative window width */
421 if (lprEdit->right < lprEdit->left)
422 lprEdit->right = lprEdit->left;
424 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
426 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
428 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
431 /***********************************************************************
432 * CBGetDroppedControlRect
434 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
436 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
437 of the combo box and the lower right corner of the listbox */
439 GetWindowRect(lphc->self, lpRect);
441 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
442 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
446 /***********************************************************************
447 * COMBO_Create
449 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
450 BOOL unicode )
452 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
453 static const WCHAR editName[] = {'E','d','i','t',0};
455 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
456 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
458 lphc->owner = hwndParent;
461 * The item height and dropped width are not set when the control
462 * is created.
464 lphc->droppedWidth = lphc->editHeight = 0;
467 * The first time we go through, we want to measure the ownerdraw item
469 lphc->wState |= CBF_MEASUREITEM;
471 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
473 if( lphc->owner || !(style & WS_VISIBLE) )
475 UINT lbeStyle = 0;
476 UINT lbeExStyle = 0;
479 * Initialize the dropped rect to the size of the client area of the
480 * control and then, force all the areas of the combobox to be
481 * recalculated.
483 GetClientRect( hwnd, &lphc->droppedRect );
484 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
487 * Adjust the position of the popup listbox if it's necessary
489 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
491 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
494 * If it's a dropdown, the listbox is offset
496 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
497 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
499 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
500 lphc->droppedRect.bottom = lphc->droppedRect.top;
501 if (lphc->droppedRect.right < lphc->droppedRect.left)
502 lphc->droppedRect.right = lphc->droppedRect.left;
503 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
506 /* create listbox popup */
508 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
509 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
511 if( lphc->dwStyle & CBS_SORT )
512 lbeStyle |= LBS_SORT;
513 if( lphc->dwStyle & CBS_HASSTRINGS )
514 lbeStyle |= LBS_HASSTRINGS;
515 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
516 lbeStyle |= LBS_NOINTEGRALHEIGHT;
517 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
518 lbeStyle |= LBS_DISABLENOSCROLL;
520 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
522 lbeStyle |= WS_VISIBLE;
525 * In win 95 look n feel, the listbox in the simple combobox has
526 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
528 lbeStyle &= ~WS_BORDER;
529 lbeExStyle |= WS_EX_CLIENTEDGE;
531 else
533 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
536 if (unicode)
537 lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
538 lphc->droppedRect.left,
539 lphc->droppedRect.top,
540 lphc->droppedRect.right - lphc->droppedRect.left,
541 lphc->droppedRect.bottom - lphc->droppedRect.top,
542 hwnd, (HMENU)ID_CB_LISTBOX,
543 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
544 else
545 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
546 lphc->droppedRect.left,
547 lphc->droppedRect.top,
548 lphc->droppedRect.right - lphc->droppedRect.left,
549 lphc->droppedRect.bottom - lphc->droppedRect.top,
550 hwnd, (HMENU)ID_CB_LISTBOX,
551 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
553 if( lphc->hWndLBox )
555 BOOL bEdit = TRUE;
556 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
558 if( lphc->wState & CBF_EDIT )
560 if( lphc->dwStyle & CBS_OEMCONVERT )
561 lbeStyle |= ES_OEMCONVERT;
562 if( lphc->dwStyle & CBS_AUTOHSCROLL )
563 lbeStyle |= ES_AUTOHSCROLL;
564 if( lphc->dwStyle & CBS_LOWERCASE )
565 lbeStyle |= ES_LOWERCASE;
566 else if( lphc->dwStyle & CBS_UPPERCASE )
567 lbeStyle |= ES_UPPERCASE;
569 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
571 if (unicode)
572 lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
573 lphc->textRect.left, lphc->textRect.top,
574 lphc->textRect.right - lphc->textRect.left,
575 lphc->textRect.bottom - lphc->textRect.top,
576 hwnd, (HMENU)ID_CB_EDIT,
577 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
578 else
579 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
580 lphc->textRect.left, lphc->textRect.top,
581 lphc->textRect.right - lphc->textRect.left,
582 lphc->textRect.bottom - lphc->textRect.top,
583 hwnd, (HMENU)ID_CB_EDIT,
584 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
586 if( !lphc->hWndEdit )
587 bEdit = FALSE;
590 if( bEdit )
592 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
594 /* Now do the trick with parent */
595 SetParent(lphc->hWndLBox, HWND_DESKTOP);
597 * If the combo is a dropdown, we must resize the control
598 * to fit only the text area and button. To do this,
599 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
600 * will take care of setting the height for us.
602 CBForceDummyResize(lphc);
605 TRACE("init done\n");
606 return 0;
608 ERR("edit control failure.\n");
609 } else ERR("listbox failure.\n");
610 } else ERR("no owner for visible combo.\n");
612 /* CreateWindow() will send WM_NCDESTROY to cleanup */
614 return -1;
617 /***********************************************************************
618 * CBPaintButton
620 * Paint combo button (normal, pressed, and disabled states).
622 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
624 UINT buttonState = DFCS_SCROLLCOMBOBOX;
626 if( lphc->wState & CBF_NOREDRAW )
627 return;
630 if (lphc->wState & CBF_BUTTONDOWN)
631 buttonState |= DFCS_PUSHED;
633 if (CB_DISABLED(lphc))
634 buttonState |= DFCS_INACTIVE;
636 DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
639 /***********************************************************************
640 * COMBO_PrepareColors
642 * This method will sent the appropriate WM_CTLCOLOR message to
643 * prepare and setup the colors for the combo's DC.
645 * It also returns the brush to use for the background.
647 static HBRUSH COMBO_PrepareColors(
648 LPHEADCOMBO lphc,
649 HDC hDC)
651 HBRUSH hBkgBrush;
654 * Get the background brush for this control.
656 if (CB_DISABLED(lphc))
658 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
659 (WPARAM)hDC, (LPARAM)lphc->self );
662 * We have to change the text color since WM_CTLCOLORSTATIC will
663 * set it to the "enabled" color. This is the same behavior as the
664 * edit control
666 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
668 else
670 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
671 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
672 (WPARAM)hDC, (LPARAM)lphc->self );
676 * Catch errors.
678 if( !hBkgBrush )
679 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
681 return hBkgBrush;
684 /***********************************************************************
685 * CBPaintText
687 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
689 static void CBPaintText(
690 LPHEADCOMBO lphc,
691 HDC hdc_paint)
693 RECT rectEdit = lphc->textRect;
694 INT id, size = 0;
695 LPWSTR pText = NULL;
697 TRACE("\n");
699 /* follow Windows combobox that sends a bunch of text
700 * inquiries to its listbox while processing WM_PAINT. */
702 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
704 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
705 if (size == LB_ERR)
706 FIXME("LB_ERR probably not handled yet\n");
707 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
709 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
710 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
711 pText[size] = '\0'; /* just in case */
712 } else return;
715 if( lphc->wState & CBF_EDIT )
717 static const WCHAR empty_stringW[] = { 0 };
718 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
719 if( lphc->wState & CBF_FOCUSED )
720 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
722 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
724 /* paint text field ourselves */
725 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
726 UINT itemState = ODS_COMBOBOXEDIT;
727 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
728 HBRUSH hPrevBrush, hBkgBrush;
731 * Give ourselves some space.
733 InflateRect( &rectEdit, -1, -1 );
735 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
736 hPrevBrush = SelectObject( hdc, hBkgBrush );
737 FillRect( hdc, &rectEdit, hBkgBrush );
739 if( CB_OWNERDRAWN(lphc) )
741 DRAWITEMSTRUCT dis;
742 HRGN clipRegion;
743 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
745 /* setup state for DRAWITEM message. Owner will highlight */
746 if ( (lphc->wState & CBF_FOCUSED) &&
747 !(lphc->wState & CBF_DROPPED) )
748 itemState |= ODS_SELECTED | ODS_FOCUS;
750 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
752 dis.CtlType = ODT_COMBOBOX;
753 dis.CtlID = ctlid;
754 dis.hwndItem = lphc->self;
755 dis.itemAction = ODA_DRAWENTIRE;
756 dis.itemID = id;
757 dis.itemState = itemState;
758 dis.hDC = hdc;
759 dis.rcItem = rectEdit;
760 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
763 * Clip the DC and have the parent draw the item.
765 clipRegion = set_control_clipping( hdc, &rectEdit );
767 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
769 SelectClipRgn( hdc, clipRegion );
770 if (clipRegion) DeleteObject( clipRegion );
772 else
774 static const WCHAR empty_stringW[] = { 0 };
776 if ( (lphc->wState & CBF_FOCUSED) &&
777 !(lphc->wState & CBF_DROPPED) ) {
779 /* highlight */
780 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
781 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
782 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
785 ExtTextOutW( hdc,
786 rectEdit.left + 1,
787 rectEdit.top + 1,
788 ETO_OPAQUE | ETO_CLIPPED,
789 &rectEdit,
790 pText ? pText : empty_stringW , size, NULL );
792 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
793 DrawFocusRect( hdc, &rectEdit );
796 if( hPrevFont )
797 SelectObject(hdc, hPrevFont );
799 if( hPrevBrush )
800 SelectObject( hdc, hPrevBrush );
802 if( !hdc_paint )
803 ReleaseDC( lphc->self, hdc );
805 HeapFree( GetProcessHeap(), 0, pText );
808 /***********************************************************************
809 * CBPaintBorder
811 static void CBPaintBorder(
812 HWND hwnd,
813 const HEADCOMBO *lphc,
814 HDC hdc)
816 RECT clientRect;
818 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
820 GetClientRect(hwnd, &clientRect);
822 else
824 clientRect = lphc->textRect;
826 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
827 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
830 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
833 /***********************************************************************
834 * COMBO_Paint
836 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
838 PAINTSTRUCT ps;
839 HDC hDC;
841 hDC = (hParamDC) ? hParamDC
842 : BeginPaint( lphc->self, &ps);
844 TRACE("hdc=%p\n", hDC);
846 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
848 HBRUSH hPrevBrush, hBkgBrush;
851 * Retrieve the background brush and select it in the
852 * DC.
854 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
856 hPrevBrush = SelectObject( hDC, hBkgBrush );
857 if (!(lphc->wState & CBF_EDIT))
858 FillRect(hDC, &lphc->textRect, hBkgBrush);
861 * In non 3.1 look, there is a sunken border on the combobox
863 CBPaintBorder(lphc->self, lphc, hDC);
865 if( !IsRectEmpty(&lphc->buttonRect) )
867 CBPaintButton(lphc, hDC, lphc->buttonRect);
870 /* paint the edit control padding area */
871 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
873 RECT rPadEdit = lphc->textRect;
875 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
877 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
880 if( !(lphc->wState & CBF_EDIT) )
881 CBPaintText( lphc, hDC );
883 if( hPrevBrush )
884 SelectObject( hDC, hPrevBrush );
887 if( !hParamDC )
888 EndPaint(lphc->self, &ps);
890 return 0;
893 /***********************************************************************
894 * CBUpdateLBox
896 * Select listbox entry according to the contents of the edit control.
898 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
900 INT length, idx;
901 LPWSTR pText = NULL;
903 idx = LB_ERR;
904 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
906 if( length > 0 )
907 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
909 TRACE("\t edit text length %i\n", length );
911 if( pText )
913 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
914 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
915 HeapFree( GetProcessHeap(), 0, pText );
918 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
920 /* probably superfluous but Windows sends this too */
921 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
922 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
924 return idx;
927 /***********************************************************************
928 * CBUpdateEdit
930 * Copy a listbox entry to the edit control.
932 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
934 INT length;
935 LPWSTR pText = NULL;
936 static const WCHAR empty_stringW[] = { 0 };
938 TRACE("\t %i\n", index );
940 if( index >= 0 ) /* got an entry */
942 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
943 if( length != LB_ERR)
945 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
947 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
952 if( CB_HASSTRINGS(lphc) )
954 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
955 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
956 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
959 if( lphc->wState & CBF_FOCUSED )
960 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
962 HeapFree( GetProcessHeap(), 0, pText );
965 /***********************************************************************
966 * CBDropDown
968 * Show listbox popup.
970 static void CBDropDown( LPHEADCOMBO lphc )
972 HMONITOR monitor;
973 MONITORINFO mon_info;
974 RECT rect,r;
975 int nItems;
976 int nDroppedHeight;
978 TRACE("[%p]: drop down\n", lphc->self);
980 CB_NOTIFY( lphc, CBN_DROPDOWN );
982 /* set selection */
984 lphc->wState |= CBF_DROPPED;
985 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
987 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
989 /* Update edit only if item is in the list */
990 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
991 CBUpdateEdit( lphc, lphc->droppedIndex );
993 else
995 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
997 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
998 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
999 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1002 /* now set popup position */
1003 GetWindowRect( lphc->self, &rect );
1006 * If it's a dropdown, the listbox is offset
1008 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1009 rect.left += COMBO_EDITBUTTONSPACE();
1011 /* if the dropped height is greater than the total height of the dropped
1012 items list, then force the drop down list height to be the total height
1013 of the items in the dropped list */
1015 /* And Remove any extra space (Best Fit) */
1016 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1017 /* if listbox length has been set directly by its handle */
1018 GetWindowRect(lphc->hWndLBox, &r);
1019 if (nDroppedHeight < r.bottom - r.top)
1020 nDroppedHeight = r.bottom - r.top;
1021 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1023 if (nItems > 0)
1025 int nHeight;
1026 int nIHeight;
1028 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1030 nHeight = nIHeight*nItems;
1032 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1033 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1035 if (nDroppedHeight < nHeight)
1037 if (nItems < 5)
1038 nDroppedHeight = (nItems+1)*nIHeight;
1039 else if (nDroppedHeight < 6*nIHeight)
1040 nDroppedHeight = 6*nIHeight;
1044 r.left = rect.left;
1045 r.top = rect.bottom;
1046 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
1047 r.bottom = r.top + nDroppedHeight;
1049 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1050 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1051 mon_info.cbSize = sizeof(mon_info);
1052 GetMonitorInfoW( monitor, &mon_info );
1054 if (r.bottom > mon_info.rcWork.bottom)
1056 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
1057 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1060 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1061 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1064 if( !(lphc->wState & CBF_NOREDRAW) )
1065 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1066 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1068 EnableWindow( lphc->hWndLBox, TRUE );
1069 if (GetCapture() != lphc->self)
1070 SetCapture(lphc->hWndLBox);
1073 /***********************************************************************
1074 * CBRollUp
1076 * Hide listbox popup.
1078 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1080 HWND hWnd = lphc->self;
1082 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1083 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1085 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1087 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1090 if( lphc->wState & CBF_DROPPED )
1092 RECT rect;
1094 lphc->wState &= ~CBF_DROPPED;
1095 ShowWindow( lphc->hWndLBox, SW_HIDE );
1097 if(GetCapture() == lphc->hWndLBox)
1099 ReleaseCapture();
1102 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1104 rect = lphc->buttonRect;
1106 else
1108 if( bButton )
1110 UnionRect( &rect,
1111 &lphc->buttonRect,
1112 &lphc->textRect);
1114 else
1115 rect = lphc->textRect;
1117 bButton = TRUE;
1120 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1121 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1122 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1123 CB_NOTIFY( lphc, CBN_CLOSEUP );
1128 /***********************************************************************
1129 * COMBO_FlipListbox
1131 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1133 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1135 if( lphc->wState & CBF_DROPPED )
1137 CBRollUp( lphc, ok, bRedrawButton );
1138 return FALSE;
1141 CBDropDown( lphc );
1142 return TRUE;
1145 /***********************************************************************
1146 * CBRepaintButton
1148 static void CBRepaintButton( LPHEADCOMBO lphc )
1150 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1151 UpdateWindow(lphc->self);
1154 /***********************************************************************
1155 * COMBO_SetFocus
1157 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1159 if( !(lphc->wState & CBF_FOCUSED) )
1161 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1162 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1164 /* This is wrong. Message sequences seem to indicate that this
1165 is set *after* the notify. */
1166 /* lphc->wState |= CBF_FOCUSED; */
1168 if( !(lphc->wState & CBF_EDIT) )
1169 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1171 CB_NOTIFY( lphc, CBN_SETFOCUS );
1172 lphc->wState |= CBF_FOCUSED;
1176 /***********************************************************************
1177 * COMBO_KillFocus
1179 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1181 HWND hWnd = lphc->self;
1183 if( lphc->wState & CBF_FOCUSED )
1185 CBRollUp( lphc, FALSE, TRUE );
1186 if( IsWindow( hWnd ) )
1188 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1189 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1191 lphc->wState &= ~CBF_FOCUSED;
1193 /* redraw text */
1194 if( !(lphc->wState & CBF_EDIT) )
1195 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1197 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1202 /***********************************************************************
1203 * COMBO_Command
1205 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1207 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1209 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1211 switch( HIWORD(wParam) >> 8 )
1213 case (EN_SETFOCUS >> 8):
1215 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1217 COMBO_SetFocus( lphc );
1218 break;
1220 case (EN_KILLFOCUS >> 8):
1222 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1224 /* NOTE: it seems that Windows' edit control sends an
1225 * undocumented message WM_USER + 0x1B instead of this
1226 * notification (only when it happens to be a part of
1227 * the combo). ?? - AK.
1230 COMBO_KillFocus( lphc );
1231 break;
1234 case (EN_CHANGE >> 8):
1236 * In some circumstances (when the selection of the combobox
1237 * is changed for example) we don't want the EN_CHANGE notification
1238 * to be forwarded to the parent of the combobox. This code
1239 * checks a flag that is set in these occasions and ignores the
1240 * notification.
1242 if (lphc->wState & CBF_NOLBSELECT)
1244 lphc->wState &= ~CBF_NOLBSELECT;
1246 else
1248 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1251 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1252 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1253 break;
1255 case (EN_UPDATE >> 8):
1256 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1257 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1258 break;
1260 case (EN_ERRSPACE >> 8):
1261 CB_NOTIFY( lphc, CBN_ERRSPACE );
1264 else if( lphc->hWndLBox == hWnd )
1266 switch( (short)HIWORD(wParam) )
1268 case LBN_ERRSPACE:
1269 CB_NOTIFY( lphc, CBN_ERRSPACE );
1270 break;
1272 case LBN_DBLCLK:
1273 CB_NOTIFY( lphc, CBN_DBLCLK );
1274 break;
1276 case LBN_SELCHANGE:
1277 case LBN_SELCANCEL:
1279 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1281 /* do not roll up if selection is being tracked
1282 * by arrow keys in the dropdown listbox */
1283 if (!(lphc->wState & CBF_NOROLLUP))
1285 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1287 else lphc->wState &= ~CBF_NOROLLUP;
1289 CB_NOTIFY( lphc, CBN_SELCHANGE );
1291 if( HIWORD(wParam) == LBN_SELCHANGE)
1293 if( lphc->wState & CBF_EDIT )
1294 lphc->wState |= CBF_NOLBSELECT;
1295 CBPaintText( lphc, NULL );
1297 break;
1299 case LBN_SETFOCUS:
1300 case LBN_KILLFOCUS:
1301 /* nothing to do here since ComboLBox always resets the focus to its
1302 * combo/edit counterpart */
1303 break;
1306 return 0;
1309 /***********************************************************************
1310 * COMBO_ItemOp
1312 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1314 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1316 HWND hWnd = lphc->self;
1317 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1319 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1321 switch( msg )
1323 case WM_DELETEITEM:
1325 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1326 lpIS->CtlType = ODT_COMBOBOX;
1327 lpIS->CtlID = id;
1328 lpIS->hwndItem = hWnd;
1329 break;
1331 case WM_DRAWITEM:
1333 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1334 lpIS->CtlType = ODT_COMBOBOX;
1335 lpIS->CtlID = id;
1336 lpIS->hwndItem = hWnd;
1337 break;
1339 case WM_COMPAREITEM:
1341 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1342 lpIS->CtlType = ODT_COMBOBOX;
1343 lpIS->CtlID = id;
1344 lpIS->hwndItem = hWnd;
1345 break;
1347 case WM_MEASUREITEM:
1349 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1350 lpIS->CtlType = ODT_COMBOBOX;
1351 lpIS->CtlID = id;
1352 break;
1355 return SendMessageW(lphc->owner, msg, id, lParam);
1359 /***********************************************************************
1360 * COMBO_GetTextW
1362 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1364 INT length;
1366 if( lphc->wState & CBF_EDIT )
1367 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1369 /* get it from the listbox */
1371 if (!count || !buf) return 0;
1372 if( lphc->hWndLBox )
1374 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1375 if (idx == LB_ERR) goto error;
1376 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1377 if (length == LB_ERR) goto error;
1379 /* 'length' is without the terminating character */
1380 if (length >= count)
1382 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1383 if (!lpBuffer) goto error;
1384 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1386 /* truncate if buffer is too short */
1387 if (length != LB_ERR)
1389 lstrcpynW( buf, lpBuffer, count );
1390 length = count;
1392 HeapFree( GetProcessHeap(), 0, lpBuffer );
1394 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1396 if (length == LB_ERR) return 0;
1397 return length;
1400 error: /* error - truncate string, return zero */
1401 buf[0] = 0;
1402 return 0;
1406 /***********************************************************************
1407 * COMBO_GetTextA
1409 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1410 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1412 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1414 INT length;
1416 if( lphc->wState & CBF_EDIT )
1417 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1419 /* get it from the listbox */
1421 if (!count || !buf) return 0;
1422 if( lphc->hWndLBox )
1424 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1425 if (idx == LB_ERR) goto error;
1426 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1427 if (length == LB_ERR) goto error;
1429 /* 'length' is without the terminating character */
1430 if (length >= count)
1432 LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1433 if (!lpBuffer) goto error;
1434 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1436 /* truncate if buffer is too short */
1437 if (length != LB_ERR)
1439 lstrcpynA( buf, lpBuffer, count );
1440 length = count;
1442 HeapFree( GetProcessHeap(), 0, lpBuffer );
1444 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1446 if (length == LB_ERR) return 0;
1447 return length;
1450 error: /* error - truncate string, return zero */
1451 buf[0] = 0;
1452 return 0;
1456 /***********************************************************************
1457 * CBResetPos
1459 * This function sets window positions according to the updated
1460 * component placement struct.
1462 static void CBResetPos(
1463 LPHEADCOMBO lphc,
1464 const RECT *rectEdit,
1465 const RECT *rectLB,
1466 BOOL bRedraw)
1468 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1470 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1471 * sizing messages */
1473 if( lphc->wState & CBF_EDIT )
1474 SetWindowPos( lphc->hWndEdit, 0,
1475 rectEdit->left, rectEdit->top,
1476 rectEdit->right - rectEdit->left,
1477 rectEdit->bottom - rectEdit->top,
1478 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1480 SetWindowPos( lphc->hWndLBox, 0,
1481 rectLB->left, rectLB->top,
1482 rectLB->right - rectLB->left,
1483 rectLB->bottom - rectLB->top,
1484 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1486 if( bDrop )
1488 if( lphc->wState & CBF_DROPPED )
1490 lphc->wState &= ~CBF_DROPPED;
1491 ShowWindow( lphc->hWndLBox, SW_HIDE );
1494 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1495 RedrawWindow( lphc->self, NULL, 0,
1496 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1501 /***********************************************************************
1502 * COMBO_Size
1504 static void COMBO_Size( LPHEADCOMBO lphc )
1507 * Those controls are always the same height. So we have to make sure
1508 * they are not resized to another value.
1510 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1512 int newComboHeight, curComboHeight, curComboWidth;
1513 RECT rc;
1515 GetWindowRect(lphc->self, &rc);
1516 curComboHeight = rc.bottom - rc.top;
1517 curComboWidth = rc.right - rc.left;
1518 newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE();
1521 * Resizing a combobox has another side effect, it resizes the dropped
1522 * rectangle as well. However, it does it only if the new height for the
1523 * combobox is more than the height it should have. In other words,
1524 * if the application resizing the combobox only had the intention to resize
1525 * the actual control, for example, to do the layout of a dialog that is
1526 * resized, the height of the dropdown is not changed.
1528 if( curComboHeight > newComboHeight )
1530 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1531 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1532 lphc->droppedRect.top);
1533 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1536 * Restore original height
1538 if( curComboHeight != newComboHeight )
1539 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1540 SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW);
1543 CBCalcPlacement(lphc->self,
1544 lphc,
1545 &lphc->textRect,
1546 &lphc->buttonRect,
1547 &lphc->droppedRect);
1549 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1553 /***********************************************************************
1554 * COMBO_Font
1556 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1559 * Set the font
1561 lphc->hFont = hFont;
1564 * Propagate to owned windows.
1566 if( lphc->wState & CBF_EDIT )
1567 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1568 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1571 * Redo the layout of the control.
1573 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1575 CBCalcPlacement(lphc->self,
1576 lphc,
1577 &lphc->textRect,
1578 &lphc->buttonRect,
1579 &lphc->droppedRect);
1581 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1583 else
1585 CBForceDummyResize(lphc);
1590 /***********************************************************************
1591 * COMBO_SetItemHeight
1593 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1595 LRESULT lRet = CB_ERR;
1597 if( index == -1 ) /* set text field height */
1599 if( height < 32768 )
1601 lphc->editHeight = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1604 * Redo the layout of the control.
1606 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1608 CBCalcPlacement(lphc->self,
1609 lphc,
1610 &lphc->textRect,
1611 &lphc->buttonRect,
1612 &lphc->droppedRect);
1614 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1616 else
1618 CBForceDummyResize(lphc);
1621 lRet = height;
1624 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1625 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1626 return lRet;
1629 /***********************************************************************
1630 * COMBO_SelectString
1632 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1634 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText) :
1635 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1636 if( index >= 0 )
1638 if( lphc->wState & CBF_EDIT )
1639 CBUpdateEdit( lphc, index );
1640 else
1642 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1645 return (LRESULT)index;
1648 /***********************************************************************
1649 * COMBO_LButtonDown
1651 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1653 POINT pt;
1654 BOOL bButton;
1655 HWND hWnd = lphc->self;
1657 pt.x = (short)LOWORD(lParam);
1658 pt.y = (short)HIWORD(lParam);
1659 bButton = PtInRect(&lphc->buttonRect, pt);
1661 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1662 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1664 lphc->wState |= CBF_BUTTONDOWN;
1665 if( lphc->wState & CBF_DROPPED )
1667 /* got a click to cancel selection */
1669 lphc->wState &= ~CBF_BUTTONDOWN;
1670 CBRollUp( lphc, TRUE, FALSE );
1671 if( !IsWindow( hWnd ) ) return;
1673 if( lphc->wState & CBF_CAPTURE )
1675 lphc->wState &= ~CBF_CAPTURE;
1676 ReleaseCapture();
1679 else
1681 /* drop down the listbox and start tracking */
1683 lphc->wState |= CBF_CAPTURE;
1684 SetCapture( hWnd );
1685 CBDropDown( lphc );
1687 if( bButton ) CBRepaintButton( lphc );
1691 /***********************************************************************
1692 * COMBO_LButtonUp
1694 * Release capture and stop tracking if needed.
1696 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1698 if( lphc->wState & CBF_CAPTURE )
1700 lphc->wState &= ~CBF_CAPTURE;
1701 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1703 INT index = CBUpdateLBox( lphc, TRUE );
1704 /* Update edit only if item is in the list */
1705 if(index >= 0)
1707 lphc->wState |= CBF_NOLBSELECT;
1708 CBUpdateEdit( lphc, index );
1709 lphc->wState &= ~CBF_NOLBSELECT;
1712 ReleaseCapture();
1713 SetCapture(lphc->hWndLBox);
1716 if( lphc->wState & CBF_BUTTONDOWN )
1718 lphc->wState &= ~CBF_BUTTONDOWN;
1719 CBRepaintButton( lphc );
1723 /***********************************************************************
1724 * COMBO_MouseMove
1726 * Two things to do - track combo button and release capture when
1727 * pointer goes into the listbox.
1729 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1731 POINT pt;
1732 RECT lbRect;
1734 pt.x = (short)LOWORD(lParam);
1735 pt.y = (short)HIWORD(lParam);
1737 if( lphc->wState & CBF_BUTTONDOWN )
1739 BOOL bButton;
1741 bButton = PtInRect(&lphc->buttonRect, pt);
1743 if( !bButton )
1745 lphc->wState &= ~CBF_BUTTONDOWN;
1746 CBRepaintButton( lphc );
1750 GetClientRect( lphc->hWndLBox, &lbRect );
1751 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1752 if( PtInRect(&lbRect, pt) )
1754 lphc->wState &= ~CBF_CAPTURE;
1755 ReleaseCapture();
1756 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1758 /* hand over pointer tracking */
1759 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1763 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1765 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1766 return FALSE;
1768 pcbi->rcItem = lphc->textRect;
1769 pcbi->rcButton = lphc->buttonRect;
1770 pcbi->stateButton = 0;
1771 if (lphc->wState & CBF_BUTTONDOWN)
1772 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1773 if (IsRectEmpty(&lphc->buttonRect))
1774 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1775 pcbi->hwndCombo = lphc->self;
1776 pcbi->hwndItem = lphc->hWndEdit;
1777 pcbi->hwndList = lphc->hWndLBox;
1778 return TRUE;
1781 static char *strdupA(LPCSTR str)
1783 char *ret;
1784 DWORD len;
1786 if(!str) return NULL;
1788 len = strlen(str);
1789 ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1790 memcpy(ret, str, len + 1);
1791 return ret;
1794 /***********************************************************************
1795 * ComboWndProc_common
1797 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1799 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1801 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1802 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1804 if (!IsWindow(hwnd)) return 0;
1806 if( lphc || message == WM_NCCREATE )
1807 switch(message)
1810 /* System messages */
1812 case WM_NCCREATE:
1814 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1815 ((LPCREATESTRUCTA)lParam)->style;
1816 return COMBO_NCCreate(hwnd, style);
1818 case WM_NCDESTROY:
1819 COMBO_NCDestroy(lphc);
1820 break;/* -> DefWindowProc */
1822 case WM_CREATE:
1824 HWND hwndParent;
1825 LONG style;
1826 if(unicode)
1828 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1829 style = ((LPCREATESTRUCTW)lParam)->style;
1831 else
1833 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1834 style = ((LPCREATESTRUCTA)lParam)->style;
1836 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1839 case WM_PRINTCLIENT:
1840 /* Fallthrough */
1841 case WM_PAINT:
1842 /* wParam may contain a valid HDC! */
1843 return COMBO_Paint(lphc, (HDC)wParam);
1845 case WM_ERASEBKGND:
1846 /* do all painting in WM_PAINT like Windows does */
1847 return 1;
1849 case WM_GETDLGCODE:
1851 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1852 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1854 int vk = (int)((LPMSG)lParam)->wParam;
1856 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1857 result |= DLGC_WANTMESSAGE;
1859 return result;
1861 case WM_SIZE:
1862 if( lphc->hWndLBox &&
1863 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1864 return TRUE;
1865 case WM_SETFONT:
1866 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1867 return TRUE;
1868 case WM_GETFONT:
1869 return (LRESULT)lphc->hFont;
1870 case WM_SETFOCUS:
1871 if( lphc->wState & CBF_EDIT ) {
1872 SetFocus( lphc->hWndEdit );
1873 /* The first time focus is received, select all the text */
1874 if( !(lphc->wState & CBF_BEENFOCUSED) ) {
1875 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1876 lphc->wState |= CBF_BEENFOCUSED;
1879 else
1880 COMBO_SetFocus( lphc );
1881 return TRUE;
1882 case WM_KILLFOCUS:
1884 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1885 if( !hwndFocus ||
1886 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1887 COMBO_KillFocus( lphc );
1888 return TRUE;
1890 case WM_COMMAND:
1891 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1892 case WM_GETTEXT:
1893 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1894 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1895 case WM_SETTEXT:
1896 case WM_GETTEXTLENGTH:
1897 case WM_CLEAR:
1898 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1900 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1901 if (j == -1) return 0;
1902 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1903 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1905 else if( lphc->wState & CBF_EDIT )
1907 LRESULT ret;
1908 lphc->wState |= CBF_NOEDITNOTIFY;
1909 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1910 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1911 lphc->wState &= ~CBF_NOEDITNOTIFY;
1912 return ret;
1914 else return CB_ERR;
1915 case WM_CUT:
1916 case WM_PASTE:
1917 case WM_COPY:
1918 if( lphc->wState & CBF_EDIT )
1920 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1921 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1923 else return CB_ERR;
1925 case WM_DRAWITEM:
1926 case WM_DELETEITEM:
1927 case WM_COMPAREITEM:
1928 case WM_MEASUREITEM:
1929 return COMBO_ItemOp(lphc, message, lParam);
1930 case WM_ENABLE:
1931 if( lphc->wState & CBF_EDIT )
1932 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1933 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1935 /* Force the control to repaint when the enabled state changes. */
1936 InvalidateRect(lphc->self, NULL, TRUE);
1937 return TRUE;
1938 case WM_SETREDRAW:
1939 if( wParam )
1940 lphc->wState &= ~CBF_NOREDRAW;
1941 else
1942 lphc->wState |= CBF_NOREDRAW;
1944 if( lphc->wState & CBF_EDIT )
1945 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1946 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1947 return 0;
1948 case WM_SYSKEYDOWN:
1949 if( KEYDATA_ALT & HIWORD(lParam) )
1950 if( wParam == VK_UP || wParam == VK_DOWN )
1951 COMBO_FlipListbox( lphc, FALSE, FALSE );
1952 return 0;
1954 case WM_KEYDOWN:
1955 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1956 (lphc->wState & CBF_DROPPED))
1958 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1959 return TRUE;
1961 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1963 COMBO_FlipListbox( lphc, FALSE, FALSE );
1964 return TRUE;
1966 /* fall through */
1967 case WM_CHAR:
1968 case WM_IME_CHAR:
1970 HWND hwndTarget;
1972 if( lphc->wState & CBF_EDIT )
1973 hwndTarget = lphc->hWndEdit;
1974 else
1975 hwndTarget = lphc->hWndLBox;
1977 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
1978 SendMessageA(hwndTarget, message, wParam, lParam);
1980 case WM_LBUTTONDOWN:
1981 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1982 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1983 return TRUE;
1984 case WM_LBUTTONUP:
1985 COMBO_LButtonUp( lphc );
1986 return TRUE;
1987 case WM_MOUSEMOVE:
1988 if( lphc->wState & CBF_CAPTURE )
1989 COMBO_MouseMove( lphc, wParam, lParam );
1990 return TRUE;
1992 case WM_MOUSEWHEEL:
1993 if (wParam & (MK_SHIFT | MK_CONTROL))
1994 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
1995 DefWindowProcA(hwnd, message, wParam, lParam);
1997 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1998 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1999 return TRUE;
2001 /* Combo messages */
2003 case CB_ADDSTRING:
2004 if( unicode )
2006 if( lphc->dwStyle & CBS_LOWERCASE )
2007 CharLowerW((LPWSTR)lParam);
2008 else if( lphc->dwStyle & CBS_UPPERCASE )
2009 CharUpperW((LPWSTR)lParam);
2010 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2012 else /* unlike the unicode version, the ansi version does not overwrite
2013 the string if converting case */
2015 char *string = NULL;
2016 LRESULT ret;
2017 if( lphc->dwStyle & CBS_LOWERCASE )
2019 string = strdupA((LPSTR)lParam);
2020 CharLowerA(string);
2023 else if( lphc->dwStyle & CBS_UPPERCASE )
2025 string = strdupA((LPSTR)lParam);
2026 CharUpperA(string);
2029 ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
2030 HeapFree(GetProcessHeap(), 0, string);
2031 return ret;
2033 case CB_INSERTSTRING:
2034 if( unicode )
2036 if( lphc->dwStyle & CBS_LOWERCASE )
2037 CharLowerW((LPWSTR)lParam);
2038 else if( lphc->dwStyle & CBS_UPPERCASE )
2039 CharUpperW((LPWSTR)lParam);
2040 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2042 else
2044 if( lphc->dwStyle & CBS_LOWERCASE )
2045 CharLowerA((LPSTR)lParam);
2046 else if( lphc->dwStyle & CBS_UPPERCASE )
2047 CharUpperA((LPSTR)lParam);
2049 return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2051 case CB_DELETESTRING:
2052 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2053 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2054 case CB_SELECTSTRING:
2055 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2056 case CB_FINDSTRING:
2057 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2058 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2059 case CB_FINDSTRINGEXACT:
2060 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2061 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2062 case CB_SETITEMHEIGHT:
2063 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2064 case CB_GETITEMHEIGHT:
2065 if( (INT)wParam >= 0 ) /* listbox item */
2066 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2067 return CBGetTextAreaHeight(hwnd, lphc);
2068 case CB_RESETCONTENT:
2069 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2070 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2072 static const WCHAR empty_stringW[] = { 0 };
2073 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2075 else
2076 InvalidateRect(lphc->self, NULL, TRUE);
2077 return TRUE;
2078 case CB_INITSTORAGE:
2079 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2080 case CB_GETHORIZONTALEXTENT:
2081 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2082 case CB_SETHORIZONTALEXTENT:
2083 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2084 case CB_GETTOPINDEX:
2085 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2086 case CB_GETLOCALE:
2087 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2088 case CB_SETLOCALE:
2089 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2090 case CB_SETDROPPEDWIDTH:
2091 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
2092 (INT)wParam >= 32768 )
2093 return CB_ERR;
2094 /* new value must be higher than combobox width */
2095 if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2096 lphc->droppedWidth = wParam;
2097 else if(wParam)
2098 lphc->droppedWidth = 0;
2100 /* recalculate the combobox area */
2101 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
2103 /* fall through */
2104 case CB_GETDROPPEDWIDTH:
2105 if( lphc->droppedWidth )
2106 return lphc->droppedWidth;
2107 return lphc->droppedRect.right - lphc->droppedRect.left;
2108 case CB_GETDROPPEDCONTROLRECT:
2109 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2110 return CB_OKAY;
2111 case CB_GETDROPPEDSTATE:
2112 return (lphc->wState & CBF_DROPPED) != 0;
2113 case CB_DIR:
2114 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2115 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2117 case CB_SHOWDROPDOWN:
2118 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2120 if( wParam )
2122 if( !(lphc->wState & CBF_DROPPED) )
2123 CBDropDown( lphc );
2125 else
2126 if( lphc->wState & CBF_DROPPED )
2127 CBRollUp( lphc, FALSE, TRUE );
2129 return TRUE;
2130 case CB_GETCOUNT:
2131 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2132 case CB_GETCURSEL:
2133 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2134 case CB_SETCURSEL:
2135 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2136 if( lParam >= 0 )
2137 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2139 /* no LBN_SELCHANGE in this case, update manually */
2140 CBPaintText( lphc, NULL );
2141 lphc->wState &= ~CBF_SELCHANGE;
2142 return lParam;
2143 case CB_GETLBTEXT:
2144 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2145 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2146 case CB_GETLBTEXTLEN:
2147 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2148 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2149 case CB_GETITEMDATA:
2150 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2151 case CB_SETITEMDATA:
2152 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2153 case CB_GETEDITSEL:
2154 /* Edit checks passed parameters itself */
2155 if( lphc->wState & CBF_EDIT )
2156 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2157 return CB_ERR;
2158 case CB_SETEDITSEL:
2159 if( lphc->wState & CBF_EDIT )
2160 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2161 (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2162 return CB_ERR;
2163 case CB_SETEXTENDEDUI:
2164 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2165 return CB_ERR;
2166 if( wParam )
2167 lphc->wState |= CBF_EUI;
2168 else lphc->wState &= ~CBF_EUI;
2169 return CB_OKAY;
2170 case CB_GETEXTENDEDUI:
2171 return (lphc->wState & CBF_EUI) != 0;
2172 case CB_GETCOMBOBOXINFO:
2173 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2174 case CB_LIMITTEXT:
2175 if( lphc->wState & CBF_EDIT )
2176 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2177 return TRUE;
2178 default:
2179 if (message >= WM_USER)
2180 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2181 message - WM_USER, wParam, lParam );
2182 break;
2184 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2185 DefWindowProcA(hwnd, message, wParam, lParam);
2188 /*************************************************************************
2189 * GetComboBoxInfo (USER32.@)
2191 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2192 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2194 TRACE("(%p, %p)\n", hwndCombo, pcbi);
2195 return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);