Release 990226.
[wine/gsoc-2012-control.git] / controls / combo.c
blob5f8e1cd8bef79f0a40c400590d0a6f2b88800118
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 "winuser.h"
12 #include "wine/winuser16.h"
13 #include "sysmetrics.h"
14 #include "win.h"
15 #include "spy.h"
16 #include "user.h"
17 #include "heap.h"
18 #include "combo.h"
19 #include "drive.h"
20 #include "debug.h"
22 /* bits in the dwKeyData */
23 #define KEYDATA_ALT 0x2000
24 #define KEYDATA_PREVSTATE 0x4000
27 * Additional combo box definitions
30 #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra))
31 #define CB_NOTIFY( lphc, code ) \
32 (SendMessageA( (lphc)->owner, WM_COMMAND, \
33 MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf))
34 #define CB_GETEDITTEXTLENGTH( lphc ) \
35 (SendMessageA( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ))
37 static HBITMAP hComboBmp = 0;
38 static UINT CBitHeight, CBitWidth;
39 static UINT CBitOffset = 8;
41 /***********************************************************************
42 * COMBO_Init
44 * Load combo button bitmap.
46 static BOOL COMBO_Init()
48 HDC hDC;
50 if( hComboBmp ) return TRUE;
51 if( (hDC = CreateCompatibleDC(0)) )
53 BOOL bRet = FALSE;
54 if( (hComboBmp = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_COMBO))) )
56 BITMAP bm;
57 HBITMAP hPrevB;
58 RECT r;
60 GetObjectA( hComboBmp, sizeof(bm), &bm );
61 CBitHeight = bm.bmHeight;
62 CBitWidth = bm.bmWidth;
64 TRACE(combo, "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight );
66 hPrevB = SelectObject16( hDC, hComboBmp);
67 SetRect( &r, 0, 0, CBitWidth, CBitHeight );
68 InvertRect( hDC, &r );
69 SelectObject( hDC, hPrevB );
70 bRet = TRUE;
72 DeleteDC( hDC );
73 return bRet;
75 return FALSE;
78 /***********************************************************************
79 * COMBO_NCCreate
81 static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam)
83 LPHEADCOMBO lphc;
85 if ( wnd && COMBO_Init() &&
86 (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) )
88 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
90 memset( lphc, 0, sizeof(HEADCOMBO) );
91 *(LPHEADCOMBO*)wnd->wExtra = lphc;
93 /* some braindead apps do try to use scrollbar/border flags */
95 lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL));
96 wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL);
98 if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) )
99 lphc->dwStyle |= CBS_HASSTRINGS;
100 if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) )
101 lphc->wState |= CBF_NOTIFY;
103 TRACE(combo, "[0x%08x], style = %08x\n",
104 (UINT)lphc, lphc->dwStyle );
106 return (LRESULT)(UINT)wnd->hwndSelf;
108 return (LRESULT)FALSE;
111 /***********************************************************************
112 * COMBO_NCDestroy
114 static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc )
117 if( lphc )
119 WND* wnd = lphc->self;
121 TRACE(combo,"[%04x]: freeing storage\n", CB_HWND(lphc));
123 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox )
124 DestroyWindow( lphc->hWndLBox );
126 HeapFree( GetProcessHeap(), 0, lphc );
127 wnd->wExtra[0] = 0;
129 return 0;
132 /***********************************************************************
133 * CBGetDefaultTextHeight
135 static void CBGetDefaultTextHeight( LPHEADCOMBO lphc, LPSIZE lpSize )
137 if( lphc->editHeight ) /* explicitly set height */
138 lpSize->cy = lphc->editHeight;
139 else
141 HDC hDC = GetDC( lphc->self->hwndSelf );
142 HFONT hPrevFont = 0;
144 if( lphc->hFont ) hPrevFont = SelectObject( hDC, lphc->hFont );
146 GetTextExtentPoint32A( hDC, "0", 1, lpSize);
148 lpSize->cy += lpSize->cy / 4 + 4 * SYSMETRICS_CYBORDER;
150 if( hPrevFont ) SelectObject( hDC, hPrevFont );
151 ReleaseDC( lphc->self->hwndSelf, hDC );
153 lpSize->cx = lphc->RectCombo.right - lphc->RectCombo.left;
157 /***********************************************************************
158 * CBCalcPlacement
160 * Set up component coordinates given valid lphc->RectCombo.
162 static void CBCalcPlacement( LPHEADCOMBO lphc, LPRECT lprEdit,
163 LPRECT lprButton, LPRECT lprLB )
165 RECT rect = lphc->RectCombo;
166 SIZE size;
168 /* get combo height and width */
170 if( CB_OWNERDRAWN(lphc) )
172 UINT u = lphc->RectEdit.bottom - lphc->RectEdit.top;
174 if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */
176 MEASUREITEMSTRUCT mi32;
178 /* calculate defaults before sending WM_MEASUREITEM */
180 CBGetDefaultTextHeight( lphc, &size );
182 lphc->wState &= ~CBF_MEASUREITEM;
184 mi32.CtlType = ODT_COMBOBOX;
185 mi32.CtlID = lphc->self->wIDmenu;
186 mi32.itemID = -1;
187 mi32.itemWidth = size.cx;
188 mi32.itemHeight = size.cy - 6; /* ownerdrawn cb is taller */
189 mi32.itemData = 0;
190 SendMessageA(lphc->owner, WM_MEASUREITEM,
191 (WPARAM)mi32.CtlID, (LPARAM)&mi32);
192 u = 6 + (UINT16)mi32.itemHeight;
194 else
195 size.cx = rect.right - rect.left;
196 size.cy = u;
198 else
199 CBGetDefaultTextHeight( lphc, &size );
201 /* calculate text and button placement */
203 lprEdit->left = lprEdit->top = lprButton->top = 0;
204 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* no button */
205 lprButton->left = lprButton->right = lprButton->bottom = 0;
206 else
208 INT i = size.cx - CBitWidth - 10; /* seems ok */
210 lprButton->right = size.cx;
211 lprButton->left = (INT16)i;
212 lprButton->bottom = lprButton->top + size.cy;
214 if( i < 0 ) size.cx = 0;
215 else size.cx = i;
218 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
220 size.cx -= CBitOffset;
221 if( size.cx < 0 ) size.cx = 0;
224 lprEdit->right = size.cx; lprEdit->bottom = size.cy;
226 /* listbox placement */
228 lprLB->left = ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
229 lprLB->top = lprEdit->bottom - SYSMETRICS_CYBORDER;
230 lprLB->right = rect.right - rect.left;
231 lprLB->bottom = rect.bottom - rect.top;
233 if( lphc->droppedWidth > (lprLB->right - lprLB->left) )
234 lprLB->right = lprLB->left + lphc->droppedWidth;
236 TRACE(combo,"[%04x]: (%i,%i-%i,%i) placement\n",
237 CB_HWND(lphc), lphc->RectCombo.left, lphc->RectCombo.top,
238 lphc->RectCombo.right, lphc->RectCombo.bottom);
240 TRACE(combo,"\ttext\t= (%i,%i-%i,%i)\n",
241 lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom);
243 TRACE(combo,"\tbutton\t= (%i,%i-%i,%i)\n",
244 lprButton->left, lprButton->top, lprButton->right, lprButton->bottom);
246 TRACE(combo,"\tlbox\t= (%i,%i-%i,%i)\n",
247 lprLB->left, lprLB->top, lprLB->right, lprLB->bottom );
250 /***********************************************************************
251 * CBGetDroppedControlRect32
253 static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect)
255 lpRect->left = lphc->RectCombo.left +
256 (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
257 lpRect->top = lphc->RectCombo.top + lphc->RectEdit.bottom -
258 SYSMETRICS_CYBORDER;
259 lpRect->right = lphc->RectCombo.right;
260 lpRect->bottom = lphc->RectCombo.bottom - SYSMETRICS_CYBORDER;
263 /***********************************************************************
264 * COMBO_Create
266 static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam)
268 static char clbName[] = "ComboLBox";
269 static char editName[] = "Edit";
271 LPCREATESTRUCTA lpcs = (CREATESTRUCTA*)lParam;
273 if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE;
274 else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT;
276 lphc->self = wnd;
277 lphc->owner = lpcs->hwndParent;
279 /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */
281 if( lphc->owner || !(lpcs->style & WS_VISIBLE) )
283 UINT lbeStyle;
284 RECT editRect, btnRect, lbRect;
286 GetWindowRect( wnd->hwndSelf, &lphc->RectCombo );
288 lphc->wState |= CBF_MEASUREITEM;
289 CBCalcPlacement( lphc, &editRect, &btnRect, &lbRect );
290 lphc->RectButton = btnRect;
291 lphc->droppedWidth = lphc->editHeight = 0;
293 /* create listbox popup */
295 lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) |
296 (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE));
298 if( lphc->dwStyle & CBS_SORT )
299 lbeStyle |= LBS_SORT;
300 if( lphc->dwStyle & CBS_HASSTRINGS )
301 lbeStyle |= LBS_HASSTRINGS;
302 if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT )
303 lbeStyle |= LBS_NOINTEGRALHEIGHT;
304 if( lphc->dwStyle & CBS_DISABLENOSCROLL )
305 lbeStyle |= LBS_DISABLENOSCROLL;
307 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */
308 lbeStyle |= WS_CHILD | WS_VISIBLE;
309 else /* popup listbox */
311 lbeStyle |= WS_POPUP;
312 OffsetRect( &lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
315 /* Dropdown ComboLBox is not a child window and we cannot pass
316 * ID_CB_LISTBOX directly because it will be treated as a menu handle.
319 lphc->hWndLBox = CreateWindowExA( 0, clbName, NULL, lbeStyle,
320 lbRect.left + SYSMETRICS_CXBORDER,
321 lbRect.top + SYSMETRICS_CYBORDER,
322 lbRect.right - lbRect.left - 2 * SYSMETRICS_CXBORDER,
323 lbRect.bottom - lbRect.top - 2 * SYSMETRICS_CYBORDER,
324 lphc->self->hwndSelf,
325 (lphc->dwStyle & CBS_DROPDOWN)? (HMENU)0 : (HMENU)ID_CB_LISTBOX,
326 lphc->self->hInstance, (LPVOID)lphc );
327 if( lphc->hWndLBox )
329 BOOL bEdit = TRUE;
330 lbeStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NOHIDESEL | ES_LEFT;
331 if( lphc->wState & CBF_EDIT )
333 if( lphc->dwStyle & CBS_OEMCONVERT )
334 lbeStyle |= ES_OEMCONVERT;
335 if( lphc->dwStyle & CBS_AUTOHSCROLL )
336 lbeStyle |= ES_AUTOHSCROLL;
337 if( lphc->dwStyle & CBS_LOWERCASE )
338 lbeStyle |= ES_LOWERCASE;
339 else if( lphc->dwStyle & CBS_UPPERCASE )
340 lbeStyle |= ES_UPPERCASE;
341 lphc->hWndEdit = CreateWindowExA( 0, editName, NULL, lbeStyle,
342 editRect.left, editRect.top, editRect.right - editRect.left,
343 editRect.bottom - editRect.top, lphc->self->hwndSelf,
344 (HMENU)ID_CB_EDIT, lphc->self->hInstance, NULL );
345 if( !lphc->hWndEdit ) bEdit = FALSE;
348 if( bEdit )
350 lphc->RectEdit = editRect;
351 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
353 lphc->wState |= CBF_NORESIZE;
354 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
355 lphc->RectCombo.right - lphc->RectCombo.left,
356 lphc->RectEdit.bottom - lphc->RectEdit.top,
357 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
358 lphc->wState &= ~CBF_NORESIZE;
360 TRACE(combo,"init done\n");
361 return wnd->hwndSelf;
363 ERR(combo, "edit control failure.\n");
364 } else ERR(combo, "listbox failure.\n");
365 } else ERR(combo, "no owner for visible combo.\n");
367 /* CreateWindow() will send WM_NCDESTROY to cleanup */
369 return -1;
372 /***********************************************************************
373 * CBPaintButton
375 * Paint combo button (normal, pressed, and disabled states).
377 static void CBPaintButton(LPHEADCOMBO lphc, HDC hdc)
379 RECT r;
380 UINT x, y;
381 BOOL bBool;
382 HDC hMemDC;
383 HBRUSH hPrevBrush;
384 COLORREF oldTextColor, oldBkColor;
386 if( lphc->wState & CBF_NOREDRAW ) return;
388 hPrevBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
389 CONV_RECT16TO32( &lphc->RectButton, &r );
391 Rectangle(hdc, r.left, r.top, r.right, r.bottom );
392 if( (bBool = lphc->wState & CBF_BUTTONDOWN) )
394 DrawEdge( hdc, &r, EDGE_SUNKEN, BF_RECT );
395 OffsetRect( &r, 1, 1 );
396 } else {
397 r.top++, r.left++;
398 DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
399 r.top--, r.left--;
402 InflateRect( &r, -1, -1 );
404 x = (r.left + r.right - CBitWidth) >> 1;
405 y = (r.top + r.bottom - CBitHeight) >> 1;
407 InflateRect( &r, -3, -3 );
409 hMemDC = CreateCompatibleDC( hdc );
410 SelectObject( hMemDC, hComboBmp );
411 oldTextColor = SetTextColor( hdc, GetSysColor(COLOR_BTNFACE) );
412 oldBkColor = SetBkColor( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) :
413 RGB(0,0,0) );
414 BitBlt( hdc, x, y, 8, 8, hMemDC, 0, 0, SRCCOPY );
415 SetBkColor( hdc, oldBkColor );
416 SetTextColor( hdc, oldTextColor );
417 DeleteDC( hMemDC );
418 SelectObject( hdc, hPrevBrush );
421 /***********************************************************************
422 * CBPaintText
424 * Paint CBS_DROPDOWNLIST text field / update edit control contents.
426 static void CBPaintText(LPHEADCOMBO lphc, HDC hdc)
428 INT id, size = 0;
429 LPSTR pText = NULL;
431 if( lphc->wState & CBF_NOREDRAW ) return;
433 /* follow Windows combobox that sends a bunch of text
434 * inquiries to its listbox while processing WM_PAINT. */
436 if( (id = SendMessageA(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR )
438 size = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, id, 0);
439 if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) )
441 SendMessageA( lphc->hWndLBox, LB_GETTEXT, (WPARAM)id, (LPARAM)pText );
442 pText[size] = '\0'; /* just in case */
443 } else return;
446 if( lphc->wState & CBF_EDIT )
448 if( CB_HASSTRINGS(lphc) ) SetWindowTextA( lphc->hWndEdit, pText ? pText : "" );
449 if( lphc->wState & CBF_FOCUSED )
450 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1));
452 else /* paint text field ourselves */
454 HBRUSH hPrevBrush = 0;
455 HDC hDC = hdc;
457 if( !hDC )
459 if ((hDC = GetDC(lphc->self->hwndSelf)))
461 HBRUSH hBrush = SendMessageA( lphc->owner,
462 WM_CTLCOLORLISTBOX,
463 hDC, lphc->self->hwndSelf );
464 hPrevBrush = SelectObject( hDC,
465 (hBrush) ? hBrush : GetStockObject(WHITE_BRUSH) );
468 if( hDC )
470 RECT rect;
471 UINT itemState;
472 HFONT hPrevFont = (lphc->hFont) ? SelectObject(hDC, lphc->hFont) : 0;
474 PatBlt( hDC, (rect.left = lphc->RectEdit.left + SYSMETRICS_CXBORDER),
475 (rect.top = lphc->RectEdit.top + SYSMETRICS_CYBORDER),
476 (rect.right = lphc->RectEdit.right - SYSMETRICS_CXBORDER),
477 (rect.bottom = lphc->RectEdit.bottom - SYSMETRICS_CYBORDER) - 1, PATCOPY );
478 InflateRect( &rect, -1, -1 );
480 if( lphc->wState & CBF_FOCUSED &&
481 !(lphc->wState & CBF_DROPPED) )
483 /* highlight */
485 FillRect( hDC, &rect, GetSysColorBrush(COLOR_HIGHLIGHT) );
486 SetBkColor( hDC, GetSysColor( COLOR_HIGHLIGHT ) );
487 SetTextColor( hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
488 itemState = ODS_SELECTED | ODS_FOCUS;
489 } else itemState = 0;
491 if( CB_OWNERDRAWN(lphc) )
493 DRAWITEMSTRUCT dis;
495 if( lphc->self->dwStyle & WS_DISABLED ) itemState |= ODS_DISABLED;
497 dis.CtlType = ODT_COMBOBOX;
498 dis.CtlID = lphc->self->wIDmenu;
499 dis.hwndItem = lphc->self->hwndSelf;
500 dis.itemAction = ODA_DRAWENTIRE;
501 dis.itemID = id;
502 dis.itemState = itemState;
503 dis.hDC = hDC;
504 dis.rcItem = rect;
505 dis.itemData = SendMessageA( lphc->hWndLBox, LB_GETITEMDATA,
506 (WPARAM)id, 0 );
507 SendMessageA( lphc->owner, WM_DRAWITEM,
508 lphc->self->wIDmenu, (LPARAM)&dis );
510 else
512 ExtTextOutA( hDC, rect.left + 1, rect.top + 1,
513 ETO_OPAQUE | ETO_CLIPPED, &rect,
514 pText ? pText : "" , size, NULL );
515 if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED))
516 DrawFocusRect( hDC, &rect );
519 if( hPrevFont ) SelectObject(hDC, hPrevFont );
520 if( !hdc )
522 if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
523 ReleaseDC( lphc->self->hwndSelf, hDC );
527 if (pText)
528 HeapFree( GetProcessHeap(), 0, pText );
531 /***********************************************************************
532 * COMBO_Paint
534 static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC hParamDC)
536 PAINTSTRUCT ps;
537 HDC hDC;
539 hDC = (hParamDC) ? hParamDC
540 : BeginPaint( lphc->self->hwndSelf, &ps);
541 if( hDC && !(lphc->wState & CBF_NOREDRAW) )
543 HBRUSH hPrevBrush, hBkgBrush;
545 hBkgBrush = SendMessageA( lphc->owner, WM_CTLCOLORLISTBOX,
546 hDC, lphc->self->hwndSelf );
547 if( !hBkgBrush ) hBkgBrush = GetStockObject(WHITE_BRUSH);
549 hPrevBrush = SelectObject( hDC, hBkgBrush );
550 if( !IsRectEmpty(&lphc->RectButton) )
552 /* paint everything to the right of the text field */
554 PatBlt( hDC, lphc->RectEdit.right, lphc->RectEdit.top,
555 lphc->RectButton.right - lphc->RectEdit.right,
556 lphc->RectEdit.bottom - lphc->RectEdit.top, PATCOPY );
557 CBPaintButton( lphc, hDC );
560 if( !(lphc->wState & CBF_EDIT) )
562 /* paint text field */
564 HPEN hPrevPen = SelectObject( hDC, GetSysColorPen(
565 COLOR_WINDOWFRAME) );
567 Rectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top,
568 lphc->RectEdit.right, lphc->RectButton.bottom );
569 SelectObject( hDC, hPrevPen );
570 CBPaintText( lphc, hDC );
572 if( hPrevBrush ) SelectObject( hDC, hPrevBrush );
574 if( !hParamDC ) EndPaint(lphc->self->hwndSelf, &ps);
575 return 0;
578 /***********************************************************************
579 * CBUpdateLBox
581 * Select listbox entry according to the contents of the edit control.
583 static INT CBUpdateLBox( LPHEADCOMBO lphc )
585 INT length, idx, ret;
586 LPSTR pText = NULL;
588 idx = ret = LB_ERR;
589 length = CB_GETEDITTEXTLENGTH( lphc );
591 if( length > 0 )
592 pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1);
594 TRACE(combo,"\t edit text length %i\n", length );
596 if( pText )
598 if( length ) GetWindowTextA( lphc->hWndEdit, pText, length + 1);
599 else pText[0] = '\0';
600 idx = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
601 (WPARAM)(-1), (LPARAM)pText );
602 if( idx == LB_ERR ) idx = 0; /* select first item */
603 else ret = idx;
604 HeapFree( GetProcessHeap(), 0, pText );
607 /* select entry */
609 SendMessageA( lphc->hWndLBox, LB_SETCURSEL, (WPARAM)idx, 0 );
611 if( idx >= 0 )
613 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)idx, 0 );
614 /* probably superfluous but Windows sends this too */
615 SendMessageA( lphc->hWndLBox, LB_SETCARETINDEX, (WPARAM)idx, 0 );
617 return ret;
620 /***********************************************************************
621 * CBUpdateEdit
623 * Copy a listbox entry to the edit control.
625 static void CBUpdateEdit( LPHEADCOMBO lphc , INT index )
627 INT length;
628 LPSTR pText = NULL;
630 TRACE(combo,"\t %i\n", index );
632 if( index == -1 )
634 length = CB_GETEDITTEXTLENGTH( lphc );
635 if( length )
637 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
639 GetWindowTextA( lphc->hWndEdit, pText, length + 1 );
640 index = SendMessageA( lphc->hWndLBox, LB_FINDSTRING,
641 (WPARAM)(-1), (LPARAM)pText );
642 HeapFree( GetProcessHeap(), 0, pText );
647 if( index >= 0 ) /* got an entry */
649 length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, (WPARAM)index, 0);
650 if( length )
652 if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) )
654 SendMessageA( lphc->hWndLBox, LB_GETTEXT,
655 (WPARAM)index, (LPARAM)pText );
656 SendMessageA( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText );
657 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
658 HeapFree( GetProcessHeap(), 0, pText );
664 /***********************************************************************
665 * CBDropDown
667 * Show listbox popup.
669 static void CBDropDown( LPHEADCOMBO lphc )
671 INT index;
672 RECT rect;
673 LPRECT pRect = NULL;
675 TRACE(combo,"[%04x]: drop down\n", CB_HWND(lphc));
677 CB_NOTIFY( lphc, CBN_DROPDOWN );
679 /* set selection */
681 lphc->wState |= CBF_DROPPED;
682 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
684 index = CBUpdateLBox( lphc );
685 if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index );
687 else
689 index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
690 if( index == LB_ERR ) index = 0;
691 SendMessageA( lphc->hWndLBox, LB_SETTOPINDEX, (WPARAM)index, 0 );
692 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
693 pRect = &lphc->RectEdit;
696 /* now set popup position */
698 GetWindowRect( lphc->self->hwndSelf, &rect );
700 rect.top += lphc->RectEdit.bottom - lphc->RectEdit.top - SYSMETRICS_CYBORDER;
701 rect.bottom = rect.top + lphc->RectCombo.bottom -
702 lphc->RectCombo.top - SYSMETRICS_CYBORDER;
703 rect.right = rect.left + lphc->RectCombo.right - lphc->RectCombo.left;
704 rect.left += ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset;
706 SetWindowPos( lphc->hWndLBox, HWND_TOP, rect.left, rect.top,
707 rect.right - rect.left, rect.bottom - rect.top,
708 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW);
710 if( !(lphc->wState & CBF_NOREDRAW) )
711 if( pRect )
712 RedrawWindow( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE |
713 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
714 ShowWindow( lphc->hWndLBox, SW_SHOWNA );
717 /***********************************************************************
718 * CBRollUp
720 * Hide listbox popup.
722 static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
724 HWND hWnd = lphc->self->hwndSelf;
726 CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL );
728 if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE )
731 TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT)ok );
733 /* always send WM_LBUTTONUP? */
734 SendMessageA( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) );
736 if( lphc->wState & CBF_DROPPED )
738 RECT rect;
740 lphc->wState &= ~CBF_DROPPED;
741 ShowWindow( lphc->hWndLBox, SW_HIDE );
743 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
745 INT index = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
746 CBUpdateEdit( lphc, index );
747 rect = lphc->RectButton;
749 else
751 if( bButton )
752 UnionRect( &rect, &lphc->RectButton,
753 &lphc->RectEdit );
754 else
755 rect = lphc->RectEdit;
756 bButton = TRUE;
759 if( bButton && !(lphc->wState & CBF_NOREDRAW) )
760 RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE |
761 RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
762 CB_NOTIFY( lphc, CBN_CLOSEUP );
767 /***********************************************************************
768 * COMBO_FlipListbox
770 * Used by the ComboLBox to show/hide itself in response to VK_F4, etc...
772 BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL bRedrawButton )
774 if( lphc->wState & CBF_DROPPED )
776 CBRollUp( lphc, TRUE, bRedrawButton );
777 return FALSE;
780 CBDropDown( lphc );
781 return TRUE;
784 /***********************************************************************
785 * COMBO_GetLBWindow
787 * Edit control helper.
789 HWND COMBO_GetLBWindow( WND* pWnd )
791 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
792 if( lphc ) return lphc->hWndLBox;
793 return 0;
797 /***********************************************************************
798 * CBRepaintButton
800 static void CBRepaintButton( LPHEADCOMBO lphc )
802 HDC hDC = GetDC( lphc->self->hwndSelf );
804 if( hDC )
806 CBPaintButton( lphc, hDC );
807 ReleaseDC( lphc->self->hwndSelf, hDC );
811 /***********************************************************************
812 * COMBO_SetFocus
814 static void COMBO_SetFocus( LPHEADCOMBO lphc )
816 if( !(lphc->wState & CBF_FOCUSED) )
818 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
819 SendMessageA( lphc->hWndLBox, LB_CARETON, 0, 0 );
821 if( lphc->wState & CBF_EDIT )
822 SendMessageA( lphc->hWndEdit, EM_SETSEL, 0, (LPARAM)(-1) );
823 lphc->wState |= CBF_FOCUSED;
824 if( !(lphc->wState & CBF_EDIT) ) CBPaintText( lphc, 0 );
826 CB_NOTIFY( lphc, CBN_SETFOCUS );
830 /***********************************************************************
831 * COMBO_KillFocus
833 static void COMBO_KillFocus( LPHEADCOMBO lphc )
835 HWND hWnd = lphc->self->hwndSelf;
837 if( lphc->wState & CBF_FOCUSED )
839 SendMessageA( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) );
841 CBRollUp( lphc, FALSE, TRUE );
842 if( IsWindow( hWnd ) )
844 if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST )
845 SendMessageA( lphc->hWndLBox, LB_CARETOFF, 0, 0 );
847 lphc->wState &= ~CBF_FOCUSED;
849 /* redraw text */
850 if( lphc->wState & CBF_EDIT )
851 SendMessageA( lphc->hWndEdit, EM_SETSEL, (WPARAM)(-1), 0 );
852 else CBPaintText( lphc, 0 );
854 CB_NOTIFY( lphc, CBN_KILLFOCUS );
859 /***********************************************************************
860 * COMBO_Command
862 static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd )
864 if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd )
866 /* ">> 8" makes gcc generate jump-table instead of cmp ladder */
868 switch( HIWORD(wParam) >> 8 )
870 case (EN_SETFOCUS >> 8):
872 TRACE(combo,"[%04x]: edit [%04x] got focus\n",
873 CB_HWND(lphc), lphc->hWndEdit );
875 if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc );
876 break;
878 case (EN_KILLFOCUS >> 8):
880 TRACE(combo,"[%04x]: edit [%04x] lost focus\n",
881 CB_HWND(lphc), lphc->hWndEdit );
883 /* NOTE: it seems that Windows' edit control sends an
884 * undocumented message WM_USER + 0x1B instead of this
885 * notification (only when it happens to be a part of
886 * the combo). ?? - AK.
889 COMBO_KillFocus( lphc );
890 break;
893 case (EN_CHANGE >> 8):
894 CB_NOTIFY( lphc, CBN_EDITCHANGE );
895 CBUpdateLBox( lphc );
896 break;
898 case (EN_UPDATE >> 8):
899 CB_NOTIFY( lphc, CBN_EDITUPDATE );
900 break;
902 case (EN_ERRSPACE >> 8):
903 CB_NOTIFY( lphc, CBN_ERRSPACE );
906 else if( lphc->hWndLBox == hWnd )
908 switch( HIWORD(wParam) )
910 case LBN_ERRSPACE:
911 CB_NOTIFY( lphc, CBN_ERRSPACE );
912 break;
914 case LBN_DBLCLK:
915 CB_NOTIFY( lphc, CBN_DBLCLK );
916 break;
918 case LBN_SELCHANGE:
919 case LBN_SELCANCEL:
921 TRACE(combo,"[%04x]: lbox selection change [%04x]\n",
922 CB_HWND(lphc), lphc->wState );
924 /* do not roll up if selection is being tracked
925 * by arrowkeys in the dropdown listbox */
927 if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) )
928 CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE );
929 else lphc->wState &= ~CBF_NOROLLUP;
931 CB_NOTIFY( lphc, CBN_SELCHANGE );
932 CBPaintText( lphc, 0 );
933 /* fall through */
935 case LBN_SETFOCUS:
936 case LBN_KILLFOCUS:
937 /* nothing to do here since ComboLBox always resets the focus to its
938 * combo/edit counterpart */
939 break;
942 return 0;
945 /***********************************************************************
946 * COMBO_ItemOp
948 * Fixup an ownerdrawn item operation and pass it up to the combobox owner.
950 static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg,
951 WPARAM wParam, LPARAM lParam )
953 HWND hWnd = lphc->self->hwndSelf;
955 TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg );
957 #define lpIS ((LPDELETEITEMSTRUCT)lParam)
959 /* two first items are the same in all 4 structs */
960 lpIS->CtlType = ODT_COMBOBOX;
961 lpIS->CtlID = lphc->self->wIDmenu;
963 switch( msg ) /* patch window handle */
965 case WM_DELETEITEM:
966 lpIS->hwndItem = hWnd;
967 #undef lpIS
968 break;
969 case WM_DRAWITEM:
970 #define lpIS ((LPDRAWITEMSTRUCT)lParam)
971 lpIS->hwndItem = hWnd;
972 #undef lpIS
973 break;
974 case WM_COMPAREITEM:
975 #define lpIS ((LPCOMPAREITEMSTRUCT)lParam)
976 lpIS->hwndItem = hWnd;
977 #undef lpIS
978 break;
981 return SendMessageA( lphc->owner, msg, lphc->self->wIDmenu, lParam );
984 /***********************************************************************
985 * COMBO_GetText
987 static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT N, LPSTR lpText)
989 if( lphc->wState & CBF_EDIT )
990 return SendMessageA( lphc->hWndEdit, WM_GETTEXT,
991 (WPARAM)N, (LPARAM)lpText );
993 /* get it from the listbox */
995 if( lphc->hWndLBox )
997 INT idx = SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0 );
998 if( idx != LB_ERR )
1000 LPSTR lpBuffer;
1001 INT length = SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN,
1002 (WPARAM)idx, 0 );
1004 /* 'length' is without the terminating character */
1005 if( length >= N )
1006 lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 );
1007 else
1008 lpBuffer = lpText;
1010 if( lpBuffer )
1012 INT n = SendMessageA( lphc->hWndLBox, LB_GETTEXT,
1013 (WPARAM)idx, (LPARAM)lpBuffer );
1015 /* truncate if buffer is too short */
1017 if( length >= N )
1019 if (N && lpText) {
1020 if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 );
1021 lpText[N - 1] = '\0';
1023 HeapFree( GetProcessHeap(), 0, lpBuffer );
1025 return (LRESULT)n;
1029 return 0;
1033 /***********************************************************************
1034 * CBResetPos
1036 * This function sets window positions according to the updated
1037 * component placement struct.
1039 static void CBResetPos( LPHEADCOMBO lphc, LPRECT lbRect, BOOL bRedraw )
1041 BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
1043 /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
1044 * sizing messages */
1046 if( lphc->wState & CBF_EDIT )
1047 SetWindowPos( lphc->hWndEdit, 0, lphc->RectEdit.left, lphc->RectEdit.top,
1048 lphc->RectEdit.right - lphc->RectEdit.left,
1049 lphc->RectEdit.bottom - lphc->RectEdit.top,
1050 SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
1052 if( bDrop )
1053 OffsetRect( lbRect, lphc->RectCombo.left, lphc->RectCombo.top );
1055 lbRect->right -= lbRect->left; /* convert to width */
1056 lbRect->bottom -= lbRect->top;
1057 SetWindowPos( lphc->hWndLBox, 0, lbRect->left, lbRect->top,
1058 lbRect->right, lbRect->bottom,
1059 SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
1061 if( bDrop )
1063 if( lphc->wState & CBF_DROPPED )
1065 lphc->wState &= ~CBF_DROPPED;
1066 ShowWindow( lphc->hWndLBox, SW_HIDE );
1069 lphc->wState |= CBF_NORESIZE;
1070 SetWindowPos( lphc->self->hwndSelf, 0, 0, 0,
1071 lphc->RectCombo.right - lphc->RectCombo.left,
1072 lphc->RectEdit.bottom - lphc->RectEdit.top,
1073 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW );
1074 lphc->wState &= ~CBF_NORESIZE;
1076 if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
1077 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
1078 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
1083 /***********************************************************************
1084 * COMBO_Size
1086 static void COMBO_Size( LPHEADCOMBO lphc )
1088 RECT rect;
1089 INT w, h;
1091 GetWindowRect( lphc->self->hwndSelf, &rect );
1092 w = rect.right - rect.left; h = rect.bottom - rect.top;
1094 TRACE(combo,"w = %i, h = %i\n", w, h );
1096 /* CreateWindow() may send a bogus WM_SIZE, ignore it */
1098 if( w == (lphc->RectCombo.right - lphc->RectCombo.left) )
1100 if( (CB_GETTYPE(lphc) == CBS_SIMPLE) &&
1101 (h == (lphc->RectCombo.bottom - lphc->RectCombo.top)) )
1102 return;
1103 else if( (lphc->dwStyle & CBS_DROPDOWN) &&
1104 (h == (lphc->RectEdit.bottom - lphc->RectEdit.top)) )
1105 return;
1107 lphc->RectCombo = rect;
1108 CBCalcPlacement( lphc, &lphc->RectEdit, &lphc->RectButton, &rect );
1109 CBResetPos( lphc, &rect, TRUE );
1113 /***********************************************************************
1114 * COMBO_Font
1116 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
1118 RECT rect;
1120 lphc->hFont = hFont;
1122 if( lphc->wState & CBF_EDIT )
1123 SendMessageA( lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw );
1124 SendMessageA( lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw );
1126 GetWindowRect( lphc->self->hwndSelf, &rect );
1127 OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1128 rect.top - lphc->RectCombo.top );
1129 CBCalcPlacement( lphc, &lphc->RectEdit,
1130 &lphc->RectButton, &rect );
1131 CBResetPos( lphc, &rect, bRedraw );
1135 /***********************************************************************
1136 * COMBO_SetItemHeight
1138 static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
1140 LRESULT lRet = CB_ERR;
1142 if( index == -1 ) /* set text field height */
1144 if( height < 32768 )
1146 RECT rect;
1148 lphc->editHeight = height;
1149 GetWindowRect( lphc->self->hwndSelf, &rect );
1150 OffsetRect( &lphc->RectCombo, rect.left - lphc->RectCombo.left,
1151 rect.top - lphc->RectCombo.top );
1152 CBCalcPlacement( lphc, &lphc->RectEdit,
1153 &lphc->RectButton, &rect );
1154 CBResetPos( lphc, &rect, TRUE );
1155 lRet = height;
1158 else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */
1159 lRet = SendMessageA( lphc->hWndLBox, LB_SETITEMHEIGHT,
1160 (WPARAM)index, (LPARAM)height );
1161 return lRet;
1164 /***********************************************************************
1165 * COMBO_SelectString
1167 static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPCSTR pText )
1169 INT index = SendMessageA( lphc->hWndLBox, LB_SELECTSTRING,
1170 (WPARAM)start, (LPARAM)pText );
1171 if( index >= 0 )
1173 if( lphc->wState & CBF_EDIT )
1174 CBUpdateEdit( lphc, index );
1175 else
1176 CBPaintText( lphc, 0 );
1178 return (LRESULT)index;
1181 /***********************************************************************
1182 * COMBO_LButtonDown
1184 static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam )
1186 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1187 BOOL bButton = PtInRect(&lphc->RectButton, pt);
1188 HWND hWnd = lphc->self->hwndSelf;
1190 if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) ||
1191 (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) )
1193 lphc->wState |= CBF_BUTTONDOWN;
1194 if( lphc->wState & CBF_DROPPED )
1196 /* got a click to cancel selection */
1198 CBRollUp( lphc, TRUE, FALSE );
1199 if( !IsWindow( hWnd ) ) return;
1201 if( lphc->wState & CBF_CAPTURE )
1203 lphc->wState &= ~CBF_CAPTURE;
1204 ReleaseCapture();
1206 lphc->wState &= ~CBF_BUTTONDOWN;
1208 else
1210 /* drop down the listbox and start tracking */
1212 lphc->wState |= CBF_CAPTURE;
1213 CBDropDown( lphc );
1214 SetCapture( hWnd );
1216 if( bButton ) CBRepaintButton( lphc );
1220 /***********************************************************************
1221 * COMBO_LButtonUp
1223 * Release capture and stop tracking if needed.
1225 static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam )
1227 if( lphc->wState & CBF_CAPTURE )
1229 lphc->wState &= ~CBF_CAPTURE;
1230 if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
1232 INT index = CBUpdateLBox( lphc );
1233 CBUpdateEdit( lphc, index );
1235 ReleaseCapture();
1238 if( lphc->wState & CBF_BUTTONDOWN )
1240 lphc->wState &= ~CBF_BUTTONDOWN;
1241 CBRepaintButton( lphc );
1245 /***********************************************************************
1246 * COMBO_MouseMove
1248 * Two things to do - track combo button and release capture when
1249 * pointer goes into the listbox.
1251 static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam )
1253 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
1254 RECT lbRect;
1256 if( lphc->wState & CBF_BUTTONDOWN )
1258 BOOL bButton = PtInRect(&lphc->RectButton, pt);
1260 if( !bButton )
1262 lphc->wState &= ~CBF_BUTTONDOWN;
1263 CBRepaintButton( lphc );
1267 GetClientRect( lphc->hWndLBox, &lbRect );
1268 MapWindowPoints( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 );
1269 if( PtInRect(&lbRect, pt) )
1271 lphc->wState &= ~CBF_CAPTURE;
1272 ReleaseCapture();
1273 if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc );
1275 /* hand over pointer tracking */
1276 SendMessageA( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam );
1281 /***********************************************************************
1282 * ComboWndProc
1284 * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm
1286 LRESULT WINAPI ComboWndProc( HWND hwnd, UINT message,
1287 WPARAM wParam, LPARAM lParam )
1289 WND* pWnd = WIN_FindWndPtr(hwnd);
1291 if( pWnd )
1293 LPHEADCOMBO lphc = CB_GETPTR(pWnd);
1295 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
1296 pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam );
1298 if( lphc || message == WM_NCCREATE )
1299 switch(message)
1302 /* System messages */
1304 case WM_NCCREATE:
1305 return COMBO_NCCreate(pWnd, lParam);
1307 case WM_NCDESTROY:
1308 COMBO_NCDestroy(lphc);
1309 break;
1311 case WM_CREATE:
1312 return COMBO_Create(lphc, pWnd, lParam);
1314 case WM_PAINT:
1315 /* wParam may contain a valid HDC! */
1316 return COMBO_Paint(lphc, wParam);
1318 case WM_ERASEBKGND:
1319 return TRUE;
1321 case WM_GETDLGCODE:
1322 return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS);
1324 case WM_SIZE:
1325 if( lphc->hWndLBox &&
1326 !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc );
1327 return TRUE;
1329 case WM_SETFONT:
1330 COMBO_Font( lphc, (HFONT16)wParam, (BOOL)lParam );
1331 return TRUE;
1333 case WM_GETFONT:
1334 return (LRESULT)lphc->hFont;
1336 case WM_SETFOCUS:
1337 if( lphc->wState & CBF_EDIT )
1338 SetFocus( lphc->hWndEdit );
1339 else
1340 COMBO_SetFocus( lphc );
1341 return TRUE;
1343 case WM_KILLFOCUS:
1344 #define hwndFocus ((HWND16)wParam)
1345 if( !hwndFocus ||
1346 (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
1347 COMBO_KillFocus( lphc );
1348 #undef hwndFocus
1349 return TRUE;
1351 case WM_COMMAND:
1352 return COMBO_Command( lphc, wParam, (HWND)lParam );
1354 case WM_GETTEXT:
1355 return COMBO_GetText( lphc, (UINT)wParam, (LPSTR)lParam );
1357 case WM_SETTEXT:
1358 case WM_GETTEXTLENGTH:
1359 case WM_CLEAR:
1360 case WM_CUT:
1361 case WM_PASTE:
1362 case WM_COPY:
1363 if( lphc->wState & CBF_EDIT )
1364 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1365 return CB_ERR;
1367 case WM_DRAWITEM:
1368 case WM_DELETEITEM:
1369 case WM_COMPAREITEM:
1370 case WM_MEASUREITEM:
1371 return COMBO_ItemOp( lphc, message, wParam, lParam );
1373 case WM_ENABLE:
1374 if( lphc->wState & CBF_EDIT )
1375 EnableWindow( lphc->hWndEdit, (BOOL)wParam );
1376 EnableWindow( lphc->hWndLBox, (BOOL)wParam );
1377 return TRUE;
1379 case WM_SETREDRAW:
1380 if( wParam )
1381 lphc->wState &= ~CBF_NOREDRAW;
1382 else
1383 lphc->wState |= CBF_NOREDRAW;
1385 if( lphc->wState & CBF_EDIT )
1386 SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1387 SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1388 return 0;
1390 case WM_SYSKEYDOWN:
1391 if( KEYDATA_ALT & HIWORD(lParam) )
1392 if( wParam == VK_UP || wParam == VK_DOWN )
1393 COMBO_FlipListbox( lphc, TRUE );
1394 break;
1396 case WM_CHAR:
1397 case WM_KEYDOWN:
1398 if( lphc->wState & CBF_EDIT )
1399 return SendMessageA( lphc->hWndEdit, message, wParam, lParam );
1400 else
1401 return SendMessageA( lphc->hWndLBox, message, wParam, lParam );
1403 case WM_LBUTTONDOWN:
1404 if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self->hwndSelf );
1405 if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
1406 return TRUE;
1408 case WM_LBUTTONUP:
1409 COMBO_LButtonUp( lphc, lParam );
1410 return TRUE;
1412 case WM_MOUSEMOVE:
1413 if( lphc->wState & CBF_CAPTURE )
1414 COMBO_MouseMove( lphc, wParam, lParam );
1415 return TRUE;
1417 /* Combo messages */
1419 case CB_ADDSTRING16:
1420 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1421 case CB_ADDSTRING:
1422 return SendMessageA( lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
1424 case CB_INSERTSTRING16:
1425 wParam = (INT)(INT16)wParam;
1426 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1427 case CB_INSERTSTRING:
1428 return SendMessageA( lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
1430 case CB_DELETESTRING16:
1431 case CB_DELETESTRING:
1432 return SendMessageA( lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
1434 case CB_SELECTSTRING16:
1435 wParam = (INT)(INT16)wParam;
1436 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1437 case CB_SELECTSTRING:
1438 return COMBO_SelectString( lphc, (INT)wParam, (LPSTR)lParam );
1440 case CB_FINDSTRING16:
1441 wParam = (INT)(INT16)wParam;
1442 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1443 case CB_FINDSTRING:
1444 return SendMessageA( lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
1446 case CB_FINDSTRINGEXACT16:
1447 wParam = (INT)(INT16)wParam;
1448 if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1449 case CB_FINDSTRINGEXACT:
1450 return SendMessageA( lphc->hWndLBox, LB_FINDSTRINGEXACT,
1451 wParam, lParam );
1452 case CB_SETITEMHEIGHT16:
1453 wParam = (INT)(INT16)wParam; /* signed integer */
1454 case CB_SETITEMHEIGHT:
1455 return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
1457 case CB_GETITEMHEIGHT16:
1458 wParam = (INT)(INT16)wParam;
1459 case CB_GETITEMHEIGHT:
1460 if( (INT)wParam >= 0 ) /* listbox item */
1461 return SendMessageA( lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
1462 return (lphc->RectEdit.bottom - lphc->RectEdit.top);
1464 case CB_RESETCONTENT16:
1465 case CB_RESETCONTENT:
1466 SendMessageA( lphc->hWndLBox, LB_RESETCONTENT, 0, 0 );
1467 CBPaintText( lphc, 0 );
1468 return TRUE;
1470 case CB_INITSTORAGE:
1471 return SendMessageA( lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
1473 case CB_GETHORIZONTALEXTENT:
1474 return SendMessageA( lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
1476 case CB_SETHORIZONTALEXTENT:
1477 return SendMessageA( lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
1479 case CB_GETTOPINDEX:
1480 return SendMessageA( lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
1482 case CB_GETLOCALE:
1483 return SendMessageA( lphc->hWndLBox, LB_GETLOCALE, 0, 0);
1485 case CB_SETLOCALE:
1486 return SendMessageA( lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
1488 case CB_GETDROPPEDWIDTH:
1489 if( lphc->droppedWidth )
1490 return lphc->droppedWidth;
1491 return lphc->RectCombo.right - lphc->RectCombo.left -
1492 (lphc->wState & CBF_EDIT) ? CBitOffset : 0;
1494 case CB_SETDROPPEDWIDTH:
1495 if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
1496 (INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
1497 return CB_ERR;
1499 case CB_GETDROPPEDCONTROLRECT16:
1500 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1501 if( lParam )
1503 RECT r;
1504 CBGetDroppedControlRect( lphc, &r );
1505 CONV_RECT32TO16( &r, (LPRECT16)lParam );
1507 return CB_OKAY;
1509 case CB_GETDROPPEDCONTROLRECT:
1510 if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
1511 return CB_OKAY;
1513 case CB_GETDROPPEDSTATE16:
1514 case CB_GETDROPPEDSTATE:
1515 return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
1517 case CB_DIR16:
1518 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1519 /* fall through */
1520 case CB_DIR:
1521 return COMBO_Directory( lphc, (UINT)wParam,
1522 (LPSTR)lParam, (message == CB_DIR));
1523 case CB_SHOWDROPDOWN16:
1524 case CB_SHOWDROPDOWN:
1525 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
1527 if( wParam )
1529 if( !(lphc->wState & CBF_DROPPED) )
1530 CBDropDown( lphc );
1532 else
1533 if( lphc->wState & CBF_DROPPED )
1534 CBRollUp( lphc, FALSE, TRUE );
1536 return TRUE;
1538 case CB_GETCOUNT16:
1539 case CB_GETCOUNT:
1540 return SendMessageA( lphc->hWndLBox, LB_GETCOUNT, 0, 0);
1542 case CB_GETCURSEL16:
1543 case CB_GETCURSEL:
1544 return SendMessageA( lphc->hWndLBox, LB_GETCURSEL, 0, 0);
1546 case CB_SETCURSEL16:
1547 wParam = (INT)(INT16)wParam;
1548 case CB_SETCURSEL:
1549 lParam = SendMessageA( lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
1550 if( lphc->wState & CBF_SELCHANGE )
1552 /* no LBN_SELCHANGE in this case, update manually */
1554 CBPaintText( lphc, 0 );
1555 lphc->wState &= ~CBF_SELCHANGE;
1557 return lParam;
1559 case CB_GETLBTEXT16:
1560 wParam = (INT)(INT16)wParam;
1561 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
1562 case CB_GETLBTEXT:
1563 return SendMessageA( lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
1565 case CB_GETLBTEXTLEN16:
1566 wParam = (INT)(INT16)wParam;
1567 case CB_GETLBTEXTLEN:
1568 return SendMessageA( lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
1570 case CB_GETITEMDATA16:
1571 wParam = (INT)(INT16)wParam;
1572 case CB_GETITEMDATA:
1573 return SendMessageA( lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
1575 case CB_SETITEMDATA16:
1576 wParam = (INT)(INT16)wParam;
1577 case CB_SETITEMDATA:
1578 return SendMessageA( lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
1580 case CB_GETEDITSEL16:
1581 wParam = lParam = 0; /* just in case */
1582 case CB_GETEDITSEL:
1583 if( lphc->wState & CBF_EDIT )
1585 INT a, b;
1587 return SendMessageA( lphc->hWndEdit, EM_GETSEL,
1588 (wParam) ? wParam : (WPARAM)&a,
1589 (lParam) ? lParam : (LPARAM)&b );
1591 return CB_ERR;
1593 case CB_SETEDITSEL16:
1594 case CB_SETEDITSEL:
1595 if( lphc->wState & CBF_EDIT )
1596 return SendMessageA( lphc->hWndEdit, EM_SETSEL,
1597 (INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
1598 return CB_ERR;
1600 case CB_SETEXTENDEDUI16:
1601 case CB_SETEXTENDEDUI:
1602 if( CB_GETTYPE(lphc) == CBS_SIMPLE ) return CB_ERR;
1604 if( wParam )
1605 lphc->wState |= CBF_EUI;
1606 else lphc->wState &= ~CBF_EUI;
1607 return CB_OKAY;
1609 case CB_GETEXTENDEDUI16:
1610 case CB_GETEXTENDEDUI:
1611 return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
1613 case (WM_USER + 0x1B):
1614 WARN(combo, "[%04x]: undocumented msg!\n", hwnd );
1616 return DefWindowProcA(hwnd, message, wParam, lParam);
1618 return CB_ERR;