3398 lines
89 KiB
C++
3398 lines
89 KiB
C++
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module HOST.C -- Text Host for CreateWindow() Rich Edit Control |
|
|
* Implements CTxtWinHost message and ITextHost interfaces
|
|
*
|
|
* Original Author: <nl>
|
|
* Original RichEdit code: David R. Fulmer
|
|
* Christian Fortini
|
|
* Murray Sargent
|
|
*
|
|
* History: <nl>
|
|
* 8/1/95 ricksa Documented and brought to new ITextHost definition
|
|
* 10/28/95 murrays cleaned up and moved default char/paraformat cache
|
|
* cache code into text services
|
|
*
|
|
* Set tabs every four (4) columns
|
|
*
|
|
* Copyright (c) 1995-1998 Microsoft Corporation. All rights reserved.
|
|
*/
|
|
#include "_common.h"
|
|
#include "_host.h"
|
|
#include "imm.h"
|
|
#include "_format.h"
|
|
#include "_edit.h"
|
|
#include "_cfpf.h"
|
|
|
|
ASSERTDATA
|
|
|
|
//////////////////////////// System Window Procs ////////////////////////////
|
|
LRESULT CreateAnsiWindow(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
CREATESTRUCTA *pcsa,
|
|
BOOL fIs10)
|
|
{
|
|
AssertSz((WM_CREATE == msg) || (WM_NCCREATE == msg),
|
|
"CreateAnsiWindow called with invalid message!");
|
|
|
|
CTxtWinHost *phost = (CTxtWinHost *) GetWindowLongPtr(hwnd, ibPed);
|
|
|
|
// The only thing we need to convert are the strings,
|
|
// so just do a structure copy and replace the strings.
|
|
CREATESTRUCTW csw = *(CREATESTRUCTW *)pcsa;
|
|
CStrInW strinwName(pcsa->lpszName, GetKeyboardCodePage());
|
|
CStrInW strinwClass(pcsa->lpszClass, CP_ACP);
|
|
|
|
csw.lpszName = (WCHAR *)strinwName;
|
|
csw.lpszClass = (WCHAR *)strinwClass;
|
|
|
|
if (!phost)
|
|
{
|
|
// No host yet so create it
|
|
phost = CTxtWinHost::OnNCCreate(hwnd, &csw, TRUE, fIs10);
|
|
}
|
|
|
|
if (WM_NCCREATE == msg)
|
|
{
|
|
return phost != NULL;
|
|
}
|
|
|
|
if (NULL == phost)
|
|
{
|
|
// For WM_CREATE -1 indicates failure
|
|
return -1;
|
|
}
|
|
|
|
// Do the stuff specific to create
|
|
return phost->OnCreate(&csw);
|
|
|
|
}
|
|
|
|
CTxtWinHost *g_phostdel = NULL;
|
|
|
|
void DeleteDanglingHosts()
|
|
{
|
|
CLock lock;
|
|
CTxtWinHost *phostdel = g_phostdel;
|
|
while(phostdel)
|
|
{
|
|
CTxtWinHost *phost = phostdel;
|
|
phostdel = phostdel->_pnextdel;
|
|
CTxtWinHost::OnNCDestroy(phost);
|
|
}
|
|
g_phostdel = NULL;
|
|
}
|
|
|
|
extern "C" LRESULT CALLBACK RichEdit10ANSIWndProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGINPARAM(TRCSUBSYSHOST, TRCSCOPEINTERN, "RichEdit10ANSIWndProc", msg);
|
|
|
|
if ((WM_CREATE == msg) || (WM_NCCREATE == msg))
|
|
{
|
|
return CreateAnsiWindow(hwnd, msg, (CREATESTRUCTA *) lparam, TRUE);
|
|
}
|
|
|
|
// ignore WM_DESTROY and wait for WM_NCDESTROY
|
|
if (WM_DESTROY == msg)
|
|
{
|
|
CLock lock;
|
|
CTxtWinHost *phost = (CTxtWinHost *) GetWindowLongPtr(hwnd, ibPed);
|
|
phost->_pnextdel = g_phostdel;
|
|
g_phostdel = phost;
|
|
return 0;
|
|
}
|
|
|
|
if (WM_NCDESTROY == msg)
|
|
msg = WM_DESTROY;
|
|
|
|
return W32->ANSIWndProc( hwnd, msg, wparam, lparam, TRUE);
|
|
}
|
|
|
|
LRESULT CALLBACK RichEditANSIWndProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGINPARAM(TRCSUBSYSHOST, TRCSCOPEINTERN, "RichEditANSIWndProc", msg);
|
|
|
|
if ((WM_CREATE == msg) || (WM_NCCREATE == msg))
|
|
{
|
|
return CreateAnsiWindow(hwnd, msg, (CREATESTRUCTA *) lparam, FALSE);
|
|
}
|
|
|
|
return W32->ANSIWndProc( hwnd, msg, wparam, lparam, FALSE);
|
|
|
|
}
|
|
|
|
/*
|
|
* RichEditWndProc (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 RichEditWndProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "RichEditWndProc");
|
|
|
|
LRESULT lres = 0;
|
|
HRESULT hr;
|
|
SETTEXTEX st;
|
|
|
|
CTxtWinHost *phost = hwnd ? (CTxtWinHost *) GetWindowLongPtr(hwnd, ibPed) : NULL;
|
|
|
|
#ifdef DEBUG
|
|
Tracef(TRCSEVINFO, "hwnd %lx, msg %lx, wparam %lx, lparam %lx", hwnd, msg, wparam, lparam);
|
|
#endif // DEBUG
|
|
|
|
switch(msg)
|
|
{
|
|
case WM_NCCREATE:
|
|
return CTxtWinHost::OnNCCreate(hwnd, (CREATESTRUCT *)lparam, FALSE, FALSE) != NULL;
|
|
|
|
case WM_CREATE:
|
|
// We may be on a system with no WM_NCCREATE (e.g. WinCE)
|
|
if (!phost)
|
|
{
|
|
phost = CTxtWinHost::OnNCCreate(hwnd, (CREATESTRUCT *) lparam, FALSE, FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if(phost)
|
|
{
|
|
CLock lock;
|
|
CTxtWinHost *phostdel = g_phostdel;
|
|
if (phostdel == phost)
|
|
g_phostdel = phost->_pnextdel;
|
|
else
|
|
{
|
|
while (phostdel)
|
|
{
|
|
if (phostdel->_pnextdel == phost)
|
|
{
|
|
phostdel->_pnextdel = phost->_pnextdel;
|
|
break;
|
|
}
|
|
phostdel = phostdel->_pnextdel;
|
|
}
|
|
}
|
|
|
|
CTxtWinHost::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();
|
|
|
|
// Handle mouse/keyboard/scroll message-filter notifications
|
|
if(phost->_fKeyMaskSet || phost->_fMouseMaskSet || phost->_fScrollMaskSet)
|
|
{
|
|
// We may need to fire a MSGFILTER notification. In the tests
|
|
// below, we check to see if mouse, keyboard, or scroll events
|
|
// are hit and enabled for notifications. If so, we fire the
|
|
// msgfilter notification. The list of events was generated
|
|
// from RichEdit 1.0 sources. The code gets all keyboard and
|
|
// mouse actions, whereas the RichEdit 1.0 code only got
|
|
// WM_KEYDOWN, WM_KEYUP, WM_CHAR, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
|
// WM_MOUSEACTIVATE, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE,
|
|
// WM_RBUTTONDBLCLK, WM_RBUTTONDOWN, WM_RBUTTONUP. Note that the
|
|
// following code doesn't send a notification for AltGr characters
|
|
// (LeftCtrl+RightAlt+vkey), since some hosts misinterpret these
|
|
// characters as hot keys.
|
|
if (phost->_fKeyMaskSet && IN_RANGE(WM_KEYFIRST, msg, WM_KEYLAST) &&
|
|
(msg != WM_KEYDOWN ||
|
|
(GetKeyboardFlags() & (ALT | CTRL)) != (LCTRL | RALT)) || // AltGr
|
|
phost->_fMouseMaskSet && (msg == WM_MOUSEACTIVATE ||
|
|
IN_RANGE(WM_MOUSEFIRST, msg, WM_MOUSELAST)) ||
|
|
phost->_fScrollMaskSet && IN_RANGE(WM_HSCROLL, msg, WM_VSCROLL))
|
|
{
|
|
MSGFILTER msgfltr;
|
|
|
|
ZeroMemory(&msgfltr.nmhdr, sizeof(NMHDR));
|
|
msgfltr.msg = msg;
|
|
msgfltr.wParam = wparam;
|
|
msgfltr.lParam = lparam;
|
|
|
|
// The MSDN document on MSGFILTER is wrong, if the
|
|
// send message returns 0 (NOERROR via TxNotify in this
|
|
// case), it means process the event. Otherwise, return.
|
|
//
|
|
// The documentation states the reverse.
|
|
//
|
|
if(phost->TxNotify(EN_MSGFILTER, &msgfltr) == NOERROR)
|
|
{
|
|
// Since client is allowed to modify the contents of
|
|
// msgfltr, we must use the returned values.
|
|
msg = msgfltr.msg;
|
|
wparam = msgfltr.wParam;
|
|
lparam = msgfltr.lParam;
|
|
}
|
|
else
|
|
{
|
|
lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(msg)
|
|
{
|
|
case EM_SETEVENTMASK:
|
|
phost->_fKeyMaskSet = !!(lparam & ENM_KEYEVENTS);
|
|
phost->_fMouseMaskSet = !!(lparam & ENM_MOUSEEVENTS);
|
|
phost->_fScrollMaskSet = !!(lparam & ENM_SCROLLEVENTS);
|
|
goto serv;
|
|
|
|
case 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;
|
|
}
|
|
goto serv;
|
|
|
|
case WM_CREATE:
|
|
{
|
|
//bug fix #5386
|
|
//need to convert ANSI -> UNICODE for Win9x systems which didn't go through
|
|
//the ANSI wndProc
|
|
if (W32->OnWin9x() && !phost->_fANSIwindow)
|
|
{
|
|
CREATESTRUCT cs = *(CREATESTRUCT*)lparam;
|
|
CStrInW strinwName(((CREATESTRUCTA*)lparam)->lpszName, GetKeyboardCodePage());
|
|
CStrInW strinwClass(((CREATESTRUCTA*)lparam)->lpszClass, CP_ACP);
|
|
|
|
cs.lpszName = (WCHAR*)strinwName;
|
|
cs.lpszClass = (WCHAR*)strinwClass;
|
|
|
|
lres = phost->OnCreate(&cs);
|
|
}
|
|
else
|
|
lres = phost->OnCreate((CREATESTRUCT*)lparam);
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
lres = phost->OnKeyDown((WORD) wparam, (DWORD) lparam);
|
|
if(lres) // Didn't process code:
|
|
goto serv; // give it to text services
|
|
|
|
break;
|
|
|
|
case WM_GETTEXT:
|
|
GETTEXTEX gt;
|
|
if (W32->OnWin9x() || phost->_fANSIwindow)
|
|
W32->AnsiFilter( msg, wparam, lparam, (void *) > );
|
|
goto serv;
|
|
|
|
case WM_COPYDATA:
|
|
PCOPYDATASTRUCT pcds;
|
|
pcds = (PCOPYDATASTRUCT) lparam;
|
|
if (HIWORD(pcds->dwData) == 1200 && // Unicode code page
|
|
LOWORD(pcds->dwData) == WM_SETTEXT) // Only message we know about
|
|
{
|
|
st.flags = ST_CHECKPROTECTION;
|
|
st.codepage = 1200;
|
|
msg = EM_SETTEXTEX;
|
|
wparam = (WPARAM) &st;
|
|
lparam = (LPARAM) pcds->lpData;
|
|
goto serv;
|
|
}
|
|
else
|
|
lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
|
|
break;
|
|
|
|
case WM_GETTEXTLENGTH:
|
|
GETTEXTLENGTHEX gtl;
|
|
if (W32->OnWin9x() || phost->_fANSIwindow)
|
|
W32->AnsiFilter( msg, wparam, lparam, (void *) >l );
|
|
goto serv;
|
|
|
|
case WM_CHAR:
|
|
if(GetKeyboardFlags() & ALTNUMPAD) // Handle Alt+0ddd in CTxtEdit
|
|
goto serv; // so that other hosts also work
|
|
|
|
else if (W32->OnWin9x() || phost->_fANSIwindow)
|
|
{
|
|
CW32System::WM_CHAR_INFO wmci;
|
|
wmci._fAccumulate = phost->_fAccumulateDBC != 0;
|
|
W32->AnsiFilter( msg, wparam, lparam, (void *) &wmci,
|
|
((CTxtEdit *)phost->_pserv)->Get10Mode() );
|
|
if (wmci._fLeadByte)
|
|
{
|
|
phost->_fAccumulateDBC = TRUE;
|
|
phost->_chLeadByte = wparam << 8;
|
|
goto Exit; // Wait for trail byte
|
|
}
|
|
else if (wmci._fTrailByte)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
lres = phost->OnChar((WORD) wparam, (DWORD) lparam);
|
|
if(lres) // Didn't process code:
|
|
goto serv; // give it to text services
|
|
break;
|
|
|
|
case WM_ENABLE:
|
|
if(!wparam ^ phost->_fDisabled)
|
|
{
|
|
// Stated of window changed so invalidate it so it will
|
|
// get redrawn.
|
|
InvalidateRect(phost->_hwnd, NULL, TRUE);
|
|
phost->SetScrollBarsForWmEnable(wparam);
|
|
}
|
|
phost->_fDisabled = !wparam; // Set disabled flag
|
|
lres = 0; // Return value for message
|
|
// Fall thru to WM_SYSCOLORCHANGE?
|
|
case WM_SYSCOLORCHANGE:
|
|
phost->OnSysColorChange();
|
|
goto serv; // Notify text services that
|
|
// system colors have changed
|
|
case WM_GETDLGCODE:
|
|
lres = phost->OnGetDlgCode(wparam, lparam);
|
|
break;
|
|
|
|
case EM_GETOPTIONS:
|
|
lres = phost->OnGetOptions();
|
|
break;
|
|
|
|
case EM_GETPASSWORDCHAR:
|
|
lres = phost->_chPassword;
|
|
break;
|
|
|
|
case EM_GETRECT:
|
|
phost->OnGetRect((LPRECT)lparam);
|
|
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_SETBKGNDCOLOR:
|
|
lres = (LRESULT) phost->_crBackground;
|
|
phost->_fNotSysBkgnd = !wparam;
|
|
phost->_crBackground = (COLORREF) lparam;
|
|
|
|
if(wparam)
|
|
phost->_crBackground = GetSysColor(COLOR_WINDOW);
|
|
|
|
if(lres != (LRESULT) phost->_crBackground)
|
|
{
|
|
// Notify text services that color has changed
|
|
LRESULT lres1 = 0;
|
|
phost->_pserv->TxSendMessage(WM_SYSCOLORCHANGE, 0, 0, &lres1);
|
|
phost->TxInvalidateRect(NULL, TRUE);
|
|
}
|
|
break;
|
|
|
|
case WM_STYLECHANGING:
|
|
// Just pass this one to the default window proc
|
|
lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
|
|
break;
|
|
|
|
case WM_STYLECHANGED:
|
|
//
|
|
// For now, we only interested in GWL_EXSTYLE Transparent mode changed.
|
|
// This is to fix Bug 753 since Window95 is not passing us
|
|
// the WS_EX_TRANSPARENT.
|
|
//
|
|
lres = 1;
|
|
if(GWL_EXSTYLE == wparam)
|
|
{
|
|
LPSTYLESTRUCT lpss = (LPSTYLESTRUCT) lparam;
|
|
if(phost->IsTransparentMode() != (BOOL)(lpss->styleNew & WS_EX_TRANSPARENT))
|
|
{
|
|
phost->_dwExStyle = lpss->styleNew;
|
|
((CTxtEdit *)phost->_pserv)->OnTxBackStyleChange(TRUE);
|
|
|
|
// Return 0 to indicate we have handled this message
|
|
lres = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EM_SHOWSCROLLBAR:
|
|
{
|
|
Assert(wparam == SB_VERT || wparam == SB_HORZ);
|
|
DWORD dwBit = wparam == SB_VERT ? WS_VSCROLL : WS_HSCROLL;
|
|
|
|
phost->_dwStyle |= dwBit;
|
|
if(!lparam)
|
|
phost->_dwStyle &= ~dwBit;
|
|
|
|
phost->TxShowScrollBar((int) wparam, lparam);
|
|
if (lparam)
|
|
phost->TxSetScrollRange((int) wparam, 0, 0, TRUE);
|
|
}
|
|
break;
|
|
|
|
case EM_SETOPTIONS:
|
|
phost->OnSetOptions((WORD) wparam, (DWORD) lparam);
|
|
lres = (phost->_dwStyle & ECO_STYLES);
|
|
if(phost->_fEnableAutoWordSel)
|
|
lres |= ECO_AUTOWORDSELECTION;
|
|
break;
|
|
|
|
case EM_SETPASSWORDCHAR:
|
|
if(phost->_chPassword != (TCHAR)wparam)
|
|
{
|
|
phost->_chPassword = (TCHAR)wparam;
|
|
phost->_pserv->OnTxPropertyBitsChange(TXTBIT_USEPASSWORD,
|
|
phost->_chPassword ? TXTBIT_USEPASSWORD : 0);
|
|
}
|
|
break;
|
|
|
|
case EM_SETREADONLY:
|
|
phost->OnSetReadOnly(BOOL(wparam));
|
|
lres = 1;
|
|
break;
|
|
|
|
case EM_SETRECTNP:
|
|
case EM_SETRECT:
|
|
phost->OnSetRect((LPRECT)lparam, wparam == 1, msg == EM_SETRECT);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
phost->_pserv->TxSendMessage(msg, wparam, lparam, &lres);
|
|
lres = phost->OnSize(hwnd, wparam, (int)LOWORD(lparam), (int)HIWORD(lparam));
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
|
|
// richedit 1.0 didn't cause InvalidateRect which OnSunkenWindowPosChanging will do
|
|
if(phost->TxGetEffects() == TXTEFFECT_SUNKEN && !((CTxtEdit *)phost->_pserv)->Get10Mode())
|
|
phost->OnSunkenWindowPosChanging(hwnd, (WINDOWPOS *) 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)))
|
|
{
|
|
POINT pt;
|
|
GetCursorPos(&pt);
|
|
::ScreenToClient(hwnd, &pt);
|
|
phost->_pserv->OnTxSetCursor(
|
|
DVASPECT_CONTENT,
|
|
-1,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, // Client rect - no redraw
|
|
pt.x,
|
|
pt.y);
|
|
lres = TRUE;
|
|
}
|
|
}
|
|
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_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HPALETTE hpalOld = NULL;
|
|
HDC hdc = BeginPaint(hwnd, &ps);
|
|
RECT rcClient;
|
|
|
|
// Set up the palette for drawing our data
|
|
if(phost->_hpal)
|
|
{
|
|
hpalOld = SelectPalette(hdc, phost->_hpal, TRUE);
|
|
RealizePalette(hdc);
|
|
}
|
|
|
|
// Since we are using the CS_PARENTDC style, make sure
|
|
// the clip region is limited to our client window.
|
|
GetClientRect(hwnd, &rcClient);
|
|
SaveDC(hdc);
|
|
IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right,
|
|
rcClient.bottom);
|
|
|
|
#if 0 // Useful for debugging
|
|
TCHAR rgch[512];
|
|
wsprintf(rgch, TEXT("Paint : (%d, %d, %d, %d)\n"),
|
|
rcClient.left,
|
|
rcClient.top,
|
|
rcClient.right,
|
|
rcClient.bottom);
|
|
OutputDebugString(rgch);
|
|
#endif
|
|
|
|
phost->_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
|
|
#ifndef PEGASUS
|
|
if(hpalOld)
|
|
SelectPalette(hdc, hpalOld, TRUE);
|
|
#endif
|
|
|
|
if(phost->TxGetEffects() == TXTEFFECT_SUNKEN && dwMajorVersion < VERS4)
|
|
phost->DrawSunkenBorder(hwnd, hdc);
|
|
|
|
RestoreDC(hdc, -1);
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
break;
|
|
|
|
case EM_SETMARGINS:
|
|
|
|
phost->OnSetMargins(wparam, LOWORD(lparam), HIWORD(lparam));
|
|
break;
|
|
|
|
case EM_SETPALETTE:
|
|
|
|
// Application is setting a palette for us to use.
|
|
phost->_hpal = (HPALETTE) wparam;
|
|
|
|
// Invalidate the window & repaint to reflect the new palette.
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
break;
|
|
|
|
default:
|
|
serv:
|
|
hr = phost->_pserv->TxSendMessage(msg, wparam, lparam, &lres);
|
|
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);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
phost->Release();
|
|
return lres;
|
|
}
|
|
|
|
static BOOL GetIconic(
|
|
HWND hwnd)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "GetIconic");
|
|
|
|
while(hwnd)
|
|
{
|
|
if(IsIconic(hwnd))
|
|
return TRUE;
|
|
hwnd = GetParent(hwnd);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//////////////// CTxtWinHost Creation/Initialization/Destruction ///////////////////////
|
|
|
|
/*
|
|
* CTxtWinHost::OnNCCreate (hwnd, pcs)
|
|
*
|
|
* @mfunc
|
|
* Static global method to handle WM_NCCREATE message (see remain.c)
|
|
*/
|
|
CTxtWinHost *CTxtWinHost::OnNCCreate(
|
|
HWND hwnd,
|
|
const CREATESTRUCT *pcs,
|
|
BOOL fIsAnsi,
|
|
BOOL fIs10Mode)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnNCCreate");
|
|
|
|
#if defined DEBUG && !defined(PEGASUS)
|
|
GdiSetBatchLimit(1);
|
|
#endif
|
|
|
|
CTxtWinHost *phost = new CTxtWinHost();
|
|
|
|
if(!phost)
|
|
return 0;
|
|
|
|
CREATESTRUCT cs = *pcs; // prefer C++ compiler not to modify constant
|
|
|
|
//bug fix #5386
|
|
// Window wasn't created with the Richedit20A window class
|
|
// and we are under Win9x, need to convert string to UNICODE
|
|
CStrInW strinwName(((LPSTR)pcs->lpszName), GetKeyboardCodePage());
|
|
CStrInW strinwClass(((LPSTR)pcs->lpszClass), CP_ACP);
|
|
if (!fIsAnsi && W32->OnWin9x())
|
|
{
|
|
cs.lpszName = (WCHAR *)strinwName;
|
|
cs.lpszClass = (WCHAR *)strinwClass;
|
|
}
|
|
|
|
// Stores phost in associated window data
|
|
if(!phost->Init(hwnd, (const CREATESTRUCT*)&cs, fIsAnsi, fIs10Mode))
|
|
{
|
|
phost->Shutdown();
|
|
delete phost;
|
|
phost = NULL;
|
|
}
|
|
return phost;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::OnNCDestroy (phost)
|
|
*
|
|
* @mfunc
|
|
* Static global method to handle WM_CREATE message
|
|
*
|
|
* @devnote
|
|
* phost ptr is stored in window data (GetWindowLong())
|
|
*/
|
|
void CTxtWinHost::OnNCDestroy(
|
|
CTxtWinHost *phost)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnNCDestroy");
|
|
|
|
phost->Shutdown();
|
|
phost->Release();
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::CTxtWinHost()
|
|
*
|
|
* @mfunc
|
|
* constructor
|
|
*/
|
|
CTxtWinHost::CTxtWinHost()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::CTxtWinHost");
|
|
|
|
#ifndef NOACCESSIBILITY
|
|
_pTypeInfo = NULL;
|
|
#endif
|
|
|
|
_fRegisteredForDrop = FALSE;
|
|
_crefs = 1;
|
|
if(!_fNotSysBkgnd)
|
|
_crBackground = GetSysColor(COLOR_WINDOW);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::~CTxtWinHost()
|
|
*
|
|
* @mfunc
|
|
* destructor
|
|
*/
|
|
CTxtWinHost::~CTxtWinHost()
|
|
{
|
|
AssertSz(_pserv == NULL,
|
|
"CTxtWinHost::~CTxtWinHost - shutdown not called till destructor");
|
|
|
|
if(_pserv)
|
|
Shutdown();
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::Shutdown()
|
|
*
|
|
* @mfunc Shut down this object, but doesn't delete memory
|
|
*/
|
|
void CTxtWinHost::Shutdown()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::Shutdown");
|
|
ITextServices *pserv;
|
|
|
|
HostRevokeDragDrop(); // Revoke our drop target
|
|
|
|
if(_pserv)
|
|
{
|
|
// Guarantee that no recursive callbacks can happen during shutdown.
|
|
pserv = _pserv;
|
|
_pserv = NULL;
|
|
|
|
pserv->OnTxInPlaceDeactivate();
|
|
pserv->Release();
|
|
|
|
// Host release was not the final release so notify
|
|
// text services that they need to keep their reference
|
|
// to the host valid.
|
|
if (!_fTextServiceFree)
|
|
{
|
|
((CTxtEdit *)pserv)->SetReleaseHost();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
ImmTerminate(); // Terminate only useful on Mac.
|
|
if(_hwnd)
|
|
SetWindowLongPtr(_hwnd, ibPed, 0);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::Init (hwnd, pcs)
|
|
*
|
|
* @mfunc
|
|
* Initialize this CTxtWinHost
|
|
*/
|
|
BOOL CTxtWinHost::Init(
|
|
HWND hwnd, //@parm Window handle for this control
|
|
const CREATESTRUCT *pcs, //@parm Corresponding CREATESTRUCT
|
|
BOOL fIsAnsi, //@parm is ansi window
|
|
BOOL fIs10Mode) //@parm is 1.0 mode window
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::Init");
|
|
|
|
AssertSz(!fIs10Mode || (fIsAnsi && fIs10Mode),
|
|
"CTxtWinHost::Init input flags are out of sync!");
|
|
|
|
if(!pcs->lpszClass)
|
|
return FALSE;
|
|
|
|
// Set pointer back to CTxtWinHost from the window
|
|
if(hwnd)
|
|
SetWindowLongPtr(hwnd, ibPed, (INT_PTR)this);
|
|
|
|
_hwnd = hwnd;
|
|
|
|
// Here we want to keep track of the "RichEdit20A"window class
|
|
// The RICHEDIT window class is handled by a wrapper dll.
|
|
// If the class name is "RICHEDIT", then we need to turn on the
|
|
// RichEdit 1.0 compatibility bit. IsAnsiWindowClass tests that class as well.
|
|
_fANSIwindow = fIsAnsi;
|
|
|
|
// Edit controls created without a window are multiline by default
|
|
// so that paragraph formats can be
|
|
_dwStyle = ES_MULTILINE;
|
|
_fHidden = TRUE;
|
|
|
|
if(pcs)
|
|
{
|
|
_hwndParent = pcs->hwndParent;
|
|
_dwExStyle = pcs->dwExStyle;
|
|
_dwStyle = pcs->style;
|
|
|
|
if (!fIs10Mode)
|
|
{
|
|
// Only set this for 2.0 windows
|
|
// According to the edit control documentation WS_HSCROLL implies that
|
|
// ES_AUTOSCROLL is set and WS_VSCROLL implies that ES_AUTOVSCROLL is
|
|
// set. Here, we make this so.
|
|
if(_dwStyle & WS_HSCROLL)
|
|
_dwStyle |= ES_AUTOHSCROLL;
|
|
|
|
// handle default disabled
|
|
if(_dwStyle & WS_DISABLED)
|
|
_fDisabled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (GetBkMode(GetDC(hwnd)) == TRANSPARENT)
|
|
{
|
|
_dwExStyle |= WS_EX_TRANSPARENT;
|
|
}
|
|
else
|
|
{
|
|
_dwExStyle &= ~WS_EX_TRANSPARENT;
|
|
}
|
|
}
|
|
|
|
if(_dwStyle & WS_VSCROLL)
|
|
_dwStyle |= ES_AUTOVSCROLL;
|
|
|
|
_fBorder = !!(_dwStyle & WS_BORDER);
|
|
|
|
if((_dwStyle & ES_SUNKEN) || (_dwExStyle & WS_EX_CLIENTEDGE))
|
|
_fBorder = TRUE;
|
|
|
|
// handle default passwords
|
|
if(_dwStyle & ES_PASSWORD)
|
|
_chPassword = TEXT('*');
|
|
|
|
// On Win95 ES_SUNKEN and WS_BORDER get mapped to WS_EX_CLIENTEDGE
|
|
if(_fBorder && dwMajorVersion >= VERS4)
|
|
{
|
|
_dwExStyle |= WS_EX_CLIENTEDGE;
|
|
SetWindowLong(_hwnd, GWL_EXSTYLE, _dwExStyle);
|
|
}
|
|
|
|
// Process some flags for mirrored control
|
|
if (_dwExStyle & WS_EX_LAYOUTRTL)
|
|
{
|
|
// Swap whatever RTL params we have
|
|
_dwStyle = (_dwStyle & ~ES_RIGHT) | (_dwStyle & ES_RIGHT ^ ES_RIGHT);
|
|
_dwExStyle = (_dwExStyle & ~WS_EX_RTLREADING) | (_dwExStyle & WS_EX_RTLREADING ^ WS_EX_RTLREADING);
|
|
_dwExStyle = (_dwExStyle & ~WS_EX_LEFTSCROLLBAR) |
|
|
(_dwStyle & ES_RIGHT ? WS_EX_LEFTSCROLLBAR : 0);
|
|
|
|
// Disable mirroring layout to avoid GDI mirroring mapping mode
|
|
_dwExStyle &= ~WS_EX_LAYOUTRTL;
|
|
|
|
SetWindowLong(_hwnd, GWL_STYLE, _dwStyle);
|
|
SetWindowLong(_hwnd, GWL_EXSTYLE, _dwExStyle);
|
|
}
|
|
}
|
|
|
|
// Create Text Services component
|
|
// Watch out for sys param and sys font initialization!! see below.
|
|
if(FAILED(CreateTextServices()))
|
|
return FALSE;
|
|
|
|
_xInset = (char)W32->GetCxBorder();
|
|
_yInset = (char)W32->GetCyBorder();
|
|
|
|
if (!_fBorder)
|
|
{
|
|
_xInset += _xInset;
|
|
_yInset += _yInset;
|
|
}
|
|
|
|
// At this point the border flag is set and so is the pixels per inch
|
|
// so we can initalize the inset.
|
|
// This must be done after CreatingTextServices so sys params are valid
|
|
SetDefaultInset();
|
|
|
|
// Set alignment and paragraph direction
|
|
PARAFORMAT PF2;
|
|
|
|
PF2.dwMask = 0;
|
|
|
|
BOOL fRCAlign = _dwStyle & (ES_RIGHT | ES_CENTER) || _dwExStyle & WS_EX_RIGHT;
|
|
if(fRCAlign)
|
|
{
|
|
PF2.dwMask |= PFM_ALIGNMENT;
|
|
PF2.wAlignment = (WORD)(_dwStyle & ES_CENTER ? PFA_CENTER : 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);
|
|
}
|
|
|
|
if (fIs10Mode)
|
|
{
|
|
((CTxtEdit *)_pserv)->Set10Mode();
|
|
// Remove the WS_VSCROLL and WS_HSCROLL initially
|
|
if (_hwnd && !(_dwStyle & ES_DISABLENOSCROLL))
|
|
{
|
|
SetScrollRange(_hwnd, SB_VERT, 0, 0, TRUE);
|
|
SetScrollRange(_hwnd, SB_HORZ, 0, 0, TRUE);
|
|
DWORD dwStyle = _dwStyle & ~(WS_VSCROLL | WS_HSCROLL);
|
|
SetWindowLong(_hwnd, GWL_STYLE, dwStyle);
|
|
|
|
// bug fix:
|
|
// On some systems, ie Digital PII-266, we don't get a WM_PAINT message
|
|
// when we change the window style. So force a WM_PAINT into the message queue
|
|
InvalidateRect(_hwnd, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
// Set window text
|
|
if(pcs && pcs->lpszName)
|
|
{
|
|
if(FAILED(_pserv->TxSetText((TCHAR *)pcs->lpszName)))
|
|
{
|
|
SafeReleaseAndNULL((IUnknown **)&_pserv);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if(_dwStyle & ES_LOWERCASE)
|
|
_pserv->TxSendMessage(EM_SETEDITSTYLE, SES_LOWERCASE,
|
|
SES_LOWERCASE | SES_UPPERCASE, NULL);
|
|
|
|
if(!ImmInitialize()) // Mac Only
|
|
{
|
|
#if defined(DEBUG) && defined(MACPORT)
|
|
OutputDebugString(TEXT("Could not register Imm ImmInitializeForMac.\r\n"));
|
|
#endif // DEBUG
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CTxtWinHost::CreateTextServices()
|
|
{
|
|
IUnknown *pUnk;
|
|
HRESULT hr = ::CreateTextServices(NULL, this, &pUnk);
|
|
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
// Get text services interface
|
|
hr = pUnk->QueryInterface(IID_ITextServices, (void **)&_pserv);
|
|
|
|
// Regardless of whether the previous call succeeded or failed, we are
|
|
// done with the private interface.
|
|
pUnk->Release();
|
|
|
|
if(hr == NOERROR)
|
|
{
|
|
((CTxtEdit *)_pserv)->_fInOurHost = TRUE;
|
|
// FE extended styles might set the fFE bit
|
|
if(_dwExStyle & (WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR))
|
|
_pserv->TxSendMessage(EM_SETEDITSTYLE, SES_BIDI, SES_BIDI, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::OnCreate (pcs)
|
|
*
|
|
* @mfunc
|
|
* Handle WM_CREATE message
|
|
*
|
|
* @rdesc
|
|
* LRESULT = -1 if failed to in-place activate; else 0
|
|
*/
|
|
LRESULT CTxtWinHost::OnCreate(
|
|
const CREATESTRUCT *pcs)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::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;
|
|
|
|
DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE);
|
|
|
|
// Hide all scrollbars to start
|
|
if(_hwnd && !(dwStyle & ES_DISABLENOSCROLL) && !((CTxtEdit *)_pserv)->Get10Mode())
|
|
{
|
|
SetScrollRange(_hwnd, SB_VERT, 0, 0, TRUE);
|
|
SetScrollRange(_hwnd, SB_HORZ, 0, 0, TRUE);
|
|
|
|
dwStyle &= ~(WS_VSCROLL | WS_HSCROLL);
|
|
SetWindowLong(_hwnd, GWL_STYLE, dwStyle);
|
|
}
|
|
|
|
if(!(dwStyle & (ES_READONLY | ES_NOOLEDRAGDROP)))
|
|
{
|
|
// This isn't a read only window or a no drop window,
|
|
// so we need a drop target.
|
|
HostRegisterDragDrop();
|
|
}
|
|
|
|
_usIMEMode = 0;
|
|
if(dwStyle & ES_NOIME)
|
|
{
|
|
_usIMEMode = ES_NOIME;
|
|
// Tell textservices to turnoff ime
|
|
_pserv->TxSendMessage(EM_SETEDITSTYLE, SES_NOIME, SES_NOIME, NULL);
|
|
}
|
|
else if(dwStyle & ES_SELFIME)
|
|
_usIMEMode = ES_SELFIME;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////// IUnknown ////////////////////////////////
|
|
|
|
HRESULT CTxtWinHost::QueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::QueryInterface");
|
|
|
|
if(IsEqualIID(riid, IID_IUnknown))
|
|
*ppv = (IUnknown *)(ITextHost2*)this;
|
|
|
|
else if(IsEqualIID(riid, IID_ITextHost) )
|
|
*ppv = (ITextHost *)(CTxtWinHost*)this;
|
|
|
|
else if(IsEqualIID(riid, IID_ITextHost2) )
|
|
*ppv = (ITextHost2 *)(CTxtWinHost*)this;
|
|
|
|
else
|
|
*ppv = NULL;
|
|
|
|
if(*ppv)
|
|
{
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
ULONG CTxtWinHost::AddRef(void)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::AddRef");
|
|
|
|
return ++_crefs;
|
|
}
|
|
|
|
ULONG CTxtWinHost::Release(void)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::Release");
|
|
|
|
--_crefs;
|
|
|
|
if(!_crefs)
|
|
{
|
|
#ifndef NOACCESSIBILITY
|
|
if(_pTypeInfo)
|
|
{
|
|
_pTypeInfo->Release();
|
|
_pTypeInfo = NULL;
|
|
}
|
|
#endif
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return _crefs;
|
|
}
|
|
|
|
|
|
//////////////////////////////// Activation ////////////////////////////////
|
|
|
|
//////////////////////////////// Properties ////////////////////////////////
|
|
|
|
|
|
TXTEFFECT CTxtWinHost::TxGetEffects() const
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::TxGetEffects");
|
|
|
|
if((_dwStyle & ES_SUNKEN) || (_dwExStyle & WS_EX_CLIENTEDGE))
|
|
return TXTEFFECT_SUNKEN;
|
|
|
|
return TXTEFFECT_NONE;
|
|
}
|
|
|
|
/////////////////////////////// Keyboard Messages //////////////////////////////////
|
|
|
|
/*
|
|
* CTxtWinHost::OnKeyDown (vkey, dwFlags)
|
|
*
|
|
* @mfunc
|
|
* Handle WM_KEYDOWN messages that need to send a message to the parent
|
|
* window (may happen when control is in a dialog box)
|
|
*
|
|
* #rdesc
|
|
* LRESULT = (code processed) ? 0 : 1
|
|
*/
|
|
LRESULT CTxtWinHost::OnKeyDown(
|
|
WORD vkey, //@parm WM_KEYDOWN wparam (virtual key code)
|
|
DWORD dwFlags) //@parm WM_KEYDOWN flags
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnKeyDown");
|
|
|
|
if(!_fInDialogBox) // Not in a dialog box
|
|
return 1; // Signal key-down msg not processed
|
|
|
|
DWORD dwKeyFlags = GetKeyboardFlags();
|
|
|
|
switch(vkey)
|
|
{
|
|
case VK_ESCAPE:
|
|
PostMessage(_hwndParent, WM_CLOSE, 0, 0);
|
|
return 0;
|
|
|
|
case VK_RETURN:
|
|
if(!(dwKeyFlags & CTRL) && !(_dwStyle & ES_WANTRETURN))
|
|
{
|
|
// Send to default button
|
|
HWND hwndT;
|
|
LRESULT id = SendMessage(_hwndParent, DM_GETDEFID, 0, 0);
|
|
|
|
if(LOWORD(id) && (hwndT = GetDlgItem(_hwndParent, LOWORD(id))))
|
|
{
|
|
SendMessage(_hwndParent, WM_NEXTDLGCTL, (WPARAM) hwndT, (LPARAM) 1);
|
|
if(GetFocus() != _hwnd)
|
|
PostMessage(hwndT, WM_KEYDOWN, (WPARAM) VK_RETURN, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case VK_TAB:
|
|
if(!(dwKeyFlags & CTRL))
|
|
{
|
|
SendMessage(_hwndParent, WM_NEXTDLGCTL,
|
|
!!(dwKeyFlags & SHIFT), 0);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::OnChar (vkey, dwFlags)
|
|
*
|
|
* @mfunc
|
|
* Eat some WM_CHAR messages for a control in a dialog box
|
|
*
|
|
* #rdesc
|
|
* LRESULT = (code processed) ? 0 : 1
|
|
*/
|
|
LRESULT CTxtWinHost::OnChar(
|
|
WORD vkey, //@parm WM_CHAR wparam (translated key code)
|
|
DWORD dwFlags) //@parm WM_CHAR flags
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnChar");
|
|
|
|
if(!_fInDialogBox || (GetKeyboardFlags() & CTRL))
|
|
return 1;
|
|
|
|
switch(vkey)
|
|
{
|
|
case 'J' - 0x40: // Ctrl-Return generates Ctrl-J (LF):
|
|
case VK_RETURN: // treat it as an ordinary return
|
|
// We need to filter-out cases where we don't want to insert <cr> in
|
|
// 1.0 mode here since the info isn't available within the ped
|
|
if (((CTxtEdit*)_pserv)->Get10Mode())
|
|
{
|
|
if (_fInDialogBox && dwFlags != MK_CONTROL && !(_dwStyle & ES_WANTRETURN))
|
|
return 0;
|
|
|
|
if (!(_dwStyle & ES_MULTILINE))
|
|
{
|
|
//richedit beeps in this case
|
|
((CTxtEdit*)_pserv)->Beep();
|
|
return 0;
|
|
}
|
|
}
|
|
else if (!(_dwStyle & ES_WANTRETURN))
|
|
return 0; // Signal char processed (eaten)
|
|
break;
|
|
|
|
case VK_TAB:
|
|
return 0;
|
|
}
|
|
|
|
return 1; // Signal char not processed
|
|
}
|
|
|
|
|
|
///////////////////////////////// View rectangle //////////////////////////////////////
|
|
|
|
void CTxtWinHost::OnGetRect(
|
|
LPRECT prc)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnGetRect");
|
|
|
|
RECT rcInset;
|
|
LONG lSelBarWidth = 0;
|
|
|
|
if(_fEmSetRectCalled)
|
|
{
|
|
// Get the selection bar width and add it back to the view inset so
|
|
// we return the rectangle that the application set.
|
|
TxGetSelectionBarWidth(&lSelBarWidth);
|
|
}
|
|
|
|
// Get view inset (in HIMETRIC)
|
|
TxGetViewInset(&rcInset);
|
|
|
|
// Get client rect in pixels
|
|
TxGetClientRect(prc);
|
|
|
|
// Modify the client rect by the inset converted to pixels
|
|
prc->left += W32->HimetricXtoDX(rcInset.left + lSelBarWidth, W32->GetXPerInchScreenDC());
|
|
prc->top += W32->HimetricYtoDY(rcInset.top, W32->GetYPerInchScreenDC());
|
|
prc->right -= W32->HimetricXtoDX(rcInset.right, W32->GetXPerInchScreenDC());
|
|
prc->bottom -= W32->HimetricYtoDY(rcInset.bottom, W32->GetYPerInchScreenDC());
|
|
}
|
|
|
|
void CTxtWinHost::OnSetRect(
|
|
LPRECT prc, //@parm Desired formatting RECT
|
|
BOOL fNewBehavior, //@parm If TRUE, prc is inset RECT directly
|
|
BOOL fRedraw) //@parm If TRUE, redraw after setting RECT
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnSetRect");
|
|
|
|
RECT rcClient;
|
|
LONG lSelBarWidth;
|
|
|
|
// Assuming this is not set to the default, turn on special EM_SETRECT
|
|
// processing. The important part of this is that we subtract the selection
|
|
// bar from the view inset because the EM_SETRECT rectangle does not
|
|
// include the selection bar.
|
|
_fEmSetRectCalled = TRUE;
|
|
|
|
if(!prc)
|
|
{
|
|
// We are back to the default so turn off special EM_SETRECT procesing.
|
|
_fEmSetRectCalled = FALSE;
|
|
SetDefaultInset();
|
|
}
|
|
else
|
|
{
|
|
// For screen display, the following intersects new view RECT
|
|
// with adjusted client area RECT
|
|
TxGetClientRect(&rcClient);
|
|
|
|
// Adjust client rect. Factors in space for borders
|
|
if(_fBorder)
|
|
{
|
|
rcClient.top += _yInset;
|
|
rcClient.bottom -= _yInset - 1;
|
|
rcClient.left += _xInset;
|
|
rcClient.right -= _xInset;
|
|
}
|
|
|
|
if(!fNewBehavior)
|
|
{
|
|
// Intersect new view rectangle with adjusted client area rectangle
|
|
if(!IntersectRect(&_rcViewInset, &rcClient, prc))
|
|
_rcViewInset = rcClient;
|
|
}
|
|
else
|
|
_rcViewInset = *prc;
|
|
|
|
// Get selection bar width
|
|
TxGetSelectionBarWidth(&lSelBarWidth);
|
|
|
|
// Compute inset in pixels and convert to HIMETRIC.
|
|
_rcViewInset.left = W32->DXtoHimetricX(_rcViewInset.left - rcClient.left, W32->GetXPerInchScreenDC())
|
|
- lSelBarWidth;
|
|
_rcViewInset.top = W32->DYtoHimetricY(_rcViewInset.top - rcClient.top, W32->GetYPerInchScreenDC());
|
|
_rcViewInset.right = W32->DXtoHimetricX(rcClient.right
|
|
- _rcViewInset.right, W32->GetXPerInchScreenDC());
|
|
_rcViewInset.bottom = W32->DYtoHimetricY(rcClient.bottom
|
|
- _rcViewInset.bottom, W32->GetYPerInchScreenDC());
|
|
}
|
|
if(fRedraw)
|
|
{
|
|
_pserv->OnTxPropertyBitsChange(TXTBIT_VIEWINSETCHANGE,
|
|
TXTBIT_VIEWINSETCHANGE);
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////// System notifications //////////////////////////////////
|
|
|
|
void CTxtWinHost::OnSysColorChange()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnSysColorChange");
|
|
|
|
if(!_fNotSysBkgnd)
|
|
_crBackground = GetSysColor(COLOR_WINDOW);
|
|
TxInvalidateRect(NULL, TRUE);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::OnGetDlgCode (wparam, lparam)
|
|
*
|
|
* @mfunc
|
|
* Handle some WM_GETDLGCODE messages
|
|
*
|
|
* #rdesc
|
|
* LRESULT = dialog code
|
|
*/
|
|
LRESULT CTxtWinHost::OnGetDlgCode(
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnGetDlgCode");
|
|
|
|
LRESULT lres = DLGC_WANTCHARS | DLGC_WANTARROWS | DLGC_WANTTAB;
|
|
|
|
if(_dwStyle & ES_MULTILINE)
|
|
lres |= DLGC_WANTALLKEYS;
|
|
|
|
if(!(_dwStyle & ES_SAVESEL))
|
|
lres |= DLGC_HASSETSEL;
|
|
|
|
// HACK: If we get one of these messages then we turn on the special
|
|
// EM_SETSEL behavior. The problem is that _fInDialogBox gets turned
|
|
// on after the EM_SETSEL has occurred.
|
|
_fUseSpecialSetSel = TRUE;
|
|
|
|
/*
|
|
** -------------------------------------------- JEFFBOG HACK ----
|
|
** Only set Dialog Box Flag if GETDLGCODE message is generated by
|
|
** IsDialogMessage -- if so, the lParam will be a pointer to the
|
|
** message structure passed to IsDialogMessage; otherwise, lParam
|
|
** will be NULL. Reason for the HACK alert: the wParam & lParam
|
|
** for GETDLGCODE is still not clearly defined and may end up
|
|
** changing in a way that would throw this off
|
|
** -------------------------------------------- JEFFBOG HACK ----
|
|
*/
|
|
if(lparam)
|
|
_fInDialogBox = TRUE;
|
|
|
|
/*
|
|
** If this is a WM_SYSCHAR message generated by the UNDO keystroke
|
|
** we want this message so we can EAT IT in remain.c, case WM_SYSCHAR:
|
|
*/
|
|
if (lparam &&
|
|
(((LPMSG)lparam)->message == WM_SYSCHAR) &&
|
|
(((LPMSG)lparam)->lParam & SYS_ALTERNATE) &&
|
|
wparam == VK_BACK)
|
|
{
|
|
lres |= DLGC_WANTMESSAGE;
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
|
|
///////////////////////////////// Other messages //////////////////////////////////////
|
|
|
|
LRESULT CTxtWinHost::OnGetOptions() const
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnGetOptions");
|
|
|
|
LRESULT lres = (_dwStyle & ECO_STYLES);
|
|
|
|
if(_fEnableAutoWordSel)
|
|
lres |= ECO_AUTOWORDSELECTION;
|
|
|
|
return lres;
|
|
}
|
|
|
|
void CTxtWinHost::OnSetOptions(
|
|
WORD wOp,
|
|
DWORD eco)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnSetOptions");
|
|
|
|
DWORD dwChangeMask = 0;
|
|
DWORD dwProp = 0;
|
|
DWORD dwStyle;
|
|
DWORD dwStyleNew = _dwStyle;
|
|
const BOOL fAutoWordSel = !!(eco & ECO_AUTOWORDSELECTION);
|
|
BOOL bNeedToTurnOffIME = FALSE;
|
|
|
|
// We keep track of the bits changed and then if any have changed we
|
|
// query for all of our property bits and then send them. This simplifies
|
|
// the code because we don't have to set all the bits specially. If the
|
|
// code is changed to make the properties more in line with the new
|
|
// model, we want to look at this code again.
|
|
|
|
// Single line controls can't have a selection bar or do vertical writing
|
|
if(!(_dwStyle & ES_MULTILINE))
|
|
eco &= ~ECO_SELECTIONBAR;
|
|
|
|
Assert((DWORD)fAutoWordSel <= 1); // Make sure that BOOL is 1/0
|
|
dwStyle = (eco & ECO_STYLES);
|
|
|
|
switch(wOp)
|
|
{
|
|
case ECOOP_SET:
|
|
dwStyleNew = (dwStyleNew & ~ECO_STYLES) | dwStyle;
|
|
_fEnableAutoWordSel = fAutoWordSel;
|
|
break;
|
|
|
|
case ECOOP_OR:
|
|
dwStyleNew |= dwStyle; // Setting a :1 flag = TRUE
|
|
if(fAutoWordSel) // or FALSE is 1 instruction
|
|
_fEnableAutoWordSel = TRUE; // Setting it to a BOOL
|
|
break; // averages 9 instructions!
|
|
|
|
case ECOOP_AND:
|
|
dwStyleNew &= (dwStyle | ~ECO_STYLES);
|
|
if(!fAutoWordSel)
|
|
_fEnableAutoWordSel = FALSE;
|
|
break;
|
|
|
|
case ECOOP_XOR:
|
|
dwStyleNew ^= dwStyle;
|
|
if(fAutoWordSel)
|
|
_fEnableAutoWordSel ^= 1;
|
|
break;
|
|
}
|
|
|
|
if(_fEnableAutoWordSel != (unsigned)fAutoWordSel)
|
|
dwChangeMask |= TXTBIT_AUTOWORDSEL;
|
|
|
|
if(dwStyleNew != _dwStyle)
|
|
{
|
|
DWORD dwChange = dwStyleNew ^ _dwStyle;
|
|
|
|
AssertSz(!(dwChange & ~ECO_STYLES), "non-eco style changed");
|
|
_dwStyle = dwStyleNew;
|
|
SetWindowLong(_hwnd, GWL_STYLE, dwStyleNew);
|
|
|
|
if(dwChange & ES_NOHIDESEL)
|
|
dwChangeMask |= TXTBIT_HIDESELECTION;
|
|
|
|
// These two local variables to use to keep track of
|
|
// previous setting of ES_READONLY
|
|
BOOL bReadOnly = (_dwStyle & ES_READONLY);
|
|
|
|
if(dwChange & ES_READONLY)
|
|
{
|
|
dwChangeMask |= TXTBIT_READONLY;
|
|
|
|
// Change drop target state as appropriate.
|
|
if(dwStyleNew & ES_READONLY)
|
|
HostRevokeDragDrop();
|
|
|
|
else
|
|
HostRegisterDragDrop();
|
|
|
|
bReadOnly = (dwStyleNew & ES_READONLY);
|
|
}
|
|
|
|
if(dwChange & ES_VERTICAL)
|
|
dwChangeMask |= TXTBIT_VERTICAL;
|
|
|
|
if(dwChange & ES_NOIME)
|
|
{
|
|
_usIMEMode = (dwStyleNew & ES_NOIME) ? ES_NOIME : 0;
|
|
bNeedToTurnOffIME = (_usIMEMode ==ES_NOIME);
|
|
}
|
|
else if(dwChange & ES_SELFIME)
|
|
_usIMEMode = (dwStyleNew & ES_SELFIME) ? ES_SELFIME : 0;
|
|
|
|
// No action required for ES_WANTRETURN nor for ES_SAVESEL
|
|
// Do this last
|
|
if(dwChange & ES_SELECTIONBAR)
|
|
dwChangeMask |= TXTBIT_SELBARCHANGE;
|
|
}
|
|
|
|
if (dwChangeMask)
|
|
{
|
|
TxGetPropertyBits(dwChangeMask, &dwProp);
|
|
_pserv->OnTxPropertyBitsChange(dwChangeMask, dwProp);
|
|
}
|
|
|
|
if (bNeedToTurnOffIME)
|
|
// Tell textservices to turnoff ime
|
|
_pserv->TxSendMessage(EM_SETEDITSTYLE, SES_NOIME, SES_NOIME, NULL);
|
|
}
|
|
|
|
void CTxtWinHost::OnSetReadOnly(
|
|
BOOL fReadOnly)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnSetReadOnly");
|
|
|
|
DWORD dwT = GetWindowLong(_hwnd, GWL_STYLE);
|
|
DWORD dwUpdatedBits = 0;
|
|
|
|
if(fReadOnly)
|
|
{
|
|
dwT |= ES_READONLY;
|
|
_dwStyle |= ES_READONLY;
|
|
|
|
// Turn off Drag Drop
|
|
HostRevokeDragDrop();
|
|
dwUpdatedBits |= TXTBIT_READONLY;
|
|
}
|
|
else
|
|
{
|
|
dwT &= ~ES_READONLY;
|
|
_dwStyle &= ~ES_READONLY;
|
|
|
|
// Turn drag drop back on
|
|
HostRegisterDragDrop();
|
|
}
|
|
|
|
_pserv->OnTxPropertyBitsChange(TXTBIT_READONLY, dwUpdatedBits);
|
|
|
|
SetWindowLong(_hwnd, GWL_STYLE, dwT);
|
|
}
|
|
|
|
|
|
//////////////////////////////////// Helpers /////////////////////////////////////////
|
|
|
|
void CTxtWinHost::SetDefaultInset()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::SetDefaultInset");
|
|
|
|
// Generate default view rect from client rect
|
|
if(_fBorder)
|
|
{
|
|
// Factors in space for borders
|
|
_rcViewInset.top = W32->DYtoHimetricY(_yInset, W32->GetYPerInchScreenDC());
|
|
_rcViewInset.bottom = W32->DYtoHimetricY(_yInset - 1, W32->GetYPerInchScreenDC());
|
|
_rcViewInset.left = W32->DXtoHimetricX(_xInset, W32->GetXPerInchScreenDC());
|
|
_rcViewInset.right = W32->DXtoHimetricX(_xInset, 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(W32->GetCxBorder(), W32->GetXPerInchScreenDC());
|
|
_rcViewInset.right = W32->DXtoHimetricX(W32->GetCxBorder(), W32->GetXPerInchScreenDC());
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////// Far East Support //////////////////////////////////////
|
|
|
|
//#ifdef WIN95_IME
|
|
HIMC CTxtWinHost::TxImmGetContext()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::TxImmGetContext");
|
|
|
|
HIMC himc;
|
|
|
|
Assert(_hwnd);
|
|
himc = ImmGetContext(_hwnd);
|
|
return himc;
|
|
}
|
|
|
|
void CTxtWinHost::TxImmReleaseContext(
|
|
HIMC himc)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::TxImmReleaseContext");
|
|
|
|
Assert(_hwnd);
|
|
ImmReleaseContext(_hwnd, himc);
|
|
}
|
|
|
|
//#endif
|
|
|
|
void CTxtWinHost::HostRevokeDragDrop()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::HostRevokeDragDrop");
|
|
|
|
if(_fRegisteredForDrop)
|
|
{
|
|
// Note that if the revoke fails we want to know about this in debug
|
|
// builds so we can fix any problems. In retail, we can't really do
|
|
// so we just ignore it.
|
|
#ifdef DEBUG
|
|
HRESULT hr =
|
|
#endif // DEBUG
|
|
|
|
RevokeDragDrop(_hwnd);
|
|
|
|
#ifdef DEBUG
|
|
TESTANDTRACEHR(hr);
|
|
#endif // DEBUG
|
|
|
|
_fRegisteredForDrop = FALSE;
|
|
}
|
|
}
|
|
|
|
void CTxtWinHost::HostRegisterDragDrop()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::RegisterDragDrop");
|
|
|
|
IDropTarget *pdt;
|
|
|
|
if(!_fRegisteredForDrop && _pserv->TxGetDropTarget(&pdt) == NOERROR)
|
|
{
|
|
// The most likely reason for RegisterDragDrop to fail is some kind of
|
|
// bug in our program.
|
|
|
|
HRESULT hr = RegisterDragDrop(_hwnd, pdt);
|
|
|
|
if(hr == NOERROR)
|
|
_fRegisteredForDrop = TRUE;
|
|
|
|
#ifndef PEGASUS
|
|
pdt->Release();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
static void DrawRectFn(
|
|
HDC hdc,
|
|
RECT *prc,
|
|
INT icrTL,
|
|
INT icrBR,
|
|
BOOL fBot,
|
|
BOOL fRght)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "DrawRectFn");
|
|
|
|
COLORREF cr = GetSysColor(icrTL);
|
|
COLORREF crSave = SetBkColor(hdc, cr);
|
|
RECT rc = *prc;
|
|
|
|
// top
|
|
rc.bottom = rc.top + 1;
|
|
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
|
|
|
|
// left
|
|
rc.bottom = prc->bottom;
|
|
rc.right = rc.left + 1;
|
|
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
|
|
|
|
if(icrTL != icrBR)
|
|
{
|
|
cr = GetSysColor(icrBR);
|
|
SetBkColor(hdc, cr);
|
|
}
|
|
|
|
// right
|
|
rc.right = prc->right;
|
|
rc.left = rc.right - 1;
|
|
if(!fBot)
|
|
rc.bottom -= W32->GetCyHScroll();
|
|
if(fRght)
|
|
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
|
|
|
|
// bottom
|
|
if(fBot)
|
|
{
|
|
rc.left = prc->left;
|
|
rc.top = rc.bottom - 1;
|
|
if(!fRght)
|
|
rc.right -= W32->GetCxVScroll();
|
|
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
|
|
}
|
|
SetBkColor(hdc, crSave);
|
|
}
|
|
|
|
#define cmultBorder 1
|
|
|
|
void CTxtWinHost::OnSunkenWindowPosChanging(
|
|
HWND hwnd,
|
|
WINDOWPOS *pwndpos)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnSunkenWindowPosChanging");
|
|
|
|
if(IsWindowVisible(hwnd))
|
|
{
|
|
RECT rc;
|
|
HWND hwndParent;
|
|
|
|
GetWindowRect(hwnd, &rc);
|
|
InflateRect(&rc, W32->GetCxBorder() * cmultBorder, W32->GetCyBorder() * cmultBorder);
|
|
hwndParent = GetParent(hwnd);
|
|
MapWindowPoints(HWND_DESKTOP, hwndParent, (POINT *) &rc, 2);
|
|
InvalidateRect(hwndParent, &rc, FALSE);
|
|
}
|
|
}
|
|
|
|
void CTxtWinHost::DrawSunkenBorder(
|
|
HWND hwnd,
|
|
HDC hdc)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::DrawSunkenBorder");
|
|
BOOL fVScroll = (_dwStyle & WS_VSCROLL);
|
|
BOOL fHScroll = (_dwStyle & WS_HSCROLL);
|
|
|
|
RECT rc;
|
|
RECT rcParent;
|
|
HWND hwndParent;
|
|
|
|
// if we're not visible, don't do anything.
|
|
if(!IsWindowVisible(hwnd))
|
|
return;
|
|
|
|
GetWindowRect(hwnd, &rc);
|
|
hwndParent = GetParent(hwnd);
|
|
rcParent = rc;
|
|
MapWindowPoints(HWND_DESKTOP, hwndParent, (POINT *)&rcParent, 2);
|
|
InflateRect(&rcParent, W32->GetCxBorder(), W32->GetCyBorder());
|
|
OffsetRect(&rc, -rc.left, -rc.top);
|
|
|
|
if(_pserv)
|
|
{
|
|
// If we have a text control then get whether it thinks there are
|
|
// scroll bars.
|
|
_pserv->TxGetHScroll(NULL, NULL, NULL, NULL, &fHScroll);
|
|
_pserv->TxGetVScroll(NULL, NULL, NULL, NULL, &fVScroll);
|
|
}
|
|
|
|
|
|
// Draw inner rect
|
|
DrawRectFn(hdc, &rc, icr3DDarkShadow, COLOR_BTNFACE,
|
|
!fHScroll, !fVScroll);
|
|
|
|
// Draw outer rect
|
|
hwndParent = GetParent(hwnd);
|
|
hdc = GetDC(hwndParent);
|
|
DrawRectFn(hdc, &rcParent, COLOR_BTNSHADOW, COLOR_BTNHIGHLIGHT,
|
|
TRUE, TRUE);
|
|
ReleaseDC(hwndParent, hdc);
|
|
}
|
|
|
|
LRESULT CTxtWinHost::OnSize(
|
|
HWND hwnd,
|
|
WORD fwSizeType,
|
|
int nWidth,
|
|
int nHeight)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnSize");
|
|
|
|
BOOL fIconic = GetIconic(hwnd);
|
|
DWORD dw = TXTBIT_CLIENTRECTCHANGE;
|
|
if(_sWidth != nWidth && !fIconic && !_fIconic)
|
|
{
|
|
_sWidth = (short)nWidth; // Be sure to update _sWidth
|
|
dw = TXTBIT_EXTENTCHANGE;
|
|
}
|
|
|
|
if(!_fVisible)
|
|
{
|
|
if(!fIconic)
|
|
_fResized = TRUE;
|
|
}
|
|
else if(!fIconic)
|
|
{
|
|
// We use this property because this will force a recalc.
|
|
// We don't actually recalc on a client rect change because
|
|
// most of the time it is pointless. We force one here because
|
|
// some applications use size changes to calculate the optimal
|
|
// size of the window.
|
|
_pserv->OnTxPropertyBitsChange(dw, dw);
|
|
|
|
if(_fIconic)
|
|
{
|
|
TRACEINFOSZ("Restoring from iconic");
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
}
|
|
|
|
// Draw borders
|
|
if(TxGetEffects() == TXTEFFECT_SUNKEN && dwMajorVersion < VERS4)
|
|
DrawSunkenBorder(hwnd, NULL);
|
|
}
|
|
_fIconic = fIconic; // Update _fIconic
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CTxtWinHost::OnTxVisibleChange(
|
|
BOOL fVisible)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::OnTxVisibleChange");
|
|
|
|
_fVisible = fVisible;
|
|
|
|
if(!_fVisible && _fResized)
|
|
{
|
|
RECT rc;
|
|
// Control was resized while hidden, need to really resize now
|
|
TxGetClientRect(&rc);
|
|
_fResized = FALSE;
|
|
_pserv->OnTxPropertyBitsChange(TXTBIT_CLIENTRECTCHANGE,
|
|
TXTBIT_CLIENTRECTCHANGE);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//////////////////////////// ITextHost Interface ////////////////////////////
|
|
|
|
// @doc EXTERNAL
|
|
/*
|
|
* CTxtWinHost::TxGetDC()
|
|
*
|
|
* @mfunc
|
|
* Abstracts GetDC so Text Services does not need a window handle.
|
|
*
|
|
* @rdesc
|
|
* A DC or NULL in the event of an error.
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
HDC CTxtWinHost::TxGetDC()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetDC");
|
|
|
|
Assert(_hwnd);
|
|
return ::GetDC(_hwnd);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxReleaseDC (hdc)
|
|
*
|
|
* @mfunc
|
|
* Release DC gotten by TxGetDC.
|
|
*
|
|
* @rdesc
|
|
* 1 - HDC was released. <nl>
|
|
* 0 - HDC was not released. <nl>
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
int CTxtWinHost::TxReleaseDC(
|
|
HDC hdc) //@parm DC to release
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxReleaseDC");
|
|
|
|
Assert(_hwnd);
|
|
return ::ReleaseDC (_hwnd, hdc);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxShowScrollBar (fnBar, fShow)
|
|
*
|
|
* @mfunc
|
|
* Shows or Hides scroll bar in Text Host window
|
|
*
|
|
* @rdesc
|
|
* TRUE on success, FALSE otherwise
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxShowScrollBar(
|
|
INT fnBar, //@parm Specifies scroll bar(s) to be shown or hidden
|
|
BOOL fShow) //@parm Specifies whether scroll bar is shown or hidden
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxShowScrollBar");
|
|
|
|
Assert(_hwnd);
|
|
LONG nMax;
|
|
|
|
if(fnBar == SB_HORZ)
|
|
_pserv->TxGetHScroll(NULL, &nMax, NULL, NULL, NULL);
|
|
else
|
|
_pserv->TxGetVScroll(NULL, &nMax, NULL, NULL, NULL);
|
|
|
|
return W32->ShowScrollBar(_hwnd, fnBar, fShow, nMax);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxEnableScrollBar (fuSBFlags, fuArrowflags)
|
|
*
|
|
* @mfunc
|
|
* Enables or disables one or both scroll bar arrows
|
|
* in Text Host window.
|
|
*
|
|
* @rdesc
|
|
* If the arrows are enabled or disabled as specified, the return
|
|
* value is TRUE. If the arrows are already in the requested state or an
|
|
* error occurs, the return value is FALSE.
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxEnableScrollBar (
|
|
INT fuSBFlags, //@parm Specifies scroll bar type
|
|
INT fuArrowflags) //@parm Specifies whether and which scroll bar arrows
|
|
// are enabled or disabled
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxEnableScrollBar");
|
|
|
|
Assert(_hwnd);
|
|
return W32->EnableScrollBar(_hwnd, fuSBFlags, fuArrowflags);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxSetScrollRange (fnBar, nMinPos, nMaxPos, fRedraw)
|
|
*
|
|
* @mfunc
|
|
* Sets the minimum and maximum position values for the specified
|
|
* scroll bar in the text host window.
|
|
*
|
|
* @rdesc
|
|
* If the arrows are enabled or disabled as specified, the return value
|
|
* is TRUE. If the arrows are already in the requested state or an error
|
|
* occurs, the return value is FALSE.
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxSetScrollRange(
|
|
INT fnBar, //@parm Scroll bar flag
|
|
LONG nMinPos, //@parm Minimum scrolling position
|
|
INT nMaxPos, //@parm Maximum scrolling position
|
|
BOOL fRedraw) //@parm Specifies whether scroll bar should be redrawn
|
|
{ // to reflect change
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxSetScrollRange");
|
|
|
|
Assert(_hwnd);
|
|
|
|
if(NULL == _pserv)
|
|
{
|
|
// We are initializing so do this instead of callback
|
|
return ::SetScrollRange(_hwnd, fnBar, nMinPos, nMaxPos, fRedraw);
|
|
}
|
|
SetScrollInfo(fnBar, fRedraw);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxSetScrollPos (fnBar, nPos, fRedraw)
|
|
*
|
|
* @mfunc
|
|
* Tells Text host to set the position of the scroll box (thumb) in the
|
|
* specified scroll bar and, if requested, redraws the scroll bar to
|
|
* reflect the new position of the scroll box.
|
|
*
|
|
* @rdesc
|
|
* TRUE on success; FALSE otherwise.
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxSetScrollPos (
|
|
INT fnBar, //@parm Scroll bar flag
|
|
INT nPos, //@parm New position in scroll box
|
|
BOOL fRedraw) //@parm Redraw flag
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxSetScrollPos");
|
|
|
|
Assert(_hwnd);
|
|
|
|
if(NULL == _pserv)
|
|
{
|
|
// We are initializing so do this instead of callback
|
|
return ::SetScrollPos(_hwnd, fnBar, nPos, fRedraw);
|
|
}
|
|
SetScrollInfo(fnBar, fRedraw);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxInvalidateRect (prc, fMode)
|
|
*
|
|
* @mfunc
|
|
* Adds a rectangle to the Text Host window's update region
|
|
*
|
|
* @comm
|
|
* This function may be called while inactive; however the host
|
|
* implementation is free to invalidate an area greater than
|
|
* the requested rect.
|
|
*/
|
|
void CTxtWinHost::TxInvalidateRect(
|
|
LPCRECT prc, //@parm Address of rectangle coordinates
|
|
BOOL fMode) //@parm Erase background flag
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxInvalidateRect");
|
|
|
|
Assert(_hwnd);
|
|
|
|
if(!_fVisible)
|
|
{
|
|
// There doesn't seem to be a deterministic way to determine whether
|
|
// our window is visible or not via message notifications. Therefore,
|
|
// we check this each time incase it might have changed.
|
|
_fVisible = IsWindowVisible(_hwnd);
|
|
|
|
if(_fVisible)
|
|
OnTxVisibleChange(TRUE);
|
|
}
|
|
|
|
// Don't bother with invalidating rect if we aren't visible
|
|
if(_fVisible)
|
|
{
|
|
if(IsTransparentMode())
|
|
{
|
|
RECT rcParent;
|
|
HWND hParent = ::GetParent (_hwnd);
|
|
|
|
Assert(hParent);
|
|
|
|
// For transparent mode, we need to invalidate the parent
|
|
// so it will paint the background.
|
|
if(prc)
|
|
rcParent = *prc;
|
|
else
|
|
TxGetClientRect(&rcParent);
|
|
|
|
::MapWindowPoints(_hwnd, hParent, (LPPOINT)&rcParent, 2);
|
|
::InvalidateRect(hParent, &rcParent, fMode);
|
|
// ::HideCaret(_hwnd);
|
|
}
|
|
::InvalidateRect(_hwnd, prc, fMode);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxViewChange (fUpdate)
|
|
*
|
|
* @mfunc
|
|
* Notify Text Host that update region should be repainted.
|
|
*
|
|
* @comm
|
|
* It is the responsibility of the text services to call
|
|
* TxViewChanged every time it decides that it's visual representation
|
|
* has changed, regardless of whether the control is active or
|
|
* not. If the control is active, the text services has the additional
|
|
* responsibility of making sure the controls window is updated.
|
|
* It can do this in a number of ways: 1) get a DC for the control's
|
|
* window and start blasting pixels (TxGetDC and TxReleaseDC), 2)
|
|
* invalidate the control's window (TxInvalidate), or 3) scroll
|
|
* the control's window (TxScrollWindowEx).
|
|
*
|
|
* Text services can choose to call TxViewChange after it has
|
|
* performed any operations to update the active view and pass a
|
|
* true along with the call. By passing true, the text host
|
|
* calls UpdateWindow to make sure any unpainted areas of the
|
|
* active control are repainted.
|
|
*/
|
|
void CTxtWinHost::TxViewChange(
|
|
BOOL fUpdate) //@parm TRUE = call update window
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxViewChange");
|
|
|
|
Assert(_hwnd);
|
|
|
|
// Don't bother with paint since we aren't visible
|
|
if(_fVisible)
|
|
{
|
|
// For updates requests that are FALSE, we will let the next WM_PAINT
|
|
// message pick up the draw.
|
|
if(fUpdate)
|
|
{
|
|
if(IsTransparentMode())
|
|
{
|
|
HWND hParent = GetParent (_hwnd);
|
|
Assert(hParent);
|
|
|
|
// For transparent mode, we need to update the parent first
|
|
// before we can update ourself. Otherwise, what we painted will
|
|
// be erased by the parent's background later.
|
|
::UpdateWindow (hParent);
|
|
}
|
|
::UpdateWindow (_hwnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxCreateCaret (hbmp, xWidth, yHeight)
|
|
*
|
|
* @mfunc
|
|
* Create new shape for Text Host's caret
|
|
*
|
|
* @rdesc
|
|
* TRUE on success, FALSE otherwise.
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxCreateCaret(
|
|
HBITMAP hbmp, //@parm Handle of bitmap for caret shape
|
|
INT xWidth, //@parm Caret width
|
|
INT yHeight) //@parm Caret height
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxCreateCaret");
|
|
|
|
Assert(_hwnd);
|
|
return ::CreateCaret (_hwnd, hbmp, xWidth, yHeight);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxShowCaret (fShow)
|
|
*
|
|
* @mfunc
|
|
* Make caret visible/invisible at caret position in Text Host window.
|
|
*
|
|
* @rdesc
|
|
* TRUE - call succeeded <nl>
|
|
* FALSE - call failed <nl>
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxShowCaret(
|
|
BOOL fShow) //@parm Flag whether caret is visible
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxShowCaret");
|
|
|
|
return fShow ? ::ShowCaret(_hwnd) : ::HideCaret(_hwnd);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxSetCaretPos (x, y)
|
|
*
|
|
* @mfunc
|
|
* Move caret position to specified coordinates in Text Host window.
|
|
*
|
|
* @rdesc
|
|
* TRUE - call succeeded <nl>
|
|
* FALSE - call failed <nl>
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxSetCaretPos(
|
|
INT x, //@parm Horizontal position (in client coordinates)
|
|
INT y) //@parm Vertical position (in client coordinates)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxSetCaretPos");
|
|
|
|
return ::SetCaretPos(x, y);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxSetTimer (idTimer, uTimeout)
|
|
*
|
|
* @mfunc
|
|
* Request Text Host to creates a timer with specified time out.
|
|
*
|
|
* @rdesc
|
|
* TRUE - call succeeded <nl>
|
|
* FALSE - call failed <nl>
|
|
*/
|
|
BOOL CTxtWinHost::TxSetTimer(
|
|
UINT idTimer, //@parm Timer identifier
|
|
UINT uTimeout) //@parm Timeout in msec
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxSetTimer");
|
|
|
|
Assert(_hwnd);
|
|
return ::SetTimer(_hwnd, idTimer, uTimeout, NULL);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxKillTimer (idTimer)
|
|
*
|
|
* @mfunc
|
|
* Destroy specified timer
|
|
*
|
|
* @rdesc
|
|
* TRUE - call succeeded <nl>
|
|
* FALSE - call failed <nl>
|
|
*
|
|
* @comm
|
|
* This method may be called at any time irrespective of active versus
|
|
* inactive state.
|
|
*/
|
|
void CTxtWinHost::TxKillTimer(
|
|
UINT idTimer) //@parm id of timer
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxKillTimer");
|
|
|
|
Assert(_hwnd);
|
|
::KillTimer(_hwnd, idTimer);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxScrollWindowEx (dx, dy, lprcScroll, lprcClip, hrgnUpdate,
|
|
* lprcUpdate, fuScroll)
|
|
* @mfunc
|
|
* Request Text Host to scroll the content of the specified client area
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
void CTxtWinHost::TxScrollWindowEx (
|
|
INT dx, //@parm Amount of horizontal scrolling
|
|
INT dy, //@parm Amount of vertical scrolling
|
|
LPCRECT lprcScroll, //@parm Scroll rectangle
|
|
LPCRECT lprcClip, //@parm Clip rectangle
|
|
HRGN hrgnUpdate, //@parm Handle of update region
|
|
LPRECT lprcUpdate, //@parm Update rectangle
|
|
UINT fuScroll) //@parm Scrolling flags
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxScrollWindowEx");
|
|
|
|
Assert(_hwnd);
|
|
::ScrollWindowEx(_hwnd, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate, fuScroll);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxSetCapture (fCapture)
|
|
*
|
|
* @mfunc
|
|
* Set mouse capture in Text Host's window.
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may do nothing.
|
|
*/
|
|
void CTxtWinHost::TxSetCapture(
|
|
BOOL fCapture) //@parm Whether to get or release capture
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxSetCapture");
|
|
|
|
Assert(_hwnd);
|
|
if (fCapture)
|
|
::SetCapture(_hwnd);
|
|
else
|
|
::ReleaseCapture();
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxSetFocus ()
|
|
*
|
|
* @mfunc
|
|
* Set focus in text host window.
|
|
*
|
|
* @comm
|
|
* This method is only valid when the control is in-place active;
|
|
* calls while inactive may fail.
|
|
*/
|
|
void CTxtWinHost::TxSetFocus()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxSetFocus");
|
|
|
|
Assert(_hwnd);
|
|
::SetFocus(_hwnd);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxSetCursor (hcur, fText)
|
|
*
|
|
* @mfunc
|
|
* Establish a new cursor shape in the Text Host's window.
|
|
*
|
|
* @comm
|
|
* This method may be called at any time, irrespective of
|
|
* active vs. inactive state.
|
|
*
|
|
* ITextHost::TxSetCursor should be called back by the Text Services
|
|
* to actually set the mouse cursor. If the fText parameter is TRUE,
|
|
* Text Services is trying to set the "text" cursor (cursor over text
|
|
* that is not selected, currently an IBEAM). In that case, the host
|
|
* can set it to whatever the control MousePointer property is. This is
|
|
* required by VB compatibility since, via the MousePointer property,
|
|
* the VB programmer has control over the shape of the mouse cursor,
|
|
* whenever it would normally be set to an IBEAM.
|
|
*/
|
|
void CTxtWinHost::TxSetCursor(
|
|
HCURSOR hcur, //@parm Handle to cursor
|
|
BOOL fText) //@parm Indicates caller wants to set text cursor
|
|
// (IBeam) if true.
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxSetCursor");
|
|
|
|
::SetCursor(hcur);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxScreenToClient (lppt)
|
|
*
|
|
* @mfunc
|
|
* Convert screen coordinates to Text Host window coordinates.
|
|
*
|
|
* @rdesc
|
|
* TRUE - call succeeded <nl>
|
|
* FALSE - call failed <nl>
|
|
*/
|
|
BOOL CTxtWinHost::TxScreenToClient(
|
|
LPPOINT lppt) //@parm Coordinates for point
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxScreenToClient");
|
|
|
|
Assert(_hwnd);
|
|
return ::ScreenToClient(_hwnd, lppt);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxClientToScreen (lppt)
|
|
*
|
|
* @mfunc
|
|
* Convert Text Host coordinates to screen coordinates
|
|
*
|
|
* @rdesc
|
|
* TRUE - call succeeded <nl>
|
|
* FALSE - call failed <nl>
|
|
*
|
|
* @comm
|
|
* This call is valid at any time, although it is allowed to
|
|
* fail. In general, if text services has coordinates it needs
|
|
* to translate from client coordinates (e.g. for TOM's
|
|
* PointFromRange method) the text services will actually be
|
|
* visible.
|
|
*
|
|
* However, if no conversion is possible, then the method will fail.
|
|
*/
|
|
BOOL CTxtWinHost::TxClientToScreen(
|
|
LPPOINT lppt) //@parm Client coordinates to convert.
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxClientToScreen");
|
|
|
|
Assert(_hwnd);
|
|
return ::ClientToScreen(_hwnd, lppt);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxActivate (plOldState)
|
|
*
|
|
* @mfunc
|
|
* Notify Text Host that control is active
|
|
*
|
|
* @rdesc
|
|
* S_OK - call succeeded. <nl>
|
|
* E_FAIL - activation is not possible at this time
|
|
*
|
|
* @comm
|
|
* It is legal for the host to refuse an activation request;
|
|
* the control may be minimized and thus invisible, for instance.
|
|
*
|
|
* The caller should be able to gracefully handle failure to activate.
|
|
*
|
|
* Calling this method more than once does not cumulate; only
|
|
* once TxDeactivate call is necessary to deactive.
|
|
*
|
|
* This function returns an opaque handle in <p plOldState>. The caller
|
|
* (Text Services) should hang onto this handle and return it in a
|
|
* subsequent call to TxDeactivate.
|
|
*/
|
|
HRESULT CTxtWinHost::TxActivate(
|
|
LONG *plOldState) //@parm Where to put previous activation state
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxActivate");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxDeactivate (lNewState)
|
|
*
|
|
* @mfunc
|
|
* Notify Text Host that control is now inactive
|
|
*
|
|
* @rdesc
|
|
* S_OK - call succeeded. <nl>
|
|
* E_FAIL <nl>
|
|
*
|
|
* @comm
|
|
* Calling this method more than once does not cumulate
|
|
*/
|
|
HRESULT CTxtWinHost::TxDeactivate(
|
|
LONG lNewState) //@parm New state (typically value returned by
|
|
// TxActivate
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxDeactivate");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetClientRect (prc)
|
|
*
|
|
* @mfunc
|
|
* Retrive client coordinates of Text Host's client area.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (success) ? S_OK : E_FAIL
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetClientRect(
|
|
LPRECT prc) //@parm Where to put client coordinates
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetClientRect");
|
|
|
|
Assert(_hwnd && prc);
|
|
return ::GetClientRect(_hwnd, prc) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetViewInset (prc)
|
|
*
|
|
* @mfunc
|
|
* Get inset for Text Host window. Inset is the "white space"
|
|
* around text.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = NOERROR
|
|
*
|
|
* @comm
|
|
* The Inset rectangle is not strictly a rectangle. The top, bottom,
|
|
* left, and right fields of the rect structure indicate how far in
|
|
* each direction drawing should be inset. Inset sizes are in client
|
|
* coordinates.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetViewInset(
|
|
LPRECT prc) //@parm Where to put inset rectangle
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetViewInset");
|
|
|
|
Assert(prc);
|
|
|
|
*prc = _rcViewInset;
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetCharFormat (ppCF)
|
|
*
|
|
* @mfunc
|
|
* Get Text Host's default character format
|
|
*
|
|
* @rdesc
|
|
* HRESULT = E_NOTIMPL (not needed in simple Windows host, since text
|
|
* services provides desired default)
|
|
*
|
|
* @comm
|
|
* The callee retains ownwership of the charformat returned. However,
|
|
* the pointer returned must remain valid until the callee notifies
|
|
* Text Services via OnTxPropertyBitsChange that the default character
|
|
* format has changed.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetCharFormat(
|
|
const CHARFORMAT **ppCF) //@parm Where to put ptr to default
|
|
// character format
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetCharFormat");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetParaFormat (ppPF)
|
|
*
|
|
* @mfunc
|
|
* Get Text Host default paragraph format
|
|
*
|
|
* @rdesc
|
|
* HRESULT = E_NOTIMPL (not needed in simple Windows host, since text
|
|
* services provides desired default)
|
|
*
|
|
* @comm
|
|
* The host object (callee) retains ownership of the PARAFORMAT returned.
|
|
* However, the pointer returned must remain valid until the host notifies
|
|
* Text Services (the caller) via OnTxPropertyBitsChange that the default
|
|
* paragraph format has changed.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetParaFormat(
|
|
const PARAFORMAT **ppPF) //@parm Where to put ptr to default
|
|
// paragraph format
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetParaFormat");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetSysColor (nIndex)
|
|
*
|
|
* @mfunc
|
|
* Get specified color identifer from Text Host.
|
|
*
|
|
* @rdesc
|
|
* Color identifier
|
|
*
|
|
* @comm
|
|
* Note that the color returned may be *different* than the
|
|
* color that would be returned from a call to GetSysColor.
|
|
* This allows hosts to override default system behavior.
|
|
*
|
|
* Needless to say, hosts should be very careful about overriding
|
|
* normal system behavior as it could result in inconsistent UI
|
|
* (particularly with respect to Accessibility options).
|
|
*/
|
|
COLORREF CTxtWinHost::TxGetSysColor(
|
|
int nIndex) //@parm Color to get, same parameter as
|
|
// GetSysColor Win32 API
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetSysColor");
|
|
|
|
if (!_fDisabled ||
|
|
nIndex != COLOR_WINDOW && nIndex != COLOR_WINDOWTEXT)
|
|
{
|
|
// This window is not disabled or the color is not interesting
|
|
// in the disabled case.
|
|
return (nIndex == COLOR_WINDOW && _fNotSysBkgnd)
|
|
? _crBackground : GetSysColor(nIndex);
|
|
}
|
|
|
|
// Disabled case
|
|
if (COLOR_WINDOWTEXT == nIndex)
|
|
{
|
|
// Color of text for disabled window
|
|
return GetSysColor(COLOR_GRAYTEXT);
|
|
}
|
|
|
|
// Background color for disabled window
|
|
return GetSysColor(COLOR_3DFACE);
|
|
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetBackStyle (pstyle)
|
|
*
|
|
* @mfunc
|
|
* Get Text Host background style.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = S_OK
|
|
*
|
|
* @xref <e TXTBACKSTYLE>
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetBackStyle(
|
|
TXTBACKSTYLE *pstyle) //@parm Where to put background style
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetBackStyle");
|
|
|
|
*pstyle = (_dwExStyle & WS_EX_TRANSPARENT)
|
|
? TXTBACK_TRANSPARENT : TXTBACK_OPAQUE;
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetMaxLength (pLength)
|
|
*
|
|
* @mfunc
|
|
* Get Text Host's maximum allowed length.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = S_OK
|
|
*
|
|
* @comm
|
|
* This method parallels the EM_LIMITTEXT message.
|
|
* If INFINITE (0xFFFFFFFF) is returned, then text services
|
|
* will use as much memory as needed to store any given text.
|
|
*
|
|
* If the limit returned is less than the number of characters
|
|
* currently in the text engine, no data is lost. Instead,
|
|
* no edits will be allowed to the text *other* than deletion
|
|
* until the text is reduced to below the limit.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetMaxLength(
|
|
DWORD *pLength) //@parm Maximum allowed length, in number of
|
|
// characters
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetMaxLength");
|
|
AssertSz(FALSE, "CTxtWinHost::TxGetMaxLength why is this being called?");
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetScrollBars (pdwScrollBar)
|
|
*
|
|
* @mfunc
|
|
* Get Text Host's scroll bars supported.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = S_OK
|
|
*
|
|
* @comm
|
|
* <p pdwScrollBar> is filled with a boolean combination of the
|
|
* window styles related to scroll bars. Specifically, these are:
|
|
*
|
|
* WS_VSCROLL <nl>
|
|
* WS_HSCROLL <nl>
|
|
* ES_AUTOVSCROLL <nl>
|
|
* ES_AUTOHSCROLL <nl>
|
|
* ES_DISABLENOSCROLL <nl>
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetScrollBars(
|
|
DWORD *pdwScrollBar) //@parm Where to put scrollbar information
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetScrollBars");
|
|
|
|
*pdwScrollBar = _dwStyle & (WS_VSCROLL | WS_HSCROLL | ES_AUTOVSCROLL |
|
|
ES_AUTOHSCROLL | ES_DISABLENOSCROLL);
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetPasswordChar (pch)
|
|
*
|
|
* @mfunc
|
|
* Get Text Host's password character.
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (password character not enabled) ? S_FALSE : S_OK
|
|
*
|
|
* @comm
|
|
* The password char will only be shown if the TXTBIT_USEPASSWORD bit
|
|
* is enabled in TextServices. If the password character changes,
|
|
* re-enable the TXTBIT_USEPASSWORD bit via
|
|
* ITextServices::OnTxPropertyBitsChange.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetPasswordChar(
|
|
TCHAR *pch) //@parm Where to put password character
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetPasswordChar");
|
|
|
|
*pch = _chPassword;
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetAcceleratorPos (pcp)
|
|
*
|
|
* @mfunc
|
|
* Get special character to use for underlining accelerator character.
|
|
*
|
|
* @rdesc
|
|
* Via <p pcp>, returns character position at which underlining
|
|
* should occur. -1 indicates that no character should be underlined.
|
|
* Return value is an HRESULT (usually S_OK).
|
|
*
|
|
* @comm
|
|
* Accelerators allow keyboard shortcuts to various UI elements (like
|
|
* buttons. Typically, the shortcut character is underlined.
|
|
*
|
|
* This function tells Text Services which character is the accelerator
|
|
* and thus should be underlined. Note that Text Services will *not*
|
|
* process accelerators; that is the responsiblity of the host.
|
|
*
|
|
* This method will typically only be called if the TXTBIT_SHOWACCELERATOR
|
|
* bit is set in text services.
|
|
*
|
|
* Note that *any* change to the text in text services will result in the
|
|
* invalidation of the accelerator underlining. In this case, it is the
|
|
* host's responsibility to recompute the appropriate character position
|
|
* and inform text services that a new accelerator is available.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetAcceleratorPos(
|
|
LONG *pcp) //@parm Out parm to receive cp of character to underline
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetAcceleratorPos");
|
|
|
|
*pcp = -1;
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::OnTxCharFormatChange
|
|
*
|
|
* @mfunc
|
|
* Set default character format for the Text Host.
|
|
*
|
|
* @rdesc
|
|
* S_OK - call succeeded. <nl>
|
|
* E_INVALIDARG <nl>
|
|
* E_FAIL <nl>
|
|
*/
|
|
HRESULT CTxtWinHost::OnTxCharFormatChange(
|
|
const CHARFORMAT *pcf) //@parm New default character format
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::OnTxCharFormatChange");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::OnTxParaFormatChange
|
|
*
|
|
* @mfunc
|
|
* Set default paragraph format for the Text Host.
|
|
*
|
|
* @rdesc
|
|
* S_OK - call succeeded. <nl>
|
|
* E_INVALIDARG <nl>
|
|
* E_FAIL <nl>
|
|
*/
|
|
HRESULT CTxtWinHost::OnTxParaFormatChange(
|
|
const PARAFORMAT *ppf) //@parm New default paragraph format
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::OnTxParaFormatChange");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetPropertyBits (dwMask, dwBits)
|
|
*
|
|
* @mfunc
|
|
* Get the bit property settings for Text Host.
|
|
*
|
|
* @rdesc
|
|
* S_OK
|
|
*
|
|
* @comm
|
|
* This call is valid at any time, for any combination of
|
|
* requested property bits. <p dwMask> may be used by the
|
|
* caller to request specific properties.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetPropertyBits(
|
|
DWORD dwMask, //@parm Mask of bit properties to get
|
|
DWORD *pdwBits) //@parm Where to put bit values
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetPropertyBits");
|
|
|
|
// FUTURE: Obvious optimization is to save bits in host the same way that
|
|
// they are returned and just return them instead of this mess.
|
|
|
|
// Note: this RichEdit host never sets TXTBIT_SHOWACCELERATOR or
|
|
// TXTBIT_SAVESELECTION. They are currently only used by Forms^3 host.
|
|
// This host is always rich text.
|
|
|
|
DWORD dwProperties = TXTBIT_RICHTEXT | TXTBIT_ALLOWBEEP;
|
|
|
|
#ifdef DEBUG
|
|
// make sure that TS doesn't think it's plain text mode when
|
|
// we return TXTBIT_RICHTEXT
|
|
if((dwMask & TXTBIT_RICHTEXT) && _pserv)
|
|
{
|
|
DWORD mode;
|
|
mode = _pserv->TxSendMessage(EM_GETTEXTMODE, 0, 0, NULL);
|
|
Assert(mode == TM_RICHTEXT);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
if(_dwStyle & ES_MULTILINE)
|
|
dwProperties |= TXTBIT_MULTILINE;
|
|
|
|
if(_dwStyle & ES_READONLY)
|
|
dwProperties |= TXTBIT_READONLY;
|
|
|
|
if(_dwStyle & ES_PASSWORD)
|
|
dwProperties |= TXTBIT_USEPASSWORD;
|
|
|
|
if(!(_dwStyle & ES_NOHIDESEL))
|
|
dwProperties |= TXTBIT_HIDESELECTION;
|
|
|
|
if(_fEnableAutoWordSel)
|
|
dwProperties |= TXTBIT_AUTOWORDSEL;
|
|
|
|
if(!(_dwStyle & ES_AUTOHSCROLL))
|
|
dwProperties |= TXTBIT_WORDWRAP;
|
|
|
|
if(_dwStyle & ES_NOOLEDRAGDROP)
|
|
dwProperties |= TXTBIT_DISABLEDRAG;
|
|
|
|
*pdwBits = dwProperties & dwMask;
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxNotify (iNotify, pv)
|
|
*
|
|
* @mfunc
|
|
* Notify Text Host of various events. Note that there are
|
|
* two basic categories of events, "direct" events and
|
|
* "delayed" events. Direct events are sent immediately as
|
|
* they need some processing: EN_PROTECTED is a canonical
|
|
* example. Delayed events are sent after all processing
|
|
* has occurred; the control is thus in a "stable" state.
|
|
* EN_CHANGE, EN_ERRSPACE, EN_SELCHANGED are examples
|
|
* of delayed notifications.
|
|
*
|
|
*
|
|
* @rdesc
|
|
* S_OK - call succeeded <nl>
|
|
* S_FALSE -- success, but do some different action
|
|
* depending on the event type (see below).
|
|
*
|
|
* @comm
|
|
* The notification events are the same as the notification
|
|
* messages sent to the parent window of a richedit window.
|
|
* The firing of events may be controlled with a mask set via
|
|
* the EM_SETEVENTMASK message.
|
|
*
|
|
* In general, is legal to make any calls to text services while
|
|
* processing this method; however, implementors are cautioned
|
|
* to avoid excessive recursion.
|
|
*
|
|
* Here is the complete list of notifications that may be
|
|
* sent and a brief description of each:
|
|
*
|
|
* <p EN_CHANGE> Sent when some data in the edit control
|
|
* changes (such as text or formatting). Controlled by the
|
|
* ENM_CHANGE event mask. This notification is sent _after_
|
|
* any screen updates have been requested.
|
|
*
|
|
* <p EN_CORRECTTEXT> PenWin only; currently unused.
|
|
*
|
|
* <p EN_DROPFILES> If the client registered the edit
|
|
* control via DragAcceptFiles, this event will be sent when
|
|
* a WM_DROPFILES or DragEnter(CF_HDROP) message is received.
|
|
* If S_FALSE is returned, the drop will be ignored, otherwise,
|
|
* the drop will be processed. The ENM_DROPFILES mask
|
|
* controls this event notification.
|
|
*
|
|
* <p EN_ERRSPACE> Sent when the edit control cannot
|
|
* allocate enough memory. No additional data is sent and
|
|
* there is no mask for this event.
|
|
*
|
|
* <p EN_HSCROLL> Sent when the user clicks on an edit
|
|
* control's horizontal scroll bar, but before the screen
|
|
* is updated. No additional data is sent. The ENM_SCROLL
|
|
* mask controls this event.
|
|
*
|
|
* <p EN_IMECHANGE> unused
|
|
*
|
|
* <p EN_KILLFOCUS> Sent when the edit control looses focus.
|
|
* No additional data is sent and there is no mask.
|
|
*
|
|
* <p EN_MAXTEXT> Sent when the current text insertion
|
|
* has exceeded the specified number of characters for the
|
|
* edit control. The text insertion has been truncated.
|
|
* There is no mask for this event.
|
|
*
|
|
* <p EN_MSGFILTER> NB!!! THIS MESSAGE IS NOT SENT TO
|
|
* TxNotify, but is included here for completeness. With
|
|
* ITextServices::TxSendMessage, client have complete
|
|
* flexibility in filtering all window messages.
|
|
*
|
|
* Sent on a keyboard or mouse event
|
|
* in the control. A MSGFILTER data structure is sent,
|
|
* containing the msg, wparam and lparam. If S_FALSE is
|
|
* returned from this notification, the msg is processed by
|
|
* TextServices, otherwise, the message is ignored. Note
|
|
* that in this case, the callee has the opportunity to modify
|
|
* the msg, wparam, and lparam before TextServices continues
|
|
* processing. The ENM_KEYEVENTS and ENM_MOUSEEVENTS masks
|
|
* control this event for the respective event types.
|
|
*
|
|
* <p EN_OLEOPFAILED> Sent when an OLE call fails. The
|
|
* ENOLEOPFAILED struct is passed with the index of the object
|
|
* and the error code. Mask value is nothing.
|
|
*
|
|
* <p EN_PROTECTED> Sent when the user is taking an
|
|
* action that would change a protected range of text. An
|
|
* ENPROTECTED data structure is sent, indicating the range
|
|
* of text affected and the window message (if any) affecting
|
|
* the change. If S_FALSE is returned, the edit will fail.
|
|
* The ENM_PROTECTED mask controls this event.
|
|
*
|
|
* <p EN_REQUESTRESIZE> Sent when a control's contents are
|
|
* either smaller or larger than the control's window size.
|
|
* The client is responsible for resizing the control. A
|
|
* REQRESIZE structure is sent, indicating the new size of
|
|
* the control. NB! Only the size is indicated in this
|
|
* structure; it is the host's responsibility to do any
|
|
* translation necessary to generate a new client rectangle.
|
|
* The ENM_REQUESTRESIZE mask controls this event.
|
|
*
|
|
* <p EN_SAVECLIPBOARD> Sent when an edit control is being
|
|
* destroyed, the callee should indicate whether or not
|
|
* OleFlushClipboard should be called. Data indicating the
|
|
* number of characters and objects to be flushed is sent
|
|
* in the ENSAVECLIPBOARD data structure.
|
|
* Mask value is nothing.
|
|
*
|
|
* <p EN_SELCHANGE> Sent when the current selection has
|
|
* changed. A SELCHANGE data structure is also sent, which
|
|
* indicates the new selection range at the type of data
|
|
* the selection is currently over. Controlled via the
|
|
* ENM_SELCHANGE mask.
|
|
*
|
|
* <p EN_SETFOCUS> Sent when the edit control receives the
|
|
* keyboard focus. No extra data is sent; there is no mask.
|
|
*
|
|
* <p EN_STOPNOUNDO> Sent when an action occurs for which
|
|
* the control cannot allocate enough memory to maintain the
|
|
* undo state. If S_FALSE is returned, the action will be
|
|
* stopped; otherwise, the action will continue.
|
|
*
|
|
* <p EN_UPDATE> Sent before an edit control requests a
|
|
* redraw of altered data or text. No additional data is
|
|
* sent. This event is controlled via the ENM_UPDATE mask.
|
|
*
|
|
* <p EN_VSCROLL> Sent when the user clicks an edit control's
|
|
* vertical scrollbar bar before the screen is updated.
|
|
* Controlled via the ENM_SCROLL mask; no extra data is sent.
|
|
*
|
|
* <p EN_LINK> Sent when a mouse event (or WM_SETCURSOR) happens
|
|
* over a range of text that has the EN_LINK mask bit set.
|
|
* An ENLINK data structure will be sent with relevant info.
|
|
*/
|
|
HRESULT CTxtWinHost::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, "CTxtWinHost::TxNotify");
|
|
|
|
HRESULT hr = NOERROR;
|
|
LONG nId;
|
|
NMHDR * phdr;
|
|
REQRESIZE * preq;
|
|
RECT rcOld;
|
|
|
|
// We assume here that TextServices has already masked out notifications,
|
|
// so if we get one here, it should be sent
|
|
if(_hwndParent)
|
|
{
|
|
nId = GetWindowLong(_hwnd, GWL_ID);
|
|
// First, handle WM_NOTIFY style notifications
|
|
switch(iNotify)
|
|
{
|
|
case EN_REQUESTRESIZE:
|
|
// Need to map new size into correct rectangle
|
|
Assert(pv);
|
|
GetWindowRect(_hwnd, &rcOld);
|
|
MapWindowPoints(HWND_DESKTOP, _hwndParent, (POINT *) &rcOld, 2);
|
|
|
|
preq = (REQRESIZE *)pv;
|
|
preq->rc.top = rcOld.top;
|
|
preq->rc.left = rcOld.left;
|
|
preq->rc.right += rcOld.left;
|
|
preq->rc.bottom += rcOld.top;
|
|
|
|
// FALL-THROUGH!!
|
|
|
|
case EN_DROPFILES:
|
|
case EN_MSGFILTER:
|
|
case EN_OLEOPFAILED:
|
|
case EN_PROTECTED:
|
|
case EN_SAVECLIPBOARD:
|
|
case EN_SELCHANGE:
|
|
case EN_STOPNOUNDO:
|
|
case EN_LINK:
|
|
case EN_OBJECTPOSITIONS:
|
|
case EN_DRAGDROPDONE:
|
|
|
|
if(pv) // Fill out NMHDR portion of pv
|
|
{
|
|
phdr = (NMHDR *)pv;
|
|
phdr->hwndFrom = _hwnd;
|
|
phdr->idFrom = nId;
|
|
phdr->code = iNotify;
|
|
}
|
|
|
|
if(SendMessage(_hwndParent, WM_NOTIFY, (WPARAM) nId, (LPARAM) pv))
|
|
hr = S_FALSE;
|
|
break;
|
|
|
|
default:
|
|
SendMessage(_hwndParent, WM_COMMAND,
|
|
GET_WM_COMMAND_MPS(nId, _hwnd, iNotify));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetExtent (lpExtent)
|
|
*
|
|
* @mfunc
|
|
* Return native size of the control in HIMETRIC
|
|
*
|
|
* @rdesc
|
|
* S_OK <nl>
|
|
* some failure code <nl>
|
|
*
|
|
* @comm
|
|
* This method is used by Text Services to implement zooming.
|
|
* Text Services would derive the zoom factor from the ratio between
|
|
* the himetric and device pixel extent of the client rectangle.
|
|
*
|
|
* [vertical zoom factor] = [pixel height of the client rect] * 2540
|
|
* / [himetric vertical extent] * [pixel per vertical inch (from DC)]
|
|
*
|
|
* If the vertical and horizontal zoom factors are not the same, Text
|
|
* Services could ignore the horizontal zoom factor and assume it is
|
|
* the same as the vertical one.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetExtent(
|
|
LPSIZEL lpExtent) //@parm Extent in himetric
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::TxGetExtent");
|
|
|
|
AssertSz(lpExtent, "CTxtWinHost::TxGetExtent Invalid lpExtent");
|
|
|
|
// We could implement the TxGetExtent in the following way. However, the
|
|
// call to this in ITextServices is implemented in such a way that it
|
|
// does something sensible in the face of an error in this call. That
|
|
// something sensible is that it sets the extent equal to the current
|
|
// client rectangle which is what the following does in a rather convoluted
|
|
// manner. Therefore, we dump the following and just return an error.
|
|
|
|
|
|
#if 0
|
|
// The window's host extent is always the same as the client
|
|
// rectangle.
|
|
RECT rc;
|
|
HRESULT hr = TxGetClientRect(&rc);
|
|
|
|
// Get our client rectangle
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
// Calculate the length & convert to himetric
|
|
lpExtent->cx = DXtoHimetricX(rc.right - rc.left, W32->GetXPerInchScreenDC());
|
|
lpExtent->cy = DYtoHimetricY(rc.bottom - rc.top, W32->GetYPerInchScreenDC());
|
|
}
|
|
|
|
return hr;
|
|
#endif // 0
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetSelectionBarWidth (lSelBarWidth)
|
|
*
|
|
* @mfunc
|
|
* Returns size of selection bar in HIMETRIC
|
|
*
|
|
* @rdesc
|
|
* S_OK <nl>
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetSelectionBarWidth (
|
|
LONG *lSelBarWidth) //@parm Where to return selection bar width
|
|
// in HIMETRIC
|
|
{
|
|
*lSelBarWidth = (_dwStyle & ES_SELECTIONBAR) ? W32->GetDxSelBar() : 0;
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// ITextHost2 methods
|
|
//
|
|
|
|
/*
|
|
* CTxtWinHost::TxIsDoubleClickPending
|
|
*
|
|
* @mfunc Look into the message queue for this hwnd and see if a
|
|
* double click is pending. This enables TextServices to
|
|
* effeciently transition between two inplace active objects.
|
|
*
|
|
* @rdesc BOOL
|
|
*/
|
|
BOOL CTxtWinHost::TxIsDoubleClickPending()
|
|
{
|
|
MSG msg;
|
|
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN,
|
|
"CTxtWinHost::TxIsDoubleClickPending");
|
|
|
|
if(PeekMessage(&msg, _hwnd, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK,
|
|
PM_NOREMOVE | PM_NOYIELD))
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetWindow
|
|
*
|
|
* @mfunc Fetches the window associated with this control (or
|
|
* set of controls potentially). Useful for answering
|
|
* IOleWindow::GetWindow.
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetWindow(HWND *phwnd)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CTxtWinHost::GetWindow");
|
|
|
|
*phwnd = _hwnd;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/*
|
|
* CTxtWinHost::SetForegroundWindow
|
|
*
|
|
* @mfunc Sets window to foreground window & gives the focus
|
|
*
|
|
* @rdesc NOERROR - succeeded
|
|
* E_FAIL - failed.
|
|
*/
|
|
HRESULT CTxtWinHost::TxSetForegroundWindow()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN,
|
|
"CTxtWinHost::SetForegroundWindow");
|
|
|
|
if(!SetForegroundWindow(_hwnd))
|
|
SetFocus(_hwnd);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetPalette
|
|
*
|
|
* @mfunc Returns application specific palette if there is one
|
|
*
|
|
* @rdesc ~NULL - there was one
|
|
* NULL - use default palette
|
|
*/
|
|
HPALETTE CTxtWinHost::TxGetPalette()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN,
|
|
"CTxtWinHost::TxGetPalette");
|
|
|
|
return _hpal;
|
|
}
|
|
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetFEFlags
|
|
*
|
|
* @mfunc return FE settings
|
|
*
|
|
* @rdesc NOERROR - succeeded
|
|
* E_FAIL - failed.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetFEFlags(LONG *pFEFlags)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN,
|
|
"CTxtWinHost::TxGetFEFlags");
|
|
|
|
if (!pFEFlags)
|
|
return E_INVALIDARG;
|
|
*pFEFlags = 0;
|
|
|
|
if (_usIMEMode == ES_NOIME)
|
|
*pFEFlags |= ES_NOIME;
|
|
if (_usIMEMode == ES_SELFIME)
|
|
*pFEFlags |= ES_SELFIME;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
// Helper function in edit.cpp
|
|
LONG GetECDefaultHeightAndWidth(
|
|
ITextServices *pts,
|
|
HDC hdc,
|
|
LONG lZoomNumerator,
|
|
LONG lZoomDenominator,
|
|
LONG yPixelsPerInch,
|
|
LONG *pxAveWidth,
|
|
LONG *pxOverhang,
|
|
LONG *pxUnderhang);
|
|
|
|
|
|
/*
|
|
* CTxtWinHost::OnSetMargins
|
|
*
|
|
* @mfunc Handle EM_SETMARGINS message
|
|
*
|
|
* @rdesc None.
|
|
*/
|
|
void CTxtWinHost::OnSetMargins(
|
|
DWORD fwMargin, //@parm Type of requested operation
|
|
DWORD xLeft, //@parm Where to put left margin
|
|
DWORD xRight) //@parm Where to put right margin
|
|
{
|
|
LONG xLeftMargin = -1;
|
|
LONG xRightMargin = -1;
|
|
HDC hdc;
|
|
|
|
if(EC_USEFONTINFO == fwMargin)
|
|
{
|
|
// Get the DC since it is needed for the call
|
|
hdc = GetDC(_hwnd);
|
|
|
|
// Multiline behaves differently than single line
|
|
if (_dwStyle & ES_MULTILINE)
|
|
{
|
|
// Multiline - over/underhange controls margin
|
|
GetECDefaultHeightAndWidth(_pserv, hdc, 1, 1,
|
|
W32->GetYPerInchScreenDC(), NULL,
|
|
&xLeftMargin, &xRightMargin);
|
|
}
|
|
else
|
|
{
|
|
// Single line edit controls set the margins to
|
|
// the average character width on both left and
|
|
// right.
|
|
GetECDefaultHeightAndWidth(_pserv, hdc, 1, 1,
|
|
W32->GetYPerInchScreenDC(), &xLeftMargin, NULL, NULL);
|
|
|
|
xRightMargin = xLeftMargin;
|
|
}
|
|
ReleaseDC(_hwnd, hdc);
|
|
}
|
|
else
|
|
{
|
|
// The request is for setting exact pixels.
|
|
if(EC_LEFTMARGIN & fwMargin)
|
|
xLeftMargin = xLeft;
|
|
|
|
if(EC_RIGHTMARGIN & fwMargin)
|
|
xRightMargin = xRight;
|
|
}
|
|
|
|
// Set left margin if so requested
|
|
if (xLeftMargin != -1)
|
|
_rcViewInset.left = W32->DXtoHimetricX(xLeftMargin, W32->GetXPerInchScreenDC());
|
|
|
|
// Set right margin if so requested
|
|
if (xRightMargin != -1)
|
|
_rcViewInset.right = W32->DXtoHimetricX(xRightMargin, W32->GetXPerInchScreenDC());
|
|
|
|
if (xLeftMargin != -1 || xRightMargin != -1)
|
|
_pserv->OnTxPropertyBitsChange(TXTBIT_VIEWINSETCHANGE, TXTBIT_VIEWINSETCHANGE);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::SetScrollInfo
|
|
*
|
|
* @mfunc Set scrolling information for the scroll bar.
|
|
*/
|
|
void CTxtWinHost::SetScrollInfo(
|
|
INT fnBar, //@parm Specifies scroll bar to be updated
|
|
BOOL fRedraw) //@parm whether redraw is necessary
|
|
{
|
|
// Set up the basic structure for the call
|
|
SCROLLINFO si;
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_ALL;
|
|
|
|
AssertSz(_pserv != NULL,
|
|
"CTxtWinHost::SetScrollInfo called with NULL _pserv");
|
|
|
|
// Call back to the control to get the parameters
|
|
if(fnBar == SB_HORZ)
|
|
{
|
|
_pserv->TxGetHScroll((LONG *) &si.nMin, (LONG *) &si.nMax,
|
|
(LONG *) &si.nPos, (LONG *) &si.nPage, NULL);
|
|
}
|
|
else
|
|
{
|
|
_pserv->TxGetVScroll((LONG *) &si.nMin,
|
|
(LONG *) &si.nMax, (LONG *) &si.nPos, (LONG *) &si.nPage, NULL);
|
|
}
|
|
|
|
// Do the call
|
|
::SetScrollInfo(_hwnd, fnBar, &si, fRedraw);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::SetScrollBarsForWmEnable
|
|
*
|
|
* @mfunc Enable/Disable scroll bars
|
|
*
|
|
* @rdesc None.
|
|
*/
|
|
void CTxtWinHost::SetScrollBarsForWmEnable(
|
|
BOOL fEnable) //@parm Whether scrollbars s/b enabled or disabled.
|
|
{
|
|
if(!_pserv) // If no edit object,
|
|
return; // no scrollbars
|
|
|
|
BOOL fHoriz = FALSE;
|
|
BOOL fVert = FALSE;
|
|
UINT wArrows = fEnable ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH;
|
|
|
|
_pserv->TxGetHScroll(NULL, NULL, NULL, NULL, &fHoriz);
|
|
_pserv->TxGetVScroll(NULL, NULL, NULL, NULL, &fVert);
|
|
|
|
if(fHoriz) // There is a horizontal scroll bar
|
|
W32->EnableScrollBar(_hwnd, SB_HORZ, wArrows);
|
|
|
|
if(fVert) // There is a vertical scroll bar
|
|
W32->EnableScrollBar(_hwnd, SB_VERT, wArrows);
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::SetScrollBarsForWmEnable
|
|
*
|
|
* @mfunc Notification that Text Services is released.
|
|
*
|
|
* @rdesc None.
|
|
*/
|
|
void CTxtWinHost::TxFreeTextServicesNotification()
|
|
{
|
|
_fTextServiceFree = TRUE;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetEditStyle
|
|
*
|
|
* @mfunc Get Edit Style flags
|
|
*
|
|
* @rdesc NOERROR is data available.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetEditStyle(
|
|
DWORD dwItem,
|
|
DWORD *pdwData)
|
|
{
|
|
if (!pdwData)
|
|
return E_INVALIDARG;
|
|
|
|
*pdwData = 0;
|
|
|
|
if (dwItem & TXES_ISDIALOG && _fInDialogBox)
|
|
*pdwData |= TXES_ISDIALOG;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* CTxtWinHost::TxGetWindowStyles
|
|
*
|
|
* @mfunc Return window style bits
|
|
*
|
|
* @rdesc NOERROR is data available.
|
|
*/
|
|
HRESULT CTxtWinHost::TxGetWindowStyles(DWORD *pdwStyle, DWORD *pdwExStyle)
|
|
{
|
|
if (!pdwStyle || !pdwExStyle)
|
|
return E_INVALIDARG;
|
|
|
|
*pdwStyle = _dwStyle;
|
|
*pdwExStyle = _dwExStyle;
|
|
|
|
return NOERROR;
|
|
}
|
|
|