winemac: Use unixlib interface for dragdrop.c calls.
[wine.git] / dlls / user32 / combo.c
blob73ec5691e8864736008f896c54e3f8f2024ce253
1 /*
2 * Combo controls
4 * Copyright 1997 Alex Korobka
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * TODO:
21 * - CB_SETTOPINDEX
24 #include <stdarg.h>
25 #include <string.h>
27 #define OEMRESOURCE
29 #include "windef.h"
30 #include "winbase.h"
31 #include "ntuser.h"
32 #include "user_private.h"
33 #include "win.h"
34 #include "controls.h"
35 #include "winternl.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(combo);
40 /* bits in the dwKeyData */
41 #define KEYDATA_ALT 0x2000
42 #define KEYDATA_PREVSTATE 0x4000
45 * Additional combo box definitions
48 #define CB_NOTIFY( lphc, code ) \
49 (SendMessageW((lphc)->owner, WM_COMMAND, \
50 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
52 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
53 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
54 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
55 #define CB_HWND( lphc ) ((lphc)->self)
56 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
58 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
61 * Drawing globals
63 static HBITMAP hComboBmp = 0;
64 static UINT CBitHeight, CBitWidth;
67 * Look and feel dependent "constants"
70 #define COMBO_YBORDERGAP 5
71 #define COMBO_XBORDERSIZE() 2
72 #define COMBO_YBORDERSIZE() 2
73 #define COMBO_EDITBUTTONSPACE() 0
74 #define EDIT_CONTROL_PADDING() 1
76 static void CBCalcPlacement(HEADCOMBO *combo);
77 static void CBResetPos(HEADCOMBO *combo, BOOL redraw);
79 /*********************************************************************
80 * combo class descriptor
82 const struct builtin_class_descr COMBO_builtin_class =
84 L"ComboBox", /* name */
85 CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */
86 WINPROC_COMBO, /* proc */
87 sizeof(HEADCOMBO *), /* extra */
88 IDC_ARROW, /* cursor */
89 0 /* brush */
93 /***********************************************************************
94 * COMBO_Init
96 * Load combo button bitmap.
98 static BOOL COMBO_Init(void)
100 HDC hDC;
102 if( hComboBmp ) return TRUE;
103 if( (hDC = CreateCompatibleDC(0)) )
105 BOOL bRet = FALSE;
106 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
108 BITMAP bm;
109 HBITMAP hPrevB;
110 RECT r;
112 GetObjectW( hComboBmp, sizeof(bm), &bm );
113 CBitHeight = bm.bmHeight;
114 CBitWidth = bm.bmWidth;
116 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
118 hPrevB = SelectObject( hDC, hComboBmp);
119 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
120 InvertRect( hDC, &r );
121 SelectObject( hDC, hPrevB );
122 bRet = TRUE;
124 DeleteDC( hDC );
125 return bRet;
127 return FALSE;
130 /***********************************************************************
131 * COMBO_NCCreate
133 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
135 LPHEADCOMBO lphc;
137 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
139 lphc->self = hwnd;
140 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
142 /* some braindead apps do try to use scrollbar/border flags */
144 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
145 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
148 * We also have to remove the client edge style to make sure
149 * we don't end-up with a non client area.
151 SetWindowLongW( hwnd, GWL_EXSTYLE,
152 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
154 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
155 lphc->dwStyle |= CBS_HASSTRINGS;
156 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
157 lphc->wState |= CBF_NOTIFY;
159 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
160 return TRUE;
162 return FALSE;
165 /***********************************************************************
166 * COMBO_NCDestroy
168 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
171 if( lphc )
173 TRACE("[%p]: freeing storage\n", lphc->self);
175 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
176 NtUserDestroyWindow( lphc->hWndLBox );
178 SetWindowLongPtrW( lphc->self, 0, 0 );
179 HeapFree( GetProcessHeap(), 0, lphc );
181 return 0;
184 static INT combo_get_text_height(const HEADCOMBO *combo)
186 HDC hdc = GetDC(combo->self);
187 HFONT prev_font = 0;
188 TEXTMETRICW tm;
190 if (combo->hFont)
191 prev_font = SelectObject(hdc, combo->hFont);
193 GetTextMetricsW(hdc, &tm);
195 if (prev_font)
196 SelectObject(hdc, prev_font);
198 NtUserReleaseDC( combo->self, hdc );
200 return tm.tmHeight + 4;
203 /***********************************************************************
204 * CBGetTextAreaHeight
206 * This method will calculate the height of the text area of the
207 * combobox.
208 * The height of the text area is set in two ways.
209 * It can be set explicitly through a combobox message or through a
210 * WM_MEASUREITEM callback.
211 * If this is not the case, the height is set to font height + 4px
212 * This height was determined through experimentation.
213 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
215 static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
217 INT item_height, text_height;
219 if (clip_item_height && !CB_OWNERDRAWN(lphc))
221 text_height = combo_get_text_height(lphc);
222 if (lphc->item_height < text_height)
223 lphc->item_height = text_height;
226 item_height = lphc->item_height;
229 * Check the ownerdraw case if we haven't asked the parent the size
230 * of the item yet.
232 if ( CB_OWNERDRAWN(lphc) &&
233 (lphc->wState & CBF_MEASUREITEM) )
235 MEASUREITEMSTRUCT measureItem;
236 RECT clientRect;
237 INT originalItemHeight = item_height;
238 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
241 * We use the client rect for the width of the item.
243 GetClientRect(lphc->self, &clientRect);
245 lphc->wState &= ~CBF_MEASUREITEM;
248 * Send a first one to measure the size of the text area
250 measureItem.CtlType = ODT_COMBOBOX;
251 measureItem.CtlID = id;
252 measureItem.itemID = -1;
253 measureItem.itemWidth = clientRect.right;
254 measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */
255 measureItem.itemData = 0;
256 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
257 item_height = 6 + measureItem.itemHeight;
260 * Send a second one in the case of a fixed ownerdraw list to calculate the
261 * size of the list items. (we basically do this on behalf of the listbox)
263 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
265 measureItem.CtlType = ODT_COMBOBOX;
266 measureItem.CtlID = id;
267 measureItem.itemID = 0;
268 measureItem.itemWidth = clientRect.right;
269 measureItem.itemHeight = originalItemHeight;
270 measureItem.itemData = 0;
271 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
272 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
276 * Keep the size for the next time
278 lphc->item_height = item_height;
281 return item_height;
284 /***********************************************************************
285 * CBForceDummyResize
287 * The dummy resize is used for listboxes that have a popup to trigger
288 * a re-arranging of the contents of the combobox and the recalculation
289 * of the size of the "real" control window.
291 static void CBForceDummyResize(LPHEADCOMBO lphc)
293 RECT windowRect;
294 int newComboHeight;
296 newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE();
298 GetWindowRect(lphc->self, &windowRect);
301 * We have to be careful, resizing a combobox also has the meaning that the
302 * dropped rect will be resized. In this case, we want to trigger a resize
303 * to recalculate layout but we don't want to change the dropped rectangle
304 * So, we pass the height of text area of control as the height.
305 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
306 * message.
308 lphc->wState |= CBF_NORESIZE;
309 NtUserSetWindowPos( lphc->self,
310 NULL,
311 0, 0,
312 windowRect.right - windowRect.left,
313 newComboHeight,
314 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
315 lphc->wState &= ~CBF_NORESIZE;
317 CBCalcPlacement(lphc);
318 CBResetPos(lphc, FALSE);
321 /***********************************************************************
322 * CBCalcPlacement
324 * Set up component coordinates given valid lphc->RectCombo.
326 static void CBCalcPlacement(HEADCOMBO *combo)
328 /* Start with the client rectangle. */
329 GetClientRect(combo->self, &combo->textRect);
331 /* Remove the borders */
332 InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
334 /* Chop off the bottom part to fit with the height of the text area. */
335 combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
337 /* The button starts the same vertical position as the text area. */
338 combo->buttonRect = combo->textRect;
340 /* If the combobox is "simple" there is no button. */
341 if (CB_GETTYPE(combo) == CBS_SIMPLE)
342 combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0;
343 else
346 * Let's assume the combobox button is the same width as the
347 * scrollbar button.
348 * size the button horizontally and cut-off the text area.
350 combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
351 combo->textRect.right = combo->buttonRect.left;
354 /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
355 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
356 combo->textRect.right -= COMBO_EDITBUTTONSPACE();
358 /* If we have an edit control, we space it away from the borders slightly. */
359 if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
360 InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
362 /* Adjust the size of the listbox popup. */
363 if (CB_GETTYPE(combo) == CBS_SIMPLE)
365 GetClientRect(combo->self, &combo->droppedRect);
366 combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
368 else
370 /* Make sure the dropped width is as large as the combobox itself. */
371 if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
373 combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
375 /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
376 with the right side of the combobox. */
377 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
378 combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
380 else
381 combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
384 /* Disallow negative window width */
385 if (combo->textRect.right < combo->textRect.left)
386 combo->textRect.right = combo->textRect.left;
388 TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
389 wine_dbgstr_rect(&combo->droppedRect));
392 /***********************************************************************
393 * CBGetDroppedControlRect
395 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
397 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
398 of the combo box and the lower right corner of the listbox */
400 GetWindowRect(lphc->self, lpRect);
402 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
403 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
407 /***********************************************************************
408 * COMBO_Create
410 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
411 BOOL unicode )
413 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
414 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
416 lphc->owner = hwndParent;
418 lphc->droppedWidth = 0;
420 lphc->item_height = combo_get_text_height(lphc);
423 * The first time we go through, we want to measure the ownerdraw item
425 lphc->wState |= CBF_MEASUREITEM;
427 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
429 if( lphc->owner || !(style & WS_VISIBLE) )
431 UINT lbeStyle = 0;
432 UINT lbeExStyle = 0;
435 * Initialize the dropped rect to the size of the client area of the
436 * control and then, force all the areas of the combobox to be
437 * recalculated.
439 GetClientRect( hwnd, &lphc->droppedRect );
440 CBCalcPlacement(lphc);
443 * Adjust the position of the popup listbox if it's necessary
445 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
447 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
450 * If it's a dropdown, the listbox is offset
452 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
453 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
455 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
456 lphc->droppedRect.bottom = lphc->droppedRect.top;
457 if (lphc->droppedRect.right < lphc->droppedRect.left)
458 lphc->droppedRect.right = lphc->droppedRect.left;
459 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
462 /* create listbox popup */
464 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
465 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
467 if( lphc->dwStyle & CBS_SORT )
468 lbeStyle |= LBS_SORT;
469 if( lphc->dwStyle & CBS_HASSTRINGS )
470 lbeStyle |= LBS_HASSTRINGS;
471 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
472 lbeStyle |= LBS_NOINTEGRALHEIGHT;
473 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
474 lbeStyle |= LBS_DISABLENOSCROLL;
476 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
478 lbeStyle |= WS_VISIBLE;
481 * In win 95 look n feel, the listbox in the simple combobox has
482 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
484 lbeStyle &= ~WS_BORDER;
485 lbeExStyle |= WS_EX_CLIENTEDGE;
487 else
489 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
492 if (unicode)
493 lphc->hWndLBox = CreateWindowExW(lbeExStyle, L"ComboLBox", NULL, lbeStyle,
494 lphc->droppedRect.left,
495 lphc->droppedRect.top,
496 lphc->droppedRect.right - lphc->droppedRect.left,
497 lphc->droppedRect.bottom - lphc->droppedRect.top,
498 hwnd, (HMENU)ID_CB_LISTBOX,
499 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
500 else
501 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
502 lphc->droppedRect.left,
503 lphc->droppedRect.top,
504 lphc->droppedRect.right - lphc->droppedRect.left,
505 lphc->droppedRect.bottom - lphc->droppedRect.top,
506 hwnd, (HMENU)ID_CB_LISTBOX,
507 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
509 if( lphc->hWndLBox )
511 BOOL bEdit = TRUE;
512 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
514 if( lphc->wState & CBF_EDIT )
516 if( lphc->dwStyle & CBS_OEMCONVERT )
517 lbeStyle |= ES_OEMCONVERT;
518 if( lphc->dwStyle & CBS_AUTOHSCROLL )
519 lbeStyle |= ES_AUTOHSCROLL;
520 if( lphc->dwStyle & CBS_LOWERCASE )
521 lbeStyle |= ES_LOWERCASE;
522 else if( lphc->dwStyle & CBS_UPPERCASE )
523 lbeStyle |= ES_UPPERCASE;
525 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
527 if (unicode)
528 lphc->hWndEdit = CreateWindowExW(0, L"Edit", NULL, lbeStyle,
529 lphc->textRect.left, lphc->textRect.top,
530 lphc->textRect.right - lphc->textRect.left,
531 lphc->textRect.bottom - lphc->textRect.top,
532 hwnd, (HMENU)ID_CB_EDIT,
533 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
534 else
535 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
536 lphc->textRect.left, lphc->textRect.top,
537 lphc->textRect.right - lphc->textRect.left,
538 lphc->textRect.bottom - lphc->textRect.top,
539 hwnd, (HMENU)ID_CB_EDIT,
540 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
542 if( !lphc->hWndEdit )
543 bEdit = FALSE;
546 if( bEdit )
548 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
550 /* Now do the trick with parent */
551 NtUserSetParent( lphc->hWndLBox, HWND_DESKTOP );
553 * If the combo is a dropdown, we must resize the control
554 * to fit only the text area and button. To do this,
555 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
556 * will take care of setting the height for us.
558 CBForceDummyResize(lphc);
561 TRACE("init done\n");
562 return 0;
564 ERR("edit control failure.\n");
565 } else ERR("listbox failure.\n");
566 } else ERR("no owner for visible combo.\n");
568 /* CreateWindow() will send WM_NCDESTROY to cleanup */
570 return -1;
573 /***********************************************************************
574 * CBPaintButton
576 * Paint combo button (normal, pressed, and disabled states).
578 static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
580 UINT buttonState = DFCS_SCROLLCOMBOBOX;
582 if (IsRectEmpty(&lphc->buttonRect))
583 return;
585 if( lphc->wState & CBF_NOREDRAW )
586 return;
589 if (lphc->wState & CBF_BUTTONDOWN)
590 buttonState |= DFCS_PUSHED;
592 if (CB_DISABLED(lphc))
593 buttonState |= DFCS_INACTIVE;
595 DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
598 /***********************************************************************
599 * COMBO_PrepareColors
601 * This method will sent the appropriate WM_CTLCOLOR message to
602 * prepare and setup the colors for the combo's DC.
604 * It also returns the brush to use for the background.
606 static HBRUSH COMBO_PrepareColors(
607 LPHEADCOMBO lphc,
608 HDC hDC)
610 HBRUSH hBkgBrush;
613 * Get the background brush for this control.
615 if (CB_DISABLED(lphc))
617 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
618 (WPARAM)hDC, (LPARAM)lphc->self );
621 * We have to change the text color since WM_CTLCOLORSTATIC will
622 * set it to the "enabled" color. This is the same behavior as the
623 * edit control
625 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
627 else
629 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
630 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
631 (WPARAM)hDC, (LPARAM)lphc->self );
635 * Catch errors.
637 if( !hBkgBrush )
638 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
640 return hBkgBrush;
643 /***********************************************************************
644 * CBPaintText
646 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
648 static void CBPaintText(
649 LPHEADCOMBO lphc,
650 HDC hdc_paint)
652 RECT rectEdit = lphc->textRect;
653 INT id, size = 0;
654 LPWSTR pText = NULL;
656 TRACE("\n");
658 /* follow Windows combobox that sends a bunch of text
659 * inquiries to its listbox while processing WM_PAINT. */
661 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
663 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
664 if (size == LB_ERR)
665 FIXME("LB_ERR probably not handled yet\n");
666 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
668 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
669 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
670 pText[size] = '\0'; /* just in case */
671 } else return;
674 if( lphc->wState & CBF_EDIT )
676 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : L"" );
677 if( lphc->wState & CBF_FOCUSED )
678 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
680 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
682 /* paint text field ourselves */
683 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
684 UINT itemState = ODS_COMBOBOXEDIT;
685 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
686 HBRUSH hPrevBrush, hBkgBrush;
689 * Give ourselves some space.
691 InflateRect( &rectEdit, -1, -1 );
693 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
694 hPrevBrush = SelectObject( hdc, hBkgBrush );
695 FillRect( hdc, &rectEdit, hBkgBrush );
697 if( CB_OWNERDRAWN(lphc) )
699 DRAWITEMSTRUCT dis;
700 HRGN clipRegion;
701 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
703 /* setup state for DRAWITEM message. Owner will highlight */
704 if ( (lphc->wState & CBF_FOCUSED) &&
705 !(lphc->wState & CBF_DROPPED) )
706 itemState |= ODS_SELECTED | ODS_FOCUS;
708 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
710 dis.CtlType = ODT_COMBOBOX;
711 dis.CtlID = ctlid;
712 dis.hwndItem = lphc->self;
713 dis.itemAction = ODA_DRAWENTIRE;
714 dis.itemID = id;
715 dis.itemState = itemState;
716 dis.hDC = hdc;
717 dis.rcItem = rectEdit;
718 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
721 * Clip the DC and have the parent draw the item.
723 clipRegion = set_control_clipping( hdc, &rectEdit );
725 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
727 SelectClipRgn( hdc, clipRegion );
728 if (clipRegion) DeleteObject( clipRegion );
730 else
732 if ( (lphc->wState & CBF_FOCUSED) &&
733 !(lphc->wState & CBF_DROPPED) ) {
735 /* highlight */
736 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
737 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
738 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
741 ExtTextOutW( hdc,
742 rectEdit.left + 1,
743 rectEdit.top + 1,
744 ETO_OPAQUE | ETO_CLIPPED,
745 &rectEdit,
746 pText ? pText : L"" , size, NULL );
748 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
749 DrawFocusRect( hdc, &rectEdit );
752 if( hPrevFont )
753 SelectObject(hdc, hPrevFont );
755 if( hPrevBrush )
756 SelectObject( hdc, hPrevBrush );
758 if (!hdc_paint)
759 NtUserReleaseDC( lphc->self, hdc );
761 HeapFree( GetProcessHeap(), 0, pText );
764 /***********************************************************************
765 * CBPaintBorder
767 static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc)
769 RECT clientRect;
771 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
773 GetClientRect(lphc->self, &clientRect);
775 else
777 clientRect = lphc->textRect;
779 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
780 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
783 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
786 /***********************************************************************
787 * COMBO_Paint
789 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
791 PAINTSTRUCT ps;
792 HDC hDC;
794 hDC = hParamDC ? hParamDC : NtUserBeginPaint( lphc->self, &ps );
796 TRACE("hdc=%p\n", hDC);
798 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
800 HBRUSH hPrevBrush, hBkgBrush;
803 * Retrieve the background brush and select it in the
804 * DC.
806 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
808 hPrevBrush = SelectObject( hDC, hBkgBrush );
809 if (!(lphc->wState & CBF_EDIT))
810 FillRect(hDC, &lphc->textRect, hBkgBrush);
813 * In non 3.1 look, there is a sunken border on the combobox
815 CBPaintBorder(lphc, hDC);
816 CBPaintButton(lphc, hDC);
818 /* paint the edit control padding area */
819 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
821 RECT rPadEdit = lphc->textRect;
823 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
825 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
828 if( !(lphc->wState & CBF_EDIT) )
829 CBPaintText( lphc, hDC );
831 if( hPrevBrush )
832 SelectObject( hDC, hPrevBrush );
835 if (!hParamDC)
836 NtUserEndPaint( lphc->self, &ps );
838 return 0;
841 /***********************************************************************
842 * CBUpdateLBox
844 * Select listbox entry according to the contents of the edit control.
846 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
848 INT length, idx;
849 LPWSTR pText = NULL;
851 idx = LB_ERR;
852 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
854 if( length > 0 )
855 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
857 TRACE("\t edit text length %i\n", length );
859 if( pText )
861 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
862 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
863 HeapFree( GetProcessHeap(), 0, pText );
866 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
868 /* probably superfluous but Windows sends this too */
869 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
870 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
872 return idx;
875 /***********************************************************************
876 * CBUpdateEdit
878 * Copy a listbox entry to the edit control.
880 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
882 INT length;
883 LPWSTR pText = NULL;
885 TRACE("\t %i\n", index );
887 if( index >= 0 ) /* got an entry */
889 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
890 if( length != LB_ERR)
892 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
894 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
899 if( CB_HASSTRINGS(lphc) )
901 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
902 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)L"");
903 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
906 if( lphc->wState & CBF_FOCUSED )
907 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
909 HeapFree( GetProcessHeap(), 0, pText );
912 /***********************************************************************
913 * CBDropDown
915 * Show listbox popup.
917 static void CBDropDown( LPHEADCOMBO lphc )
919 HMONITOR monitor;
920 MONITORINFO mon_info;
921 RECT rect,r;
922 int nItems;
923 int nDroppedHeight;
925 TRACE("[%p]: drop down\n", lphc->self);
927 CB_NOTIFY( lphc, CBN_DROPDOWN );
929 /* set selection */
931 lphc->wState |= CBF_DROPPED;
932 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
934 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
936 /* Update edit only if item is in the list */
937 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
938 CBUpdateEdit( lphc, lphc->droppedIndex );
940 else
942 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
944 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
945 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
946 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
949 /* now set popup position */
950 GetWindowRect( lphc->self, &rect );
953 * If it's a dropdown, the listbox is offset
955 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
956 rect.left += COMBO_EDITBUTTONSPACE();
958 /* if the dropped height is greater than the total height of the dropped
959 items list, then force the drop down list height to be the total height
960 of the items in the dropped list */
962 /* And Remove any extra space (Best Fit) */
963 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
964 /* if listbox length has been set directly by its handle */
965 GetWindowRect(lphc->hWndLBox, &r);
966 if (nDroppedHeight < r.bottom - r.top)
967 nDroppedHeight = r.bottom - r.top;
968 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
970 if (nItems > 0)
972 int nHeight;
973 int nIHeight;
975 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
977 nHeight = nIHeight*nItems;
979 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
980 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
983 r.left = rect.left;
984 r.top = rect.bottom;
985 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
986 r.bottom = r.top + nDroppedHeight;
988 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
989 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
990 mon_info.cbSize = sizeof(mon_info);
991 GetMonitorInfoW( monitor, &mon_info );
993 if (r.bottom > mon_info.rcWork.bottom)
995 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
996 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
999 NtUserSetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1000 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1003 if( !(lphc->wState & CBF_NOREDRAW) )
1004 NtUserRedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1006 EnableWindow( lphc->hWndLBox, TRUE );
1007 if (GetCapture() != lphc->self)
1008 NtUserSetCapture(lphc->hWndLBox);
1011 /***********************************************************************
1012 * CBRollUp
1014 * Hide listbox popup.
1016 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1018 HWND hWnd = lphc->self;
1020 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1021 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1023 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1025 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1028 if( lphc->wState & CBF_DROPPED )
1030 RECT rect;
1032 lphc->wState &= ~CBF_DROPPED;
1033 NtUserShowWindow( lphc->hWndLBox, SW_HIDE );
1035 if(GetCapture() == lphc->hWndLBox)
1037 ReleaseCapture();
1040 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1042 rect = lphc->buttonRect;
1044 else
1046 if( bButton )
1048 UnionRect( &rect,
1049 &lphc->buttonRect,
1050 &lphc->textRect);
1052 else
1053 rect = lphc->textRect;
1055 bButton = TRUE;
1058 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1059 NtUserRedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1060 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1061 CB_NOTIFY( lphc, CBN_CLOSEUP );
1066 /***********************************************************************
1067 * COMBO_FlipListbox
1069 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1071 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1073 if( lphc->wState & CBF_DROPPED )
1075 CBRollUp( lphc, ok, bRedrawButton );
1076 return FALSE;
1079 CBDropDown( lphc );
1080 return TRUE;
1083 /***********************************************************************
1084 * CBRepaintButton
1086 static void CBRepaintButton( LPHEADCOMBO lphc )
1088 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1089 UpdateWindow(lphc->self);
1092 /***********************************************************************
1093 * COMBO_SetFocus
1095 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1097 if( !(lphc->wState & CBF_FOCUSED) )
1099 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1100 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1102 /* This is wrong. Message sequences seem to indicate that this
1103 is set *after* the notify. */
1104 /* lphc->wState |= CBF_FOCUSED; */
1106 if( !(lphc->wState & CBF_EDIT) )
1107 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1109 CB_NOTIFY( lphc, CBN_SETFOCUS );
1110 lphc->wState |= CBF_FOCUSED;
1114 /***********************************************************************
1115 * COMBO_KillFocus
1117 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1119 HWND hWnd = lphc->self;
1121 if( lphc->wState & CBF_FOCUSED )
1123 CBRollUp( lphc, FALSE, TRUE );
1124 if( IsWindow( hWnd ) )
1126 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1127 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1129 lphc->wState &= ~CBF_FOCUSED;
1131 /* redraw text */
1132 if( !(lphc->wState & CBF_EDIT) )
1133 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1135 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1140 /***********************************************************************
1141 * COMBO_Command
1143 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1145 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1147 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1149 switch( HIWORD(wParam) >> 8 )
1151 case (EN_SETFOCUS >> 8):
1153 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1155 COMBO_SetFocus( lphc );
1156 break;
1158 case (EN_KILLFOCUS >> 8):
1160 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1162 /* NOTE: it seems that Windows' edit control sends an
1163 * undocumented message WM_USER + 0x1B instead of this
1164 * notification (only when it happens to be a part of
1165 * the combo). ?? - AK.
1168 COMBO_KillFocus( lphc );
1169 break;
1172 case (EN_CHANGE >> 8):
1174 * In some circumstances (when the selection of the combobox
1175 * is changed for example) we don't want the EN_CHANGE notification
1176 * to be forwarded to the parent of the combobox. This code
1177 * checks a flag that is set in these occasions and ignores the
1178 * notification.
1180 if (lphc->wState & CBF_NOLBSELECT)
1182 lphc->wState &= ~CBF_NOLBSELECT;
1184 else
1186 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1189 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1190 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1191 break;
1193 case (EN_UPDATE >> 8):
1194 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1195 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1196 break;
1198 case (EN_ERRSPACE >> 8):
1199 CB_NOTIFY( lphc, CBN_ERRSPACE );
1202 else if( lphc->hWndLBox == hWnd )
1204 switch( (short)HIWORD(wParam) )
1206 case LBN_ERRSPACE:
1207 CB_NOTIFY( lphc, CBN_ERRSPACE );
1208 break;
1210 case LBN_DBLCLK:
1211 CB_NOTIFY( lphc, CBN_DBLCLK );
1212 break;
1214 case LBN_SELCHANGE:
1215 case LBN_SELCANCEL:
1217 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1219 /* do not roll up if selection is being tracked
1220 * by arrow keys in the dropdown listbox */
1221 if (!(lphc->wState & CBF_NOROLLUP))
1223 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1225 else lphc->wState &= ~CBF_NOROLLUP;
1227 CB_NOTIFY( lphc, CBN_SELCHANGE );
1229 if( HIWORD(wParam) == LBN_SELCHANGE)
1231 if( lphc->wState & CBF_EDIT )
1232 lphc->wState |= CBF_NOLBSELECT;
1233 CBPaintText( lphc, NULL );
1235 break;
1237 case LBN_SETFOCUS:
1238 case LBN_KILLFOCUS:
1239 /* nothing to do here since ComboLBox always resets the focus to its
1240 * combo/edit counterpart */
1241 break;
1244 return 0;
1247 /***********************************************************************
1248 * COMBO_ItemOp
1250 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1252 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1254 HWND hWnd = lphc->self;
1255 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1257 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1259 switch( msg )
1261 case WM_DELETEITEM:
1263 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1264 lpIS->CtlType = ODT_COMBOBOX;
1265 lpIS->CtlID = id;
1266 lpIS->hwndItem = hWnd;
1267 break;
1269 case WM_DRAWITEM:
1271 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1272 lpIS->CtlType = ODT_COMBOBOX;
1273 lpIS->CtlID = id;
1274 lpIS->hwndItem = hWnd;
1275 break;
1277 case WM_COMPAREITEM:
1279 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1280 lpIS->CtlType = ODT_COMBOBOX;
1281 lpIS->CtlID = id;
1282 lpIS->hwndItem = hWnd;
1283 break;
1285 case WM_MEASUREITEM:
1287 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1288 lpIS->CtlType = ODT_COMBOBOX;
1289 lpIS->CtlID = id;
1290 break;
1293 return SendMessageW(lphc->owner, msg, id, lParam);
1297 /***********************************************************************
1298 * COMBO_GetTextW
1300 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1302 INT length;
1304 if( lphc->wState & CBF_EDIT )
1305 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1307 /* get it from the listbox */
1309 if (!count || !buf) return 0;
1310 if( lphc->hWndLBox )
1312 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1313 if (idx == LB_ERR) goto error;
1314 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1315 if (length == LB_ERR) goto error;
1317 /* 'length' is without the terminating character */
1318 if (length >= count)
1320 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1321 if (!lpBuffer) goto error;
1322 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1324 /* truncate if buffer is too short */
1325 if (length != LB_ERR)
1327 lstrcpynW( buf, lpBuffer, count );
1328 length = count;
1330 HeapFree( GetProcessHeap(), 0, lpBuffer );
1332 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1334 if (length == LB_ERR) return 0;
1335 return length;
1338 error: /* error - truncate string, return zero */
1339 buf[0] = 0;
1340 return 0;
1344 /***********************************************************************
1345 * COMBO_GetTextA
1347 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1348 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1350 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1352 INT length;
1354 if( lphc->wState & CBF_EDIT )
1355 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1357 /* get it from the listbox */
1359 if (!count || !buf) return 0;
1360 if( lphc->hWndLBox )
1362 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1363 if (idx == LB_ERR) goto error;
1364 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1365 if (length == LB_ERR) goto error;
1367 /* 'length' is without the terminating character */
1368 if (length >= count)
1370 LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1371 if (!lpBuffer) goto error;
1372 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1374 /* truncate if buffer is too short */
1375 if (length != LB_ERR)
1377 lstrcpynA( buf, lpBuffer, count );
1378 length = count;
1380 HeapFree( GetProcessHeap(), 0, lpBuffer );
1382 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1384 if (length == LB_ERR) return 0;
1385 return length;
1388 error: /* error - truncate string, return zero */
1389 buf[0] = 0;
1390 return 0;
1394 /***********************************************************************
1395 * CBResetPos
1397 * This function sets window positions according to the updated
1398 * component placement struct.
1400 static void CBResetPos(HEADCOMBO *combo, BOOL redraw)
1402 BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
1404 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1405 * sizing messages */
1406 if (combo->wState & CBF_EDIT)
1407 NtUserSetWindowPos( combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
1408 combo->textRect.right - combo->textRect.left,
1409 combo->textRect.bottom - combo->textRect.top,
1410 SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0) );
1412 NtUserSetWindowPos( combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
1413 combo->droppedRect.right - combo->droppedRect.left,
1414 combo->droppedRect.bottom - combo->droppedRect.top,
1415 SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0) );
1417 if (drop)
1419 if (combo->wState & CBF_DROPPED)
1421 combo->wState &= ~CBF_DROPPED;
1422 NtUserShowWindow( combo->hWndLBox, SW_HIDE );
1425 if (redraw && !(combo->wState & CBF_NOREDRAW))
1426 NtUserRedrawWindow( combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1430 /***********************************************************************
1431 * COMBO_Size
1433 static void COMBO_Size( HEADCOMBO *lphc )
1435 if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
1436 return;
1439 * Those controls are always the same height. So we have to make sure
1440 * they are not resized to another value.
1442 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1444 int newComboHeight, curComboHeight, curComboWidth;
1445 RECT rc;
1447 GetWindowRect(lphc->self, &rc);
1448 curComboHeight = rc.bottom - rc.top;
1449 curComboWidth = rc.right - rc.left;
1450 newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1453 * Resizing a combobox has another side effect, it resizes the dropped
1454 * rectangle as well. However, it does it only if the new height for the
1455 * combobox is more than the height it should have. In other words,
1456 * if the application resizing the combobox only had the intention to resize
1457 * the actual control, for example, to do the layout of a dialog that is
1458 * resized, the height of the dropdown is not changed.
1460 if( curComboHeight > newComboHeight )
1462 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%ld, oldDropTop=%ld\n",
1463 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1464 lphc->droppedRect.top);
1465 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1468 * Restore original height
1470 if (curComboHeight != newComboHeight)
1472 lphc->wState |= CBF_NORESIZE;
1473 NtUserSetWindowPos( lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1474 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW );
1475 lphc->wState &= ~CBF_NORESIZE;
1479 CBCalcPlacement(lphc);
1481 CBResetPos(lphc, FALSE);
1485 /***********************************************************************
1486 * COMBO_Font
1488 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1490 lphc->hFont = hFont;
1491 lphc->item_height = combo_get_text_height(lphc);
1494 * Propagate to owned windows.
1496 if( lphc->wState & CBF_EDIT )
1497 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1498 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1501 * Redo the layout of the control.
1503 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1505 CBCalcPlacement(lphc);
1507 CBResetPos(lphc, TRUE);
1509 else
1511 CBForceDummyResize(lphc);
1516 /***********************************************************************
1517 * COMBO_SetItemHeight
1519 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1521 LRESULT lRet = CB_ERR;
1523 if( index == -1 ) /* set text field height */
1525 if( height < 32768 )
1527 lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1530 * Redo the layout of the control.
1532 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1534 CBCalcPlacement(lphc);
1536 CBResetPos(lphc, TRUE);
1538 else
1540 CBForceDummyResize(lphc);
1543 lRet = height;
1546 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1547 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1548 return lRet;
1551 /***********************************************************************
1552 * COMBO_SelectString
1554 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1556 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText) :
1557 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1558 if( index >= 0 )
1560 if( lphc->wState & CBF_EDIT )
1561 CBUpdateEdit( lphc, index );
1562 else
1564 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1567 return (LRESULT)index;
1570 /***********************************************************************
1571 * COMBO_LButtonDown
1573 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1575 POINT pt;
1576 BOOL bButton;
1577 HWND hWnd = lphc->self;
1579 pt.x = (short)LOWORD(lParam);
1580 pt.y = (short)HIWORD(lParam);
1581 bButton = PtInRect(&lphc->buttonRect, pt);
1583 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1584 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1586 lphc->wState |= CBF_BUTTONDOWN;
1587 if( lphc->wState & CBF_DROPPED )
1589 /* got a click to cancel selection */
1591 lphc->wState &= ~CBF_BUTTONDOWN;
1592 CBRollUp( lphc, TRUE, FALSE );
1593 if( !IsWindow( hWnd ) ) return;
1595 if( lphc->wState & CBF_CAPTURE )
1597 lphc->wState &= ~CBF_CAPTURE;
1598 ReleaseCapture();
1601 else
1603 /* drop down the listbox and start tracking */
1605 lphc->wState |= CBF_CAPTURE;
1606 NtUserSetCapture( hWnd );
1607 CBDropDown( lphc );
1609 if( bButton ) CBRepaintButton( lphc );
1613 /***********************************************************************
1614 * COMBO_LButtonUp
1616 * Release capture and stop tracking if needed.
1618 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1620 if( lphc->wState & CBF_CAPTURE )
1622 lphc->wState &= ~CBF_CAPTURE;
1623 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1625 INT index = CBUpdateLBox( lphc, TRUE );
1626 /* Update edit only if item is in the list */
1627 if(index >= 0)
1629 lphc->wState |= CBF_NOLBSELECT;
1630 CBUpdateEdit( lphc, index );
1631 lphc->wState &= ~CBF_NOLBSELECT;
1634 ReleaseCapture();
1635 NtUserSetCapture(lphc->hWndLBox);
1638 if( lphc->wState & CBF_BUTTONDOWN )
1640 lphc->wState &= ~CBF_BUTTONDOWN;
1641 CBRepaintButton( lphc );
1645 /***********************************************************************
1646 * COMBO_MouseMove
1648 * Two things to do - track combo button and release capture when
1649 * pointer goes into the listbox.
1651 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1653 POINT pt;
1654 RECT lbRect;
1656 pt.x = (short)LOWORD(lParam);
1657 pt.y = (short)HIWORD(lParam);
1659 if( lphc->wState & CBF_BUTTONDOWN )
1661 BOOL bButton;
1663 bButton = PtInRect(&lphc->buttonRect, pt);
1665 if( !bButton )
1667 lphc->wState &= ~CBF_BUTTONDOWN;
1668 CBRepaintButton( lphc );
1672 GetClientRect( lphc->hWndLBox, &lbRect );
1673 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1674 if( PtInRect(&lbRect, pt) )
1676 lphc->wState &= ~CBF_CAPTURE;
1677 ReleaseCapture();
1678 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1680 /* hand over pointer tracking */
1681 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1685 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1687 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1688 return FALSE;
1690 pcbi->rcItem = lphc->textRect;
1691 pcbi->rcButton = lphc->buttonRect;
1692 pcbi->stateButton = 0;
1693 if (lphc->wState & CBF_BUTTONDOWN)
1694 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1695 if (IsRectEmpty(&lphc->buttonRect))
1696 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1697 pcbi->hwndCombo = lphc->self;
1698 pcbi->hwndItem = lphc->hWndEdit;
1699 pcbi->hwndList = lphc->hWndLBox;
1700 return TRUE;
1703 static char *strdupA(LPCSTR str)
1705 char *ret;
1706 DWORD len;
1708 if(!str) return NULL;
1710 len = strlen(str);
1711 ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1712 memcpy(ret, str, len + 1);
1713 return ret;
1716 /***********************************************************************
1717 * ComboWndProc_common
1719 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1721 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1723 TRACE("[%p]: msg %s wp %08Ix lp %08Ix\n",
1724 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1726 if (!IsWindow(hwnd)) return 0;
1728 if( lphc || message == WM_NCCREATE )
1729 switch(message)
1732 /* System messages */
1734 case WM_NCCREATE:
1736 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1737 ((LPCREATESTRUCTA)lParam)->style;
1738 return COMBO_NCCreate(hwnd, style);
1740 case WM_NCDESTROY:
1741 COMBO_NCDestroy(lphc);
1742 break;/* -> DefWindowProc */
1744 case WM_CREATE:
1746 HWND hwndParent;
1747 LONG style;
1748 if(unicode)
1750 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1751 style = ((LPCREATESTRUCTW)lParam)->style;
1753 else
1755 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1756 style = ((LPCREATESTRUCTA)lParam)->style;
1758 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1761 case WM_PRINTCLIENT:
1762 /* Fallthrough */
1763 case WM_PAINT:
1764 /* wParam may contain a valid HDC! */
1765 return COMBO_Paint(lphc, (HDC)wParam);
1767 case WM_ERASEBKGND:
1768 /* do all painting in WM_PAINT like Windows does */
1769 return 1;
1771 case WM_GETDLGCODE:
1773 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1774 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1776 int vk = (int)((LPMSG)lParam)->wParam;
1778 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1779 result |= DLGC_WANTMESSAGE;
1781 return result;
1783 case WM_SIZE:
1784 COMBO_Size( lphc );
1785 return TRUE;
1786 case WM_SETFONT:
1787 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1788 return TRUE;
1789 case WM_GETFONT:
1790 return (LRESULT)lphc->hFont;
1791 case WM_SETFOCUS:
1792 if( lphc->wState & CBF_EDIT ) {
1793 NtUserSetFocus( lphc->hWndEdit );
1794 /* The first time focus is received, select all the text */
1795 if( !(lphc->wState & CBF_BEENFOCUSED) ) {
1796 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1797 lphc->wState |= CBF_BEENFOCUSED;
1800 else
1801 COMBO_SetFocus( lphc );
1802 return TRUE;
1803 case WM_KILLFOCUS:
1805 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1806 if( !hwndFocus ||
1807 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1808 COMBO_KillFocus( lphc );
1809 return TRUE;
1811 case WM_COMMAND:
1812 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1813 case WM_GETTEXT:
1814 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1815 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1816 case WM_SETTEXT:
1817 case WM_GETTEXTLENGTH:
1818 case WM_CLEAR:
1819 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1821 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1822 if (j == -1) return 0;
1823 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1824 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1826 else if( lphc->wState & CBF_EDIT )
1828 LRESULT ret;
1829 lphc->wState |= CBF_NOEDITNOTIFY;
1830 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1831 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1832 lphc->wState &= ~CBF_NOEDITNOTIFY;
1833 return ret;
1835 else return CB_ERR;
1836 case WM_CUT:
1837 case WM_PASTE:
1838 case WM_COPY:
1839 if( lphc->wState & CBF_EDIT )
1841 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1842 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1844 else return CB_ERR;
1846 case WM_DRAWITEM:
1847 case WM_DELETEITEM:
1848 case WM_COMPAREITEM:
1849 case WM_MEASUREITEM:
1850 return COMBO_ItemOp(lphc, message, lParam);
1851 case WM_ENABLE:
1852 if( lphc->wState & CBF_EDIT )
1853 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1854 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1856 /* Force the control to repaint when the enabled state changes. */
1857 InvalidateRect(lphc->self, NULL, TRUE);
1858 return TRUE;
1859 case WM_SETREDRAW:
1860 if( wParam )
1861 lphc->wState &= ~CBF_NOREDRAW;
1862 else
1863 lphc->wState |= CBF_NOREDRAW;
1865 if( lphc->wState & CBF_EDIT )
1866 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1867 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1868 return 0;
1869 case WM_SYSKEYDOWN:
1870 if( KEYDATA_ALT & HIWORD(lParam) )
1871 if( wParam == VK_UP || wParam == VK_DOWN )
1872 COMBO_FlipListbox( lphc, FALSE, FALSE );
1873 return 0;
1875 case WM_KEYDOWN:
1876 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1877 (lphc->wState & CBF_DROPPED))
1879 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1880 return TRUE;
1882 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1884 COMBO_FlipListbox( lphc, FALSE, FALSE );
1885 return TRUE;
1887 /* fall through */
1888 case WM_CHAR:
1889 case WM_IME_CHAR:
1891 HWND hwndTarget;
1893 if( lphc->wState & CBF_EDIT )
1894 hwndTarget = lphc->hWndEdit;
1895 else
1896 hwndTarget = lphc->hWndLBox;
1898 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
1899 SendMessageA(hwndTarget, message, wParam, lParam);
1901 case WM_LBUTTONDOWN:
1902 if (!(lphc->wState & CBF_FOCUSED)) NtUserSetFocus( lphc->self );
1903 if (lphc->wState & CBF_FOCUSED) COMBO_LButtonDown( lphc, lParam );
1904 return TRUE;
1905 case WM_LBUTTONUP:
1906 COMBO_LButtonUp( lphc );
1907 return TRUE;
1908 case WM_MOUSEMOVE:
1909 if( lphc->wState & CBF_CAPTURE )
1910 COMBO_MouseMove( lphc, wParam, lParam );
1911 return TRUE;
1913 case WM_MOUSEWHEEL:
1914 if (wParam & (MK_SHIFT | MK_CONTROL))
1915 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
1916 DefWindowProcA(hwnd, message, wParam, lParam);
1918 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1919 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1920 return TRUE;
1922 case WM_CTLCOLOR:
1923 case WM_CTLCOLORMSGBOX:
1924 case WM_CTLCOLOREDIT:
1925 case WM_CTLCOLORLISTBOX:
1926 case WM_CTLCOLORBTN:
1927 case WM_CTLCOLORDLG:
1928 case WM_CTLCOLORSCROLLBAR:
1929 case WM_CTLCOLORSTATIC:
1930 if (lphc->owner)
1931 return SendMessageW(lphc->owner, message, wParam, lParam);
1932 break;
1934 /* Combo messages */
1936 case CB_ADDSTRING:
1937 if( unicode )
1939 if( lphc->dwStyle & CBS_LOWERCASE )
1940 CharLowerW((LPWSTR)lParam);
1941 else if( lphc->dwStyle & CBS_UPPERCASE )
1942 CharUpperW((LPWSTR)lParam);
1943 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1945 else /* unlike the unicode version, the ansi version does not overwrite
1946 the string if converting case */
1948 char *string = NULL;
1949 LRESULT ret;
1950 if( lphc->dwStyle & CBS_LOWERCASE )
1952 string = strdupA((LPSTR)lParam);
1953 CharLowerA(string);
1956 else if( lphc->dwStyle & CBS_UPPERCASE )
1958 string = strdupA((LPSTR)lParam);
1959 CharUpperA(string);
1962 ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
1963 HeapFree(GetProcessHeap(), 0, string);
1964 return ret;
1966 case CB_INSERTSTRING:
1967 if( unicode )
1969 if( lphc->dwStyle & CBS_LOWERCASE )
1970 CharLowerW((LPWSTR)lParam);
1971 else if( lphc->dwStyle & CBS_UPPERCASE )
1972 CharUpperW((LPWSTR)lParam);
1973 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1975 else
1977 if( lphc->dwStyle & CBS_LOWERCASE )
1978 CharLowerA((LPSTR)lParam);
1979 else if( lphc->dwStyle & CBS_UPPERCASE )
1980 CharUpperA((LPSTR)lParam);
1982 return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1984 case CB_DELETESTRING:
1985 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
1986 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1987 case CB_SELECTSTRING:
1988 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
1989 case CB_FINDSTRING:
1990 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
1991 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1992 case CB_FINDSTRINGEXACT:
1993 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
1994 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1995 case CB_SETITEMHEIGHT:
1996 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1997 case CB_GETITEMHEIGHT:
1998 if( (INT)wParam >= 0 ) /* listbox item */
1999 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2000 return CBGetTextAreaHeight(lphc, FALSE);
2001 case CB_RESETCONTENT:
2002 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2003 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2004 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)L"");
2005 else
2006 InvalidateRect(lphc->self, NULL, TRUE);
2007 return TRUE;
2008 case CB_INITSTORAGE:
2009 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2010 case CB_GETHORIZONTALEXTENT:
2011 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2012 case CB_SETHORIZONTALEXTENT:
2013 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2014 case CB_GETTOPINDEX:
2015 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2016 case CB_GETLOCALE:
2017 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2018 case CB_SETLOCALE:
2019 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2020 case CB_SETDROPPEDWIDTH:
2021 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
2022 (INT)wParam >= 32768 )
2023 return CB_ERR;
2024 /* new value must be higher than combobox width */
2025 if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2026 lphc->droppedWidth = wParam;
2027 else if(wParam)
2028 lphc->droppedWidth = 0;
2030 /* recalculate the combobox area */
2031 CBCalcPlacement(lphc);
2033 /* fall through */
2034 case CB_GETDROPPEDWIDTH:
2035 if( lphc->droppedWidth )
2036 return lphc->droppedWidth;
2037 return lphc->droppedRect.right - lphc->droppedRect.left;
2038 case CB_GETDROPPEDCONTROLRECT:
2039 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2040 return CB_OKAY;
2041 case CB_GETDROPPEDSTATE:
2042 return (lphc->wState & CBF_DROPPED) != 0;
2043 case CB_DIR:
2044 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2045 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2047 case CB_SHOWDROPDOWN:
2048 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2050 if( wParam )
2052 if( !(lphc->wState & CBF_DROPPED) )
2053 CBDropDown( lphc );
2055 else
2056 if( lphc->wState & CBF_DROPPED )
2057 CBRollUp( lphc, FALSE, TRUE );
2059 return TRUE;
2060 case CB_GETCOUNT:
2061 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2062 case CB_GETCURSEL:
2063 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2064 case CB_SETCURSEL:
2065 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2066 if( lParam >= 0 )
2067 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2069 /* no LBN_SELCHANGE in this case, update manually */
2070 CBPaintText( lphc, NULL );
2071 lphc->wState &= ~CBF_SELCHANGE;
2072 return lParam;
2073 case CB_GETLBTEXT:
2074 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2075 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2076 case CB_GETLBTEXTLEN:
2077 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2078 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2079 case CB_GETITEMDATA:
2080 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2081 case CB_SETITEMDATA:
2082 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2083 case CB_GETEDITSEL:
2084 /* Edit checks passed parameters itself */
2085 if( lphc->wState & CBF_EDIT )
2086 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2087 return CB_ERR;
2088 case CB_SETEDITSEL:
2089 if( lphc->wState & CBF_EDIT )
2090 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2091 (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2092 return CB_ERR;
2093 case CB_SETEXTENDEDUI:
2094 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2095 return CB_ERR;
2096 if( wParam )
2097 lphc->wState |= CBF_EUI;
2098 else lphc->wState &= ~CBF_EUI;
2099 return CB_OKAY;
2100 case CB_GETEXTENDEDUI:
2101 return (lphc->wState & CBF_EUI) != 0;
2102 case CB_GETCOMBOBOXINFO:
2103 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2104 case CB_LIMITTEXT:
2105 if( lphc->wState & CBF_EDIT )
2106 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2107 return TRUE;
2108 default:
2109 if (message >= WM_USER)
2110 WARN("unknown msg WM_USER+%04x wp=%04Ix lp=%08Ix\n",
2111 message - WM_USER, wParam, lParam );
2112 break;
2114 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2115 DefWindowProcA(hwnd, message, wParam, lParam);
2118 /*************************************************************************
2119 * GetComboBoxInfo (USER32.@)
2121 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2122 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2124 TRACE("(%p, %p)\n", hwndCombo, pcbi);
2125 return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);