/* * Combo controls * * Copyright 1993 Martin Ayotte * Copyright 1995 Bernd Schmidt * Copyright 1996 Albrecht Kleine [some fixes] * */ #include #include #include #include #include #include "windows.h" #include "sysmetrics.h" #include "win.h" #include "combo.h" #include "user.h" #include "graphics.h" #include "heap.h" #include "listbox.h" #include "drive.h" #include "stddebug.h" #include "debug.h" #include "xmalloc.h" /* * Note: Combos are probably implemented in a different way by Windows. * Using a message spy for Windows, you can see some undocumented * messages being passed between ComboBox and ComboLBox. * I hope no programs rely on the implementation of combos. */ #define ID_EDIT 1 #define ID_CLB 2 #define CBLMM_EDGE 4 /* distance inside box which is same as moving mouse outside box, to trigger scrolling of CBL */ static BOOL32 CBCheckSize(HWND16 hwnd); static BOOL32 CBLCheckSize(HWND16 hwnd); static HBITMAP16 hComboBit = 0; static WORD CBitHeight, CBitWidth; static int COMBO_Init() { BITMAP16 bm; dprintf_combo(stddeb, "COMBO_Init\n"); hComboBit = LoadBitmap16(0, MAKEINTRESOURCE(OBM_COMBO)); GetObject16( hComboBit, sizeof(bm), &bm ); CBitHeight = bm.bmHeight; CBitWidth = bm.bmWidth; return 0; } LPHEADCOMBO ComboGetStorageHeader(HWND16 hwnd) { return (LPHEADCOMBO)GetWindowLong32A(hwnd,4); } LPHEADLIST ComboGetListHeader(HWND16 hwnd) { return (LPHEADLIST)GetWindowLong32A(hwnd,0); } int CreateComboStruct(HWND16 hwnd, LONG style) { LPHEADCOMBO lphc; lphc = (LPHEADCOMBO)xmalloc(sizeof(HEADCOMBO)); SetWindowLong32A(hwnd,4,(LONG)lphc); lphc->hWndEdit = 0; lphc->hWndLBox = 0; lphc->dwState = 0; lphc->LastSel = -1; lphc->dwStyle = style; lphc->DropDownVisible = FALSE; return TRUE; } void ComboUpdateWindow(HWND16 hwnd, LPHEADLIST lphl, LPHEADCOMBO lphc, BOOL32 repaint) { WND *wndPtr = WIN_FindWndPtr(hwnd); if (wndPtr->dwStyle & WS_VSCROLL) SetScrollRange32(lphc->hWndLBox,SB_VERT,0,ListMaxFirstVisible(lphl),TRUE); if (repaint && lphl->bRedrawFlag) InvalidateRect32( hwnd, NULL, TRUE ); } /*********************************************************************** * CBNCCreate */ static LRESULT CBNCCreate(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { CREATESTRUCT16 *createStruct; if (!hComboBit) COMBO_Init(); createStruct = (CREATESTRUCT16 *)PTR_SEG_TO_LIN(lParam); createStruct->style |= WS_BORDER; SetWindowLong32A(hwnd, GWL_STYLE, createStruct->style); dprintf_combo(stddeb,"ComboBox WM_NCCREATE!\n"); return DefWindowProc16(hwnd, WM_NCCREATE, wParam, lParam); } /*********************************************************************** * CBCreate */ static LRESULT CBCreate(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl; LPHEADCOMBO lphc; LONG style = 0; LONG cstyle = GetWindowLong32A(hwnd,GWL_STYLE); RECT16 rect,lboxrect; WND* wndPtr = WIN_FindWndPtr(hwnd); char className[] = "COMBOLBOX"; /* Hack so that class names are > 0x10000 */ char editName[] = "EDIT"; HWND16 hwndp=0; /* translate combo into listbox styles */ cstyle |= WS_BORDER; if (cstyle & CBS_OWNERDRAWFIXED) style |= LBS_OWNERDRAWFIXED; if (cstyle & CBS_OWNERDRAWVARIABLE) style |= LBS_OWNERDRAWVARIABLE; if (cstyle & CBS_SORT) style |= LBS_SORT; if (cstyle & CBS_HASSTRINGS) style |= LBS_HASSTRINGS; style |= LBS_NOTIFY; CreateListBoxStruct(hwnd, ODT_COMBOBOX, style, GetParent16(hwnd)); CreateComboStruct(hwnd,cstyle); lphl = ComboGetListHeader(hwnd); lphc = ComboGetStorageHeader(hwnd); GetClientRect16(hwnd,&rect); lphc->LBoxTop = lphl->StdItemHeight; switch(cstyle & 3) { case CBS_SIMPLE: /* edit control, list always visible */ lboxrect=rect; dprintf_combo(stddeb,"CBS_SIMPLE\n"); style= WS_BORDER | WS_CHILD | WS_VISIBLE | WS_VSCROLL; SetRectEmpty16(&lphc->RectButton); hwndp=hwnd; break; case CBS_DROPDOWNLIST: /* static control, dropdown listbox */ case CBS_DROPDOWN: /* edit control, dropdown listbox */ GetWindowRect16(hwnd,&lboxrect); style = WS_POPUP | WS_BORDER | WS_VSCROLL; /* FIXME: WinSight says these should be CHILD windows with the TOPMOST flag * set. Wine doesn't support TOPMOST, and simply setting the WS_CHILD * flag doesn't work. */ lphc->RectButton = rect; lphc->RectButton.left = lphc->RectButton.right - 6 - CBitWidth; lphc->RectButton.bottom = lphc->RectButton.top + lphl->StdItemHeight; SetWindowPos32(hwnd, 0, 0, 0, rect.right -rect.left + 2*SYSMETRICS_CXBORDER, lphl->StdItemHeight + 2*SYSMETRICS_CYBORDER, SWP_NOMOVE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOACTIVATE); dprintf_combo(stddeb,(cstyle & 3)==CBS_DROPDOWN ? "CBS_DROPDOWN\n": "CBS_DROPDOWNLIST\n"); break; default: fprintf(stderr,"COMBOBOX error: bad class style!\n"); return 0; } if ((cstyle & 3) != CBS_DROPDOWNLIST) lphc->hWndEdit = CreateWindow16( editName, NULL, WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | ES_LEFT, 0, 0, rect.right-6-CBitWidth, lphl->StdItemHeight+2*SYSMETRICS_CYBORDER, hwnd, (HMENU16)ID_EDIT, WIN_GetWindowInstance(hwnd), NULL ); lboxrect.top+=lphc->LBoxTop; lphc->hWndLBox = CreateWindow16( className, NULL, style | ((cstyle & WS_HSCROLL)? WS_HSCROLL : 0) | ((cstyle & WS_VSCROLL)? WS_VSCROLL : 0), lboxrect.left, lboxrect.top, lboxrect.right - lboxrect.left, lboxrect.bottom - lboxrect.top, hwndp,(HMENU16)ID_CLB, WIN_GetWindowInstance(hwnd), (LPVOID)(HWND32)hwnd ); wndPtr->dwStyle &= ~(WS_VSCROLL | WS_HSCROLL); dprintf_combo( stddeb, "Combo Creation hwnd=%04x LBox=%04x Edit=%04x\n", hwnd, lphc->hWndLBox, lphc->hWndEdit); dprintf_combo( stddeb, " lbox %d,%d-%d,%d button %d,%d-%d,%d\n", lboxrect.left, lboxrect.top, lboxrect.right, lboxrect.bottom, lphc->RectButton.left, lphc->RectButton.top, lphc->RectButton.right, lphc->RectButton.bottom ); dprintf_combo( stddeb, " client %d,%d-%d,%d window %d,%d-%d,%d\n", wndPtr->rectClient.left, wndPtr->rectClient.top, wndPtr->rectClient.right, wndPtr->rectClient.bottom, wndPtr->rectWindow.left, wndPtr->rectWindow.top, wndPtr->rectWindow.right, wndPtr->rectWindow.bottom ); return 0; } /*********************************************************************** * CBDestroy */ static LRESULT CBDestroy(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); if (lphc->hWndEdit) DestroyWindow32( lphc->hWndEdit ); if (lphc->hWndLBox) DestroyWindow32( lphc->hWndLBox ); return 0; } /*********************************************************************** * CBPaint */ static LRESULT CBPaint(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); LPLISTSTRUCT lpls; PAINTSTRUCT16 ps; HBRUSH32 hBrush; HFONT32 hOldFont; HDC16 hdc; RECT16 rect; hdc = BeginPaint16(hwnd, &ps); GetClientRect16(hwnd, &rect); CBCheckSize(hwnd); /* 1 for button border */ rect.right = lphc->RectButton.left - 1; if (hComboBit != 0 && !IsRectEmpty16(&lphc->RectButton)) { Rectangle32(hdc,lphc->RectButton.left-1,lphc->RectButton.top-1, lphc->RectButton.right+1,lphc->RectButton.bottom+1); { RECT32 r; CONV_RECT16TO32( &lphc->RectButton, &r ); GRAPH_DrawReliefRect(hdc, &r, 2, 2, FALSE); } GRAPH_DrawBitmap(hdc, hComboBit, lphc->RectButton.left + 2,lphc->RectButton.top + 2, 0, 0, CBitWidth, CBitHeight ); } if (!IsWindowVisible16(hwnd) || !lphl->bRedrawFlag || (lphc->dwStyle & 3) != CBS_DROPDOWNLIST) { /* we don't want to draw an entry when there is an edit control */ EndPaint16(hwnd, &ps); return 0; } hOldFont = SelectObject32(hdc, lphl->hFont); hBrush = SendMessage32A( lphl->hParent, WM_CTLCOLORLISTBOX, hdc, hwnd ); if (hBrush == 0) hBrush = GetStockObject32(WHITE_BRUSH); lpls = ListBoxGetItem(lphl,lphl->ItemFocused); if (lpls != NULL) { FillRect16(hdc, &rect, hBrush); ListBoxDrawItem (hwnd, lphl, hdc, lpls, &rect, ODA_DRAWENTIRE, 0); if (GetFocus32() == hwnd) ListBoxDrawItem (hwnd,lphl, hdc, lpls, &rect, ODA_FOCUS, ODS_FOCUS); } else FillRect16(hdc, &rect, hBrush); SelectObject32(hdc,hOldFont); EndPaint16(hwnd, &ps); return 0; } /*********************************************************************** * CBGetDlgCode */ static LRESULT CBGetDlgCode(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { return DLGC_WANTARROWS | DLGC_WANTCHARS; } /*********************************************************************** * CBLButtonDown */ static LRESULT CBLButtonDown(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); SendMessage16(hwnd,CB_SHOWDROPDOWN16,!lphc->DropDownVisible,0); return 0; } /*********************************************************************** * CBKeyDown */ static LRESULT CBKeyDown(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); WORD newFocused = lphl->ItemFocused; switch(wParam) { case VK_HOME: newFocused = 0; break; case VK_END: newFocused = lphl->ItemsCount - 1; break; case VK_UP: if (newFocused > 0) newFocused--; break; case VK_DOWN: newFocused++; break; default: return 0; } if (newFocused >= lphl->ItemsCount) newFocused = lphl->ItemsCount - 1; ListBoxSetCurSel(lphl, newFocused); SendMessage16(hwnd, WM_COMMAND,ID_CLB,MAKELONG(0,CBN_SELCHANGE)); ListBoxSendNotification(lphl, CBN_SELCHANGE); lphl->ItemFocused = newFocused; ListBoxScrollToFocus(lphl); /* SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);*/ InvalidateRect32( hwnd, NULL, TRUE ); return 0; } /*********************************************************************** * CBChar */ static LRESULT CBChar(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); WORD newFocused; newFocused = ListBoxFindNextMatch(lphl, wParam); if (newFocused == (WORD)LB_ERR) return 0; if (newFocused >= lphl->ItemsCount) newFocused = lphl->ItemsCount - 1; ListBoxSetCurSel(lphl, newFocused); SendMessage16(hwnd, WM_COMMAND,ID_CLB,MAKELONG(0,CBN_SELCHANGE)); ListBoxSendNotification(lphl, CBN_SELCHANGE); lphl->ItemFocused = newFocused; ListBoxScrollToFocus(lphl); /* SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);*/ InvalidateRect32( hwnd, NULL, TRUE ); return 0; } /*********************************************************************** * CBKillFocus */ static LRESULT CBKillFocus(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { return 0; } /*********************************************************************** * CBSetFocus */ static LRESULT CBSetFocus(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { return 0; } /*********************************************************************** * CBResetContent */ static LRESULT CBResetContent(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); ListBoxResetContent(lphl); ComboUpdateWindow(hwnd, lphl, lphc, TRUE); return 0; } /*********************************************************************** * CBDir */ static LRESULT CBDir(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { WORD wRet; LPHEADLIST lphl = ComboGetListHeader(hwnd); LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); wRet = ListBoxDirectory(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam)); ComboUpdateWindow(hwnd, lphl, lphc, TRUE); return wRet; } /*********************************************************************** * CBInsertString */ static LRESULT CBInsertString(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { WORD wRet; LPHEADLIST lphl = ComboGetListHeader(hwnd); LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); if (lphl->HasStrings) wRet = ListBoxInsertString(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam)); else wRet = ListBoxInsertString(lphl, wParam, (LPSTR)lParam); ComboUpdateWindow(hwnd, lphl, lphc, TRUE); return wRet; } /*********************************************************************** * CBAddString */ static LRESULT CBAddString(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { WORD wRet; LPHEADLIST lphl = ComboGetListHeader(hwnd); LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); wRet = ListBoxAddString(lphl, (SEGPTR)lParam); ComboUpdateWindow(hwnd, lphl, lphc, TRUE); return wRet; } /*********************************************************************** * CBDeleteString */ static LRESULT CBDeleteString(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); LONG lRet = ListBoxDeleteString(lphl,wParam); ComboUpdateWindow(hwnd, lphl, lphc, TRUE); return lRet; } /*********************************************************************** * CBSelectString */ static LRESULT CBSelectString(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); WORD wRet; wRet = ListBoxFindString(lphl, wParam, (SEGPTR)lParam); /* XXX add functionality here */ return 0; } /*********************************************************************** * CBFindString */ static LRESULT CBFindString(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return ListBoxFindString(lphl, wParam, (SEGPTR)lParam); } /*********************************************************************** * CBFindStringExact */ static LRESULT CBFindStringExact(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return ListBoxFindStringExact(lphl, wParam, (SEGPTR)lParam); } /*********************************************************************** * CBGetCount */ static LRESULT CBGetCount(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return lphl->ItemsCount; } /*********************************************************************** * CBSetCurSel */ static LRESULT CBSetCurSel(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); WORD wRet; wRet = ListBoxSetCurSel(lphl, wParam); dprintf_combo(stddeb,"CBSetCurSel: hwnd %04x wp %x lp %lx wRet %d\n", hwnd,wParam,lParam,wRet); /* SetScrollPos(hwnd, SB_VERT, lphl->FirstVisible, TRUE);*/ InvalidateRect32( hwnd, NULL, TRUE ); return wRet; } /*********************************************************************** * CBGetCurSel */ static LRESULT CBGetCurSel(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return lphl->ItemFocused; } /*********************************************************************** * CBGetItemHeight */ static LRESULT CBGetItemHeight(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); LPLISTSTRUCT lpls = ListBoxGetItem (lphl, wParam); if (lpls == NULL) return LB_ERR; return lpls->mis.itemHeight; } /*********************************************************************** * CBSetItemHeight */ static LRESULT CBSetItemHeight(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return ListBoxSetItemHeight(lphl, wParam, lParam); } /*********************************************************************** * CBSetRedraw */ static LRESULT CBSetRedraw(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); lphl->bRedrawFlag = wParam; return 0; } /*********************************************************************** * CBSetFont */ static LRESULT CBSetFont(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); if (wParam == 0) lphl->hFont = GetStockObject32(SYSTEM_FONT); else lphl->hFont = (HFONT16)wParam; if (lphc->hWndEdit) SendMessage16(lphc->hWndEdit,WM_SETFONT,lphl->hFont,0); return 0; } /*********************************************************************** * CBGetLBTextLen */ static LRESULT CBGetLBTextLen(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wParam); if (lpls == NULL || !lphl->HasStrings) return LB_ERR; return strlen(lpls->itemText); } /*********************************************************************** * CBGetLBText */ static LRESULT CBGetLBText(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return ListBoxGetText(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam)); } /*********************************************************************** * CBGetItemData */ static LRESULT CBGetItemData(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return ListBoxGetItemData(lphl, wParam); } /*********************************************************************** * CBSetItemData */ static LRESULT CBSetItemData(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADLIST lphl = ComboGetListHeader(hwnd); return ListBoxSetItemData(lphl, wParam, lParam); } /*********************************************************************** * CBShowDropDown */ static LRESULT CBShowDropDown(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); RECT32 rect; if ((lphc->dwStyle & 3) == CBS_SIMPLE) return LB_ERR; wParam = !!wParam; if (wParam != lphc->DropDownVisible) { lphc->DropDownVisible = wParam; GetWindowRect32(hwnd,&rect); SetWindowPos32(lphc->hWndLBox, 0, rect.left, rect.top+lphc->LBoxTop, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | (wParam ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)); if (!wParam) SetFocus32(hwnd); } return 0; } /*********************************************************************** * CBCheckSize */ static BOOL32 CBCheckSize(HWND16 hwnd) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); LPHEADLIST lphl = ComboGetListHeader(hwnd); LONG cstyle = GetWindowLong32A(hwnd,GWL_STYLE); RECT16 cRect, wRect; if (lphc->hWndLBox == 0) return FALSE; GetClientRect16(hwnd,&cRect); GetWindowRect16(hwnd,&wRect); dprintf_combo(stddeb, "CBCheckSize: hwnd %04x Rect %d,%d-%d,%d wRect %d,%d-%d,%d\n", hwnd,cRect.left,cRect.top,cRect.right,cRect.bottom, wRect.left,wRect.top,wRect.right,wRect.bottom); if ((cstyle & 3) == CBS_SIMPLE) return TRUE; if ((cRect.bottom - cRect.top) > (lphl->StdItemHeight + 2*SYSMETRICS_CYBORDER)) { SetWindowPos32(hwnd, 0, 0, 0, cRect.right-cRect.left, lphl->StdItemHeight+2*SYSMETRICS_CYBORDER, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE ); GetClientRect16(hwnd,&cRect); GetWindowRect16(hwnd,&wRect); lphc->RectButton.right = cRect.right; lphc->RectButton.left = cRect.right - 2*SYSMETRICS_CXBORDER - 4 - CBitWidth; lphc->RectButton.top = cRect.top; lphc->RectButton.bottom = cRect.bottom; } if (cRect.right < lphc->RectButton.left) { /* if the button is outside the window move it in */ if ((wRect.right - wRect.left - 2*SYSMETRICS_CXBORDER) == (cRect.right - cRect.left)) { lphc->RectButton.right = cRect.right; lphc->RectButton.left = cRect.right - 2*SYSMETRICS_CXBORDER - 4 - CBitWidth; lphc->RectButton.top = cRect.top; lphc->RectButton.bottom = cRect.bottom; } /* otherwise we need to make the client include the button */ else SetWindowPos32(hwnd, 0, 0, 0, lphc->RectButton.right, lphl->StdItemHeight+2*SYSMETRICS_CYBORDER, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE); if ((lphc->dwStyle & 3) != CBS_DROPDOWNLIST) SetWindowPos32(lphc->hWndEdit, 0, 0, 0, lphc->RectButton.left, lphl->StdItemHeight, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); } CBLCheckSize(hwnd); return TRUE; } /*********************************************************************** * CBCommand */ static LRESULT CBCommand(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); LPHEADLIST lphl = ComboGetListHeader(hwnd); char buffer[256]; WORD newFocused; WORD id; if (lphc->hWndEdit) /* interdependence only used for CBS_SIMPLE and CBS_DROPDOWN styles */ { switch (wParam) { case ID_CLB: /* update EDIT window */ if (HIWORD(lParam)==CBN_SELCHANGE) if (lphl->HasStrings) { ListBoxGetText(lphl,lphl->ItemFocused, buffer); dprintf_combo(stddeb,"CBCommand: update Edit: %s\n",buffer); SetWindowText32A( lphc->hWndEdit, buffer ); } break; case ID_EDIT: /* update LISTBOX window */ id = GetWindowWord32(hwnd,GWW_ID); switch (HIWORD(lParam)) { case EN_UPDATE:GetWindowText32A(lphc->hWndEdit,buffer,255); if (*buffer) { char *str = SEGPTR_STRDUP(buffer); newFocused=ListBoxFindString(lphl, -1, SEGPTR_GET(str)); SEGPTR_FREE(str); dprintf_combo(stddeb,"CBCommand: new selection #%d is= %s\n", newFocused,buffer); if (newFocused != (WORD)LB_ERR) { /* if found something */ ListBoxSetCurSel(lphl, newFocused); ListBoxSendNotification(lphl, CBN_SELCHANGE); InvalidateRect32(hwnd, NULL, TRUE); } } SendMessage16(GetParent16(hwnd),WM_COMMAND,id, MAKELONG(hwnd, CBN_EDITUPDATE)); break; case EN_CHANGE:SendMessage16(GetParent16(hwnd),WM_COMMAND,id, MAKELONG(hwnd, CBN_EDITCHANGE)); break; case EN_ERRSPACE:SendMessage16(GetParent16(hwnd),WM_COMMAND,id, MAKELONG(hwnd, CBN_ERRSPACE)); break; } break; } } return 0; } /*********************************************************************** * CBGetEditSel * Look out! Under Win32, the parameter packing is very different. */ static LRESULT CBGetEditSel(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); if ((lphc->dwStyle & 3) == CBS_DROPDOWNLIST) return CB_ERR; /* err, documented for CBSetEditSel */ return SendMessage16(lphc->hWndEdit, EM_GETSEL16, 0, 0); } /*********************************************************************** * CBSetEditSel * Look out! Under Win32, the parameter packing is very different. */ static LRESULT CBSetEditSel(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); if ((lphc->dwStyle & 3) == CBS_DROPDOWNLIST) return CB_ERR; return SendMessage16(lphc->hWndEdit, EM_SETSEL16, 0, lParam); } /*********************************************************************** * CBGetText */ static LRESULT CBGetText(HWND16 hwnd, WPARAM16 wParam, LPARAM lParam) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); return SendMessage16(lphc->hWndEdit, WM_GETTEXT, wParam, lParam); } /*********************************************************************** * ComboWndProc */ LRESULT ComboBoxWndProc(HWND16 hwnd, UINT16 message, WPARAM16 wParam, LPARAM lParam) { switch(message) { case WM_NCCREATE: return CBNCCreate(hwnd, wParam, lParam); case WM_CREATE: return CBCreate(hwnd, wParam, lParam); case WM_DESTROY: return CBDestroy(hwnd, wParam, lParam); case WM_GETDLGCODE: return CBGetDlgCode(hwnd, wParam, lParam); case WM_KEYDOWN: return CBKeyDown(hwnd, wParam, lParam); case WM_CHAR: return CBChar(hwnd, wParam, lParam); case WM_SETFONT: return CBSetFont(hwnd, wParam, lParam); case WM_SETREDRAW: return CBSetRedraw(hwnd, wParam, lParam); case WM_PAINT: return CBPaint(hwnd, wParam, lParam); case WM_GETTEXT: return CBGetText( hwnd, wParam, lParam); case WM_LBUTTONDOWN: return CBLButtonDown(hwnd, wParam, lParam); case WM_SETFOCUS: return CBSetFocus(hwnd, wParam, lParam); case WM_KILLFOCUS: return CBKillFocus(hwnd, wParam, lParam); case WM_SIZE: return CBCheckSize(hwnd); case WM_COMMAND: return CBCommand(hwnd, wParam, lParam); case CB_RESETCONTENT16: return CBResetContent(hwnd, wParam, lParam); case CB_DIR16: return CBDir(hwnd, wParam, lParam); case CB_ADDSTRING16: return CBAddString(hwnd, wParam, lParam); case CB_INSERTSTRING16: return CBInsertString(hwnd, wParam, lParam); case CB_DELETESTRING16: return CBDeleteString(hwnd, wParam, lParam); case CB_FINDSTRING16: return CBFindString(hwnd, wParam, lParam); case CB_GETCOUNT16: return CBGetCount(hwnd, wParam, lParam); case CB_GETCURSEL16: return CBGetCurSel(hwnd, wParam, lParam); case CB_GETITEMDATA16: return CBGetItemData(hwnd, wParam, lParam); case CB_GETITEMHEIGHT16: return CBGetItemHeight(hwnd, wParam, lParam); case CB_GETLBTEXT16: return CBGetLBText(hwnd, wParam, lParam); case CB_GETLBTEXTLEN16: return CBGetLBTextLen(hwnd, wParam, lParam); case CB_SELECTSTRING16: return CBSelectString(hwnd, wParam, lParam); case CB_SETITEMDATA16: return CBSetItemData(hwnd, wParam, lParam); case CB_SETCURSEL16: return CBSetCurSel(hwnd, wParam, lParam); case CB_SETITEMHEIGHT16: return CBSetItemHeight(hwnd, wParam, lParam); case CB_SHOWDROPDOWN16: return CBShowDropDown(hwnd, wParam, lParam); case CB_GETEDITSEL16: return CBGetEditSel(hwnd, wParam, lParam); case CB_SETEDITSEL16: return CBSetEditSel(hwnd, wParam, lParam); case CB_FINDSTRINGEXACT16: return CBFindStringExact(hwnd, wParam, lParam); } return DefWindowProc16(hwnd, message, wParam, lParam); } /*--------------------------------------------------------------------*/ /* ComboLBox code starts here */ HWND16 CLBoxGetCombo(HWND16 hwnd) { return (HWND16)GetWindowLong32A(hwnd,0); } LPHEADLIST CLBoxGetListHeader(HWND16 hwnd) { return ComboGetListHeader(CLBoxGetCombo(hwnd)); } /*********************************************************************** * CBLCreate */ static LRESULT CBLCreate( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { CREATESTRUCT16 *createStruct = (CREATESTRUCT16 *)PTR_SEG_TO_LIN(lParam); SetWindowLong32A(hwnd,0,(LONG)createStruct->lpCreateParams); return 0; } /*********************************************************************** * CBLGetDlgCode */ static LRESULT CBLGetDlgCode( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { return DLGC_WANTARROWS | DLGC_WANTCHARS; } /*********************************************************************** * CBLKeyDown */ static LRESULT CBLKeyDown( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { LPHEADLIST lphl = CLBoxGetListHeader(hwnd); WORD newFocused = lphl->ItemFocused; switch(wParam) { case VK_HOME: newFocused = 0; break; case VK_END: newFocused = lphl->ItemsCount - 1; break; case VK_UP: if (newFocused > 0) newFocused--; break; case VK_DOWN: newFocused++; break; case VK_PRIOR: if (newFocused > lphl->ItemsVisible) { newFocused -= lphl->ItemsVisible; } else { newFocused = 0; } break; case VK_NEXT: newFocused += lphl->ItemsVisible; break; default: return 0; } if (newFocused >= lphl->ItemsCount) newFocused = lphl->ItemsCount - 1; ListBoxSetCurSel(lphl, newFocused); ListBoxSendNotification(lphl, CBN_SELCHANGE); SendMessage16(GetParent16(hwnd), WM_COMMAND,ID_CLB,MAKELONG(0,CBN_SELCHANGE)); lphl->ItemFocused = newFocused; ListBoxScrollToFocus(lphl); SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE); InvalidateRect32( hwnd, NULL, TRUE ); return 0; } /*********************************************************************** * CBLChar */ static LRESULT CBLChar( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { return 0; } /*********************************************************************** * CBLPaint */ static LRESULT CBLPaint( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { LPHEADLIST lphl = CLBoxGetListHeader(hwnd); LPLISTSTRUCT lpls; PAINTSTRUCT16 ps; HBRUSH32 hBrush; HFONT32 hOldFont; WND * wndPtr = WIN_FindWndPtr(hwnd); HWND16 combohwnd = CLBoxGetCombo(hwnd); HDC16 hdc; RECT16 rect; int i, top, height; top = 0; if (!lphl) return 0; hdc = BeginPaint16( hwnd, &ps ); if (!lphl) { fprintf(stdnimp,"CBLPaint: CLBoxGetListHeader returned NULL!\n"); } if (!IsWindowVisible16(hwnd) || !lphl || !lphl->bRedrawFlag) { EndPaint16(hwnd, &ps); return 0; } hOldFont = SelectObject32(hdc, lphl->hFont); /* listboxes should be white */ hBrush = GetStockObject32(WHITE_BRUSH); GetClientRect16(hwnd, &rect); FillRect16(hdc, &rect, hBrush); CBLCheckSize(hwnd); lpls = lphl->lpFirst; lphl->ItemsVisible = 0; for(i = 0; i < lphl->ItemsCount; i++) { if (lpls == NULL) break; if (i >= lphl->FirstVisible) { height = lpls->mis.itemHeight; /* must have enough room to draw entire item */ if (top > (rect.bottom-height+1)) break; lpls->itemRect.top = top; lpls->itemRect.bottom = top + height; lpls->itemRect.left = rect.left; lpls->itemRect.right = rect.right; dprintf_listbox(stddeb,"drawing item: %d %d %d %d %d\n", rect.left,top,rect.right,top+height,lpls->itemState); if (lphl->OwnerDrawn) { ListBoxDrawItem (combohwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE, 0); if (lpls->itemState) ListBoxDrawItem (combohwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_SELECT, ODS_SELECTED); } else { ListBoxDrawItem (combohwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE, lpls->itemState); } if ((lphl->ItemFocused == i) && GetFocus32() == hwnd) ListBoxDrawItem (combohwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_FOCUS, ODS_FOCUS); top += height; lphl->ItemsVisible++; } lpls = lpls->lpNext; } if (wndPtr->dwStyle & WS_VSCROLL) SetScrollRange32(hwnd, SB_VERT, 0, ListMaxFirstVisible(lphl), TRUE); SelectObject32(hdc,hOldFont); EndPaint16( hwnd, &ps ); return 0; } /*********************************************************************** * CBLKillFocus */ static LRESULT CBLKillFocus( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { /* SendMessage16(CLBoxGetCombo(hwnd),CB_SHOWDROPDOWN16,0,0);*/ return 0; } /*********************************************************************** * CBLActivate */ static LRESULT CBLActivate( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { if (wParam == WA_INACTIVE) SendMessage16(CLBoxGetCombo(hwnd),CB_SHOWDROPDOWN16,0,0); return 0; } /*********************************************************************** * CBLLButtonDown */ static LRESULT CBLLButtonDown( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { LPHEADLIST lphl = CLBoxGetListHeader(hwnd); int y; RECT16 rectsel; /* SetFocus32(hwnd); */ SetCapture32(hwnd); lphl->PrevFocused = lphl->ItemFocused; y = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam)); if (y == -1) return 0; ListBoxSetCurSel(lphl, y); ListBoxGetItemRect(lphl, y, &rectsel); InvalidateRect32( hwnd, NULL, TRUE ); return 0; } /*********************************************************************** * CBLLButtonUp */ static LRESULT CBLLButtonUp( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { LPHEADLIST lphl = CLBoxGetListHeader(hwnd); if (GetCapture32() == hwnd) ReleaseCapture(); if(!lphl) { fprintf(stdnimp,"CBLLButtonUp: CLBoxGetListHeader returned NULL!\n"); } else if (lphl->PrevFocused != lphl->ItemFocused) { SendMessage16(CLBoxGetCombo(hwnd),CB_SETCURSEL16,lphl->ItemFocused,0); SendMessage16(GetParent16(hwnd), WM_COMMAND,ID_CLB,MAKELONG(0,CBN_SELCHANGE)); ListBoxSendNotification(lphl, CBN_SELCHANGE); } SendMessage16(CLBoxGetCombo(hwnd),CB_SHOWDROPDOWN16,0,0); return 0; } /*********************************************************************** * CBLMouseMove */ static LRESULT CBLMouseMove( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { LPHEADLIST lphl = CLBoxGetListHeader(hwnd); short y; WORD wRet; RECT16 rect, rectsel; y = SHIWORD(lParam); wRet = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam)); ListBoxGetItemRect(lphl, wRet, &rectsel); GetClientRect16(hwnd, &rect); dprintf_combo(stddeb,"CBLMouseMove: hwnd %04x wp %x lp %lx y %d if %d wret %d %d,%d-%d,%d\n", hwnd,wParam,lParam,y,lphl->ItemFocused,wRet,rectsel.left,rectsel.top,rectsel.right,rectsel.bottom); if ((wParam & MK_LBUTTON) != 0) { if (y < CBLMM_EDGE) { if (lphl->FirstVisible > 0) { lphl->FirstVisible--; SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE); ListBoxSetCurSel(lphl, wRet); InvalidateRect32( hwnd, NULL, TRUE ); return 0; } } else if (y >= (rect.bottom-CBLMM_EDGE)) { if (lphl->FirstVisible < ListMaxFirstVisible(lphl)) { lphl->FirstVisible++; SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE); ListBoxSetCurSel(lphl, wRet); InvalidateRect32( hwnd, NULL, TRUE ); return 0; } } else { if ((short) wRet == lphl->ItemFocused) return 0; ListBoxSetCurSel(lphl, wRet); InvalidateRect32( hwnd, NULL, TRUE ); } } return 0; } /*********************************************************************** * CBLVScroll */ static LRESULT CBLVScroll( HWND16 hwnd, WPARAM16 wParam, LPARAM lParam ) { LPHEADLIST lphl = CLBoxGetListHeader(hwnd); int y; y = lphl->FirstVisible; switch(wParam) { case SB_LINEUP: if (lphl->FirstVisible > 0) lphl->FirstVisible--; break; case SB_LINEDOWN: lphl->FirstVisible++; break; case SB_PAGEUP: if (lphl->FirstVisible > lphl->ItemsVisible) { lphl->FirstVisible -= lphl->ItemsVisible; } else { lphl->FirstVisible = 0; } break; case SB_PAGEDOWN: lphl->FirstVisible += lphl->ItemsVisible; break; case SB_THUMBTRACK: lphl->FirstVisible = LOWORD(lParam); break; } if (lphl->FirstVisible > ListMaxFirstVisible(lphl)) lphl->FirstVisible = ListMaxFirstVisible(lphl); if (y != lphl->FirstVisible) { SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE); InvalidateRect32( hwnd, NULL, TRUE ); } return 0; } /*********************************************************************** * CBLCheckSize */ static BOOL32 CBLCheckSize(HWND16 hwnd) { LPHEADCOMBO lphc = ComboGetStorageHeader(hwnd); LPHEADLIST lphl = ComboGetListHeader(hwnd); LPLISTSTRUCT lpls; HWND16 hWndLBox; RECT16 cRect,wRect,lRect,lwRect; int totheight,dw; char className[80]; GetClassName32A(hwnd,className,80); fflush(stddeb); if (strncmp(className,"COMBOBOX",8)) return FALSE; if ((hWndLBox = lphc->hWndLBox) == 0) return FALSE; dprintf_combo(stddeb,"CBLCheckSize headers hw %04x lb %04x name %s\n", hwnd,hWndLBox,className); GetClientRect16(hwnd,&cRect); GetWindowRect16(hwnd,&wRect); GetClientRect16(hWndLBox,&lRect); GetWindowRect16(hWndLBox,&lwRect); dprintf_combo(stddeb,"CBLCheckSize: init cRect %d,%d-%d,%d wRect %d,%d-%d,%d\n", cRect.left,cRect.top,cRect.right,cRect.bottom, wRect.left,wRect.top,wRect.right,wRect.bottom); dprintf_combo(stddeb," lRect %d,%d-%d,%d lwRect %d,%d-%d,%d\n", lRect.left,lRect.top,lRect.right,lRect.bottom, lwRect.left,lwRect.top,lwRect.right,lwRect.bottom); fflush(stddeb); totheight = 0; for (lpls=lphl->lpFirst; lpls != NULL; lpls=lpls->lpNext) totheight += lpls->mis.itemHeight; dw = cRect.right-cRect.left+2*SYSMETRICS_CXBORDER+SYSMETRICS_CXVSCROLL; dw -= lwRect.right-lwRect.left; dw -= SYSMETRICS_CXVSCROLL; /* TODO: This isn't really what windows does */ if ((lRect.bottom-lRect.top < 3*lphl->StdItemHeight) || dw) { dprintf_combo(stddeb," Changing; totHeight %d StdItemHght %d dw %d\n", totheight,lphl->StdItemHeight,dw); SetWindowPos32(hWndLBox, 0, lRect.left, lRect.top, lwRect.right-lwRect.left+dw, totheight+2*SYSMETRICS_CYBORDER, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE ); } return TRUE; } /*********************************************************************** * ComboLBoxWndProc */ LRESULT ComboLBoxWndProc(HWND16 hwnd, UINT16 message, WPARAM16 wParam, LPARAM lParam) { switch(message) { case WM_CREATE: return CBLCreate(hwnd, wParam, lParam); case WM_GETDLGCODE: return CBLGetDlgCode(hwnd, wParam, lParam); case WM_KEYDOWN: return CBLKeyDown(hwnd, wParam, lParam); case WM_CHAR: return CBLChar(hwnd, wParam, lParam); case WM_PAINT: return CBLPaint(hwnd, wParam, lParam); case WM_KILLFOCUS: return CBLKillFocus(hwnd, wParam, lParam); case WM_ACTIVATE: return CBLActivate(hwnd, wParam, lParam); case WM_LBUTTONDOWN: return CBLLButtonDown(hwnd, wParam, lParam); case WM_LBUTTONUP: return CBLLButtonUp(hwnd, wParam, lParam); case WM_MOUSEMOVE: return CBLMouseMove(hwnd, wParam, lParam); case WM_VSCROLL: return CBLVScroll(hwnd, wParam, lParam); case WM_SIZE: return CBLCheckSize(hwnd); case WM_MOUSEACTIVATE: /* We don't want to be activated */ return MA_NOACTIVATE; } return DefWindowProc16(hwnd, message, wParam, lParam); }