| /* |
| * 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 "graphics.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; |
| HBRUSH32 hPrevBrush; |
| UINT32 x, y; |
| BOOL32 bBool; |
| |
| if( lphc->wState & CBF_NOREDRAW ) return; |
| |
| hPrevBrush = (HBRUSH32)SelectObject32(hdc, GetSysColorBrush32(COLOR_BTNFACE)); |
| CONV_RECT16TO32( &lphc->RectButton, &r ); |
| |
| Rectangle32(hdc, r.left, r.top, r.right, r.bottom ); |
| InflateRect32( &r, -1, -1 ); |
| if( (bBool = lphc->wState & CBF_BUTTONDOWN) ) |
| { |
| GRAPH_DrawReliefRect(hdc, &r, 1, 0, TRUE); |
| OffsetRect32( &r, 1, 1 ); |
| } else GRAPH_DrawReliefRect(hdc, &r, 1, 2, FALSE); |
| |
| x = (r.left + r.right - CBitWidth) >> 1; |
| y = (r.top + r.bottom - CBitHeight) >> 1; |
| |
| InflateRect32( &r, -3, -3 ); |
| if( (bBool = CB_DISABLED(lphc)) ) |
| { |
| GRAPH_SelectClipMask(hdc, hComboBmp, x + 1, y + 1 ); |
| FillRect32(hdc, &r, (HBRUSH32)GetStockObject32(WHITE_BRUSH)); |
| } |
| |
| GRAPH_SelectClipMask(hdc, hComboBmp, x, y ); |
| FillRect32(hdc, &r, (HBRUSH32)GetStockObject32((bBool) ? GRAY_BRUSH : BLACK_BRUSH)); |
| |
| GRAPH_SelectClipMask(hdc, (HBITMAP32)0, 0, 0); |
| 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 */ |
| |
| GRAPH_DrawRectangle( hDC, lphc->RectEdit.left, lphc->RectEdit.top, |
| lphc->RectEdit.right - lphc->RectEdit.left, |
| lphc->RectButton.bottom - lphc->RectButton.top, |
| GetSysColorPen32(COLOR_WINDOWFRAME) ); |
| 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; |
| } |
| |