Added implementation of SetItemW.
[wine/multimedia.git] / controls / combo.c
blobf09969a27ecd3bd2bc85328d94e0291535f8e015
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)), (LPARAM)(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("[%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 0;
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 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
751 size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText);
752 pText[size] = '\0'; /* just in case */
753 } else return;
755 else
756 if( !CB_OWNERDRAWN(lphc) )
757 return;
759 if( lphc->wState & CBF_EDIT )
761 static const WCHAR empty_stringW[] = { 0 };
762 if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW );
763 if( lphc->wState & CBF_FOCUSED )
764 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
766 else /* paint text field ourselves */
768 UINT itemState = ODS_COMBOBOXEDIT;
769 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0;
772 * Give ourselves some space.
774 InflateRect( &rectEdit, -1, -1 );
776 if( CB_OWNERDRAWN(lphc) )
778 DRAWITEMSTRUCT dis;
779 HRGN clipRegion;
780 UINT ctlid = GetWindowLongA( lphc->self, GWL_ID );
782 /* setup state for DRAWITEM message. Owner will highlight */
783 if ( (lphc->wState & CBF_FOCUSED) &&
784 !(lphc->wState & CBF_DROPPED) )
785 itemState |= ODS_SELECTED | ODS_FOCUS;
788 * Save the current clip region.
789 * To retrieve the clip region, we need to create one "dummy"
790 * clip region.
792 clipRegion = CreateRectRgnIndirect(&rectEdit);
794 if (GetClipRgn(hdc, clipRegion)!=1)
796 DeleteObject(clipRegion);
797 clipRegion=(HRGN)NULL;
800 if (!IsWindowEnabled(lphc->self) & WS_DISABLED) itemState |= ODS_DISABLED;
802 dis.CtlType = ODT_COMBOBOX;
803 dis.CtlID = ctlid;
804 dis.hwndItem = lphc->self;
805 dis.itemAction = ODA_DRAWENTIRE;
806 dis.itemID = id;
807 dis.itemState = itemState;
808 dis.hDC = hdc;
809 dis.rcItem = rectEdit;
810 dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA,
811 (WPARAM)id, 0 );
814 * Clip the DC and have the parent draw the item.
816 IntersectClipRect(hdc,
817 rectEdit.left, rectEdit.top,
818 rectEdit.right, rectEdit.bottom);
820 SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis );
823 * Reset the clipping region.
825 SelectClipRgn(hdc, clipRegion);
827 else
829 static const WCHAR empty_stringW[] = { 0 };
831 if ( (lphc->wState & CBF_FOCUSED) &&
832 !(lphc->wState & CBF_DROPPED) ) {
834 /* highlight */
835 FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) );
836 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
837 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
840 ExtTextOutW( hdc,
841 rectEdit.left + 1,
842 rectEdit.top + 1,
843 ETO_OPAQUE | ETO_CLIPPED,
844 &rectEdit,
845 pText ? pText : empty_stringW , size, NULL );
847 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
848 DrawFocusRect( hdc, &rectEdit );
851 if( hPrevFont )
852 SelectObject(hdc, hPrevFont );
854 if (pText)
855 HeapFree( GetProcessHeap(), 0, pText );
858 /***********************************************************************
859 * CBPaintBorder
861 static void CBPaintBorder(
862 HWND hwnd,
863 LPHEADCOMBO lphc,
864 HDC hdc)
866 RECT clientRect;
868 if (CB_GETTYPE(lphc) != CBS_SIMPLE)
870 GetClientRect(hwnd, &clientRect);
872 else
874 CopyRect(&clientRect, &lphc->textRect);
876 InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
877 InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE());
880 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT);
883 /***********************************************************************
884 * COMBO_PrepareColors
886 * This method will sent the appropriate WM_CTLCOLOR message to
887 * prepare and setup the colors for the combo's DC.
889 * It also returns the brush to use for the background.
891 static HBRUSH COMBO_PrepareColors(
892 LPHEADCOMBO lphc,
893 HDC hDC)
895 HBRUSH hBkgBrush;
898 * Get the background brush for this control.
900 if (CB_DISABLED(lphc))
902 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORSTATIC, hDC, (LPARAM)lphc->self );
905 * We have to change the text color since WM_CTLCOLORSTATIC will
906 * set it to the "enabled" color. This is the same behavior as the
907 * edit control
909 SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
911 else
913 if (lphc->wState & CBF_EDIT)
915 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLOREDIT, hDC, (LPARAM)lphc->self );
917 else
919 hBkgBrush = SendMessageW(lphc->owner, WM_CTLCOLORLISTBOX, hDC, (LPARAM)lphc->self );
924 * Catch errors.
926 if( !hBkgBrush )
927 hBkgBrush = GetSysColorBrush(COLOR_WINDOW);
929 return hBkgBrush;
932 /***********************************************************************
933 * COMBO_EraseBackground
935 static LRESULT COMBO_EraseBackground(
936 HWND hwnd,
937 LPHEADCOMBO lphc,
938 HDC hParamDC)
940 HBRUSH hBkgBrush;
941 HDC hDC;
943 if(lphc->wState & CBF_EDIT)
944 return TRUE;
946 hDC = (hParamDC) ? hParamDC
947 : GetDC(hwnd);
949 * Retrieve the background brush
951 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
953 FillRect(hDC, &lphc->textRect, hBkgBrush);
955 if (!hParamDC)
956 ReleaseDC(hwnd, hDC);
958 return TRUE;
961 /***********************************************************************
962 * COMBO_Paint
964 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
966 PAINTSTRUCT ps;
967 HDC hDC;
969 hDC = (hParamDC) ? hParamDC
970 : BeginPaint( lphc->self, &ps);
972 TRACE("hdc=%04x\n", hDC);
974 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
976 HBRUSH hPrevBrush, hBkgBrush;
979 * Retrieve the background brush and select it in the
980 * DC.
982 hBkgBrush = COMBO_PrepareColors(lphc, hDC);
984 hPrevBrush = SelectObject( hDC, hBkgBrush );
987 * In non 3.1 look, there is a sunken border on the combobox
989 if (TWEAK_WineLook != WIN31_LOOK)
991 CBPaintBorder(lphc->self, lphc, hDC);
994 if( !IsRectEmpty(&lphc->buttonRect) )
996 CBPaintButton(lphc, hDC, lphc->buttonRect);
999 /* paint the edit control padding area */
1000 if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
1002 RECT rPadEdit = lphc->textRect;
1004 InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING());
1006 FrameRect( hDC, &rPadEdit, GetSysColorBrush(COLOR_WINDOW) );
1009 if( !(lphc->wState & CBF_EDIT) )
1012 * The text area has a border only in Win 3.1 look.
1014 if (TWEAK_WineLook == WIN31_LOOK)
1016 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1018 Rectangle( hDC,
1019 lphc->textRect.left, lphc->textRect.top,
1020 lphc->textRect.right - 1, lphc->textRect.bottom - 1);
1022 SelectObject( hDC, hPrevPen );
1025 CBPaintText( lphc, hDC, lphc->textRect);
1028 if( hPrevBrush )
1029 SelectObject( hDC, hPrevBrush );
1032 if( !hParamDC )
1033 EndPaint(lphc->self, &ps);
1035 return 0;
1038 /***********************************************************************
1039 * CBUpdateLBox
1041 * Select listbox entry according to the contents of the edit control.
1043 static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect )
1045 INT length, idx;
1046 LPWSTR pText = NULL;
1048 idx = LB_ERR;
1049 length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 );
1051 if( length > 0 )
1052 pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1054 TRACE("\t edit text length %i\n", length );
1056 if( pText )
1058 if( length ) GetWindowTextW( lphc->hWndEdit, pText, length + 1);
1059 else pText[0] = '\0';
1060 idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING,
1061 (WPARAM)(-1), (LPARAM)pText );
1062 HeapFree( GetProcessHeap(), 0, pText );
1065 SendMessageW(lphc->hWndLBox, LB_SETCURSEL, (WPARAM)(bSelect ? idx : -1), 0);
1067 /* probably superfluous but Windows sends this too */
1068 SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1069 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)(idx < 0 ? 0 : idx), 0);
1071 return idx;
1074 /***********************************************************************
1075 * CBUpdateEdit
1077 * Copy a listbox entry to the edit control.
1079 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
1081 INT length;
1082 LPWSTR pText = NULL;
1083 static const WCHAR empty_stringW[] = { 0 };
1085 TRACE("\t %i\n", index );
1087 if( index >= 0 ) /* got an entry */
1089 length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
1090 if( length )
1092 if( (pText = HeapAlloc( GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR))) )
1094 SendMessageW(lphc->hWndLBox, LB_GETTEXT,
1095 (WPARAM)index, (LPARAM)pText );
1100 lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1101 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW);
1102 lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT);
1104 if( lphc->wState & CBF_FOCUSED )
1105 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1107 if( pText )
1108 HeapFree( GetProcessHeap(), 0, pText );
1111 /***********************************************************************
1112 * CBDropDown
1114 * Show listbox popup.
1116 static void CBDropDown( LPHEADCOMBO lphc )
1118 RECT rect,r;
1119 int nItems = 0;
1120 int nDroppedHeight;
1122 TRACE("[%04x]: drop down\n", lphc->self);
1124 CB_NOTIFY( lphc, CBN_DROPDOWN );
1126 /* set selection */
1128 lphc->wState |= CBF_DROPPED;
1129 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1131 lphc->droppedIndex = CBUpdateLBox( lphc, TRUE );
1133 /* Update edit only if item is in the list */
1134 if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0)
1135 CBUpdateEdit( lphc, lphc->droppedIndex );
1137 else
1139 lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1141 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX,
1142 (WPARAM)(lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex), 0 );
1143 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1146 /* now set popup position */
1147 GetWindowRect( lphc->self, &rect );
1150 * If it's a dropdown, the listbox is offset
1152 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1153 rect.left += COMBO_EDITBUTTONSPACE();
1155 /* if the dropped height is greater than the total height of the dropped
1156 items list, then force the drop down list height to be the total height
1157 of the items in the dropped list */
1159 /* And Remove any extra space (Best Fit) */
1160 nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top;
1161 /* if listbox length has been set directly by its handle */
1162 GetWindowRect(lphc->hWndLBox, &r);
1163 if (nDroppedHeight < r.bottom - r.top)
1164 nDroppedHeight = r.bottom - r.top;
1165 nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1167 if (nItems > 0)
1169 int nHeight;
1171 nHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0);
1172 nHeight *= nItems;
1174 if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE())
1175 nDroppedHeight = nHeight + COMBO_YBORDERSIZE();
1178 /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/
1179 if( (rect.bottom + nDroppedHeight) >= GetSystemMetrics( SM_CYSCREEN ) )
1180 rect.bottom = rect.top - nDroppedHeight;
1182 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.bottom,
1183 lphc->droppedRect.right - lphc->droppedRect.left,
1184 nDroppedHeight,
1185 SWP_NOACTIVATE | SWP_SHOWWINDOW);
1188 if( !(lphc->wState & CBF_NOREDRAW) )
1189 RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE |
1190 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1192 EnableWindow( lphc->hWndLBox, TRUE );
1193 if (GetCapture() != lphc->self)
1194 SetCapture(lphc->hWndLBox);
1197 /***********************************************************************
1198 * CBRollUp
1200 * Hide listbox popup.
1202 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
1204 HWND hWnd = lphc->self;
1206 TRACE("[%04x]: sel ok? [%i] dropped? [%i]\n",
1207 lphc->self, (INT)ok, (INT)(lphc->wState & CBF_DROPPED));
1209 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
1211 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
1214 if( lphc->wState & CBF_DROPPED )
1216 RECT rect;
1218 lphc->wState &= ~CBF_DROPPED;
1219 ShowWindow( lphc->hWndLBox, SW_HIDE );
1221 if(GetCapture() == lphc->hWndLBox)
1223 ReleaseCapture();
1226 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1228 rect = lphc->buttonRect;
1230 else
1232 if( bButton )
1234 UnionRect( &rect,
1235 &lphc->buttonRect,
1236 &lphc->textRect);
1238 else
1239 rect = lphc->textRect;
1241 bButton = TRUE;
1244 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
1245 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
1246 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
1247 CB_NOTIFY( lphc, CBN_CLOSEUP );
1252 /***********************************************************************
1253 * COMBO_FlipListbox
1255 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
1257 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
1259 if( lphc->wState & CBF_DROPPED )
1261 CBRollUp( lphc, ok, bRedrawButton );
1262 return FALSE;
1265 CBDropDown( lphc );
1266 return TRUE;
1269 /***********************************************************************
1270 * CBRepaintButton
1272 static void CBRepaintButton( LPHEADCOMBO lphc )
1274 InvalidateRect(lphc->self, &lphc->buttonRect, TRUE);
1275 UpdateWindow(lphc->self);
1278 /***********************************************************************
1279 * COMBO_SetFocus
1281 static void COMBO_SetFocus( LPHEADCOMBO lphc )
1283 if( !(lphc->wState & CBF_FOCUSED) )
1285 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1286 SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0);
1288 /* This is wrong. Message sequences seem to indicate that this
1289 is set *after* the notify. */
1290 /* lphc->wState |= CBF_FOCUSED; */
1292 if( !(lphc->wState & CBF_EDIT) )
1293 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1295 CB_NOTIFY( lphc, CBN_SETFOCUS );
1296 lphc->wState |= CBF_FOCUSED;
1300 /***********************************************************************
1301 * COMBO_KillFocus
1303 static void COMBO_KillFocus( LPHEADCOMBO lphc )
1305 HWND hWnd = lphc->self;
1307 if( lphc->wState & CBF_FOCUSED )
1309 CBRollUp( lphc, FALSE, TRUE );
1310 if( IsWindow( hWnd ) )
1312 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
1313 SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0);
1315 lphc->wState &= ~CBF_FOCUSED;
1317 /* redraw text */
1318 if( !(lphc->wState & CBF_EDIT) )
1319 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1321 CB_NOTIFY( lphc, CBN_KILLFOCUS );
1326 /***********************************************************************
1327 * COMBO_Command
1329 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
1331 if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
1333 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
1335 switch( HIWORD(wParam) >> 8 )
1337 case (EN_SETFOCUS >> 8):
1339 TRACE("[%04x]: edit [%04x] got focus\n",
1340 lphc->self, lphc->hWndEdit );
1342 COMBO_SetFocus( lphc );
1343 break;
1345 case (EN_KILLFOCUS >> 8):
1347 TRACE("[%04x]: edit [%04x] lost focus\n",
1348 lphc->self, lphc->hWndEdit );
1350 /* NOTE: it seems that Windows' edit control sends an
1351 * undocumented message WM_USER + 0x1B instead of this
1352 * notification (only when it happens to be a part of
1353 * the combo). ?? - AK.
1356 COMBO_KillFocus( lphc );
1357 break;
1360 case (EN_CHANGE >> 8):
1362 * In some circumstances (when the selection of the combobox
1363 * is changed for example) we don't wans the EN_CHANGE notification
1364 * to be forwarded to the parent of the combobox. This code
1365 * checks a flag that is set in these occasions and ignores the
1366 * notification.
1368 if (lphc->wState & CBF_NOLBSELECT)
1370 lphc->wState &= ~CBF_NOLBSELECT;
1372 else
1374 CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED );
1377 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1378 CB_NOTIFY( lphc, CBN_EDITCHANGE );
1379 break;
1381 case (EN_UPDATE >> 8):
1382 if (!(lphc->wState & CBF_NOEDITNOTIFY))
1383 CB_NOTIFY( lphc, CBN_EDITUPDATE );
1384 break;
1386 case (EN_ERRSPACE >> 8):
1387 CB_NOTIFY( lphc, CBN_ERRSPACE );
1390 else if( lphc->hWndLBox == hWnd )
1392 switch( HIWORD(wParam) )
1394 case LBN_ERRSPACE:
1395 CB_NOTIFY( lphc, CBN_ERRSPACE );
1396 break;
1398 case LBN_DBLCLK:
1399 CB_NOTIFY( lphc, CBN_DBLCLK );
1400 break;
1402 case LBN_SELCHANGE:
1403 case LBN_SELCANCEL:
1405 TRACE("[%04x]: lbox selection change [%04x]\n",
1406 lphc->self, lphc->wState );
1408 if( HIWORD(wParam) == LBN_SELCHANGE)
1410 if( lphc->wState & CBF_EDIT )
1412 INT index = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1413 lphc->wState |= CBF_NOLBSELECT;
1414 CBUpdateEdit( lphc, index );
1415 /* select text in edit, as Windows does */
1416 SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
1418 else
1419 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1422 /* do not roll up if selection is being tracked
1423 * by arrowkeys in the dropdown listbox */
1424 if( ((lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP)) )
1426 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
1428 else lphc->wState &= ~CBF_NOROLLUP;
1430 CB_NOTIFY( lphc, CBN_SELCHANGE );
1432 /* fall through */
1434 case LBN_SETFOCUS:
1435 case LBN_KILLFOCUS:
1436 /* nothing to do here since ComboLBox always resets the focus to its
1437 * combo/edit counterpart */
1438 break;
1441 return 0;
1444 /***********************************************************************
1445 * COMBO_ItemOp
1447 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
1449 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
1451 HWND hWnd = lphc->self;
1452 UINT id = GetWindowLongA( hWnd, GWL_ID );
1454 TRACE("[%04x]: ownerdraw op %04x\n", lphc->self, msg );
1456 switch( msg )
1458 case WM_DELETEITEM:
1460 DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
1461 lpIS->CtlType = ODT_COMBOBOX;
1462 lpIS->CtlID = id;
1463 lpIS->hwndItem = hWnd;
1464 break;
1466 case WM_DRAWITEM:
1468 DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
1469 lpIS->CtlType = ODT_COMBOBOX;
1470 lpIS->CtlID = id;
1471 lpIS->hwndItem = hWnd;
1472 break;
1474 case WM_COMPAREITEM:
1476 COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
1477 lpIS->CtlType = ODT_COMBOBOX;
1478 lpIS->CtlID = id;
1479 lpIS->hwndItem = hWnd;
1480 break;
1482 case WM_MEASUREITEM:
1484 MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
1485 lpIS->CtlType = ODT_COMBOBOX;
1486 lpIS->CtlID = id;
1487 break;
1490 return SendMessageW(lphc->owner, msg, id, lParam);
1493 /***********************************************************************
1494 * COMBO_GetText
1496 * NOTE! LB_GETTEXT does not count terminating \0, WM_GETTEXT does.
1497 * also LB_GETTEXT might return values < 0, WM_GETTEXT doesn't.
1499 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, INT N, LPARAM lParam, BOOL unicode)
1501 if( lphc->wState & CBF_EDIT )
1502 return unicode ? SendMessageW(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam) :
1503 SendMessageA(lphc->hWndEdit, WM_GETTEXT, (WPARAM)N, lParam);
1505 /* get it from the listbox */
1507 if( lphc->hWndLBox )
1509 INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1510 if( idx != LB_ERR )
1512 INT n = 0;
1513 INT length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN,
1514 (WPARAM)idx, 0 );
1516 if(unicode)
1518 LPWSTR lpBuffer, lpText = (LPWSTR)lParam;
1520 /* 'length' is without the terminating character */
1521 if(length >= N)
1522 lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
1523 else
1524 lpBuffer = lpText;
1526 if(lpBuffer)
1528 n = SendMessageW(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1530 /* truncate if buffer is too short */
1531 if(length >= N)
1533 if(N && lpText)
1535 if(n != LB_ERR)
1536 strncpyW(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1537 lpText[N - 1] = '\0';
1539 HeapFree( GetProcessHeap(), 0, lpBuffer );
1543 else
1545 LPSTR lpBuffer, lpText = (LPSTR)lParam;
1547 /* 'length' is without the terminating character */
1548 if(length >= N)
1549 lpBuffer = HeapAlloc(GetProcessHeap(), 0, length + 1);
1550 else
1551 lpBuffer = lpText;
1553 if(lpBuffer)
1555 n = SendMessageA(lphc->hWndLBox, LB_GETTEXT, (WPARAM)idx, (LPARAM)lpBuffer);
1557 /* truncate if buffer is too short */
1558 if(length >= N)
1560 if(N && lpText)
1562 if(n != LB_ERR)
1563 strncpy(lpText, lpBuffer, (N > n) ? n+1 : N-1);
1564 lpText[N - 1] = '\0';
1566 HeapFree( GetProcessHeap(), 0, lpBuffer );
1570 if (n<0)
1571 n=0;
1572 else
1573 n++;
1574 return (LRESULT)n;
1577 return 0;
1581 /***********************************************************************
1582 * CBResetPos
1584 * This function sets window positions according to the updated
1585 * component placement struct.
1587 static void CBResetPos(
1588 LPHEADCOMBO lphc,
1589 LPRECT rectEdit,
1590 LPRECT rectLB,
1591 BOOL bRedraw)
1593 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1595 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1596 * sizing messages */
1598 if( lphc->wState & CBF_EDIT )
1599 SetWindowPos( lphc->hWndEdit, 0,
1600 rectEdit->left, rectEdit->top,
1601 rectEdit->right - rectEdit->left,
1602 rectEdit->bottom - rectEdit->top,
1603 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1605 SetWindowPos( lphc->hWndLBox, 0,
1606 rectLB->left, rectLB->top,
1607 rectLB->right - rectLB->left,
1608 rectLB->bottom - rectLB->top,
1609 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1611 if( bDrop )
1613 if( lphc->wState & CBF_DROPPED )
1615 lphc->wState &= ~CBF_DROPPED;
1616 ShowWindow( lphc->hWndLBox, SW_HIDE );
1619 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1620 RedrawWindow( lphc->self, NULL, 0,
1621 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1626 /***********************************************************************
1627 * COMBO_Size
1629 static void COMBO_Size( LPHEADCOMBO lphc )
1631 CBCalcPlacement(lphc->self,
1632 lphc,
1633 &lphc->textRect,
1634 &lphc->buttonRect,
1635 &lphc->droppedRect);
1637 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1641 /***********************************************************************
1642 * COMBO_Font
1644 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1647 * Set the font
1649 lphc->hFont = hFont;
1652 * Propagate to owned windows.
1654 if( lphc->wState & CBF_EDIT )
1655 SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw);
1656 SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw);
1659 * Redo the layout of the control.
1661 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1663 CBCalcPlacement(lphc->self,
1664 lphc,
1665 &lphc->textRect,
1666 &lphc->buttonRect,
1667 &lphc->droppedRect);
1669 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1671 else
1673 CBForceDummyResize(lphc);
1678 /***********************************************************************
1679 * COMBO_SetItemHeight
1681 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1683 LRESULT lRet = CB_ERR;
1685 if( index == -1 ) /* set text field height */
1687 if( height < 32768 )
1689 lphc->editHeight = height;
1692 * Redo the layout of the control.
1694 if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
1696 CBCalcPlacement(lphc->self,
1697 lphc,
1698 &lphc->textRect,
1699 &lphc->buttonRect,
1700 &lphc->droppedRect);
1702 CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
1704 else
1706 CBForceDummyResize(lphc);
1709 lRet = height;
1712 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1713 lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT,
1714 (WPARAM)index, (LPARAM)height );
1715 return lRet;
1718 /***********************************************************************
1719 * COMBO_SelectString
1721 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText, BOOL unicode )
1723 INT index = unicode ? SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText) :
1724 SendMessageA(lphc->hWndLBox, LB_SELECTSTRING, (WPARAM)start, pText);
1725 if( index >= 0 )
1727 if( lphc->wState & CBF_EDIT )
1728 CBUpdateEdit( lphc, index );
1729 else
1731 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
1734 return (LRESULT)index;
1737 /***********************************************************************
1738 * COMBO_LButtonDown
1740 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1742 POINT pt;
1743 BOOL bButton;
1744 HWND hWnd = lphc->self;
1746 pt.x = LOWORD(lParam);
1747 pt.y = HIWORD(lParam);
1748 bButton = PtInRect(&lphc->buttonRect, pt);
1750 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1751 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1753 lphc->wState |= CBF_BUTTONDOWN;
1754 if( lphc->wState & CBF_DROPPED )
1756 /* got a click to cancel selection */
1758 lphc->wState &= ~CBF_BUTTONDOWN;
1759 CBRollUp( lphc, TRUE, FALSE );
1760 if( !IsWindow( hWnd ) ) return;
1762 if( lphc->wState & CBF_CAPTURE )
1764 lphc->wState &= ~CBF_CAPTURE;
1765 ReleaseCapture();
1768 else
1770 /* drop down the listbox and start tracking */
1772 lphc->wState |= CBF_CAPTURE;
1773 SetCapture( hWnd );
1774 CBDropDown( lphc );
1776 if( bButton ) CBRepaintButton( lphc );
1780 /***********************************************************************
1781 * COMBO_LButtonUp
1783 * Release capture and stop tracking if needed.
1785 static void COMBO_LButtonUp( LPHEADCOMBO lphc )
1787 if( lphc->wState & CBF_CAPTURE )
1789 lphc->wState &= ~CBF_CAPTURE;
1790 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1792 INT index = CBUpdateLBox( lphc, TRUE );
1793 /* Update edit only if item is in the list */
1794 if(index >= 0)
1796 lphc->wState |= CBF_NOLBSELECT;
1797 CBUpdateEdit( lphc, index );
1798 lphc->wState &= ~CBF_NOLBSELECT;
1801 ReleaseCapture();
1802 SetCapture(lphc->hWndLBox);
1805 if( lphc->wState & CBF_BUTTONDOWN )
1807 lphc->wState &= ~CBF_BUTTONDOWN;
1808 CBRepaintButton( lphc );
1812 /***********************************************************************
1813 * COMBO_MouseMove
1815 * Two things to do - track combo button and release capture when
1816 * pointer goes into the listbox.
1818 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1820 POINT pt;
1821 RECT lbRect;
1823 pt.x = LOWORD(lParam);
1824 pt.y = HIWORD(lParam);
1826 if( lphc->wState & CBF_BUTTONDOWN )
1828 BOOL bButton;
1830 bButton = PtInRect(&lphc->buttonRect, pt);
1832 if( !bButton )
1834 lphc->wState &= ~CBF_BUTTONDOWN;
1835 CBRepaintButton( lphc );
1839 GetClientRect( lphc->hWndLBox, &lbRect );
1840 MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 );
1841 if( PtInRect(&lbRect, pt) )
1843 lphc->wState &= ~CBF_CAPTURE;
1844 ReleaseCapture();
1845 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE );
1847 /* hand over pointer tracking */
1848 SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam);
1853 /***********************************************************************
1854 * ComboWndProc_common
1856 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1858 static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
1859 WPARAM wParam, LPARAM lParam, BOOL unicode )
1861 LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongA( hwnd, 0 );
1863 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
1864 hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
1866 if( lphc || message == WM_NCCREATE )
1867 switch(message)
1870 /* System messages */
1872 case WM_NCCREATE:
1874 LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
1875 ((LPCREATESTRUCTA)lParam)->style;
1876 return COMBO_NCCreate(hwnd, style);
1878 case WM_NCDESTROY:
1879 COMBO_NCDestroy(lphc);
1880 break;/* -> DefWindowProc */
1882 case WM_CREATE:
1884 HWND hwndParent;
1885 LONG style;
1886 if(unicode)
1888 hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
1889 style = ((LPCREATESTRUCTW)lParam)->style;
1891 else
1893 hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
1894 style = ((LPCREATESTRUCTA)lParam)->style;
1896 return COMBO_Create(hwnd, lphc, hwndParent, style);
1899 case WM_PRINTCLIENT:
1900 if (lParam & PRF_ERASEBKGND)
1901 COMBO_EraseBackground(hwnd, lphc, wParam);
1903 /* Fallthrough */
1904 case WM_PAINT:
1905 /* wParam may contain a valid HDC! */
1906 return COMBO_Paint(lphc, wParam);
1907 case WM_ERASEBKGND:
1908 return COMBO_EraseBackground(hwnd, lphc, wParam);
1909 case WM_GETDLGCODE:
1911 LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS;
1912 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
1914 int vk = (int)((LPMSG)lParam)->wParam;
1916 if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
1917 result |= DLGC_WANTMESSAGE;
1919 return result;
1921 case WM_WINDOWPOSCHANGING:
1922 return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
1923 case WM_WINDOWPOSCHANGED:
1924 /* SetWindowPos can be called on a Combobox to resize its Listbox.
1925 * In that case, the Combobox itself will not be resized, so we won't
1926 * get a WM_SIZE. Since we still want to update the Listbox, we have to
1927 * do it here.
1929 /* fall through */
1930 case WM_SIZE:
1931 if( lphc->hWndLBox &&
1932 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1933 return TRUE;
1934 case WM_SETFONT:
1935 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1936 return TRUE;
1937 case WM_GETFONT:
1938 return (LRESULT)lphc->hFont;
1939 case WM_SETFOCUS:
1940 if( lphc->wState & CBF_EDIT )
1941 SetFocus( lphc->hWndEdit );
1942 else
1943 COMBO_SetFocus( lphc );
1944 return TRUE;
1945 case WM_KILLFOCUS:
1947 HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
1948 if( !hwndFocus ||
1949 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1950 COMBO_KillFocus( lphc );
1951 return TRUE;
1953 case WM_COMMAND:
1954 return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );
1955 case WM_GETTEXT:
1956 return COMBO_GetText( lphc, (INT)wParam, lParam, unicode );
1957 case WM_SETTEXT:
1958 case WM_GETTEXTLENGTH:
1959 case WM_CLEAR:
1960 if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
1962 int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1963 if (j == -1) return 0;
1964 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
1966 else if( lphc->wState & CBF_EDIT )
1968 LRESULT ret;
1969 lphc->wState |= CBF_NOEDITNOTIFY;
1970 ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1971 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1972 lphc->wState &= ~CBF_NOEDITNOTIFY;
1973 return ret;
1975 else return CB_ERR;
1976 case WM_CUT:
1977 case WM_PASTE:
1978 case WM_COPY:
1979 if( lphc->wState & CBF_EDIT )
1981 return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
1982 SendMessageA(lphc->hWndEdit, message, wParam, lParam);
1984 else return CB_ERR;
1986 case WM_DRAWITEM:
1987 case WM_DELETEITEM:
1988 case WM_COMPAREITEM:
1989 case WM_MEASUREITEM:
1990 return COMBO_ItemOp(lphc, message, lParam);
1991 case WM_ENABLE:
1992 if( lphc->wState & CBF_EDIT )
1993 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1994 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1996 /* Force the control to repaint when the enabled state changes. */
1997 InvalidateRect(lphc->self, NULL, TRUE);
1998 return TRUE;
1999 case WM_SETREDRAW:
2000 if( wParam )
2001 lphc->wState &= ~CBF_NOREDRAW;
2002 else
2003 lphc->wState |= CBF_NOREDRAW;
2005 if( lphc->wState & CBF_EDIT )
2006 SendMessageW(lphc->hWndEdit, message, wParam, lParam);
2007 SendMessageW(lphc->hWndLBox, message, wParam, lParam);
2008 return 0;
2009 case WM_SYSKEYDOWN:
2010 if( KEYDATA_ALT & HIWORD(lParam) )
2011 if( wParam == VK_UP || wParam == VK_DOWN )
2012 COMBO_FlipListbox( lphc, FALSE, FALSE );
2013 return 0;
2015 case WM_CHAR:
2016 case WM_KEYDOWN:
2018 HWND hwndTarget;
2020 if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
2021 (lphc->wState & CBF_DROPPED))
2023 CBRollUp( lphc, wParam == VK_RETURN, FALSE );
2024 return TRUE;
2027 if( lphc->wState & CBF_EDIT )
2028 hwndTarget = lphc->hWndEdit;
2029 else
2030 hwndTarget = lphc->hWndLBox;
2032 return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
2033 SendMessageA(hwndTarget, message, wParam, lParam);
2035 case WM_LBUTTONDOWN:
2036 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
2037 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
2038 return TRUE;
2039 case WM_LBUTTONUP:
2040 COMBO_LButtonUp( lphc );
2041 return TRUE;
2042 case WM_MOUSEMOVE:
2043 if( lphc->wState & CBF_CAPTURE )
2044 COMBO_MouseMove( lphc, wParam, lParam );
2045 return TRUE;
2047 case WM_MOUSEWHEEL:
2048 if (wParam & (MK_SHIFT | MK_CONTROL))
2049 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2050 DefWindowProcA(hwnd, message, wParam, lParam);
2051 if (SHIWORD(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
2052 if (SHIWORD(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
2053 return TRUE;
2055 /* Combo messages */
2057 case CB_ADDSTRING16:
2058 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2059 /* fall through */
2060 case CB_ADDSTRING:
2061 return unicode ? SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam) :
2062 SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
2063 case CB_INSERTSTRING16:
2064 wParam = (INT)(INT16)wParam;
2065 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2066 /* fall through */
2067 case CB_INSERTSTRING:
2068 return unicode ? SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam) :
2069 SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
2070 case CB_DELETESTRING16:
2071 case CB_DELETESTRING:
2072 return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
2073 SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
2074 case CB_SELECTSTRING16:
2075 wParam = (INT)(INT16)wParam;
2076 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2077 /* fall through */
2078 case CB_SELECTSTRING:
2079 return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
2080 case CB_FINDSTRING16:
2081 wParam = (INT)(INT16)wParam;
2082 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2083 /* fall through */
2084 case CB_FINDSTRING:
2085 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
2086 SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
2087 case CB_FINDSTRINGEXACT16:
2088 wParam = (INT)(INT16)wParam;
2089 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)MapSL(lParam);
2090 /* fall through */
2091 case CB_FINDSTRINGEXACT:
2092 return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
2093 SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
2094 case CB_SETITEMHEIGHT16:
2095 wParam = (INT)(INT16)wParam; /* signed integer */
2096 /* fall through */
2097 case CB_SETITEMHEIGHT:
2098 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
2099 case CB_GETITEMHEIGHT16:
2100 wParam = (INT)(INT16)wParam;
2101 /* fall through */
2102 case CB_GETITEMHEIGHT:
2103 if( (INT)wParam >= 0 ) /* listbox item */
2104 return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
2105 return CBGetTextAreaHeight(hwnd, lphc);
2106 case CB_RESETCONTENT16:
2107 case CB_RESETCONTENT:
2108 SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
2109 if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
2111 static const WCHAR empty_stringW[] = { 0 };
2112 SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
2114 else
2115 InvalidateRect(lphc->self, NULL, TRUE);
2116 return TRUE;
2117 case CB_INITSTORAGE:
2118 return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
2119 case CB_GETHORIZONTALEXTENT:
2120 return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
2121 case CB_SETHORIZONTALEXTENT:
2122 return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
2123 case CB_GETTOPINDEX:
2124 return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
2125 case CB_GETLOCALE:
2126 return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
2127 case CB_SETLOCALE:
2128 return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
2129 case CB_GETDROPPEDWIDTH:
2130 if( lphc->droppedWidth )
2131 return lphc->droppedWidth;
2132 return lphc->droppedRect.right - lphc->droppedRect.left;
2133 case CB_SETDROPPEDWIDTH:
2134 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
2135 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
2136 return CB_ERR;
2137 case CB_GETDROPPEDCONTROLRECT16:
2138 lParam = (LPARAM)MapSL(lParam);
2139 if( lParam )
2141 RECT r;
2142 CBGetDroppedControlRect( lphc, &r );
2143 CONV_RECT32TO16( &r, (LPRECT16)lParam );
2145 return CB_OKAY;
2146 case CB_GETDROPPEDCONTROLRECT:
2147 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
2148 return CB_OKAY;
2149 case CB_GETDROPPEDSTATE16:
2150 case CB_GETDROPPEDSTATE:
2151 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
2152 case CB_DIR16:
2153 lParam = (LPARAM)MapSL(lParam);
2154 message = LB_DIR16;
2155 /* fall through */
2156 case CB_DIR:
2157 if(message == CB_DIR) message = LB_DIR;
2158 return unicode ? SendMessageW(lphc->hWndLBox, message, wParam, lParam) :
2159 SendMessageA(lphc->hWndLBox, message, wParam, lParam);
2161 case CB_SHOWDROPDOWN16:
2162 case CB_SHOWDROPDOWN:
2163 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2165 if( wParam )
2167 if( !(lphc->wState & CBF_DROPPED) )
2168 CBDropDown( lphc );
2170 else
2171 if( lphc->wState & CBF_DROPPED )
2172 CBRollUp( lphc, FALSE, TRUE );
2174 return TRUE;
2175 case CB_GETCOUNT16:
2176 case CB_GETCOUNT:
2177 return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
2178 case CB_GETCURSEL16:
2179 case CB_GETCURSEL:
2180 return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
2181 case CB_SETCURSEL16:
2182 wParam = (INT)(INT16)wParam;
2183 /* fall through */
2184 case CB_SETCURSEL:
2185 lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
2186 if( lParam >= 0 )
2187 SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
2189 /* no LBN_SELCHANGE in this case, update manually */
2190 if( lphc->wState & CBF_EDIT )
2191 CBUpdateEdit( lphc, (INT)wParam );
2192 else
2193 InvalidateRect(lphc->self, &lphc->textRect, TRUE);
2194 lphc->wState &= ~CBF_SELCHANGE;
2195 return lParam;
2196 case CB_GETLBTEXT16:
2197 wParam = (INT)(INT16)wParam;
2198 lParam = (LPARAM)MapSL(lParam);
2199 /* fall through */
2200 case CB_GETLBTEXT:
2201 return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
2202 SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
2203 case CB_GETLBTEXTLEN16:
2204 wParam = (INT)(INT16)wParam;
2205 /* fall through */
2206 case CB_GETLBTEXTLEN:
2207 return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
2208 case CB_GETITEMDATA16:
2209 wParam = (INT)(INT16)wParam;
2210 /* fall through */
2211 case CB_GETITEMDATA:
2212 return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
2213 case CB_SETITEMDATA16:
2214 wParam = (INT)(INT16)wParam;
2215 /* fall through */
2216 case CB_SETITEMDATA:
2217 return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
2218 case CB_GETEDITSEL16:
2219 wParam = lParam = 0; /* just in case */
2220 /* fall through */
2221 case CB_GETEDITSEL:
2222 /* Edit checks passed parameters itself */
2223 if( lphc->wState & CBF_EDIT )
2224 return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
2225 return CB_ERR;
2226 case CB_SETEDITSEL16:
2227 case CB_SETEDITSEL:
2228 if( lphc->wState & CBF_EDIT )
2229 return SendMessageW(lphc->hWndEdit, EM_SETSEL,
2230 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
2231 return CB_ERR;
2232 case CB_SETEXTENDEDUI16:
2233 case CB_SETEXTENDEDUI:
2234 if( CB_GETTYPE(lphc) == CBS_SIMPLE )
2235 return CB_ERR;
2236 if( wParam )
2237 lphc->wState |= CBF_EUI;
2238 else lphc->wState &= ~CBF_EUI;
2239 return CB_OKAY;
2240 case CB_GETEXTENDEDUI16:
2241 case CB_GETEXTENDEDUI:
2242 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
2244 default:
2245 if (message >= WM_USER)
2246 WARN("unknown msg WM_USER+%04x wp=%04x lp=%08lx\n",
2247 message - WM_USER, wParam, lParam );
2248 break;
2250 return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
2251 DefWindowProcA(hwnd, message, wParam, lParam);
2254 /***********************************************************************
2255 * ComboWndProcA
2257 * This is just a wrapper for the real ComboWndProc which locks/unlocks
2258 * window structs.
2260 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2262 if (!IsWindow(hwnd)) return 0;
2263 return ComboWndProc_common( hwnd, message, wParam, lParam, FALSE );
2266 /***********************************************************************
2267 * ComboWndProcW
2269 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
2271 if (!IsWindow(hwnd)) return 0;
2272 return ComboWndProc_common( hwnd, message, wParam, lParam, TRUE );