/* * @doc INTERNAL * * @module LBHOST.CPP -- Text Host for CreateWindow() Rich Edit * Combo Box Control | * Implements CCmbBxWinHost message * * Original Author: * Jerry Kim * * History: * 01/30/97 - v-jerrki Created * * Set tabs every four (4) columns * * Copyright (c) 1997-1998 Microsoft Corporation. All rights reserved. */ #include "_common.h" #include "_host.h" #include "imm.h" #include "_format.h" #include "_edit.h" #include "_cfpf.h" #include "_cbhost.h" ASSERTDATA // Helper function in edit.cpp LONG GetECDefaultHeightAndWidth( ITextServices *pts, HDC hdc, LONG lZoomNumerator, LONG lZoomDenominator, LONG yPixelsPerInch, LONG *pxAveWidth, LONG *pxOverhang, LONG *pxUnderhang); // For effeciency and to avoid Winnt thunking layer we will call // the listbox winproc directly LRESULT CALLBACK RichListBoxWndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); //////////////////////////// System Window Procs //////////////////////////// /* * RichComboBoxWndProc (hwnd, msg, wparam, lparam) * * @mfunc * Handle window messages pertinent to the host and pass others on to * text services. * #rdesc * LRESULT = (code processed) ? 0 : 1 */ LRESULT CALLBACK RichComboBoxWndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "RichComboBoxWndProc"); LRESULT lres = 1; //signify we didn't handle the message HRESULT hr = S_FALSE; CCmbBxWinHost *phost = (CCmbBxWinHost *) GetWindowLongPtr(hwnd, ibPed); #ifdef DEBUG Tracef(TRCSEVINFO, "hwnd %lx, msg %lx, wparam %lx, lparam %lx", hwnd, msg, wparam, lparam); #endif // DEBUG switch(msg) { case WM_NCCREATE: return CCmbBxWinHost::OnNCCreate(hwnd, (CREATESTRUCT *)lparam); case WM_CREATE: // We may be on a system with no WM_NCCREATE (e.g. WINCE) if (!phost) { (void) CCmbBxWinHost::OnNCCreate(hwnd, (CREATESTRUCT *) lparam); phost = (CCmbBxWinHost *) GetWindowLongPtr(hwnd, ibPed); } break; case WM_DESTROY: if(phost) CCmbBxWinHost::OnNCDestroy(phost); return 0; } if (!phost) return ::DefWindowProc(hwnd, msg, wparam, lparam); // in certain out-of-memory situations, clients may try to re-enter us // with calls. Just bail on the call if we don't have a text services // pointer. if(!phost->_pserv) return 0; // stabilize ourselves phost->AddRef(); switch(msg) { case WM_MOUSEMOVE: if (!phost->OnMouseMove(wparam, lparam)) break; goto serv; case WM_LBUTTONUP: if (!phost->OnLButtonUp(wparam, lparam)) break; goto serv; case WM_MOUSEWHEEL: if (!phost->OnMouseWheel(wparam, lparam)) break; goto defproc; case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: if (!phost->OnLButtonDown(wparam, lparam)) goto Exit; goto serv; case WM_COMMAND: if (!phost->OnCommand(wparam, lparam)) break; goto serv; case WM_CREATE: lres = phost->OnCreate((CREATESTRUCT*)lparam); break; case WM_KEYDOWN: if (!phost->OnKeyDown((WORD) wparam, (DWORD) lparam)) break; goto serv; // give it to text services case WM_SETTEXT: if (phost->_cbType != CCmbBxWinHost::kDropDown) { lres = CB_ERR; break; } goto serv; case WM_GETTEXT: GETTEXTEX gt; if (W32->OnWin9x() || phost->_fANSIwindow) W32->AnsiFilter( msg, wparam, lparam, (void *) > ); goto serv; case WM_GETTEXTLENGTH: GETTEXTLENGTHEX gtl; if (W32->OnWin9x() || phost->_fANSIwindow) W32->AnsiFilter( msg, wparam, lparam, (void *) >l ); goto serv; case WM_CHAR: if (W32->OnWin9x() || phost->_fANSIwindow) { CW32System::WM_CHAR_INFO wmci; wmci._fAccumulate = phost->_fAccumulateDBC != 0; W32->AnsiFilter( msg, wparam, lparam, (void *) &wmci ); if (wmci._fLeadByte) { phost->_fAccumulateDBC = TRUE; phost->_chLeadByte = wparam << 8; goto Exit; // Wait for trail byte } else if (wmci._fTrailByte) { // UNDONE: // Need to see what we should do in WM_IME_CHAR wparam = phost->_chLeadByte | wparam; phost->_fAccumulateDBC = FALSE; phost->_chLeadByte = 0; msg = WM_IME_CHAR; goto serv; } else if (wmci._fIMEChar) { msg = WM_IME_CHAR; goto serv; } else if (wmci._fIMEChar) { msg = WM_IME_CHAR; goto serv; } } if(!phost->OnChar((WORD) wparam, (DWORD) lparam)) // processed code: break out break; goto serv; // else give it to text services case WM_DRAWITEM: lres = phost->CbMessageItemHandler(NULL, ITEM_MSG_DRAWLIST, wparam, lparam); if (lres) break; goto defproc; case WM_DELETEITEM: lres = phost->CbMessageItemHandler(NULL, ITEM_MSG_DELETE, wparam, lparam); if (lres) break; goto defproc; case WM_ENABLE: if (phost->OnEnable(wparam, lparam)) { if(!wparam ^ phost->_fDisabled) { // Stated of window changed so invalidate it so it will // get redrawn. InvalidateRect(phost->_hwnd, NULL, TRUE); phost->SetScrollBarsForWmEnable(wparam); // Need to enable the listbox window ::EnableWindow(phost->_hwndList, wparam); } phost->_fDisabled = !wparam; // Set disabled flag lres = 0; // Return value for message } // Fall thru to WM_SYSCOLORCHANGE? case WM_SYSCOLORCHANGE: //forward message to listbox first then pass to textservice SendMessage(phost->_hwndList, msg, wparam, lparam); phost->OnSysColorChange(); goto serv; // Notify text services that // system colors have changed case WM_GETDLGCODE: //forward message to listbox first then pass to textservice SendMessage(phost->_hwndList, msg, wparam, lparam); lres = phost->OnGetDlgCode(wparam, lparam); break; case WM_STYLECHANGING: // Just pass this one to the default window proc lres = ::DefWindowProc(hwnd, msg, wparam, lparam); break; case WM_SIZE: lres = phost->OnSize(wparam, lparam); break; case WM_SETCURSOR: // Only set cursor when over us rather than a child; this // helps prevent us from fighting it out with an inplace child if((HWND)wparam == hwnd) { if(!(lres = ::DefWindowProc(hwnd, msg, wparam, lparam))) lres = phost->OnSetCursor(wparam, lparam); } break; case WM_SHOWWINDOW: hr = phost->OnTxVisibleChange((BOOL)wparam); break; case WM_NCPAINT: lres = ::DefWindowProc(hwnd, msg, wparam, lparam); if(phost->TxGetEffects() == TXTEFFECT_SUNKEN && dwMajorVersion < VERS4) { HDC hdc = GetDC(hwnd); if(hdc) { phost->DrawSunkenBorder(hwnd, hdc); ReleaseDC(hwnd, hdc); } } break; case WM_PAINT: lres = phost->OnPaint(wparam, lparam); break; case WM_KILLFOCUS: lres = phost->OnKillFocus(wparam, lparam); if (!lres) goto serv; goto defproc; case LBCB_TRACKING: // release any mousedown stuff phost->OnLButtonUp(0, 0); phost->_fFocus = 1; phost->_fLBCBMessage = 1; // Fall through case!!! case WM_SETFOCUS: lres = phost->OnSetFocus(wparam, lparam); if (lres) goto defproc; goto serv; case WM_SYSKEYDOWN: if (phost->OnSyskeyDown((WORD)wparam, (DWORD)lparam)) goto serv; break; case WM_CAPTURECHANGED: if (!phost->OnCaptureChanged(wparam, lparam)) goto serv; break; //bug fix #4076 case CB_GETDROPPEDSTATE: lres = phost->_fListVisible; goto Exit; // combo box messages case CB_GETEXTENDEDUI: lres = phost->CbGetExtendedUI(); break; case CB_SETEXTENDEDUI: lres = phost->CbSetExtendedUI(wparam); break; case CB_SETITEMHEIGHT: lres = phost->CbSetItemHeight((wparam == (unsigned)-1) ? TRUE : FALSE, (int)lparam); break; case CB_GETITEMHEIGHT: lres = phost->CbGetItemHeight((wparam == (unsigned)-1) ? TRUE : FALSE); break; // Listbox specific messages case CB_DELETESTRING: msg = LB_DELETESTRING; goto deflstproc; case CB_SETTOPINDEX: msg = LB_SETTOPINDEX; goto deflstproc; case CB_GETTOPINDEX: msg = LB_GETTOPINDEX; goto deflstproc; case CB_GETCOUNT: msg = LB_GETCOUNT; goto deflstproc; case CB_GETCURSEL: msg = LB_GETCURSEL; goto deflstproc; case CB_GETLBTEXT: msg = LB_GETTEXT; goto deflstproc; case CB_GETLBTEXTLEN: msg = LB_GETTEXTLEN; goto deflstproc; case CB_INSERTSTRING: msg = LB_INSERTSTRING; goto deflstproc; case CB_RESETCONTENT: msg = LB_RESETCONTENT; goto deflstproc; case CB_FINDSTRING: msg = LB_FINDSTRING; goto deflstproc; case CB_FINDSTRINGEXACT: msg = LB_FINDSTRINGEXACT; goto deflstproc; case CB_SELECTSTRING: //bug fix // The system control does 2 things here. 1) selects the requested item // 2) sets the newly selected item to the top of the list lres = CB_ERR; if (phost->_hwndList) { lres = RichListBoxWndProc(phost->_hwndList, LB_SELECTSTRING, wparam, lparam); phost->UpdateEditBox(); } break; case CB_GETITEMDATA: msg = LB_GETITEMDATA; goto deflstproc; case CB_SETITEMDATA: msg = LB_SETITEMDATA; goto deflstproc; case CB_SETCURSEL: //bug fix // The system control does 2 things here. 1) selects the requested item // 2) sets the newly selected item to the top of the list if (phost->_hwndList) { lres = RichListBoxWndProc(phost->_hwndList, LB_SETCURSEL, wparam, lparam); if (lres != -1) RichListBoxWndProc(phost->_hwndList, LB_SETTOPINDEX, wparam, 0); phost->UpdateEditBox(); } break; case CB_ADDSTRING: msg = LB_ADDSTRING; goto deflstproc; // edit box specific messages case CB_GETEDITSEL: msg = EM_GETSEL; goto serv; case CB_LIMITTEXT: msg = EM_SETLIMITTEXT; goto serv; case CB_SETEDITSEL: if (phost->_cbType == CCmbBxWinHost::kDropDownList) { lres = CB_ERR; break; } msg = EM_SETSEL; // When we are in a dialog box that is empty, EM_SETSEL will not select // the final always existing EOP if the control is rich. if (phost->_fUseSpecialSetSel && ((CTxtEdit *)phost->_pserv)->GetAdjustedTextLength() == 0 && wparam != -1) { lparam = 0; wparam = 0; } else { //parameters are different between CB and EM messages wparam = (WPARAM)(signed short)LOWORD(lparam); lparam = (LPARAM)(signed short)HIWORD(lparam); } goto serv; case EM_SETMARGINS: //PPT uses this message for the combo box. bug fix #4072 // We need to keep track of the margins size because we have a minimum inset // value bug fix #4659 if (wparam & EC_LEFTMARGIN) phost->_dxLOffset = LOWORD(lparam); if (wparam & EC_RIGHTMARGIN) phost->_dxROffset = HIWORD(lparam); phost->OnSetMargins(wparam, LOWORD(lparam) + phost->_dxLInset, HIWORD(lparam) + phost->_dxRInset); break; case EM_GETOPTIONS: lres = phost->OnGetOptions(); break; case EM_SETOPTIONS: phost->OnSetOptions((WORD) wparam, (DWORD) lparam); lres = (phost->_dwStyle & ECO_STYLES); if(phost->_fEnableAutoWordSel) lres |= ECO_AUTOWORDSELECTION; break; case EM_HIDESELECTION: if(lparam) { DWORD dwPropertyBits = 0; phost->_dwStyle |= ES_NOHIDESEL; if(wparam) { phost->_dwStyle &= ~ES_NOHIDESEL; dwPropertyBits = TXTBIT_HIDESELECTION; } // Notify text services of change in status. phost->_pserv->OnTxPropertyBitsChange(TXTBIT_HIDESELECTION, dwPropertyBits); } goto serv; case EM_GETPASSWORDCHAR: #ifndef NOACCESSIBILITY lres = 0; break; #endif // We should ignore any EM_ messages which we don't handle ourselves case EM_SETPALETTE: case EM_GETRECT: case EM_SETBKGNDCOLOR: case EM_SETPASSWORDCHAR: case EM_SETREADONLY: case EM_SETRECTNP: case EM_SETRECT: case CB_GETDROPPEDCONTROLRECT: case CB_SETDROPPEDWIDTH: case CB_GETDROPPEDWIDTH: case CB_INITSTORAGE: case CB_GETHORIZONTALEXTENT: case CB_SETHORIZONTALEXTENT: case CB_SETLOCALE: case CB_GETLOCALE: AssertSz(FALSE, "Message not supported"); //FALL THROUGH!!! case WM_STYLECHANGED: break; case EM_SETTEXTEX: phost->OnSetTextEx(wparam, lparam); break; case CB_SHOWDROPDOWN: if (wparam && !phost->_fListVisible) { phost->ShowListBox(TRUE); } else if (!wparam && phost->_fListVisible) { phost->HideListBox(TRUE, FALSE); } break; #ifndef NOACCESSIBILITY case WM_GETOBJECT: IUnknown* punk; phost->QueryInterface(IID_IUnknown, (void**)&punk); Assert(punk); lres = W32->LResultFromObject(IID_IUnknown, wparam, (LPUNKNOWN)punk); AssertSz(!FAILED((HRESULT)lres), "WM_GETOBJECT message FAILED\n"); punk->Release(); break; #endif default: //CTxtWinHost message handler serv: hr = phost->_pserv->TxSendMessage(msg, wparam, lparam, &lres); defproc: if(hr == S_FALSE) { // Message was not processed by text services so send it // to the default window proc. lres = ::DefWindowProc(hwnd, msg, wparam, lparam); } // Need to do some things after we send the message to ITextService switch (msg) { case EM_SETSEL: phost->_pserv->TxSendMessage(EM_HIDESELECTION, 0, 0, NULL); lres = 1; break; // Need to return 1 per SDK documentation case EM_SETLIMITTEXT: lres = 1; break; case WM_SETFONT: { // Special border processing. The inset changes based on the size of the // defautl character set. So if we got a message that changes the default // character set, we need to update the inset. // Update our font height member variable with the new fonts height // Get the inset information HDC hdc = GetDC(hwnd); LONG xAveCharWidth = 0; LONG yCharHeight = GetECDefaultHeightAndWidth(phost->_pserv, hdc, 1, 1, W32->GetYPerInchScreenDC(), &xAveCharWidth, NULL, NULL); ReleaseDC(hwnd, hdc); if (yCharHeight) phost->_dyFont = yCharHeight; // force a recalculation of the edit control phost->_dyEdit = 0; phost->CbCalcControlRects(&phost->_rcWindow, TRUE); // force a resize of the control phost->_fListVisible = 1; phost->HideListBox(FALSE, FALSE); } goto deflstproc; case EM_FORMATRANGE: case EM_SETPARAFORMAT: case EM_SETCHARFORMAT: case EM_SETLANGOPTIONS: case EM_SETBIDIOPTIONS: case EM_SETTYPOGRAPHYOPTIONS: goto deflstproc; } break; deflstproc: //CLstBxWinHost message handler Assert(phost->_hwndList); if (phost->_hwndList) { lres = SendMessage(phost->_hwndList, msg, wparam, lparam); switch (msg) { case LB_RESETCONTENT: //need to remove the content from the edit box phost->_pserv->TxSendMessage(WM_SETTEXT, wparam, NULL, &lres); break; case LB_SETCURSEL: // need to update the edit control phost->UpdateEditBox(); break; } } break; } Exit: phost->Release(); return lres; } //////////////// CCmbBxWinHost Creation/Initialization/Destruction /////////////////////// #ifndef NOACCESSIBILITY /* * CCmbBxWinHost::QueryInterface(REFIID riid, void **ppv) * * @mfunc * */ HRESULT CCmbBxWinHost::QueryInterface(REFIID riid, void **ppv) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::QueryInterface"); if(riid == IID_IAccessible) *ppv = (IAccessible*)this; else if (riid == IID_IDispatch) *ppv = (IDispatch*)(IAccessible*)this; else if (IsEqualIID(riid, IID_IUnknown)) *ppv = (IUnknown*)(IAccessible*)this; else return CTxtWinHost::QueryInterface(riid, ppv); AddRef(); return NOERROR; } #endif /* * CCmbBxWinHost::OnNCCreate (hwnd, pcs) * * @mfunc * Static global method to handle WM_NCCREATE message (see remain.c) */ LRESULT CCmbBxWinHost::OnNCCreate( HWND hwnd, const CREATESTRUCT *pcs) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnNCCreate"); CCmbBxWinHost *phost = new CCmbBxWinHost(); if (!phost) { // Allocation failure. return 0; } if(!phost->Init(hwnd, pcs)) // Stores phost in associated { // window data phost->Shutdown(); delete phost; return 0; } return TRUE; } /* * CCmbBxWinHost::OnNCDestroy (phost) * * @mfunc * Static global method to handle WM_NCCREATE message * * @devnote * phost ptr is stored in window data (GetWindowLongPtr()) */ void CCmbBxWinHost::OnNCDestroy( CCmbBxWinHost *phost) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnNCDestroy"); // NOTE: // We have to be careful when we destroy the window because there can be cases // when we have a valid hwnd but no host for the hwnd so we have to check for // both cases if (phost->_plbHost) { // ALERT!! :The DestroyWindow function does not send the WM_NCDESTROY message for Windows CE phost->_plbHost->Release(); } // Destroy list box here so we will get the WM_DELETEITEM before the // combo box gets destroyed if (phost->_hwndList) DestroyWindow(phost->_hwndList); phost->Shutdown(); phost->Release(); } /* * CCmbBxWinHost::CCmbBxWinHost() * * @mfunc * constructor */ CCmbBxWinHost::CCmbBxWinHost(): CTxtWinHost(), _plbHost(NULL), _hwndList(NULL), _hcurOld(NULL) { _dxLInset = _dxRInset = 0; _fIgnoreUpdate = 0; TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CTxtWinHost"); } /* * CCmbBxWinHost::~CCmbBxWinHost() * * @mfunc * destructor */ CCmbBxWinHost::~CCmbBxWinHost() { } /* * CCmbBxWinHost::Init (hwnd, pcs) * * @mfunc * Initialize this CCmbBxWinHost */ BOOL CCmbBxWinHost::Init( HWND hwnd, //@parm Window handle for this control const CREATESTRUCT *pcs) //@parm Corresponding CREATESTRUCT { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::Init"); if(!pcs->lpszClass) return -1; _fRightAlign = 0; _fListVisible = 0; _fOwnerDraw = 0; _fFocus = 0; _fMousedown = 0; _cyList = 0; _fDisabled = 0; _fNoIntegralHeight = 0; _idCtrl = (UINT)(DWORD_PTR) pcs->hMenu; _fKeyMaskSet = 0; _fMouseMaskSet = 0; _fScrollMaskSet = 0; _fMousedown = 0; _nCursor = -2; _fExtendedUI = 0; _fLBCBMessage = 0; _dxROffset = _dxLOffset = 0; // Set pointer back to CCmbBxWinHost from the window if(hwnd) SetWindowLongPtr(hwnd, ibPed, (INT_PTR)this); _hwnd = hwnd; if(pcs) { _hwndParent = pcs->hwndParent; _dwExStyle = pcs->dwExStyle; _dwStyle = pcs->style; // We need to change our Extended because we don't support most of them DWORD dwExStyle = _dwExStyle & (WS_EX_LEFTSCROLLBAR | WS_EX_TOPMOST | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_CLIENTEDGE); // NOTE: // The order in which we check the style flags immulate // WinNT's order. So please verify with NT order before // reaaranging order. if (_dwStyle & CBS_DROPDOWN) { _cbType = kDropDown; if (_dwStyle & CBS_SIMPLE) _cbType = kDropDownList; } else { AssertSz(FALSE, "CBS_SIMPLE not supported"); } if (_dwStyle & CBS_OWNERDRAWFIXED) _fOwnerDraw = 1; if (_dwStyle & WS_DISABLED) _fDisabled = 1; if (_dwStyle & CBS_NOINTEGRALHEIGHT) _fNoIntegralHeight = 1; // the combobox doesn't support ES_RIGHT because its value is the // same as CBS_DROPDOWN!! if (_dwExStyle & WS_EX_RIGHT) { _fRightAlign = 1; _dwStyle |= ES_RIGHT; } // implicitly set the ES_AUTOHSCROLL style bit _dwStyle |= ES_AUTOHSCROLL; _dwStyle &= ~ES_AUTOVSCROLL; // If we have any kind of border it will always be a 3d border if (_dwStyle & WS_BORDER || _dwExStyle & WS_EX_CLIENTEDGE) { _fBorder = 1; _dwStyle &= ~WS_BORDER; _dwExStyle |= WS_EX_CLIENTEDGE; dwExStyle |= WS_EX_CLIENTEDGE; } // handle default disabled if(_dwStyle & WS_DISABLED) _fDisabled = TRUE; DWORD dwStyle = _dwStyle; // Remove the verticle scroll style for the window if (_dwStyle & WS_VSCROLL) dwStyle &= ~WS_VSCROLL; // Set the window styles SetWindowLong(_hwnd, GWL_STYLE, dwStyle); SetWindowLong(_hwnd, GWL_EXSTYLE, dwExStyle); } // Create Text Services component if(FAILED(CreateTextServices())) return FALSE; _xInset = 1; _yInset = 1; PARAFORMAT PF2; PF2.dwMask = 0; if(_dwExStyle & WS_EX_RIGHT) { PF2.dwMask |= PFM_ALIGNMENT; PF2.wAlignment = (WORD)(PFA_RIGHT); // right or center-aligned } if(_dwExStyle & WS_EX_RTLREADING) { PF2.dwMask |= PFM_RTLPARA; PF2.wEffects = PFE_RTLPARA; // RTL reading order } if (PF2.dwMask) { PF2.cbSize = sizeof(PARAFORMAT2); // tell text services _pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&PF2, NULL); } PARAFORMAT PF; // If left or right alignment, if(_fRightAlign) // tell text services { PF.cbSize = sizeof(PARAFORMAT); PF.dwMask = PFM_ALIGNMENT; PF.wAlignment = (WORD)PFA_RIGHT; _pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&PF, NULL); } //bug fix #4644 we want the EN_CHANGE and EN_UPDATE notifications _pserv->TxSendMessage(EM_SETEVENTMASK, 0, ENM_UPDATE | ENM_CHANGE, NULL); // Tell textservices to turn-on auto font sizing _pserv->TxSendMessage(EM_SETLANGOPTIONS, 0, IMF_AUTOKEYBOARD | IMF_AUTOFONT | IMF_AUTOFONTSIZEADJUST | IMF_UIFONTS | IMF_IMEALWAYSSENDNOTIFY, NULL); return TRUE; } /* * CCmbBxWinHost::OnCreate (pcs) * * @mfunc * Handle WM_CREATE message * * @rdesc * LRESULT = -1 if failed to in-place activate; else 0 */ LRESULT CCmbBxWinHost::OnCreate(const CREATESTRUCT *pcs) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnCreate"); RECT rcClient; // sometimes, these values are -1 (from windows itself); just treat them // as zero in that case LONG cy = (pcs->cy < 0) ? 0 : pcs->cy; LONG cx = (pcs->cx < 0) ? 0 : pcs->cx; rcClient.top = pcs->y; rcClient.bottom = rcClient.top + cy; rcClient.left = pcs->x; rcClient.right = rcClient.left + cx; // Notify Text Services that we are in place active if(FAILED(_pserv->OnTxInPlaceActivate(&rcClient))) return -1; // Get the font height to base the control heights from // Initially the font height is the item height HDC hdc = GetDC(_hwnd); LONG xAveCharWidth = 0; _dyFont = GetECDefaultHeightAndWidth(_pserv, hdc, 1, 1, W32->GetYPerInchScreenDC(), &xAveCharWidth, NULL, NULL); Assert(_dyFont != 0); // _yInset should be zero since listbox's doesn't have yinsets ReleaseDC(_hwnd, hdc); // init variables _idCtrl = (UINT)(DWORD_PTR)pcs->hMenu; // Need to calculate the rects of EVERYTHING!! // Force a request of itemHeight _rcButton.left = 0; _dyEdit = 0; _cyList = -1; CbCalcControlRects(&rcClient, TRUE); // Now lets handle the listbox stuff! // create and tranlate styles for combo box to listbox DWORD lStyle = WS_BORDER | WS_CHILD | WS_VISIBLE | LBS_NOTIFY | LBS_COMBOBOX | WS_CLIPSIBLINGS; if (_dwStyle & CBS_HASSTRINGS) lStyle |= LBS_HASSTRINGS; if (_dwStyle & CBS_SORT) lStyle |= LBS_SORT; if (_dwStyle & CBS_DISABLENOSCROLL) lStyle |= LBS_DISABLENOSCROLL; if (_dwStyle & CBS_NOINTEGRALHEIGHT) lStyle |= LBS_NOINTEGRALHEIGHT; if (_dwStyle & CBS_OWNERDRAWFIXED) { _fOwnerDraw; lStyle |= LBS_OWNERDRAWFIXED; } // copy over some window styles lStyle |= (_dwStyle & WS_DISABLED); lStyle |= (_dwStyle & WS_VSCROLL); DWORD lExStyle = _dwExStyle & (WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR); //NOTE. It doesn't matter if the listbox is made with the correct size since // it's going to get resized anyways if (!W32->OnWin9x()) { //WinNT _hwndList = ::CreateWindowExW(lExStyle | WS_EX_TOOLWINDOW, L"REListBox20W", NULL, lStyle, _rcList.left, _rcList.top, _rcList.right - _rcList.left, _rcList.bottom - _rcList.top, _hwnd, (HMENU)CB_LISTBOXID, NULL, this); } else { // Win '95, '98 system _hwndList = ::CreateWindowExA(lExStyle | WS_EX_TOOLWINDOW, "REListBox20W", NULL, lStyle, _rcList.left, _rcList.top, _rcList.right - _rcList.left, _rcList.bottom - _rcList.top, _hwnd, (HMENU)CB_LISTBOXID, NULL, this); } Assert(_hwndList); _plbHost = (CLstBxWinHost *) GetWindowLongPtr(_hwndList, ibPed); Assert(_plbHost); if (!_plbHost) return -1; // increment reference counter! _plbHost->AddRef(); if (_cbType != kSimple) ShowWindow(_hwndList, SW_HIDE); SetParent(_hwndList, NULL); if (_cbType == kDropDownList) { AssertSz(!((CTxtEdit*)_pserv)->_fReadOnly, "edit is readonly"); // Tell textservices to select the entire background _pserv->TxSendMessage(EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR, NULL); // format the paragraph to immulate the system control PARAFORMAT2 pf; pf.cbSize = sizeof(PARAFORMAT2); pf.dwMask = PFM_STARTINDENT; pf.dxStartIndent = (1440.0 / W32->GetXPerInchScreenDC()); _pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&pf, NULL); _usIMEMode = ES_NOIME; // Tell textservices to turnoff ime _pserv->TxSendMessage(EM_SETEDITSTYLE, SES_NOIME, SES_NOIME, NULL); } else { // make the richedit control behave like the edit control _pserv->TxSendMessage(EM_SETEDITSTYLE, SES_EMULATESYSEDIT, SES_EMULATESYSEDIT, NULL); } // Need to resize the list box if (_cbType != kSimple) SetDropSize(&_rcList); return 0; } /////////////////////////// CCmbBxWinHost Helper functions ///////////////////////////////// /* * CCmbBxWinHost::GetTextLength () * * @mfunc * returns the text length of the edit control using CR and NOT CRLF * * @rdesc * LRESULT = text length */ LRESULT CCmbBxWinHost::GetTextLength() { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::GetTextLength"); LRESULT lr = 0; GETTEXTLENGTHEX gtl; gtl.flags = GTL_NUMCHARS | GTL_PRECISE; gtl.codepage = 1200; #ifdef DEBUG HRESULT hr = _pserv->TxSendMessage(EM_GETTEXTLENGTHEX, (WPARAM)>l, 0, &lr); Assert(hr == NOERROR); #else _pserv->TxSendMessage(EM_GETTEXTLENGTHEX, (WPARAM)>l, 0, &lr); #endif return lr; } /* * CCmbBxWinHost::GetEditText (LPTSTR, int) * * @mfunc * returns the text length in the edit control in UNICODE * * @rdesc * LRESULT = text length copied to passed in buffer */ LRESULT CCmbBxWinHost::GetEditText (LPTSTR szStr, int nSize) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::GetEditText"); LRESULT lr = 0; GETTEXTEX gt; gt.cb = nSize * sizeof(TCHAR); gt.flags = 0; gt.codepage = 1200; gt.lpDefaultChar = NULL; gt.lpUsedDefChar = NULL; #ifdef DEBUG HRESULT hr = _pserv->TxSendMessage(EM_GETTEXTEX, (WPARAM)>, (LPARAM)szStr, &lr); Assert(hr == NOERROR); #else _pserv->TxSendMessage(EM_GETTEXTEX, (WPARAM)>, (LPARAM)szStr, &lr); #endif return lr; } /* * CCmbBxWinHost::SetDropSize(RECT* prc) * * @mfunc * Compute the drop down window's width and max height * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ void CCmbBxWinHost::SetDropSize(RECT* prc) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::SetDropSize"); _fListVisible = TRUE; HideListBox(FALSE, FALSE); POINT pt1 = {prc->left, prc->top}; POINT pt2 = {prc->right, prc->bottom}; ::ClientToScreen(_hwnd, &pt1); ::ClientToScreen(_hwnd, &pt2); MoveWindow(_hwndList, pt1.x, pt1.y, pt2.x - pt1.x, pt2.y - pt1.y, FALSE); } /* * CCmbBxWinHost::SetSizeEdit(int nLeft, int nTop, int nRight, int nBottom) * * @mfunc * sets the edit controls size * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ void CCmbBxWinHost::SetSizeEdit(int nLeft, int nTop, int nRight, int nBottom) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::SizeEdit"); // Generate default view rect from client rect if(_fBorder) { // Factors in space for borders _rcViewInset.top = W32->DYtoHimetricY(nTop, W32->GetYPerInchScreenDC()); _rcViewInset.bottom = W32->DYtoHimetricY(nBottom, W32->GetYPerInchScreenDC()); _rcViewInset.left = W32->DXtoHimetricX(nLeft, W32->GetXPerInchScreenDC()); _rcViewInset.right = W32->DXtoHimetricX(nRight, W32->GetXPerInchScreenDC()); } else { // Default the top and bottom inset to 0 and the left and right // to the size of the border. _rcViewInset.top = 0; _rcViewInset.bottom = 0; _rcViewInset.left = W32->DXtoHimetricX(nLeft, W32->GetXPerInchScreenDC()); _rcViewInset.right = W32->DXtoHimetricX(nRight, W32->GetXPerInchScreenDC()); } } /* * CCmbBxWinHost::CbCalcControlRects(RECT* prc, BOOL bCalcChange) * * @mfunc * Calculates the RECT for all the controls. The rect should * include the non-client area's also * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ BOOL CCmbBxWinHost::CbCalcControlRects(RECT* prc, BOOL bCalcChange) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbCalcControlRects"); // copy over the window rect _rcWindow = *prc; // Item specific things const int smY = GetSystemMetrics(SM_CYEDGE); const int smX = GetSystemMetrics(SM_CXEDGE); _cxCombo = _rcWindow.right - _rcWindow.left; if (!_dyEdit) _dyEdit = _dyFont + 2 + ((_fBorder) ? (2 * _yInset) : 0); if (_fOwnerDraw) { if (bCalcChange) { // No height has been defined yet for the static text window. Send // a measure item message to the parent MEASUREITEMSTRUCT mis; mis.CtlType = ODT_COMBOBOX; mis.CtlID = _idCtrl; mis.itemID = (UINT)-1; mis.itemHeight = _dyEdit; mis.itemData = 0; SendMessage(_hwndParent, WM_MEASUREITEM, _idCtrl, (LPARAM)&mis); _dyEdit = mis.itemHeight; } } else { // NOTE: // Richedit prevents us from trying to set the itemHeight less than the // font height so we need to take account of this by preventing user from // setting height less than font height int nyEdit = _dyFont + ((_fBorder) ? 2 * _yInset : 0); if (_dyEdit > nyEdit) { //In order for the highlighting to work properly we need to empty //the richedit control LRESULT nLen; _pserv->TxSendMessage(WM_GETTEXTLENGTH, 0, 0, &nLen); TCHAR* pwch = NULL; if (nLen && _cbType == kDropDownList) { pwch = new TCHAR[nLen + 1 /*NULL*/]; AssertSz(pwch, "Unable to allocate memory for string"); if (pwch) { // Get the text from richedit and emtpy it _pserv->TxSendMessage(WM_GETTEXT, nLen + 1, (LPARAM)pwch, NULL); _pserv->TxSendMessage(WM_SETTEXT, 0, NULL, NULL); } else { // something bad happened so send a message // to client TxNotify(EN_ERRSPACE, NULL); } } else if (_cbType == kDropDown && nLen == 0) { // we need to insert a dummy character into the richedit // control so it won't try to highlight space after // the paragraph _pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)L" ", NULL); } // Calculate the difference in size nyEdit = _dyEdit - nyEdit; PARAFORMAT2 pf; pf.cbSize = sizeof(PARAFORMAT2); pf.dwMask = PFM_SPACEAFTER; pf.dySpaceAfter = (int)(((double)nyEdit * 1440.0) / (double)W32->GetYPerInchScreenDC()); _pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&pf, NULL); //Reset the text which was there before in the richedit control if (pwch || (_cbType == kDropDown && nLen == 0)) { _pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)(pwch ? pwch : NULL), NULL); if (pwch) delete pwch; } } else _dyEdit = nyEdit; // stabalize ourselves } // For Bordered Combobox we take account of the clientedge for the top // and bottom. And since we want to draw the focus rect within the yellow // area we need to subtract 1. _cyCombo = min(_dyEdit + ((_fBorder) ? 2 * smY : 0), _rcWindow.bottom - _rcWindow.top); // recompute the max height of the dropdown listbox -- full window // size MINUS edit/static height if (_cyList == -1) _cyList = max((_rcWindow.bottom - _rcWindow.top) - _cyCombo, 0); // calculate the rect for the buttons if (_cbType != kSimple) { _rcButton.top = 0; _rcButton.bottom = min(_dyEdit, _rcWindow.bottom - _rcWindow.top); if (_fRightAlign) { _rcButton.left = 0; _rcButton.right = _rcButton.left + GetSystemMetrics(SM_CXVSCROLL); } else { _rcButton.right = _cxCombo - ((_fBorder) ? (2 * smX): 0); _rcButton.left = _rcButton.right - GetSystemMetrics(SM_CXVSCROLL); } } // calculate the edit control rect int nTop = _yInset; int nBottom = 0; _dxLInset = _xInset; _dxRInset = _xInset; if (_cbType != kSimple) { if (_fRightAlign) _dxLInset = (_rcButton.right - _rcButton.left) + smX; else _dxRInset = (_rcButton.right - _rcButton.left) + smX; } SetSizeEdit(_dxLInset + _dxLOffset, nTop, _dxRInset + _dxROffset, nBottom); // calculate the rect for the list box window _rcList.left = (_fBorder) ? - smX : 0; _rcList.top = _cyCombo - ((_fBorder) ? smY : 0); _rcList.right = (_fBorder) ? max(_cxCombo - smX, 0) : _rcWindow.right; _rcList.bottom = _cyCombo + _cyList; return TRUE; } /* * CCmbBxWinHost::DrawButton(HDC, BOOL) * * @mfunc * Draws the combo box button given an hdc * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ void CCmbBxWinHost::DrawButton(HDC hdc, BOOL bDown) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::DrawButton"); // Check if we have to draw the drop down button if (_cbType != kSimple) { BOOL bRelease = !hdc; if (!hdc) hdc = TxGetDC(); DrawFrameControl(hdc, &_rcButton, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | (bDown ? DFCS_PUSHED | DFCS_FLAT: 0) | (!_fBorder ? DFCS_FLAT : 0) | (!_fDisabled ? 0 : DFCS_INACTIVE)); if (bRelease) TxReleaseDC(hdc); } } /* * CCmbBxWinHost::TxNotify (iNotify, pv) * * @mfunc * Notify Text Host of various events. Note that there are * two basic categories of events, "direct" events and * "delayed" events. In the case of the combobox we will * notify parent of only two edit notifications; EN_CHANGE * and EN_UPDATE. The others will be from the listbox * or be generated because of focus changing * * * @rdesc * S_OK - call succeeded * S_FALSE -- success, but do some different action * depending on the event type (see below). * * @comm * user double-clicks an item in the list box * * The list box cannot allocate enough memory to * fulfill a request * * The list box loses the keyboard focus * * notification message is sent when the user * selects an item, but then selects another control or closes the * dialog box * * notification message is sent when the user changes * the current selection in the list box of a combo box * * The list box receives the keyboard focus * * This message is sent when the listbox has been closed * * notification message is sent when the user selects a * list item, or selects an item and then closes the list * * notification message is sent after the user * has taken an action that may have altered the text in the edit * control portion of a combo box * * notification message is sent when the edit control * portion of a combo box is about to display altered text * * This message is sent when the listbox has been made visible */ HRESULT CCmbBxWinHost::TxNotify( DWORD iNotify, //@parm Event to notify host of. One of the // EN_XXX values from Win32, e.g., EN_CHANGE void *pv) //@parm In-only parameter with extra data. Type // dependent on

{ TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CCmbBxWinHost::TxNotify"); HRESULT hr = S_FALSE; if(_hwndParent) { // First, handle WM_NOTIFY style notifications //WPARAM LOWORD(_idCtrl) ; LPARAM - HWND(COMBO) switch(iNotify) { case EN_CHANGE: //update the listbox bug fix #5206 if (_fIgnoreChange) { _fIgnoreChange = 0; return hr; } if (_fListVisible && _cbType == kDropDown) UpdateListBox(FALSE); else if (_cbType == kDropDownList) // don't send notification if dropdownlist return S_FALSE; iNotify = CBN_EDITCHANGE; goto sndmsg; case EN_UPDATE: //bug fix - we're sending too much CBN_UPDATE notifications if (_fIgnoreUpdate) return hr; if (_cbType == kDropDownList) return S_FALSE; iNotify = CBN_EDITUPDATE; goto sndmsg; case EN_ERRSPACE: iNotify = (unsigned)CBN_ERRSPACE; goto sndmsg; case CBN_SELCHANGE: case CBN_SELENDCANCEL: case CBN_CLOSEUP: case CBN_DBLCLK: case CBN_DROPDOWN: case CBN_KILLFOCUS: case CBN_SELENDOK: case CBN_SETFOCUS: sndmsg: hr = SendMessage(_hwndParent, WM_COMMAND, GET_WM_COMMAND_MPS(_idCtrl, _hwnd, iNotify)); } } return hr; } /* * CCmbBxWinHost::DrawEditFocus(HDC) * * @mfunc * Either draws or notifies owner to draw the focus rect * * @rdesc * void */ void CCmbBxWinHost::DrawEditFocus(HDC hdc) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::DrawEditFocus"); BOOL bRelease = FALSE; if (!hdc) { hdc = TxGetDC(); bRelease = TRUE; } RECT rc; GetClientRect(_hwnd, &rc); if (!_fOwnerDraw) { HiliteEdit(_fFocus); if (_cbType == kDropDownList) { // shrink the focus rect by the inset rc.top += _yInset; rc.bottom -= _yInset; if (_fRightAlign) rc.left = _rcButton.right; else rc.right = _rcButton.left; rc.left += _xInset; rc.right -= _xInset; DrawFocusRect(hdc, &rc); } } if (bRelease) TxReleaseDC(hdc); } /* * CCmbBxWinHost::SetSelectionInfo(BOOL bOk, int nIdx) * * @mfunc * Completes the text in the edit box with the closest match from the * listbox. If a prefix match can't be found, the edit control text isn't * updated. Assume a DROPDOWN style combo box. * * @rdesc * void */ void CCmbBxWinHost::SetSelectionInfo(BOOL bOk, int nIdx) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::SetSelectionInfo"); _nCursor = nIdx; _bSelOk = bOk; } /* * CCmbBxWinHost::AutoUpdateEdit(int i) * * @mfunc * Completes the text in the edit box with the closest match from the * listbox. If a prefix match can't be found, the edit control text isn't * updated. Assume a DROPDOWN style combo box. * * @rdesc * void */ void CCmbBxWinHost::AutoUpdateEdit(int nItem) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::AutoUpdateEdit"); // We update the edit part of the combo box with the current selection of // the list box int cch; TCHAR* pszText; LRESULT lr; // find the best matching string in the list box if (nItem == -1 || nItem == -2) { cch = GetTextLength(); // no text to search so just get out if (!cch) return; cch++; // account for null character pszText = new TCHAR[cch]; AssertSz(pszText, "string allocation failed"); if (!pszText) { TxNotify((unsigned)CBN_ERRSPACE, NULL); return; } // get string from edit control and try to find a exact match else a match // in the list box GetEditText(pszText, cch); nItem = RichListBoxWndProc(_hwndList, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pszText); if (nItem == -1) nItem = RichListBoxWndProc(_hwndList, LB_FINDSTRING, (WPARAM)-1, (LPARAM)pszText); delete [] pszText; // no match found so just get out if (nItem == -1) return; } cch = RichListBoxWndProc(_hwndList, LB_GETTEXTLEN, nItem, 0); if (cch <= 0) return; cch++; // account for null character pszText = new TCHAR[cch]; AssertSz(pszText, "Unable to allocate string"); if (!pszText) { TxNotify((unsigned)CBN_ERRSPACE, NULL); return; } RichListBoxWndProc(_hwndList, LB_GETTEXT, nItem, (LPARAM)pszText); _fIgnoreChange = 1; _pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)pszText, &lr); HiliteEdit(TRUE); delete [] pszText; } /* * CCmbBxWinHost::HiliteEdit(BOOL) * * @mfunc * Sets the hilite background or selects the entire text for the * edit control * * @rdesc * void */ void CCmbBxWinHost::HiliteEdit(BOOL bSelect) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::HiliteEdit"); //bug fix 4073 Assert(!_fOwnerDraw || _cbType == kDropDown); if (_cbType != kDropDownList) { //if bSelect is true else put cursor at beginning of text _pserv->TxSendMessage(EM_SETSEL, 0, (LPARAM)((bSelect) ? -1 : 0), NULL); } else { //Get the range of the paragraph ITextRange* pRange; if (NOERROR != ((CTxtEdit*)_pserv)->Range(0, 0, &pRange)) { AssertSz(FALSE, "unable to get range"); return; } Assert(pRange); DWORD crFore = (unsigned)tomAutoColor; DWORD crBack = (unsigned)tomAutoColor; if (bSelect) { crFore = ::GetSysColor(COLOR_HIGHLIGHTTEXT); crBack = ::GetSysColor(COLOR_HIGHLIGHT); } // Get the entire paragraph ITextFont* pFont = NULL; // Select entire text CHECKNOERROR(pRange->SetIndex(tomParagraph, 1, 1)); // Set the background and forground color CHECKNOERROR(pRange->GetFont(&pFont)); Assert(pFont); CHECKNOERROR(pFont->SetBackColor(crBack)); CHECKNOERROR(pFont->SetForeColor(crFore)); CleanExit: // Release pointers if (pFont) pFont->Release(); pRange->Release(); } } /* * CCmbBxWinHost::UpdateEditBox() * * @mfunc * Updates the editcontrol window so that it contains the text * given by the current selection in the listbox. If the listbox has no * selection (ie. -1), then we erase all the text in the editcontrol. * * hdc is from WM_PAINT messages Begin/End Paint hdc. If null, we should * get our own dc. * * @rdesc * void */ void CCmbBxWinHost::UpdateEditBox() { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::UpdateEditBox"); Assert(_hwndList); Assert(_plbHost); // Update the edit box if (_cbType == kDropDownList && _fOwnerDraw) { CbMessageItemHandler(NULL, ITEM_MSG_DRAWCOMBO, 0, 0); return; } else { TCHAR* pszText = NULL; int nItem = (signed)_plbHost->GetCursor(); if (nItem != -1) { int cch = RichListBoxWndProc(_hwndList, LB_GETTEXTLEN, (LPARAM)nItem, 0); pszText = new TCHAR[cch + 1]; AssertSz(pszText, "allocation failed"); // just get out if memory allocation failed if (!pszText) { TxNotify((unsigned)CBN_ERRSPACE, NULL); return; } RichListBoxWndProc(_hwndList, LB_GETTEXT, (WPARAM)nItem, (LPARAM)pszText); } // if the cursor is on a valid item then update edit with the item text // else we just display a blank text TCHAR szEmpty[] = L""; _fIgnoreChange = 1; _pserv->TxSendMessage(WM_SETTEXT, 0, (LPARAM)((pszText) ? pszText : szEmpty), NULL); DrawEditFocus(NULL); if (pszText) delete pszText; } } /* * CCmbBxWinHost::UpdateListBox(BOOL) * * @mfunc * Updates the list box by searching and moving to the top the text in * edit control. And possibly pre-selecting the item if bSetSel is set * * @rdesc * int = found ? index of item : -1 */ int CCmbBxWinHost::UpdateListBox(BOOL bSetSel) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::UpdateListBox"); int nItem = -1; int nSel = -1; TCHAR* pszText; int cch; // Get text from edit box cch = GetTextLength(); if (cch) { // add one for null string cch++; pszText = new TCHAR[cch]; if (pszText != NULL) { if (GetEditText(pszText, cch)) { //Bypass Winnt thunking layer by calling the function directly nItem = RichListBoxWndProc(_hwndList, LB_FINDSTRING, (WPARAM)-1L, (LPARAM)pszText); } delete [] pszText; } else { TxNotify((unsigned)CBN_ERRSPACE, NULL); return 0; } } if (bSetSel) nSel = nItem; // update the list box RichListBoxWndProc(_hwndList, LB_SETCURSEL, (LPARAM)nSel, 0); RichListBoxWndProc(_hwndList, LB_SETTOPINDEX, (LPARAM)max(nItem, 0), 0); return nItem; } /* * CCmbBxWinHost::HideListBox(BOOL, BOOL) * * @mfunc * Hides the list box * * @rdesc * void */ BOOL CCmbBxWinHost::HideListBox(BOOL bNotify, BOOL fSelOk) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::HideListBox"); //send CBN_SELENDOK to all types of comboboxes but only // allow CBN_SELENDCANCEL to be sent for droppable comboboxes if (bNotify) { if (fSelOk) { TxNotify(CBN_SELENDOK, NULL); } else if (_cbType != kSimple) { TxNotify(CBN_SELENDCANCEL, NULL); } } // return, we don't hide simple combo boxes. if (!_fListVisible || _cbType == kSimple) return TRUE; // Tell the listbox to end tracking Assert(_plbHost); _plbHost->OnCBTracking(LBCBM_END, 0); // Hide the listbox window _fListVisible = 0; ShowWindow(_hwndList, SW_HIDE); if (_fCapture) { _fCapture = FALSE; TxSetCapture(FALSE); } _fResizing = 1; // Invalidate the item area now since SWP() might update stuff. // Since the combo is CS_VREDRAW/CS_HREDRAW, a size change will // redraw the whole thing, including the item rect. But if it // isn't changing size, we still want to redraw the item anyway // to show focus/selection if (_cbType == kDropDownList) { if (!_fOwnerDraw) HiliteEdit(_fFocus); InvalidateRect(_hwnd, NULL, TRUE); } //bug fix // The button may look depressed so we must redraw the button if (_fMousedown) { _fMousedown = FALSE; InvalidateRect(_hwnd, &_rcButton, FALSE); } SetWindowPos(_hwnd, HWND_TOP, 0, 0, _cxCombo, _cyCombo, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); _fResizing = 0; if (_cbType == kDropDown) AutoUpdateEdit(_nCursor); _nCursor = -2; // In case size didn't change UpdateWindow(_hwnd); if (bNotify) { //Notify parent we will be popping up the combo box. TxNotify(CBN_CLOSEUP, NULL); } // reset back to old cursor if mouse cursor was set if (_hcurOld) { TxSetCursor2(_hcurOld, NULL); _hcurOld = NULL; } return(TRUE); } /* * CCmbBxWinHost::ShowListBox(BOOL) * * @mfunc * Displays the list box * * @rdesc * void */ void CCmbBxWinHost::ShowListBox(BOOL fTrack) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::ShowListBox"); Assert(_cbType != kSimple); Assert(_hwndList); // Notify parent window we are about to drop down the list box TxNotify(CBN_DROPDOWN, NULL); // force a redraw of the button so it looks depressed InvalidateRect(_hwnd, &_rcButton, TRUE); _fListVisible = TRUE; _fIgnoreChange = 0; _bSelOk = 0; if (_cbType == kDropDown) { UpdateListBox(!_fMousedown); if (!_fMousedown) AutoUpdateEdit(-1); _nCursor = _plbHost->GetCursor(); } else { // Scroll the currently selected item to the top of the listbox. int idx = (signed)_plbHost->GetCursor(); _nCursor = idx; if (idx == -1) idx = 0; // set the top index if there is something in the list box if (_plbHost->GetCount() > 0) RichListBoxWndProc(_hwndList, LB_SETTOPINDEX, idx, 0); // We are to lose focus in this case _fFocus = 0; if (!_fOwnerDraw) HiliteEdit(FALSE); // We need to invalidate the edit rect so that the focus frame/invert // will be turned off when the listbox is visible. Tandy wants this for // his typical reasons... InvalidateRect(_hwnd, NULL, TRUE); } // Figure out where to position the dropdown listbox. // We want the dropdown to pop below or above the combo // Get screen coords RECT rcList; POINT pt1; pt1.x = _rcList.left; pt1.y = _rcList.top; TxClientToScreen(&pt1); rcList.left = pt1.x; rcList.top = pt1.y; rcList.right = rcList.left + (_rcList.right - _rcList.left); rcList.bottom = rcList.top + _cyList; // List area int cyItem = _plbHost->GetItemHeight(); AssertSz(cyItem, "LB_GETITEMHEIGHT is returning 0"); if (cyItem == 0) cyItem = _plbHost->GetFontHeight(); // Windows NT comment: // we shoulda' just been able to use cyDrop here, but thanks to VB's need // to do things their OWN SPECIAL WAY, we have to keep monitoring the size // of the listbox 'cause VB changes it directly (jeffbog 03/21/94) int iHeight = max(_cyList, _rcWindow.bottom - _rcWindow.top); DWORD dwMult = (DWORD)RichListBoxWndProc(_hwndList, LB_GETCOUNT, 0, 0); if (dwMult) { dwMult = (DWORD)(LOWORD(dwMult) * cyItem); dwMult += GetSystemMetrics(SM_CYEDGE); if (dwMult < 0x7FFF) iHeight = min(LOWORD(dwMult), iHeight); } if (!_fNoIntegralHeight) { iHeight = ((iHeight - GetSystemMetrics(SM_CYEDGE)) / cyItem) * cyItem + GetSystemMetrics(SM_CYEDGE); } //UNDONE: Multi-monitor // We need to change the following code if we are to support multi-monitor int yTop; int nScreenHeight = GetSystemMetrics(SM_CYFULLSCREEN); if (rcList.top + iHeight <= nScreenHeight) { yTop = rcList.top; if (!_fBorder) yTop -= GetSystemMetrics(SM_CYBORDER); } else { yTop = max(rcList.top - iHeight - _cyCombo + ((_fBorder) ? GetSystemMetrics(SM_CYBORDER) : 0), 0); } SetWindowPos(_hwndList, HWND_TOPMOST, rcList.left, yTop, rcList.right - rcList.left, iHeight, 0); Assert(_plbHost); _plbHost->SetScrollInfo(SB_VERT, FALSE); if (_cbType == kDropDownList) _fFocus = 0; // UNDONE: // Are we going to support window animation? ShowWindow(_hwndList, SW_SHOW); // We send a message to the listbox to prepare for tracking if (fTrack) { Assert(_plbHost); // initialize type searching _plbHost->InitSearch(); _plbHost->OnCBTracking(LBCBM_PREPARE, LBCBM_PREPARE_SAVECURSOR | ((_cbType == kDropDownList) ? LBCBM_PREPARE_SETFOCUS : 0)); } // Since we are about to display the list box change mouse cursor to arrow if (!_hcurOld) _hcurOld = TxSetCursor2(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)), NULL); } ////////////////////////// Combo box Message Handlers //////////////////////////////// /* * CCmbBxWinHost::CbSetItemHeight(BOOL, int) * * @mfunc * Sets the size of the edit or list box. * * * @rdesc * LRESULT = successful ? 1 : CB_ERR */ LRESULT CCmbBxWinHost::CbSetItemHeight(BOOL bEdit, int nHeight) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbSetItemHeight"); //bug fix #4556 if (nHeight == 0 || nHeight > 255) return CB_ERR; // We need to update the height internally if (bEdit) { RECT rc; GetClientRect(_hwnd, &rc); _dyEdit = nHeight; OnSize(0, MAKELONG(rc.right - rc.left, rc.bottom - rc.top)); } else { RichListBoxWndProc(_hwndList, LB_SETITEMHEIGHT, 0, MAKELPARAM(nHeight, 0)); } return 1; } /* * CCmbBxWinHost::CbGetItemHeight(BOOL) * * @mfunc * Retrieves the size of the edit or list box. * * * @rdesc * LRESULT = successful ? 1 : CB_ERR */ LRESULT CCmbBxWinHost::CbGetItemHeight(BOOL bEdit) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbGetItemHeight"); // We need to update the height internally if (bEdit) { return _dyEdit; } else { return RichListBoxWndProc(_hwndList, LB_GETITEMHEIGHT, 0, 0); } } /* * CCmbBxWinHost::CbSetExtendedUI(BOOL) * * @mfunc * Retrieves the size of the edit or list box. * * * @rdesc * LRESULT = successful ? CB_OKAY : CB_ERR */ LRESULT CCmbBxWinHost::CbSetExtendedUI(BOOL bExtendedUI) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbSetExtendedUI"); // We need to update the height internally _fExtendedUI = bExtendedUI ? 1 : 0; return CB_OKAY; } /* * CCmbBxWinHost::CbMessageItemHandler(int, WPARAM, LPARAM) * * @mfunc * Handles any and all WM_DRAWITEM and WM_DELETEITEM messages * * * @rdesc * LRESULT = whatever the parent window returns */ LRESULT CCmbBxWinHost::CbMessageItemHandler(HDC hdc, int ff, WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::CbMessageItemHandler"); // modify the structure info a bit and pass it to the parent window DRAWITEMSTRUCT dis; BOOL bRelease = FALSE; UINT msg = WM_DRAWITEM; switch (ff) { case ITEM_MSG_DRAWLIST: ((LPDRAWITEMSTRUCT)lparam)->CtlType = ODT_COMBOBOX; ((LPDRAWITEMSTRUCT)lparam)->CtlID = _idCtrl; ((LPDRAWITEMSTRUCT)lparam)->hwndItem = _hwnd; break; case ITEM_MSG_DELETE: ((LPDELETEITEMSTRUCT)lparam)->CtlType = ODT_COMBOBOX; ((LPDELETEITEMSTRUCT)lparam)->CtlID = _idCtrl; ((LPDELETEITEMSTRUCT)lparam)->hwndItem = _hwnd; msg = WM_DELETEITEM; break; case ITEM_MSG_DRAWCOMBO: if (!hdc) { bRelease = TRUE; hdc = TxGetDC(); } //Fill the DRAWITEMSTRUCT with the unchanging constants dis.CtlType = ODT_COMBOBOX; dis.CtlID = _idCtrl; // Use -1 if an invalid item number is being used. This is so that the app // can detect if it should draw the caret (which indicates the lb has the // focus) in an empty listbox dis.itemID = _plbHost->GetCursor(); dis.itemAction = ODA_DRAWENTIRE; dis.hwndItem = _hwnd; dis.hDC = hdc; dis.itemData = (_plbHost->GetCount()) ? (((signed)dis.itemID >= 0) ? _plbHost->GetData(dis.itemID) : 0) : 0; dis.itemState = (UINT)((_fFocus && !_fListVisible ? ODS_SELECTED | ODS_FOCUS : 0) | ((_fDisabled) ? ODS_DISABLED : 0) | ODS_COMBOBOXEDIT); // Calculate the drawing rect TxGetClientRect(&dis.rcItem); if (_cbType != kSimple) { if (_fRightAlign) dis.rcItem.left = _rcButton.right; else dis.rcItem.right = _rcButton.left; } // immulate the system by making the HDC invert text if we have focus SetBkMode(hdc, OPAQUE); PatBlt(hdc, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right - dis.rcItem.left, dis.rcItem.bottom - dis.rcItem.top, PATCOPY); if (_fFocus && !_fListVisible) { // only do the FillRect if we know its not // ownerdraw item, otherwise we mess up people up // BUT: for Compat's sake we still do this for Win 3.1 guys SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); } // Don't let ownerdraw dudes draw outside of the combo client // bounds. InflateRect(&dis.rcItem, -1, -1); IntersectClipRect(hdc, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom); lparam = (LPARAM)&dis; } LRESULT lres = SendMessage(_hwndParent, msg, _idCtrl, lparam); if (bRelease) TxReleaseDC(hdc); return lres; } /////////////////////////// Windows Message Handlers ///////////////////////////////// /* * CCmbBxWinHost::OnCommand(WPARAM, LPARAM) * * @mfunc * Handles notification from listbox and reflects it to the parent of * the combo box * * @comm * LRESULT = Handled ? 0 : 1 * * */ HRESULT CCmbBxWinHost::OnCommand(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CCmbBxWinHost::OnCommand"); // Filter-out all the messages except Listbox notification messages Assert(_hwndParent); switch (HIWORD(wparam)) { case LBN_DBLCLK: TxNotify(CBN_DBLCLK, NULL); break; case LBN_ERRSPACE: TxNotify((unsigned)CBN_ERRSPACE, NULL); break; case LBN_SELCHANGE: case LBN_SELCANCEL: if (!_fListVisible) HideListBox(TRUE, TRUE); TxNotify(CBN_SELCHANGE, NULL); UpdateEditBox(); break; default: // not handled so pass down the line return 1; } return 0; } /* * CCmbBxWinHost::OnEnable(WPARAM, LPARAM) * * @mfunc * handles the WM_ENABLE message * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnEnable(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnEnable"); if (_fMousedown) { _fMousedown = FALSE; DrawButton(NULL, FALSE); // // Pop combo listbox back up, canceling. // if (_fListVisible) HideListBox(TRUE, FALSE); } return 1; } /* * CCmbBxWinHost::OnChar(WPARAM, LPARAM) * * @mfunc * handles the WM_CHAR message * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnChar(WORD wparam, DWORD lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnChar"); // Check if we should eat the message or not if (_cbType == kDropDownList) { //bug fix #5318 - ignore delete, insert and clear if (((WCHAR)wparam) == VK_DELETE || ((WCHAR)wparam) == VK_INSERT || ((WCHAR)wparam) == VK_CLEAR) return 0; // Sending WM_CHAR is BAD!!! call the message handler directly // send the character string message to the listbox if visible _plbHost->OnChar(LOWORD(wparam), lparam); // If Hi-Ansi need to send a wm_syskeyup message to ITextServices to // stabalize the state if (0x80 <= wparam && wparam <= 0xFF && !HIWORD(GetKeyState(VK_MENU))) { LRESULT lres; _pserv->TxSendMessage(WM_SYSKEYUP, VK_MENU, 0xC0000000, &lres); } return 0; } if (_cbType == kDropDown) { if (_fListVisible) { if (!_fCapture) { // Tell listbox to reset capturing by ending then starting it up _plbHost->OnCBTracking(LBCBM_END, 0); _plbHost->OnCBTracking(LBCBM_PREPARE, 0); } // Send the message to the edit control iff it's not a tab if (((WCHAR)wparam) != VK_TAB) _pserv->TxSendMessage(WM_CHAR, wparam, lparam, NULL); if (!_fCapture) { // capture the cursor TxSetCapture(TRUE); _fCapture = 1; } } else { // set the cursel to -1 if it already isn't if ((wparam != VK_RETURN) && (_plbHost->GetCursor() != -1)) RichListBoxWndProc(_hwndList, LB_SETCURSEL, (WPARAM)-1, 0); // Send the message to the edit control iff it's not CTRL+i or CTRL+h if (((WCHAR)wparam) != VK_TAB) _pserv->TxSendMessage(WM_CHAR, wparam, lparam, NULL); } return 0; } return 1; } /* * CCmbBxWinHost::OnKeyDown(WPARAM, LPARAM) * * @mfunc * handles the WM_KEYDOWN message * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnKeyDown(WORD wparam, DWORD lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnKeyDown"); if (_fListVisible && (wparam == VK_RETURN || wparam == VK_ESCAPE)) { if (wparam == VK_RETURN) _nCursor = _plbHost->GetCursor(); HideListBox(TRUE, wparam == VK_RETURN); return 0; } // if we are in extended mode and F4 is hit // we just ignore it if (_fExtendedUI && wparam == VK_F4) return 0; Assert(_plbHost); int fExtUI = _fExtendedUI; int nCurSel = _plbHost->GetCursor(); Assert(nCurSel >= -1); // if we are a dropdownlist combo box then just forward the message on to the // list box if (_cbType == kDropDownList) { switch (wparam) { case VK_F4: if (_fListVisible) break;; fExtUI = 1; Assert(fExtUI && !_fListVisible); // fall through case case VK_DOWN: if (fExtUI && !_fListVisible) { ShowListBox(TRUE); TxSetCapture(TRUE); _fCapture = TRUE; return 1; } // Fall through case case VK_UP: case VK_NEXT: case VK_PRIOR: case VK_RETURN: case VK_ESCAPE: break; //bug fix #5318 /* case VK_DELETE: case VK_CLEAR: case VK_INSERT: */ default: // There no reason for us to pass these keys to ITextServices since the control is suppose // to be read-only return 0; } } else { switch (wparam) { case VK_F4: if (_fListVisible) break; fExtUI = 1; Assert(fExtUI && !_fListVisible); // fall through case case VK_DOWN: if (fExtUI && !_fListVisible) { ShowListBox(TRUE); TxSetCapture(TRUE); _fCapture = TRUE; return 0; } // Fall through case case VK_UP: case VK_NEXT: case VK_PRIOR: if (_fListVisible) { if (_fCapture) { // release our capture flag and tell lb to start tracking _fCapture = 0; _plbHost->OnCBTracking(LBCBM_START, _fMousedown); } // selecting the top index and then sending the keydown to the // listbox causes 2 moves so handle this ourselves if (nCurSel == -1) { RichListBoxWndProc(_hwndList, LB_SETCURSEL, _plbHost->GetTopIndex(), 0); UpdateEditBox(); UpdateCbWindow(); return 0; } } else { // if Listbox isn't visible and the listbox cursor is -1 // then we should try to select the correct item in the list // box if (nCurSel == -1) { UpdateListBox(TRUE); if (_plbHost->GetCursor() >= 0) { HiliteEdit(TRUE); return 0; } else if (!_plbHost->GetCount()) { return 0; } } } break; case VK_RETURN: case VK_ESCAPE: break; default: // return zero to say we didn't handle this return 1; } } // pass message to list box _plbHost->OnKeyDown(wparam, lparam, 0); UpdateCbWindow(); return 0; } /* * CCmbBxWinHost::OnSyskeyDown(WORD, DWORD) * * @mfunc * handles the WM_SYSKEYDOWN message * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnSyskeyDown(WORD wparam, DWORD lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSyskeyDown"); if (lparam & 0x20000000L) /* Check if the alt key is down */ { // Handle Combobox support. We want alt up or down arrow to behave // like F4 key which completes the combo box selection if (lparam & 0x1000000) { // We just want to ignore keys on the number pad... // This is an extended key such as the arrow keys not on the // numeric keypad so just drop the combobox. if (wparam != VK_DOWN && wparam != VK_UP) return 1; } else if (GetKeyState(VK_NUMLOCK) & 0x1) { //If numlock down, just send all system keys to dwp return 1; } else { if (wparam != VK_DOWN && wparam != VK_UP) return 1; } // If the listbox isn't visible, just show it if (!_fListVisible) { ShowListBox(TRUE); TxSetCapture(TRUE); _fCapture = TRUE; } else //Ok, the listbox is visible. So hide the listbox window. HideListBox(TRUE, TRUE); return 0; } return 1; } /* * CCmbBxWinHost::OnCaptureChanged(WPARAM, LPARAM) * * @mfunc * handles the WM_CAPTURECHANGED message * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnCaptureChanged(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnCaptureChanged"); if (_fCapture) { // Pop combo listbox back up, canceling. if (_fListVisible) HideListBox(TRUE, FALSE); else { _fCapture = FALSE; _fMousedown = FALSE; DrawButton(NULL, FALSE); } return 0; } return 1; } /* * CCmbBxWinHost::OnMouseMove(WPARAM, LPARAM) * * @mfunc * handles the WM_MOUSEMOVE message * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnMouseMove(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnMouseMove"); // We do the following if we have mouse captured or if the listbox is visible if (_cbType != kSimple && _fCapture) { // get the point coordinates of mouse POINT pt; POINTSTOPOINT(pt, lparam); if (_fListVisible) { // if the listbox is visible visible check if the cursor went over // list box RECT rc; POINT ptScreen = pt; GetWindowRect(_hwndList, &rc); TxClientToScreen(&ptScreen); if (PtInRect(&rc, ptScreen)) { // Release the capture state of the mouse if (_fCapture) { _fCapture = FALSE; TxSetCapture(FALSE); } // notify the listbox to start tracking Assert(_plbHost); ::PostMessage(_hwndList, LBCB_TRACKING, LBCBM_START, _fMousedown); _fMousedown = 0; } } DrawButton(NULL, _fMousedown ? PtInRect(&_rcButton, pt) : FALSE); return FALSE; } #ifdef DEBUG if (_cbType != kSimple) Assert(!_fListVisible); #endif return TRUE; } /* * CCmbBxWinHost::OnLButtonUp(WPARAM, LPARAM) * * @mfunc * handles the WM_LBUTTONUP message * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnLButtonUp(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnLButtonUp"); if (_fMousedown) { _fMousedown = FALSE; if (_cbType != kSimple) { // If an item in the listbox matches the text in the edit // control, scroll it to the top of the listbox. Select the // item only if the mouse button isn't down otherwise we // will select the item when the mouse button goes up. if (_cbType == kDropDown) { UpdateListBox(TRUE); AutoUpdateEdit(-1); } // if we recieved a mouse up and the listbox is still visible then user // hasn't selected any items from the listbox so don't release the capture yet if (_fCapture && !_fListVisible) { _fCapture = FALSE; TxSetCapture(FALSE); } DrawButton(NULL, FALSE); return FALSE; } } return TRUE; } /* * CCmbBxWinHost::OnLButtonDown(WPARAM, LPARAM) * * @mfunc * Draws the client edges of the combo box * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnLButtonDown(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnLButtonDown"); // check if we should dropdown the list box POINT pt; POINTSTOPOINT(pt, lparam); // if we don't have focus then set the focus first if (!_fFocus) TxSetFocus(); _fFocus = 1; // listbox is down so pop it back up if (_fListVisible) { return !HideListBox(TRUE, FALSE); } else if (_cbType == kDropDownList || (_cbType == kDropDown && PtInRect(&_rcButton, pt))) { // need to show listbox ShowListBox(TRUE); _fMousedown = TRUE; TxSetCapture(TRUE); _fCapture = TRUE; return 0; } return 1; } /* * CCmbBxWinHost::OnMouseWheel(WPARAM, LPARAM) * * @mfunc * Draws the client edges of the combo box * * @rdesc * LRESULT = Handled ? 0 : 1 */ LRESULT CCmbBxWinHost::OnMouseWheel(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnMouseWheel"); // Handle only scrolling. if (wparam & (MK_CONTROL | MK_SHIFT)) return 1; // If the listbox is visible, send it the message to scroll. // if the listbox is if (_fListVisible) { _plbHost->OnMouseWheel(wparam, lparam); return 0; } // If we're in extended UI mode or the edit control isn't yet created, // bail. if (_fExtendedUI) return 0; // Emulate arrow up/down messages to the edit control. int i = abs(((short)HIWORD(wparam))/WHEEL_DELTA); wparam = ((short)HIWORD(wparam) > 0) ? VK_UP : VK_DOWN; while (i-- > 0) OnKeyDown(wparam, lparam); return 0; } /* * CCmbBxWinHost::OnSetCursor(WPARAM, LPARAM) * * @mfunc * Changes the cursor depending on where the cursor is * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ LRESULT CCmbBxWinHost::OnSetCursor(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSetCursor"); POINT pt; GetCursorPos(&pt); ::ScreenToClient(_hwnd, &pt); if ((_cbType == kDropDownList) || (_cbType == kDropDown && ((_fRightAlign) ? _rcButton.right >= pt.x : _rcButton.left <= pt.x))) { TxSetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)), NULL); } else _pserv->OnTxSetCursor(DVASPECT_CONTENT, -1, NULL, NULL, NULL, NULL, NULL, pt.x, pt.y); return TRUE; } /* * CCmbBxWinHost::OnSetFocus(WPARAM, LPARAM) * * @mfunc * Draws the button and sends the WM_DRAWITEM message for owner draw * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ LRESULT CCmbBxWinHost::OnSetFocus(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSetFocus"); _fFocus = TRUE; // Hide the list box if (_fListVisible) HideListBox(TRUE, _bSelOk); else if (_fOwnerDraw && _cbType == kDropDownList) CbMessageItemHandler(NULL, ITEM_MSG_DRAWCOMBO, 0, 0); else DrawEditFocus(NULL); // Draw the focus // Notify the parent we have the focus iff this function // wasn't called in response to LBCB_TRACKING if (_fLBCBMessage) _fLBCBMessage = 0; else TxNotify(CBN_SETFOCUS, NULL); // we return 1 if we are owner draw or if // we are a kDropDownList, this is because // we have to prevent the message from being passed // to _pserv return (_cbType == kDropDownList); } /* * CCmbBxWinHost::OnKillFocus(WPARAM, LPARAM) * * @mfunc * Draws the button and sends the WM_DRAWITEM message for owner draw * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ LRESULT CCmbBxWinHost::OnKillFocus(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnKillFocus"); // if we never had focus or if not list window just get out if (_hwndList == NULL) return 0; if ((HWND)wparam != _hwndList) { // We only give up the focus if the new window getting the focus // doesn't belong to the combo box. // The combo box is losing the focus. Send buttonup clicks so that // things release the mouse capture if they have it... If the // pwndListBox is null, don't do anything. This occurs if the combo box // is destroyed while it has the focus. OnLButtonUp(0L, 0xFFFFFFFFL); if (_fListVisible) HideListBox(TRUE, FALSE); } //bug fix #4013 if (!_fFocus) return 0; _fFocus = FALSE; // Remove Focus Rect if (_cbType != kDropDownList) { HiliteEdit(FALSE); // Hide any selections _pserv->TxSendMessage(EM_HIDESELECTION, 1, 0, NULL); } else if (_fOwnerDraw) CbMessageItemHandler(NULL, ITEM_MSG_DRAWCOMBO, 0, 0); else DrawEditFocus(NULL); TxNotify(CBN_KILLFOCUS, NULL); if (_cbType == kDropDownList) return 1; return 0; } /* * CCmbBxWinHost::OnSize(WPARAM, LPARAM) * * @mfunc * Draws the button and sends the WM_DRAWITEM message for owner draw * * @rdesc * BOOL = Processed ? FALSE : TRUE */ LRESULT CCmbBxWinHost::OnSize(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnCbSize"); // only deal with this message if we didn't generate the message and // the new size is a valid one if (!_fResizing && _hwndList) { _fResizing = 1; RECT rc; GetWindowRect(_hwnd, &rc); rc.right -= rc.left; rc.bottom -= rc.top; rc.left = rc.top = 0; CbCalcControlRects(&rc, FALSE); // Need to resize the list box if (_cbType != kSimple) SetDropSize(&_rcList); _fResizing = 0; } _pserv->TxSendMessage(WM_SIZE, wparam, lparam, NULL); CTxtWinHost::OnSize(_hwnd, wparam, (int)LOWORD(lparam), (int)HIWORD(lparam)); return FALSE; } /* * CCmbBxWinHost::OnGetDlgCode(WPARAM, LPARAM) * * @mfunc * Draws the button and sends the WM_DRAWITEM message for owner draw * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ LRESULT CCmbBxWinHost::OnGetDlgCode(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnGetDlgCode"); // call the parents GetDlgCode first LRESULT code = DLGC_WANTCHARS | DLGC_WANTARROWS; if (_cbType != kDropDownList) code |= DLGC_HASSETSEL; // If the listbox is dropped and the ENTER key is pressed, // we want this message so we can close up the listbox if ((lparam != 0) && (((LPMSG)lparam)->message == WM_KEYDOWN) && _fListVisible && ((wparam == VK_RETURN) || (wparam == VK_ESCAPE))) { code |= DLGC_WANTMESSAGE; } _fInDialogBox = TRUE; return((LRESULT)code); } /* * CCmbBxWinHost::OnSetTextEx(WPARAM, LPARAM) * * @mfunc * Draws the button and sends the WM_DRAWITEM message for owner draw * * @rdesc * BOOL = SUCCESSFUL ? TRUE : FALSE */ LRESULT CCmbBxWinHost::OnSetTextEx(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnSetTextEx"); WCHAR* psz = (WCHAR*)lparam; while (*psz != L'\r') psz++; *psz = L'\0'; //Send message to host _pserv->TxSendMessage(EM_SETTEXTEX, wparam, lparam, NULL); //Set the string back to old and send message to listbox *psz = L'\r'; psz++; return ::SendMessage(_hwndList, EM_SETTEXTEX, wparam, (LPARAM)psz); } /* * CCmbBxWinHost::OnPaint(WPARAM, LPARAM) * * @mfunc * Draws the button and sends the WM_DRAWITEM message for owner draw * * @rdesc * BOOL = processed ? 0 : 1 */ LRESULT CCmbBxWinHost::OnPaint(WPARAM wparam, LPARAM lparam) { TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::OnPaint"); PAINTSTRUCT ps; HPALETTE hpalOld = NULL; HDC hdc = BeginPaint(_hwnd, &ps); RECT rcClient; // Since we are using the CS_PARENTDC style, make sure // the clip region is limited to our client window. GetClientRect(_hwnd, &rcClient); // pass message on to the parentwindow if owner draw if (_cbType != kDropDownList || !_fOwnerDraw) { RECT rcFocus = rcClient; // Set up the palette for drawing our data if (_hpal) { hpalOld = SelectPalette(hdc, _hpal, TRUE); RealizePalette(hdc); } SaveDC(hdc); IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); // Fill-in the gap between the button and richedit control RECT rcGap; if (_fRightAlign) { rcGap.left = _rcButton.right; rcGap.right = rcGap.left + _xInset + 1; } else { rcGap.right = _rcButton.left; rcGap.left = rcGap.right - _xInset - 1; } rcGap.top = rcClient.top; rcGap.bottom = rcClient.bottom; FillRect(hdc, &rcGap, (HBRUSH)(DWORD_PTR)(((_fDisabled) ? COLOR_BTNFACE : COLOR_WINDOW) + 1)); if (_fFocus && _cbType == kDropDownList) { //First if there is a focus rect then remove the focus rect // shrink the focus rect by the inset rcFocus.top += _yInset; rcFocus.bottom -= _yInset; if (_fRightAlign) rcFocus.left = _rcButton.right; else rcFocus.right = _rcButton.left; rcFocus.left += _xInset; rcFocus.right -= _xInset; // We need to erase the focus rect if we haven't already // erased the background DrawFocusRect(hdc, &rcFocus); } _pserv->TxDraw( DVASPECT_CONTENT, // Draw Aspect -1, // Lindex NULL, // Info for drawing optimazation NULL, // target device information hdc, // Draw device HDC NULL, // Target device HDC (const RECTL *) &rcClient,// Bounding client rectangle NULL, // Clipping rectangle for metafiles &ps.rcPaint, // Update rectangle NULL, // Call back function NULL, // Call back parameter TXTVIEW_ACTIVE); // What view - the active one! // Restore palette if there is one if(hpalOld) SelectPalette(hdc, hpalOld, TRUE); RestoreDC(hdc, -1); if(TxGetEffects() == TXTEFFECT_SUNKEN && dwMajorVersion < VERS4) DrawSunkenBorder(_hwnd, hdc); //Redraw the focus rect, don't have to recalc since we already did above if (_fFocus && _cbType == kDropDownList) DrawFocusRect(hdc, &rcFocus); DrawButton(hdc, _fMousedown); } else { // We have to draw the button first because CbMessageItemHandler // will perform a IntersectClipRect which will prevent us from // drawing the button later DrawButton(hdc, _fMousedown); CbMessageItemHandler(hdc, ITEM_MSG_DRAWCOMBO, 0, 0); } EndPaint(_hwnd, &ps); return FALSE; }