d3d10: Return the read value from read_dword().
[wine.git] / dlls / user32 / combo.c
blobaeb7fa08e5f5a77df9f73159f772b754d65f834f
1 /*
2 * Combo controls
4 * Copyright 1997 Alex Korobka
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * TODO:
21 * - CB_SETTOPINDEX
24 #include <stdarg.h>
25 #include <string.h>
27 #define OEMRESOURCE
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "user_private.h"
34 #include "win.h"
35 #include "controls.h"
36 #include "winternl.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(combo);
41 /* bits in the dwKeyData */
42 #define KEYDATA_ALT 0x2000
43 #define KEYDATA_PREVSTATE 0x4000
46 * Additional combo box definitions
49 #define CB_NOTIFY( lphc, code ) \
50 (SendMessageW((lphc)->owner, WM_COMMAND, \
51 MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self))
53 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
54 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
55 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
56 #define CB_HWND( lphc ) ((lphc)->self)
57 #define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST))
59 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
62 * Drawing globals
64 static HBITMAP hComboBmp = 0;
65 static UINT CBitHeight, CBitWidth;
68 * Look and feel dependent "constants"
71 #define COMBO_YBORDERGAP 5
72 #define COMBO_XBORDERSIZE() 2
73 #define COMBO_YBORDERSIZE() 2
74 #define COMBO_EDITBUTTONSPACE() 0
75 #define EDIT_CONTROL_PADDING() 1
77 static void CBCalcPlacement(HEADCOMBO *combo);
78 static void CBResetPos(HEADCOMBO *combo, BOOL redraw);
80 /*********************************************************************
81 * combo class descriptor
83 const struct builtin_class_descr COMBO_builtin_class =
85 L"ComboBox", /* name */
86 CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */
87 WINPROC_COMBO, /* proc */
88 sizeof(HEADCOMBO *), /* extra */
89 IDC_ARROW, /* cursor */
90 0 /* brush */
94 /***********************************************************************
95 * COMBO_Init
97 * Load combo button bitmap.
99 static BOOL COMBO_Init(void)
101 HDC hDC;
103 if( hComboBmp ) return TRUE;
104 if( (hDC = CreateCompatibleDC(0)) )
106 BOOL bRet = FALSE;
107 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
109 BITMAP bm;
110 HBITMAP hPrevB;
111 RECT r;
113 GetObjectW( hComboBmp, sizeof(bm), &bm );
114 CBitHeight = bm.bmHeight;
115 CBitWidth = bm.bmWidth;
117 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
119 hPrevB = SelectObject( hDC, hComboBmp);
120 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
121 InvertRect( hDC, &r );
122 SelectObject( hDC, hPrevB );
123 bRet = TRUE;
125 DeleteDC( hDC );
126 return bRet;
128 return FALSE;
131 /***********************************************************************
132 * COMBO_NCCreate
134 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
136 LPHEADCOMBO lphc;
138 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
140 lphc->self = hwnd;
141 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
143 /* some braindead apps do try to use scrollbar/border flags */
145 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
146 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
149 * We also have to remove the client edge style to make sure
150 * we don't end-up with a non client area.
152 SetWindowLongW( hwnd, GWL_EXSTYLE,
153 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
155 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
156 lphc->dwStyle |= CBS_HASSTRINGS;
157 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
158 lphc->wState |= CBF_NOTIFY;
160 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
161 return TRUE;
163 return FALSE;
166 /***********************************************************************
167 * COMBO_NCDestroy
169 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
172 if( lphc )
174 TRACE("[%p]: freeing storage\n", lphc->self);
176 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
177 DestroyWindow( lphc->hWndLBox );
179 SetWindowLongPtrW( lphc->self, 0, 0 );
180 HeapFree( GetProcessHeap(), 0, lphc );
182 return 0;
185 static INT combo_get_text_height(const HEADCOMBO *combo)
187 HDC hdc = GetDC(combo->self);
188 HFONT prev_font = 0;
189 TEXTMETRICW tm;
191 if (combo->hFont)
192 prev_font = SelectObject(hdc, combo->hFont);
194 GetTextMetricsW(hdc, &tm);
196 if (prev_font)
197 SelectObject(hdc, prev_font);
199 ReleaseDC(combo->self, hdc);
201 return tm.tmHeight + 4;
204 /***********************************************************************
205 * CBGetTextAreaHeight
207 * This method will calculate the height of the text area of the
208 * combobox.
209 * The height of the text area is set in two ways.
210 * It can be set explicitly through a combobox message or through a
211 * WM_MEASUREITEM callback.
212 * If this is not the case, the height is set to font height + 4px
213 * This height was determined through experimentation.
214 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
216 static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
218 INT item_height, text_height;
220 if (clip_item_height && !CB_OWNERDRAWN(lphc))
222 text_height = combo_get_text_height(lphc);
223 if (lphc->item_height < text_height)
224 lphc->item_height = text_height;
227 item_height = lphc->item_height;
230 * Check the ownerdraw case if we haven't asked the parent the size
231 * of the item yet.
233 if ( CB_OWNERDRAWN(lphc) &&
234 (lphc->wState & CBF_MEASUREITEM) )
236 MEASUREITEMSTRUCT measureItem;
237 RECT clientRect;
238 INT originalItemHeight = item_height;
239 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
242 * We use the client rect for the width of the item.
244 GetClientRect(lphc->self, &clientRect);
246 lphc->wState &= ~CBF_MEASUREITEM;
249 * Send a first one to measure the size of the text area
251 measureItem.CtlType = ODT_COMBOBOX;
252 measureItem.CtlID = id;
253 measureItem.itemID = -1;
254 measureItem.itemWidth = clientRect.right;
255 measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */
256 measureItem.itemData = 0;
257 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
258 item_height = 6 + measureItem.itemHeight;
261 * Send a second one in the case of a fixed ownerdraw list to calculate the
262 * size of the list items. (we basically do this on behalf of the listbox)
264 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
266 measureItem.CtlType = ODT_COMBOBOX;
267 measureItem.CtlID = id;
268 measureItem.itemID = 0;
269 measureItem.itemWidth = clientRect.right;
270 measureItem.itemHeight = originalItemHeight;
271 measureItem.itemData = 0;
272 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
273 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
277 * Keep the size for the next time
279 lphc->item_height = item_height;
282 return item_height;
285 /***********************************************************************
286 * CBForceDummyResize
288 * The dummy resize is used for listboxes that have a popup to trigger
289 * a re-arranging of the contents of the combobox and the recalculation
290 * of the size of the "real" control window.
292 static void CBForceDummyResize(LPHEADCOMBO lphc)
294 RECT windowRect;
295 int newComboHeight;
297 newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE();
299 GetWindowRect(lphc->self, &windowRect);
302 * We have to be careful, resizing a combobox also has the meaning that the
303 * dropped rect will be resized. In this case, we want to trigger a resize
304 * to recalculate layout but we don't want to change the dropped rectangle
305 * So, we pass the height of text area of control as the height.
306 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
307 * message.
309 lphc->wState |= CBF_NORESIZE;
310 SetWindowPos( lphc->self,
311 NULL,
312 0, 0,
313 windowRect.right - windowRect.left,
314 newComboHeight,
315 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
316 lphc->wState &= ~CBF_NORESIZE;
318 CBCalcPlacement(lphc);
319 CBResetPos(lphc, FALSE);
322 /***********************************************************************
323 * CBCalcPlacement
325 * Set up component coordinates given valid lphc->RectCombo.
327 static void CBCalcPlacement(HEADCOMBO *combo)
329 /* Start with the client rectangle. */
330 GetClientRect(combo->self, &combo->textRect);
332 /* Remove the borders */
333 InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
335 /* Chop off the bottom part to fit with the height of the text area. */
336 combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
338 /* The button starts the same vertical position as the text area. */
339 combo->buttonRect = combo->textRect;
341 /* If the combobox is "simple" there is no button. */
342 if (CB_GETTYPE(combo) == CBS_SIMPLE)
343 combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0;
344 else
347 * Let's assume the combobox button is the same width as the
348 * scrollbar button.
349 * size the button horizontally and cut-off the text area.
351 combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
352 combo->textRect.right = combo->buttonRect.left;
355 /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
356 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
357 combo->textRect.right -= COMBO_EDITBUTTONSPACE();
359 /* If we have an edit control, we space it away from the borders slightly. */
360 if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
361 InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
363 /* Adjust the size of the listbox popup. */
364 if (CB_GETTYPE(combo) == CBS_SIMPLE)
366 GetClientRect(combo->self, &combo->droppedRect);
367 combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
369 else
371 /* Make sure the dropped width is as large as the combobox itself. */
372 if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
374 combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
376 /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
377 with the right side of the combobox. */
378 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
379 combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
381 else
382 combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
385 /* Disallow negative window width */
386 if (combo->textRect.right < combo->textRect.left)
387 combo->textRect.right = combo->textRect.left;
389 TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
390 wine_dbgstr_rect(&combo->droppedRect));
393 /***********************************************************************
394 * CBGetDroppedControlRect
396 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
398 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
399 of the combo box and the lower right corner of the listbox */
401 GetWindowRect(lphc->self, lpRect);
403 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
404 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
408 /***********************************************************************
409 * COMBO_Create
411 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style,
412 BOOL unicode )
414 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
415 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
417 lphc->owner = hwndParent;
419 lphc->droppedWidth = 0;
421 lphc->item_height = combo_get_text_height(lphc);
424 * The first time we go through, we want to measure the ownerdraw item
426 lphc->wState |= CBF_MEASUREITEM;
428 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
430 if( lphc->owner || !(style & WS_VISIBLE) )
432 UINT lbeStyle = 0;
433 UINT lbeExStyle = 0;
436 * Initialize the dropped rect to the size of the client area of the
437 * control and then, force all the areas of the combobox to be
438 * recalculated.
440 GetClientRect( hwnd, &lphc->droppedRect );
441 CBCalcPlacement(lphc);
444 * Adjust the position of the popup listbox if it's necessary
446 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
448 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
451 * If it's a dropdown, the listbox is offset
453 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
454 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
456 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
457 lphc->droppedRect.bottom = lphc->droppedRect.top;
458 if (lphc->droppedRect.right < lphc->droppedRect.left)
459 lphc->droppedRect.right = lphc->droppedRect.left;
460 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
463 /* create listbox popup */
465 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
466 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
468 if( lphc->dwStyle & CBS_SORT )
469 lbeStyle |= LBS_SORT;
470 if( lphc->dwStyle & CBS_HASSTRINGS )
471 lbeStyle |= LBS_HASSTRINGS;
472 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
473 lbeStyle |= LBS_NOINTEGRALHEIGHT;
474 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
475 lbeStyle |= LBS_DISABLENOSCROLL;
477 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
479 lbeStyle |= WS_VISIBLE;
482 * In win 95 look n feel, the listbox in the simple combobox has
483 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
485 lbeStyle &= ~WS_BORDER;
486 lbeExStyle |= WS_EX_CLIENTEDGE;
488 else
490 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
493 if (unicode)
494 lphc->hWndLBox = CreateWindowExW(lbeExStyle, L"ComboLBox", NULL, lbeStyle,
495 lphc->droppedRect.left,
496 lphc->droppedRect.top,
497 lphc->droppedRect.right - lphc->droppedRect.left,
498 lphc->droppedRect.bottom - lphc->droppedRect.top,
499 hwnd, (HMENU)ID_CB_LISTBOX,
500 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
501 else
502 lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
503 lphc->droppedRect.left,
504 lphc->droppedRect.top,
505 lphc->droppedRect.right - lphc->droppedRect.left,
506 lphc->droppedRect.bottom - lphc->droppedRect.top,
507 hwnd, (HMENU)ID_CB_LISTBOX,
508 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
510 if( lphc->hWndLBox )
512 BOOL bEdit = TRUE;
513 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
515 if( lphc->wState & CBF_EDIT )
517 if( lphc->dwStyle & CBS_OEMCONVERT )
518 lbeStyle |= ES_OEMCONVERT;
519 if( lphc->dwStyle & CBS_AUTOHSCROLL )
520 lbeStyle |= ES_AUTOHSCROLL;
521 if( lphc->dwStyle & CBS_LOWERCASE )
522 lbeStyle |= ES_LOWERCASE;
523 else if( lphc->dwStyle & CBS_UPPERCASE )
524 lbeStyle |= ES_UPPERCASE;
526 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
528 if (unicode)
529 lphc->hWndEdit = CreateWindowExW(0, L"Edit", NULL, lbeStyle,
530 lphc->textRect.left, lphc->textRect.top,
531 lphc->textRect.right - lphc->textRect.left,
532 lphc->textRect.bottom - lphc->textRect.top,
533 hwnd, (HMENU)ID_CB_EDIT,
534 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
535 else
536 lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
537 lphc->textRect.left, lphc->textRect.top,
538 lphc->textRect.right - lphc->textRect.left,
539 lphc->textRect.bottom - lphc->textRect.top,
540 hwnd, (HMENU)ID_CB_EDIT,
541 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
543 if( !lphc->hWndEdit )
544 bEdit = FALSE;
547 if( bEdit )
549 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
551 /* Now do the trick with parent */
552 SetParent(lphc->hWndLBox, HWND_DESKTOP);
554 * If the combo is a dropdown, we must resize the control
555 * to fit only the text area and button. To do this,
556 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
557 * will take care of setting the height for us.
559 CBForceDummyResize(lphc);
562 TRACE("init done\n");
563 return 0;
565 ERR("edit control failure.\n");
566 } else ERR("listbox failure.\n");
567 } else ERR("no owner for visible combo.\n");
569 /* CreateWindow() will send WM_NCDESTROY to cleanup */
571 return -1;
574 /***********************************************************************
575 * CBPaintButton
577 * Paint combo button (normal, pressed, and disabled states).
579 static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
581 UINT buttonState = DFCS_SCROLLCOMBOBOX;
583 if (IsRectEmpty(&lphc->buttonRect))
584 return;
586 if( lphc->wState & CBF_NOREDRAW )
587 return;
590 if (lphc->wState & CBF_BUTTONDOWN)
591 buttonState |= DFCS_PUSHED;
593 if (CB_DISABLED(lphc))
594 buttonState |= DFCS_INACTIVE;
596 DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
599 /***********************************************************************
600 * COMBO_PrepareColors
602 * This method will sent the appropriate WM_CTLCOLOR message to
603 * prepare and setup the colors for the combo's DC.
605 * It also returns the brush to use for the background.
607 static HBRUSH COMBO_PrepareColors(
608 LPHEADCOMBO lphc,
609 HDC hDC)
611 HBRUSH hBkgBrush;
614 * Get the background brush for this control.
616 if (CB_DISABLED(lphc))
618 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
619 (WPARAM)hDC, (LPARAM)lphc->self );
622 * We have to change the text color since WM_CTLCOLORSTATIC will
623 * set it to the "enabled" color. This is the same behavior as the
624 * edit control
626 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
628 else
630 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
631 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
632 (WPARAM)hDC, (LPARAM)lphc->self );
636 * Catch errors.
638 if( !hBkgBrush )
639 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
641 return hBkgBrush;
644 /***********************************************************************
645 * CBPaintText
647 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
649 static void CBPaintText(
650 LPHEADCOMBO lphc,
651 HDC hdc_paint)
653 RECT rectEdit = lphc->textRect;
654 INT id, size = 0;
655 LPWSTR pText = NULL;
657 TRACE("\n");
659 /* follow Windows combobox that sends a bunch of text
660 * inquiries to its listbox while processing WM_PAINT. */
662 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
664 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
665 if (size == LB_ERR)
666 FIXME("LB_ERR probably not handled yet\n");
667 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
669 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
670 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
671 pText[size] = '\0'; /* just in case */
672 } else return;
675 if( lphc->wState & CBF_EDIT )
677 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : L"" );
678 if( lphc->wState & CBF_FOCUSED )
679 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
681 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
683 /* paint text field ourselves */
684 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
685 UINT itemState = ODS_COMBOBOXEDIT;
686 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
687 HBRUSH hPrevBrush, hBkgBrush;
690 * Give ourselves some space.
692 InflateRect( &rectEdit, -1, -1 );
694 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
695 hPrevBrush = SelectObject( hdc, hBkgBrush );
696 FillRect( hdc, &rectEdit, hBkgBrush );
698 if( CB_OWNERDRAWN(lphc) )
700 DRAWITEMSTRUCT dis;
701 HRGN clipRegion;
702 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
704 /* setup state for DRAWITEM message. Owner will highlight */
705 if ( (lphc->wState & CBF_FOCUSED) &&
706 !(lphc->wState & CBF_DROPPED) )
707 itemState |= ODS_SELECTED | ODS_FOCUS;
709 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
711 dis.CtlType = ODT_COMBOBOX;
712 dis.CtlID = ctlid;
713 dis.hwndItem = lphc->self;
714 dis.itemAction = ODA_DRAWENTIRE;
715 dis.itemID = id;
716 dis.itemState = itemState;
717 dis.hDC = hdc;
718 dis.rcItem = rectEdit;
719 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
722 * Clip the DC and have the parent draw the item.
724 clipRegion = set_control_clipping( hdc, &rectEdit );
726 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
728 SelectClipRgn( hdc, clipRegion );
729 if (clipRegion) DeleteObject( clipRegion );
731 else
733 if ( (lphc->wState & CBF_FOCUSED) &&
734 !(lphc->wState & CBF_DROPPED) ) {
736 /* highlight */
737 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
738 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
739 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
742 ExtTextOutW( hdc,
743 rectEdit.left + 1,
744 rectEdit.top + 1,
745 ETO_OPAQUE | ETO_CLIPPED,
746 &rectEdit,
747 pText ? pText : L"" , size, NULL );
749 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
750 DrawFocusRect( hdc, &rectEdit );
753 if( hPrevFont )
754 SelectObject(hdc, hPrevFont );
756 if( hPrevBrush )
757 SelectObject( hdc, hPrevBrush );
759 if( !hdc_paint )
760 ReleaseDC( lphc->self, hdc );
762 HeapFree( GetProcessHeap(), 0, pText );
765 /***********************************************************************
766 * CBPaintBorder
768 static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc)
770 RECT clientRect;
772 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
774 GetClientRect(lphc->self, &clientRect);
776 else
778 clientRect = lphc->textRect;
780 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
781 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
784 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
787 /***********************************************************************
788 * COMBO_Paint
790 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
792 PAINTSTRUCT ps;
793 HDC hDC;
795 hDC = (hParamDC) ? hParamDC
796 : BeginPaint( lphc->self, &ps);
798 TRACE("hdc=%p\n", hDC);
800 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
802 HBRUSH hPrevBrush, hBkgBrush;
805 * Retrieve the background brush and select it in the
806 * DC.
808 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
810 hPrevBrush = SelectObject( hDC, hBkgBrush );
811 if (!(lphc->wState & CBF_EDIT))
812 FillRect(hDC, &lphc->textRect, hBkgBrush);
815 * In non 3.1 look, there is a sunken border on the combobox
817 CBPaintBorder(lphc, hDC);
818 CBPaintButton(lphc, hDC);
820 /* paint the edit control padding area */
821 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
823 RECT rPadEdit = lphc->textRect;
825 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
827 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
830 if( !(lphc->wState & CBF_EDIT) )
831 CBPaintText( lphc, hDC );
833 if( hPrevBrush )
834 SelectObject( hDC, hPrevBrush );
837 if( !hParamDC )
838 EndPaint(lphc->self, &ps);
840 return 0;
843 /***********************************************************************
844 * CBUpdateLBox
846 * Select listbox entry according to the contents of the edit control.
848 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
850 INT length, idx;
851 LPWSTR pText = NULL;
853 idx = LB_ERR;
854 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
856 if( length > 0 )
857 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
859 TRACE("\t edit text length %i\n", length );
861 if( pText )
863 GetWindowTextW( lphc->hWndEdit, pText, length + 1);
864 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText);
865 HeapFree( GetProcessHeap(), 0, pText );
868 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0);
870 /* probably superfluous but Windows sends this too */
871 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0);
872 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0);
874 return idx;
877 /***********************************************************************
878 * CBUpdateEdit
880 * Copy a listbox entry to the edit control.
882 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
884 INT length;
885 LPWSTR pText = NULL;
887 TRACE("\t %i\n", index );
889 if( index >= 0 ) /* got an entry */
891 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0);
892 if( length != LB_ERR)
894 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
896 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
901 if( CB_HASSTRINGS(lphc) )
903 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
904 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)L"");
905 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
908 if( lphc->wState & CBF_FOCUSED )
909 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
911 HeapFree( GetProcessHeap(), 0, pText );
914 /***********************************************************************
915 * CBDropDown
917 * Show listbox popup.
919 static void CBDropDown( LPHEADCOMBO lphc )
921 HMONITOR monitor;
922 MONITORINFO mon_info;
923 RECT rect,r;
924 int nItems;
925 int nDroppedHeight;
927 TRACE("[%p]: drop down\n", lphc->self);
929 CB_NOTIFY( lphc, CBN_DROPDOWN );
931 /* set selection */
933 lphc->wState |= CBF_DROPPED;
934 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
936 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
938 /* Update edit only if item is in the list */
939 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
940 CBUpdateEdit( lphc, lphc->droppedIndex );
942 else
944 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
946 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
947 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
948 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
951 /* now set popup position */
952 GetWindowRect( lphc->self, &rect );
955 * If it's a dropdown, the listbox is offset
957 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
958 rect.left += COMBO_EDITBUTTONSPACE();
960 /* if the dropped height is greater than the total height of the dropped
961 items list, then force the drop down list height to be the total height
962 of the items in the dropped list */
964 /* And Remove any extra space (Best Fit) */
965 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
966 /* if listbox length has been set directly by its handle */
967 GetWindowRect(lphc->hWndLBox, &r);
968 if (nDroppedHeight < r.bottom - r.top)
969 nDroppedHeight = r.bottom - r.top;
970 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
972 if (nItems > 0)
974 int nHeight;
975 int nIHeight;
977 nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
979 nHeight = nIHeight*nItems;
981 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
982 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
985 r.left = rect.left;
986 r.top = rect.bottom;
987 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
988 r.bottom = r.top + nDroppedHeight;
990 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
991 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
992 mon_info.cbSize = sizeof(mon_info);
993 GetMonitorInfoW( monitor, &mon_info );
995 if (r.bottom > mon_info.rcWork.bottom)
997 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
998 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1001 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1002 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1005 if( !(lphc->wState & CBF_NOREDRAW) )
1006 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1008 EnableWindow( lphc->hWndLBox, TRUE );
1009 if (GetCapture() != lphc->self)
1010 SetCapture(lphc->hWndLBox);
1013 /***********************************************************************
1014 * CBRollUp
1016 * Hide listbox popup.
1018 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1020 HWND hWnd = lphc->self;
1022 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1023 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1025 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1027 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1030 if( lphc->wState & CBF_DROPPED )
1032 RECT rect;
1034 lphc->wState &= ~CBF_DROPPED;
1035 ShowWindow( lphc->hWndLBox, SW_HIDE );
1037 if(GetCapture() == lphc->hWndLBox)
1039 ReleaseCapture();
1042 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1044 rect = lphc->buttonRect;
1046 else
1048 if( bButton )
1050 UnionRect( &rect,
1051 &lphc->buttonRect,
1052 &lphc->textRect);
1054 else
1055 rect = lphc->textRect;
1057 bButton = TRUE;
1060 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1061 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1062 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1063 CB_NOTIFY( lphc, CBN_CLOSEUP );
1068 /***********************************************************************
1069 * COMBO_FlipListbox
1071 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1073 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1075 if( lphc->wState & CBF_DROPPED )
1077 CBRollUp( lphc, ok, bRedrawButton );
1078 return FALSE;
1081 CBDropDown( lphc );
1082 return TRUE;
1085 /***********************************************************************
1086 * CBRepaintButton
1088 static void CBRepaintButton( LPHEADCOMBO lphc )
1090 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1091 UpdateWindow(lphc->self);
1094 /***********************************************************************
1095 * COMBO_SetFocus
1097 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1099 if( !(lphc->wState & CBF_FOCUSED) )
1101 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1102 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1104 /* This is wrong. Message sequences seem to indicate that this
1105 is set *after* the notify. */
1106 /* lphc->wState |= CBF_FOCUSED; */
1108 if( !(lphc->wState & CBF_EDIT) )
1109 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1111 CB_NOTIFY( lphc, CBN_SETFOCUS );
1112 lphc->wState |= CBF_FOCUSED;
1116 /***********************************************************************
1117 * COMBO_KillFocus
1119 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1121 HWND hWnd = lphc->self;
1123 if( lphc->wState & CBF_FOCUSED )
1125 CBRollUp( lphc, FALSE, TRUE );
1126 if( IsWindow( hWnd ) )
1128 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1129 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1131 lphc->wState &= ~CBF_FOCUSED;
1133 /* redraw text */
1134 if( !(lphc->wState & CBF_EDIT) )
1135 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1137 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1142 /***********************************************************************
1143 * COMBO_Command
1145 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1147 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1149 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1151 switch( HIWORD(wParam) >> 8 )
1153 case (EN_SETFOCUS >> 8):
1155 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1157 COMBO_SetFocus( lphc );
1158 break;
1160 case (EN_KILLFOCUS >> 8):
1162 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1164 /* NOTE: it seems that Windows' edit control sends an
1165 * undocumented message WM_USER + 0x1B instead of this
1166 * notification (only when it happens to be a part of
1167 * the combo). ?? - AK.
1170 COMBO_KillFocus( lphc );
1171 break;
1174 case (EN_CHANGE >> 8):
1176 * In some circumstances (when the selection of the combobox
1177 * is changed for example) we don't want the EN_CHANGE notification
1178 * to be forwarded to the parent of the combobox. This code
1179 * checks a flag that is set in these occasions and ignores the
1180 * notification.
1182 if (lphc->wState & CBF_NOLBSELECT)
1184 lphc->wState &= ~CBF_NOLBSELECT;
1186 else
1188 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1191 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1192 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1193 break;
1195 case (EN_UPDATE >> 8):
1196 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1197 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1198 break;
1200 case (EN_ERRSPACE >> 8):
1201 CB_NOTIFY( lphc, CBN_ERRSPACE );
1204 else if( lphc->hWndLBox == hWnd )
1206 switch( (short)HIWORD(wParam) )
1208 case LBN_ERRSPACE:
1209 CB_NOTIFY( lphc, CBN_ERRSPACE );
1210 break;
1212 case LBN_DBLCLK:
1213 CB_NOTIFY( lphc, CBN_DBLCLK );
1214 break;
1216 case LBN_SELCHANGE:
1217 case LBN_SELCANCEL:
1219 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1221 /* do not roll up if selection is being tracked
1222 * by arrow keys in the dropdown listbox */
1223 if (!(lphc->wState & CBF_NOROLLUP))
1225 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1227 else lphc->wState &= ~CBF_NOROLLUP;
1229 CB_NOTIFY( lphc, CBN_SELCHANGE );
1231 if( HIWORD(wParam) == LBN_SELCHANGE)
1233 if( lphc->wState & CBF_EDIT )
1234 lphc->wState |= CBF_NOLBSELECT;
1235 CBPaintText( lphc, NULL );
1237 break;
1239 case LBN_SETFOCUS:
1240 case LBN_KILLFOCUS:
1241 /* nothing to do here since ComboLBox always resets the focus to its
1242 * combo/edit counterpart */
1243 break;
1246 return 0;
1249 /***********************************************************************
1250 * COMBO_ItemOp
1252 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1254 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1256 HWND hWnd = lphc->self;
1257 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1259 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1261 switch( msg )
1263 case WM_DELETEITEM:
1265 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1266 lpIS->CtlType = ODT_COMBOBOX;
1267 lpIS->CtlID = id;
1268 lpIS->hwndItem = hWnd;
1269 break;
1271 case WM_DRAWITEM:
1273 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1274 lpIS->CtlType = ODT_COMBOBOX;
1275 lpIS->CtlID = id;
1276 lpIS->hwndItem = hWnd;
1277 break;
1279 case WM_COMPAREITEM:
1281 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1282 lpIS->CtlType = ODT_COMBOBOX;
1283 lpIS->CtlID = id;
1284 lpIS->hwndItem = hWnd;
1285 break;
1287 case WM_MEASUREITEM:
1289 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1290 lpIS->CtlType = ODT_COMBOBOX;
1291 lpIS->CtlID = id;
1292 break;
1295 return SendMessageW(lphc->owner, msg, id, lParam);
1299 /***********************************************************************
1300 * COMBO_GetTextW
1302 static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
1304 INT length;
1306 if( lphc->wState & CBF_EDIT )
1307 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1309 /* get it from the listbox */
1311 if (!count || !buf) return 0;
1312 if( lphc->hWndLBox )
1314 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1315 if (idx == LB_ERR) goto error;
1316 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1317 if (length == LB_ERR) goto error;
1319 /* 'length' is without the terminating character */
1320 if (length >= count)
1322 LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1323 if (!lpBuffer) goto error;
1324 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1326 /* truncate if buffer is too short */
1327 if (length != LB_ERR)
1329 lstrcpynW( buf, lpBuffer, count );
1330 length = count;
1332 HeapFree( GetProcessHeap(), 0, lpBuffer );
1334 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1336 if (length == LB_ERR) return 0;
1337 return length;
1340 error: /* error - truncate string, return zero */
1341 buf[0] = 0;
1342 return 0;
1346 /***********************************************************************
1347 * COMBO_GetTextA
1349 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1350 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1352 static LRESULT COMBO_GetTextA( LPHEADCOMBO lphc, INT count, LPSTR buf )
1354 INT length;
1356 if( lphc->wState & CBF_EDIT )
1357 return SendMessageA( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1359 /* get it from the listbox */
1361 if (!count || !buf) return 0;
1362 if( lphc->hWndLBox )
1364 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1365 if (idx == LB_ERR) goto error;
1366 length = SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1367 if (length == LB_ERR) goto error;
1369 /* 'length' is without the terminating character */
1370 if (length >= count)
1372 LPSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) );
1373 if (!lpBuffer) goto error;
1374 length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1376 /* truncate if buffer is too short */
1377 if (length != LB_ERR)
1379 lstrcpynA( buf, lpBuffer, count );
1380 length = count;
1382 HeapFree( GetProcessHeap(), 0, lpBuffer );
1384 else length = SendMessageA(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1386 if (length == LB_ERR) return 0;
1387 return length;
1390 error: /* error - truncate string, return zero */
1391 buf[0] = 0;
1392 return 0;
1396 /***********************************************************************
1397 * CBResetPos
1399 * This function sets window positions according to the updated
1400 * component placement struct.
1402 static void CBResetPos(HEADCOMBO *combo, BOOL redraw)
1404 BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
1406 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1407 * sizing messages */
1408 if (combo->wState & CBF_EDIT)
1409 SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
1410 combo->textRect.right - combo->textRect.left,
1411 combo->textRect.bottom - combo->textRect.top,
1412 SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0));
1414 SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
1415 combo->droppedRect.right - combo->droppedRect.left,
1416 combo->droppedRect.bottom - combo->droppedRect.top,
1417 SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0));
1419 if (drop)
1421 if (combo->wState & CBF_DROPPED)
1423 combo->wState &= ~CBF_DROPPED;
1424 ShowWindow(combo->hWndLBox, SW_HIDE);
1427 if (redraw && !(combo->wState & CBF_NOREDRAW))
1428 RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
1432 /***********************************************************************
1433 * COMBO_Size
1435 static void COMBO_Size( HEADCOMBO *lphc )
1437 if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
1438 return;
1441 * Those controls are always the same height. So we have to make sure
1442 * they are not resized to another value.
1444 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1446 int newComboHeight, curComboHeight, curComboWidth;
1447 RECT rc;
1449 GetWindowRect(lphc->self, &rc);
1450 curComboHeight = rc.bottom - rc.top;
1451 curComboWidth = rc.right - rc.left;
1452 newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1455 * Resizing a combobox has another side effect, it resizes the dropped
1456 * rectangle as well. However, it does it only if the new height for the
1457 * combobox is more than the height it should have. In other words,
1458 * if the application resizing the combobox only had the intention to resize
1459 * the actual control, for example, to do the layout of a dialog that is
1460 * resized, the height of the dropdown is not changed.
1462 if( curComboHeight > newComboHeight )
1464 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n",
1465 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1466 lphc->droppedRect.top);
1467 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1470 * Restore original height
1472 if (curComboHeight != newComboHeight)
1474 lphc->wState |= CBF_NORESIZE;
1475 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1476 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW);
1477 lphc->wState &= ~CBF_NORESIZE;
1481 CBCalcPlacement(lphc);
1483 CBResetPos(lphc, FALSE);
1487 /***********************************************************************
1488 * COMBO_Font
1490 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1492 lphc->hFont = hFont;
1493 lphc->item_height = combo_get_text_height(lphc);
1496 * Propagate to owned windows.
1498 if( lphc->wState & CBF_EDIT )
1499 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1500 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1503 * Redo the layout of the control.
1505 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1507 CBCalcPlacement(lphc);
1509 CBResetPos(lphc, TRUE);
1511 else
1513 CBForceDummyResize(lphc);
1518 /***********************************************************************
1519 * COMBO_SetItemHeight
1521 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1523 LRESULT lRet = CB_ERR;
1525 if( index == -1 ) /* set text field height */
1527 if( height < 32768 )
1529 lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1532 * Redo the layout of the control.
1534 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1536 CBCalcPlacement(lphc);
1538 CBResetPos(lphc, TRUE);
1540 else
1542 CBForceDummyResize(lphc);
1545 lRet = height;
1548 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1549 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1550 return lRet;
1553 /***********************************************************************
1554 * COMBO_SelectString
1556 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1558 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText) :
1559 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1560 if( index >= 0 )
1562 if( lphc->wState & CBF_EDIT )
1563 CBUpdateEdit( lphc, index );
1564 else
1566 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1569 return (LRESULT)index;
1572 /***********************************************************************
1573 * COMBO_LButtonDown
1575 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1577 POINT pt;
1578 BOOL bButton;
1579 HWND hWnd = lphc->self;
1581 pt.x = (short)LOWORD(lParam);
1582 pt.y = (short)HIWORD(lParam);
1583 bButton = PtInRect(&lphc->buttonRect, pt);
1585 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1586 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1588 lphc->wState |= CBF_BUTTONDOWN;
1589 if( lphc->wState & CBF_DROPPED )
1591 /* got a click to cancel selection */
1593 lphc->wState &= ~CBF_BUTTONDOWN;
1594 CBRollUp( lphc, TRUE, FALSE );
1595 if( !IsWindow( hWnd ) ) return;
1597 if( lphc->wState & CBF_CAPTURE )
1599 lphc->wState &= ~CBF_CAPTURE;
1600 ReleaseCapture();
1603 else
1605 /* drop down the listbox and start tracking */
1607 lphc->wState |= CBF_CAPTURE;
1608 SetCapture( hWnd );
1609 CBDropDown( lphc );
1611 if( bButton ) CBRepaintButton( lphc );
1615 /***********************************************************************
1616 * COMBO_LButtonUp
1618 * Release capture and stop tracking if needed.
1620 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1622 if( lphc->wState & CBF_CAPTURE )
1624 lphc->wState &= ~CBF_CAPTURE;
1625 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1627 INT index = CBUpdateLBox( lphc, TRUE );
1628 /* Update edit only if item is in the list */
1629 if(index >= 0)
1631 lphc->wState |= CBF_NOLBSELECT;
1632 CBUpdateEdit( lphc, index );
1633 lphc->wState &= ~CBF_NOLBSELECT;
1636 ReleaseCapture();
1637 SetCapture(lphc->hWndLBox);
1640 if( lphc->wState & CBF_BUTTONDOWN )
1642 lphc->wState &= ~CBF_BUTTONDOWN;
1643 CBRepaintButton( lphc );
1647 /***********************************************************************
1648 * COMBO_MouseMove
1650 * Two things to do - track combo button and release capture when
1651 * pointer goes into the listbox.
1653 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1655 POINT pt;
1656 RECT lbRect;
1658 pt.x = (short)LOWORD(lParam);
1659 pt.y = (short)HIWORD(lParam);
1661 if( lphc->wState & CBF_BUTTONDOWN )
1663 BOOL bButton;
1665 bButton = PtInRect(&lphc->buttonRect, pt);
1667 if( !bButton )
1669 lphc->wState &= ~CBF_BUTTONDOWN;
1670 CBRepaintButton( lphc );
1674 GetClientRect( lphc->hWndLBox, &lbRect );
1675 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1676 if( PtInRect(&lbRect, pt) )
1678 lphc->wState &= ~CBF_CAPTURE;
1679 ReleaseCapture();
1680 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1682 /* hand over pointer tracking */
1683 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1687 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1689 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1690 return FALSE;
1692 pcbi->rcItem = lphc->textRect;
1693 pcbi->rcButton = lphc->buttonRect;
1694 pcbi->stateButton = 0;
1695 if (lphc->wState & CBF_BUTTONDOWN)
1696 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1697 if (IsRectEmpty(&lphc->buttonRect))
1698 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1699 pcbi->hwndCombo = lphc->self;
1700 pcbi->hwndItem = lphc->hWndEdit;
1701 pcbi->hwndList = lphc->hWndLBox;
1702 return TRUE;
1705 static char *strdupA(LPCSTR str)
1707 char *ret;
1708 DWORD len;
1710 if(!str) return NULL;
1712 len = strlen(str);
1713 ret = HeapAlloc(GetProcessHeap(), 0, len + 1);
1714 memcpy(ret, str, len + 1);
1715 return ret;
1718 /***********************************************************************
1719 * ComboWndProc_common
1721 LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
1723 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
1725 TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
1726 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1728 if (!IsWindow(hwnd)) return 0;
1730 if( lphc || message == WM_NCCREATE )
1731 switch(message)
1734 /* System messages */
1736 case WM_NCCREATE:
1738 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1739 ((LPCREATESTRUCTA)lParam)->style;
1740 return COMBO_NCCreate(hwnd, style);
1742 case WM_NCDESTROY:
1743 COMBO_NCDestroy(lphc);
1744 break;/* -> DefWindowProc */
1746 case WM_CREATE:
1748 HWND hwndParent;
1749 LONG style;
1750 if(unicode)
1752 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1753 style = ((LPCREATESTRUCTW)lParam)->style;
1755 else
1757 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1758 style = ((LPCREATESTRUCTA)lParam)->style;
1760 return COMBO_Create(hwnd, lphc, hwndParent, style, unicode);
1763 case WM_PRINTCLIENT:
1764 /* Fallthrough */
1765 case WM_PAINT:
1766 /* wParam may contain a valid HDC! */
1767 return COMBO_Paint(lphc, (HDC)wParam);
1769 case WM_ERASEBKGND:
1770 /* do all painting in WM_PAINT like Windows does */
1771 return 1;
1773 case WM_GETDLGCODE:
1775 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1776 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1778 int vk = (int)((LPMSG)lParam)->wParam;
1780 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1781 result |= DLGC_WANTMESSAGE;
1783 return result;
1785 case WM_SIZE:
1786 COMBO_Size( lphc );
1787 return TRUE;
1788 case WM_SETFONT:
1789 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1790 return TRUE;
1791 case WM_GETFONT:
1792 return (LRESULT)lphc->hFont;
1793 case WM_SETFOCUS:
1794 if( lphc->wState & CBF_EDIT ) {
1795 SetFocus( lphc->hWndEdit );
1796 /* The first time focus is received, select all the text */
1797 if( !(lphc->wState & CBF_BEENFOCUSED) ) {
1798 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1799 lphc->wState |= CBF_BEENFOCUSED;
1802 else
1803 COMBO_SetFocus( lphc );
1804 return TRUE;
1805 case WM_KILLFOCUS:
1807 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1808 if( !hwndFocus ||
1809 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1810 COMBO_KillFocus( lphc );
1811 return TRUE;
1813 case WM_COMMAND:
1814 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1815 case WM_GETTEXT:
1816 return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
1817 : COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
1818 case WM_SETTEXT:
1819 case WM_GETTEXTLENGTH:
1820 case WM_CLEAR:
1821 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1823 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1824 if (j == -1) return 0;
1825 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
1826 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1828 else if( lphc->wState & CBF_EDIT )
1830 LRESULT ret;
1831 lphc->wState |= CBF_NOEDITNOTIFY;
1832 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1833 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1834 lphc->wState &= ~CBF_NOEDITNOTIFY;
1835 return ret;
1837 else return CB_ERR;
1838 case WM_CUT:
1839 case WM_PASTE:
1840 case WM_COPY:
1841 if( lphc->wState & CBF_EDIT )
1843 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1844 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1846 else return CB_ERR;
1848 case WM_DRAWITEM:
1849 case WM_DELETEITEM:
1850 case WM_COMPAREITEM:
1851 case WM_MEASUREITEM:
1852 return COMBO_ItemOp(lphc, message, lParam);
1853 case WM_ENABLE:
1854 if( lphc->wState & CBF_EDIT )
1855 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1856 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1858 /* Force the control to repaint when the enabled state changes. */
1859 InvalidateRect(lphc->self, NULL, TRUE);
1860 return TRUE;
1861 case WM_SETREDRAW:
1862 if( wParam )
1863 lphc->wState &= ~CBF_NOREDRAW;
1864 else
1865 lphc->wState |= CBF_NOREDRAW;
1867 if( lphc->wState & CBF_EDIT )
1868 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1869 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1870 return 0;
1871 case WM_SYSKEYDOWN:
1872 if( KEYDATA_ALT & HIWORD(lParam) )
1873 if( wParam == VK_UP || wParam == VK_DOWN )
1874 COMBO_FlipListbox( lphc, FALSE, FALSE );
1875 return 0;
1877 case WM_KEYDOWN:
1878 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1879 (lphc->wState & CBF_DROPPED))
1881 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1882 return TRUE;
1884 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1886 COMBO_FlipListbox( lphc, FALSE, FALSE );
1887 return TRUE;
1889 /* fall through */
1890 case WM_CHAR:
1891 case WM_IME_CHAR:
1893 HWND hwndTarget;
1895 if( lphc->wState & CBF_EDIT )
1896 hwndTarget = lphc->hWndEdit;
1897 else
1898 hwndTarget = lphc->hWndLBox;
1900 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
1901 SendMessageA(hwndTarget, message, wParam, lParam);
1903 case WM_LBUTTONDOWN:
1904 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1905 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1906 return TRUE;
1907 case WM_LBUTTONUP:
1908 COMBO_LButtonUp( lphc );
1909 return TRUE;
1910 case WM_MOUSEMOVE:
1911 if( lphc->wState & CBF_CAPTURE )
1912 COMBO_MouseMove( lphc, wParam, lParam );
1913 return TRUE;
1915 case WM_MOUSEWHEEL:
1916 if (wParam & (MK_SHIFT | MK_CONTROL))
1917 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
1918 DefWindowProcA(hwnd, message, wParam, lParam);
1920 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1921 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1922 return TRUE;
1924 case WM_CTLCOLOR:
1925 case WM_CTLCOLORMSGBOX:
1926 case WM_CTLCOLOREDIT:
1927 case WM_CTLCOLORLISTBOX:
1928 case WM_CTLCOLORBTN:
1929 case WM_CTLCOLORDLG:
1930 case WM_CTLCOLORSCROLLBAR:
1931 case WM_CTLCOLORSTATIC:
1932 if (lphc->owner)
1933 return SendMessageW(lphc->owner, message, wParam, lParam);
1934 break;
1936 /* Combo messages */
1938 case CB_ADDSTRING:
1939 if( unicode )
1941 if( lphc->dwStyle & CBS_LOWERCASE )
1942 CharLowerW((LPWSTR)lParam);
1943 else if( lphc->dwStyle & CBS_UPPERCASE )
1944 CharUpperW((LPWSTR)lParam);
1945 return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1947 else /* unlike the unicode version, the ansi version does not overwrite
1948 the string if converting case */
1950 char *string = NULL;
1951 LRESULT ret;
1952 if( lphc->dwStyle & CBS_LOWERCASE )
1954 string = strdupA((LPSTR)lParam);
1955 CharLowerA(string);
1958 else if( lphc->dwStyle & CBS_UPPERCASE )
1960 string = strdupA((LPSTR)lParam);
1961 CharUpperA(string);
1964 ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
1965 HeapFree(GetProcessHeap(), 0, string);
1966 return ret;
1968 case CB_INSERTSTRING:
1969 if( unicode )
1971 if( lphc->dwStyle & CBS_LOWERCASE )
1972 CharLowerW((LPWSTR)lParam);
1973 else if( lphc->dwStyle & CBS_UPPERCASE )
1974 CharUpperW((LPWSTR)lParam);
1975 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1977 else
1979 if( lphc->dwStyle & CBS_LOWERCASE )
1980 CharLowerA((LPSTR)lParam);
1981 else if( lphc->dwStyle & CBS_UPPERCASE )
1982 CharUpperA((LPSTR)lParam);
1984 return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1986 case CB_DELETESTRING:
1987 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
1988 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1989 case CB_SELECTSTRING:
1990 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
1991 case CB_FINDSTRING:
1992 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
1993 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1994 case CB_FINDSTRINGEXACT:
1995 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
1996 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1997 case CB_SETITEMHEIGHT:
1998 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1999 case CB_GETITEMHEIGHT:
2000 if( (INT)wParam >= 0 ) /* listbox item */
2001 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2002 return CBGetTextAreaHeight(lphc, FALSE);
2003 case CB_RESETCONTENT:
2004 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2005 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2006 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)L"");
2007 else
2008 InvalidateRect(lphc->self, NULL, TRUE);
2009 return TRUE;
2010 case CB_INITSTORAGE:
2011 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2012 case CB_GETHORIZONTALEXTENT:
2013 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2014 case CB_SETHORIZONTALEXTENT:
2015 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2016 case CB_GETTOPINDEX:
2017 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2018 case CB_GETLOCALE:
2019 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2020 case CB_SETLOCALE:
2021 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2022 case CB_SETDROPPEDWIDTH:
2023 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) ||
2024 (INT)wParam >= 32768 )
2025 return CB_ERR;
2026 /* new value must be higher than combobox width */
2027 if((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2028 lphc->droppedWidth = wParam;
2029 else if(wParam)
2030 lphc->droppedWidth = 0;
2032 /* recalculate the combobox area */
2033 CBCalcPlacement(lphc);
2035 /* fall through */
2036 case CB_GETDROPPEDWIDTH:
2037 if( lphc->droppedWidth )
2038 return lphc->droppedWidth;
2039 return lphc->droppedRect.right - lphc->droppedRect.left;
2040 case CB_GETDROPPEDCONTROLRECT:
2041 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2042 return CB_OKAY;
2043 case CB_GETDROPPEDSTATE:
2044 return (lphc->wState & CBF_DROPPED) != 0;
2045 case CB_DIR:
2046 return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
2047 SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
2049 case CB_SHOWDROPDOWN:
2050 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2052 if( wParam )
2054 if( !(lphc->wState & CBF_DROPPED) )
2055 CBDropDown( lphc );
2057 else
2058 if( lphc->wState & CBF_DROPPED )
2059 CBRollUp( lphc, FALSE, TRUE );
2061 return TRUE;
2062 case CB_GETCOUNT:
2063 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2064 case CB_GETCURSEL:
2065 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2066 case CB_SETCURSEL:
2067 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2068 if( lParam >= 0 )
2069 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2071 /* no LBN_SELCHANGE in this case, update manually */
2072 CBPaintText( lphc, NULL );
2073 lphc->wState &= ~CBF_SELCHANGE;
2074 return lParam;
2075 case CB_GETLBTEXT:
2076 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2077 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2078 case CB_GETLBTEXTLEN:
2079 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
2080 SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2081 case CB_GETITEMDATA:
2082 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2083 case CB_SETITEMDATA:
2084 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2085 case CB_GETEDITSEL:
2086 /* Edit checks passed parameters itself */
2087 if( lphc->wState & CBF_EDIT )
2088 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2089 return CB_ERR;
2090 case CB_SETEDITSEL:
2091 if( lphc->wState & CBF_EDIT )
2092 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2093 (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2094 return CB_ERR;
2095 case CB_SETEXTENDEDUI:
2096 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2097 return CB_ERR;
2098 if( wParam )
2099 lphc->wState |= CBF_EUI;
2100 else lphc->wState &= ~CBF_EUI;
2101 return CB_OKAY;
2102 case CB_GETEXTENDEDUI:
2103 return (lphc->wState & CBF_EUI) != 0;
2104 case CB_GETCOMBOBOXINFO:
2105 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2106 case CB_LIMITTEXT:
2107 if( lphc->wState & CBF_EDIT )
2108 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2109 return TRUE;
2110 default:
2111 if (message >= WM_USER)
2112 WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
2113 message - WM_USER, wParam, lParam );
2114 break;
2116 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2117 DefWindowProcA(hwnd, message, wParam, lParam);
2120 /*************************************************************************
2121 * GetComboBoxInfo (USER32.@)
2123 BOOL WINAPI GetComboBoxInfo(HWND hwndCombo, /* [in] handle to combo box */
2124 PCOMBOBOXINFO pcbi /* [in/out] combo box information */)
2126 TRACE("(%p, %p)\n", hwndCombo, pcbi);
2127 return SendMessageW(hwndCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);