mmdevapi: Query MemoryWineUnixFuncs virtual memory and store the resulting handle.
[wine.git] / dlls / comctl32 / combo.c
blobcb0e9745b2a947943f02010dc4ccb08d44abbe6b
1 /*
2 * Combo controls
4 * Copyright 1997 Alex Korobka
5 * Copyright (c) 2005 by Frank Richter
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <string.h>
26 #define OEMRESOURCE
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "uxtheme.h"
33 #include "vssym32.h"
34 #include "commctrl.h"
35 #include "wine/debug.h"
37 #include "comctl32.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 #define ID_CB_LISTBOX 1000
78 #define ID_CB_EDIT 1001
80 static void CBCalcPlacement(HEADCOMBO *combo);
81 static void CBResetPos(HEADCOMBO *combo);
83 /***********************************************************************
84 * COMBO_Init
86 * Load combo button bitmap.
88 static BOOL COMBO_Init(void)
90 HDC hDC;
92 if( hComboBmp ) return TRUE;
93 if( (hDC = CreateCompatibleDC(0)) )
95 BOOL bRet = FALSE;
96 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
98 BITMAP bm;
99 HBITMAP hPrevB;
100 RECT r;
102 GetObjectW( hComboBmp, sizeof(bm), &bm );
103 CBitHeight = bm.bmHeight;
104 CBitWidth = bm.bmWidth;
106 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
108 hPrevB = SelectObject( hDC, hComboBmp);
109 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
110 InvertRect( hDC, &r );
111 SelectObject( hDC, hPrevB );
112 bRet = TRUE;
114 DeleteDC( hDC );
115 return bRet;
117 return FALSE;
120 /***********************************************************************
121 * COMBO_NCCreate
123 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
125 HEADCOMBO *lphc;
127 if (COMBO_Init() && (lphc = Alloc(sizeof(*lphc))))
129 lphc->self = hwnd;
130 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc );
132 /* some braindead apps do try to use scrollbar/border flags */
134 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
135 SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
138 * We also have to remove the client edge style to make sure
139 * we don't end-up with a non client area.
141 SetWindowLongW( hwnd, GWL_EXSTYLE,
142 GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
144 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
145 lphc->dwStyle |= CBS_HASSTRINGS;
146 if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
147 lphc->wState |= CBF_NOTIFY;
149 TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle );
150 return TRUE;
152 return FALSE;
155 /***********************************************************************
156 * COMBO_NCDestroy
158 static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc )
160 if (lphc)
162 TRACE("[%p]: freeing storage\n", lphc->self);
164 if ( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
165 DestroyWindow( lphc->hWndLBox );
167 SetWindowLongPtrW( lphc->self, 0, 0 );
168 Free( lphc );
171 return 0;
174 static INT combo_get_text_height(const HEADCOMBO *combo)
176 HDC hdc = GetDC(combo->self);
177 HFONT prev_font = 0;
178 TEXTMETRICW tm;
180 if (combo->hFont)
181 prev_font = SelectObject(hdc, combo->hFont);
183 GetTextMetricsW(hdc, &tm);
185 if (prev_font)
186 SelectObject(hdc, prev_font);
188 ReleaseDC(combo->self, hdc);
190 return tm.tmHeight + 4;
193 /***********************************************************************
194 * CBGetTextAreaHeight
196 * This method will calculate the height of the text area of the
197 * combobox.
198 * The height of the text area is set in two ways.
199 * It can be set explicitly through a combobox message or through a
200 * WM_MEASUREITEM callback.
201 * If this is not the case, the height is set to font height + 4px
202 * This height was determined through experimentation.
203 * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
205 static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
207 INT item_height, text_height;
209 if (clip_item_height && !CB_OWNERDRAWN(lphc))
211 text_height = combo_get_text_height(lphc);
212 if (lphc->item_height < text_height)
213 lphc->item_height = text_height;
215 item_height = lphc->item_height;
219 * Check the ownerdraw case if we haven't asked the parent the size
220 * of the item yet.
222 if ( CB_OWNERDRAWN(lphc) &&
223 (lphc->wState & CBF_MEASUREITEM) )
225 MEASUREITEMSTRUCT measureItem;
226 RECT clientRect;
227 INT originalItemHeight = item_height;
228 UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
231 * We use the client rect for the width of the item.
233 GetClientRect(lphc->self, &clientRect);
235 lphc->wState &= ~CBF_MEASUREITEM;
238 * Send a first one to measure the size of the text area
240 measureItem.CtlType = ODT_COMBOBOX;
241 measureItem.CtlID = id;
242 measureItem.itemID = -1;
243 measureItem.itemWidth = clientRect.right;
244 measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */
245 measureItem.itemData = 0;
246 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
247 item_height = 6 + measureItem.itemHeight;
250 * Send a second one in the case of a fixed ownerdraw list to calculate the
251 * size of the list items. (we basically do this on behalf of the listbox)
253 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
255 measureItem.CtlType = ODT_COMBOBOX;
256 measureItem.CtlID = id;
257 measureItem.itemID = 0;
258 measureItem.itemWidth = clientRect.right;
259 measureItem.itemHeight = originalItemHeight;
260 measureItem.itemData = 0;
261 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
262 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
266 * Keep the size for the next time
268 lphc->item_height = item_height;
271 return item_height;
274 /***********************************************************************
275 * CBForceDummyResize
277 * The dummy resize is used for listboxes that have a popup to trigger
278 * a re-arranging of the contents of the combobox and the recalculation
279 * of the size of the "real" control window.
281 static void CBForceDummyResize(LPHEADCOMBO lphc)
283 RECT windowRect;
284 int newComboHeight;
286 newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE();
288 GetWindowRect(lphc->self, &windowRect);
291 * We have to be careful, resizing a combobox also has the meaning that the
292 * dropped rect will be resized. In this case, we want to trigger a resize
293 * to recalculate layout but we don't want to change the dropped rectangle
294 * So, we pass the height of text area of control as the height.
295 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
296 * message.
298 lphc->wState |= CBF_NORESIZE;
299 SetWindowPos( lphc->self,
300 NULL,
301 0, 0,
302 windowRect.right - windowRect.left,
303 newComboHeight,
304 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
305 lphc->wState &= ~CBF_NORESIZE;
307 CBCalcPlacement(lphc);
308 CBResetPos(lphc);
311 /***********************************************************************
312 * CBCalcPlacement
314 * Set up component coordinates given valid lphc->RectCombo.
316 static void CBCalcPlacement(HEADCOMBO *combo)
318 /* Start with the client rectangle. */
319 GetClientRect(combo->self, &combo->textRect);
321 /* Remove the borders */
322 InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
324 /* Chop off the bottom part to fit with the height of the text area. */
325 combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
327 /* The button starts the same vertical position as the text area. */
328 combo->buttonRect = combo->textRect;
330 /* If the combobox is "simple" there is no button. */
331 if (CB_GETTYPE(combo) == CBS_SIMPLE)
332 combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0;
333 else
336 * Let's assume the combobox button is the same width as the
337 * scrollbar button.
338 * size the button horizontally and cut-off the text area.
340 combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
341 combo->textRect.right = combo->buttonRect.left;
344 /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
345 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
346 combo->textRect.right -= COMBO_EDITBUTTONSPACE();
348 /* If we have an edit control, we space it away from the borders slightly. */
349 if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
350 InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
352 /* Adjust the size of the listbox popup. */
353 if (CB_GETTYPE(combo) == CBS_SIMPLE)
355 GetClientRect(combo->self, &combo->droppedRect);
356 combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
358 else
360 /* Make sure the dropped width is as large as the combobox itself. */
361 if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
363 combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
365 /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
366 with the right side of the combobox */
367 if (CB_GETTYPE(combo) == CBS_DROPDOWN)
368 combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
370 else
371 combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
374 /* Disallow negative window width */
375 if (combo->textRect.right < combo->textRect.left)
376 combo->textRect.right = combo->textRect.left;
378 TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
379 wine_dbgstr_rect(&combo->droppedRect));
382 /***********************************************************************
383 * CBGetDroppedControlRect
385 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
387 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
388 of the combo box and the lower right corner of the listbox */
390 GetWindowRect(lphc->self, lpRect);
392 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
393 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
397 /***********************************************************************
398 * COMBO_Create
400 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style )
402 OpenThemeData( hwnd, WC_COMBOBOXW );
403 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
404 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
406 lphc->owner = hwndParent;
408 lphc->droppedWidth = 0;
410 lphc->item_height = combo_get_text_height(lphc);
413 * The first time we go through, we want to measure the ownerdraw item
415 lphc->wState |= CBF_MEASUREITEM;
418 * Per default the comctl32 version of combo shows up to 30 items
420 lphc->visibleItems = 30;
422 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
424 if( lphc->owner || !(style & WS_VISIBLE) )
426 UINT lbeStyle = 0;
427 UINT lbeExStyle = 0;
430 * Initialize the dropped rect to the size of the client area of the
431 * control and then, force all the areas of the combobox to be
432 * recalculated.
434 GetClientRect( hwnd, &lphc->droppedRect );
435 CBCalcPlacement(lphc);
438 * Adjust the position of the popup listbox if it's necessary
440 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
442 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
445 * If it's a dropdown, the listbox is offset
447 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
448 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
450 if (lphc->droppedRect.bottom < lphc->droppedRect.top)
451 lphc->droppedRect.bottom = lphc->droppedRect.top;
452 if (lphc->droppedRect.right < lphc->droppedRect.left)
453 lphc->droppedRect.right = lphc->droppedRect.left;
454 MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 );
457 /* create listbox popup */
459 lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
460 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
462 if( lphc->dwStyle & CBS_SORT )
463 lbeStyle |= LBS_SORT;
464 if( lphc->dwStyle & CBS_HASSTRINGS )
465 lbeStyle |= LBS_HASSTRINGS;
466 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
467 lbeStyle |= LBS_NOINTEGRALHEIGHT;
468 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
469 lbeStyle |= LBS_DISABLENOSCROLL;
471 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
473 lbeStyle |= WS_VISIBLE;
476 * In win 95 look n feel, the listbox in the simple combobox has
477 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
479 lbeStyle &= ~WS_BORDER;
480 lbeExStyle |= WS_EX_CLIENTEDGE;
482 else
484 lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW);
487 lphc->hWndLBox = CreateWindowExW(lbeExStyle, L"ComboLBox", NULL, lbeStyle,
488 lphc->droppedRect.left, lphc->droppedRect.top, lphc->droppedRect.right - lphc->droppedRect.left,
489 lphc->droppedRect.bottom - lphc->droppedRect.top, hwnd, (HMENU)ID_CB_LISTBOX,
490 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
491 if( lphc->hWndLBox )
493 BOOL bEdit = TRUE;
494 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
496 if( lphc->wState & CBF_EDIT )
498 if( lphc->dwStyle & CBS_OEMCONVERT )
499 lbeStyle |= ES_OEMCONVERT;
500 if( lphc->dwStyle & CBS_AUTOHSCROLL )
501 lbeStyle |= ES_AUTOHSCROLL;
502 if( lphc->dwStyle & CBS_LOWERCASE )
503 lbeStyle |= ES_LOWERCASE;
504 else if( lphc->dwStyle & CBS_UPPERCASE )
505 lbeStyle |= ES_UPPERCASE;
507 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
509 lphc->hWndEdit = CreateWindowExW(0, WC_EDITW, NULL, lbeStyle,
510 lphc->textRect.left, lphc->textRect.top,
511 lphc->textRect.right - lphc->textRect.left,
512 lphc->textRect.bottom - lphc->textRect.top,
513 hwnd, (HMENU)ID_CB_EDIT,
514 (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
515 if( !lphc->hWndEdit )
516 bEdit = FALSE;
519 if( bEdit )
521 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
523 /* Now do the trick with parent */
524 SetParent(lphc->hWndLBox, HWND_DESKTOP);
526 * If the combo is a dropdown, we must resize the control
527 * to fit only the text area and button. To do this,
528 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
529 * will take care of setting the height for us.
531 CBForceDummyResize(lphc);
534 TRACE("init done\n");
535 return 0;
537 ERR("edit control failure.\n");
538 } else ERR("listbox failure.\n");
539 } else ERR("no owner for visible combo.\n");
541 /* CreateWindow() will send WM_NCDESTROY to cleanup */
543 return -1;
546 /***********************************************************************
547 * CBPaintButton
549 * Paint combo button (normal, pressed, and disabled states).
551 static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
553 UINT buttonState = DFCS_SCROLLCOMBOBOX;
555 if (IsRectEmpty(&lphc->buttonRect))
556 return;
558 if( lphc->wState & CBF_NOREDRAW )
559 return;
562 if (lphc->wState & CBF_BUTTONDOWN)
563 buttonState |= DFCS_PUSHED;
565 if (CB_DISABLED(lphc))
566 buttonState |= DFCS_INACTIVE;
568 DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
571 /***********************************************************************
572 * COMBO_PrepareColors
574 * This method will sent the appropriate WM_CTLCOLOR message to
575 * prepare and setup the colors for the combo's DC.
577 * It also returns the brush to use for the background.
579 static HBRUSH COMBO_PrepareColors(
580 LPHEADCOMBO lphc,
581 HDC hDC)
583 HBRUSH hBkgBrush;
586 * Get the background brush for this control.
588 if (CB_DISABLED(lphc))
590 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC,
591 (WPARAM)hDC, (LPARAM)lphc->self );
594 * We have to change the text color since WM_CTLCOLORSTATIC will
595 * set it to the "enabled" color. This is the same behavior as the
596 * edit control
598 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
600 else
602 /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */
603 hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT,
604 (WPARAM)hDC, (LPARAM)lphc->self );
608 * Catch errors.
610 if( !hBkgBrush )
611 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
613 return hBkgBrush;
616 /***********************************************************************
617 * CBPaintText
619 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
621 static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint)
623 RECT rectEdit = lphc->textRect;
624 INT id, size = 0;
625 LPWSTR pText = NULL;
627 TRACE("\n");
629 /* follow Windows combobox that sends a bunch of text
630 * inquiries to its listbox while processing WM_PAINT. */
632 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
634 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
635 if (size == LB_ERR)
636 FIXME("LB_ERR probably not handled yet\n");
637 if ((pText = Alloc((size + 1) * sizeof(WCHAR))))
639 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
640 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText);
641 pText[size] = '\0'; /* just in case */
642 } else return;
645 if( lphc->wState & CBF_EDIT )
647 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : L"" );
648 if( lphc->wState & CBF_FOCUSED )
649 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG);
651 else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self ))
653 /* paint text field ourselves */
654 HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self);
655 UINT itemState = ODS_COMBOBOXEDIT;
656 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
657 HBRUSH hPrevBrush, hBkgBrush;
660 * Give ourselves some space.
662 InflateRect( &rectEdit, -1, -1 );
664 hBkgBrush = COMBO_PrepareColors( lphc, hdc );
665 hPrevBrush = SelectObject( hdc, hBkgBrush );
666 FillRect( hdc, &rectEdit, hBkgBrush );
668 if( CB_OWNERDRAWN(lphc) )
670 DRAWITEMSTRUCT dis;
671 HRGN clipRegion;
672 UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
674 /* setup state for DRAWITEM message. Owner will highlight */
675 if ( (lphc->wState & CBF_FOCUSED) &&
676 !(lphc->wState & CBF_DROPPED) )
677 itemState |= ODS_SELECTED | ODS_FOCUS;
679 if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED;
681 dis.CtlType = ODT_COMBOBOX;
682 dis.CtlID = ctlid;
683 dis.hwndItem = lphc->self;
684 dis.itemAction = ODA_DRAWENTIRE;
685 dis.itemID = id;
686 dis.itemState = itemState;
687 dis.hDC = hdc;
688 dis.rcItem = rectEdit;
689 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0);
692 * Clip the DC and have the parent draw the item.
694 clipRegion = set_control_clipping( hdc, &rectEdit );
696 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
698 SelectClipRgn( hdc, clipRegion );
699 if (clipRegion) DeleteObject( clipRegion );
701 else
703 if ( (lphc->wState & CBF_FOCUSED) &&
704 !(lphc->wState & CBF_DROPPED) ) {
706 /* highlight */
707 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
708 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
709 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
712 ExtTextOutW( hdc,
713 rectEdit.left + 1,
714 rectEdit.top + 1,
715 ETO_OPAQUE | ETO_CLIPPED,
716 &rectEdit,
717 pText ? pText : L"" , size, NULL );
719 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
720 DrawFocusRect( hdc, &rectEdit );
723 if( hPrevFont )
724 SelectObject(hdc, hPrevFont );
726 if( hPrevBrush )
727 SelectObject( hdc, hPrevBrush );
729 if( !hdc_paint )
730 ReleaseDC( lphc->self, hdc );
733 Free(pText);
736 /***********************************************************************
737 * CBPaintBorder
739 static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc)
741 RECT clientRect;
743 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
745 GetClientRect(lphc->self, &clientRect);
747 else
749 clientRect = lphc->textRect;
751 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
752 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
755 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
758 static LRESULT COMBO_ThemedPaint(HTHEME theme, HEADCOMBO *lphc, HDC hdc)
760 int button_state;
761 RECT frame;
763 /* paint border */
764 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
765 GetClientRect(lphc->self, &frame);
766 else
768 frame = lphc->textRect;
769 InflateRect(&frame, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
770 InflateRect(&frame, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
773 DrawThemeBackground(theme, hdc, 0, IsWindowEnabled(lphc->self) ? CBXS_NORMAL : CBXS_DISABLED, &frame, NULL);
775 /* Paint button */
776 if (!IsRectEmpty(&lphc->buttonRect))
778 if (!IsWindowEnabled(lphc->self))
779 button_state = CBXS_DISABLED;
780 else if (lphc->wState & CBF_BUTTONDOWN)
781 button_state = CBXS_PRESSED;
782 else if (lphc->wState & CBF_HOT)
783 button_state = CBXS_HOT;
784 else
785 button_state = CBXS_NORMAL;
786 DrawThemeBackground(theme, hdc, CP_DROPDOWNBUTTON, button_state, &lphc->buttonRect, NULL);
789 if ((lphc->dwStyle & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST)
790 CBPaintText(lphc, hdc);
791 else
792 InvalidateRect(lphc->hWndEdit, NULL, TRUE);
794 return 0;
797 /***********************************************************************
798 * COMBO_Paint
800 static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc)
802 HBRUSH hPrevBrush, hBkgBrush;
804 TRACE("hdc=%p\n", hdc);
807 * Retrieve the background brush and select it in the
808 * DC.
810 hBkgBrush = COMBO_PrepareColors(lphc, hdc);
811 hPrevBrush = SelectObject(hdc, hBkgBrush);
812 if (!(lphc->wState & CBF_EDIT))
813 FillRect(hdc, &lphc->textRect, hBkgBrush);
816 * In non 3.1 look, there is a sunken border on the combobox
818 CBPaintBorder(lphc, hdc);
820 CBPaintButton(lphc, hdc);
822 /* paint the edit control padding area */
823 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
825 RECT rPadEdit = lphc->textRect;
827 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
829 FrameRect(hdc, &rPadEdit, GetSysColorBrush(COLOR_WINDOW));
832 if (!(lphc->wState & CBF_EDIT))
833 CBPaintText( lphc, hdc );
835 if (hPrevBrush)
836 SelectObject( hdc, hPrevBrush );
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 = Alloc((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 Free( 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 = Alloc((length + 1) * sizeof(WCHAR))))
893 SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText);
897 if( CB_HASSTRINGS(lphc) )
899 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
900 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)L"");
901 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
904 if( lphc->wState & CBF_FOCUSED )
905 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
907 Free( pText );
910 /***********************************************************************
911 * CBDropDown
913 * Show listbox popup.
915 static void CBDropDown( LPHEADCOMBO lphc )
917 HMONITOR monitor;
918 MONITORINFO mon_info;
919 RECT rect,r;
920 int nItems;
921 int nDroppedHeight;
923 TRACE("[%p]: drop down\n", lphc->self);
925 CB_NOTIFY( lphc, CBN_DROPDOWN );
927 /* set selection */
929 lphc->wState |= CBF_DROPPED;
930 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
932 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
934 /* Update edit only if item is in the list */
935 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
936 CBUpdateEdit( lphc, lphc->droppedIndex );
938 else
940 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
942 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
943 lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0);
944 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
947 /* now set popup position */
948 GetWindowRect( lphc->self, &rect );
951 * If it's a dropdown, the listbox is offset
953 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
954 rect.left += COMBO_EDITBUTTONSPACE();
956 /* if the dropped height is greater than the total height of the dropped
957 items list, then force the drop down list height to be the total height
958 of the items in the dropped list */
960 /* And Remove any extra space (Best Fit) */
961 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
962 /* if listbox length has been set directly by its handle */
963 GetWindowRect(lphc->hWndLBox, &r);
964 if (nDroppedHeight < r.bottom - r.top)
965 nDroppedHeight = r.bottom - r.top;
966 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
968 if (nItems > 0)
970 int nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
972 if (lphc->dwStyle & CBS_NOINTEGRALHEIGHT)
974 nDroppedHeight -= 1;
976 else
978 if (nItems > lphc->visibleItems)
979 nItems = lphc->visibleItems;
980 nDroppedHeight = nItems * nIHeight + COMBO_YBORDERSIZE();
984 r.left = rect.left;
985 r.top = rect.bottom;
986 r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left;
987 r.bottom = r.top + nDroppedHeight;
989 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
990 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
991 mon_info.cbSize = sizeof(mon_info);
992 GetMonitorInfoW( monitor, &mon_info );
994 if (r.bottom > mon_info.rcWork.bottom)
996 r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top );
997 r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom );
1000 SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top,
1001 SWP_NOACTIVATE | SWP_SHOWWINDOW );
1004 if( !(lphc->wState & CBF_NOREDRAW) )
1005 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1007 EnableWindow( lphc->hWndLBox, TRUE );
1008 if (GetCapture() != lphc->self)
1009 SetCapture(lphc->hWndLBox);
1012 /***********************************************************************
1013 * CBRollUp
1015 * Hide listbox popup.
1017 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1019 HWND hWnd = lphc->self;
1021 TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
1022 lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
1024 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1026 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1029 if( lphc->wState & CBF_DROPPED )
1031 RECT rect;
1033 lphc->wState &= ~CBF_DROPPED;
1034 ShowWindow( lphc->hWndLBox, SW_HIDE );
1036 if(GetCapture() == lphc->hWndLBox)
1038 ReleaseCapture();
1041 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1043 rect = lphc->buttonRect;
1045 else
1047 if( bButton )
1049 UnionRect( &rect,
1050 &lphc->buttonRect,
1051 &lphc->textRect);
1053 else
1054 rect = lphc->textRect;
1056 bButton = TRUE;
1059 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1060 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1061 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1062 CB_NOTIFY( lphc, CBN_CLOSEUP );
1067 /***********************************************************************
1068 * COMBO_FlipListbox
1070 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1072 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1074 if( lphc->wState & CBF_DROPPED )
1076 CBRollUp( lphc, ok, bRedrawButton );
1077 return FALSE;
1080 CBDropDown( lphc );
1081 return TRUE;
1084 /***********************************************************************
1085 * CBRepaintButton
1087 static void CBRepaintButton( LPHEADCOMBO lphc )
1089 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1090 UpdateWindow(lphc->self);
1093 /***********************************************************************
1094 * COMBO_SetFocus
1096 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1098 if( !(lphc->wState & CBF_FOCUSED) )
1100 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1101 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1103 /* This is wrong. Message sequences seem to indicate that this
1104 is set *after* the notify. */
1105 /* lphc->wState |= CBF_FOCUSED; */
1107 if( !(lphc->wState & CBF_EDIT) )
1108 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1110 CB_NOTIFY( lphc, CBN_SETFOCUS );
1111 lphc->wState |= CBF_FOCUSED;
1115 /***********************************************************************
1116 * COMBO_KillFocus
1118 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1120 HWND hWnd = lphc->self;
1122 if( lphc->wState & CBF_FOCUSED )
1124 CBRollUp( lphc, FALSE, TRUE );
1125 if( IsWindow( hWnd ) )
1127 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1128 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1130 lphc->wState &= ~CBF_FOCUSED;
1132 /* redraw text */
1133 if( !(lphc->wState & CBF_EDIT) )
1134 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1136 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1141 /***********************************************************************
1142 * COMBO_Command
1144 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1146 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1148 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1150 switch( HIWORD(wParam) >> 8 )
1152 case (EN_SETFOCUS >> 8):
1154 TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit );
1156 COMBO_SetFocus( lphc );
1157 break;
1159 case (EN_KILLFOCUS >> 8):
1161 TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit );
1163 /* NOTE: it seems that Windows' edit control sends an
1164 * undocumented message WM_USER + 0x1B instead of this
1165 * notification (only when it happens to be a part of
1166 * the combo). ?? - AK.
1169 COMBO_KillFocus( lphc );
1170 break;
1173 case (EN_CHANGE >> 8):
1175 * In some circumstances (when the selection of the combobox
1176 * is changed for example) we don't want the EN_CHANGE notification
1177 * to be forwarded to the parent of the combobox. This code
1178 * checks a flag that is set in these occasions and ignores the
1179 * notification.
1181 if (lphc->wState & CBF_NOLBSELECT)
1183 lphc->wState &= ~CBF_NOLBSELECT;
1185 else
1187 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1190 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1191 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1192 break;
1194 case (EN_UPDATE >> 8):
1195 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1196 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1197 break;
1199 case (EN_ERRSPACE >> 8):
1200 CB_NOTIFY( lphc, CBN_ERRSPACE );
1203 else if( lphc->hWndLBox == hWnd )
1205 switch( (short)HIWORD(wParam) )
1207 case LBN_ERRSPACE:
1208 CB_NOTIFY( lphc, CBN_ERRSPACE );
1209 break;
1211 case LBN_DBLCLK:
1212 CB_NOTIFY( lphc, CBN_DBLCLK );
1213 break;
1215 case LBN_SELCHANGE:
1216 case LBN_SELCANCEL:
1218 TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState );
1220 /* do not roll up if selection is being tracked
1221 * by arrow keys in the dropdown listbox */
1222 if (!(lphc->wState & CBF_NOROLLUP))
1224 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1226 else lphc->wState &= ~CBF_NOROLLUP;
1228 CB_NOTIFY( lphc, CBN_SELCHANGE );
1230 if( HIWORD(wParam) == LBN_SELCHANGE)
1232 if( lphc->wState & CBF_EDIT )
1233 lphc->wState |= CBF_NOLBSELECT;
1234 CBPaintText( lphc, NULL );
1236 break;
1238 case LBN_SETFOCUS:
1239 case LBN_KILLFOCUS:
1240 /* nothing to do here since ComboLBox always resets the focus to its
1241 * combo/edit counterpart */
1242 break;
1245 return 0;
1248 /***********************************************************************
1249 * COMBO_ItemOp
1251 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1253 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1255 HWND hWnd = lphc->self;
1256 UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
1258 TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
1260 switch( msg )
1262 case WM_DELETEITEM:
1264 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1265 lpIS->CtlType = ODT_COMBOBOX;
1266 lpIS->CtlID = id;
1267 lpIS->hwndItem = hWnd;
1268 break;
1270 case WM_DRAWITEM:
1272 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1273 lpIS->CtlType = ODT_COMBOBOX;
1274 lpIS->CtlID = id;
1275 lpIS->hwndItem = hWnd;
1276 break;
1278 case WM_COMPAREITEM:
1280 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1281 lpIS->CtlType = ODT_COMBOBOX;
1282 lpIS->CtlID = id;
1283 lpIS->hwndItem = hWnd;
1284 break;
1286 case WM_MEASUREITEM:
1288 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1289 lpIS->CtlType = ODT_COMBOBOX;
1290 lpIS->CtlID = id;
1291 break;
1294 return SendMessageW(lphc->owner, msg, id, lParam);
1298 /***********************************************************************
1299 * COMBO_GetTextW
1301 static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
1303 INT length;
1305 if( lphc->wState & CBF_EDIT )
1306 return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf );
1308 /* get it from the listbox */
1310 if (!count || !buf) return 0;
1311 if( lphc->hWndLBox )
1313 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1314 if (idx == LB_ERR) goto error;
1315 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
1316 if (length == LB_ERR) goto error;
1318 /* 'length' is without the terminating character */
1319 if (length >= count)
1321 WCHAR *lpBuffer = Alloc((length + 1) * sizeof(WCHAR));
1322 if (!lpBuffer) goto error;
1323 length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
1325 /* truncate if buffer is too short */
1326 if (length != LB_ERR)
1328 lstrcpynW( buf, lpBuffer, count );
1329 length = count;
1331 Free( lpBuffer );
1333 else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
1335 if (length == LB_ERR) return 0;
1336 return length;
1339 error: /* error - truncate string, return zero */
1340 buf[0] = 0;
1341 return 0;
1344 /***********************************************************************
1345 * CBResetPos
1347 * This function sets window positions according to the updated
1348 * component placement struct.
1350 static void CBResetPos(HEADCOMBO *combo)
1352 BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
1354 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1355 * sizing messages */
1356 if (combo->wState & CBF_EDIT)
1357 SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
1358 combo->textRect.right - combo->textRect.left,
1359 combo->textRect.bottom - combo->textRect.top,
1360 SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0));
1362 SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
1363 combo->droppedRect.right - combo->droppedRect.left,
1364 combo->droppedRect.bottom - combo->droppedRect.top,
1365 SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0));
1367 if (drop)
1369 if (combo->wState & CBF_DROPPED)
1371 combo->wState &= ~CBF_DROPPED;
1372 ShowWindow(combo->hWndLBox, SW_HIDE);
1375 if (!(combo->wState & CBF_NOREDRAW))
1376 RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
1381 /***********************************************************************
1382 * COMBO_Size
1384 static void COMBO_Size( HEADCOMBO *lphc )
1386 if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
1387 return;
1390 * Those controls are always the same height. So we have to make sure
1391 * they are not resized to another value.
1393 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1395 int newComboHeight, curComboHeight, curComboWidth;
1396 RECT rc;
1398 GetWindowRect(lphc->self, &rc);
1399 curComboHeight = rc.bottom - rc.top;
1400 curComboWidth = rc.right - rc.left;
1401 newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
1404 * Resizing a combobox has another side effect, it resizes the dropped
1405 * rectangle as well. However, it does it only if the new height for the
1406 * combobox is more than the height it should have. In other words,
1407 * if the application resizing the combobox only had the intention to resize
1408 * the actual control, for example, to do the layout of a dialog that is
1409 * resized, the height of the dropdown is not changed.
1411 if( curComboHeight > newComboHeight )
1413 TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%ld, oldDropTop=%ld\n",
1414 curComboHeight, newComboHeight, lphc->droppedRect.bottom,
1415 lphc->droppedRect.top);
1416 lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight;
1419 * Restore original height
1421 if (curComboHeight != newComboHeight)
1423 lphc->wState |= CBF_NORESIZE;
1424 SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
1425 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW);
1426 lphc->wState &= ~CBF_NORESIZE;
1430 CBCalcPlacement(lphc);
1432 CBResetPos(lphc);
1436 /***********************************************************************
1437 * COMBO_Font
1439 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1441 lphc->hFont = hFont;
1442 lphc->item_height = combo_get_text_height(lphc);
1445 * Propagate to owned windows.
1447 if( lphc->wState & CBF_EDIT )
1448 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1449 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1452 * Redo the layout of the control.
1454 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1456 CBCalcPlacement(lphc);
1458 CBResetPos(lphc);
1460 else
1462 CBForceDummyResize(lphc);
1467 /***********************************************************************
1468 * COMBO_SetItemHeight
1470 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1472 LRESULT lRet = CB_ERR;
1474 if( index == -1 ) /* set text field height */
1476 if( height < 32768 )
1478 lphc->item_height = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
1481 * Redo the layout of the control.
1483 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1485 CBCalcPlacement(lphc);
1487 CBResetPos(lphc);
1489 else
1491 CBForceDummyResize(lphc);
1494 lRet = height;
1497 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1498 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height);
1499 return lRet;
1502 /***********************************************************************
1503 * COMBO_SelectString
1505 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText)
1507 INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText);
1508 if( index >= 0 )
1510 if( lphc->wState & CBF_EDIT )
1511 CBUpdateEdit( lphc, index );
1512 else
1514 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1517 return (LRESULT)index;
1520 /***********************************************************************
1521 * COMBO_LButtonDown
1523 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1525 POINT pt;
1526 BOOL bButton;
1527 HWND hWnd = lphc->self;
1529 pt.x = (short)LOWORD(lParam);
1530 pt.y = (short)HIWORD(lParam);
1531 bButton = PtInRect(&lphc->buttonRect, pt);
1533 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1534 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1536 lphc->wState |= CBF_BUTTONDOWN;
1537 if( lphc->wState & CBF_DROPPED )
1539 /* got a click to cancel selection */
1541 lphc->wState &= ~CBF_BUTTONDOWN;
1542 CBRollUp( lphc, TRUE, FALSE );
1543 if( !IsWindow( hWnd ) ) return;
1545 if( lphc->wState & CBF_CAPTURE )
1547 lphc->wState &= ~CBF_CAPTURE;
1548 ReleaseCapture();
1551 else
1553 /* drop down the listbox and start tracking */
1555 lphc->wState |= CBF_CAPTURE;
1556 SetCapture( hWnd );
1557 CBDropDown( lphc );
1559 if( bButton ) CBRepaintButton( lphc );
1563 /***********************************************************************
1564 * COMBO_LButtonUp
1566 * Release capture and stop tracking if needed.
1568 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1570 if( lphc->wState & CBF_CAPTURE )
1572 lphc->wState &= ~CBF_CAPTURE;
1573 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1575 INT index = CBUpdateLBox( lphc, TRUE );
1576 /* Update edit only if item is in the list */
1577 if(index >= 0)
1579 lphc->wState |= CBF_NOLBSELECT;
1580 CBUpdateEdit( lphc, index );
1581 lphc->wState &= ~CBF_NOLBSELECT;
1584 ReleaseCapture();
1585 SetCapture(lphc->hWndLBox);
1588 if( lphc->wState & CBF_BUTTONDOWN )
1590 lphc->wState &= ~CBF_BUTTONDOWN;
1591 CBRepaintButton( lphc );
1595 /***********************************************************************
1596 * COMBO_MouseMove
1598 * Two things to do - track combo button and release capture when
1599 * pointer goes into the listbox.
1601 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1603 POINT pt;
1604 RECT lbRect;
1606 pt.x = (short)LOWORD(lParam);
1607 pt.y = (short)HIWORD(lParam);
1609 if( lphc->wState & CBF_BUTTONDOWN )
1611 BOOL bButton;
1613 bButton = PtInRect(&lphc->buttonRect, pt);
1615 if( !bButton )
1617 lphc->wState &= ~CBF_BUTTONDOWN;
1618 CBRepaintButton( lphc );
1622 GetClientRect( lphc->hWndLBox, &lbRect );
1623 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1624 if( PtInRect(&lbRect, pt) )
1626 lphc->wState &= ~CBF_CAPTURE;
1627 ReleaseCapture();
1628 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1630 /* hand over pointer tracking */
1631 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1635 static LRESULT COMBO_MouseLeave(LPHEADCOMBO lphc)
1637 lphc->wState &= ~CBF_HOT;
1638 RedrawWindow(lphc->self, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1639 return 0;
1642 static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi)
1644 if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO)))
1645 return FALSE;
1647 pcbi->rcItem = lphc->textRect;
1648 pcbi->rcButton = lphc->buttonRect;
1649 pcbi->stateButton = 0;
1650 if (lphc->wState & CBF_BUTTONDOWN)
1651 pcbi->stateButton |= STATE_SYSTEM_PRESSED;
1652 if (IsRectEmpty(&lphc->buttonRect))
1653 pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
1654 pcbi->hwndCombo = lphc->self;
1655 pcbi->hwndItem = lphc->hWndEdit;
1656 pcbi->hwndList = lphc->hWndLBox;
1657 return TRUE;
1660 static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1662 HEADCOMBO *lphc = (HEADCOMBO *)GetWindowLongPtrW( hwnd, 0 );
1663 HTHEME theme;
1665 TRACE("[%p]: msg %#x, wp %Ix, lp %Ix\n", hwnd, message, wParam, lParam );
1667 if (!IsWindow(hwnd)) return 0;
1669 if (lphc || message == WM_NCCREATE)
1670 switch(message)
1672 case WM_NCCREATE:
1674 LONG style = ((CREATESTRUCTW *)lParam)->style;
1675 return COMBO_NCCreate(hwnd, style);
1678 case WM_NCDESTROY:
1679 COMBO_NCDestroy(lphc);
1680 break;/* -> DefWindowProc */
1682 case WM_CREATE:
1684 HWND hwndParent;
1685 LONG style;
1687 hwndParent = ((CREATESTRUCTW *)lParam)->hwndParent;
1688 style = ((CREATESTRUCTW *)lParam)->style;
1689 return COMBO_Create(hwnd, lphc, hwndParent, style);
1692 case WM_DESTROY:
1693 theme = GetWindowTheme( hwnd );
1694 CloseThemeData( theme );
1695 break;
1697 case WM_THEMECHANGED:
1698 theme = GetWindowTheme( hwnd );
1699 CloseThemeData( theme );
1700 OpenThemeData( hwnd, WC_COMBOBOXW );
1701 InvalidateRect( hwnd, NULL, TRUE );
1702 break;
1704 case WM_PRINTCLIENT:
1705 case WM_PAINT:
1707 LRESULT ret = 0;
1708 PAINTSTRUCT ps;
1709 HDC hdc;
1711 hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1713 if (hdc && !(lphc->wState & CBF_NOREDRAW))
1715 HTHEME theme = GetWindowTheme(hwnd);
1717 if (theme)
1718 ret = COMBO_ThemedPaint(theme, lphc, hdc);
1719 else
1720 ret = COMBO_Paint(lphc, hdc);
1723 if (!wParam)
1724 EndPaint(hwnd, &ps);
1726 return ret;
1728 case WM_ERASEBKGND:
1729 /* do all painting in WM_PAINT like Windows does */
1730 return 1;
1732 case WM_GETDLGCODE:
1734 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1735 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1737 int vk = (int)((LPMSG)lParam)->wParam;
1739 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1740 result |= DLGC_WANTMESSAGE;
1742 return result;
1745 case WM_SIZE:
1746 COMBO_Size( lphc );
1747 return TRUE;
1749 case WM_SETFONT:
1750 COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
1751 return TRUE;
1753 case WM_GETFONT:
1754 return (LRESULT)lphc->hFont;
1756 case WM_SETFOCUS:
1757 if (lphc->wState & CBF_EDIT)
1759 SetFocus( lphc->hWndEdit );
1760 /* The first time focus is received, select all the text */
1761 if (!(lphc->wState & CBF_BEENFOCUSED))
1763 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1);
1764 lphc->wState |= CBF_BEENFOCUSED;
1767 else
1768 COMBO_SetFocus( lphc );
1769 return TRUE;
1771 case WM_KILLFOCUS:
1773 HWND hwndFocus = (HWND)wParam;
1774 if (!hwndFocus || (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox))
1775 COMBO_KillFocus( lphc );
1776 return TRUE;
1779 case WM_COMMAND:
1780 return COMBO_Command( lphc, wParam, (HWND)lParam );
1782 case WM_GETTEXT:
1783 return COMBO_GetText( lphc, wParam, (LPWSTR)lParam );
1785 case WM_SETTEXT:
1786 case WM_GETTEXTLENGTH:
1787 case WM_CLEAR:
1788 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1790 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1791 if (j == -1) return 0;
1792 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1794 else if ( lphc->wState & CBF_EDIT )
1796 LRESULT ret;
1797 lphc->wState |= CBF_NOEDITNOTIFY;
1798 ret = SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1799 lphc->wState &= ~CBF_NOEDITNOTIFY;
1800 return ret;
1802 else
1803 return CB_ERR;
1805 case WM_CUT:
1806 case WM_PASTE:
1807 case WM_COPY:
1808 if (lphc->wState & CBF_EDIT)
1809 return SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1810 else return CB_ERR;
1812 case WM_DRAWITEM:
1813 case WM_DELETEITEM:
1814 case WM_COMPAREITEM:
1815 case WM_MEASUREITEM:
1816 return COMBO_ItemOp(lphc, message, lParam);
1818 case WM_ENABLE:
1819 if (lphc->wState & CBF_EDIT)
1820 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1821 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1823 /* Force the control to repaint when the enabled state changes. */
1824 InvalidateRect(lphc->self, NULL, TRUE);
1825 return TRUE;
1827 case WM_SETREDRAW:
1828 if (wParam)
1829 lphc->wState &= ~CBF_NOREDRAW;
1830 else
1831 lphc->wState |= CBF_NOREDRAW;
1833 if ( lphc->wState & CBF_EDIT )
1834 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
1835 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
1836 return 0;
1838 case WM_SYSKEYDOWN:
1839 if ( KEYDATA_ALT & HIWORD(lParam) )
1840 if( wParam == VK_UP || wParam == VK_DOWN )
1841 COMBO_FlipListbox( lphc, FALSE, FALSE );
1842 return 0;
1844 case WM_KEYDOWN:
1845 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
1846 (lphc->wState & CBF_DROPPED))
1848 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
1849 return TRUE;
1851 else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
1853 COMBO_FlipListbox( lphc, FALSE, FALSE );
1854 return TRUE;
1856 /* fall through */
1857 case WM_CHAR:
1858 case WM_IME_CHAR:
1860 HWND hwndTarget;
1862 if ( lphc->wState & CBF_EDIT )
1863 hwndTarget = lphc->hWndEdit;
1864 else
1865 hwndTarget = lphc->hWndLBox;
1867 return SendMessageW(hwndTarget, message, wParam, lParam);
1870 case WM_LBUTTONDOWN:
1871 if ( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
1872 if ( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1873 return TRUE;
1875 case WM_LBUTTONUP:
1876 COMBO_LButtonUp( lphc );
1877 return TRUE;
1879 case WM_MOUSEMOVE:
1880 if (!IsRectEmpty(&lphc->buttonRect))
1882 TRACKMOUSEEVENT event;
1883 POINT pt;
1885 pt.x = (short)LOWORD(lParam);
1886 pt.y = (short)HIWORD(lParam);
1888 if (PtInRect(&lphc->buttonRect, pt))
1890 if (!(lphc->wState & CBF_HOT))
1892 lphc->wState |= CBF_HOT;
1893 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1895 event.cbSize = sizeof(TRACKMOUSEEVENT);
1896 event.dwFlags = TME_QUERY;
1897 if (!TrackMouseEvent(&event) || event.hwndTrack != hwnd || !(event.dwFlags & TME_LEAVE))
1899 event.hwndTrack = hwnd;
1900 event.dwFlags = TME_LEAVE;
1901 TrackMouseEvent(&event);
1905 else if (lphc->wState & CBF_HOT)
1907 lphc->wState &= ~CBF_HOT;
1908 RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW);
1912 if ( lphc->wState & CBF_CAPTURE )
1913 COMBO_MouseMove( lphc, wParam, lParam );
1914 return TRUE;
1916 case WM_MOUSELEAVE:
1917 return COMBO_MouseLeave(lphc);
1919 case WM_MOUSEWHEEL:
1920 if (wParam & (MK_SHIFT | MK_CONTROL))
1921 return DefWindowProcW(hwnd, message, wParam, lParam);
1923 if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
1924 if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
1925 return TRUE;
1927 case WM_CTLCOLOR:
1928 case WM_CTLCOLORMSGBOX:
1929 case WM_CTLCOLOREDIT:
1930 case WM_CTLCOLORLISTBOX:
1931 case WM_CTLCOLORBTN:
1932 case WM_CTLCOLORDLG:
1933 case WM_CTLCOLORSCROLLBAR:
1934 case WM_CTLCOLORSTATIC:
1935 return SendMessageW(lphc->owner, message, wParam, lParam);
1937 /* Combo messages */
1938 case CB_ADDSTRING:
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 case CB_INSERTSTRING:
1946 if (lphc->dwStyle & CBS_LOWERCASE)
1947 CharLowerW((LPWSTR)lParam);
1948 else if (lphc->dwStyle & CBS_UPPERCASE)
1949 CharUpperW((LPWSTR)lParam);
1950 return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1952 case CB_DELETESTRING:
1953 return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1955 case CB_SELECTSTRING:
1956 return COMBO_SelectString(lphc, (INT)wParam, lParam);
1958 case CB_FINDSTRING:
1959 return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1961 case CB_FINDSTRINGEXACT:
1962 return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
1964 case CB_SETITEMHEIGHT:
1965 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1967 case CB_GETITEMHEIGHT:
1968 if ((INT)wParam >= 0) /* listbox item */
1969 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1970 return CBGetTextAreaHeight(lphc, FALSE);
1972 case CB_RESETCONTENT:
1973 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
1975 if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc))
1976 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)L"");
1977 else
1978 InvalidateRect(lphc->self, NULL, TRUE);
1979 return TRUE;
1981 case CB_INITSTORAGE:
1982 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1984 case CB_GETHORIZONTALEXTENT:
1985 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1987 case CB_SETHORIZONTALEXTENT:
1988 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1990 case CB_GETTOPINDEX:
1991 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1993 case CB_GETLOCALE:
1994 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1996 case CB_SETLOCALE:
1997 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1999 case CB_SETDROPPEDWIDTH:
2000 if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768)
2001 return CB_ERR;
2003 /* new value must be higher than combobox width */
2004 if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left)
2005 lphc->droppedWidth = wParam;
2006 else if (wParam)
2007 lphc->droppedWidth = 0;
2009 /* recalculate the combobox area */
2010 CBCalcPlacement(lphc);
2012 /* fall through */
2013 case CB_GETDROPPEDWIDTH:
2014 if (lphc->droppedWidth)
2015 return lphc->droppedWidth;
2016 return lphc->droppedRect.right - lphc->droppedRect.left;
2018 case CB_GETDROPPEDCONTROLRECT:
2019 if (lParam)
2020 CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2021 return CB_OKAY;
2023 case CB_GETDROPPEDSTATE:
2024 return (lphc->wState & CBF_DROPPED) != 0;
2026 case CB_DIR:
2027 return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam);
2029 case CB_SHOWDROPDOWN:
2030 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
2032 if (wParam)
2034 if (!(lphc->wState & CBF_DROPPED))
2035 CBDropDown( lphc );
2037 else if (lphc->wState & CBF_DROPPED)
2038 CBRollUp( lphc, FALSE, TRUE );
2040 return TRUE;
2042 case CB_GETCOUNT:
2043 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2045 case CB_GETCURSEL:
2046 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2048 case CB_SETCURSEL:
2049 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2050 if (lParam >= 0)
2051 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2053 /* no LBN_SELCHANGE in this case, update manually */
2054 CBPaintText(lphc, NULL);
2055 lphc->wState &= ~CBF_SELCHANGE;
2056 return lParam;
2058 case CB_GETLBTEXT:
2059 return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2061 case CB_GETLBTEXTLEN:
2062 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2064 case CB_GETITEMDATA:
2065 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2067 case CB_SETITEMDATA:
2068 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2070 case CB_GETEDITSEL:
2071 /* Edit checks passed parameters itself */
2072 if (lphc->wState & CBF_EDIT)
2073 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2074 return CB_ERR;
2076 case CB_SETEDITSEL:
2077 if (lphc->wState & CBF_EDIT)
2078 return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) );
2079 return CB_ERR;
2081 case CB_SETEXTENDEDUI:
2082 if (CB_GETTYPE(lphc) == CBS_SIMPLE )
2083 return CB_ERR;
2084 if (wParam)
2085 lphc->wState |= CBF_EUI;
2086 else
2087 lphc->wState &= ~CBF_EUI;
2088 return CB_OKAY;
2090 case CB_GETEXTENDEDUI:
2091 return (lphc->wState & CBF_EUI) != 0;
2093 case CB_GETCOMBOBOXINFO:
2094 return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
2096 case CB_LIMITTEXT:
2097 if (lphc->wState & CBF_EDIT)
2098 return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
2099 return TRUE;
2101 case CB_GETMINVISIBLE:
2102 return lphc->visibleItems;
2104 case CB_SETMINVISIBLE:
2105 lphc->visibleItems = (INT)wParam;
2106 return TRUE;
2108 default:
2109 if (message >= WM_USER)
2110 WARN("unknown msg WM_USER+%04x, wp %Ix, lp %Ix\n", message - WM_USER, wParam, lParam );
2111 break;
2114 return DefWindowProcW(hwnd, message, wParam, lParam);
2117 void COMBO_Register(void)
2119 WNDCLASSW wndClass;
2121 memset(&wndClass, 0, sizeof(wndClass));
2122 wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
2123 wndClass.lpfnWndProc = COMBO_WindowProc;
2124 wndClass.cbClsExtra = 0;
2125 wndClass.cbWndExtra = sizeof(HEADCOMBO *);
2126 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2127 wndClass.hbrBackground = NULL;
2128 wndClass.lpszClassName = WC_COMBOBOXW;
2129 RegisterClassW(&wndClass);