windows-nt/Source/XPSP1/NT/windows/richedit/re30/cbhost.cpp
2020-09-26 16:20:57 +08:00

3088 lines
74 KiB
C++

/*
* @doc INTERNAL
*
* @module LBHOST.CPP -- Text Host for CreateWindow() Rich Edit
* Combo Box Control |
* Implements CCmbBxWinHost message
*
* Original Author:
* Jerry Kim
*
* History: <nl>
* 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 *) &gt );
goto serv;
case WM_GETTEXTLENGTH:
GETTEXTLENGTHEX gtl;
if (W32->OnWin9x() || phost->_fANSIwindow)
W32->AnsiFilter( msg, wparam, lparam, (void *) &gtl );
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)&gtl, 0, &lr);
Assert(hr == NOERROR);
#else
_pserv->TxSendMessage(EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 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)&gt, (LPARAM)szStr, &lr);
Assert(hr == NOERROR);
#else
_pserv->TxSendMessage(EM_GETTEXTEX, (WPARAM)&gt, (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 <nl>
* S_FALSE -- success, but do some different action
* depending on the event type (see below).
*
* @comm
* <CBN_DBLCLK> user double-clicks an item in the list box
*
* <CBN_ERRSPACE> The list box cannot allocate enough memory to
* fulfill a request
*
* <CBN_KILLFOCUS> The list box loses the keyboard focus
*
* <CBN_SELENDCANCEL> notification message is sent when the user
* selects an item, but then selects another control or closes the
* dialog box
*
* <CBN_SELCHANGE> notification message is sent when the user changes
* the current selection in the list box of a combo box
*
* <CBN_SETFOCUS> The list box receives the keyboard focus
*
* <CBN_CLOSEUP> This message is sent when the listbox has been closed
*
* <CBN_SELENDOK> notification message is sent when the user selects a
* list item, or selects an item and then closes the list
*
* <CBN_EDITCHANGE> 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
*
* <CBN_EDITUPDATE> notification message is sent when the edit control
* portion of a combo box is about to display altered text
*
* <CBN_DROPDOWN> 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 <p iNotify>
{
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;
}