| /* | |
| * Combo controls | |
| * | |
| * Copyright 1997 Alex Korobka | |
| * | |
| * FIXME: roll up in Netscape 3.01. | |
| */ | |
| #include <string.h> | |
| #include "windows.h" | |
| #include "sysmetrics.h" | |
| #include "win.h" | |
| #include "spy.h" | |
| #include "user.h" | |
| #include "heap.h" | |
| #include "combo.h" | |
| #include "drive.h" | |
| #include "debug.h" | |
| /* bits in the dwKeyData */ | |
| #define KEYDATA_ALT 0x2000 | |
| #define KEYDATA_PREVSTATE 0x4000 | |
| /* | |
| * Additional combo box definitions | |
| */ | |
| #define CB_GETPTR( wnd ) (*(LPHEADCOMBO*)((wnd)->wExtra)) | |
| #define CB_NOTIFY( lphc, code ) \ | |
| (SendMessage32A( (lphc)->owner, WM_COMMAND, \ | |
| MAKEWPARAM((lphc)->self->wIDmenu, (code)), (lphc)->self->hwndSelf)) | |
| #define CB_GETEDITTEXTLENGTH( lphc ) \ | |
| (SendMessage32A( (lphc)->hWndEdit, WM_GETTEXTLENGTH, 0, 0 )) | |
| static HBITMAP32 hComboBmp = 0; | |
| static UINT32 CBitHeight, CBitWidth; | |
| static UINT32 CBitOffset = 8; | |
| /*********************************************************************** | |
| * COMBO_Init | |
| * | |
| * Load combo button bitmap. | |
| */ | |
| static BOOL32 COMBO_Init() | |
| { | |
| HDC32 hDC; | |
| if( hComboBmp ) return TRUE; | |
| if( (hDC = CreateCompatibleDC32(0)) ) | |
| { | |
| BOOL32 bRet = FALSE; | |
| if( (hComboBmp = LoadBitmap32A(0, MAKEINTRESOURCE32A(OBM_COMBO))) ) | |
| { | |
| BITMAP32 bm; | |
| HBITMAP32 hPrevB; | |
| RECT32 r; | |
| GetObject32A( hComboBmp, sizeof(bm), &bm ); | |
| CBitHeight = bm.bmHeight; | |
| CBitWidth = bm.bmWidth; | |
| TRACE(combo, "combo bitmap [%i,%i]\n", CBitWidth, CBitHeight ); | |
| hPrevB = SelectObject16( hDC, hComboBmp); | |
| SetRect32( &r, 0, 0, CBitWidth, CBitHeight ); | |
| InvertRect32( hDC, &r ); | |
| SelectObject32( hDC, hPrevB ); | |
| bRet = TRUE; | |
| } | |
| DeleteDC32( hDC ); | |
| return bRet; | |
| } | |
| return FALSE; | |
| } | |
| /*********************************************************************** | |
| * COMBO_NCCreate | |
| */ | |
| static LRESULT COMBO_NCCreate(WND* wnd, LPARAM lParam) | |
| { | |
| LPHEADCOMBO lphc; | |
| if ( wnd && COMBO_Init() && | |
| (lphc = HeapAlloc(GetProcessHeap(), 0, sizeof(HEADCOMBO))) ) | |
| { | |
| LPCREATESTRUCT32A lpcs = (CREATESTRUCT32A*)lParam; | |
| memset( lphc, 0, sizeof(HEADCOMBO) ); | |
| *(LPHEADCOMBO*)wnd->wExtra = lphc; | |
| /* some braindead apps do try to use scrollbar/border flags */ | |
| lphc->dwStyle = (lpcs->style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL)); | |
| wnd->dwStyle &= ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL); | |
| if( !(lpcs->style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) ) | |
| lphc->dwStyle |= CBS_HASSTRINGS; | |
| if( !(wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) ) | |
| lphc->wState |= CBF_NOTIFY; | |
| TRACE(combo, "[0x%08x], style = %08x\n", | |
| (UINT32)lphc, lphc->dwStyle ); | |
| return (LRESULT)(UINT32)wnd->hwndSelf; | |
| } | |
| return (LRESULT)FALSE; | |
| } | |
| /*********************************************************************** | |
| * COMBO_NCDestroy | |
| */ | |
| static LRESULT COMBO_NCDestroy( LPHEADCOMBO lphc ) | |
| { | |
| if( lphc ) | |
| { | |
| WND* wnd = lphc->self; | |
| TRACE(combo,"[%04x]: freeing storage\n", CB_HWND(lphc)); | |
| if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox ) | |
| DestroyWindow32( lphc->hWndLBox ); | |
| HeapFree( GetProcessHeap(), 0, lphc ); | |
| wnd->wExtra[0] = 0; | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * CBGetDefaultTextHeight | |
| */ | |
| static void CBGetDefaultTextHeight( LPHEADCOMBO lphc, LPSIZE32 lpSize ) | |
| { | |
| if( lphc->editHeight ) /* explicitly set height */ | |
| lpSize->cy = lphc->editHeight; | |
| else | |
| { | |
| HDC32 hDC = GetDC32( lphc->self->hwndSelf ); | |
| HFONT32 hPrevFont = 0; | |
| if( lphc->hFont ) hPrevFont = SelectObject32( hDC, lphc->hFont ); | |
| GetTextExtentPoint32A( hDC, "0", 1, lpSize); | |
| lpSize->cy += lpSize->cy / 4 + 4 * SYSMETRICS_CYBORDER; | |
| if( hPrevFont ) SelectObject32( hDC, hPrevFont ); | |
| ReleaseDC32( lphc->self->hwndSelf, hDC ); | |
| } | |
| lpSize->cx = lphc->RectCombo.right - lphc->RectCombo.left; | |
| } | |
| /*********************************************************************** | |
| * CBCalcPlacement | |
| * | |
| * Set up component coordinates given valid lphc->RectCombo. | |
| */ | |
| static void CBCalcPlacement( LPHEADCOMBO lphc, LPRECT32 lprEdit, | |
| LPRECT32 lprButton, LPRECT32 lprLB ) | |
| { | |
| RECT32 rect = lphc->RectCombo; | |
| SIZE32 size; | |
| /* get combo height and width */ | |
| if( CB_OWNERDRAWN(lphc) ) | |
| { | |
| UINT32 u = lphc->RectEdit.bottom - lphc->RectEdit.top; | |
| if( lphc->wState & CBF_MEASUREITEM ) /* first initialization */ | |
| { | |
| MEASUREITEMSTRUCT32 mi32; | |
| /* calculate defaults before sending WM_MEASUREITEM */ | |
| CBGetDefaultTextHeight( lphc, &size ); | |
| lphc->wState &= ~CBF_MEASUREITEM; | |
| mi32.CtlType = ODT_COMBOBOX; | |
| mi32.CtlID = lphc->self->wIDmenu; | |
| mi32.itemID = -1; | |
| mi32.itemWidth = size.cx; | |
| mi32.itemHeight = size.cy - 6; /* ownerdrawn cb is taller */ | |
| mi32.itemData = 0; | |
| SendMessage32A(lphc->owner, WM_MEASUREITEM, | |
| (WPARAM32)mi32.CtlID, (LPARAM)&mi32); | |
| u = 6 + (UINT16)mi32.itemHeight; | |
| } | |
| else | |
| size.cx = rect.right - rect.left; | |
| size.cy = u; | |
| } | |
| else | |
| CBGetDefaultTextHeight( lphc, &size ); | |
| /* calculate text and button placement */ | |
| lprEdit->left = lprEdit->top = lprButton->top = 0; | |
| if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* no button */ | |
| lprButton->left = lprButton->right = lprButton->bottom = 0; | |
| else | |
| { | |
| INT32 i = size.cx - CBitWidth - 10; /* seems ok */ | |
| lprButton->right = size.cx; | |
| lprButton->left = (INT16)i; | |
| lprButton->bottom = lprButton->top + size.cy; | |
| if( i < 0 ) size.cx = 0; | |
| else size.cx = i; | |
| } | |
| if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | |
| { | |
| size.cx -= CBitOffset; | |
| if( size.cx < 0 ) size.cx = 0; | |
| } | |
| lprEdit->right = size.cx; lprEdit->bottom = size.cy; | |
| /* listbox placement */ | |
| lprLB->left = ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset; | |
| lprLB->top = lprEdit->bottom - SYSMETRICS_CYBORDER; | |
| lprLB->right = rect.right - rect.left; | |
| lprLB->bottom = rect.bottom - rect.top; | |
| if( lphc->droppedWidth > (lprLB->right - lprLB->left) ) | |
| lprLB->right = lprLB->left + lphc->droppedWidth; | |
| TRACE(combo,"[%04x]: (%i,%i-%i,%i) placement\n", | |
| CB_HWND(lphc), lphc->RectCombo.left, lphc->RectCombo.top, | |
| lphc->RectCombo.right, lphc->RectCombo.bottom); | |
| TRACE(combo,"\ttext\t= (%i,%i-%i,%i)\n", | |
| lprEdit->left, lprEdit->top, lprEdit->right, lprEdit->bottom); | |
| TRACE(combo,"\tbutton\t= (%i,%i-%i,%i)\n", | |
| lprButton->left, lprButton->top, lprButton->right, lprButton->bottom); | |
| TRACE(combo,"\tlbox\t= (%i,%i-%i,%i)\n", | |
| lprLB->left, lprLB->top, lprLB->right, lprLB->bottom ); | |
| } | |
| /*********************************************************************** | |
| * CBGetDroppedControlRect32 | |
| */ | |
| static void CBGetDroppedControlRect32( LPHEADCOMBO lphc, LPRECT32 lpRect) | |
| { | |
| lpRect->left = lphc->RectCombo.left + | |
| (lphc->wState & CBF_EDIT) ? CBitOffset : 0; | |
| lpRect->top = lphc->RectCombo.top + lphc->RectEdit.bottom - | |
| SYSMETRICS_CYBORDER; | |
| lpRect->right = lphc->RectCombo.right; | |
| lpRect->bottom = lphc->RectCombo.bottom - SYSMETRICS_CYBORDER; | |
| } | |
| /*********************************************************************** | |
| * COMBO_Create | |
| */ | |
| static LRESULT COMBO_Create( LPHEADCOMBO lphc, WND* wnd, LPARAM lParam) | |
| { | |
| static char clbName[] = "ComboLBox"; | |
| static char editName[] = "Edit"; | |
| LPCREATESTRUCT32A lpcs = (CREATESTRUCT32A*)lParam; | |
| if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE; | |
| else if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT; | |
| lphc->self = wnd; | |
| lphc->owner = lpcs->hwndParent; | |
| /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */ | |
| if( lphc->owner || !(lpcs->style & WS_VISIBLE) ) | |
| { | |
| UINT32 lbeStyle; | |
| RECT32 editRect, btnRect, lbRect; | |
| GetWindowRect32( wnd->hwndSelf, &lphc->RectCombo ); | |
| lphc->wState |= CBF_MEASUREITEM; | |
| CBCalcPlacement( lphc, &editRect, &btnRect, &lbRect ); | |
| lphc->RectButton = btnRect; | |
| lphc->droppedWidth = lphc->editHeight = 0; | |
| /* create listbox popup */ | |
| lbeStyle = (LBS_NOTIFY | WS_BORDER | WS_CLIPSIBLINGS) | | |
| (lpcs->style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)); | |
| if( lphc->dwStyle & CBS_SORT ) | |
| lbeStyle |= LBS_SORT; | |
| if( lphc->dwStyle & CBS_HASSTRINGS ) | |
| lbeStyle |= LBS_HASSTRINGS; | |
| if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT ) | |
| lbeStyle |= LBS_NOINTEGRALHEIGHT; | |
| if( lphc->dwStyle & CBS_DISABLENOSCROLL ) | |
| lbeStyle |= LBS_DISABLENOSCROLL; | |
| if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */ | |
| lbeStyle |= WS_CHILD | WS_VISIBLE; | |
| else /* popup listbox */ | |
| { | |
| lbeStyle |= WS_POPUP; | |
| OffsetRect32( &lbRect, lphc->RectCombo.left, lphc->RectCombo.top ); | |
| } | |
| /* Dropdown ComboLBox is not a child window and we cannot pass | |
| * ID_CB_LISTBOX directly because it will be treated as a menu handle. | |
| */ | |
| lphc->hWndLBox = CreateWindowEx32A( 0, clbName, NULL, lbeStyle, | |
| lbRect.left + SYSMETRICS_CXBORDER, | |
| lbRect.top + SYSMETRICS_CYBORDER, | |
| lbRect.right - lbRect.left - 2 * SYSMETRICS_CXBORDER, | |
| lbRect.bottom - lbRect.top - 2 * SYSMETRICS_CYBORDER, | |
| lphc->self->hwndSelf, | |
| (lphc->dwStyle & CBS_DROPDOWN)? (HMENU32)0 : (HMENU32)ID_CB_LISTBOX, | |
| lphc->self->hInstance, (LPVOID)lphc ); | |
| if( lphc->hWndLBox ) | |
| { | |
| BOOL32 bEdit = TRUE; | |
| lbeStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NOHIDESEL | ES_LEFT; | |
| if( lphc->wState & CBF_EDIT ) | |
| { | |
| if( lphc->dwStyle & CBS_OEMCONVERT ) | |
| lbeStyle |= ES_OEMCONVERT; | |
| if( lphc->dwStyle & CBS_AUTOHSCROLL ) | |
| lbeStyle |= ES_AUTOHSCROLL; | |
| if( lphc->dwStyle & CBS_LOWERCASE ) | |
| lbeStyle |= ES_LOWERCASE; | |
| else if( lphc->dwStyle & CBS_UPPERCASE ) | |
| lbeStyle |= ES_UPPERCASE; | |
| lphc->hWndEdit = CreateWindowEx32A( 0, editName, NULL, lbeStyle, | |
| editRect.left, editRect.top, editRect.right - editRect.left, | |
| editRect.bottom - editRect.top, lphc->self->hwndSelf, | |
| (HMENU32)ID_CB_EDIT, lphc->self->hInstance, NULL ); | |
| if( !lphc->hWndEdit ) bEdit = FALSE; | |
| } | |
| if( bEdit ) | |
| { | |
| lphc->RectEdit = editRect; | |
| if( CB_GETTYPE(lphc) != CBS_SIMPLE ) | |
| { | |
| lphc->wState |= CBF_NORESIZE; | |
| SetWindowPos32( wnd->hwndSelf, 0, 0, 0, | |
| lphc->RectCombo.right - lphc->RectCombo.left, | |
| lphc->RectEdit.bottom - lphc->RectEdit.top, | |
| SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE ); | |
| lphc->wState &= ~CBF_NORESIZE; | |
| } | |
| TRACE(combo,"init done\n"); | |
| return wnd->hwndSelf; | |
| } | |
| ERR(combo, "edit control failure.\n"); | |
| } else ERR(combo, "listbox failure.\n"); | |
| } else ERR(combo, "no owner for visible combo.\n"); | |
| /* CreateWindow() will send WM_NCDESTROY to cleanup */ | |
| return -1; | |
| } | |
| /*********************************************************************** | |
| * CBPaintButton | |
| * | |
| * Paint combo button (normal, pressed, and disabled states). | |
| */ | |
| static void CBPaintButton(LPHEADCOMBO lphc, HDC32 hdc) | |
| { | |
| RECT32 r; | |
| UINT32 x, y; | |
| BOOL32 bBool; | |
| HDC32 hMemDC; | |
| HBRUSH32 hPrevBrush; | |
| COLORREF oldTextColor, oldBkColor; | |
| if( lphc->wState & CBF_NOREDRAW ) return; | |
| hPrevBrush = SelectObject32(hdc, GetSysColorBrush32(COLOR_BTNFACE)); | |
| CONV_RECT16TO32( &lphc->RectButton, &r ); | |
| Rectangle32(hdc, r.left, r.top, r.right, r.bottom ); | |
| if( (bBool = lphc->wState & CBF_BUTTONDOWN) ) | |
| { | |
| DrawEdge32( hdc, &r, EDGE_SUNKEN, BF_RECT ); | |
| OffsetRect32( &r, 1, 1 ); | |
| } else { | |
| r.top++, r.left++; | |
| DrawEdge32( hdc, &r, EDGE_RAISED, BF_RECT ); | |
| r.top--, r.left--; | |
| } | |
| InflateRect32( &r, -1, -1 ); | |
| x = (r.left + r.right - CBitWidth) >> 1; | |
| y = (r.top + r.bottom - CBitHeight) >> 1; | |
| InflateRect32( &r, -3, -3 ); | |
| hMemDC = CreateCompatibleDC32( hdc ); | |
| SelectObject32( hMemDC, hComboBmp ); | |
| oldTextColor = SetTextColor32( hdc, GetSysColor32(COLOR_BTNFACE) ); | |
| oldBkColor = SetBkColor32( hdc, CB_DISABLED(lphc) ? RGB(128,128,128) : | |
| RGB(0,0,0) ); | |
| BitBlt32( hdc, x, y, 8, 8, hMemDC, 0, 0, SRCCOPY ); | |
| SetBkColor32( hdc, oldBkColor ); | |
| SetTextColor32( hdc, oldTextColor ); | |
| DeleteDC32( hMemDC ); | |
| SelectObject32( hdc, hPrevBrush ); | |
| } | |
| /*********************************************************************** | |
| * CBPaintText | |
| * | |
| * Paint CBS_DROPDOWNLIST text field / update edit control contents. | |
| */ | |
| static void CBPaintText(LPHEADCOMBO lphc, HDC32 hdc) | |
| { | |
| INT32 id, size = 0; | |
| LPSTR pText = NULL; | |
| if( lphc->wState & CBF_NOREDRAW ) return; | |
| /* follow Windows combobox that sends a bunch of text | |
| * inquiries to its listbox while processing WM_PAINT. */ | |
| if( (id = SendMessage32A(lphc->hWndLBox, LB_GETCURSEL32, 0, 0) ) != LB_ERR ) | |
| { | |
| size = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, id, 0); | |
| if( (pText = HeapAlloc( GetProcessHeap(), 0, size + 1)) ) | |
| { | |
| SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, (WPARAM32)id, (LPARAM)pText ); | |
| pText[size] = '\0'; /* just in case */ | |
| } else return; | |
| } | |
| if( lphc->wState & CBF_EDIT ) | |
| { | |
| if( CB_HASSTRINGS(lphc) ) SetWindowText32A( lphc->hWndEdit, pText ? pText : "" ); | |
| if( lphc->wState & CBF_FOCUSED ) | |
| SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1)); | |
| } | |
| else /* paint text field ourselves */ | |
| { | |
| HBRUSH32 hPrevBrush = 0; | |
| HDC32 hDC = hdc; | |
| if( !hDC ) | |
| { | |
| if ((hDC = GetDC32(lphc->self->hwndSelf))) | |
| { | |
| HBRUSH32 hBrush = SendMessage32A( lphc->owner, | |
| WM_CTLCOLORLISTBOX, | |
| hDC, lphc->self->hwndSelf ); | |
| hPrevBrush = SelectObject32( hDC, | |
| (hBrush) ? hBrush : GetStockObject32(WHITE_BRUSH) ); | |
| } | |
| } | |
| if( hDC ) | |
| { | |
| RECT32 rect; | |
| UINT32 itemState; | |
| HFONT32 hPrevFont = (lphc->hFont) ? SelectObject32(hDC, lphc->hFont) : 0; | |
| PatBlt32( hDC, (rect.left = lphc->RectEdit.left + SYSMETRICS_CXBORDER), | |
| (rect.top = lphc->RectEdit.top + SYSMETRICS_CYBORDER), | |
| (rect.right = lphc->RectEdit.right - SYSMETRICS_CXBORDER), | |
| (rect.bottom = lphc->RectEdit.bottom - SYSMETRICS_CYBORDER) - 1, PATCOPY ); | |
| InflateRect32( &rect, -1, -1 ); | |
| if( lphc->wState & CBF_FOCUSED && | |
| !(lphc->wState & CBF_DROPPED) ) | |
| { | |
| /* highlight */ | |
| FillRect32( hDC, &rect, GetSysColorBrush32(COLOR_HIGHLIGHT) ); | |
| SetBkColor32( hDC, GetSysColor32( COLOR_HIGHLIGHT ) ); | |
| SetTextColor32( hDC, GetSysColor32( COLOR_HIGHLIGHTTEXT ) ); | |
| itemState = ODS_SELECTED | ODS_FOCUS; | |
| } else itemState = 0; | |
| if( CB_OWNERDRAWN(lphc) ) | |
| { | |
| DRAWITEMSTRUCT32 dis; | |
| if( lphc->self->dwStyle & WS_DISABLED ) itemState |= ODS_DISABLED; | |
| dis.CtlType = ODT_COMBOBOX; | |
| dis.CtlID = lphc->self->wIDmenu; | |
| dis.hwndItem = lphc->self->hwndSelf; | |
| dis.itemAction = ODA_DRAWENTIRE; | |
| dis.itemID = id; | |
| dis.itemState = itemState; | |
| dis.hDC = hDC; | |
| dis.rcItem = rect; | |
| dis.itemData = SendMessage32A( lphc->hWndLBox, LB_GETITEMDATA32, | |
| (WPARAM32)id, 0 ); | |
| SendMessage32A( lphc->owner, WM_DRAWITEM, | |
| lphc->self->wIDmenu, (LPARAM)&dis ); | |
| } | |
| else | |
| { | |
| ExtTextOut32A( hDC, rect.left + 1, rect.top + 1, | |
| ETO_OPAQUE | ETO_CLIPPED, &rect, | |
| pText ? pText : "" , size, NULL ); | |
| if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED)) | |
| DrawFocusRect32( hDC, &rect ); | |
| } | |
| if( hPrevFont ) SelectObject32(hDC, hPrevFont ); | |
| if( !hdc ) | |
| { | |
| if( hPrevBrush ) SelectObject32( hDC, hPrevBrush ); | |
| ReleaseDC32( lphc->self->hwndSelf, hDC ); | |
| } | |
| } | |
| } | |
| if (pText) | |
| HeapFree( GetProcessHeap(), 0, pText ); | |
| } | |
| /*********************************************************************** | |
| * COMBO_Paint | |
| */ | |
| static LRESULT COMBO_Paint(LPHEADCOMBO lphc, HDC32 hParamDC) | |
| { | |
| PAINTSTRUCT32 ps; | |
| HDC32 hDC; | |
| hDC = (hParamDC) ? hParamDC | |
| : BeginPaint32( lphc->self->hwndSelf, &ps); | |
| if( hDC && !(lphc->wState & CBF_NOREDRAW) ) | |
| { | |
| HBRUSH32 hPrevBrush, hBkgBrush; | |
| hBkgBrush = SendMessage32A( lphc->owner, WM_CTLCOLORLISTBOX, | |
| hDC, lphc->self->hwndSelf ); | |
| if( !hBkgBrush ) hBkgBrush = GetStockObject32(WHITE_BRUSH); | |
| hPrevBrush = SelectObject32( hDC, hBkgBrush ); | |
| if( !IsRectEmpty32(&lphc->RectButton) ) | |
| { | |
| /* paint everything to the right of the text field */ | |
| PatBlt32( hDC, lphc->RectEdit.right, lphc->RectEdit.top, | |
| lphc->RectButton.right - lphc->RectEdit.right, | |
| lphc->RectEdit.bottom - lphc->RectEdit.top, PATCOPY ); | |
| CBPaintButton( lphc, hDC ); | |
| } | |
| if( !(lphc->wState & CBF_EDIT) ) | |
| { | |
| /* paint text field */ | |
| HPEN32 hPrevPen = SelectObject32( hDC, GetSysColorPen32( | |
| COLOR_WINDOWFRAME) ); | |
| Rectangle32( hDC, lphc->RectEdit.left, lphc->RectEdit.top, | |
| lphc->RectEdit.right, lphc->RectButton.bottom ); | |
| SelectObject32( hDC, hPrevPen ); | |
| CBPaintText( lphc, hDC ); | |
| } | |
| if( hPrevBrush ) SelectObject32( hDC, hPrevBrush ); | |
| } | |
| if( !hParamDC ) EndPaint32(lphc->self->hwndSelf, &ps); | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * CBUpdateLBox | |
| * | |
| * Select listbox entry according to the contents of the edit control. | |
| */ | |
| static INT32 CBUpdateLBox( LPHEADCOMBO lphc ) | |
| { | |
| INT32 length, idx, ret; | |
| LPSTR pText = NULL; | |
| idx = ret = LB_ERR; | |
| length = CB_GETEDITTEXTLENGTH( lphc ); | |
| if( length > 0 ) | |
| pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1); | |
| TRACE(combo,"\t edit text length %i\n", length ); | |
| if( pText ) | |
| { | |
| if( length ) GetWindowText32A( lphc->hWndEdit, pText, length + 1); | |
| else pText[0] = '\0'; | |
| idx = SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32, | |
| (WPARAM32)(-1), (LPARAM)pText ); | |
| if( idx == LB_ERR ) idx = 0; /* select first item */ | |
| else ret = idx; | |
| HeapFree( GetProcessHeap(), 0, pText ); | |
| } | |
| /* select entry */ | |
| SendMessage32A( lphc->hWndLBox, LB_SETCURSEL32, (WPARAM32)idx, 0 ); | |
| if( idx >= 0 ) | |
| { | |
| SendMessage32A( lphc->hWndLBox, LB_SETTOPINDEX32, (WPARAM32)idx, 0 ); | |
| /* probably superfluous but Windows sends this too */ | |
| SendMessage32A( lphc->hWndLBox, LB_SETCARETINDEX32, (WPARAM32)idx, 0 ); | |
| } | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * CBUpdateEdit | |
| * | |
| * Copy a listbox entry to the edit control. | |
| */ | |
| static void CBUpdateEdit( LPHEADCOMBO lphc , INT32 index ) | |
| { | |
| INT32 length; | |
| LPSTR pText = NULL; | |
| TRACE(combo,"\t %i\n", index ); | |
| if( index == -1 ) | |
| { | |
| length = CB_GETEDITTEXTLENGTH( lphc ); | |
| if( length ) | |
| { | |
| if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) ) | |
| { | |
| GetWindowText32A( lphc->hWndEdit, pText, length + 1 ); | |
| index = SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32, | |
| (WPARAM32)(-1), (LPARAM)pText ); | |
| HeapFree( GetProcessHeap(), 0, pText ); | |
| } | |
| } | |
| } | |
| if( index >= 0 ) /* got an entry */ | |
| { | |
| length = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, (WPARAM32)index, 0); | |
| if( length ) | |
| { | |
| if( (pText = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1)) ) | |
| { | |
| SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, | |
| (WPARAM32)index, (LPARAM)pText ); | |
| SendMessage32A( lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)pText ); | |
| SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1) ); | |
| HeapFree( GetProcessHeap(), 0, pText ); | |
| } | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * CBDropDown | |
| * | |
| * Show listbox popup. | |
| */ | |
| static void CBDropDown( LPHEADCOMBO lphc ) | |
| { | |
| INT32 index; | |
| RECT32 rect; | |
| LPRECT32 pRect = NULL; | |
| TRACE(combo,"[%04x]: drop down\n", CB_HWND(lphc)); | |
| CB_NOTIFY( lphc, CBN_DROPDOWN ); | |
| /* set selection */ | |
| lphc->wState |= CBF_DROPPED; | |
| if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | |
| { | |
| index = CBUpdateLBox( lphc ); | |
| if( !(lphc->wState & CBF_CAPTURE) ) CBUpdateEdit( lphc, index ); | |
| } | |
| else | |
| { | |
| index = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 ); | |
| if( index == LB_ERR ) index = 0; | |
| SendMessage32A( lphc->hWndLBox, LB_SETTOPINDEX32, (WPARAM32)index, 0 ); | |
| SendMessage32A( lphc->hWndLBox, LB_CARETON32, 0, 0 ); | |
| pRect = &lphc->RectEdit; | |
| } | |
| /* now set popup position */ | |
| GetWindowRect32( lphc->self->hwndSelf, &rect ); | |
| rect.top += lphc->RectEdit.bottom - lphc->RectEdit.top - SYSMETRICS_CYBORDER; | |
| rect.bottom = rect.top + lphc->RectCombo.bottom - | |
| lphc->RectCombo.top - SYSMETRICS_CYBORDER; | |
| rect.right = rect.left + lphc->RectCombo.right - lphc->RectCombo.left; | |
| rect.left += ( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) ? 0 : CBitOffset; | |
| SetWindowPos32( lphc->hWndLBox, HWND_TOP, rect.left, rect.top, | |
| rect.right - rect.left, rect.bottom - rect.top, | |
| SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW); | |
| if( !(lphc->wState & CBF_NOREDRAW) ) | |
| if( pRect ) | |
| RedrawWindow32( lphc->self->hwndSelf, pRect, 0, RDW_INVALIDATE | | |
| RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN ); | |
| ShowWindow32( lphc->hWndLBox, SW_SHOWNA ); | |
| } | |
| /*********************************************************************** | |
| * CBRollUp | |
| * | |
| * Hide listbox popup. | |
| */ | |
| static void CBRollUp( LPHEADCOMBO lphc, BOOL32 ok, BOOL32 bButton ) | |
| { | |
| HWND32 hWnd = lphc->self->hwndSelf; | |
| CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL ); | |
| if( IsWindow32( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE ) | |
| { | |
| TRACE(combo,"[%04x]: roll up [%i]\n", CB_HWND(lphc), (INT32)ok ); | |
| /* always send WM_LBUTTONUP? */ | |
| SendMessage32A( lphc->hWndLBox, WM_LBUTTONUP, 0, (LPARAM)(-1) ); | |
| if( lphc->wState & CBF_DROPPED ) | |
| { | |
| RECT32 rect; | |
| lphc->wState &= ~CBF_DROPPED; | |
| ShowWindow32( lphc->hWndLBox, SW_HIDE ); | |
| if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | |
| { | |
| INT32 index = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 ); | |
| CBUpdateEdit( lphc, index ); | |
| rect = lphc->RectButton; | |
| } | |
| else | |
| { | |
| if( bButton ) | |
| UnionRect32( &rect, &lphc->RectButton, | |
| &lphc->RectEdit ); | |
| else | |
| rect = lphc->RectEdit; | |
| bButton = TRUE; | |
| } | |
| if( bButton && !(lphc->wState & CBF_NOREDRAW) ) | |
| RedrawWindow32( hWnd, &rect, 0, RDW_INVALIDATE | | |
| RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN ); | |
| CB_NOTIFY( lphc, CBN_CLOSEUP ); | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * COMBO_FlipListbox | |
| * | |
| * Used by the ComboLBox to show/hide itself in response to VK_F4, etc... | |
| */ | |
| BOOL32 COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL32 bRedrawButton ) | |
| { | |
| if( lphc->wState & CBF_DROPPED ) | |
| { | |
| CBRollUp( lphc, TRUE, bRedrawButton ); | |
| return FALSE; | |
| } | |
| CBDropDown( lphc ); | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * COMBO_GetLBWindow | |
| * | |
| * Edit control helper. | |
| */ | |
| HWND32 COMBO_GetLBWindow( WND* pWnd ) | |
| { | |
| LPHEADCOMBO lphc = CB_GETPTR(pWnd); | |
| if( lphc ) return lphc->hWndLBox; | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * CBRepaintButton | |
| */ | |
| static void CBRepaintButton( LPHEADCOMBO lphc ) | |
| { | |
| HDC32 hDC = GetDC32( lphc->self->hwndSelf ); | |
| if( hDC ) | |
| { | |
| CBPaintButton( lphc, hDC ); | |
| ReleaseDC32( lphc->self->hwndSelf, hDC ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * COMBO_SetFocus | |
| */ | |
| static void COMBO_SetFocus( LPHEADCOMBO lphc ) | |
| { | |
| if( !(lphc->wState & CBF_FOCUSED) ) | |
| { | |
| if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) | |
| SendMessage32A( lphc->hWndLBox, LB_CARETON32, 0, 0 ); | |
| if( lphc->wState & CBF_EDIT ) | |
| SendMessage32A( lphc->hWndEdit, EM_SETSEL32, 0, (LPARAM)(-1) ); | |
| lphc->wState |= CBF_FOCUSED; | |
| if( !(lphc->wState & CBF_EDIT) ) CBPaintText( lphc, 0 ); | |
| CB_NOTIFY( lphc, CBN_SETFOCUS ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * COMBO_KillFocus | |
| */ | |
| static void COMBO_KillFocus( LPHEADCOMBO lphc ) | |
| { | |
| HWND32 hWnd = lphc->self->hwndSelf; | |
| if( lphc->wState & CBF_FOCUSED ) | |
| { | |
| SendMessage32A( hWnd, WM_LBUTTONUP, 0, (LPARAM)(-1) ); | |
| CBRollUp( lphc, FALSE, TRUE ); | |
| if( IsWindow32( hWnd ) ) | |
| { | |
| if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) | |
| SendMessage32A( lphc->hWndLBox, LB_CARETOFF32, 0, 0 ); | |
| lphc->wState &= ~CBF_FOCUSED; | |
| /* redraw text */ | |
| if( lphc->wState & CBF_EDIT ) | |
| SendMessage32A( lphc->hWndEdit, EM_SETSEL32, (WPARAM32)(-1), 0 ); | |
| else CBPaintText( lphc, 0 ); | |
| CB_NOTIFY( lphc, CBN_KILLFOCUS ); | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * COMBO_Command | |
| */ | |
| static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM32 wParam, HWND32 hWnd ) | |
| { | |
| if( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd ) | |
| { | |
| /* ">> 8" makes gcc generate jump-table instead of cmp ladder */ | |
| switch( HIWORD(wParam) >> 8 ) | |
| { | |
| case (EN_SETFOCUS >> 8): | |
| TRACE(combo,"[%04x]: edit [%04x] got focus\n", | |
| CB_HWND(lphc), lphc->hWndEdit ); | |
| if( !(lphc->wState & CBF_FOCUSED) ) COMBO_SetFocus( lphc ); | |
| break; | |
| case (EN_KILLFOCUS >> 8): | |
| TRACE(combo,"[%04x]: edit [%04x] lost focus\n", | |
| CB_HWND(lphc), lphc->hWndEdit ); | |
| /* NOTE: it seems that Windows' edit control sends an | |
| * undocumented message WM_USER + 0x1B instead of this | |
| * notification (only when it happens to be a part of | |
| * the combo). ?? - AK. | |
| */ | |
| COMBO_KillFocus( lphc ); | |
| break; | |
| case (EN_CHANGE >> 8): | |
| CB_NOTIFY( lphc, CBN_EDITCHANGE ); | |
| CBUpdateLBox( lphc ); | |
| break; | |
| case (EN_UPDATE >> 8): | |
| CB_NOTIFY( lphc, CBN_EDITUPDATE ); | |
| break; | |
| case (EN_ERRSPACE >> 8): | |
| CB_NOTIFY( lphc, CBN_ERRSPACE ); | |
| } | |
| } | |
| else if( lphc->hWndLBox == hWnd ) | |
| { | |
| switch( HIWORD(wParam) ) | |
| { | |
| case LBN_ERRSPACE: | |
| CB_NOTIFY( lphc, CBN_ERRSPACE ); | |
| break; | |
| case LBN_DBLCLK: | |
| CB_NOTIFY( lphc, CBN_DBLCLK ); | |
| break; | |
| case LBN_SELCHANGE: | |
| case LBN_SELCANCEL: | |
| TRACE(combo,"[%04x]: lbox selection change [%04x]\n", | |
| CB_HWND(lphc), lphc->wState ); | |
| /* do not roll up if selection is being tracked | |
| * by arrowkeys in the dropdown listbox */ | |
| if( (lphc->wState & CBF_DROPPED) && !(lphc->wState & CBF_NOROLLUP) ) | |
| CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE ); | |
| else lphc->wState &= ~CBF_NOROLLUP; | |
| CB_NOTIFY( lphc, CBN_SELCHANGE ); | |
| CBPaintText( lphc, 0 ); | |
| /* fall through */ | |
| case LBN_SETFOCUS: | |
| case LBN_KILLFOCUS: | |
| /* nothing to do here since ComboLBox always resets the focus to its | |
| * combo/edit counterpart */ | |
| break; | |
| } | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * COMBO_ItemOp | |
| * | |
| * Fixup an ownerdrawn item operation and pass it up to the combobox owner. | |
| */ | |
| static LRESULT COMBO_ItemOp32( LPHEADCOMBO lphc, UINT32 msg, | |
| WPARAM32 wParam, LPARAM lParam ) | |
| { | |
| HWND32 hWnd = lphc->self->hwndSelf; | |
| TRACE(combo,"[%04x]: ownerdraw op %04x\n", CB_HWND(lphc), msg ); | |
| #define lpIS ((LPDELETEITEMSTRUCT32)lParam) | |
| /* two first items are the same in all 4 structs */ | |
| lpIS->CtlType = ODT_COMBOBOX; | |
| lpIS->CtlID = lphc->self->wIDmenu; | |
| switch( msg ) /* patch window handle */ | |
| { | |
| case WM_DELETEITEM: | |
| lpIS->hwndItem = hWnd; | |
| #undef lpIS | |
| break; | |
| case WM_DRAWITEM: | |
| #define lpIS ((LPDRAWITEMSTRUCT32)lParam) | |
| lpIS->hwndItem = hWnd; | |
| #undef lpIS | |
| break; | |
| case WM_COMPAREITEM: | |
| #define lpIS ((LPCOMPAREITEMSTRUCT32)lParam) | |
| lpIS->hwndItem = hWnd; | |
| #undef lpIS | |
| break; | |
| } | |
| return SendMessage32A( lphc->owner, msg, lphc->self->wIDmenu, lParam ); | |
| } | |
| /*********************************************************************** | |
| * COMBO_GetText | |
| */ | |
| static LRESULT COMBO_GetText( LPHEADCOMBO lphc, UINT32 N, LPSTR lpText) | |
| { | |
| if( lphc->wState & CBF_EDIT ) | |
| return SendMessage32A( lphc->hWndEdit, WM_GETTEXT, | |
| (WPARAM32)N, (LPARAM)lpText ); | |
| /* get it from the listbox */ | |
| if( lphc->hWndLBox ) | |
| { | |
| INT32 idx = SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0 ); | |
| if( idx != LB_ERR ) | |
| { | |
| LPSTR lpBuffer; | |
| INT32 length = SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, | |
| (WPARAM32)idx, 0 ); | |
| /* 'length' is without the terminating character */ | |
| if( length >= N ) | |
| lpBuffer = (LPSTR) HeapAlloc( GetProcessHeap(), 0, length + 1 ); | |
| else | |
| lpBuffer = lpText; | |
| if( lpBuffer ) | |
| { | |
| INT32 n = SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, | |
| (WPARAM32)idx, (LPARAM)lpBuffer ); | |
| /* truncate if buffer is too short */ | |
| if( length >= N ) | |
| { | |
| if (N && lpText) { | |
| if( n != LB_ERR ) memcpy( lpText, lpBuffer, (N>n) ? n+1 : N-1 ); | |
| lpText[N - 1] = '\0'; | |
| } | |
| HeapFree( GetProcessHeap(), 0, lpBuffer ); | |
| } | |
| return (LRESULT)n; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * CBResetPos | |
| * | |
| * This function sets window positions according to the updated | |
| * component placement struct. | |
| */ | |
| static void CBResetPos( LPHEADCOMBO lphc, LPRECT32 lbRect, BOOL32 bRedraw ) | |
| { | |
| BOOL32 bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE); | |
| /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of | |
| * sizing messages */ | |
| if( lphc->wState & CBF_EDIT ) | |
| SetWindowPos32( lphc->hWndEdit, 0, lphc->RectEdit.left, lphc->RectEdit.top, | |
| lphc->RectEdit.right - lphc->RectEdit.left, | |
| lphc->RectEdit.bottom - lphc->RectEdit.top, | |
| SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) ); | |
| if( bDrop ) | |
| OffsetRect32( lbRect, lphc->RectCombo.left, lphc->RectCombo.top ); | |
| lbRect->right -= lbRect->left; /* convert to width */ | |
| lbRect->bottom -= lbRect->top; | |
| SetWindowPos32( lphc->hWndLBox, 0, lbRect->left, lbRect->top, | |
| lbRect->right, lbRect->bottom, | |
| SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) ); | |
| if( bDrop ) | |
| { | |
| if( lphc->wState & CBF_DROPPED ) | |
| { | |
| lphc->wState &= ~CBF_DROPPED; | |
| ShowWindow32( lphc->hWndLBox, SW_HIDE ); | |
| } | |
| lphc->wState |= CBF_NORESIZE; | |
| SetWindowPos32( lphc->self->hwndSelf, 0, 0, 0, | |
| lphc->RectCombo.right - lphc->RectCombo.left, | |
| lphc->RectEdit.bottom - lphc->RectEdit.top, | |
| SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW ); | |
| lphc->wState &= ~CBF_NORESIZE; | |
| if( bRedraw && !(lphc->wState & CBF_NOREDRAW) ) | |
| RedrawWindow32( lphc->self->hwndSelf, NULL, 0, | |
| RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * COMBO_Size | |
| */ | |
| static void COMBO_Size( LPHEADCOMBO lphc ) | |
| { | |
| RECT32 rect; | |
| INT32 w, h; | |
| GetWindowRect32( lphc->self->hwndSelf, &rect ); | |
| w = rect.right - rect.left; h = rect.bottom - rect.top; | |
| TRACE(combo,"w = %i, h = %i\n", w, h ); | |
| /* CreateWindow() may send a bogus WM_SIZE, ignore it */ | |
| if( w == (lphc->RectCombo.right - lphc->RectCombo.left) ) | |
| { | |
| if( (CB_GETTYPE(lphc) == CBS_SIMPLE) && | |
| (h == (lphc->RectCombo.bottom - lphc->RectCombo.top)) ) | |
| return; | |
| else if( (lphc->dwStyle & CBS_DROPDOWN) && | |
| (h == (lphc->RectEdit.bottom - lphc->RectEdit.top)) ) | |
| return; | |
| } | |
| lphc->RectCombo = rect; | |
| CBCalcPlacement( lphc, &lphc->RectEdit, &lphc->RectButton, &rect ); | |
| CBResetPos( lphc, &rect, TRUE ); | |
| } | |
| /*********************************************************************** | |
| * COMBO_Font | |
| */ | |
| static void COMBO_Font( LPHEADCOMBO lphc, HFONT32 hFont, BOOL32 bRedraw ) | |
| { | |
| RECT32 rect; | |
| lphc->hFont = hFont; | |
| if( lphc->wState & CBF_EDIT ) | |
| SendMessage32A( lphc->hWndEdit, WM_SETFONT, (WPARAM32)hFont, bRedraw ); | |
| SendMessage32A( lphc->hWndLBox, WM_SETFONT, (WPARAM32)hFont, bRedraw ); | |
| GetWindowRect32( lphc->self->hwndSelf, &rect ); | |
| OffsetRect32( &lphc->RectCombo, rect.left - lphc->RectCombo.left, | |
| rect.top - lphc->RectCombo.top ); | |
| CBCalcPlacement( lphc, &lphc->RectEdit, | |
| &lphc->RectButton, &rect ); | |
| CBResetPos( lphc, &rect, bRedraw ); | |
| } | |
| /*********************************************************************** | |
| * COMBO_SetItemHeight | |
| */ | |
| static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT32 index, INT32 height ) | |
| { | |
| LRESULT lRet = CB_ERR; | |
| if( index == -1 ) /* set text field height */ | |
| { | |
| if( height < 32768 ) | |
| { | |
| RECT32 rect; | |
| lphc->editHeight = height; | |
| GetWindowRect32( lphc->self->hwndSelf, &rect ); | |
| OffsetRect32( &lphc->RectCombo, rect.left - lphc->RectCombo.left, | |
| rect.top - lphc->RectCombo.top ); | |
| CBCalcPlacement( lphc, &lphc->RectEdit, | |
| &lphc->RectButton, &rect ); | |
| CBResetPos( lphc, &rect, TRUE ); | |
| lRet = height; | |
| } | |
| } | |
| else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */ | |
| lRet = SendMessage32A( lphc->hWndLBox, LB_SETITEMHEIGHT32, | |
| (WPARAM32)index, (LPARAM)height ); | |
| return lRet; | |
| } | |
| /*********************************************************************** | |
| * COMBO_SelectString | |
| */ | |
| static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT32 start, LPCSTR pText ) | |
| { | |
| INT32 index = SendMessage32A( lphc->hWndLBox, LB_SELECTSTRING32, | |
| (WPARAM32)start, (LPARAM)pText ); | |
| if( index >= 0 ) | |
| { | |
| if( lphc->wState & CBF_EDIT ) | |
| CBUpdateEdit( lphc, index ); | |
| else | |
| CBPaintText( lphc, 0 ); | |
| } | |
| return (LRESULT)index; | |
| } | |
| /*********************************************************************** | |
| * COMBO_LButtonDown | |
| */ | |
| static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam ) | |
| { | |
| POINT32 pt = { LOWORD(lParam), HIWORD(lParam) }; | |
| BOOL32 bButton = PtInRect32(&lphc->RectButton, pt); | |
| HWND32 hWnd = lphc->self->hwndSelf; | |
| if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) || | |
| (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) ) | |
| { | |
| lphc->wState |= CBF_BUTTONDOWN; | |
| if( lphc->wState & CBF_DROPPED ) | |
| { | |
| /* got a click to cancel selection */ | |
| CBRollUp( lphc, TRUE, FALSE ); | |
| if( !IsWindow32( hWnd ) ) return; | |
| if( lphc->wState & CBF_CAPTURE ) | |
| { | |
| lphc->wState &= ~CBF_CAPTURE; | |
| ReleaseCapture(); | |
| } | |
| lphc->wState &= ~CBF_BUTTONDOWN; | |
| } | |
| else | |
| { | |
| /* drop down the listbox and start tracking */ | |
| lphc->wState |= CBF_CAPTURE; | |
| CBDropDown( lphc ); | |
| SetCapture32( hWnd ); | |
| } | |
| if( bButton ) CBRepaintButton( lphc ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * COMBO_LButtonUp | |
| * | |
| * Release capture and stop tracking if needed. | |
| */ | |
| static void COMBO_LButtonUp( LPHEADCOMBO lphc, LPARAM lParam ) | |
| { | |
| if( lphc->wState & CBF_CAPTURE ) | |
| { | |
| lphc->wState &= ~CBF_CAPTURE; | |
| if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) | |
| { | |
| INT32 index = CBUpdateLBox( lphc ); | |
| CBUpdateEdit( lphc, index ); | |
| } | |
| ReleaseCapture(); | |
| } | |
| if( lphc->wState & CBF_BUTTONDOWN ) | |
| { | |
| lphc->wState &= ~CBF_BUTTONDOWN; | |
| CBRepaintButton( lphc ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * COMBO_MouseMove | |
| * | |
| * Two things to do - track combo button and release capture when | |
| * pointer goes into the listbox. | |
| */ | |
| static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM32 wParam, LPARAM lParam ) | |
| { | |
| POINT32 pt = { LOWORD(lParam), HIWORD(lParam) }; | |
| RECT32 lbRect; | |
| if( lphc->wState & CBF_BUTTONDOWN ) | |
| { | |
| BOOL32 bButton = PtInRect32(&lphc->RectButton, pt); | |
| if( !bButton ) | |
| { | |
| lphc->wState &= ~CBF_BUTTONDOWN; | |
| CBRepaintButton( lphc ); | |
| } | |
| } | |
| GetClientRect32( lphc->hWndLBox, &lbRect ); | |
| MapWindowPoints32( lphc->self->hwndSelf, lphc->hWndLBox, &pt, 1 ); | |
| if( PtInRect32(&lbRect, pt) ) | |
| { | |
| lphc->wState &= ~CBF_CAPTURE; | |
| ReleaseCapture(); | |
| if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc ); | |
| /* hand over pointer tracking */ | |
| SendMessage32A( lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * ComboWndProc | |
| * | |
| * http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/ctrl/src/combobox_15.htm | |
| */ | |
| LRESULT WINAPI ComboWndProc( HWND32 hwnd, UINT32 message, | |
| WPARAM32 wParam, LPARAM lParam ) | |
| { | |
| WND* pWnd = WIN_FindWndPtr(hwnd); | |
| if( pWnd ) | |
| { | |
| LPHEADCOMBO lphc = CB_GETPTR(pWnd); | |
| TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n", | |
| pWnd->hwndSelf, SPY_GetMsgName(message), wParam, lParam ); | |
| if( lphc || message == WM_NCCREATE ) | |
| switch(message) | |
| { | |
| /* System messages */ | |
| case WM_NCCREATE: | |
| return COMBO_NCCreate(pWnd, lParam); | |
| case WM_NCDESTROY: | |
| COMBO_NCDestroy(lphc); | |
| break; | |
| case WM_CREATE: | |
| return COMBO_Create(lphc, pWnd, lParam); | |
| case WM_PAINT: | |
| /* wParam may contain a valid HDC! */ | |
| return COMBO_Paint(lphc, wParam); | |
| case WM_ERASEBKGND: | |
| return TRUE; | |
| case WM_GETDLGCODE: | |
| return (LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS); | |
| case WM_SIZE: | |
| if( lphc->hWndLBox && | |
| !(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc ); | |
| return TRUE; | |
| case WM_SETFONT: | |
| COMBO_Font( lphc, (HFONT16)wParam, (BOOL32)lParam ); | |
| return TRUE; | |
| case WM_GETFONT: | |
| return (LRESULT)lphc->hFont; | |
| case WM_SETFOCUS: | |
| if( lphc->wState & CBF_EDIT ) | |
| SetFocus32( lphc->hWndEdit ); | |
| else | |
| COMBO_SetFocus( lphc ); | |
| return TRUE; | |
| case WM_KILLFOCUS: | |
| #define hwndFocus ((HWND16)wParam) | |
| if( !hwndFocus || | |
| (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox )) | |
| COMBO_KillFocus( lphc ); | |
| #undef hwndFocus | |
| return TRUE; | |
| case WM_COMMAND: | |
| return COMBO_Command( lphc, wParam, (HWND32)lParam ); | |
| case WM_GETTEXT: | |
| return COMBO_GetText( lphc, (UINT32)wParam, (LPSTR)lParam ); | |
| case WM_SETTEXT: | |
| case WM_GETTEXTLENGTH: | |
| case WM_CLEAR: | |
| case WM_CUT: | |
| case WM_PASTE: | |
| case WM_COPY: | |
| if( lphc->wState & CBF_EDIT ) | |
| return SendMessage32A( lphc->hWndEdit, message, wParam, lParam ); | |
| return CB_ERR; | |
| case WM_DRAWITEM: | |
| case WM_DELETEITEM: | |
| case WM_COMPAREITEM: | |
| case WM_MEASUREITEM: | |
| return COMBO_ItemOp32( lphc, message, wParam, lParam ); | |
| case WM_ENABLE: | |
| if( lphc->wState & CBF_EDIT ) | |
| EnableWindow32( lphc->hWndEdit, (BOOL32)wParam ); | |
| EnableWindow32( lphc->hWndLBox, (BOOL32)wParam ); | |
| return TRUE; | |
| case WM_SETREDRAW: | |
| if( wParam ) | |
| lphc->wState &= ~CBF_NOREDRAW; | |
| else | |
| lphc->wState |= CBF_NOREDRAW; | |
| if( lphc->wState & CBF_EDIT ) | |
| SendMessage32A( lphc->hWndEdit, message, wParam, lParam ); | |
| SendMessage32A( lphc->hWndLBox, message, wParam, lParam ); | |
| return 0; | |
| case WM_SYSKEYDOWN: | |
| if( KEYDATA_ALT & HIWORD(lParam) ) | |
| if( wParam == VK_UP || wParam == VK_DOWN ) | |
| COMBO_FlipListbox( lphc, TRUE ); | |
| break; | |
| case WM_CHAR: | |
| case WM_KEYDOWN: | |
| if( lphc->wState & CBF_EDIT ) | |
| return SendMessage32A( lphc->hWndEdit, message, wParam, lParam ); | |
| else | |
| return SendMessage32A( lphc->hWndLBox, message, wParam, lParam ); | |
| case WM_LBUTTONDOWN: | |
| if( !(lphc->wState & CBF_FOCUSED) ) SetFocus32( lphc->self->hwndSelf ); | |
| if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam ); | |
| return TRUE; | |
| case WM_LBUTTONUP: | |
| COMBO_LButtonUp( lphc, lParam ); | |
| return TRUE; | |
| case WM_MOUSEMOVE: | |
| if( lphc->wState & CBF_CAPTURE ) | |
| COMBO_MouseMove( lphc, wParam, lParam ); | |
| return TRUE; | |
| /* Combo messages */ | |
| case CB_ADDSTRING16: | |
| if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| case CB_ADDSTRING32: | |
| return SendMessage32A( lphc->hWndLBox, LB_ADDSTRING32, 0, lParam); | |
| case CB_INSERTSTRING16: | |
| wParam = (INT32)(INT16)wParam; | |
| if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| case CB_INSERTSTRING32: | |
| return SendMessage32A( lphc->hWndLBox, LB_INSERTSTRING32, wParam, lParam); | |
| case CB_DELETESTRING16: | |
| case CB_DELETESTRING32: | |
| return SendMessage32A( lphc->hWndLBox, LB_DELETESTRING32, wParam, 0); | |
| case CB_SELECTSTRING16: | |
| wParam = (INT32)(INT16)wParam; | |
| if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| case CB_SELECTSTRING32: | |
| return COMBO_SelectString( lphc, (INT32)wParam, (LPSTR)lParam ); | |
| case CB_FINDSTRING16: | |
| wParam = (INT32)(INT16)wParam; | |
| if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| case CB_FINDSTRING32: | |
| return SendMessage32A( lphc->hWndLBox, LB_FINDSTRING32, wParam, lParam); | |
| case CB_FINDSTRINGEXACT16: | |
| wParam = (INT32)(INT16)wParam; | |
| if( CB_HASSTRINGS(lphc) ) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| case CB_FINDSTRINGEXACT32: | |
| return SendMessage32A( lphc->hWndLBox, LB_FINDSTRINGEXACT32, | |
| wParam, lParam ); | |
| case CB_SETITEMHEIGHT16: | |
| wParam = (INT32)(INT16)wParam; /* signed integer */ | |
| case CB_SETITEMHEIGHT32: | |
| return COMBO_SetItemHeight( lphc, (INT32)wParam, (INT32)lParam); | |
| case CB_GETITEMHEIGHT16: | |
| wParam = (INT32)(INT16)wParam; | |
| case CB_GETITEMHEIGHT32: | |
| if( (INT32)wParam >= 0 ) /* listbox item */ | |
| return SendMessage32A( lphc->hWndLBox, LB_GETITEMHEIGHT32, wParam, 0); | |
| return (lphc->RectEdit.bottom - lphc->RectEdit.top); | |
| case CB_RESETCONTENT16: | |
| case CB_RESETCONTENT32: | |
| SendMessage32A( lphc->hWndLBox, LB_RESETCONTENT32, 0, 0 ); | |
| CBPaintText( lphc, 0 ); | |
| return TRUE; | |
| case CB_INITSTORAGE32: | |
| return SendMessage32A( lphc->hWndLBox, LB_INITSTORAGE32, wParam, lParam); | |
| case CB_GETHORIZONTALEXTENT32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETHORIZONTALEXTENT32, 0, 0); | |
| case CB_SETHORIZONTALEXTENT32: | |
| return SendMessage32A( lphc->hWndLBox, LB_SETHORIZONTALEXTENT32, wParam, 0); | |
| case CB_GETTOPINDEX32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETTOPINDEX32, 0, 0); | |
| case CB_GETLOCALE32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETLOCALE32, 0, 0); | |
| case CB_SETLOCALE32: | |
| return SendMessage32A( lphc->hWndLBox, LB_SETLOCALE32, wParam, 0); | |
| case CB_GETDROPPEDWIDTH32: | |
| if( lphc->droppedWidth ) | |
| return lphc->droppedWidth; | |
| return lphc->RectCombo.right - lphc->RectCombo.left - | |
| (lphc->wState & CBF_EDIT) ? CBitOffset : 0; | |
| case CB_SETDROPPEDWIDTH32: | |
| if( (CB_GETTYPE(lphc) != CBS_SIMPLE) && | |
| (INT32)wParam < 32768 ) lphc->droppedWidth = (INT32)wParam; | |
| return CB_ERR; | |
| case CB_GETDROPPEDCONTROLRECT16: | |
| lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| if( lParam ) | |
| { | |
| RECT32 r; | |
| CBGetDroppedControlRect32( lphc, &r ); | |
| CONV_RECT32TO16( &r, (LPRECT16)lParam ); | |
| } | |
| return CB_OKAY; | |
| case CB_GETDROPPEDCONTROLRECT32: | |
| if( lParam ) CBGetDroppedControlRect32(lphc, (LPRECT32)lParam ); | |
| return CB_OKAY; | |
| case CB_GETDROPPEDSTATE16: | |
| case CB_GETDROPPEDSTATE32: | |
| return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE; | |
| case CB_DIR16: | |
| lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| /* fall through */ | |
| case CB_DIR32: | |
| return COMBO_Directory( lphc, (UINT32)wParam, | |
| (LPSTR)lParam, (message == CB_DIR32)); | |
| case CB_SHOWDROPDOWN16: | |
| case CB_SHOWDROPDOWN32: | |
| if( CB_GETTYPE(lphc) != CBS_SIMPLE ) | |
| { | |
| if( wParam ) | |
| { | |
| if( !(lphc->wState & CBF_DROPPED) ) | |
| CBDropDown( lphc ); | |
| } | |
| else | |
| if( lphc->wState & CBF_DROPPED ) | |
| CBRollUp( lphc, FALSE, TRUE ); | |
| } | |
| return TRUE; | |
| case CB_GETCOUNT16: | |
| case CB_GETCOUNT32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETCOUNT32, 0, 0); | |
| case CB_GETCURSEL16: | |
| case CB_GETCURSEL32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETCURSEL32, 0, 0); | |
| case CB_SETCURSEL16: | |
| wParam = (INT32)(INT16)wParam; | |
| case CB_SETCURSEL32: | |
| return SendMessage32A( lphc->hWndLBox, LB_SETCURSEL32, wParam, 0); | |
| case CB_GETLBTEXT16: | |
| wParam = (INT32)(INT16)wParam; | |
| lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| case CB_GETLBTEXT32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETTEXT32, wParam, lParam); | |
| case CB_GETLBTEXTLEN16: | |
| wParam = (INT32)(INT16)wParam; | |
| case CB_GETLBTEXTLEN32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETTEXTLEN32, wParam, 0); | |
| case CB_GETITEMDATA16: | |
| wParam = (INT32)(INT16)wParam; | |
| case CB_GETITEMDATA32: | |
| return SendMessage32A( lphc->hWndLBox, LB_GETITEMDATA32, wParam, 0); | |
| case CB_SETITEMDATA16: | |
| wParam = (INT32)(INT16)wParam; | |
| case CB_SETITEMDATA32: | |
| return SendMessage32A( lphc->hWndLBox, LB_SETITEMDATA32, wParam, lParam); | |
| case CB_GETEDITSEL16: | |
| wParam = lParam = 0; /* just in case */ | |
| case CB_GETEDITSEL32: | |
| if( lphc->wState & CBF_EDIT ) | |
| { | |
| INT32 a, b; | |
| return SendMessage32A( lphc->hWndEdit, EM_GETSEL32, | |
| (wParam) ? wParam : (WPARAM32)&a, | |
| (lParam) ? lParam : (LPARAM)&b ); | |
| } | |
| return CB_ERR; | |
| case CB_SETEDITSEL16: | |
| case CB_SETEDITSEL32: | |
| if( lphc->wState & CBF_EDIT ) | |
| return SendMessage32A( lphc->hWndEdit, EM_SETSEL32, | |
| (INT32)(INT16)LOWORD(lParam), (INT32)(INT16)HIWORD(lParam) ); | |
| return CB_ERR; | |
| case CB_SETEXTENDEDUI16: | |
| case CB_SETEXTENDEDUI32: | |
| if( CB_GETTYPE(lphc) == CBS_SIMPLE ) return CB_ERR; | |
| if( wParam ) | |
| lphc->wState |= CBF_EUI; | |
| else lphc->wState &= ~CBF_EUI; | |
| return CB_OKAY; | |
| case CB_GETEXTENDEDUI16: | |
| case CB_GETEXTENDEDUI32: | |
| return (lphc->wState & CBF_EUI) ? TRUE : FALSE; | |
| case (WM_USER + 0x1B): | |
| WARN(combo, "[%04x]: undocumented msg!\n", hwnd ); | |
| } | |
| return DefWindowProc32A(hwnd, message, wParam, lParam); | |
| } | |
| return CB_ERR; | |
| } | |