user32: Pre-allocate the window procedure for the combobox class.
[wine/wine-gecko.git] / dlls / user32 / combo.c
blob948c6a747a6582a2a2d4edb34abf9cdb49798f14
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 * NOTES
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Oct. 4, 2004, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
29 * TODO:
30 * - ComboBox_[GS]etMinVisible()
31 * - CB_GETMINVISIBLE, CB_SETMINVISIBLE
32 * - CB_SETTOPINDEX
35 #include <stdarg.h>
36 #include <string.h>
38 #define OEMRESOURCE
40 #include "windef.h"
41 #include "winbase.h"
42 #include "wingdi.h"
43 #include "winuser.h"
44 #include "wine/unicode.h"
45 #include "user_private.h"
46 #include "win.h"
47 #include "controls.h"
48 #include "winternl.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(combo);
53 /* bits in the dwKeyData */
54 #define KEYDATA_ALT 0x2000
55 #define KEYDATA_PREVSTATE 0x4000
58 * Additional combo box definitions
61 #define CB_NOTIFY( lphc, code ) \
62 (SendMessageW((lphc)->owner, WM_COMMAND, \
63 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
65 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
66 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
67 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
68 #define CB_HWND( lphc ) ((lphc)->self)
69 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
71 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
74 * Drawing globals
76 static HBITMAP hComboBmp = 0;
77 static UINT CBitHeight, CBitWidth;
80 * Look and feel dependent "constants"
83 #define COMBO_YBORDERGAP 5
84 #define COMBO_XBORDERSIZE() 2
85 #define COMBO_YBORDERSIZE() 2
86 #define COMBO_EDITBUTTONSPACE() 0
87 #define EDIT_CONTROL_PADDING() 1
89 /*********************************************************************
90 * combo class descriptor
92 static const WCHAR comboboxW[] = {'C','o','m','b','o','B','o','x',0};
93 const struct builtin_class_descr COMBO_builtin_class =
95 comboboxW, /* name */
96 CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */
97 NULL, /* procA */
98 BUILTIN_WINPROC(WINPROC_COMBO), /* procW */
99 sizeof(HEADCOMBO *), /* extra */
100 IDC_ARROW, /* cursor */
101 0 /* brush */
105 /***********************************************************************
106 * COMBO_Init
108 * Load combo button bitmap.
110 static BOOL COMBO_Init(void)
112 HDC hDC;
114 if( hComboBmp ) return TRUE;
115 if( (hDC = CreateCompatibleDC(0)) )
117 BOOL bRet = FALSE;
118 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
120 BITMAP bm;
121 HBITMAP hPrevB;
122 RECT r;
124 GetObjectW( hComboBmp, sizeof(bm), &bm );
125 CBitHeight = bm.bmHeight;
126 CBitWidth = bm.bmWidth;
128 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
130 hPrevB = SelectObject( hDC, hComboBmp);
131 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
132 InvertRect( hDC, &r );
133 SelectObject( hDC, hPrevB );
134 bRet = TRUE;
136 DeleteDC( hDC );
137 return bRet;
139 return FALSE;
142 /***********************************************************************
143 * COMBO_NCCreate
145 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
147 LPHEADCOMBO lphc;
149 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
151 lphc->self = hwnd;
152 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
154 /* some braindead apps do try to use scrollbar/border flags */
156 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
157 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
160 * We also have to remove the client edge style to make sure
161 * we don't end-up with a non client area.
163 SetWindowLongW( hwnd, GWL_EXSTYLE,
164 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
166 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
167 lphc->dwStyle |= CBS_HASSTRINGS;
168 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
169 lphc->wState |= CBF_NOTIFY;
171 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
172 return TRUE;
174 return FALSE;
177 /***********************************************************************
178 * COMBO_NCDestroy
180 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
183 if( lphc )
185 TRACE("[%p]: freeing storage\n", lphc->self);
187 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
188 DestroyWindow( lphc->hWndLBox );
190 SetWindowLongPtrW( lphc->self, 0, 0 );
191 HeapFree( GetProcessHeap(), 0, lphc );
193 return 0;
196 /***********************************************************************
197 * CBGetTextAreaHeight
199 * This method will calculate the height of the text area of the
200 * combobox.
201 * The height of the text area is set in two ways.
202 * It can be set explicitly through a combobox message or through a
203 * WM_MEASUREITEM callback.
204 * If this is not the case, the height is set to font height + 4px
205 * This height was determined through experimentation.
206 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
208 static INT CBGetTextAreaHeight(
209 HWND hwnd,
210 LPHEADCOMBO lphc)
212 INT iTextItemHeight;
214 if( lphc->editHeight ) /* explicitly set height */
216 iTextItemHeight = lphc->editHeight;
218 else
220 TEXTMETRICW tm;
221 HDC hDC = GetDC(hwnd);
222 HFONT hPrevFont = 0;
223 INT baseUnitY;
225 if (lphc->hFont)
226 hPrevFont = SelectObject( hDC, lphc->hFont );
228 GetTextMetricsW(hDC, &tm);
230 baseUnitY = tm.tmHeight;
232 if( hPrevFont )
233 SelectObject( hDC, hPrevFont );
235 ReleaseDC(hwnd, hDC);
237 iTextItemHeight = baseUnitY + 4;
241 * Check the ownerdraw case if we haven't asked the parent the size
242 * of the item yet.
244 if ( CB_OWNERDRAWN(lphc) &&
245 (lphc->wState & CBF_MEASUREITEM) )
247 MEASUREITEMSTRUCT measureItem;
248 RECT clientRect;
249 INT originalItemHeight = iTextItemHeight;
250 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
253 * We use the client rect for the width of the item.
255 GetClientRect(hwnd, &clientRect);
257 lphc->wState &= ~CBF_MEASUREITEM;
260 * Send a first one to measure the size of the text area
262 measureItem.CtlType = ODT_COMBOBOX;
263 measureItem.CtlID = id;
264 measureItem.itemID = -1;
265 measureItem.itemWidth = clientRect.right;
266 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
267 measureItem.itemData = 0;
268 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
269 iTextItemHeight = 6 + measureItem.itemHeight;
272 * Send a second one in the case of a fixed ownerdraw list to calculate the
273 * size of the list items. (we basically do this on behalf of the listbox)
275 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
277 measureItem.CtlType = ODT_COMBOBOX;
278 measureItem.CtlID = id;
279 measureItem.itemID = 0;
280 measureItem.itemWidth = clientRect.right;
281 measureItem.itemHeight = originalItemHeight;
282 measureItem.itemData = 0;
283 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
284 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
288 * Keep the size for the next time
290 lphc->editHeight = iTextItemHeight;
293 return iTextItemHeight;
296 /***********************************************************************
297 * CBForceDummyResize
299 * The dummy resize is used for listboxes that have a popup to trigger
300 * a re-arranging of the contents of the combobox and the recalculation
301 * of the size of the "real" control window.
303 static void CBForceDummyResize(
304 LPHEADCOMBO lphc)
306 RECT windowRect;
307 int newComboHeight;
309 newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
311 GetWindowRect(lphc->self, &windowRect);
314 * We have to be careful, resizing a combobox also has the meaning that the
315 * dropped rect will be resized. In this case, we want to trigger a resize
316 * to recalculate layout but we don't want to change the dropped rectangle
317 * So, we pass the height of text area of control as the height.
318 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
319 * message.
321 SetWindowPos( lphc->self,
322 NULL,
323 0, 0,
324 windowRect.right - windowRect.left,
325 newComboHeight,
326 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
329 /***********************************************************************
330 * CBCalcPlacement
332 * Set up component coordinates given valid lphc->RectCombo.
334 static void CBCalcPlacement(
335 HWND hwnd,
336 LPHEADCOMBO lphc,
337 LPRECT lprEdit,
338 LPRECT lprButton,
339 LPRECT lprLB)
342 * Again, start with the client rectangle.
344 GetClientRect(hwnd, lprEdit);
347 * Remove the borders
349 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
352 * Chop off the bottom part to fit with the height of the text area.
354 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
357 * The button starts the same vertical position as the text area.
359 CopyRect(lprButton, lprEdit);
362 * If the combobox is "simple" there is no button.
364 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
365 lprButton->left = lprButton->right = lprButton->bottom = 0;
366 else
369 * Let's assume the combobox button is the same width as the
370 * scrollbar button.
371 * size the button horizontally and cut-off the text area.
373 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
374 lprEdit->right = lprButton->left;
378 * In the case of a dropdown, there is an additional spacing between the
379 * text area and the button.
381 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
383 lprEdit->right -= COMBO_EDITBUTTONSPACE();
387 * If we have an edit control, we space it away from the borders slightly.
389 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
391 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
395 * Adjust the size of the listbox popup.
397 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
400 * Use the client rectangle to initialize the listbox rectangle
402 GetClientRect(hwnd, lprLB);
405 * Then, chop-off the top part.
407 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
409 else
412 * Make sure the dropped width is as large as the combobox itself.
414 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
416 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
419 * In the case of a dropdown, the popup listbox is offset to the right.
420 * so, we want to make sure it's flush with the right side of the
421 * combobox
423 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
424 lprLB->right -= COMBO_EDITBUTTONSPACE();
426 else
427 lprLB->right = lprLB->left + lphc->droppedWidth;
430 /* don't allow negative window width */
431 if (lprEdit->right < lprEdit->left)
432 lprEdit->right = lprEdit->left;
434 TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
436 TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
438 TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
441 /***********************************************************************
442 * CBGetDroppedControlRect
444 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
446 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
447 of the combo box and the lower right corner of the listbox */
449 GetWindowRect(lphc->self, lpRect);
451 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
452 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
456 /***********************************************************************
457 * COMBO_WindowPosChanging
459 static LRESULT COMBO_WindowPosChanging(
460 HWND hwnd,
461 LPHEADCOMBO lphc,
462 WINDOWPOS* posChanging)
465 * We need to override the WM_WINDOWPOSCHANGING method to handle all
466 * the non-simple comboboxes. The problem is that those controls are
467 * always the same height. We have to make sure they are not resized
468 * to another value.
470 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
471 ((posChanging->flags & SWP_NOSIZE) == 0) )
473 int newComboHeight;
475 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
476 2*COMBO_YBORDERSIZE();
479 * Resizing a combobox has another side effect, it resizes the dropped
480 * rectangle as well. However, it does it only if the new height for the
481 * combobox is more than the height it should have. In other words,
482 * if the application resizing the combobox only had the intention to resize
483 * the actual control, for example, to do the layout of a dialog that is
484 * resized, the height of the dropdown is not changed.
486 if (posChanging->cy > newComboHeight)
488 TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
489 posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
490 lphc->droppedRect.top);
491 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
494 posChanging->cy = newComboHeight;
497 return 0;
500 /***********************************************************************
501 * COMBO_Create
503 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
504 BOOL unicode )
506 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
507 static const WCHAR editName[] = {'E','d','i','t',0};
509 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
510 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
512 lphc->owner = hwndParent;
515 * The item height and dropped width are not set when the control
516 * is created.
518 lphc->droppedWidth = lphc->editHeight = 0;
521 * The first time we go through, we want to measure the ownerdraw item
523 lphc->wState |= CBF_MEASUREITEM;
525 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
527 if( lphc->owner || !(style & WS_VISIBLE) )
529 UINT lbeStyle = 0;
530 UINT lbeExStyle = 0;
533 * Initialize the dropped rect to the size of the client area of the
534 * control and then, force all the areas of the combobox to be
535 * recalculated.
537 GetClientRect( hwnd, &lphc->droppedRect );
538 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
541 * Adjust the position of the popup listbox if it's necessary
543 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
545 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
548 * If it's a dropdown, the listbox is offset
550 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
551 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
553 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
554 lphc->droppedRect.bottom = lphc->droppedRect.top;
555 if (lphc->droppedRect.right < lphc->droppedRect.left)
556 lphc->droppedRect.right = lphc->droppedRect.left;
557 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
560 /* create listbox popup */
562 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
563 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
565 if( lphc->dwStyle & CBS_SORT )
566 lbeStyle |= LBS_SORT;
567 if( lphc->dwStyle & CBS_HASSTRINGS )
568 lbeStyle |= LBS_HASSTRINGS;
569 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
570 lbeStyle |= LBS_NOINTEGRALHEIGHT;
571 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
572 lbeStyle |= LBS_DISABLENOSCROLL;
574 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
576 lbeStyle |= WS_VISIBLE;
579 * In win 95 look n feel, the listbox in the simple combobox has
580 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
582 lbeStyle &= ~WS_BORDER;
583 lbeExStyle |= WS_EX_CLIENTEDGE;
585 else
587 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
590 if (unicode)
591 lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle,
592 lphc->droppedRect.left,
593 lphc->droppedRect.top,
594 lphc->droppedRect.right - lphc->droppedRect.left,
595 lphc->droppedRect.bottom - lphc->droppedRect.top,
596 hwnd, (HMENU)ID_CB_LISTBOX,
597 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
598 else
599 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
600 lphc->droppedRect.left,
601 lphc->droppedRect.top,
602 lphc->droppedRect.right - lphc->droppedRect.left,
603 lphc->droppedRect.bottom - lphc->droppedRect.top,
604 hwnd, (HMENU)ID_CB_LISTBOX,
605 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
607 if( lphc->hWndLBox )
609 BOOL bEdit = TRUE;
610 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
612 if( lphc->wState & CBF_EDIT )
614 if( lphc->dwStyle & CBS_OEMCONVERT )
615 lbeStyle |= ES_OEMCONVERT;
616 if( lphc->dwStyle & CBS_AUTOHSCROLL )
617 lbeStyle |= ES_AUTOHSCROLL;
618 if( lphc->dwStyle & CBS_LOWERCASE )
619 lbeStyle |= ES_LOWERCASE;
620 else if( lphc->dwStyle & CBS_UPPERCASE )
621 lbeStyle |= ES_UPPERCASE;
623 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
625 if (unicode)
626 lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle,
627 lphc->textRect.left, lphc->textRect.top,
628 lphc->textRect.right - lphc->textRect.left,
629 lphc->textRect.bottom - lphc->textRect.top,
630 hwnd, (HMENU)ID_CB_EDIT,
631 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
632 else
633 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
634 lphc->textRect.left, lphc->textRect.top,
635 lphc->textRect.right - lphc->textRect.left,
636 lphc->textRect.bottom - lphc->textRect.top,
637 hwnd, (HMENU)ID_CB_EDIT,
638 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
640 if( !lphc->hWndEdit )
641 bEdit = FALSE;
644 if( bEdit )
646 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
648 /* Now do the trick with parent */
649 SetParent(lphc->hWndLBox, HWND_DESKTOP);
651 * If the combo is a dropdown, we must resize the control
652 * to fit only the text area and button. To do this,
653 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
654 * will take care of setting the height for us.
656 CBForceDummyResize(lphc);
659 TRACE("init done\n");
660 return 0;
662 ERR("edit control failure.\n");
663 } else ERR("listbox failure.\n");
664 } else ERR("no owner for visible combo.\n");
666 /* CreateWindow() will send WM_NCDESTROY to cleanup */
668 return -1;
671 /***********************************************************************
672 * CBPaintButton
674 * Paint combo button (normal, pressed, and disabled states).
676 static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
678 UINT buttonState = DFCS_SCROLLCOMBOBOX;
680 if( lphc->wState & CBF_NOREDRAW )
681 return;
684 if (lphc->wState & CBF_BUTTONDOWN)
685 buttonState |= DFCS_PUSHED;
687 if (CB_DISABLED(lphc))
688 buttonState |= DFCS_INACTIVE;
690 DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
693 /***********************************************************************
694 * CBPaintText
696 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
698 static void CBPaintText(
699 LPHEADCOMBO lphc,
700 HDC hdc,
701 RECT rectEdit)
703 INT id, size = 0;
704 LPWSTR pText = NULL;
706 if( lphc->wState & CBF_NOREDRAW ) return;
708 TRACE("\n");
710 /* follow Windows combobox that sends a bunch of text
711 * inquiries to its listbox while processing WM_PAINT. */
713 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
715 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
716 if (size == LB_ERR)
717 FIXME("LB_ERR probably not handled yet\n");
718 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
720 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
721 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
722 pText[size] = '\0'; /* just in case */
723 } else return;
725 else
726 if( !CB_OWNERDRAWN(lphc) )
727 return;
729 if( lphc->wState & CBF_EDIT )
731 static const WCHAR empty_stringW[] = { 0 };
732 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
733 if( lphc->wState & CBF_FOCUSED )
734 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
736 else /* paint text field ourselves */
738 UINT itemState = ODS_COMBOBOXEDIT;
739 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
742 * Give ourselves some space.
744 InflateRect( &rectEdit, -1, -1 );
746 if( CB_OWNERDRAWN(lphc) )
748 DRAWITEMSTRUCT dis;
749 HRGN clipRegion;
750 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
752 /* setup state for DRAWITEM message. Owner will highlight */
753 if ( (lphc->wState & CBF_FOCUSED) &&
754 !(lphc->wState & CBF_DROPPED) )
755 itemState |= ODS_SELECTED | ODS_FOCUS;
758 * Save the current clip region.
759 * To retrieve the clip region, we need to create one "dummy"
760 * clip region.
762 clipRegion = CreateRectRgnIndirect(&rectEdit);
764 if (GetClipRgn(hdc, clipRegion)!=1)
766 DeleteObject(clipRegion);
767 clipRegion=NULL;
770 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
772 dis.CtlType = ODT_COMBOBOX;
773 dis.CtlID = ctlid;
774 dis.hwndItem = lphc->self;
775 dis.itemAction = ODA_DRAWENTIRE;
776 dis.itemID = id;
777 dis.itemState = itemState;
778 dis.hDC = hdc;
779 dis.rcItem = rectEdit;
780 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
781 (WPARAM)id, 0 );
784 * Clip the DC and have the parent draw the item.
786 IntersectClipRect(hdc,
787 rectEdit.left, rectEdit.top,
788 rectEdit.right, rectEdit.bottom);
790 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
793 * Reset the clipping region.
795 SelectClipRgn(hdc, clipRegion);
797 else
799 static const WCHAR empty_stringW[] = { 0 };
801 if ( (lphc->wState & CBF_FOCUSED) &&
802 !(lphc->wState & CBF_DROPPED) ) {
804 /* highlight */
805 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
806 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
807 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
810 ExtTextOutW( hdc,
811 rectEdit.left + 1,
812 rectEdit.top + 1,
813 ETO_OPAQUE | ETO_CLIPPED,
814 &rectEdit,
815 pText ? pText : empty_stringW , size, NULL );
817 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
818 DrawFocusRect( hdc, &rectEdit );
821 if( hPrevFont )
822 SelectObject(hdc, hPrevFont );
824 HeapFree( GetProcessHeap(), 0, pText );
827 /***********************************************************************
828 * CBPaintBorder
830 static void CBPaintBorder(
831 HWND hwnd,
832 const HEADCOMBO *lphc,
833 HDC hdc)
835 RECT clientRect;
837 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
839 GetClientRect(hwnd, &clientRect);
841 else
843 CopyRect(&clientRect, &lphc->textRect);
845 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
846 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
849 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
852 /***********************************************************************
853 * COMBO_PrepareColors
855 * This method will sent the appropriate WM_CTLCOLOR message to
856 * prepare and setup the colors for the combo's DC.
858 * It also returns the brush to use for the background.
860 static HBRUSH COMBO_PrepareColors(
861 LPHEADCOMBO lphc,
862 HDC hDC)
864 HBRUSH hBkgBrush;
867 * Get the background brush for this control.
869 if (CB_DISABLED(lphc))
871 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
872 (WPARAM)hDC, (LPARAM)lphc->self );
875 * We have to change the text color since WM_CTLCOLORSTATIC will
876 * set it to the "enabled" color. This is the same behavior as the
877 * edit control
879 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
881 else
883 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
884 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
885 (WPARAM)hDC, (LPARAM)lphc->self );
889 * Catch errors.
891 if( !hBkgBrush )
892 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
894 return hBkgBrush;
898 /***********************************************************************
899 * COMBO_Paint
901 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
903 PAINTSTRUCT ps;
904 HDC hDC;
906 hDC = (hParamDC) ? hParamDC
907 : BeginPaint( lphc->self, &ps);
909 TRACE("hdc=%p\n", hDC);
911 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
913 HBRUSH hPrevBrush, hBkgBrush;
916 * Retrieve the background brush and select it in the
917 * DC.
919 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
921 hPrevBrush = SelectObject( hDC, hBkgBrush );
922 if (!(lphc->wState & CBF_EDIT))
923 FillRect(hDC, &lphc->textRect, hBkgBrush);
926 * In non 3.1 look, there is a sunken border on the combobox
928 CBPaintBorder(lphc->self, lphc, hDC);
930 if( !IsRectEmpty(&lphc->buttonRect) )
932 CBPaintButton(lphc, hDC, lphc->buttonRect);
935 /* paint the edit control padding area */
936 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
938 RECT rPadEdit = lphc->textRect;
940 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
942 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
945 if( !(lphc->wState & CBF_EDIT) )
946 CBPaintText( lphc, hDC, lphc->textRect);
948 if( hPrevBrush )
949 SelectObject( hDC, hPrevBrush );
952 if( !hParamDC )
953 EndPaint(lphc->self, &ps);
955 return 0;
958 /***********************************************************************
959 * CBUpdateLBox
961 * Select listbox entry according to the contents of the edit control.
963 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
965 INT length, idx;
966 LPWSTR pText = NULL;
968 idx = LB_ERR;
969 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
971 if( length > 0 )
972 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
974 TRACE("\t edit text length %i\n", length );
976 if( pText )
978 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
979 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
980 (WPARAM)(-1), (LPARAM)pText );
981 HeapFree( GetProcessHeap(), 0, pText );
984 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
986 /* probably superfluous but Windows sends this too */
987 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
988 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
990 return idx;
993 /***********************************************************************
994 * CBUpdateEdit
996 * Copy a listbox entry to the edit control.
998 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1000 INT length;
1001 LPWSTR pText = NULL;
1002 static const WCHAR empty_stringW[] = { 0 };
1004 TRACE("\t %i\n", index );
1006 if( index >= 0 ) /* got an entry */
1008 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1009 if( length != LB_ERR)
1011 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1013 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1014 (WPARAM)index, (LPARAM)pText );
1019 if( CB_HASSTRINGS(lphc) )
1021 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1022 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1023 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1026 if( lphc->wState & CBF_FOCUSED )
1027 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1029 HeapFree( GetProcessHeap(), 0, pText );
1032 /***********************************************************************
1033 * CBDropDown
1035 * Show listbox popup.
1037 static void CBDropDown( LPHEADCOMBO lphc )
1039 HMONITOR monitor;
1040 MONITORINFO mon_info;
1041 RECT rect,r;
1042 int nItems = 0;
1043 int nDroppedHeight;
1045 TRACE("[%p]: drop down\n", lphc->self);
1047 CB_NOTIFY( lphc, CBN_DROPDOWN );
1049 /* set selection */
1051 lphc->wState |= CBF_DROPPED;
1052 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1054 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1056 /* Update edit only if item is in the list */
1057 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1058 CBUpdateEdit( lphc, lphc->droppedIndex );
1060 else
1062 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1064 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1065 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1066 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1069 /* now set popup position */
1070 GetWindowRect( lphc->self, &rect );
1073 * If it's a dropdown, the listbox is offset
1075 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1076 rect.left += COMBO_EDITBUTTONSPACE();
1078 /* if the dropped height is greater than the total height of the dropped
1079 items list, then force the drop down list height to be the total height
1080 of the items in the dropped list */
1082 /* And Remove any extra space (Best Fit) */
1083 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1084 /* if listbox length has been set directly by its handle */
1085 GetWindowRect(lphc->hWndLBox, &r);
1086 if (nDroppedHeight < r.bottom - r.top)
1087 nDroppedHeight = r.bottom - r.top;
1088 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1090 if (nItems > 0)
1092 int nHeight;
1093 int nIHeight;
1095 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1097 nHeight = nIHeight*nItems;
1099 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1100 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1102 if (nDroppedHeight < nHeight)
1104 if (nItems < 5)
1105 nDroppedHeight = (nItems+1)*nIHeight;
1106 else if (nDroppedHeight < 6*nIHeight)
1107 nDroppedHeight = 6*nIHeight;
1111 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1112 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
1113 mon_info.cbSize = sizeof(mon_info);
1114 GetMonitorInfoW( monitor, &mon_info );
1116 if( (rect.bottom + nDroppedHeight) >= mon_info.rcWork.bottom )
1117 rect.bottom = rect.top - nDroppedHeight;
1119 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1120 lphc->droppedRect.right - lphc->droppedRect.left,
1121 nDroppedHeight,
1122 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1125 if( !(lphc->wState & CBF_NOREDRAW) )
1126 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1127 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1129 EnableWindow( lphc->hWndLBox, TRUE );
1130 if (GetCapture() != lphc->self)
1131 SetCapture(lphc->hWndLBox);
1134 /***********************************************************************
1135 * CBRollUp
1137 * Hide listbox popup.
1139 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1141 HWND hWnd = lphc->self;
1143 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1144 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1146 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1148 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1151 if( lphc->wState & CBF_DROPPED )
1153 RECT rect;
1155 lphc->wState &= ~CBF_DROPPED;
1156 ShowWindow( lphc->hWndLBox, SW_HIDE );
1158 if(GetCapture() == lphc->hWndLBox)
1160 ReleaseCapture();
1163 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1165 rect = lphc->buttonRect;
1167 else
1169 if( bButton )
1171 UnionRect( &rect,
1172 &lphc->buttonRect,
1173 &lphc->textRect);
1175 else
1176 rect = lphc->textRect;
1178 bButton = TRUE;
1181 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1182 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1183 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1184 CB_NOTIFY( lphc, CBN_CLOSEUP );
1189 /***********************************************************************
1190 * COMBO_FlipListbox
1192 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1194 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1196 if( lphc->wState & CBF_DROPPED )
1198 CBRollUp( lphc, ok, bRedrawButton );
1199 return FALSE;
1202 CBDropDown( lphc );
1203 return TRUE;
1206 /***********************************************************************
1207 * CBRepaintButton
1209 static void CBRepaintButton( LPHEADCOMBO lphc )
1211 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1212 UpdateWindow(lphc->self);
1215 /***********************************************************************
1216 * COMBO_SetFocus
1218 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1220 if( !(lphc->wState & CBF_FOCUSED) )
1222 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1223 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1225 /* This is wrong. Message sequences seem to indicate that this
1226 is set *after* the notify. */
1227 /* lphc->wState |= CBF_FOCUSED; */
1229 if( !(lphc->wState & CBF_EDIT) )
1230 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1232 CB_NOTIFY( lphc, CBN_SETFOCUS );
1233 lphc->wState |= CBF_FOCUSED;
1237 /***********************************************************************
1238 * COMBO_KillFocus
1240 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1242 HWND hWnd = lphc->self;
1244 if( lphc->wState & CBF_FOCUSED )
1246 CBRollUp( lphc, FALSE, TRUE );
1247 if( IsWindow( hWnd ) )
1249 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1250 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1252 lphc->wState &= ~CBF_FOCUSED;
1254 /* redraw text */
1255 if( !(lphc->wState & CBF_EDIT) )
1256 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1258 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1263 /***********************************************************************
1264 * COMBO_Command
1266 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1268 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1270 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1272 switch( HIWORD(wParam) >> 8 )
1274 case (EN_SETFOCUS >> 8):
1276 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1278 COMBO_SetFocus( lphc );
1279 break;
1281 case (EN_KILLFOCUS >> 8):
1283 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1285 /* NOTE: it seems that Windows' edit control sends an
1286 * undocumented message WM_USER + 0x1B instead of this
1287 * notification (only when it happens to be a part of
1288 * the combo). ?? - AK.
1291 COMBO_KillFocus( lphc );
1292 break;
1295 case (EN_CHANGE >> 8):
1297 * In some circumstances (when the selection of the combobox
1298 * is changed for example) we don't want the EN_CHANGE notification
1299 * to be forwarded to the parent of the combobox. This code
1300 * checks a flag that is set in these occasions and ignores the
1301 * notification.
1303 if (lphc->wState & CBF_NOLBSELECT)
1305 lphc->wState &= ~CBF_NOLBSELECT;
1307 else
1309 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1312 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1313 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1314 break;
1316 case (EN_UPDATE >> 8):
1317 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1318 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1319 break;
1321 case (EN_ERRSPACE >> 8):
1322 CB_NOTIFY( lphc, CBN_ERRSPACE );
1325 else if( lphc->hWndLBox == hWnd )
1327 switch( (short)HIWORD(wParam) )
1329 case LBN_ERRSPACE:
1330 CB_NOTIFY( lphc, CBN_ERRSPACE );
1331 break;
1333 case LBN_DBLCLK:
1334 CB_NOTIFY( lphc, CBN_DBLCLK );
1335 break;
1337 case LBN_SELCHANGE:
1338 case LBN_SELCANCEL:
1340 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1342 /* do not roll up if selection is being tracked
1343 * by arrow keys in the dropdown listbox */
1344 if (!(lphc->wState & CBF_NOROLLUP))
1346 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1348 else lphc->wState &= ~CBF_NOROLLUP;
1350 CB_NOTIFY( lphc, CBN_SELCHANGE );
1352 if( HIWORD(wParam) == LBN_SELCHANGE)
1354 if( lphc->wState & CBF_EDIT )
1356 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1357 lphc->wState |= CBF_NOLBSELECT;
1358 CBUpdateEdit( lphc, index );
1359 /* select text in edit, as Windows does */
1360 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1362 else
1364 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1365 UpdateWindow(lphc->self);
1368 break;
1370 case LBN_SETFOCUS:
1371 case LBN_KILLFOCUS:
1372 /* nothing to do here since ComboLBox always resets the focus to its
1373 * combo/edit counterpart */
1374 break;
1377 return 0;
1380 /***********************************************************************
1381 * COMBO_ItemOp
1383 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1385 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1387 HWND hWnd = lphc->self;
1388 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1390 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1392 switch( msg )
1394 case WM_DELETEITEM:
1396 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1397 lpIS->CtlType = ODT_COMBOBOX;
1398 lpIS->CtlID = id;
1399 lpIS->hwndItem = hWnd;
1400 break;
1402 case WM_DRAWITEM:
1404 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1405 lpIS->CtlType = ODT_COMBOBOX;
1406 lpIS->CtlID = id;
1407 lpIS->hwndItem = hWnd;
1408 break;
1410 case WM_COMPAREITEM:
1412 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1413 lpIS->CtlType = ODT_COMBOBOX;
1414 lpIS->CtlID = id;
1415 lpIS->hwndItem = hWnd;
1416 break;
1418 case WM_MEASUREITEM:
1420 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1421 lpIS->CtlType = ODT_COMBOBOX;
1422 lpIS->CtlID = id;
1423 break;
1426 return SendMessageW(lphc->owner, msg, id, lParam);
1430 /***********************************************************************
1431 * COMBO_GetTextW
1433 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1435 INT length;
1437 if( lphc->wState & CBF_EDIT )
1438 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1440 /* get it from the listbox */
1442 if (!count || !buf) return 0;
1443 if( lphc->hWndLBox )
1445 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1446 if (idx == LB_ERR) goto error;
1447 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1448 if (length == LB_ERR) goto error;
1450 /* 'length' is without the terminating character */
1451 if (length >= count)
1453 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1454 if (!lpBuffer) goto error;
1455 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1457 /* truncate if buffer is too short */
1458 if (length != LB_ERR)
1460 lstrcpynW( buf, lpBuffer, count );
1461 length = count;
1463 HeapFree( GetProcessHeap(), 0, lpBuffer );
1465 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1467 if (length == LB_ERR) return 0;
1468 return length;
1471 error: /* error - truncate string, return zero */
1472 buf[0] = 0;
1473 return 0;
1477 /***********************************************************************
1478 * COMBO_GetTextA
1480 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1481 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1483 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1485 INT length;
1487 if( lphc->wState & CBF_EDIT )
1488 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1490 /* get it from the listbox */
1492 if (!count || !buf) return 0;
1493 if( lphc->hWndLBox )
1495 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1496 if (idx == LB_ERR) goto error;
1497 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1498 if (length == LB_ERR) goto error;
1500 /* 'length' is without the terminating character */
1501 if (length >= count)
1503 LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1504 if (!lpBuffer) goto error;
1505 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1507 /* truncate if buffer is too short */
1508 if (length != LB_ERR)
1510 lstrcpynA( buf, lpBuffer, count );
1511 length = count;
1513 HeapFree( GetProcessHeap(), 0, lpBuffer );
1515 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1517 if (length == LB_ERR) return 0;
1518 return length;
1521 error: /* error - truncate string, return zero */
1522 buf[0] = 0;
1523 return 0;
1527 /***********************************************************************
1528 * CBResetPos
1530 * This function sets window positions according to the updated
1531 * component placement struct.
1533 static void CBResetPos(
1534 LPHEADCOMBO lphc,
1535 const RECT *rectEdit,
1536 const RECT *rectLB,
1537 BOOL bRedraw)
1539 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1541 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1542 * sizing messages */
1544 if( lphc->wState & CBF_EDIT )
1545 SetWindowPos( lphc->hWndEdit, 0,
1546 rectEdit->left, rectEdit->top,
1547 rectEdit->right - rectEdit->left,
1548 rectEdit->bottom - rectEdit->top,
1549 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1551 SetWindowPos( lphc->hWndLBox, 0,
1552 rectLB->left, rectLB->top,
1553 rectLB->right - rectLB->left,
1554 rectLB->bottom - rectLB->top,
1555 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1557 if( bDrop )
1559 if( lphc->wState & CBF_DROPPED )
1561 lphc->wState &= ~CBF_DROPPED;
1562 ShowWindow( lphc->hWndLBox, SW_HIDE );
1565 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1566 RedrawWindow( lphc->self, NULL, 0,
1567 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1572 /***********************************************************************
1573 * COMBO_Size
1575 static void COMBO_Size( LPHEADCOMBO lphc, BOOL bRedraw )
1577 CBCalcPlacement(lphc->self,
1578 lphc,
1579 &lphc->textRect,
1580 &lphc->buttonRect,
1581 &lphc->droppedRect);
1583 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, bRedraw );
1587 /***********************************************************************
1588 * COMBO_Font
1590 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1593 * Set the font
1595 lphc->hFont = hFont;
1598 * Propagate to owned windows.
1600 if( lphc->wState & CBF_EDIT )
1601 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1602 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1605 * Redo the layout of the control.
1607 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1609 CBCalcPlacement(lphc->self,
1610 lphc,
1611 &lphc->textRect,
1612 &lphc->buttonRect,
1613 &lphc->droppedRect);
1615 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1617 else
1619 CBForceDummyResize(lphc);
1624 /***********************************************************************
1625 * COMBO_SetItemHeight
1627 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1629 LRESULT lRet = CB_ERR;
1631 if( index == -1 ) /* set text field height */
1633 if( height < 32768 )
1635 lphc->editHeight = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1638 * Redo the layout of the control.
1640 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1642 CBCalcPlacement(lphc->self,
1643 lphc,
1644 &lphc->textRect,
1645 &lphc->buttonRect,
1646 &lphc->droppedRect);
1648 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1650 else
1652 CBForceDummyResize(lphc);
1655 lRet = height;
1658 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1659 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1660 (WPARAM)index, (LPARAM)height );
1661 return lRet;
1664 /***********************************************************************
1665 * COMBO_SelectString
1667 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1669 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1670 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1671 if( index >= 0 )
1673 if( lphc->wState & CBF_EDIT )
1674 CBUpdateEdit( lphc, index );
1675 else
1677 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1680 return (LRESULT)index;
1683 /***********************************************************************
1684 * COMBO_LButtonDown
1686 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1688 POINT pt;
1689 BOOL bButton;
1690 HWND hWnd = lphc->self;
1692 pt.x = (short)LOWORD(lParam);
1693 pt.y = (short)HIWORD(lParam);
1694 bButton = PtInRect(&lphc->buttonRect, pt);
1696 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1697 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1699 lphc->wState |= CBF_BUTTONDOWN;
1700 if( lphc->wState & CBF_DROPPED )
1702 /* got a click to cancel selection */
1704 lphc->wState &= ~CBF_BUTTONDOWN;
1705 CBRollUp( lphc, TRUE, FALSE );
1706 if( !IsWindow( hWnd ) ) return;
1708 if( lphc->wState & CBF_CAPTURE )
1710 lphc->wState &= ~CBF_CAPTURE;
1711 ReleaseCapture();
1714 else
1716 /* drop down the listbox and start tracking */
1718 lphc->wState |= CBF_CAPTURE;
1719 SetCapture( hWnd );
1720 CBDropDown( lphc );
1722 if( bButton ) CBRepaintButton( lphc );
1726 /***********************************************************************
1727 * COMBO_LButtonUp
1729 * Release capture and stop tracking if needed.
1731 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1733 if( lphc->wState & CBF_CAPTURE )
1735 lphc->wState &= ~CBF_CAPTURE;
1736 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1738 INT index = CBUpdateLBox( lphc, TRUE );
1739 /* Update edit only if item is in the list */
1740 if(index >= 0)
1742 lphc->wState |= CBF_NOLBSELECT;
1743 CBUpdateEdit( lphc, index );
1744 lphc->wState &= ~CBF_NOLBSELECT;
1747 ReleaseCapture();
1748 SetCapture(lphc->hWndLBox);
1751 if( lphc->wState & CBF_BUTTONDOWN )
1753 lphc->wState &= ~CBF_BUTTONDOWN;
1754 CBRepaintButton( lphc );
1758 /***********************************************************************
1759 * COMBO_MouseMove
1761 * Two things to do - track combo button and release capture when
1762 * pointer goes into the listbox.
1764 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1766 POINT pt;
1767 RECT lbRect;
1769 pt.x = (short)LOWORD(lParam);
1770 pt.y = (short)HIWORD(lParam);
1772 if( lphc->wState & CBF_BUTTONDOWN )
1774 BOOL bButton;
1776 bButton = PtInRect(&lphc->buttonRect, pt);
1778 if( !bButton )
1780 lphc->wState &= ~CBF_BUTTONDOWN;
1781 CBRepaintButton( lphc );
1785 GetClientRect( lphc->hWndLBox, &lbRect );
1786 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1787 if( PtInRect(&lbRect, pt) )
1789 lphc->wState &= ~CBF_CAPTURE;
1790 ReleaseCapture();
1791 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1793 /* hand over pointer tracking */
1794 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1798 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1800 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1801 return FALSE;
1803 pcbi->rcItem = lphc->textRect;
1804 pcbi->rcButton = lphc->buttonRect;
1805 pcbi->stateButton = 0;
1806 if (lphc->wState & CBF_BUTTONDOWN)
1807 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1808 if (IsRectEmpty(&lphc->buttonRect))
1809 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1810 pcbi->hwndCombo = lphc->self;
1811 pcbi->hwndItem = lphc->hWndEdit;
1812 pcbi->hwndList = lphc->hWndLBox;
1813 return TRUE;
1816 static char *strdupA(LPCSTR str)
1818 char *ret;
1819 DWORD len;
1821 if(!str) return NULL;
1823 len = strlen(str);
1824 ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1825 memcpy(ret, str, len + 1);
1826 return ret;
1829 /***********************************************************************
1830 * ComboWndProc_common
1832 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1834 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1836 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1837 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1839 if (!IsWindow(hwnd)) return 0;
1841 if( lphc || message == WM_NCCREATE )
1842 switch(message)
1845 /* System messages */
1847 case WM_NCCREATE:
1849 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1850 ((LPCREATESTRUCTA)lParam)->style;
1851 return COMBO_NCCreate(hwnd, style);
1853 case WM_NCDESTROY:
1854 COMBO_NCDestroy(lphc);
1855 break;/* -> DefWindowProc */
1857 case WM_CREATE:
1859 HWND hwndParent;
1860 LONG style;
1861 if(unicode)
1863 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1864 style = ((LPCREATESTRUCTW)lParam)->style;
1866 else
1868 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1869 style = ((LPCREATESTRUCTA)lParam)->style;
1871 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1874 case WM_PRINTCLIENT:
1875 /* Fallthrough */
1876 case WM_PAINT:
1877 /* wParam may contain a valid HDC! */
1878 return COMBO_Paint(lphc, (HDC)wParam);
1880 case WM_ERASEBKGND:
1881 /* do all painting in WM_PAINT like Windows does */
1882 return 1;
1884 case WM_GETDLGCODE:
1886 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1887 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1889 int vk = (int)((LPMSG)lParam)->wParam;
1891 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1892 result |= DLGC_WANTMESSAGE;
1894 return result;
1896 case WM_WINDOWPOSCHANGING:
1897 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1898 case WM_WINDOWPOSCHANGED:
1899 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1900 * In that case, the Combobox itself will not be resized, so we won't
1901 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1902 * do it here.
1904 /* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
1905 * Z-order based painting.
1907 /* fall through */
1908 case WM_SIZE:
1909 if( lphc->hWndLBox &&
1910 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc, message == WM_SIZE );
1911 return TRUE;
1912 case WM_SETFONT:
1913 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1914 return TRUE;
1915 case WM_GETFONT:
1916 return (LRESULT)lphc->hFont;
1917 case WM_SETFOCUS:
1918 if( lphc->wState & CBF_EDIT )
1919 SetFocus( lphc->hWndEdit );
1920 else
1921 COMBO_SetFocus( lphc );
1922 return TRUE;
1923 case WM_KILLFOCUS:
1925 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1926 if( !hwndFocus ||
1927 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1928 COMBO_KillFocus( lphc );
1929 return TRUE;
1931 case WM_COMMAND:
1932 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1933 case WM_GETTEXT:
1934 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1935 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1936 case WM_SETTEXT:
1937 case WM_GETTEXTLENGTH:
1938 case WM_CLEAR:
1939 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1941 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1942 if (j == -1) return 0;
1943 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1944 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1946 else if( lphc->wState & CBF_EDIT )
1948 LRESULT ret;
1949 lphc->wState |= CBF_NOEDITNOTIFY;
1950 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1951 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1952 lphc->wState &= ~CBF_NOEDITNOTIFY;
1953 return ret;
1955 else return CB_ERR;
1956 case WM_CUT:
1957 case WM_PASTE:
1958 case WM_COPY:
1959 if( lphc->wState & CBF_EDIT )
1961 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1962 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1964 else return CB_ERR;
1966 case WM_DRAWITEM:
1967 case WM_DELETEITEM:
1968 case WM_COMPAREITEM:
1969 case WM_MEASUREITEM:
1970 return COMBO_ItemOp(lphc, message, lParam);
1971 case WM_ENABLE:
1972 if( lphc->wState & CBF_EDIT )
1973 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1974 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1976 /* Force the control to repaint when the enabled state changes. */
1977 InvalidateRect(lphc->self, NULL, TRUE);
1978 return TRUE;
1979 case WM_SETREDRAW:
1980 if( wParam )
1981 lphc->wState &= ~CBF_NOREDRAW;
1982 else
1983 lphc->wState |= CBF_NOREDRAW;
1985 if( lphc->wState & CBF_EDIT )
1986 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1987 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1988 return 0;
1989 case WM_SYSKEYDOWN:
1990 if( KEYDATA_ALT & HIWORD(lParam) )
1991 if( wParam == VK_UP || wParam == VK_DOWN )
1992 COMBO_FlipListbox( lphc, FALSE, FALSE );
1993 return 0;
1995 case WM_CHAR:
1996 case WM_IME_CHAR:
1997 case WM_KEYDOWN:
1999 HWND hwndTarget;
2001 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2002 (lphc->wState & CBF_DROPPED))
2004 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2005 return TRUE;
2007 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
2009 COMBO_FlipListbox( lphc, FALSE, FALSE );
2010 return TRUE;
2013 if( lphc->wState & CBF_EDIT )
2014 hwndTarget = lphc->hWndEdit;
2015 else
2016 hwndTarget = lphc->hWndLBox;
2018 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2019 SendMessageA(hwndTarget, message, wParam, lParam);
2021 case WM_LBUTTONDOWN:
2022 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2023 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2024 return TRUE;
2025 case WM_LBUTTONUP:
2026 COMBO_LButtonUp( lphc );
2027 return TRUE;
2028 case WM_MOUSEMOVE:
2029 if( lphc->wState & CBF_CAPTURE )
2030 COMBO_MouseMove( lphc, wParam, lParam );
2031 return TRUE;
2033 case WM_MOUSEWHEEL:
2034 if (wParam & (MK_SHIFT | MK_CONTROL))
2035 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2036 DefWindowProcA(hwnd, message, wParam, lParam);
2038 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2039 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2040 return TRUE;
2042 /* Combo messages */
2044 case CB_ADDSTRING:
2045 if( unicode )
2047 if( lphc->dwStyle & CBS_LOWERCASE )
2048 CharLowerW((LPWSTR)lParam);
2049 else if( lphc->dwStyle & CBS_UPPERCASE )
2050 CharUpperW((LPWSTR)lParam);
2051 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2053 else /* unlike the unicode version, the ansi version does not overwrite
2054 the string if converting case */
2056 char *string = NULL;
2057 LRESULT ret;
2058 if( lphc->dwStyle & CBS_LOWERCASE )
2060 string = strdupA((LPSTR)lParam);
2061 CharLowerA(string);
2064 else if( lphc->dwStyle & CBS_UPPERCASE )
2066 string = strdupA((LPSTR)lParam);
2067 CharUpperA(string);
2070 ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
2071 HeapFree(GetProcessHeap(), 0, string);
2072 return ret;
2074 case CB_INSERTSTRING:
2075 if( unicode )
2077 if( lphc->dwStyle & CBS_LOWERCASE )
2078 CharLowerW((LPWSTR)lParam);
2079 else if( lphc->dwStyle & CBS_UPPERCASE )
2080 CharUpperW((LPWSTR)lParam);
2081 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2083 else
2085 if( lphc->dwStyle & CBS_LOWERCASE )
2086 CharLowerA((LPSTR)lParam);
2087 else if( lphc->dwStyle & CBS_UPPERCASE )
2088 CharUpperA((LPSTR)lParam);
2090 return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2092 case CB_DELETESTRING:
2093 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2094 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2095 case CB_SELECTSTRING:
2096 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2097 case CB_FINDSTRING:
2098 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2099 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2100 case CB_FINDSTRINGEXACT:
2101 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2102 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2103 case CB_SETITEMHEIGHT:
2104 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2105 case CB_GETITEMHEIGHT:
2106 if( (INT)wParam >= 0 ) /* listbox item */
2107 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2108 return CBGetTextAreaHeight(hwnd, lphc);
2109 case CB_RESETCONTENT:
2110 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2111 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2113 static const WCHAR empty_stringW[] = { 0 };
2114 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2116 else
2117 InvalidateRect(lphc->self, NULL, TRUE);
2118 return TRUE;
2119 case CB_INITSTORAGE:
2120 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2121 case CB_GETHORIZONTALEXTENT:
2122 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2123 case CB_SETHORIZONTALEXTENT:
2124 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2125 case CB_GETTOPINDEX:
2126 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2127 case CB_GETLOCALE:
2128 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2129 case CB_SETLOCALE:
2130 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2131 case CB_GETDROPPEDWIDTH:
2132 if( lphc->droppedWidth )
2133 return lphc->droppedWidth;
2134 return lphc->droppedRect.right - lphc->droppedRect.left;
2135 case CB_SETDROPPEDWIDTH:
2136 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2137 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2138 return CB_ERR;
2139 case CB_GETDROPPEDCONTROLRECT:
2140 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2141 return CB_OKAY;
2142 case CB_GETDROPPEDSTATE:
2143 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2144 case CB_DIR:
2145 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2146 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2148 case CB_SHOWDROPDOWN:
2149 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2151 if( wParam )
2153 if( !(lphc->wState & CBF_DROPPED) )
2154 CBDropDown( lphc );
2156 else
2157 if( lphc->wState & CBF_DROPPED )
2158 CBRollUp( lphc, FALSE, TRUE );
2160 return TRUE;
2161 case CB_GETCOUNT:
2162 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2163 case CB_GETCURSEL:
2164 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2165 case CB_SETCURSEL:
2166 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2167 if( lParam >= 0 )
2168 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2170 /* no LBN_SELCHANGE in this case, update manually */
2171 if( lphc->wState & CBF_EDIT )
2172 CBUpdateEdit( lphc, (INT)wParam );
2173 else
2174 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2175 lphc->wState &= ~CBF_SELCHANGE;
2176 return lParam;
2177 case CB_GETLBTEXT:
2178 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2179 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2180 case CB_GETLBTEXTLEN:
2181 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2182 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2183 case CB_GETITEMDATA:
2184 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2185 case CB_SETITEMDATA:
2186 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2187 case CB_GETEDITSEL:
2188 /* Edit checks passed parameters itself */
2189 if( lphc->wState & CBF_EDIT )
2190 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2191 return CB_ERR;
2192 case CB_SETEDITSEL:
2193 if( lphc->wState & CBF_EDIT )
2194 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2195 (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2196 return CB_ERR;
2197 case CB_SETEXTENDEDUI:
2198 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2199 return CB_ERR;
2200 if( wParam )
2201 lphc->wState |= CBF_EUI;
2202 else lphc->wState &= ~CBF_EUI;
2203 return CB_OKAY;
2204 case CB_GETEXTENDEDUI:
2205 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2206 case CB_GETCOMBOBOXINFO:
2207 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2208 case CB_LIMITTEXT:
2209 if( lphc->wState & CBF_EDIT )
2210 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2211 default:
2212 if (message >= WM_USER)
2213 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2214 message - WM_USER, wParam, lParam );
2215 break;
2217 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2218 DefWindowProcA(hwnd, message, wParam, lParam);
2221 /*************************************************************************
2222 * GetComboBoxInfo (USER32.@)
2224 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2225 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2227 TRACE("(%p, %p)\n", hwndCombo, pcbi);
2228 return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);