Make sure that HWND comparisons are always done with full 32-bit
[wine/multimedia.git] / controls / combo.c
blob92dddb2bcb359f5a6bfa0c2e89c96c6fd52cc7cf
1 /*
2 * Combo controls
3 *
4 * Copyright 1997 Alex Korobka
5 *
6 * FIXME: roll up in Netscape 3.01.
7 */
9 #include <string.h>
11 #include "winbase.h"
12 #include "windef.h"
13 #include "wingdi.h"
14 #include "winuser.h"
15 #include "wine/winuser16.h"
16 #include "wine/unicode.h"
17 #include "spy.h"
18 #include "user.h"
19 #include "win.h"
20 #include "controls.h"
21 #include "debugtools.h"
23 DEFAULT_DEBUG_CHANNEL(combo);
25 /* bits in the dwKeyData */
26 #define KEYDATA_ALT 0x2000
27 #define KEYDATA_PREVSTATE 0x4000
30 * Additional combo box definitions
33 #define CB_NOTIFY( lphc, code ) \
34 (SendMessageW((lphc)->owner, WM_COMMAND, \
35 MAKEWPARAM(GetWindowLongA((lphc)->self,GWL_ID), (code)), (lphc)->self))
37 #define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self))
38 #define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE))
39 #define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS)
40 #define CB_HWND( lphc ) ((lphc)->self)
42 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
45 * Drawing globals
47 static HBITMAP hComboBmp = 0;
48 static UINT CBitHeight, CBitWidth;
51 * Look and feel dependant "constants"
54 #define COMBO_YBORDERGAP 5
55 #define COMBO_XBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
56 #define COMBO_YBORDERSIZE() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 2 )
57 #define COMBO_EDITBUTTONSPACE() ( (TWEAK_WineLook == WIN31_LOOK) ? 8 : 0 )
58 #define EDIT_CONTROL_PADDING() ( (TWEAK_WineLook == WIN31_LOOK) ? 0 : 1 )
60 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
61 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
63 /*********************************************************************
64 * combo class descriptor
66 const struct builtin_class_descr COMBO_builtin_class =
68 "ComboBox", /* name */
69 CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS, /* style */
70 ComboWndProcA, /* procA */
71 ComboWndProcW, /* procW */
72 sizeof(HEADCOMBO *), /* extra */
73 IDC_ARROWA, /* cursor */
74 0 /* brush */
78 /***********************************************************************
79 * COMBO_Init
81 * Load combo button bitmap.
83 static BOOL COMBO_Init()
85 HDC hDC;
87 if( hComboBmp ) return TRUE;
88 if( (hDC = CreateCompatibleDC(0)) )
90 BOOL bRet = FALSE;
91 if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) )
93 BITMAP bm;
94 HBITMAP hPrevB;
95 RECT r;
97 GetObjectW( hComboBmp, sizeof(bm), &bm );
98 CBitHeight = bm.bmHeight;
99 CBitWidth = bm.bmWidth;
101 TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
103 hPrevB = SelectObject( hDC, hComboBmp);
104 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
105 InvertRect( hDC, &r );
106 SelectObject( hDC, hPrevB );
107 bRet = TRUE;
109 DeleteDC( hDC );
110 return bRet;
112 return FALSE;
115 /***********************************************************************
116 * COMBO_NCCreate
118 static LRESULT COMBO_NCCreate(HWND hwnd, LONG style)
120 LPHEADCOMBO lphc;
122 if (COMBO_Init() && (lphc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEADCOMBO))) )
124 lphc->self = hwnd;
125 SetWindowLongA( hwnd, 0, (LONG)lphc );
127 /* some braindead apps do try to use scrollbar/border flags */
129 lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
130 SetWindowLongA( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) );
133 * We also have to remove the client edge style to make sure
134 * we don't end-up with a non client area.
136 SetWindowLongA( hwnd, GWL_EXSTYLE,
137 GetWindowLongA( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE );
139 if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
140 lphc->dwStyle |= CBS_HASSTRINGS;
141 if( !(GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) )
142 lphc->wState |= CBF_NOTIFY;
144 TRACE("[0x%p], style = %08x\n", lphc, lphc->dwStyle );
145 return TRUE;
147 return FALSE;
150 /***********************************************************************
151 * COMBO_NCDestroy
153 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
156 if( lphc )
158 TRACE("[%04x]: freeing storage\n", lphc->self);
160 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
161 DestroyWindow( lphc->hWndLBox );
163 SetWindowLongA( lphc->self, 0, 0 );
164 HeapFree( GetProcessHeap(), 0, lphc );
166 return 0;
169 /***********************************************************************
170 * CBGetTextAreaHeight
172 * This method will calculate the height of the text area of the
173 * combobox.
174 * The height of the text area is set in two ways.
175 * It can be set explicitly through a combobox message or through a
176 * WM_MEASUREITEM callback.
177 * If this is not the case, the height is set to 13 dialog units.
178 * This height was determined through experimentation.
180 static INT CBGetTextAreaHeight(
181 HWND hwnd,
182 LPHEADCOMBO lphc)
184 INT iTextItemHeight;
186 if( lphc->editHeight ) /* explicitly set height */
188 iTextItemHeight = lphc->editHeight;
190 else
192 TEXTMETRICW tm;
193 HDC hDC = GetDC(hwnd);
194 HFONT hPrevFont = 0;
195 INT baseUnitY;
197 if (lphc->hFont)
198 hPrevFont = SelectObject( hDC, lphc->hFont );
200 GetTextMetricsW(hDC, &tm);
202 baseUnitY = tm.tmHeight;
204 if( hPrevFont )
205 SelectObject( hDC, hPrevFont );
207 ReleaseDC(hwnd, hDC);
209 iTextItemHeight = ((13 * baseUnitY) / 8);
212 * This "formula" calculates the height of the complete control.
213 * To calculate the height of the text area, we have to remove the
214 * borders.
216 iTextItemHeight -= 2*COMBO_YBORDERSIZE();
220 * Check the ownerdraw case if we haven't asked the parent the size
221 * of the item yet.
223 if ( CB_OWNERDRAWN(lphc) &&
224 (lphc->wState & CBF_MEASUREITEM) )
226 MEASUREITEMSTRUCT measureItem;
227 RECT clientRect;
228 INT originalItemHeight = iTextItemHeight;
229 UINT id = GetWindowLongA( lphc->self, GWL_ID );
232 * We use the client rect for the width of the item.
234 GetClientRect(hwnd, &clientRect);
236 lphc->wState &= ~CBF_MEASUREITEM;
239 * Send a first one to measure the size of the text area
241 measureItem.CtlType = ODT_COMBOBOX;
242 measureItem.CtlID = id;
243 measureItem.itemID = -1;
244 measureItem.itemWidth = clientRect.right;
245 measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
246 measureItem.itemData = 0;
247 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
248 iTextItemHeight = 6 + measureItem.itemHeight;
251 * Send a second one in the case of a fixed ownerdraw list to calculate the
252 * size of the list items. (we basically do this on behalf of the listbox)
254 if (lphc->dwStyle & CBS_OWNERDRAWFIXED)
256 measureItem.CtlType = ODT_COMBOBOX;
257 measureItem.CtlID = id;
258 measureItem.itemID = 0;
259 measureItem.itemWidth = clientRect.right;
260 measureItem.itemHeight = originalItemHeight;
261 measureItem.itemData = 0;
262 SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
263 lphc->fixedOwnerDrawHeight = measureItem.itemHeight;
267 * Keep the size for the next time
269 lphc->editHeight = iTextItemHeight;
272 return iTextItemHeight;
275 /***********************************************************************
276 * CBForceDummyResize
278 * The dummy resize is used for listboxes that have a popup to trigger
279 * a re-arranging of the contents of the combobox and the recalculation
280 * of the size of the "real" control window.
282 static void CBForceDummyResize(
283 LPHEADCOMBO lphc)
285 RECT windowRect;
286 int newComboHeight;
288 newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
290 GetWindowRect(lphc->self, &windowRect);
293 * We have to be careful, resizing a combobox also has the meaning that the
294 * dropped rect will be resized. In this case, we want to trigger a resize
295 * to recalculate layout but we don't want to change the dropped rectangle
296 * So, we pass the height of text area of control as the height.
297 * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
298 * message.
300 SetWindowPos( lphc->self,
301 (HWND)NULL,
302 0, 0,
303 windowRect.right - windowRect.left,
304 newComboHeight,
305 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
308 /***********************************************************************
309 * CBCalcPlacement
311 * Set up component coordinates given valid lphc->RectCombo.
313 static void CBCalcPlacement(
314 HWND hwnd,
315 LPHEADCOMBO lphc,
316 LPRECT lprEdit,
317 LPRECT lprButton,
318 LPRECT lprLB)
321 * Again, start with the client rectangle.
323 GetClientRect(hwnd, lprEdit);
326 * Remove the borders
328 InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
331 * Chop off the bottom part to fit with the height of the text area.
333 lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
336 * The button starts the same vertical position as the text area.
338 CopyRect(lprButton, lprEdit);
341 * If the combobox is "simple" there is no button.
343 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
344 lprButton->left = lprButton->right = lprButton->bottom = 0;
345 else
348 * Let's assume the combobox button is the same width as the
349 * scrollbar button.
350 * size the button horizontally and cut-off the text area.
352 lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
353 lprEdit->right = lprButton->left;
357 * In the case of a dropdown, there is an additional spacing between the
358 * text area and the button.
360 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
362 lprEdit->right -= COMBO_EDITBUTTONSPACE();
366 * If we have an edit control, we space it away from the borders slightly.
368 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
370 InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
374 * Adjust the size of the listbox popup.
376 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
379 * Use the client rectangle to initialize the listbox rectangle
381 GetClientRect(hwnd, lprLB);
384 * Then, chop-off the top part.
386 lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
388 else
391 * Make sure the dropped width is as large as the combobox itself.
393 if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
395 lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
398 * In the case of a dropdown, the popup listbox is offset to the right.
399 * so, we want to make sure it's flush with the right side of the
400 * combobox
402 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
403 lprLB->right -= COMBO_EDITBUTTONSPACE();
405 else
406 lprLB->right = lprLB->left + lphc->droppedWidth;
409 TRACE("\ttext\t= (%i,%i-%i,%i)\n",
410 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
412 TRACE("\tbutton\t= (%i,%i-%i,%i)\n",
413 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
415 TRACE("\tlbox\t= (%i,%i-%i,%i)\n",
416 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
419 /***********************************************************************
420 * CBGetDroppedControlRect
422 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
424 /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner
425 of the combo box and the lower right corner of the listbox */
427 GetWindowRect(lphc->self, lpRect);
429 lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left;
430 lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top;
434 /***********************************************************************
435 * COMBO_WindowPosChanging
437 static LRESULT COMBO_WindowPosChanging(
438 HWND hwnd,
439 LPHEADCOMBO lphc,
440 WINDOWPOS* posChanging)
443 * We need to override the WM_WINDOWPOSCHANGING method to handle all
444 * the non-simple comboboxes. The problem is that those controls are
445 * always the same height. We have to make sure they are not resized
446 * to another value.
448 if ( ( CB_GETTYPE(lphc) != CBS_SIMPLE ) &&
449 ((posChanging->flags & SWP_NOSIZE) == 0) )
451 int newComboHeight;
453 newComboHeight = CBGetTextAreaHeight(hwnd,lphc) +
454 2*COMBO_YBORDERSIZE();
457 * Resizing a combobox has another side effect, it resizes the dropped
458 * rectangle as well. However, it does it only if the new height for the
459 * combobox is different from the height it should have. In other words,
460 * if the application resizing the combobox only had the intention to resize
461 * the actual control, for example, to do the layout of a dialog that is
462 * resized, the height of the dropdown is not changed.
464 if (posChanging->cy != newComboHeight)
466 TRACE("posChanging->cy=%d, newComboHeight=%d, oldbot=%d, oldtop=%d\n",
467 posChanging->cy, newComboHeight, lphc->droppedRect.bottom,
468 lphc->droppedRect.top);
469 lphc->droppedRect.bottom = lphc->droppedRect.top + posChanging->cy - newComboHeight;
471 posChanging->cy = newComboHeight;
475 return 0;
478 /***********************************************************************
479 * COMBO_Create
481 static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style )
483 static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0};
484 static const WCHAR editName[] = {'E','d','i','t',0};
486 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
487 if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
489 lphc->owner = hwndParent;
492 * The item height and dropped width are not set when the control
493 * is created.
495 lphc->droppedWidth = lphc->editHeight = 0;
498 * The first time we go through, we want to measure the ownerdraw item
500 lphc->wState |= CBF_MEASUREITEM;
502 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
504 if( lphc->owner || !(style & WS_VISIBLE) )
506 UINT lbeStyle = 0;
507 UINT lbeExStyle = 0;
510 * Initialize the dropped rect to the size of the client area of the
511 * control and then, force all the areas of the combobox to be
512 * recalculated.
514 GetClientRect( hwnd, &lphc->droppedRect );
515 CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
518 * Adjust the position of the popup listbox if it's necessary
520 if ( CB_GETTYPE(lphc) != CBS_SIMPLE )
522 lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE();
525 * If it's a dropdown, the listbox is offset
527 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
528 lphc->droppedRect.left += COMBO_EDITBUTTONSPACE();
530 ClientToScreen(hwnd, (LPPOINT)&lphc->droppedRect);
531 ClientToScreen(hwnd, (LPPOINT)&lphc->droppedRect.right);
534 /* create listbox popup */
536 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) |
537 (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
539 if( lphc->dwStyle & CBS_SORT )
540 lbeStyle |= LBS_SORT;
541 if( lphc->dwStyle & CBS_HASSTRINGS )
542 lbeStyle |= LBS_HASSTRINGS;
543 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
544 lbeStyle |= LBS_NOINTEGRALHEIGHT;
545 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
546 lbeStyle |= LBS_DISABLENOSCROLL;
548 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
550 lbeStyle |= WS_VISIBLE;
553 * In win 95 look n feel, the listbox in the simple combobox has
554 * the WS_EXCLIENTEDGE style instead of the WS_BORDER style.
556 if (TWEAK_WineLook > WIN31_LOOK)
558 lbeStyle &= ~WS_BORDER;
559 lbeExStyle |= WS_EX_CLIENTEDGE;
563 lphc->hWndLBox = CreateWindowExW(lbeExStyle,
564 clbName,
565 NULL,
566 lbeStyle,
567 lphc->droppedRect.left,
568 lphc->droppedRect.top,
569 lphc->droppedRect.right - lphc->droppedRect.left,
570 lphc->droppedRect.bottom - lphc->droppedRect.top,
571 hwnd, (HMENU)ID_CB_LISTBOX,
572 GetWindowLongA( hwnd, GWL_HINSTANCE ), lphc );
574 if( lphc->hWndLBox )
576 BOOL bEdit = TRUE;
577 lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO;
580 * In Win95 look, the border fo the edit control is
581 * provided by the combobox
583 if (TWEAK_WineLook == WIN31_LOOK)
584 lbeStyle |= WS_BORDER;
586 if( lphc->wState & CBF_EDIT )
588 if( lphc->dwStyle & CBS_OEMCONVERT )
589 lbeStyle |= ES_OEMCONVERT;
590 if( lphc->dwStyle & CBS_AUTOHSCROLL )
591 lbeStyle |= ES_AUTOHSCROLL;
592 if( lphc->dwStyle & CBS_LOWERCASE )
593 lbeStyle |= ES_LOWERCASE;
594 else if( lphc->dwStyle & CBS_UPPERCASE )
595 lbeStyle |= ES_UPPERCASE;
597 if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED;
599 lphc->hWndEdit = CreateWindowExW(0,
600 editName,
601 NULL,
602 lbeStyle,
603 lphc->textRect.left, lphc->textRect.top,
604 lphc->textRect.right - lphc->textRect.left,
605 lphc->textRect.bottom - lphc->textRect.top,
606 hwnd, (HMENU)ID_CB_EDIT,
607 GetWindowLongA( hwnd, GWL_HINSTANCE ), NULL );
609 if( !lphc->hWndEdit )
610 bEdit = FALSE;
613 if( bEdit )
615 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
617 /* Now do the trick with parent */
618 SetParent(lphc->hWndLBox, HWND_DESKTOP);
620 * If the combo is a dropdown, we must resize the control
621 * to fit only the text area and button. To do this,
622 * we send a dummy resize and the WM_WINDOWPOSCHANGING message
623 * will take care of setting the height for us.
625 CBForceDummyResize(lphc);
628 TRACE("init done\n");
629 return hwnd;
631 ERR("edit control failure.\n");
632 } else ERR("listbox failure.\n");
633 } else ERR("no owner for visible combo.\n");
635 /* CreateWindow() will send WM_NCDESTROY to cleanup */
637 return -1;
640 /***********************************************************************
641 * CBPaintButton
643 * Paint combo button (normal, pressed, and disabled states).
645 static void CBPaintButton(
646 LPHEADCOMBO lphc,
647 HDC hdc,
648 RECT rectButton)
650 if( lphc->wState & CBF_NOREDRAW )
651 return;
653 if (TWEAK_WineLook == WIN31_LOOK)
655 UINT x, y;
656 BOOL bBool;
657 HDC hMemDC;
658 HBRUSH hPrevBrush;
659 COLORREF oldTextColor, oldBkColor;
662 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
665 * Draw the button background
667 PatBlt( hdc,
668 rectButton.left,
669 rectButton.top,
670 rectButton.right-rectButton.left,
671 rectButton.bottom-rectButton.top,
672 PATCOPY );
674 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
676 DrawEdge( hdc, &rectButton, EDGE_SUNKEN, BF_RECT );
678 else
680 DrawEdge( hdc, &rectButton, EDGE_RAISED, BF_RECT );
684 * Remove the edge of the button from the rectangle
685 * and calculate the position of the bitmap.
687 InflateRect( &rectButton, -2, -2);
689 x = (rectButton.left + rectButton.right - CBitWidth) >> 1;
690 y = (rectButton.top + rectButton.bottom - CBitHeight) >> 1;
693 hMemDC = CreateCompatibleDC( hdc );
694 SelectObject( hMemDC, hComboBmp );
695 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
696 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
697 RGB(0,0,0) );
698 BitBlt( hdc, x, y, CBitWidth, CBitHeight, hMemDC, 0, 0, SRCCOPY );
699 SetBkColor( hdc, oldBkColor );
700 SetTextColor( hdc, oldTextColor );
701 DeleteDC( hMemDC );
702 SelectObject( hdc, hPrevBrush );
704 else
706 UINT buttonState = DFCS_SCROLLCOMBOBOX;
708 if (lphc->wState & CBF_BUTTONDOWN)
710 buttonState |= DFCS_PUSHED;
713 if (CB_DISABLED(lphc))
715 buttonState |= DFCS_INACTIVE;
718 DrawFrameControl(hdc,
719 &rectButton,
720 DFC_SCROLL,
721 buttonState);
725 /***********************************************************************
726 * CBPaintText
728 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
730 static void CBPaintText(
731 LPHEADCOMBO lphc,
732 HDC hdc,
733 RECT rectEdit)
735 INT id, size = 0;
736 LPWSTR pText = NULL;
738 if( lphc->wState & CBF_NOREDRAW ) return;
740 TRACE("\n");
742 /* follow Windows combobox that sends a bunch of text
743 * inquiries to its listbox while processing WM_PAINT. */
745 if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
747 size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
748 if( (pText = HeapAlloc( GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))) )
750 SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
751 pText[size] = '\0'; /* just in case */
752 } else return;
754 else
755 if( !CB_OWNERDRAWN(lphc) )
756 return;
758 if( lphc->wState & CBF_EDIT )
760 static const WCHAR empty_stringW[] = { 0 };
761 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
762 if( lphc->wState & CBF_FOCUSED )
763 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
765 else /* paint text field ourselves */
767 UINT itemState = ODS_COMBOBOXEDIT;
768 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
771 * Give ourselves some space.
773 InflateRect( &rectEdit, -1, -1 );
775 if( CB_OWNERDRAWN(lphc) )
777 DRAWITEMSTRUCT dis;
778 HRGN clipRegion;
779 UINT ctlid = GetWindowLongA( lphc->self, GWL_ID );
781 /* setup state for DRAWITEM message. Owner will highlight */
782 if ( (lphc->wState & CBF_FOCUSED) &&
783 !(lphc->wState & CBF_DROPPED) )
784 itemState |= ODS_SELECTED | ODS_FOCUS;
787 * Save the current clip region.
788 * To retrieve the clip region, we need to create one "dummy"
789 * clip region.
791 clipRegion = CreateRectRgnIndirect(&rectEdit);
793 if (GetClipRgn(hdc, clipRegion)!=1)
795 DeleteObject(clipRegion);
796 clipRegion=(HRGN)NULL;
799 if (!IsWindowEnabled(lphc->self) & WS_DISABLED) itemState |= ODS_DISABLED;
801 dis.CtlType = ODT_COMBOBOX;
802 dis.CtlID = ctlid;
803 dis.hwndItem = lphc->self;
804 dis.itemAction = ODA_DRAWENTIRE;
805 dis.itemID = id;
806 dis.itemState = itemState;
807 dis.hDC = hdc;
808 dis.rcItem = rectEdit;
809 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
810 (WPARAM)id, 0 );
813 * Clip the DC and have the parent draw the item.
815 IntersectClipRect(hdc,
816 rectEdit.left, rectEdit.top,
817 rectEdit.right, rectEdit.bottom);
819 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
822 * Reset the clipping region.
824 SelectClipRgn(hdc, clipRegion);
826 else
828 static const WCHAR empty_stringW[] = { 0 };
830 if ( (lphc->wState & CBF_FOCUSED) &&
831 !(lphc->wState & CBF_DROPPED) ) {
833 /* highlight */
834 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
835 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
836 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
839 ExtTextOutW( hdc,
840 rectEdit.left + 1,
841 rectEdit.top + 1,
842 ETO_OPAQUE | ETO_CLIPPED,
843 &rectEdit,
844 pText ? pText : empty_stringW , size, NULL );
846 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
847 DrawFocusRect( hdc, &rectEdit );
850 if( hPrevFont )
851 SelectObject(hdc, hPrevFont );
853 if (pText)
854 HeapFree( GetProcessHeap(), 0, pText );
857 /***********************************************************************
858 * CBPaintBorder
860 static void CBPaintBorder(
861 HWND hwnd,
862 LPHEADCOMBO lphc,
863 HDC hdc)
865 RECT clientRect;
867 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
869 GetClientRect(hwnd, &clientRect);
871 else
873 CopyRect(&clientRect, &lphc->textRect);
875 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
876 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
879 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
882 /***********************************************************************
883 * COMBO_PrepareColors
885 * This method will sent the appropriate WM_CTLCOLOR message to
886 * prepare and setup the colors for the combo's DC.
888 * It also returns the brush to use for the background.
890 static HBRUSH COMBO_PrepareColors(
891 LPHEADCOMBO lphc,
892 HDC hDC)
894 HBRUSH hBkgBrush;
897 * Get the background brush for this control.
899 if (CB_DISABLED(lphc))
901 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORSTATIC, hDC, lphc->self );
904 * We have to change the text color since WM_CTLCOLORSTATIC will
905 * set it to the "enabled" color. This is the same behavior as the
906 * edit control
908 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
910 else
912 if (lphc->wState & CBF_EDIT)
914 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLOREDIT, hDC, lphc->self );
916 else
918 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX, hDC, lphc->self );
923 * Catch errors.
925 if( !hBkgBrush )
926 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
928 return hBkgBrush;
931 /***********************************************************************
932 * COMBO_EraseBackground
934 static LRESULT COMBO_EraseBackground(
935 HWND hwnd,
936 LPHEADCOMBO lphc,
937 HDC hParamDC)
939 HBRUSH hBkgBrush;
940 HDC hDC;
942 if(lphc->wState & CBF_EDIT)
943 return TRUE;
945 hDC = (hParamDC) ? hParamDC
946 : GetDC(hwnd);
948 * Retrieve the background brush
950 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
952 FillRect(hDC, &lphc->textRect, hBkgBrush);
954 if (!hParamDC)
955 ReleaseDC(hwnd, hDC);
957 return TRUE;
960 /***********************************************************************
961 * COMBO_Paint
963 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
965 PAINTSTRUCT ps;
966 HDC hDC;
968 hDC = (hParamDC) ? hParamDC
969 : BeginPaint( lphc->self, &ps);
971 TRACE("hdc=%04x\n", hDC);
973 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
975 HBRUSH hPrevBrush, hBkgBrush;
978 * Retrieve the background brush and select it in the
979 * DC.
981 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
983 hPrevBrush = SelectObject( hDC, hBkgBrush );
986 * In non 3.1 look, there is a sunken border on the combobox
988 if (TWEAK_WineLook != WIN31_LOOK)
990 CBPaintBorder(lphc->self, lphc, hDC);
993 if( !IsRectEmpty(&lphc->buttonRect) )
995 CBPaintButton(lphc, hDC, lphc->buttonRect);
998 /* paint the edit control padding area */
999 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
1001 RECT rPadEdit = lphc->textRect;
1003 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
1005 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
1008 if( !(lphc->wState & CBF_EDIT) )
1011 * The text area has a border only in Win 3.1 look.
1013 if (TWEAK_WineLook == WIN31_LOOK)
1015 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1017 Rectangle( hDC,
1018 lphc->textRect.left, lphc->textRect.top,
1019 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
1021 SelectObject( hDC, hPrevPen );
1024 CBPaintText( lphc, hDC, lphc->textRect);
1027 if( hPrevBrush )
1028 SelectObject( hDC, hPrevBrush );
1031 if( !hParamDC )
1032 EndPaint(lphc->self, &ps);
1034 return 0;
1037 /***********************************************************************
1038 * CBUpdateLBox
1040 * Select listbox entry according to the contents of the edit control.
1042 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1044 INT length, idx;
1045 LPWSTR pText = NULL;
1047 idx = LB_ERR;
1048 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
1050 if( length > 0 )
1051 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1053 TRACE("\t edit text length %i\n", length );
1055 if( pText )
1057 if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
1058 else pText[0] = '\0';
1059 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1060 (WPARAM)(-1), (LPARAM)pText );
1061 HeapFree( GetProcessHeap(), 0, pText );
1064 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
1066 /* probably superfluous but Windows sends this too */
1067 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1068 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1070 return idx;
1073 /***********************************************************************
1074 * CBUpdateEdit
1076 * Copy a listbox entry to the edit control.
1078 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1080 INT length;
1081 LPWSTR pText = NULL;
1082 static const WCHAR empty_stringW[] = { 0 };
1084 TRACE("\t %i\n", index );
1086 if( index >= 0 ) /* got an entry */
1088 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1089 if( length )
1091 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1093 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1094 (WPARAM)index, (LPARAM)pText );
1099 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1100 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1101 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1103 if( lphc->wState & CBF_FOCUSED )
1104 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1106 if( pText )
1107 HeapFree( GetProcessHeap(), 0, pText );
1110 /***********************************************************************
1111 * CBDropDown
1113 * Show listbox popup.
1115 static void CBDropDown( LPHEADCOMBO lphc )
1117 RECT rect,r;
1118 int nItems = 0;
1119 int nDroppedHeight;
1121 TRACE("[%04x]: drop down\n", lphc->self);
1123 CB_NOTIFY( lphc, CBN_DROPDOWN );
1125 /* set selection */
1127 lphc->wState |= CBF_DROPPED;
1128 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1130 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1132 /* Update edit only if item is in the list */
1133 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1134 CBUpdateEdit( lphc, lphc->droppedIndex );
1136 else
1138 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1140 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1141 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1142 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1145 /* now set popup position */
1146 GetWindowRect( lphc->self, &rect );
1149 * If it's a dropdown, the listbox is offset
1151 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1152 rect.left += COMBO_EDITBUTTONSPACE();
1154 /* if the dropped height is greater than the total height of the dropped
1155 items list, then force the drop down list height to be the total height
1156 of the items in the dropped list */
1158 /* And Remove any extra space (Best Fit) */
1159 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1160 /* if listbox length has been set directly by its handle */
1161 GetWindowRect(lphc->hWndLBox, &r);
1162 if (nDroppedHeight < r.bottom - r.top)
1163 nDroppedHeight = r.bottom - r.top;
1164 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1166 if (nItems > 0)
1168 int nHeight;
1170 nHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1171 nHeight *= nItems;
1173 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1174 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1177 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1178 if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1179 rect.bottom = rect.top - nDroppedHeight;
1181 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1182 lphc->droppedRect.right - lphc->droppedRect.left,
1183 nDroppedHeight,
1184 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1187 if( !(lphc->wState & CBF_NOREDRAW) )
1188 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1189 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1191 EnableWindow( lphc->hWndLBox, TRUE );
1192 if (GetCapture() != lphc->self)
1193 SetCapture(lphc->hWndLBox);
1196 /***********************************************************************
1197 * CBRollUp
1199 * Hide listbox popup.
1201 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1203 HWND hWnd = lphc->self;
1205 TRACE("[%04x]: sel ok? [%i] dropped? [%i]\n",
1206 lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1208 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1210 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1213 if( lphc->wState & CBF_DROPPED )
1215 RECT rect;
1217 lphc->wState &= ~CBF_DROPPED;
1218 ShowWindow( lphc->hWndLBox, SW_HIDE );
1220 if(GetCapture() == lphc->hWndLBox)
1222 ReleaseCapture();
1225 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1227 rect = lphc->buttonRect;
1229 else
1231 if( bButton )
1233 UnionRect( &rect,
1234 &lphc->buttonRect,
1235 &lphc->textRect);
1237 else
1238 rect = lphc->textRect;
1240 bButton = TRUE;
1243 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1244 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1245 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1246 CB_NOTIFY( lphc, CBN_CLOSEUP );
1251 /***********************************************************************
1252 * COMBO_FlipListbox
1254 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1256 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1258 if( lphc->wState & CBF_DROPPED )
1260 CBRollUp( lphc, ok, bRedrawButton );
1261 return FALSE;
1264 CBDropDown( lphc );
1265 return TRUE;
1268 /***********************************************************************
1269 * CBRepaintButton
1271 static void CBRepaintButton( LPHEADCOMBO lphc )
1273 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1274 UpdateWindow(lphc->self);
1277 /***********************************************************************
1278 * COMBO_SetFocus
1280 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1282 if( !(lphc->wState & CBF_FOCUSED) )
1284 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1285 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1287 /* This is wrong. Message sequences seem to indicate that this
1288 is set *after* the notify. */
1289 /* lphc->wState |= CBF_FOCUSED; */
1291 if( !(lphc->wState & CBF_EDIT) )
1292 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1294 CB_NOTIFY( lphc, CBN_SETFOCUS );
1295 lphc->wState |= CBF_FOCUSED;
1299 /***********************************************************************
1300 * COMBO_KillFocus
1302 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1304 HWND hWnd = lphc->self;
1306 if( lphc->wState & CBF_FOCUSED )
1308 CBRollUp( lphc, FALSE, TRUE );
1309 if( IsWindow( hWnd ) )
1311 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1312 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1314 lphc->wState &= ~CBF_FOCUSED;
1316 /* redraw text */
1317 if( !(lphc->wState & CBF_EDIT) )
1318 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1320 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1325 /***********************************************************************
1326 * COMBO_Command
1328 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1330 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1332 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1334 switch( HIWORD(wParam) >> 8 )
1336 case (EN_SETFOCUS >> 8):
1338 TRACE("[%04x]: edit [%04x] got focus\n",
1339 lphc->self, lphc->hWndEdit );
1341 COMBO_SetFocus( lphc );
1342 break;
1344 case (EN_KILLFOCUS >> 8):
1346 TRACE("[%04x]: edit [%04x] lost focus\n",
1347 lphc->self, lphc->hWndEdit );
1349 /* NOTE: it seems that Windows' edit control sends an
1350 * undocumented message WM_USER + 0x1B instead of this
1351 * notification (only when it happens to be a part of
1352 * the combo). ?? - AK.
1355 COMBO_KillFocus( lphc );
1356 break;
1359 case (EN_CHANGE >> 8):
1361 * In some circumstances (when the selection of the combobox
1362 * is changed for example) we don't wans the EN_CHANGE notification
1363 * to be forwarded to the parent of the combobox. This code
1364 * checks a flag that is set in these occasions and ignores the
1365 * notification.
1367 if (lphc->wState & CBF_NOLBSELECT)
1369 lphc->wState &= ~CBF_NOLBSELECT;
1371 else
1373 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1376 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1377 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1378 break;
1380 case (EN_UPDATE >> 8):
1381 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1382 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1383 break;
1385 case (EN_ERRSPACE >> 8):
1386 CB_NOTIFY( lphc, CBN_ERRSPACE );
1389 else if( lphc->hWndLBox == hWnd )
1391 switch( HIWORD(wParam) )
1393 case LBN_ERRSPACE:
1394 CB_NOTIFY( lphc, CBN_ERRSPACE );
1395 break;
1397 case LBN_DBLCLK:
1398 CB_NOTIFY( lphc, CBN_DBLCLK );
1399 break;
1401 case LBN_SELCHANGE:
1402 case LBN_SELCANCEL:
1404 TRACE("[%04x]: lbox selection change [%04x]\n",
1405 lphc->self, lphc->wState );
1407 if( HIWORD(wParam) == LBN_SELCHANGE)
1409 if( lphc->wState & CBF_EDIT )
1411 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1412 lphc->wState |= CBF_NOLBSELECT;
1413 CBUpdateEdit( lphc, index );
1414 /* select text in edit, as Windows does */
1415 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1417 else
1418 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1421 /* do not roll up if selection is being tracked
1422 * by arrowkeys in the dropdown listbox */
1423 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1425 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1427 else lphc->wState &= ~CBF_NOROLLUP;
1429 CB_NOTIFY( lphc, CBN_SELCHANGE );
1431 /* fall through */
1433 case LBN_SETFOCUS:
1434 case LBN_KILLFOCUS:
1435 /* nothing to do here since ComboLBox always resets the focus to its
1436 * combo/edit counterpart */
1437 break;
1440 return 0;
1443 /***********************************************************************
1444 * COMBO_ItemOp
1446 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1448 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1450 HWND hWnd = lphc->self;
1451 UINT id = GetWindowLongA( hWnd, GWL_ID );
1453 TRACE("[%04x]: ownerdraw op %04x\n", lphc->self, msg );
1455 switch( msg )
1457 case WM_DELETEITEM:
1459 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1460 lpIS->CtlType = ODT_COMBOBOX;
1461 lpIS->CtlID = id;
1462 lpIS->hwndItem = hWnd;
1463 break;
1465 case WM_DRAWITEM:
1467 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1468 lpIS->CtlType = ODT_COMBOBOX;
1469 lpIS->CtlID = id;
1470 lpIS->hwndItem = hWnd;
1471 break;
1473 case WM_COMPAREITEM:
1475 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1476 lpIS->CtlType = ODT_COMBOBOX;
1477 lpIS->CtlID = id;
1478 lpIS->hwndItem = hWnd;
1479 break;
1481 case WM_MEASUREITEM:
1483 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1484 lpIS->CtlType = ODT_COMBOBOX;
1485 lpIS->CtlID = id;
1486 break;
1489 return SendMessageW(lphc->owner, msg, id, lParam);
1492 /***********************************************************************
1493 * COMBO_GetText
1495 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1496 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1498 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, INT N, LPARAM lParam, BOOL unicode)
1500 if( lphc->wState & CBF_EDIT )
1501 return unicode ? SendMessageW(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam) :
1502 SendMessageA(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam);
1504 /* get it from the listbox */
1506 if( lphc->hWndLBox )
1508 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1509 if( idx != LB_ERR )
1511 INT n = 0;
1512 INT length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN,
1513 (WPARAM)idx, 0 );
1515 if(unicode)
1517 LPWSTR lpBuffer, lpText = (LPWSTR)lParam;
1519 /* 'length' is without the terminating character */
1520 if(length >= N)
1521 lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1522 else
1523 lpBuffer = lpText;
1525 if(lpBuffer)
1527 n = SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1529 /* truncate if buffer is too short */
1530 if(length >= N)
1532 if(N && lpText)
1534 if(n != LB_ERR)
1535 strncpyW(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1536 lpText[N - 1] = '\0';
1538 HeapFree( GetProcessHeap(), 0, lpBuffer );
1542 else
1544 LPSTR lpBuffer, lpText = (LPSTR)lParam;
1546 /* 'length' is without the terminating character */
1547 if(length >= N)
1548 lpBuffer = HeapAlloc(GetProcessHeap(), 0, length + 1);
1549 else
1550 lpBuffer = lpText;
1552 if(lpBuffer)
1554 n = SendMessageA(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1556 /* truncate if buffer is too short */
1557 if(length >= N)
1559 if(N && lpText)
1561 if(n != LB_ERR)
1562 strncpy(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1563 lpText[N - 1] = '\0';
1565 HeapFree( GetProcessHeap(), 0, lpBuffer );
1569 if (n<0)
1570 n=0;
1571 else
1572 n++;
1573 return (LRESULT)n;
1576 return 0;
1580 /***********************************************************************
1581 * CBResetPos
1583 * This function sets window positions according to the updated
1584 * component placement struct.
1586 static void CBResetPos(
1587 LPHEADCOMBO lphc,
1588 LPRECT rectEdit,
1589 LPRECT rectLB,
1590 BOOL bRedraw)
1592 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1594 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1595 * sizing messages */
1597 if( lphc->wState & CBF_EDIT )
1598 SetWindowPos( lphc->hWndEdit, 0,
1599 rectEdit->left, rectEdit->top,
1600 rectEdit->right - rectEdit->left,
1601 rectEdit->bottom - rectEdit->top,
1602 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1604 SetWindowPos( lphc->hWndLBox, 0,
1605 rectLB->left, rectLB->top,
1606 rectLB->right - rectLB->left,
1607 rectLB->bottom - rectLB->top,
1608 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1610 if( bDrop )
1612 if( lphc->wState & CBF_DROPPED )
1614 lphc->wState &= ~CBF_DROPPED;
1615 ShowWindow( lphc->hWndLBox, SW_HIDE );
1618 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1619 RedrawWindow( lphc->self, NULL, 0,
1620 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1625 /***********************************************************************
1626 * COMBO_Size
1628 static void COMBO_Size( LPHEADCOMBO lphc )
1630 CBCalcPlacement(lphc->self,
1631 lphc,
1632 &lphc->textRect,
1633 &lphc->buttonRect,
1634 &lphc->droppedRect);
1636 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1640 /***********************************************************************
1641 * COMBO_Font
1643 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1646 * Set the font
1648 lphc->hFont = hFont;
1651 * Propagate to owned windows.
1653 if( lphc->wState & CBF_EDIT )
1654 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1655 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1658 * Redo the layout of the control.
1660 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1662 CBCalcPlacement(lphc->self,
1663 lphc,
1664 &lphc->textRect,
1665 &lphc->buttonRect,
1666 &lphc->droppedRect);
1668 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1670 else
1672 CBForceDummyResize(lphc);
1677 /***********************************************************************
1678 * COMBO_SetItemHeight
1680 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1682 LRESULT lRet = CB_ERR;
1684 if( index == -1 ) /* set text field height */
1686 if( height < 32768 )
1688 lphc->editHeight = height;
1691 * Redo the layout of the control.
1693 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1695 CBCalcPlacement(lphc->self,
1696 lphc,
1697 &lphc->textRect,
1698 &lphc->buttonRect,
1699 &lphc->droppedRect);
1701 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1703 else
1705 CBForceDummyResize(lphc);
1708 lRet = height;
1711 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1712 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1713 (WPARAM)index, (LPARAM)height );
1714 return lRet;
1717 /***********************************************************************
1718 * COMBO_SelectString
1720 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1722 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1723 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1724 if( index >= 0 )
1726 if( lphc->wState & CBF_EDIT )
1727 CBUpdateEdit( lphc, index );
1728 else
1730 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1733 return (LRESULT)index;
1736 /***********************************************************************
1737 * COMBO_LButtonDown
1739 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1741 POINT pt;
1742 BOOL bButton;
1743 HWND hWnd = lphc->self;
1745 pt.x = LOWORD(lParam);
1746 pt.y = HIWORD(lParam);
1747 bButton = PtInRect(&lphc->buttonRect, pt);
1749 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1750 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1752 lphc->wState |= CBF_BUTTONDOWN;
1753 if( lphc->wState & CBF_DROPPED )
1755 /* got a click to cancel selection */
1757 lphc->wState &= ~CBF_BUTTONDOWN;
1758 CBRollUp( lphc, TRUE, FALSE );
1759 if( !IsWindow( hWnd ) ) return;
1761 if( lphc->wState & CBF_CAPTURE )
1763 lphc->wState &= ~CBF_CAPTURE;
1764 ReleaseCapture();
1767 else
1769 /* drop down the listbox and start tracking */
1771 lphc->wState |= CBF_CAPTURE;
1772 SetCapture( hWnd );
1773 CBDropDown( lphc );
1775 if( bButton ) CBRepaintButton( lphc );
1779 /***********************************************************************
1780 * COMBO_LButtonUp
1782 * Release capture and stop tracking if needed.
1784 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1786 if( lphc->wState & CBF_CAPTURE )
1788 lphc->wState &= ~CBF_CAPTURE;
1789 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1791 INT index = CBUpdateLBox( lphc, TRUE );
1792 /* Update edit only if item is in the list */
1793 if(index >= 0)
1795 lphc->wState |= CBF_NOLBSELECT;
1796 CBUpdateEdit( lphc, index );
1797 lphc->wState &= ~CBF_NOLBSELECT;
1800 ReleaseCapture();
1801 SetCapture(lphc->hWndLBox);
1804 if( lphc->wState & CBF_BUTTONDOWN )
1806 lphc->wState &= ~CBF_BUTTONDOWN;
1807 CBRepaintButton( lphc );
1811 /***********************************************************************
1812 * COMBO_MouseMove
1814 * Two things to do - track combo button and release capture when
1815 * pointer goes into the listbox.
1817 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1819 POINT pt;
1820 RECT lbRect;
1822 pt.x = LOWORD(lParam);
1823 pt.y = HIWORD(lParam);
1825 if( lphc->wState & CBF_BUTTONDOWN )
1827 BOOL bButton;
1829 bButton = PtInRect(&lphc->buttonRect, pt);
1831 if( !bButton )
1833 lphc->wState &= ~CBF_BUTTONDOWN;
1834 CBRepaintButton( lphc );
1838 GetClientRect( lphc->hWndLBox, &lbRect );
1839 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1840 if( PtInRect(&lbRect, pt) )
1842 lphc->wState &= ~CBF_CAPTURE;
1843 ReleaseCapture();
1844 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1846 /* hand over pointer tracking */
1847 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1852 /***********************************************************************
1853 * ComboWndProc_common
1855 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1857 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1858 WPARAM wParam, LPARAM lParam, BOOL unicode )
1860 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
1862 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1863 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1865 if( lphc || message == WM_NCCREATE )
1866 switch(message)
1869 /* System messages */
1871 case WM_NCCREATE:
1873 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1874 ((LPCREATESTRUCTA)lParam)->style;
1875 return COMBO_NCCreate(hwnd, style);
1877 case WM_NCDESTROY:
1878 COMBO_NCDestroy(lphc);
1879 break;/* -> DefWindowProc */
1881 case WM_CREATE:
1883 HWND hwndParent;
1884 LONG style;
1885 if(unicode)
1887 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1888 style = ((LPCREATESTRUCTW)lParam)->style;
1890 else
1892 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1893 style = ((LPCREATESTRUCTA)lParam)->style;
1895 return COMBO_Create(hwnd, lphc, hwndParent, style);
1898 case WM_PRINTCLIENT:
1899 if (lParam & PRF_ERASEBKGND)
1900 COMBO_EraseBackground(hwnd, lphc, wParam);
1902 /* Fallthrough */
1903 case WM_PAINT:
1904 /* wParam may contain a valid HDC! */
1905 return COMBO_Paint(lphc, wParam);
1906 case WM_ERASEBKGND:
1907 return COMBO_EraseBackground(hwnd, lphc, wParam);
1908 case WM_GETDLGCODE:
1910 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1911 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1913 int vk = (int)((LPMSG)lParam)->wParam;
1915 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1916 result |= DLGC_WANTMESSAGE;
1918 return result;
1920 case WM_WINDOWPOSCHANGING:
1921 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1922 case WM_WINDOWPOSCHANGED:
1923 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1924 * In that case, the Combobox itself will not be resized, so we won't
1925 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1926 * do it here.
1928 /* fall through */
1929 case WM_SIZE:
1930 if( lphc->hWndLBox &&
1931 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1932 return TRUE;
1933 case WM_SETFONT:
1934 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1935 return TRUE;
1936 case WM_GETFONT:
1937 return (LRESULT)lphc->hFont;
1938 case WM_SETFOCUS:
1939 if( lphc->wState & CBF_EDIT )
1940 SetFocus( lphc->hWndEdit );
1941 else
1942 COMBO_SetFocus( lphc );
1943 return TRUE;
1944 case WM_KILLFOCUS:
1946 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1947 if( !hwndFocus ||
1948 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1949 COMBO_KillFocus( lphc );
1950 return TRUE;
1952 case WM_COMMAND:
1953 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1954 case WM_GETTEXT:
1955 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1956 case WM_SETTEXT:
1957 case WM_GETTEXTLENGTH:
1958 case WM_CLEAR:
1959 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1961 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1962 if (j == -1) return 0;
1963 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1965 else if( lphc->wState & CBF_EDIT )
1967 LRESULT ret;
1968 lphc->wState |= CBF_NOEDITNOTIFY;
1969 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1970 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1971 lphc->wState &= ~CBF_NOEDITNOTIFY;
1972 return ret;
1974 else return CB_ERR;
1975 case WM_CUT:
1976 case WM_PASTE:
1977 case WM_COPY:
1978 if( lphc->wState & CBF_EDIT )
1980 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1981 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1983 else return CB_ERR;
1985 case WM_DRAWITEM:
1986 case WM_DELETEITEM:
1987 case WM_COMPAREITEM:
1988 case WM_MEASUREITEM:
1989 return COMBO_ItemOp(lphc, message, lParam);
1990 case WM_ENABLE:
1991 if( lphc->wState & CBF_EDIT )
1992 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1993 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1995 /* Force the control to repaint when the enabled state changes. */
1996 InvalidateRect(lphc->self, NULL, TRUE);
1997 return TRUE;
1998 case WM_SETREDRAW:
1999 if( wParam )
2000 lphc->wState &= ~CBF_NOREDRAW;
2001 else
2002 lphc->wState |= CBF_NOREDRAW;
2004 if( lphc->wState & CBF_EDIT )
2005 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2006 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2007 return 0;
2008 case WM_SYSKEYDOWN:
2009 if( KEYDATA_ALT & HIWORD(lParam) )
2010 if( wParam == VK_UP || wParam == VK_DOWN )
2011 COMBO_FlipListbox( lphc, FALSE, FALSE );
2012 return 0;
2014 case WM_CHAR:
2015 case WM_KEYDOWN:
2017 HWND hwndTarget;
2019 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2020 (lphc->wState & CBF_DROPPED))
2022 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2023 return TRUE;
2026 if( lphc->wState & CBF_EDIT )
2027 hwndTarget = lphc->hWndEdit;
2028 else
2029 hwndTarget = lphc->hWndLBox;
2031 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2032 SendMessageA(hwndTarget, message, wParam, lParam);
2034 case WM_LBUTTONDOWN:
2035 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2036 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2037 return TRUE;
2038 case WM_LBUTTONUP:
2039 COMBO_LButtonUp( lphc );
2040 return TRUE;
2041 case WM_MOUSEMOVE:
2042 if( lphc->wState & CBF_CAPTURE )
2043 COMBO_MouseMove( lphc, wParam, lParam );
2044 return TRUE;
2046 case WM_MOUSEWHEEL:
2047 if (wParam & (MK_SHIFT | MK_CONTROL))
2048 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2049 DefWindowProcA(hwnd, message, wParam, lParam);
2050 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2051 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2052 return TRUE;
2054 /* Combo messages */
2056 case CB_ADDSTRING16:
2057 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2058 /* fall through */
2059 case CB_ADDSTRING:
2060 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2061 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2062 case CB_INSERTSTRING16:
2063 wParam = (INT)(INT16)wParam;
2064 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2065 /* fall through */
2066 case CB_INSERTSTRING:
2067 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2068 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2069 case CB_DELETESTRING16:
2070 case CB_DELETESTRING:
2071 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2072 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2073 case CB_SELECTSTRING16:
2074 wParam = (INT)(INT16)wParam;
2075 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2076 /* fall through */
2077 case CB_SELECTSTRING:
2078 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2079 case CB_FINDSTRING16:
2080 wParam = (INT)(INT16)wParam;
2081 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2082 /* fall through */
2083 case CB_FINDSTRING:
2084 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2085 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2086 case CB_FINDSTRINGEXACT16:
2087 wParam = (INT)(INT16)wParam;
2088 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2089 /* fall through */
2090 case CB_FINDSTRINGEXACT:
2091 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2092 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2093 case CB_SETITEMHEIGHT16:
2094 wParam = (INT)(INT16)wParam; /* signed integer */
2095 /* fall through */
2096 case CB_SETITEMHEIGHT:
2097 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2098 case CB_GETITEMHEIGHT16:
2099 wParam = (INT)(INT16)wParam;
2100 /* fall through */
2101 case CB_GETITEMHEIGHT:
2102 if( (INT)wParam >= 0 ) /* listbox item */
2103 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2104 return CBGetTextAreaHeight(hwnd, lphc);
2105 case CB_RESETCONTENT16:
2106 case CB_RESETCONTENT:
2107 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2108 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2110 static const WCHAR empty_stringW[] = { 0 };
2111 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2113 else
2114 InvalidateRect(lphc->self, NULL, TRUE);
2115 return TRUE;
2116 case CB_INITSTORAGE:
2117 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2118 case CB_GETHORIZONTALEXTENT:
2119 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2120 case CB_SETHORIZONTALEXTENT:
2121 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2122 case CB_GETTOPINDEX:
2123 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2124 case CB_GETLOCALE:
2125 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2126 case CB_SETLOCALE:
2127 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2128 case CB_GETDROPPEDWIDTH:
2129 if( lphc->droppedWidth )
2130 return lphc->droppedWidth;
2131 return lphc->droppedRect.right - lphc->droppedRect.left;
2132 case CB_SETDROPPEDWIDTH:
2133 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2134 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2135 return CB_ERR;
2136 case CB_GETDROPPEDCONTROLRECT16:
2137 lParam = (LPARAM)MapSL(lParam);
2138 if( lParam )
2140 RECT r;
2141 CBGetDroppedControlRect( lphc, &r );
2142 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2144 return CB_OKAY;
2145 case CB_GETDROPPEDCONTROLRECT:
2146 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2147 return CB_OKAY;
2148 case CB_GETDROPPEDSTATE16:
2149 case CB_GETDROPPEDSTATE:
2150 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2151 case CB_DIR16:
2152 lParam = (LPARAM)MapSL(lParam);
2153 message = LB_DIR16;
2154 /* fall through */
2155 case CB_DIR:
2156 if(message == CB_DIR) message = LB_DIR;
2157 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2158 SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2160 case CB_SHOWDROPDOWN16:
2161 case CB_SHOWDROPDOWN:
2162 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2164 if( wParam )
2166 if( !(lphc->wState & CBF_DROPPED) )
2167 CBDropDown( lphc );
2169 else
2170 if( lphc->wState & CBF_DROPPED )
2171 CBRollUp( lphc, FALSE, TRUE );
2173 return TRUE;
2174 case CB_GETCOUNT16:
2175 case CB_GETCOUNT:
2176 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2177 case CB_GETCURSEL16:
2178 case CB_GETCURSEL:
2179 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2180 case CB_SETCURSEL16:
2181 wParam = (INT)(INT16)wParam;
2182 /* fall through */
2183 case CB_SETCURSEL:
2184 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2185 if( lParam >= 0 )
2186 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2188 /* no LBN_SELCHANGE in this case, update manually */
2189 if( lphc->wState & CBF_EDIT )
2190 CBUpdateEdit( lphc, (INT)wParam );
2191 else
2192 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2193 lphc->wState &= ~CBF_SELCHANGE;
2194 return lParam;
2195 case CB_GETLBTEXT16:
2196 wParam = (INT)(INT16)wParam;
2197 lParam = (LPARAM)MapSL(lParam);
2198 /* fall through */
2199 case CB_GETLBTEXT:
2200 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2201 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2202 case CB_GETLBTEXTLEN16:
2203 wParam = (INT)(INT16)wParam;
2204 /* fall through */
2205 case CB_GETLBTEXTLEN:
2206 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2207 case CB_GETITEMDATA16:
2208 wParam = (INT)(INT16)wParam;
2209 /* fall through */
2210 case CB_GETITEMDATA:
2211 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2212 case CB_SETITEMDATA16:
2213 wParam = (INT)(INT16)wParam;
2214 /* fall through */
2215 case CB_SETITEMDATA:
2216 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2217 case CB_GETEDITSEL16:
2218 wParam = lParam = 0; /* just in case */
2219 /* fall through */
2220 case CB_GETEDITSEL:
2221 /* Edit checks passed parameters itself */
2222 if( lphc->wState & CBF_EDIT )
2223 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2224 return CB_ERR;
2225 case CB_SETEDITSEL16:
2226 case CB_SETEDITSEL:
2227 if( lphc->wState & CBF_EDIT )
2228 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2229 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2230 return CB_ERR;
2231 case CB_SETEXTENDEDUI16:
2232 case CB_SETEXTENDEDUI:
2233 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2234 return CB_ERR;
2235 if( wParam )
2236 lphc->wState |= CBF_EUI;
2237 else lphc->wState &= ~CBF_EUI;
2238 return CB_OKAY;
2239 case CB_GETEXTENDEDUI16:
2240 case CB_GETEXTENDEDUI:
2241 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2243 default:
2244 if (message >= WM_USER)
2245 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2246 message - WM_USER, wParam, lParam );
2247 break;
2249 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2250 DefWindowProcA(hwnd, message, wParam, lParam);
2253 /***********************************************************************
2254 * ComboWndProcA
2256 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2257 * window structs.
2259 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2261 if (!IsWindow(hwnd)) return 0;
2262 return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2265 /***********************************************************************
2266 * ComboWndProcW
2268 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2270 if (!IsWindow(hwnd)) return 0;
2271 return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );