You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3885 lines
110 KiB
3885 lines
110 KiB
/*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @module TEXTSERV.CPP -- Text Services Implementation |
|
|
*
|
|
* Original Author: <nl>
|
|
* Rick Sailor
|
|
*
|
|
* History: <nl>
|
|
* 8/1/95 ricksa Created and documented
|
|
* 10/95 murrays Further doc and simplifications
|
|
*
|
|
* Documentation is generated straight from the code. The following
|
|
* date/time stamp indicates the version of code from which the
|
|
* the documentation was generated.
|
|
*
|
|
* $Header: /richedit/src/textserv.cpp 53 11/15/95 2:39p Ricksa $
|
|
*
|
|
* Copyright (c) 1995-2001, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "_common.h"
|
|
#include "_edit.h"
|
|
#include "_dispprt.h"
|
|
#include "_dispml.h"
|
|
#include "_dispsl.h"
|
|
#include "_select.h"
|
|
#include "_text.h"
|
|
#include "_runptr.h"
|
|
#include "_font.h"
|
|
#include "_measure.h"
|
|
#include "_render.h"
|
|
#include "_m_undo.h"
|
|
#include "_antievt.h"
|
|
#include "_rtext.h"
|
|
#include "_urlsup.h"
|
|
#include "_magelln.h"
|
|
#ifndef NOLINESERVICES
|
|
#include "_ols.h"
|
|
#endif
|
|
#include "_clasfyc.h"
|
|
|
|
#include "_tomfmt.h"
|
|
|
|
#ifndef OBJID_NATIVEOM
|
|
#define OBJID_NATIVEOM 0xFFFFFFF0
|
|
#endif
|
|
|
|
#ifndef NOPRIVATEMESSAGE
|
|
#include "_MSREMSG.H"
|
|
#include "_dxfrobj.h"
|
|
#endif
|
|
|
|
#ifndef NOACCESSIBILITY
|
|
#include "oleacc.h"
|
|
#endif
|
|
|
|
// By turning on the PROFILE_TS compiler directive, you tell IceCap2.0
|
|
// to turn on profiling for only ITextServices API's. Typically only
|
|
// used during profiling work.
|
|
//#define PROFILE_TS
|
|
#ifdef PROFILE_TS
|
|
#include <icapexp.h>
|
|
|
|
class CapProfile
|
|
{
|
|
public:
|
|
CapProfile() { StartCAP(); }
|
|
~CapProfile() { StopCAP(); }
|
|
};
|
|
|
|
#define START_PROFILING CapProfile capprf;
|
|
#else
|
|
#define START_PROFILING
|
|
#endif //PROFILE_TS
|
|
|
|
ASSERTDATA
|
|
|
|
// Macros to get mouse coordinates out of a message
|
|
// need to cast to SHORT first for sign extension
|
|
#define MOUSEX ((INT)(SHORT)LOWORD(lparam))
|
|
#define MOUSEY ((INT)(SHORT)HIWORD(lparam))
|
|
|
|
LONG ValidateTextRange(TEXTRANGE *pstrg);
|
|
|
|
BOOL g_OLSBusy = 0;
|
|
|
|
|
|
// Helper function in edit.cpp
|
|
LONG GetECDefaultHeightAndWidth(
|
|
ITextServices *pts,
|
|
HDC hdc,
|
|
LONG lZoomNumerator,
|
|
LONG lZoomDenominator,
|
|
LONG yPixelsPerInch,
|
|
LONG *pxAveWidth,
|
|
LONG *pxOverhang,
|
|
LONG *pxUnderhang);
|
|
|
|
// if there's an active object being dragged around, on WM_PAINT we always
|
|
// try to reposition it to there it should be. A not-so-well-behaved object
|
|
// my generate another WM_PAINT message in response to that, even if it actually
|
|
// did not move. So we limit our number of attempts to reposition it and reset
|
|
// this counter every time a mouse moves.
|
|
// The corresponding field is declared as :2, so don't try to bump it up
|
|
// without allocating more bits!!
|
|
#define MAX_ACTIVE_OBJ_POS_TRIES (3)
|
|
|
|
// Interchange horizontal and vertical commands
|
|
WORD InterchangeScrollCode(WORD wCode)
|
|
{
|
|
switch(wCode)
|
|
{
|
|
case SB_BOTTOM:
|
|
return SB_TOP;
|
|
|
|
case SB_LINEDOWN:
|
|
return SB_LINEUP;
|
|
|
|
case SB_LINEUP:
|
|
return SB_LINEDOWN;
|
|
|
|
case SB_PAGEDOWN:
|
|
return SB_PAGEUP;
|
|
|
|
case SB_PAGEUP:
|
|
return SB_PAGEDOWN;
|
|
|
|
case SB_TOP:
|
|
return SB_BOTTOM;
|
|
|
|
default:
|
|
return wCode;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////// Helper Functions ///////////////////////////////////
|
|
/*
|
|
* BOOL CTxtEdit::LoadMsgFilter(msg, wparam, lparam)
|
|
*
|
|
* @func
|
|
* Check if we should load the IME message filter
|
|
*
|
|
* @rdesc
|
|
* TRUE - Load it
|
|
* FALSE - Don't load
|
|
*/
|
|
BOOL CTxtEdit::LoadMsgFilter(
|
|
UINT msg, //@parm Message ID
|
|
WPARAM wparam, //@parm Message wparam
|
|
LPARAM lparam) //@parm Message lparam
|
|
{
|
|
//TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::LoadMsgFilter");
|
|
|
|
// For the first ever message, we want to check if
|
|
// our client has created AIMM object for current thread
|
|
#ifndef NOFEPROCESSING
|
|
if (!_fCheckAIMM)
|
|
{
|
|
DWORD dwThreadId;
|
|
DWORD dwLoadActiveInput = 0;
|
|
|
|
_fCheckAIMM = 1;
|
|
|
|
#ifndef NOPRIVATEMESSAGE
|
|
if (!_f10Mode) // Don't check if 1.0 mode
|
|
{
|
|
if (FindAtomA("_CTF_PROCESS_ATOM_")) // Is process using Cicero?
|
|
dwLoadActiveInput = SES_USECTF;
|
|
|
|
if (FindAtomA("_AIMM12_PROCESS_ATOM_")) // Is process using Aimm 1.2?
|
|
dwLoadActiveInput = SES_USEAIMM12; // Yes, this will override SES_USECTF.
|
|
else if (dwThreadId = GetCurrentThreadId())
|
|
{
|
|
char szBuf[20];
|
|
sprintf(szBuf, "AIMM:%08x", dwThreadId);
|
|
if (FindAtomA(szBuf)) // Is thread using Aimm 1.1?
|
|
dwLoadActiveInput = SES_USEAIMM11; // Yes, load Aimm 1.1
|
|
}
|
|
|
|
if (!dwLoadActiveInput) // Process is not using anything...
|
|
{
|
|
if (W32->fUseCTF()) // Ini file say use Cicero
|
|
dwLoadActiveInput = SES_USECTF;
|
|
else if (W32->fUseAimm()) // Ini file say use Aimm 1.2
|
|
dwLoadActiveInput = SES_USEAIMM12;
|
|
}
|
|
|
|
if (dwLoadActiveInput)
|
|
{
|
|
HWND hWnd = NULL;
|
|
|
|
TxGetWindow( &hWnd );
|
|
|
|
if (hWnd)
|
|
{
|
|
if (_fInOurHost)
|
|
PostMessage(hWnd, EM_SETUIM, dwLoadActiveInput, dwLoadActiveInput);
|
|
else
|
|
{
|
|
LRESULT lResult;
|
|
TxSendMessage(EM_SETUIM, dwLoadActiveInput, dwLoadActiveInput, &lResult);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_KEYDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
if ( (WORD) wparam == VK_PROCESSKEY )
|
|
return TRUE;
|
|
break;
|
|
|
|
case WM_INPUTLANGCHANGE:
|
|
if (IsFELCID((WORD)(lparam)))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_SETEDITSTYLE:
|
|
if ((lparam & (SES_USEAIMM | SES_NOIME | SES_USECTF | SES_CTFALLOWEMBED | SES_CTFALLOWSMARTTAG | SES_CTFALLOWPROOFING)) ||
|
|
(wparam & (SES_USEAIMM | SES_NOIME | SES_USECTF | SES_CTFALLOWEMBED | SES_CTFALLOWSMARTTAG | SES_CTFALLOWPROOFING)))
|
|
return TRUE;
|
|
break;
|
|
|
|
case EM_RECONVERSION:
|
|
case WM_IME_NOTIFY:
|
|
case WM_IME_REQUEST:
|
|
case WM_IME_STARTCOMPOSITION:
|
|
case EM_GETIMEOPTIONS:
|
|
case EM_SETIMEOPTIONS:
|
|
case EM_SETIMECOLOR:
|
|
case EM_GETIMECOLOR:
|
|
case WM_IME_CHAR:
|
|
#ifndef NOPRIVATEMESSAGE
|
|
case EM_SETUIM:
|
|
#endif
|
|
case EM_SETIMEMODEBIAS:
|
|
case EM_GETIMEMODEBIAS:
|
|
case EM_GETCTFMODEBIAS:
|
|
case EM_SETCTFMODEBIAS:
|
|
case EM_GETCTFOPENSTATUS:
|
|
case EM_SETCTFOPENSTATUS:
|
|
case EM_GETIMECOMPTEXT:
|
|
case EM_ISIME:
|
|
case EM_GETIMEPROPERTY:
|
|
return TRUE;
|
|
|
|
case EM_SETLANGOPTIONS:
|
|
if (lparam & (IMF_IMEALWAYSSENDNOTIFY | IMF_IMECANCELCOMPLETE))
|
|
return TRUE;
|
|
break;
|
|
|
|
default:
|
|
if (msg)
|
|
{
|
|
if (msg == MSIMEReconvertMsg || msg == MSIMEDocFeedMsg
|
|
|| msg == MSIMEQueryPositionMsg)
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::FormatAndPrint (hdcDraw, hicTargetDev, ptd, lprcBounds,
|
|
* lprcWBounds)
|
|
* @mfunc
|
|
* Format and Print data in control
|
|
*
|
|
* @rdesc
|
|
* S_OK - everything worked
|
|
* E_FAIL - unexpected failure occurred
|
|
*/
|
|
HRESULT CTxtEdit::FormatAndPrint(
|
|
HDC hdcDraw, //@parm HDC to draw on
|
|
HDC hicTargetDev, //@parm Input information context if any
|
|
DVTARGETDEVICE *ptd, //@parm Device target information
|
|
RECT *lprcBounds, //@parm Rectangle to measure
|
|
RECT *lprcWBounds) //@parm Metafile information
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::FormatAndPrint");
|
|
|
|
// Put client rectangle in format structure
|
|
FORMATRANGE fr;
|
|
fr.rc = *lprcBounds;
|
|
|
|
// Get number of device units per inch
|
|
LONG xPerInch;
|
|
LONG yPerInch;
|
|
|
|
if (NULL == lprcWBounds)
|
|
{
|
|
xPerInch = GetDeviceCaps(hdcDraw, LOGPIXELSX);
|
|
yPerInch = GetDeviceCaps(hdcDraw, LOGPIXELSY);
|
|
}
|
|
else
|
|
{
|
|
//Forms ^3 draws using screen resolution, while OLE specifies HIMETRIC
|
|
xPerInch = fInOurHost() ? 2540 : W32->GetXPerInchScreenDC();
|
|
yPerInch = fInOurHost() ? 2540 : W32->GetYPerInchScreenDC();
|
|
|
|
SetWindowOrgEx(hdcDraw, lprcWBounds->left, lprcWBounds->top, NULL);
|
|
SetWindowExtEx(hdcDraw, lprcWBounds->right, lprcWBounds->bottom, NULL);
|
|
}
|
|
|
|
|
|
// Convert rectangle into TWIPS
|
|
fr.rc.left = MulDiv(fr.rc.left, LX_PER_INCH, xPerInch);
|
|
fr.rc.top = MulDiv(fr.rc.top, LY_PER_INCH, yPerInch);
|
|
fr.rc.right = MulDiv(fr.rc.right, LX_PER_INCH, xPerInch);
|
|
fr.rc.bottom = MulDiv(fr.rc.bottom, LY_PER_INCH, yPerInch);
|
|
|
|
// Use message based printing code to do our printing for us
|
|
fr.hdc = hdcDraw;
|
|
fr.hdcTarget = hicTargetDev;
|
|
fr.rcPage = fr.rc;
|
|
fr.chrg.cpMin = _pdp->GetFirstVisibleCp();
|
|
fr.chrg.cpMost = -1;
|
|
|
|
// Assume this is all going to work
|
|
HRESULT hr = S_OK;
|
|
|
|
SPrintControl prtcon;
|
|
prtcon._fDoPrint = TRUE;
|
|
prtcon._fPrintFromDraw = TRUE;
|
|
|
|
// Print control
|
|
if(OnFormatRange(&fr, prtcon, TRUE) == -1)
|
|
{
|
|
// For some reason the control could not be printed
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::SetText (pstr, flags, CodePage, publdr, plres)
|
|
*
|
|
* @mfunc Sets the text in the document, clearing out any existing text
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
HRESULT CTxtEdit::SetText(
|
|
LPCWSTR pstr, //@parm Text to set
|
|
DWORD flags, //@parm 0 or more ST_xxx's
|
|
LONG CodePage, //@parm CodePage
|
|
IUndoBuilder *publdr, //@parm Optional place to put undo events
|
|
LRESULT * plres) //@parm Optional place to return cch added
|
|
{
|
|
CCallMgr callmgr(this);
|
|
BOOL fSel = flags & ST_SELECTION;
|
|
BOOL fSetTextMax = TRUE;
|
|
CTxtRange rg(this, 0, -GetTextLength()); // Select whole story
|
|
CTxtRange * prg = &rg;
|
|
LRESULT lres = 0;
|
|
CFreezeDisplay fd(_pdp);
|
|
CCharFormat CF;
|
|
BOOL fSetCF = FALSE;
|
|
BOOL fInputString = FALSE; // Initialize to no input string
|
|
|
|
// NOTE: WM_SETTEXT is the only message using flags == ST_CHECKPROTECTION.
|
|
// This is sent in via ANSIWndProc(). Do we need another flag to indicate
|
|
// WM_SETTEXT or is this check good enough? This only affect 1.0 mode.
|
|
BOOL f10WM_SETTEXT = flags & ST_10WM_SETTEXT;
|
|
|
|
if(plres)
|
|
*plres = 0;
|
|
|
|
if(fSel) // Set selected text
|
|
{
|
|
if(!_psel)
|
|
{
|
|
Beep();
|
|
return E_FAIL;
|
|
}
|
|
// Bug fix: 6498 - we need to know if scroll position is at bottom
|
|
// before inserting text
|
|
if (Get10Mode())
|
|
{
|
|
LONG nMin, nMax, nPos, nPage;
|
|
BOOL nEnable;
|
|
TxGetVScroll(&nMin, &nMax, &nPos, &nPage, &nEnable);
|
|
if (nEnable)
|
|
_psel->SetAutoVScroll((nMax - nPage - 3) <= nPos);
|
|
}
|
|
prg = _psel;
|
|
}
|
|
else
|
|
{
|
|
_qwCharFlags &= FRTL | FDIGITSHAPE; // No chars, so kill char flags
|
|
if (!IsRich())
|
|
{
|
|
// Deleting all text from Plain text, we want to
|
|
// go back to the -1 format
|
|
prg->Set_iCF(-1);
|
|
prg->SetUseiFormat(TRUE);
|
|
}
|
|
else
|
|
_qwCharFlags |= FBELOWX40; // For final EOP
|
|
}
|
|
|
|
if (flags & ST_CHECKPROTECTION &&
|
|
IsProtectedRange(WM_SETTEXT, 0, (LPARAM)pstr, prg))
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
// 1.0 COMPATABILITY
|
|
// 1.0 didn't scroll to selection if text is inserted via EM_REPLACESEL
|
|
// and fHideSelection is FALSE;
|
|
BOOL fUpdateCaret = !(Get10Mode() && (flags & ST_10REPLACESEL) &&
|
|
fHideSelection() && !_psel->GetAutoVScroll());
|
|
|
|
if(!(flags & ST_KEEPUNDO))
|
|
{
|
|
if(IsRich() && !fSel)
|
|
{
|
|
if (f10WM_SETTEXT)
|
|
{
|
|
// If pstr is empty string, retain format at end of current text.
|
|
// If pstr is not empty string, retain format at cp = 1.
|
|
// Note: prg->_rpCF is already at SetRun(0,0)
|
|
CFormatRunPtr rp(prg->_rpCF);
|
|
|
|
if (!pstr || *(LPBYTE)pstr == '\0')
|
|
{
|
|
LONG cchAdjusted = GetAdjustedTextLength() - 1;
|
|
|
|
if (cchAdjusted > 0)
|
|
rp.Move(cchAdjusted);
|
|
}
|
|
|
|
CF = *(GetCharFormat(rp.GetFormat()));
|
|
fSetCF = TRUE;
|
|
|
|
prg->SetText(NULL); // delete all the text first
|
|
}
|
|
|
|
// SetText causing all formatting to return to the default. We use
|
|
// the notification system to remove the formatting. This is
|
|
// particularly important for the final EOP which cannot be deleted.
|
|
|
|
// Notify every interested party that they should dump their formatting
|
|
_nm.NotifyPreReplaceRange(NULL, CONVERT_TO_PLAIN, 0, 0, 0, 0);
|
|
|
|
// Tell document to dump its format runs
|
|
_story.DeleteFormatRuns();
|
|
|
|
if (fSetCF)
|
|
prg->SetCharFormat(&CF, 0, NULL, CFM_ALL, 0);
|
|
}
|
|
|
|
publdr = NULL;
|
|
if(_pundo)
|
|
_pundo->ClearAll();
|
|
|
|
if(_predo)
|
|
_predo->ClearAll();
|
|
|
|
// If we are re-entered, there may be anti-events higher up the
|
|
// chain. Grab the undo builder and clear things away if necessary.
|
|
CGenUndoBuilder undobldr(this, 0);
|
|
undobldr.Discard();
|
|
}
|
|
if(publdr)
|
|
publdr->StopGroupTyping();
|
|
|
|
// Need to reinit zoomin variables if entire text is being replaced
|
|
if (!fSel)
|
|
InitDocInfo();
|
|
|
|
else if(_psel->GetCch()) // If insert into selection, need to
|
|
{ // insert an EOP if selection ends
|
|
CPFRunPtr rp(*_psel); // at a table row delimiter
|
|
if(_psel->GetCch() < 0)
|
|
rp.Move(-_psel->GetCch());
|
|
if(rp.IsTableRowDelimiter())
|
|
_psel->InsertEOP(publdr, 0);
|
|
}
|
|
|
|
LONG lStreamFormat = IsRich() && IsRTF((LPSTR)pstr, 10) ? SF_RTF : 0;
|
|
|
|
if(pstr && *(LPSTR)pstr && (CodePage != 1200 || lStreamFormat || *pstr < 128 && fSel && !*(pstr+1)))
|
|
{
|
|
LONG cch = strlen((LPSTR)pstr); // REMARK: little endian dependence
|
|
DWORD ch = *(LPBYTE)pstr; // for CodePage = 1200 cases
|
|
|
|
fInputString = TRUE;
|
|
if(ch < 128 && fSel && cch == 1)
|
|
{
|
|
lres = 1;
|
|
fSetTextMax = FALSE;
|
|
TxSetMaxToMaxText(1);
|
|
if(ch == CR)
|
|
InsertEOP(0, FALSE, publdr);
|
|
else
|
|
_psel->PutChar(ch, 0, publdr);
|
|
}
|
|
else if(cch == 2 && ch == CR && *((LPSTR)pstr + 1) == LF)
|
|
{
|
|
lres = 2;
|
|
fSetTextMax = FALSE;
|
|
TxSetMaxToMaxText(2);
|
|
InsertEOP(0, FALSE, publdr);
|
|
}
|
|
else
|
|
{
|
|
READHGLOBAL rhg = {(LPSTR)pstr, cch};
|
|
EDITSTREAM es = {(DWORD_PTR)&rhg, S_OK, ReadHGlobal};
|
|
HCURSOR hcur = NULL;
|
|
|
|
// Want wait cursor to display sooner
|
|
bool fSetCursor = rhg.cbLeft > NUMPASTECHARSWAITCURSOR;
|
|
if(fSetCursor)
|
|
hcur = TxSetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
if (CodePage <= 0)
|
|
{
|
|
if (Get10Mode())
|
|
{
|
|
LONG iFormat = _psel->GetiFormat();
|
|
const CCharFormat *pCF = GetCharFormat(iFormat);
|
|
|
|
CodePage = CodePageFromCharRep(pCF->_iCharRep);
|
|
}
|
|
else
|
|
CodePage = (CodePage == 0) ? GetACP() : GetDefaultCodePage(EM_SETTEXTEX);
|
|
}
|
|
|
|
if(!lStreamFormat)
|
|
lStreamFormat = SF_TEXT;
|
|
|
|
lStreamFormat |= SF_USECODEPAGE | (CodePage << 16);
|
|
|
|
if(fSel)
|
|
lStreamFormat |= SFF_SELECTION;
|
|
|
|
lres = _ldte.LoadFromEs(prg, lStreamFormat, &es, FALSE, publdr);
|
|
if(fSetCursor)
|
|
TxSetCursor(hcur);
|
|
|
|
if(es.dwError != NOERROR)
|
|
return (HRESULT)es.dwError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 9052: Don't delete all if 4 (ST_NEWCHARS) is passed in EM_SETTEXTEX
|
|
DWORD dwFlags = (flags & 4)
|
|
? RR_ITMZ_UNICODEBIDI | RR_NEW_CHARS
|
|
: RR_ITMZ_UNICODEBIDI;
|
|
|
|
if (CodePage != 1200 && pstr && !*(LPSTR)pstr)
|
|
pstr = NULL;
|
|
|
|
if(pstr && *pstr)
|
|
fInputString = TRUE;
|
|
lres = prg->CleanseAndReplaceRange(-1, pstr, FALSE, publdr, NULL, NULL, dwFlags);
|
|
}
|
|
|
|
if(!lres && fInputString)
|
|
{
|
|
// There was an input string but for some reason there was no update.
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (_fOutlineView)
|
|
{
|
|
// Outline view must have formatting.
|
|
_psel->Check_rpPF();
|
|
}
|
|
|
|
|
|
if(_psel)
|
|
{
|
|
if(fSel)
|
|
_psel->Update(fUpdateCaret);
|
|
else
|
|
{
|
|
// Setting the text means a new document so if there is a selection
|
|
// turn it into an insertion point at the beginning of the document.
|
|
_psel->ClearPrevSel();
|
|
_psel->Set(0, 0);
|
|
|
|
// Since the text is being completely replaced and all formatting
|
|
// is being lost, let's go back to the default format for the
|
|
// selection.
|
|
if (!f10WM_SETTEXT)
|
|
_psel->Set_iCF(-1);
|
|
else if (fSetCF)
|
|
_psel->SetCharFormat(&CF, 0, NULL, CFM_ALL, 0);
|
|
|
|
if(_fFocus || _psel->IsParaRTL())
|
|
{
|
|
// Update caret to reflect new postion
|
|
_psel->UpdateCaret(fUpdateCaret);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we've replaced the entire document, the control isn't
|
|
// really "modified" anymore. This is necessary to match
|
|
// the Windows MLE behavior. However, since RichEdit 1.0
|
|
// did _not_ do this (they left fModified to be TRUE), we
|
|
// only do this for RichEdit 2.0 and later.
|
|
|
|
if(!Get10Mode() && !publdr && !fSel)
|
|
_fModified = FALSE;
|
|
|
|
_fSaved = FALSE; // ITextDocument isn't Saved
|
|
|
|
// Adjust text limit if necessary
|
|
if (fSetTextMax)
|
|
TxSetMaxToMaxText();
|
|
|
|
if(plres)
|
|
*plres = fSel ? lres : 1;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////// ITextServices Methods ////////////////////////////////
|
|
|
|
// External IME Message Filter Interface factory
|
|
#ifndef NOFEPROCESSING
|
|
void CreateIMEMessageFilter(ITextMsgFilter **ppNewFilter);
|
|
#endif
|
|
|
|
/*
|
|
* @doc EXTERNAL
|
|
*
|
|
* CTxtEdit::TxSendMessage (msg, wparam, lparam, plresult)
|
|
*
|
|
* @mfunc
|
|
* Used by window host to forward messages sent to its window to the
|
|
* text services.
|
|
*
|
|
* @rdesc
|
|
* NOERROR Message was processed, and some action taken <nl>
|
|
* S_FALSE Message was not processed. Typically indicates that caller
|
|
* should process message, maybe by calling DefWindowProc <nl>
|
|
* S_MSG_KEYIGNORED Message processed, but no action was taken <nl>
|
|
* E_OUTOFMEMORY <nl>
|
|
*
|
|
* @comm
|
|
* Note that two return values are passed back from this function.
|
|
* <p plresult> is the return value that should be passed back from a
|
|
* window proc. However, in some cases, the returned LRESULT does not
|
|
* contain enough information. For example, to implement cursoring
|
|
* around controls, it's useful to know if a keystroke (such as right
|
|
* arrow) was processed, but ignored (e.g. the caret is already at the
|
|
* rightmost position in the the text). In these cases, extra
|
|
* information may be returned via the returned HRESULT.
|
|
*
|
|
* WM_CHAR and WM_KEYDOWN should return S_MSG_KEYIGNORED when a key or
|
|
* char has been recognized but had no effect given the current state,
|
|
* e.g., a VK_RIGHT key when the insertion point is already at the end of
|
|
* the document). This is used by Forms3 to pass the key up the visual
|
|
* hierarchy, so that for example, focus moves to the next control in the
|
|
* TAB order.
|
|
*
|
|
* This includes the following cases:
|
|
*
|
|
* 1. Any key trying to move the insertion point beyond the end of the
|
|
* document; or before the begining of the document.
|
|
*
|
|
* 2. Any key trying to move the insertion point beyond the last line or
|
|
* before the first line.
|
|
*
|
|
* 3. Any insertion of character (WM_CHAR) that would move the insertion
|
|
* point past the maximum length of the control.
|
|
*/
|
|
HRESULT CTxtEdit::TxSendMessage (
|
|
UINT msg, //@parm Message id
|
|
WPARAM wparam, //@parm WPARAM from window's message
|
|
LPARAM lparam, //@parm LPARAM from window's message
|
|
LRESULT *plresult) //@parm Where to put message's return LRESULT
|
|
{
|
|
TRACEBEGINPARAM(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxSendMessage", msg);
|
|
|
|
CObjectMgr *pobjmgr;
|
|
HRESULT hr = NOERROR;
|
|
LRESULT lres = 0;
|
|
CCallMgr callmgr(this);
|
|
|
|
if ( CW32System::_MSMouseRoller == msg ) // map Magellan msg.
|
|
{
|
|
// map this message to WM_MOUSEWHEEL
|
|
// In these cases the driver doesn't set the key state properly so
|
|
// we have to do it ourselves
|
|
short zdelta = (short)(long)wparam;
|
|
short kstate = 0;
|
|
if (GetKeyboardFlag(CTRL, VK_CONTROL))
|
|
kstate |= MK_CONTROL;
|
|
if (GetKeyboardFlag(SHIFT, VK_SHIFT))
|
|
kstate |= MK_SHIFT;
|
|
|
|
wparam = MAKELONG(kstate, zdelta);
|
|
msg = WM_MOUSEWHEEL;
|
|
}
|
|
|
|
#ifndef NOFEPROCESSING
|
|
if (_pMsgFilter)
|
|
{
|
|
PassMsg:
|
|
hr = _pMsgFilter->HandleMessage(&msg, &wparam, &lparam, &lres);
|
|
if (hr == S_OK) // Message has been handled.
|
|
{
|
|
if(plresult)
|
|
*plresult = lres;
|
|
|
|
return S_OK;
|
|
}
|
|
hr = S_OK; // Reset
|
|
}
|
|
else if (LoadMsgFilter(msg, wparam, lparam))
|
|
{
|
|
HWND hwnd = NULL;
|
|
if (_fInOurHost)
|
|
{
|
|
// If not in Forms^3 we can get the window from our host.
|
|
// For Forms^3 we will use NULL for the desktop Window and pray
|
|
TxGetWindow( &hwnd );
|
|
}
|
|
ITextMsgFilter *pNewFilter = NULL;
|
|
|
|
CreateIMEMessageFilter(&pNewFilter);
|
|
|
|
if (pNewFilter)
|
|
{
|
|
pNewFilter->AttachDocument( hwnd, (ITextDocument2 *) this, (ITextServices *) this );
|
|
AttachMsgFilter(pNewFilter);
|
|
goto PassMsg;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
START_PROFILING
|
|
|
|
IUndoBuilder * publdr;
|
|
CGenUndoBuilder undobldr(this, UB_AUTOCOMMIT, &publdr);
|
|
|
|
switch(msg)
|
|
{
|
|
case EM_CANPASTE:
|
|
// we don't check for protection here, as RichEdit 1.0
|
|
// doesn't
|
|
lres = _ldte.CanPaste(NULL, (CLIPFORMAT) wparam, RECO_PASTE);
|
|
break;
|
|
|
|
case EM_CANUNDO:
|
|
if(_pundo)
|
|
lres = _pundo->CanUndo();
|
|
break;
|
|
|
|
case EM_CANREDO:
|
|
if(_predo)
|
|
lres = _predo->CanUndo();
|
|
break;
|
|
|
|
case EM_GETUNDONAME:
|
|
if(_pundo)
|
|
lres = _pundo->GetNameIDFromAE((void*)wparam);
|
|
break;
|
|
|
|
case EM_GETREDONAME:
|
|
if(_predo)
|
|
lres = _predo->GetNameIDFromAE((void*)wparam);
|
|
break;
|
|
|
|
case EM_STOPGROUPTYPING:
|
|
if(_pundo)
|
|
{
|
|
// we'll only stop group typing iff wparam
|
|
// is zero (meaning stop regardless) _or_ if
|
|
// wparam matches the merge anti event.
|
|
//
|
|
// This feature allows clients to say that only
|
|
// a specific anti-event should close out it's
|
|
// "fuzzy" state. Note that currently, only the
|
|
// merge anti-event has this fuzzy state.
|
|
|
|
if(!wparam || (IAntiEvent *)wparam == _pundo->GetMergeAntiEvent())
|
|
_pundo->StopGroupTyping();
|
|
}
|
|
break;
|
|
|
|
case WM_UNICHAR: // Unambiguous Unicode character
|
|
if(wparam == NOTACHAR)
|
|
{
|
|
lres = TRUE; // Tell caller we understand msg
|
|
break;
|
|
} // Else fall thru to WM_CHAR
|
|
|
|
case WM_CHAR:
|
|
if(GetKeyboardFlags() & (ALTNUMPAD | HOTEURO))
|
|
{
|
|
if (GetKeyboardFlags() & ALTNUMPAD)
|
|
{
|
|
if (GetKeyboardFlags() & ALT0 &&
|
|
GetCharFormat(-1)->_iCharRep == MAC_INDEX &&
|
|
GetKeyboardCharRep(0) == ANSI_INDEX)
|
|
{
|
|
WCHAR ch;
|
|
UnicodeFromMbcs(&ch, 1, (char *)&wparam, 1, 10000);
|
|
wparam = ch;
|
|
SetKeyPadNumber(ch);
|
|
ResetKeyboardFlag(ALTNUMPAD | ALT0);
|
|
}
|
|
#ifndef NOANSIWINDOWS
|
|
else
|
|
{
|
|
CW32System::WM_CHAR_INFO wmci;
|
|
wmci._fAccumulate = FALSE;
|
|
W32->AnsiFilter(msg, wparam, lparam, (void *)&wmci);
|
|
}
|
|
#endif
|
|
}
|
|
else // (GetKeyboardFlags() & HOTEURO) case
|
|
{
|
|
// We have handled the Euro, just eat this WM_CHAR
|
|
ResetKeyboardFlag(HOTEURO);
|
|
break;
|
|
}
|
|
} // Fall thru to WM_IME_CHAR
|
|
|
|
case WM_IME_CHAR: // 2 byte character, usually FE.
|
|
lres = hr = OnTxChar((DWORD)wparam, (DWORD)lparam, publdr);
|
|
ResetKeyboardFlag(HOTEURO);
|
|
break;
|
|
|
|
case WM_USER + 39: // For backward compat with NT 3.51
|
|
case EM_CHARFROMPOS:
|
|
hr = TxCharFromPos((LPPOINT)lparam, &lres);
|
|
break;
|
|
|
|
#ifdef WM_INPUTLANGCHANGE
|
|
case WM_INPUTLANGCHANGE:
|
|
if (_fSingleCodePage)
|
|
{
|
|
// See if the charset for the specified keyboard is supported by
|
|
// the single code page we support. If we don't have a _pDocInfo,
|
|
// assume the code page is the system code page. We will always
|
|
// support the ANSI charset, as all code pages contain at least
|
|
// a large portion of this charset (the ASCII block).
|
|
wparam = (!wparam || wparam == GetCharSet(_pDocInfo ?
|
|
_pDocInfo->_wCpg : GetSystemDefaultCodePage()));
|
|
}
|
|
goto update_kbd;
|
|
|
|
case WM_INPUTLANGCHANGEREQUEST:
|
|
// If the SingleCodePage option is set, then we must have a
|
|
// "good" code page to go to; if not, just eat this message.
|
|
//
|
|
// This will prevent folks from typing French and Greek
|
|
// on the same edit control, which is useful for certain
|
|
// kinds of backward compatibility scenarios.
|
|
//
|
|
// HACK ALERT! the documentation on WM_INPUTLANGCHANGEREQUEST
|
|
// is wrong. It turns out that _only_ the low bit of wparam
|
|
// indicates whether or not the new keyboard can be considered
|
|
// as the same code page.
|
|
|
|
if (_fSingleCodePage && !(wparam & 1))
|
|
{
|
|
// The lowest bit check is not reliable in some platforms e.g. Viet OSR2
|
|
// since it doesnt allow English kbd to match system charset (bug #6365).
|
|
|
|
wparam = PRIMARYLANGID(LOWORD(lparam)) == LANG_ENGLISH &&
|
|
IN_RANGE (SUBLANG_ENGLISH_US, SUBLANGID(LOWORD(lparam)), SUBLANG_ENGLISH_UK);
|
|
}
|
|
|
|
update_kbd:
|
|
if(!_fSingleCodePage || (wparam & 1))
|
|
{
|
|
WORD wKLCurrent = LOWORD(GetKeyboardLayout(0));
|
|
|
|
// Update our idea of current keyboard layout
|
|
W32->RefreshKeyboardLayout();
|
|
|
|
if(GetKeyboardFlags() & CTRL && GetKeyboardFlags() & SHIFT)
|
|
SetKeyboardFlag(LETAFTERSHIFT);
|
|
|
|
if( wKLCurrent == LOWORD(lparam) || // No change in keyboard
|
|
GetSel()->CheckChangeFont((HKL)lparam, CharRepFromLID(LOWORD(lparam))))
|
|
hr = S_FALSE; // cause default window to allow kb switch.
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_CLEAR:
|
|
OnClear(publdr);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
hr = OnContextMenu(lparam);
|
|
break;
|
|
|
|
case WM_COPY:
|
|
case WM_CUT:
|
|
lres = hr = CutOrCopySelection(msg, wparam, lparam, publdr);
|
|
break;
|
|
|
|
case WM_RENDERFORMAT:
|
|
lres = hr = _ldte.RenderClipboardFormat(wparam);
|
|
break;
|
|
|
|
case WM_RENDERALLFORMATS:
|
|
lres = hr = _ldte.RenderAllClipboardFormats();
|
|
break;
|
|
|
|
case WM_DESTROYCLIPBOARD:
|
|
lres = hr = _ldte.DestroyClipboard();
|
|
break;
|
|
|
|
case EM_DISPLAYBAND:
|
|
if (fInplaceActive())
|
|
{
|
|
{
|
|
CLock lock;
|
|
|
|
if (g_OLSBusy)
|
|
{
|
|
hr = OLE_E_INVALIDRECT;
|
|
break;
|
|
}
|
|
}
|
|
OnDisplayBand((const RECT *) lparam, FALSE);
|
|
lres = 1;
|
|
}
|
|
else
|
|
hr = OLE_E_INVALIDRECT;
|
|
break;
|
|
|
|
#ifdef WM_DROPFILES
|
|
case WM_DROPFILES:
|
|
OnDropFiles((HANDLE) wparam);
|
|
break;
|
|
#endif
|
|
|
|
case EM_EMPTYUNDOBUFFER:
|
|
ClearUndo(publdr);
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
lres = 1; // We handle background erase during painting
|
|
break;
|
|
|
|
case EM_EXGETSEL: // Has cp output parameter
|
|
OnExGetSel((CHARRANGE *)lparam);
|
|
break;
|
|
|
|
case EM_FINDTEXT: // Has cp input/output parms
|
|
case EM_FINDTEXTW: // Has cp input/output parms
|
|
case EM_FINDTEXTEX: // Has cp input/output parms
|
|
case EM_FINDTEXTEXW: // Has cp input/output parms
|
|
lres = OnFindText(msg, (DWORD)wparam, (FINDTEXTEX *)lparam);
|
|
break;
|
|
|
|
case EM_FINDWORDBREAK: // Has cp input/output parms
|
|
hr = TxFindWordBreak((INT)wparam, (LONG)lparam, &lres);
|
|
break;
|
|
|
|
case EM_FORMATRANGE: // Has cp input/output parms
|
|
if(fInplaceActive())
|
|
{
|
|
{
|
|
CLock lock;
|
|
|
|
if (g_OLSBusy)
|
|
{
|
|
hr = OLE_E_INVALIDRECT;
|
|
break;
|
|
}
|
|
}
|
|
SPrintControl prtcon;
|
|
prtcon._fDoPrint = (wparam) ? TRUE : FALSE;
|
|
lres = OnFormatRange((FORMATRANGE *) lparam, prtcon);
|
|
|
|
}
|
|
else
|
|
hr = OLE_E_INVALIDRECT;
|
|
break;
|
|
|
|
case EM_GETTYPOGRAPHYOPTIONS:
|
|
lres = _bTypography;
|
|
break;
|
|
|
|
case EM_GETBIDIOPTIONS:
|
|
if((Get10Mode() || !wparam) && lparam && ((BIDIOPTIONS *)lparam)->cbSize == sizeof(BIDIOPTIONS))
|
|
{
|
|
((BIDIOPTIONS *)lparam)->wMask =
|
|
BOM_NEUTRALOVERRIDE | BOM_CONTEXTREADING | BOM_CONTEXTALIGNMENT;
|
|
((BIDIOPTIONS *)lparam)->wEffects =
|
|
(_fNeutralOverride ? BOE_NEUTRALOVERRIDE : 0) |
|
|
(_nContextDir == CTX_NONE ? 0 : BOE_CONTEXTREADING) |
|
|
(_nContextAlign == CTX_NONE ? 0 : BOE_CONTEXTALIGNMENT);
|
|
}
|
|
break;
|
|
|
|
case EM_GETCHARFORMAT:
|
|
lres = OnGetCharFormat((CHARFORMAT2 *)lparam, wparam);
|
|
break;
|
|
|
|
case EM_GETCODEPAGE:
|
|
lres = GetDefaultCodePage((UINT)wparam);
|
|
break;
|
|
|
|
case EM_GETFIRSTVISIBLELINE:
|
|
if (fInplaceActive())
|
|
lres = _pdp->GetFirstVisibleLine();
|
|
else
|
|
hr = OLE_E_INVALIDRECT;
|
|
break;
|
|
|
|
case EM_GETLIMITTEXT: // Has cp output parameter (sort of)
|
|
lres = TxGetMaxLength(); // Ignore unless testing screams
|
|
break;
|
|
|
|
case EM_GETLINE:
|
|
if(fInplaceActive())
|
|
{
|
|
lres = _pdp->GetLineText((LONG)wparam, (TCHAR *)lparam,
|
|
(LONG) (*(WORD *) lparam));
|
|
}
|
|
else
|
|
hr = OLE_E_INVALIDRECT;
|
|
break;
|
|
|
|
case EM_GETLINECOUNT:
|
|
hr = TxGetLineCount(&lres);
|
|
break;
|
|
|
|
case EM_GETMODIFY: // RichEdit 1.0 returned -1 if _fModified
|
|
lres = -(LONG)_fModified; // is TRUE (go figure). So for backward
|
|
break; // compatibility, we do too :-(
|
|
|
|
case EM_GETOLEINTERFACE:
|
|
if(lparam)
|
|
{
|
|
#ifndef NOFEPROCESSING
|
|
if (wparam == 0x065737777) // 'AIMM'
|
|
W32->GetAimmObject((IUnknown **)(lparam));
|
|
else
|
|
#endif
|
|
{
|
|
*(IRichEditOle **)lparam = (IRichEditOle *)this;
|
|
AddRef();
|
|
}
|
|
}
|
|
lres = TRUE;
|
|
break;
|
|
|
|
case EM_GETSCROLLPOS:
|
|
{
|
|
POINT *point = (POINT *)lparam;
|
|
point->x = _pdp->GetUpScroll();
|
|
point->y = _pdp->GetVpScroll();
|
|
point->y = _pdp->ConvertVPosToScrollPos(point->y);
|
|
lres = 1;
|
|
}
|
|
break;
|
|
|
|
case EM_SETOLECALLBACK:
|
|
hr = E_FAIL;
|
|
if(lparam)
|
|
{
|
|
pobjmgr = GetObjectMgr();
|
|
if(pobjmgr)
|
|
{
|
|
pobjmgr->SetRECallback((IRichEditOleCallback *)lparam);
|
|
lres = TRUE;
|
|
hr = NOERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EM_GETPAGE:
|
|
lres = -1; // Signal page not available
|
|
if(_pdp)
|
|
{
|
|
LONG i;
|
|
hr = _pdp->GetPage(&i, (DWORD)wparam, (CHARRANGE *)lparam);
|
|
if(hr == NOERROR)
|
|
lres = i;
|
|
}
|
|
break;
|
|
|
|
case EM_GETPARAFORMAT:
|
|
lres = OnGetParaFormat((PARAFORMAT2 *)lparam, wparam);
|
|
break;
|
|
|
|
case EM_GETSEL: // Has cp output parameter
|
|
lres = OnGetSel((LONG*)wparam, (LONG*)lparam);
|
|
break;
|
|
|
|
case EM_GETSELTEXT:
|
|
lres = OnGetSelText((TCHAR *)lparam);
|
|
break;
|
|
|
|
case WM_GETTEXT:
|
|
{
|
|
GETTEXTEX gt;
|
|
|
|
gt.cb = wparam * 2;
|
|
gt.flags = GT_USECRLF;
|
|
gt.codepage = 1200;
|
|
gt.lpDefaultChar = NULL;
|
|
gt.lpUsedDefChar = NULL;
|
|
|
|
lres = GetTextEx(>, (TCHAR *)lparam);
|
|
}
|
|
break;
|
|
|
|
case WM_GETTEXTLENGTH: // Has cp output parameter
|
|
{
|
|
GETTEXTLENGTHEX gtl;
|
|
|
|
gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
|
|
gtl.codepage = 1200;
|
|
|
|
lres = GetTextLengthEx(>l);
|
|
}
|
|
break;
|
|
|
|
case EM_GETTEXTEX:
|
|
lres = GetTextEx((GETTEXTEX *)wparam, (TCHAR *)lparam);
|
|
break;
|
|
|
|
case EM_GETTEXTLENGTHEX: // Has cp output parameter
|
|
lres = GetTextLengthEx((GETTEXTLENGTHEX *)wparam);
|
|
break;
|
|
|
|
case EM_GETTEXTRANGE: // Has cp input parameter
|
|
{
|
|
TEXTRANGE * const ptr = (TEXTRANGE *)lparam;
|
|
LONG cch = ValidateTextRange(ptr);
|
|
|
|
// Only copy if there's something to copy and destination is valid
|
|
if(cch)
|
|
{
|
|
LONG cpMin = GetCpFromAcp(ptr->chrg.cpMin);
|
|
if(cch < 0) // Get text character count
|
|
cch = GetTextLength(); // because caller wants it all
|
|
else // + 1 is for terminating 0
|
|
cch = GetCpFromAcp(ptr->chrg.cpMost) - cpMin + 1;
|
|
|
|
if(!IsBadWritePtr(ptr->lpstrText, cch * sizeof(TCHAR)))
|
|
lres = GetTextRange(cpMin, cch, ptr->lpstrText);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EM_GETVIEWKIND:
|
|
GetViewKind(&lres);
|
|
break;
|
|
|
|
#ifndef NOWORDBREAKPROC
|
|
case EM_GETWORDBREAKPROC:
|
|
// Client can only use either WordBreakProc or ExWordBreakProc
|
|
// Return NULL if ExWordBreakProc is being used.
|
|
if (!_fExWordBreakProc)
|
|
lres = (LRESULT) _pfnWB;
|
|
break;
|
|
|
|
case EM_GETWORDBREAKPROCEX:
|
|
// Return ExWordBreakProc if it is being used.
|
|
if (_fExWordBreakProc)
|
|
lres = (LRESULT) _pfnWB;
|
|
break;
|
|
#endif
|
|
|
|
case EM_GETZOOM:
|
|
if(wparam && lparam)
|
|
{
|
|
*(unsigned *)wparam = GetZoomNumerator();
|
|
*(unsigned *)lparam = GetZoomDenominator();
|
|
lres = 1;
|
|
}
|
|
break;
|
|
|
|
case EM_HIDESELECTION:
|
|
if (Get10Mode() && lparam)
|
|
_fHideSelection = !!wparam;
|
|
|
|
if(!lparam || !_fFocus)
|
|
lres = OnHideSelectionChange((BOOL)wparam);
|
|
break;
|
|
|
|
case WM_HSCROLL:
|
|
if (IsUVerticalTflow(_pdp->GetTflow()))
|
|
{
|
|
WORD wCode = LOWORD(wparam);
|
|
wCode = InterchangeScrollCode(wCode);
|
|
LONG vpPos = HIWORD(wparam);
|
|
|
|
//In vertical displays the scrollbar position needs to be swapped.
|
|
if (_pdp->GetTflow() == tflowSW &&
|
|
(wCode == SB_THUMBTRACK || wCode == SB_THUMBPOSITION))
|
|
{
|
|
LONG vpMax, vpPage;
|
|
TxGetHScroll(NULL, &vpMax, NULL, &vpPage, NULL);
|
|
vpPos = vpMax - vpPos - vpPage;
|
|
vpPos = max(vpPos, 0);
|
|
}
|
|
|
|
hr = _pdp->VScroll(wCode, vpPos);
|
|
}
|
|
else
|
|
{
|
|
_pdp->UScroll(LOWORD(wparam), HIWORD(wparam));
|
|
hr = 0;
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
hr = OnTxKeyDown((WORD) wparam, (DWORD) lparam, publdr);
|
|
break;
|
|
|
|
case WM_KEYUP:
|
|
if(wparam == VK_APPS)
|
|
HandleKbdContextMenu();
|
|
else // Else don't say we processed
|
|
hr = S_FALSE; // message
|
|
|
|
W32->_fLRMorRLM = 0;
|
|
if(wparam == VK_CONTROL || wparam == VK_SHIFT)
|
|
{
|
|
// If a BiDi keyboard is installed, no strong-context behavior,
|
|
// both a Ctrl and a Shift key are pressed, no letter has been
|
|
// typed after the Ctrl and Shift keys have been pressed, and
|
|
// ReadOnly/Protected tests allow, then the selected paragraphs
|
|
// are set to RTL/LTR direction for the right/left Shift key,
|
|
// respectively. The keyboard and caret are also matched to this
|
|
// direction, and an alignment notification is sent.
|
|
// ReadOnly/Protected tests is removed for backward compatibility.
|
|
DWORD dwFlags = GetKeyboardFlags();
|
|
|
|
if (IsBiDiKbdInstalled() &&
|
|
!IsStrongContext(_nContextDir) &&
|
|
!IsStrongContext(_nContextAlign) &&
|
|
(dwFlags & CTRL) && (dwFlags & SHIFT) &&
|
|
!(dwFlags & LETAFTERSHIFT)
|
|
/* && IsntProtectedOrReadOnly(WM_KEYUP, wparam, lparam) */ )
|
|
{
|
|
CParaFormat PF;
|
|
PF._wEffects = (dwFlags & RSHIFT) ? PFE_RTLPARA : 0;
|
|
|
|
OnSetParaFormat(0, &PF, publdr, PFM_RTLPARA, PFM2_PARAFORMAT);
|
|
TxNotify(PF._wEffects ? EN_ALIGNRTL : EN_ALIGNLTR, 0);
|
|
}
|
|
if(wparam == VK_CONTROL)
|
|
lparam = (HIWORD(lparam) & KF_EXTENDED) ? RCTRL : LCTRL;
|
|
else
|
|
{
|
|
lparam = (LOBYTE(HIWORD(lparam)) == 0x36) ? RSHIFT : LSHIFT;
|
|
if(GetKeyState(VK_SHIFT) >= 0) // Ensure both shifts are off
|
|
lparam = SHIFT; // (potential Win95 problem)
|
|
}
|
|
ResetKeyboardFlag(lparam | LETAFTERSHIFT | HOTEURO);
|
|
}
|
|
else if(wparam == VK_MENU)
|
|
ResetKeyboardFlag((HIWORD(lparam) & KF_EXTENDED) ? (RALT | HOTEURO) : (LALT | HOTEURO));
|
|
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
lres = OnKillFocus();
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
hr = OnTxLButtonDblClk(MOUSEX, MOUSEY, (WORD) wparam);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
if(_fEatLeftDown)
|
|
{
|
|
TxSetFocus();
|
|
_fEatLeftDown = FALSE;
|
|
}
|
|
else
|
|
hr = OnTxLButtonDown(MOUSEX, MOUSEY, (WORD) wparam);
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
hr = OnTxLButtonUp(MOUSEX, MOUSEY, (WORD) wparam, LB_RELEASECAPTURE | LB_FLUSHNOTIFY);
|
|
break;
|
|
|
|
#if !defined(NOMAGELLAN)
|
|
case WM_MBUTTONDBLCLK: // Magellan zmouse scroll
|
|
case WM_NCMBUTTONDOWN: // support commandeers middle
|
|
case WM_MBUTTONDOWN: // button.
|
|
OnTxMButtonDown(MOUSEX, MOUSEY, (WORD) wparam);
|
|
break;
|
|
|
|
case WM_MBUTTONUP:
|
|
OnTxMButtonUp(MOUSEX, MOUSEY, (WORD) wparam);
|
|
break;
|
|
|
|
case WM_MOUSEWHEEL: // Magellan zmouse scroll n lines.
|
|
lres = HandleMouseWheel(wparam, lparam);
|
|
break;
|
|
#endif
|
|
|
|
case EM_LINEFROMCHAR: // Has cp input parameter
|
|
lparam = wparam; // Fall thru to EM_EXLINEFROMCHAR
|
|
|
|
case EM_EXLINEFROMCHAR: // Has cp input parameter
|
|
hr = TxLineFromCp((LONG)lparam, &lres);
|
|
break;
|
|
|
|
case EM_LINEINDEX: // Has cp output parameter
|
|
hr = TxLineIndex((LONG)wparam, &lres);
|
|
break;
|
|
|
|
case EM_LINELENGTH: // Has cp input/output parameters
|
|
hr = TxLineLength((LONG)wparam, &lres);
|
|
break;
|
|
|
|
case EM_LINESCROLL: // Has cp input parameter (cch)
|
|
// Documentation says the line to scroll to should be relative to the current top line.
|
|
// Richedit 2.0 based it on an absolute position. We're breaking richedit 2.0 compatibility
|
|
// to go back to the documentation specification and to match what riched 1.0 originally
|
|
// did
|
|
hr = TxLineScroll((LONG)lparam, (LONG)wparam);// but not curr impl
|
|
lres = _pdp->IsMultiLine();
|
|
break;
|
|
|
|
#ifdef MA_ACTIVATE
|
|
case WM_MOUSEACTIVATE:
|
|
lres = MA_ACTIVATE;
|
|
// If the window that currently has focus is part of our "application",
|
|
// then don't eat the mouse click. Otherwise, if it's from another
|
|
// app, the user is probably trying to swap apps, so eat the mouse
|
|
// down message and let our host app get a chance to come to the
|
|
// foreground.
|
|
if (!(IsChild((HWND)wparam, GetFocus()) ||
|
|
(wparam && (HWND)wparam == GetFocus())))
|
|
{
|
|
_fEatLeftDown = TRUE;
|
|
}
|
|
hr = S_FALSE; // pass WM_MOUSEACTIVATE message to DefWindProc
|
|
break;
|
|
#endif
|
|
|
|
case WM_MOUSEMOVE:
|
|
// We reset the "number of tries to put an active object
|
|
// in place" count here
|
|
_cActiveObjPosTries = MAX_ACTIVE_OBJ_POS_TRIES;
|
|
hr = OnTxMouseMove(MOUSEX, MOUSEY, (WORD)wparam, publdr);
|
|
break;
|
|
|
|
case EM_OUTLINE:
|
|
{
|
|
CFreezeDisplay cfd(_pdp);
|
|
|
|
if(wparam == EMO_GETVIEWMODE)
|
|
{
|
|
hr = GetViewKind(&lres);
|
|
break;
|
|
}
|
|
|
|
CTxtSelection * psel = GetSelNC();
|
|
|
|
if(!_pdp->IsMultiLine() || !IsRich() || !psel) // Control must be rich,
|
|
break; // multiline and active
|
|
|
|
if(wparam == EMO_ENTER || wparam == EMO_EXIT)
|
|
{
|
|
hr = SetViewKind(wparam == EMO_ENTER ? VM_OUTLINE : VM_NORMAL);
|
|
lres = !hr;
|
|
break;
|
|
}
|
|
|
|
if(!IsInOutlineView() || !IsntProtectedOrReadOnly(msg, wparam, lparam))
|
|
break;
|
|
|
|
CTxtRange rg(*psel);
|
|
|
|
switch(wparam)
|
|
{
|
|
case EMO_PROMOTE:
|
|
hr = rg.Promote(lparam, publdr);
|
|
psel->Update_iFormat(-1);
|
|
psel->Update(FALSE);
|
|
break;
|
|
|
|
case EMO_EXPAND:
|
|
hr = rg.ExpandOutline((short)LOWORD(lparam),
|
|
HIWORD(lparam) == EMO_EXPANDDOCUMENT);
|
|
break;
|
|
|
|
case EMO_MOVESELECTION:
|
|
hr = MoveSelection(lparam, publdr);
|
|
psel->Update(TRUE);
|
|
break;
|
|
|
|
default:
|
|
// TraceMessage("Unknown outline function received\r\n");
|
|
break;
|
|
};
|
|
lres = !hr;
|
|
_fModified = TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_PASTE:
|
|
case EM_PASTESPECIAL:
|
|
if(IsntProtectedOrReadOnly(msg, wparam, lparam))
|
|
{
|
|
CTxtSelection *psel = GetSel();
|
|
|
|
hr = PasteDataObjectToRange(NULL, psel,
|
|
(CLIPFORMAT) wparam, (REPASTESPECIAL *)lparam, publdr,
|
|
PDOR_NONE);
|
|
}
|
|
break;
|
|
|
|
case WM_USER + 38: // For backward compat with NT 3.51
|
|
case EM_POSFROMCHAR: // Has cp input parameter
|
|
// RichEdit 2.x used wparam instead of lparam for the cp and ignored
|
|
// wparam, unlike RE 1.0 and the Win32 documentation (sigh!). We fix
|
|
// this, but are compatible with RE 2.x for cp's whose values
|
|
// correspond to invalid write ptr's.
|
|
if(IsBadWritePtr((LPPOINT)wparam, sizeof(POINT)))
|
|
{
|
|
// Invalid write ptr, so assume incorrect RE 2.0 params
|
|
// TODO: enable following Assert when msgtest gets updated
|
|
//AssertSz(FALSE,
|
|
// "EM_POSFROMCHAR: wparam is illegal ptr, assuming cp value");
|
|
POINT pt;
|
|
hr = TxPosFromChar((LONG)wparam, &pt);
|
|
lres = SUCCEEDED(hr) ? MAKELONG(pt.x, pt.y) : -1;
|
|
}
|
|
else
|
|
hr = TxPosFromChar((LONG)lparam, (LPPOINT)wparam);
|
|
break;
|
|
|
|
#ifndef NORBUTTON
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
// Give client a chance to handle these messages,
|
|
// if we are over a link
|
|
if(HandleLinkNotification(msg, wparam, lparam))
|
|
break;
|
|
|
|
if(msg == WM_RBUTTONUP)
|
|
hr = OnTxRButtonUp(MOUSEX, MOUSEY, (WORD) wparam, RB_DEFAULT);
|
|
|
|
else if( msg == WM_RBUTTONDOWN)
|
|
hr = OnTxRButtonDown(MOUSEX, MOUSEY, (WORD) wparam);
|
|
|
|
break;
|
|
#endif
|
|
|
|
case EM_INSERTTABLE:
|
|
lres = hr = OnInsertTable((TABLEROWPARMS *)wparam, (TABLECELLPARMS *)lparam, publdr);
|
|
break;
|
|
|
|
case EM_REPLACESEL:
|
|
wparam = wparam ? ST_CHECKPROTECTION | ST_SELECTION | ST_KEEPUNDO
|
|
: ST_CHECKPROTECTION | ST_SELECTION;
|
|
hr = SetText((LPTSTR)lparam, wparam, 1200, publdr, &lres);
|
|
break;
|
|
|
|
case EM_REQUESTRESIZE:
|
|
hr = _pdp->RequestResize();
|
|
break;
|
|
|
|
case EM_SCROLL:
|
|
// TxVScroll returns the number of lines scrolled;
|
|
// this info should be returned in lres
|
|
lres = _pdp->VScroll((WORD)wparam, 0);
|
|
break;
|
|
|
|
case EM_SCROLLCARET:
|
|
OnScrollCaret();
|
|
break;
|
|
|
|
case EM_SELECTIONTYPE:
|
|
{
|
|
SELCHANGE selchg;
|
|
|
|
GetSel()->SetSelectionInfo(&selchg);
|
|
lres = selchg.seltyp;
|
|
}
|
|
break;
|
|
|
|
case EM_SETTYPOGRAPHYOPTIONS:
|
|
// Don't allow typography options for password & accelerator instances
|
|
hr = OnSetTypographyOptions(wparam, lparam);
|
|
lres = (hr == S_OK);
|
|
break;
|
|
|
|
case EM_SETBIDIOPTIONS:
|
|
if((Get10Mode() || !wparam) && lparam && !IsRich())
|
|
{
|
|
WORD wMask = ((BIDIOPTIONS *)lparam)->wMask;
|
|
WORD wEffects = (_fNeutralOverride ? BOE_NEUTRALOVERRIDE : 0) |
|
|
(_nContextDir == CTX_NONE ? 0 : BOE_CONTEXTREADING) |
|
|
(_nContextAlign == CTX_NONE ? 0 : BOE_CONTEXTALIGNMENT);
|
|
|
|
wEffects &= ~wMask;
|
|
wEffects |= (wMask & ((BIDIOPTIONS *)lparam)->wEffects);
|
|
if (_fNeutralOverride != !!(wEffects & BOE_NEUTRALOVERRIDE))
|
|
{
|
|
_fNeutralOverride = !_fNeutralOverride;
|
|
if (!_pdp->IsPrinter()) // Refresh display
|
|
{
|
|
_pdp->InvalidateRecalc();
|
|
TxInvalidate();
|
|
}
|
|
}
|
|
_nContextDir = (WORD)((wEffects & BOE_CONTEXTREADING) ? CTX_NEUTRAL : CTX_NONE);
|
|
_nContextAlign = (WORD)((wEffects & BOE_CONTEXTALIGNMENT) ? CTX_NEUTRAL : CTX_NONE);
|
|
if(_nContextDir != CTX_NONE || _nContextAlign != CTX_NONE)
|
|
{
|
|
SetContextDirection(TRUE);
|
|
Assert(_nContextDir != CTX_NONE || _nContextAlign != CTX_NONE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
hr = OnSetFocus();
|
|
break;
|
|
|
|
case EM_SETFONTSIZE:
|
|
lres = OnSetFontSize(LONG(wparam), (DWORD)lparam, publdr);
|
|
break;
|
|
|
|
case EM_SETMODIFY:
|
|
_fModified = wparam != 0;
|
|
|
|
#ifdef LATER
|
|
if (!_fModified)
|
|
ObFreezeFrames();
|
|
#endif // LATER
|
|
break;
|
|
|
|
case EM_SETSCROLLPOS:
|
|
{
|
|
POINT *pPoint = (POINT*)lparam;
|
|
_pdp->ScrollView(pPoint->x, pPoint->y, FALSE, TRUE);
|
|
lres = 1;
|
|
}
|
|
break;
|
|
|
|
case EM_EXSETSEL:
|
|
// EM_EXSETSEL duplicates the functionality of the 32-bit EM_SETSEL
|
|
// and exists purely for backward compatibility with Win16. We just
|
|
// repackage the params and fall thru to EM_SETSEL
|
|
wparam = (WPARAM)((CHARRANGE *)lparam)->cpMin;
|
|
lparam = (LPARAM)((CHARRANGE *)lparam)->cpMost;
|
|
|
|
// FALL-THROUGH to EM_SETSEL!!!
|
|
|
|
case EM_SETSEL:
|
|
lres = OnSetSel((LONG)wparam, (LONG)lparam);
|
|
break;
|
|
|
|
// INCREDIBLY EVIL HACK ALERT!!!!! Win95's dialog manager doesn't even
|
|
// pretend to be 32 bits despite the best attempts of our marketing dudes.
|
|
// WM_USER + 1 is the old Win3.0 EM_SETSEL in which the selection range
|
|
// was packed into the lparam.
|
|
//
|
|
// Sometimes (like tabbing through a dialog), Win95 will send us the 16
|
|
// bit EM_SETSEL message, so process it here.
|
|
case (WM_USER + 1):
|
|
lres = OnSetSel(LOWORD(lparam), HIWORD(lparam));
|
|
break;
|
|
|
|
case EM_SETTARGETDEVICE:
|
|
// Keep width sane so that LXtoDX works OK at least for displays
|
|
// Note that 0x7fffff = 485 feet! This keeps LXtoDX working provided
|
|
// _xPerInch < 257 (it's typically 96 for displays). For more
|
|
// generality, we'd need to use 64-bit arithmetic (see LXtoDX).
|
|
lparam = min(lparam, (LPARAM)0x7fffff);
|
|
lres = _pdp->SetMainTargetDC((HDC)wparam, (LONG)lparam);
|
|
break;
|
|
|
|
case EM_SETTEXTEX:
|
|
hr = SetText((LPTSTR)lparam, ((SETTEXTEX *)wparam)->flags,
|
|
((SETTEXTEX *)wparam)->codepage, publdr, &lres);
|
|
break;
|
|
|
|
case WM_SETTEXT:
|
|
hr = SetText((LPTSTR)lparam, ST_CHECKPROTECTION, wparam ? wparam : 1200, publdr, &lres);
|
|
break;
|
|
|
|
case EM_SETVIEWKIND:
|
|
hr = SetViewKind(wparam);
|
|
break;
|
|
|
|
#ifndef NOWORDBREAKPROC
|
|
case EM_SETWORDBREAKPROC:
|
|
_fExWordBreakProc = 0;
|
|
_pfnWB = (EDITWORDBREAKPROC) lparam;
|
|
break;
|
|
|
|
case EM_SETWORDBREAKPROCEX:
|
|
// We don't support this API in 2.0 and greater because we pass
|
|
// UNICODE text to the callback function. Therefore there
|
|
// are no benefits to this API. Exists for 1.0 backward
|
|
// compatibility only.
|
|
if (Get10Mode())
|
|
{
|
|
_fExWordBreakProc = 1;
|
|
|
|
// This is a bit deceiving, but lparam is a EDITWORDBREAKPROCEX but the compiler
|
|
// will generate an error if you try to type cast the l-value
|
|
_pfnWB = (EDITWORDBREAKPROC) lparam;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_SYSCHAR:
|
|
lres = hr = OnTxSysChar((WORD)wparam, (DWORD)lparam, publdr);
|
|
if(hr == S_OK)
|
|
break;
|
|
goto def;
|
|
|
|
case WM_SYSKEYUP:
|
|
if(IN_RANGE(VK_SHIFT, wparam, VK_MENU))
|
|
ResetKeyboardFlag(GetKbdFlags((WORD)wparam, (DWORD)lparam));
|
|
|
|
if (IN_RANGE(VK_NUMPAD0, wparam, VK_NUMPAD9) ||
|
|
wparam == VK_CLEAR || wparam == VK_INSERT ||
|
|
IN_RANGE(VK_PRIOR, wparam, VK_DOWN))
|
|
{
|
|
// Collect AltNumPad number to word around NT 4 bug and
|
|
// generalize to any Unicode value (in decimal, ugh!)
|
|
const static BYTE VkeyNumbers[] = {VK_NUMPAD9, VK_NUMPAD3, VK_NUMPAD1,
|
|
VK_NUMPAD7, VK_NUMPAD4, VK_NUMPAD8, VK_NUMPAD6, VK_NUMPAD2, 0, 0,
|
|
0, 0, VK_NUMPAD0};
|
|
// Flag Alt NumPad char typed to
|
|
SetKeyboardFlag(ALTNUMPAD); // distinguish hiANSI from lead
|
|
if(!IN_RANGE(VK_NUMPAD0, wparam, VK_NUMPAD9)) // byte in Win3.1 IME
|
|
{ // Collect AltNumPad number
|
|
if(wparam == VK_CLEAR) // NumLock not active: translate
|
|
wparam = VK_NUMPAD5; // to digit codes
|
|
else
|
|
wparam = VkeyNumbers[wparam - VK_PRIOR];
|
|
}
|
|
DWORD dwNum = GetKeyPadNumber();
|
|
if(!dwNum && wparam == VK_NUMPAD0)
|
|
SetKeyboardFlag(ALT0); // Flag that 0 is first digit
|
|
SetKeyPadNumber(10*dwNum + wparam - VK_NUMPAD0);
|
|
}
|
|
goto def;
|
|
|
|
case WM_SYSKEYDOWN:
|
|
if(OnTxSysKeyDown(wparam, lparam, publdr) == S_OK)
|
|
{
|
|
lres = TRUE;
|
|
break;
|
|
}
|
|
goto def;
|
|
|
|
case WM_TIMER:
|
|
OnTxTimer((UINT)wparam);
|
|
goto def;
|
|
|
|
case EM_UNDO:
|
|
case WM_UNDO:
|
|
if (!_fReadOnly)
|
|
{
|
|
hr = PopAndExecuteAntiEvent(_pundo, (void*)wparam);
|
|
if(hr == NOERROR)
|
|
lres = TRUE;
|
|
}
|
|
break;
|
|
|
|
case EM_REDO:
|
|
if (!_fReadOnly)
|
|
{
|
|
hr = PopAndExecuteAntiEvent(_predo, (void*)wparam);
|
|
if(hr == NOERROR)
|
|
lres = TRUE;
|
|
}
|
|
break;
|
|
|
|
case EM_SETUNDOLIMIT:
|
|
lres = HandleSetUndoLimit((DWORD)wparam);
|
|
break;
|
|
|
|
case WM_VSCROLL:
|
|
// TxVScroll returns the number of lines scrolled;
|
|
// WM_VSCROLL doesn't care about that info however.
|
|
Assert(lres == 0);
|
|
Assert(hr == NOERROR);
|
|
|
|
if (IsUVerticalTflow(_pdp->GetTflow()))
|
|
_pdp->UScroll(LOWORD(wparam), HIWORD(wparam));
|
|
else
|
|
_pdp->VScroll(LOWORD(wparam), HIWORD(wparam));
|
|
break;
|
|
|
|
case EM_GETHYPHENATEINFO:
|
|
{
|
|
HYPHENATEINFO *phyphinfo = (HYPHENATEINFO*) wparam;
|
|
if (phyphinfo->cbSize >= sizeof(HYPHENATEINFO))
|
|
{
|
|
phyphinfo->pfnHyphenate = _pfnHyphenate;
|
|
phyphinfo->dxHyphenateZone = _dulHyphenateZone;
|
|
}
|
|
else
|
|
hr = E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
case EM_SETHYPHENATEINFO:
|
|
{
|
|
HYPHENATEINFO *phyphinfo = (HYPHENATEINFO*) wparam;
|
|
if (phyphinfo->cbSize >= sizeof(HYPHENATEINFO))
|
|
{
|
|
BOOL fRedraw = FALSE;
|
|
if (phyphinfo->pfnHyphenate != _pfnHyphenate ||
|
|
_pfnHyphenate && _dulHyphenateZone != phyphinfo->dxHyphenateZone)
|
|
fRedraw = TRUE;
|
|
|
|
_pfnHyphenate = phyphinfo->pfnHyphenate;
|
|
_dulHyphenateZone = _pfnHyphenate ? phyphinfo->dxHyphenateZone : 0;
|
|
if (fRedraw)
|
|
_pdp->UpdateView();
|
|
}
|
|
else
|
|
hr = E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
case EM_GETAUTOCORRECTPROC:
|
|
{
|
|
if (_pDocInfo)
|
|
lres = (LRESULT) _pDocInfo->_pfnAutoCorrect;
|
|
}
|
|
break;
|
|
|
|
case EM_SETAUTOCORRECTPROC:
|
|
{
|
|
CDocInfo *pDocInfo = GetDocInfo();
|
|
if (pDocInfo)
|
|
pDocInfo->_pfnAutoCorrect = (AutoCorrectProc) wparam;
|
|
else;
|
|
lres = TRUE;
|
|
}
|
|
break;
|
|
|
|
// Old stuff that's no longer supported
|
|
case EM_FMTLINES: // Controls returning CRCRLFs for soft
|
|
// line breaks in EM_GETTEXT. Could
|
|
// implement
|
|
case WM_GETFONT: // Can support but have to hang onto a
|
|
// default HFONT. CCcs has an _hfont, but
|
|
// need to be sure default font is in
|
|
// cache at time of return
|
|
#ifdef EM_GETHANDLE
|
|
case EM_GETHANDLE: // Not supported by Win95 32-bit MLE either
|
|
case EM_SETHANDLE: // Not supported by Win95 32-bit MLE either
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
TRACEINFOSZ("Old message that is no longer supported.");
|
|
#endif
|
|
break;
|
|
|
|
case EM_SETTABSTOPS:
|
|
{
|
|
// this message only works for multi-line edit controls
|
|
if(!_pdp->IsMultiLine())
|
|
break;
|
|
|
|
// perform some validation checks
|
|
Assert(lparam || !wparam);
|
|
LPDWORD prgdwdlgCoord = (LPDWORD)lparam;
|
|
if (wparam && (!prgdwdlgCoord || !(*prgdwdlgCoord)))
|
|
break;
|
|
AssertSz(wparam <= MAX_TAB_STOPS, "Tab stop count beyond maximum allowed");
|
|
if (wparam > MAX_TAB_STOPS)
|
|
wparam = MAX_TAB_STOPS;
|
|
|
|
PARAFORMAT2 pf;
|
|
ZeroMemory(&pf, sizeof(PARAFORMAT2));
|
|
pf.cbSize = sizeof(PARAFORMAT2);
|
|
|
|
// contains the average width for the default font
|
|
LONG lAvgWidth;
|
|
|
|
//Average char width based on default font
|
|
HDC hdc = _phost->TxGetDC();
|
|
GetECDefaultHeightAndWidth(this, hdc, 1, 1,
|
|
W32->GetYPerInchScreenDC(), &lAvgWidth, NULL, NULL);
|
|
_phost->TxReleaseDC(hdc);
|
|
|
|
Assert(lAvgWidth);
|
|
|
|
// According to documentation wparam == 1 means the tab settings
|
|
// will be set at incremental positions *prgdwdlgCoord and wparam == 0
|
|
// the tab settings will be set at the default incremental position 32 (dialog coord)
|
|
long lTab = (wparam) ? *prgdwdlgCoord : 32;
|
|
long nCt = (wparam <= 1) ? MAX_TAB_STOPS : (signed)wparam;
|
|
for (int i = 0; i < nCt; i++)
|
|
{
|
|
long lval;
|
|
lval = (wparam <= 1) ? ((i+1) * lTab) : *prgdwdlgCoord;
|
|
pf.rgxTabs[i] = MulDiv(MulDiv(lval, lAvgWidth, 4), 1440, W32->GetXPerInchScreenDC());
|
|
if((unsigned)pf.rgxTabs[i] > 0xFFFFFF) // Keep in range
|
|
pf.rgxTabs[i] = 0xFFFFFF;
|
|
prgdwdlgCoord++;
|
|
}
|
|
|
|
// Set the default paragraph formatting
|
|
pf.cTabCount = nCt;
|
|
pf.dwMask = PFM_TABSTOPS;
|
|
CParaFormat PF;
|
|
PF.Set(&pf);
|
|
|
|
// Need to turn off group typing just like the selection would.
|
|
if (publdr)
|
|
publdr->StopGroupTyping();
|
|
|
|
lres = OnSetParaFormat(SPF_SETDEFAULT, &PF, publdr, PFM_TABSTOPS, PFM2_PARAFORMAT);
|
|
GetTabsCache()->Release(PF._iTabs);
|
|
break;
|
|
}
|
|
|
|
case EM_SETCHARFORMAT:
|
|
{
|
|
CHARFORMAT2 *pCF2 = (CHARFORMAT2 *)lparam;
|
|
UINT CodePage = 1200;
|
|
DWORD dwMask = pCF2->dwMask;
|
|
DWORD dwMask2 = 0;
|
|
|
|
if(!IsValidCharFormatW(pCF2))
|
|
{
|
|
if(!IsValidCharFormatA((CHARFORMAT2A *)lparam))
|
|
break;
|
|
if(dwMask & CFM_FACE) // Need to convert to Unicode
|
|
CodePage = GetDefaultCodePage(EM_SETCHARFORMAT);
|
|
}
|
|
|
|
if(dwMask & CFM_CHARSET && CharRepFromCharSet(pCF2->bCharSet) < 0)
|
|
dwMask &= ~CFM_CHARSET;
|
|
|
|
if(wparam & (SCF_ASSOCIATEFONT | SCF_ASSOCIATEFONT2))
|
|
{
|
|
lres = OnSetAssociateFont(pCF2, (DWORD)wparam);
|
|
break;
|
|
}
|
|
|
|
if(Get10Mode() && (dwMask & CFM_SIZE) && (pCF2->yHeight <= 0))
|
|
{
|
|
// 1.0 has a hack where if the height is being set and it is
|
|
// negative, then the height field is ignored.
|
|
dwMask &= ~CFM_SIZE;
|
|
}
|
|
|
|
if (pCF2->cbSize == sizeof(CHARFORMATW) ||
|
|
pCF2->cbSize == sizeof(CHARFORMATA))
|
|
{
|
|
// Restrict specifications to CHARFORMAT parameters. If the
|
|
// host isn't our Windows host, we allow this to include the
|
|
// CHARFORMAT2 disabled effect, since Forms^3 wanted that effect
|
|
// but wasn't willing to use CHARFORMAT2 (even tho they asked
|
|
// for it...)
|
|
dwMask &= fInOurHost() ? CFM_ALL : (CFM_ALL | CFM_DISABLED);
|
|
dwMask2 = CFM2_CHARFORMAT; // Tell callees that CHARFORMAT
|
|
} // was used
|
|
|
|
CCharFormat CF; // Transfer external CHARFORMAT(2)
|
|
CF.Set(pCF2, CodePage); // parms to internal CCharFormat
|
|
lres = OnSetCharFormat(wparam, &CF, publdr, dwMask, dwMask2);
|
|
break;
|
|
}
|
|
case WM_SETFONT:
|
|
lres = OnSetFont((HFONT)wparam);
|
|
break;
|
|
|
|
case EM_SETPAGE:
|
|
if(_pdp)
|
|
hr = _pdp->SetPage(wparam);
|
|
break;
|
|
|
|
case EM_SETPARAFORMAT:
|
|
{
|
|
PARAFORMAT2 *pPF2 = (PARAFORMAT2 *)lparam;
|
|
|
|
if(!IsValidParaFormat(pPF2))
|
|
break;
|
|
|
|
DWORD dwMask = pPF2->dwMask;
|
|
|
|
// Two more things to validate: (1) We don't let an applications set
|
|
// up tables and (2) Tabs coming from applications must be valid.
|
|
if(dwMask & (PFM_TABLE | PFM_TABLEROWDELIMITER |
|
|
PFM_OUTLINELEVEL | PFM_COLLAPSED))
|
|
{
|
|
// Trying to set up a table or outline view
|
|
break;
|
|
}
|
|
|
|
if ((dwMask & PFM_TABSTOPS) && (pPF2->cTabCount != 0))
|
|
{
|
|
// Make sure all submitted tabstops make sense.
|
|
int iMax = min(MAX_TAB_STOPS, pPF2->cTabCount);
|
|
|
|
for (int i = 0; i < iMax; i++)
|
|
{
|
|
// Make sure that tab stops make sense - make sure alignment
|
|
// is valid.
|
|
if (GetTabAlign(pPF2->rgxTabs[i]) > tomAlignBar)
|
|
{
|
|
// Invalid alignment.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i != iMax)
|
|
{
|
|
// Found error in validation loop so we are done.
|
|
break;
|
|
}
|
|
}
|
|
|
|
DWORD dwMask2 = 0;
|
|
if(pPF2->cbSize == sizeof(PARAFORMAT))
|
|
{
|
|
dwMask &= PFM_ALL; // Restrict to PARAFORMAT parms
|
|
dwMask2 = PFM2_PARAFORMAT; // Tell callees that
|
|
} // PARAFORMAT was used
|
|
|
|
CParaFormat PF; // Transfer external PARAFORMAT(2)
|
|
PF.Set(pPF2); // parms to internal CParaFormat
|
|
lres = OnSetParaFormat(wparam, &PF, publdr, dwMask, dwMask2);
|
|
GetTabsCache()->Release(PF._iTabs);
|
|
break;
|
|
}
|
|
|
|
case EM_SETZOOM:
|
|
if ((unsigned)(wparam | lparam) < 65536 && (!(wparam | lparam) ||
|
|
(LONG)wparam < (lparam << 6) && lparam < (LONG)(wparam << 6)))
|
|
{
|
|
// Only get here if
|
|
// 1) 0 <= wparam <= 65535 and 0 <= lparam <= 65535, and
|
|
// 2) either wparam = lparam = 0 (which turns off zooming by this
|
|
// message) or 1/64 < (zoom factor given by wparam/lparam) < 64.
|
|
SetZoomNumerator(wparam);
|
|
SetZoomDenominator(lparam);
|
|
_pdp->UpdateView();
|
|
lres = 1;
|
|
}
|
|
break;
|
|
|
|
case EM_STREAMIN:
|
|
case EM_STREAMOUT:
|
|
{
|
|
CTxtRange rg(this, 0, -GetTextLength());
|
|
CTxtRange * prg = &rg; // Default whole doc
|
|
|
|
wparam = W32->ValidateStreamWparam(wparam);
|
|
if(wparam & SFF_SELECTION) // Save to current selection
|
|
{
|
|
prg = (CTxtRange *)GetSel();
|
|
AssertSz(prg,
|
|
"EM_STREAMIN/OUT: requested selection doesn't exist");
|
|
}
|
|
else if(msg == EM_STREAMIN)
|
|
{
|
|
// If we are not streaming into the selection, then we are
|
|
// "loading" the entire file; this is not an undo-able operation,
|
|
// so set the undo builder to NULL and get rid of the current
|
|
// undo stacks
|
|
publdr = NULL;
|
|
ClearUndo(&undobldr);
|
|
|
|
// Clear away the file info if necessary
|
|
if(!(wparam & SFF_KEEPDOCINFO))
|
|
CloseFile(FALSE);
|
|
}
|
|
|
|
if(msg == EM_STREAMIN)
|
|
{
|
|
// If we are going to be loading an entire file, we only
|
|
// want to check "normal' protection; we can ignore the
|
|
// fIsDBCS protection. This does mean that somebody
|
|
// can do an "insert file" and break apart a DBCS combo,
|
|
// but we'll have to live with that. Outlook uses
|
|
// RTF streaming in many different places, so the strong
|
|
// fIsDBCS protection breaks them.
|
|
if ((_dwEventMask & ENM_PROTECTED) &&
|
|
prg->IsProtected(CHKPROT_EITHER) == PROTECTED_ASK &&
|
|
QueryUseProtection(prg, msg, wparam, lparam))
|
|
{
|
|
Beep();
|
|
Assert(lres == 0);
|
|
break;
|
|
}
|
|
|
|
// Freeze the display before loading
|
|
CFreezeDisplay fd(_pdp);
|
|
|
|
lres = _ldte.LoadFromEs(prg, wparam, (EDITSTREAM *)lparam,
|
|
FALSE, publdr);
|
|
|
|
if (_fOutlineView)
|
|
{
|
|
// Outline view must have formatting.
|
|
_psel->Check_rpPF();
|
|
}
|
|
|
|
if (_fFocus)
|
|
{
|
|
// Update caret but delay till display is thawed and do so only
|
|
// if we have the focus. If we do this all the time we get wierd
|
|
// scrolling effects such as scrolling to the beginning of a
|
|
// document when the focus is set. See bug #1649 for repro of
|
|
// wierd effects.
|
|
_pdp->SaveUpdateCaret(TRUE);
|
|
}
|
|
}
|
|
else
|
|
lres = _ldte.SaveToEs (prg, wparam, (EDITSTREAM *)lparam);
|
|
break;
|
|
}
|
|
|
|
#ifdef WM_SYSCOLORCHANGE
|
|
case WM_SYSCOLORCHANGE:
|
|
#ifndef NODRAFTMODE
|
|
if (_fDraftMode)
|
|
{
|
|
W32->InitSysParams(TRUE);
|
|
_pdp->InvalidateRecalc();
|
|
}
|
|
#endif
|
|
TxInvalidate();
|
|
break;
|
|
#endif
|
|
|
|
// debug stuff
|
|
#if defined(DEBUG) && !defined(NOFULLDEBUG)
|
|
case EM_DBGPED:
|
|
OnDumpPed();
|
|
break;
|
|
#endif // DEBUG
|
|
|
|
case EM_SETEVENTMASK:
|
|
lres = _dwEventMask; // Set up to return value before
|
|
_dwEventMask = (DWORD)lparam; // the change
|
|
|
|
if (lparam & ENM_REQUESTRESIZE)
|
|
{
|
|
// We need to update the display just in case it changes.
|
|
_pdp->UpdateView();
|
|
}
|
|
break;
|
|
|
|
case EM_GETEVENTMASK:
|
|
lres = _dwEventMask;
|
|
break;
|
|
|
|
#ifdef EM_GETTHUMB
|
|
case EM_GETTHUMB:
|
|
LONG Pos;
|
|
BOOL fIsEnabled;
|
|
|
|
if (TxGetVScroll(NULL, NULL, &Pos, NULL, &fIsEnabled) == S_OK
|
|
&& fIsEnabled)
|
|
{
|
|
lres = Pos;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case EM_SETLANGOPTIONS:
|
|
_fAutoFont = (lparam & IMF_AUTOFONT) != 0;
|
|
_fAutoKeyboard = (lparam & IMF_AUTOKEYBOARD) != 0;
|
|
_fAutoFontSizeAdjust = (lparam & IMF_AUTOFONTSIZEADJUST) != 0;
|
|
_fDualFont = (lparam & IMF_DUALFONT) != 0;
|
|
_fUIFont = (lparam & IMF_UIFONTS) != 0;
|
|
lres = 1;
|
|
break;
|
|
|
|
case EM_GETLANGOPTIONS:
|
|
if(_fAutoFont)
|
|
lres |= IMF_AUTOFONT;
|
|
if(_fAutoKeyboard)
|
|
lres |= IMF_AUTOKEYBOARD;
|
|
if(_fAutoFontSizeAdjust)
|
|
lres |= IMF_AUTOFONTSIZEADJUST;
|
|
if(_fDualFont)
|
|
lres |= IMF_DUALFONT;
|
|
if(_fUIFont)
|
|
lres |= IMF_UIFONTS;
|
|
break;
|
|
|
|
case EM_SETEDITSTYLE:
|
|
if (!Get10Mode()) // Not support in 1.0 mode
|
|
{
|
|
BOOL fForceRepaint = FALSE;
|
|
DWORD dwEditStyle = _dwEditStyle & ~lparam; // Kill current flag values
|
|
// Change following mask to give the largest SES_xxx defined)
|
|
dwEditStyle |= wparam & lparam & (SES_CTFALLOWPROOFING*2 - 1); // Or in new values
|
|
|
|
// Certain bits aren't switchable
|
|
dwEditStyle |= (_fSystemEditMode ? SES_EMULATESYSEDIT : 0);
|
|
_dwEditStyle = dwEditStyle;
|
|
|
|
// There are certain things which we won't allow user to reset, ie SES_EMULATESYSEDIT.
|
|
// So reset everything and except for the SES_EMULATESYSEDIT
|
|
if(dwEditStyle & SES_EMULATESYSEDIT)
|
|
{
|
|
if(SUCCEEDED(HandleSetTextMode(TM_SINGLELEVELUNDO | TM_PLAINTEXT)))
|
|
{
|
|
// SES_EMULATESYSEDIT implies SES_BEEPONMAXTEXT
|
|
_fSystemEditBeep = TRUE;
|
|
}
|
|
else
|
|
_fSystemEditMode = FALSE;
|
|
}
|
|
|
|
#ifndef NODRAFTMODE
|
|
// Repaint and recalc if draft mode is turned on or off
|
|
if (lparam & SES_DRAFTMODE)
|
|
fForceRepaint = TRUE;
|
|
#endif
|
|
|
|
if (fForceRepaint || (lparam & SES_USEATFONT))
|
|
{
|
|
_pdp->InvalidateRecalc();
|
|
TxInvalidate();
|
|
}
|
|
|
|
if(dwEditStyle & SES_BIDI)
|
|
OrCharFlags(FRTL, publdr);
|
|
|
|
_fLowerCase = !_fUpperCase && (dwEditStyle & SES_LOWERCASE);
|
|
} // Fall thru to EM_GETEDITSTYLE
|
|
// to return _bEditStyle
|
|
case EM_GETEDITSTYLE:
|
|
if (!Get10Mode()) // Not support in 1.0 mode
|
|
lres |= _dwEditStyle; // Some EditStyles have been filled in Cmsgflt
|
|
|
|
break;
|
|
|
|
case EM_SETPAGEROTATE:
|
|
lres = HandleSetTextFlow(wparam);
|
|
break;
|
|
|
|
case EM_GETPAGEROTATE:
|
|
lres = _pdp->GetTflow();
|
|
break;
|
|
|
|
case EM_SETTEXTMODE:
|
|
// 1.0 mode does not supported EM_SETTEXTMODE
|
|
if (!Get10Mode())
|
|
lres = HandleSetTextMode(wparam);
|
|
break;
|
|
|
|
case EM_GETTEXTMODE:
|
|
lres = IsRich() ? TM_RICHTEXT : TM_PLAINTEXT;
|
|
|
|
lres |= (_pundo && ((CUndoStack *)_pundo)->GetSingleLevelMode())
|
|
? TM_SINGLELEVELUNDO : TM_MULTILEVELUNDO;
|
|
|
|
lres |= _fSingleCodePage ? TM_SINGLECODEPAGE : TM_MULTICODEPAGE;
|
|
break;
|
|
|
|
case EM_LIMITTEXT:
|
|
lparam = wparam;
|
|
// Intentionally fall through. These messages are duplicates. But
|
|
// Win9x manages to convert wparam = 0x3FFFFFFF to 0xFFFFFFFF. No
|
|
// such problem exists with EM_EXLIMITTEXT.
|
|
|
|
case EM_EXLIMITTEXT: // Has cp input parameter (sort of)
|
|
if(!lparam) // We ignore translation between
|
|
{ // acp and cp
|
|
// 0 means set the control to the maximum size. However, because
|
|
// 1.0 set this to 64K will keep this the same value so as not to
|
|
// surprise anyone. Apps are free to set the value to be above 64K.
|
|
lparam = (LPARAM)cResetTextMax;
|
|
}
|
|
if (Get10Mode())
|
|
{
|
|
// 1.0 used a signed variable to hold the length of the string. So
|
|
// if lparam is negative then just set lparam to zero to emulate
|
|
// 1.0 behavior
|
|
if ((LONG)lparam < 0)
|
|
lparam = 0;
|
|
}
|
|
_cchTextMost = (LONG)lparam;
|
|
break;
|
|
|
|
case EM_AUTOURLDETECT:
|
|
if(lparam || (wparam | 1) != 1)
|
|
{
|
|
hr = lres = E_INVALIDARG;
|
|
break;
|
|
}
|
|
if(wparam == TRUE && !_pdetecturl)
|
|
{
|
|
_pdetecturl = new CDetectURL(this);
|
|
if(!_pdetecturl)
|
|
hr = lres = E_OUTOFMEMORY;
|
|
}
|
|
else if(!wparam && _pdetecturl)
|
|
{
|
|
delete _pdetecturl;
|
|
_pdetecturl = NULL;
|
|
}
|
|
break;
|
|
|
|
case EM_GETAUTOURLDETECT:
|
|
Assert(lres == 0 && hr == NOERROR);
|
|
if(_pdetecturl)
|
|
lres = TRUE;
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
// We reset the "number of tries to put an active object
|
|
// in place" count here
|
|
_cActiveObjPosTries = MAX_ACTIVE_OBJ_POS_TRIES;
|
|
hr = S_FALSE;
|
|
break;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
// System parameters have changed. We need to update them.
|
|
// Note : Since we don't protect access to system parameters
|
|
// with locks, it may be possible for some instances to not
|
|
// see the changes immediately
|
|
lres = 0;
|
|
W32->InitSysParams(TRUE);
|
|
|
|
#ifndef NODRAFTMODE
|
|
if (_fDraftMode)
|
|
{
|
|
_pdp->InvalidateRecalc();
|
|
TxInvalidate();
|
|
}
|
|
#endif
|
|
|
|
#ifndef NOCOMPLEXSCRIPTS
|
|
if (W32->GetDigitSubstitutionMode() != DIGITS_NOTIMPL)
|
|
OrCharFlags(FRTL, publdr);
|
|
#endif
|
|
|
|
break;
|
|
|
|
case EM_CONVPOSITION:
|
|
if (wparam)
|
|
lres = GetCpFromAcp(lparam);
|
|
else
|
|
lres = GetAcpFromCp(lparam);
|
|
break;
|
|
|
|
#ifndef NOPRIVATEMESSAGE
|
|
case EM_INSERTOBJ:
|
|
{
|
|
// This message supports Cicero InsertEmbedded
|
|
int cpMin = ((CHARRANGE *)wparam)->cpMin;
|
|
int cpMost = ((CHARRANGE *)wparam)->cpMost;
|
|
CTxtRange rg(this, cpMin, cpMin - cpMost);
|
|
REPASTESPECIAL rps; // @parm Special paste info
|
|
rps.dwAspect = DVASPECT_CONTENT;
|
|
|
|
hr = S_FALSE;
|
|
if (!rg.WriteAccessDenied())
|
|
hr = _ldte.CreateOleObjFromDataObj((IDataObject *)lparam, &rg, &rps, iEmbObj, publdr);
|
|
}
|
|
break;;
|
|
|
|
case EM_SETCALLBACK: // Setup message filter callback
|
|
_pMsgCallBack = (CMsgCallBack *)lparam;
|
|
break;
|
|
|
|
case EM_SETUPNOTIFY:
|
|
if (!_pMsgNotify)
|
|
_pMsgNotify = new CTextNotify(this);
|
|
|
|
if (_pMsgNotify)
|
|
{
|
|
if (wparam == 0)
|
|
_pMsgNotify->Remove((ITxNotify *)lparam);
|
|
else
|
|
_pMsgNotify->Add((ITxNotify *)lparam);
|
|
}
|
|
|
|
break;
|
|
|
|
case EM_GETDOCFLAGS:
|
|
lres = 0;
|
|
if (_fReadOnly)
|
|
lres |= GDF_READONLY;
|
|
|
|
if (_fOverstrike)
|
|
lres |= GDF_OVERTYPE;
|
|
|
|
if (_fSingleCodePage)
|
|
lres |= GDF_SINGLECPG;
|
|
|
|
if (_fRich)
|
|
lres |= GDF_RICHTEXT;
|
|
|
|
lres &= wparam;
|
|
|
|
break;
|
|
|
|
case EM_GETPARATXTFLOW:
|
|
{
|
|
CTxtPara *pTxtPara = (CTxtPara *)lparam;
|
|
lres = pTxtPara->_PF.IsRtlPara();
|
|
break;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef NOACCESSIBILITY
|
|
case WM_GETOBJECT:
|
|
{
|
|
IUnknown* punk = NULL;
|
|
|
|
lres = 0;
|
|
|
|
if (lparam == OBJID_NATIVEOM)
|
|
{
|
|
QueryInterface(IID_IUnknown, (void**)&punk); // Need to expose Tom interdface
|
|
}
|
|
else if (lparam == OBJID_CLIENT && _fInOurHost)
|
|
{
|
|
HWND hwnd = NULL;
|
|
|
|
TxGetWindow( &hwnd );
|
|
|
|
if (hwnd)
|
|
W32->CreateStdAccessibleProxyW(hwnd, L"RichEdit", OBJID_CLIENT, IID_IAccessible, (void**)&punk);
|
|
}
|
|
|
|
if (punk)
|
|
{
|
|
lres = W32->LResultFromObject(IID_IUnknown, wparam, (LPUNKNOWN)punk);
|
|
punk->Release();
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
def: hr = S_FALSE;
|
|
break;
|
|
}
|
|
|
|
if(plresult)
|
|
*plresult = lres;
|
|
|
|
#ifndef NOFEPROCESSING
|
|
if (hr == S_FALSE && _pMsgFilter && _pMsgCallBack)
|
|
{
|
|
HWND hWnd;
|
|
|
|
TxGetWindow(&hWnd);
|
|
hr = _pMsgCallBack->HandlePostMessage(hWnd, msg, wparam, lparam, plresult);
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxDraw (dwDrawAspect, lindex, pvAspect, ptd, hdcDraw,
|
|
* hicTargetDev, lprcBounds, lprcWBounds, lprcUpdate,
|
|
* pfnContinue, dwContinue)
|
|
*
|
|
* @mfunc Draws the text services object
|
|
*
|
|
* @rdesc HRESULT (typically S_OK).
|
|
*
|
|
* @comm
|
|
*
|
|
* This method renders the Text Services. It accepts the same parameters
|
|
* as the corresponding IViewObject::Draw method in OLE, with the extra
|
|
* <p lprcUpdate > parameter. It can be used while the host is inactive
|
|
* or active (in-place).
|
|
*
|
|
* If dwDrawAspect is DVASPECT_CONTENT, this method should render a screen
|
|
* image of the text content to the hdcDraw device context. The hicTargetDev
|
|
* and ptd parameters give information on the target device context if any
|
|
* (usually a printer).
|
|
*
|
|
* The lprcClient parameter gives the rectangle to render to, also called
|
|
* "client rectangle". This rectangle represents the position and extents
|
|
* of the entire image of the Text Services to be drawn. It is expressed in
|
|
* the logical coordinate system of hdcDraw. This parameter can only be NULL
|
|
* if the control is active. In that case, Text Services should render the
|
|
* in-place active view (which client rectangle can be obtained by calling
|
|
* TxGetClientRect on the host).
|
|
*
|
|
* The lprcUpdate parameter, if not NULL, gives the rectangle to update
|
|
* inside that client rectangle. It is given in the logical coordinate system
|
|
* of hdcDraw. If NULL, the entire client rectangle should be painted.
|
|
*
|
|
* Text Services should render with the appropriate zooming factor, which
|
|
* can be obtained from the client rect and the native size given by
|
|
* ITextHost::TxGetExtent. For more information, see ITextHost::TxGetExtent.
|
|
*
|
|
* If the drawing aspect is DVASPECT_DOCPRINT, the TxDraw method can assume
|
|
* that it is rendering to the printer. In that case, hdcDraw is the printer
|
|
* device context. TxDraw should still render the lprcBounds rectangle,
|
|
* starting at the current scrolling position. TS can make optimization for
|
|
* rendering to the printer (like not painting the background color if white)
|
|
* and certain screen specific elements (such as the selection) should not be
|
|
* rendered.
|
|
*
|
|
* General comments on OLE hosts and TxDraw (and TxSetCursor, TxQueryHitPoint):
|
|
*
|
|
* OLE hosts can call the TxDraw method at any time with any rendering DC or
|
|
* client rectangle. All an inactive OLE object has on a permanent basis is
|
|
* a himetric extent. It gets the rectangle in which to render only via the
|
|
* IViewObject::Draw call and this rectangle is valid only for the scope of
|
|
* that method. In fact, the same control can be rendered consecutively in
|
|
* different rectangles and different DCs for example because it is displayed
|
|
* simultaneously in different views on the screen.
|
|
*
|
|
* The client rectangle and DC passed to TxDraw should normally not be cached.
|
|
* However, this would force Text Services to recalc lines for every single
|
|
* draw, which would lead to terrible performance. So it is likely that Text
|
|
* Services will actually cache some information computed for a specific
|
|
* client rectangle and DC (such as the line breaks for example). On the
|
|
* next call to TxDraw, however, the validity of the cached information
|
|
* should be checked before it gets used, and updated information should be
|
|
* regenerated if necessary.
|
|
*
|
|
* When the control is in-place active, the problem is even more complex
|
|
* since TxDraw can still be called to render other views than the in-place
|
|
* active one. In other words, the client rectangle passed to TxDraw may
|
|
* not be the same as the active view one (passed to OnTxInPlaceActivate
|
|
* and obtained via TxGetClientRect on the host).The the host specifies
|
|
* what view they wish to display based on the lViewId parameter. If the
|
|
* value for lViewId is TXTVIEW_ACTIVE, the view referred to is the inplace
|
|
* active view. TXTVIEW_INACTIVE means some other view such as a print
|
|
* preview or even printing itself. It is important to note that
|
|
* TXTVIEW_INACTIVE views may not have scroll bars.
|
|
*
|
|
* The same comments apply to TxSetCursor and TxQueryHitPoint, discussed
|
|
* in the following sections.
|
|
*/
|
|
HRESULT CTxtEdit::TxDraw(
|
|
DWORD dwDrawAspect, //@parm Draw aspect
|
|
LONG lindex, //@parm Currently unused
|
|
void * pvAspect, //@parm Info for drawing optimizations (OCX 96)
|
|
DVTARGETDEVICE *ptd, //@parm Info on target device
|
|
HDC hdcDraw, //@parm Rendering device context
|
|
HDC hicTargetDev, //@parm Target information context
|
|
LPCRECTL lprcBounds, //@parm Bounding (client) rectangle
|
|
LPCRECTL lprcWBounds, //@parm Clipping rect for metafiles
|
|
LPRECT lprcUpdate, //@parm Dirty rectangle inside lprcBounds
|
|
BOOL (CALLBACK * pfnContinue) (DWORD), //@parm Callback for interupting
|
|
// long display (currently unused)
|
|
DWORD dwContinue, //@parm Parameter to pass to pfnContinue function
|
|
LONG lViewId) //@parm View identifier
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxDraw");
|
|
|
|
HRESULT hr;
|
|
|
|
// JMO : FUTURE : We should do something about reentrant draws sometime.
|
|
// If we do that may procide a simpler fix for RAID bug 7212.
|
|
|
|
#if !defined(NOMAGELLAN)
|
|
CMagellanBMPStateWrap bmpOff(*this, hdcDraw);
|
|
#endif
|
|
|
|
CCallMgr callmgr(this);
|
|
|
|
START_PROFILING
|
|
|
|
{
|
|
CLock lock;
|
|
if (g_OLSBusy)
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// If the display is frozen, don't let ourselves draw. This is a pretty
|
|
// hoaky re-entrancy check.
|
|
// FUTURE (alexgo/ricksa): be better about this.
|
|
if(TXTVIEW_ACTIVE == lViewId && _pdp->IsFrozen())
|
|
{
|
|
_pdp->SetNeedRedisplayOnThaw(TRUE);
|
|
TRACEINFOSZ("Forcing a redisplay on thaw");
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if(dwDrawAspect != DVASPECT_CONTENT && dwDrawAspect != DVASPECT_DOCPRINT)
|
|
{
|
|
// We don't support the aspect requested
|
|
return DV_E_DVASPECT;
|
|
}
|
|
|
|
if(!lprcBounds && !_fInPlaceActive || hicTargetDev && !ptd)
|
|
{
|
|
// If we are not inplace active we must have a client rectangle
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HDC hicLocal = NULL;
|
|
|
|
// Did they give us a ptd without a hic?
|
|
if(!hicTargetDev && ptd)
|
|
{
|
|
// Create and information context for the device information
|
|
// since it wasn't supplied.
|
|
hicLocal = CreateIC(
|
|
(TCHAR *)((BYTE *) ptd + ptd->tdDriverNameOffset),
|
|
(TCHAR *)((BYTE *) ptd + ptd->tdDeviceNameOffset),
|
|
(TCHAR *)((BYTE *) ptd + ptd->tdPortNameOffset),
|
|
(DEVMODE *)((BYTE *) ptd + ptd->tdExtDevmodeOffset));
|
|
if(!hicLocal)
|
|
return E_FAIL; // Couldn't create it
|
|
|
|
hicTargetDev = hicLocal;
|
|
}
|
|
|
|
AssertSz(GetMapMode(hdcDraw) == MM_TEXT || GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE,
|
|
"RichEdit requires MM_TEXT."); // REVIEW (keithcu) Clients do (and should) use MM_TEXT
|
|
|
|
// Preallocate the memory so the set cannnot fail and we don't
|
|
// have to use the heap. Note that all clean up is handled
|
|
// outside of this object. Also note that we don't assign any
|
|
// information here because we may not use this structure. If
|
|
// recursion is happening we will use the top level structure.
|
|
CDrawInfo di(this);
|
|
|
|
_pdp->SetDrawInfo(
|
|
&di,
|
|
dwDrawAspect,
|
|
lindex,
|
|
pvAspect,
|
|
ptd,
|
|
hicTargetDev);
|
|
|
|
// We use our main display object if we are the active view (which is
|
|
// indicate by the supplied client rectangle) or if the object is
|
|
// inactive and the ptd is NULL. We assume that the ptd being NULL means
|
|
// that the display request is for the screen and not for a print or
|
|
// print preview.
|
|
if(TXTVIEW_ACTIVE == lViewId || !ptd)
|
|
{
|
|
hr = S_FALSE;
|
|
|
|
// The main display object draws active views and tries to draw
|
|
// inactive views if the control is not active.
|
|
if (!lprcWBounds &&
|
|
( fInplaceActive() && TXTVIEW_ACTIVE == lViewId ||
|
|
!fInplaceActive() && TXTVIEW_INACTIVE == lViewId))
|
|
{
|
|
hr = _pdp->Draw(hdcDraw, hicTargetDev, // We aren't interruptable
|
|
(RECT *)lprcBounds, // drawing to screen, so
|
|
(RECT *)lprcWBounds, // why pretend?
|
|
lprcUpdate, NULL, 0);
|
|
}
|
|
|
|
if(S_FALSE == hr)
|
|
{
|
|
// This is an inactive view for which the cached state
|
|
// does not match the input request so we make a special
|
|
// object to do the drawing.
|
|
CDisplay *pdp = _pdp->Clone();
|
|
if(pdp)
|
|
{
|
|
// Force recalc - this tells Draw to draw no matter what
|
|
pdp->InvalidateRecalc();
|
|
hr = pdp->Draw(hdcDraw, hicTargetDev, // Do the draw
|
|
(RECT *)lprcBounds,
|
|
(RECT *)lprcWBounds,
|
|
lprcUpdate, NULL, 0);
|
|
}
|
|
delete pdp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Make a copy so that we can update it
|
|
RECT rcForPrint = *((RECT *)lprcBounds);
|
|
|
|
// We want data both formatted and printed
|
|
hr = FormatAndPrint(hdcDraw, hicTargetDev, ptd, &rcForPrint,
|
|
(RECT*)lprcWBounds);
|
|
|
|
struct SPrintControl prtcon;
|
|
|
|
// This call to OnFormatRange simply cleans up printer object
|
|
OnFormatRange(NULL, prtcon);
|
|
}
|
|
|
|
_pdp->ReleaseDrawInfo();
|
|
|
|
if(hicLocal) // Clean up information context
|
|
DeleteDC(hicLocal); // if we created one
|
|
|
|
// An active OLE object might have been dragged/scrolled away
|
|
// from where it belongs. We need to put it back.
|
|
// The _cActiveObjPosTries guards us from an indefinite looop here
|
|
// (OnReposition may post another paint message, and so on)
|
|
// We only do this when we were notified of a position or size change
|
|
COleObject* poleobjActive;
|
|
if (HasObjects() && _cActiveObjPosTries &&
|
|
(poleobjActive = GetObjectMgr()->GetInPlaceActiveObject()))
|
|
{
|
|
// Reduce number of tries
|
|
_cActiveObjPosTries--;
|
|
|
|
// BUG FIX 6073
|
|
// Only fetch the extent if the object view size or position
|
|
// has changed
|
|
// Get new object size (we might have resized it,
|
|
// and we don't want to lose that!!)
|
|
if (poleobjActive->GetViewChanged())
|
|
{
|
|
poleobjActive->FetchObjectExtents();
|
|
poleobjActive->ResetViewChanged();
|
|
}
|
|
|
|
// and put it there!!
|
|
poleobjActive->OnReposition();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetHScroll (plMin, plMax, plPos, plPage, pfEnabled)
|
|
*
|
|
* @mfunc
|
|
* Get horizontal scroll bar state information
|
|
*
|
|
* @rdesc
|
|
* HRESULT = S_OK
|
|
*/
|
|
HRESULT CTxtEdit::TxGetHScroll(
|
|
LONG *plMin, //@parm Minimum scroll position
|
|
LONG *plMax, //@parm Maximum scroll position
|
|
LONG *plPos, //@parm Current scroll position
|
|
LONG *plPage, //@parm View width in pixels
|
|
BOOL *pfEnabled) //@parm Whether horizonatl scrolling is enabled.
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetHScroll");
|
|
|
|
START_PROFILING
|
|
|
|
if(plMin)
|
|
*plMin = 0;
|
|
|
|
if (IsUVerticalTflow(_pdp->GetTflow()))
|
|
{
|
|
if(plMax)
|
|
*plMax = _pdp->GetScrollRange(SB_VERT);
|
|
|
|
if(plPage)
|
|
*plPage = _pdp->ConvertVPosToScrollPos(_pdp->GetDvpView());
|
|
|
|
if(plPos)
|
|
{
|
|
*plPos = _pdp->ConvertVPosToScrollPos(_pdp->GetVpScroll());
|
|
if (_pdp->GetTflow() == tflowSW)
|
|
*plPos = *plMax - *plPos - *plPage;
|
|
*plPos = max(*plPos, 0);
|
|
}
|
|
|
|
if(pfEnabled)
|
|
*pfEnabled = _pdp->IsVScrollEnabled();
|
|
}
|
|
else
|
|
{
|
|
if(plMax)
|
|
*plMax = _pdp->GetScrollRange(SB_HORZ);
|
|
|
|
if(plPos)
|
|
*plPos = _pdp->GetUpScroll();
|
|
|
|
if(plPage)
|
|
*plPage = _pdp->GetDupView();
|
|
|
|
// CDisplay::_fUScrollEnabled may be TRUE when not in-place active
|
|
// because it has a dual meaning: 1) need Horiz scroll bars, and 2)
|
|
// CDisplay::_upScroll is allowed for ES_AUTOHSCROLL even with no
|
|
// horizontal scrollbar. The latter can turn on _fUScrollEnabled when
|
|
// the control is active and when the control goes inactive, it stays
|
|
// on, so we say it's off to keep Forms^3 from displaying a horizontal
|
|
// scroll bar. We probably should have two flags: _fUScrollEnabled and
|
|
// _fUScrollbarEnabled, but for now, we stick with one. No such problem
|
|
// for vertical case, since vertical scrolling always uses a scrollbar.
|
|
if(pfEnabled)
|
|
*pfEnabled = _fInPlaceActive ? _pdp->IsUScrollEnabled() : 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetVScroll (plMin, plMax, plPos, plPage, pfEnabled)
|
|
*
|
|
* @mfunc
|
|
* Get vertical scroll bar state information
|
|
*
|
|
* @rdesc
|
|
* HRESULT = S_OK
|
|
*/
|
|
HRESULT CTxtEdit::TxGetVScroll(
|
|
LONG *plMin, //@parm Minimum scroll position
|
|
LONG *plMax, //@parm Maximum scroll position
|
|
LONG *plPos, //@parm Current scroll position
|
|
LONG *plPage, //@parm Height of view in pixels
|
|
BOOL *pfEnabled) //@parm Whether vertical scroll bar is enabled.
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetVScroll");
|
|
|
|
if(plMin)
|
|
*plMin = 0;
|
|
|
|
if (IsUVerticalTflow(_pdp->GetTflow()))
|
|
{
|
|
if(plMax)
|
|
*plMax = _pdp->GetScrollRange(SB_HORZ);
|
|
|
|
if(plPos)
|
|
*plPos = _pdp->GetUpScroll();
|
|
|
|
if(plPage)
|
|
*plPage = _pdp->GetDupView();
|
|
|
|
if(pfEnabled)
|
|
*pfEnabled = _fInPlaceActive ? _pdp->IsUScrollEnabled() : 0;
|
|
}
|
|
else
|
|
{
|
|
if(plMax)
|
|
*plMax = _pdp->GetScrollRange(SB_VERT);
|
|
|
|
if(plPos)
|
|
*plPos = _pdp->ConvertVPosToScrollPos(_pdp->GetVpScroll());
|
|
|
|
if(plPage)
|
|
*plPage = _pdp->ConvertVPosToScrollPos(_pdp->GetDvpView());
|
|
|
|
if(pfEnabled)
|
|
*pfEnabled = _pdp->IsVScrollEnabled();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::OnTxSetCursor (dwDrawAspect, lindex, pvAspect, ptd, hdcDraw,
|
|
* hicTargetDev, lprcClient, x, y)
|
|
* @mfunc
|
|
* Notification for text services to set the cursor
|
|
*
|
|
* @rdesc
|
|
* HRESULT = FAILED(RectChangeHelper()) ? E_INVALIDARG : S_OK
|
|
*
|
|
* @comm
|
|
* Text Services may remeasure as a result of this call in
|
|
* order to determine the correct cursor. The correct
|
|
* cursor will be set via ITextHost::TxSetCursor
|
|
*
|
|
* More details:
|
|
*
|
|
* The lprcClient parameter is the client rectangle of the view of the
|
|
* control over which the mouse cursor is. It is in device coordinates
|
|
* of the containing window in the same way the WM_SIZE message is. This
|
|
* may not be the view that was rendered last. Furthermore, if the control
|
|
* is in-place active, this may not be the view currently being active.
|
|
* As a consequence, Text Services should check this rectangle against
|
|
* its current caches values and determine whether recalcing the lines
|
|
* is necessary or not. The zoom factor should be included in this
|
|
* computation.
|
|
*
|
|
* This method should only be called for screen views of the control.
|
|
* Therefore the DC is not passed in but should be assumed to be a screen
|
|
* DC.
|
|
*
|
|
* The x and y parameters hold the cursor position in the same coordinate
|
|
* system as lprcClient, i.e., the client coordinates of the containing
|
|
* window.
|
|
*/
|
|
//REVIEW (keithcu) Do people really pass rectangles different from those
|
|
//returned from TxGetClientRect? I'd be surprised if that happens, and if it did
|
|
//who cares if we display the wrong cursor??
|
|
HRESULT CTxtEdit::OnTxSetCursor (
|
|
DWORD dwDrawAspect, //@parm Draw aspect
|
|
LONG lindex, //@parm Currently unused
|
|
void * pvAspect, //@parm Info for drawing optimizations (OCX 96)
|
|
DVTARGETDEVICE *ptd, //@parm Info on target device
|
|
HDC hdcDraw, //@parm Rendering device context
|
|
HDC hicTargetDev, //@parm Target information context
|
|
LPCRECT lprcClient, //@parm Control's client rectangle
|
|
INT x, //@parm x position of cursor
|
|
INT y) //@parm y position of cursor
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::OnTxSetCursor");
|
|
|
|
CCallMgr callmgr(this);
|
|
BOOL fText = FALSE;
|
|
HITTEST Hit;
|
|
RECT rcxyClient;
|
|
BOOL fGotDC = FALSE;
|
|
START_PROFILING
|
|
|
|
if (_fInPlaceActive)
|
|
TxGetClientRect(&rcxyClient);
|
|
else if (lprcClient)
|
|
rcxyClient = *lprcClient;
|
|
else
|
|
return E_INVALIDARG;
|
|
|
|
if (!_pdp->IsValid())
|
|
{
|
|
HDC hdc = GetDC(NULL);
|
|
_pdp->SetDC(hdc);
|
|
fGotDC = TRUE;
|
|
}
|
|
|
|
// Set the cursor
|
|
CTxtSelection * const psel = GetSel();
|
|
HCURSOR hcurNew = _hcurArrow; // Default using system arrow
|
|
POINT ptxy = {x, y};
|
|
POINTUV pt;
|
|
RECTUV rcClient;
|
|
BOOL fInLink = FALSE;
|
|
|
|
_pdp->RectuvFromRect(rcClient, rcxyClient);
|
|
_pdp->PointuvFromPoint(pt, ptxy);
|
|
|
|
if(PtInRect(&rcxyClient, ptxy))
|
|
{
|
|
// Find out what the cursor is pointing at
|
|
_pdp->CpFromPoint(pt, &rcClient, NULL, NULL, FALSE, &Hit);
|
|
|
|
if(Hit == HT_LeftOfText)
|
|
hcurNew = _hcurSelBar;
|
|
|
|
// This is a bit strange, but RichEdit 1.0 does this--give client a
|
|
// chance to handle cursor itself if we are over a link.
|
|
else if(Hit == HT_Link)
|
|
{
|
|
if(HandleLinkNotification(WM_SETCURSOR, 0, MAKELPARAM(ptxy.x, ptxy.y), &fInLink))
|
|
{
|
|
return NOERROR;
|
|
}
|
|
hcurNew = _hcurHand;
|
|
}
|
|
else if(Hit != HT_Nothing && Hit != HT_OutlineSymbol && Hit != HT_BulletArea)
|
|
{
|
|
if(!psel || !psel->PointInSel(pt, &rcClient, Hit) || _fDisableDrag)
|
|
{
|
|
if (IsUVerticalTflow(_pdp->GetTflow()))
|
|
hcurNew = (Hit == HT_Italic) ? _hcurVItalic : _hcurVIBeam;
|
|
else
|
|
hcurNew = (Hit == HT_Italic) ? _hcurItalic : _hcurIBeam;
|
|
fText = TRUE;
|
|
}
|
|
|
|
// If we have an object manager and if there is a selected object,
|
|
// check for hits on the frame handles.
|
|
if(_pobjmgr)
|
|
{
|
|
COleObject *pobjselect = _pobjmgr->GetSingleSelect();
|
|
if(pobjselect)
|
|
{
|
|
// Handle hits on frame handles.
|
|
LPTSTR idcur = pobjselect->CheckForHandleHit(pt);
|
|
HCURSOR hcurObj = W32->GetSizeCursor(idcur);
|
|
if(hcurObj)
|
|
hcurNew = hcurObj;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_phost->TxSetCursor(hcurNew, fText); // Tell host to set cursor
|
|
|
|
if (fGotDC)
|
|
{
|
|
HDC hdc = _pdp->GetDC();
|
|
::ReleaseDC(NULL, hdc);
|
|
_pdp->SetDC(NULL);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxQueryHitPoint (dwDrawAspect, lindex, pvAspect, ptd, hdcDraw,
|
|
* hicTargetDev, lprcClient, x, y, pHitResult)
|
|
* @mfunc
|
|
* Returns whether point is within text services rectangle
|
|
*
|
|
* @rdesc
|
|
* HRESULT
|
|
*
|
|
* @comm
|
|
* This method allows the host to implement transparent hit-testing
|
|
* on text.
|
|
*
|
|
* The lprcClient parameter is the client rectangle in device coordinates
|
|
* of the view on which hit testing is performed.
|
|
*
|
|
* The pt parameter hold the position of the cursor in the same
|
|
* coordinate system as the lprcClient rectangle (the client
|
|
* coordinates of the containing window).
|
|
*
|
|
* Same general comments about client rectangle and DC as for
|
|
* TxSetCursor apply.
|
|
*
|
|
* pHitResult returns one of the following values: <nl>
|
|
*
|
|
* TXTHITRESULT_NOHIT Hit was outside client rectangle. <nl>
|
|
* TXTHITRESULT_HIT Point was inside client rectangle and over
|
|
* either text or an opaque background.
|
|
* TXTHITRESULT_TRANSPARENT Point was inside client rectangle with a
|
|
* transparent background and not over text.
|
|
* TXTHITRESULT_CLOSE Hit was close to an opaque area.
|
|
*
|
|
* Refer to the Windowless OLE Control spec for more details on
|
|
* these return values and how they should be determined.
|
|
*/
|
|
HRESULT CTxtEdit::TxQueryHitPoint(
|
|
DWORD dwDrawAspect, //@parm Draw aspect
|
|
LONG lindex, //@parm Currently unused
|
|
void * pvAspect, //@parm Info for drawing optimizations (OCX 96)
|
|
DVTARGETDEVICE *ptd, //@parm Info on target device
|
|
HDC hdcDraw, //@parm Rendering device context
|
|
HDC hicTargetDev, //@parm Target information context
|
|
LPCRECT lprcClient, //@parm Control's client rectangle
|
|
INT x, //@parm x coordinate to check
|
|
INT y, //@parm y coordinate to check
|
|
DWORD * pHitResult) //@parm Result of hit test see TXTHITRESULT
|
|
// enumeration for valid values
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxQueryHitPoint");
|
|
|
|
RECTUV rcClient;
|
|
RECT rcxyClient;
|
|
CCallMgr callmgr(this);
|
|
|
|
START_PROFILING
|
|
|
|
if (_fInPlaceActive)
|
|
TxGetClientRect(&rcxyClient);
|
|
else if (lprcClient)
|
|
rcxyClient = *lprcClient;
|
|
else
|
|
return E_INVALIDARG;
|
|
|
|
HRESULT hr;
|
|
POINT ptxy = {x, y};
|
|
POINTUV pt;
|
|
|
|
_pdp->PointuvFromPoint(pt, ptxy);
|
|
_pdp->RectuvFromRect(rcClient, rcxyClient);
|
|
|
|
if(!_fTransparent)
|
|
{
|
|
*pHitResult = TXTHITRESULT_HIT;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = _pdp->TransparentHitTest(hdcDraw, &rcxyClient, pt, pHitResult);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::OnTxInPlaceActivate (prcClient)
|
|
*
|
|
* @mfunc
|
|
* Notifies text services that this control is inplace active
|
|
*
|
|
* @rdesc
|
|
* S_OK - successfully activated object <nl>
|
|
* E_FAIL - could not activate object due to error. <nl>
|
|
*
|
|
* @comm
|
|
* When transitioning directly from a non-active state to the UI-active
|
|
* state, the host should call OnTxInPlaceActivate first and then
|
|
* OnTxUIActivate. Similarly, when transitioning from the UI-active
|
|
* state to a non active state, the host should call OnTxUIDeactivate
|
|
* first and then OnTxInPlaceDeactivate.
|
|
*
|
|
* OnTxInPlaceActivate takes the client rectangle of the view being
|
|
* activated as a parameter. This rectangle is given in client coordinate
|
|
* of the containing window. It is the same as would be obtained by
|
|
* calling TxGetClientRect on the host.
|
|
*
|
|
* UI-activation is different from getting the focus. To let Text
|
|
* Services know that the control is getting or losing focus, the
|
|
* host will send WM_SETFOCUS and WM_KILLFOCUS messages. Note that a
|
|
* windowless host will pass NULL as the wParam (window that lost the
|
|
* focus) for these messages.
|
|
*
|
|
* As a reminder, inplace activation typically refers to an embedded
|
|
* object "running inplace" (for regular controls && embeddings, it
|
|
* would have a window to draw in, for example). UI active means that
|
|
* an object currently has the 'editing focus'. Specifically, things
|
|
* like menus and toolbars on the container may also contain elements
|
|
* from the UI active control/embedding. There can only be one
|
|
* UI active control at any given time, while many can be inplace active
|
|
* at once.
|
|
*/
|
|
HRESULT CTxtEdit::OnTxInPlaceActivate(
|
|
const RECT *prcClient) //@parm Control's client rectangle
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::OnTxInPlaceActivate");
|
|
|
|
RECTUV rcView;
|
|
HDC hdc;
|
|
BOOL fSucceeded = TRUE;
|
|
CCallMgr callmgr(this);
|
|
RECTUV rcClient;
|
|
|
|
START_PROFILING
|
|
|
|
// Needs to set here for further TxGetDC() to work
|
|
_fInPlaceActive = TRUE;
|
|
|
|
// Set rendering DC to be the screen
|
|
hdc = TxGetDC();
|
|
if(!hdc)
|
|
goto err;
|
|
|
|
// Tell display that it is active
|
|
_pdp->SetActiveFlag(TRUE);
|
|
_pdp->SetDC(hdc);
|
|
|
|
//REVIEW (keithcu) I removed prcClient usage here because it appears that
|
|
//clients pass in wrong rectangles to OnTxInPlaceActivate. That is quite
|
|
//scary--do they pass in wrong rectangles to other places? (We could see
|
|
//weird results when not in-place active. If (as the comment says) the prcClient
|
|
//should be the same as what would be gotten from the host, we will now just fetch
|
|
//the value from the host rather than look at what is passed down.
|
|
#if 1
|
|
prcClient = 0;
|
|
#else
|
|
|
|
if (prcClient)
|
|
_pdp->RectuvFromRect(rcClient, *prcClient);
|
|
|
|
//Verify the rectangle passed in is the same as what we'd fetch from
|
|
//getting it from the client
|
|
#ifdef DEBUG
|
|
{
|
|
RECT rcDebug;
|
|
TxGetClientRect(&rcDebug);
|
|
AssertSz(rcDebug.right - rcDebug.left == prcClient->right - prcClient->left &&
|
|
rcDebug.bottom - rcDebug.top == prcClient->bottom - prcClient->top,
|
|
"CLIENT BUG: Inconsistent rectangles between ITextServices::"
|
|
"OnTxInPlaceActivate(RECT*) and ITextHost::TxGetClientRect(RECT*)");
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Compute view rect from passed in client rect
|
|
_pdp->GetViewRect(rcView, prcClient ? &rcClient : 0);
|
|
|
|
// Recalc/update view
|
|
_pdp->RecalcView(rcView);
|
|
|
|
// Get selection. Otherwise, if SetFocus is called later without
|
|
// selection, then the Selection state is not set correctly.
|
|
GetSel();
|
|
if(_pdp->GetDupView())
|
|
{
|
|
// Get selection if we can
|
|
if(_psel) // Set the caret
|
|
_psel->Update(FALSE);
|
|
else // Couldn't create selection,
|
|
fSucceeded = FALSE; // so fail activation
|
|
}
|
|
|
|
// Release the DC
|
|
TxReleaseDC(hdc);
|
|
_pdp->SetDC(NULL);
|
|
|
|
// If getting the selection worked we are home free
|
|
if(fSucceeded)
|
|
return S_OK;
|
|
|
|
err:
|
|
_fInPlaceActive = FALSE;
|
|
return E_FAIL;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::OnTxInPlaceDeactivate()
|
|
*
|
|
* @mfunc Notifies text services that this is no longer in place active.
|
|
*
|
|
* @rdesc S_OK
|
|
*
|
|
* @comm See OnTxInPlaceActivate for a detailed description of
|
|
* activation/deactivation.
|
|
*
|
|
* @xref <mf CTxtEdit::OnTxInPlaceActivate>
|
|
*
|
|
*/
|
|
HRESULT CTxtEdit::OnTxInPlaceDeactivate()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::OnTxInPlaceDeactivate");
|
|
|
|
START_PROFILING
|
|
|
|
// Get properties that affect whether we will discard the selection
|
|
DWORD dwBits;
|
|
|
|
// Tell display that it is not longer active
|
|
_pdp->SetActiveFlag(FALSE);
|
|
|
|
// Because we are inactive, this will tell any background recalc going
|
|
// on to stop.
|
|
_pdp->StepBackgroundRecalc();
|
|
|
|
_phost->TxGetPropertyBits(TXTBIT_HIDESELECTION | TXTBIT_SAVESELECTION,
|
|
&dwBits);
|
|
|
|
// If we don't want to save the selection and we want to hide it while
|
|
// inactive, then we discard our selection
|
|
if(!(dwBits & TXTBIT_SAVESELECTION) && (dwBits & TXTBIT_HIDESELECTION))
|
|
DiscardSelection();
|
|
|
|
_fInPlaceActive = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::OnTxUIActivate()
|
|
*
|
|
* @mfunc Informs text services that the control is now UI active.
|
|
*
|
|
* @rdesc S_OK
|
|
*
|
|
* @comm See OnTxInPlaceActivate for a detailed description of
|
|
* activation/deactivation.
|
|
*
|
|
* @xref <mf CTxtEdit::OnTxInPlaceActivate>
|
|
*
|
|
*
|
|
*/
|
|
HRESULT CTxtEdit::OnTxUIActivate()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::OnTxUIActivate");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::OnTxUIDeactivate()
|
|
*
|
|
* @mfunc Informs text services that the control is now UI deactive.
|
|
*
|
|
* @rdesc S_OK
|
|
*
|
|
* @comm See OnTxInPlaceActivate for a detailed description of
|
|
* activation/deactivation.
|
|
*
|
|
* @xref <mf CTxtEdit::OnTxInPlaceActivate>
|
|
*
|
|
*/
|
|
HRESULT CTxtEdit::OnTxUIDeactivate()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::OnTxUIDeactivate");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetText (pbstrText)
|
|
*
|
|
* @mfunc Returns all of the UNICODE plain text in the control as an
|
|
* OLE BSTR.
|
|
*
|
|
* @rdesc
|
|
* S_OK - Text successfully returned in the output argument <nl>
|
|
* E_INVALIDARG - invalid BSTR pointer passed in. <nl>
|
|
* E_OUTOFMEMORY - could not allocate memory for copy of the text <nl>
|
|
*
|
|
* @comm The caller takes ownership of the returned BSTR. WM_GETTEXT
|
|
* and TOM ITextRange::GetText are alternate techniques for
|
|
* retrieving plain text data.
|
|
*
|
|
* If there is no text in the control, no BSTR will be allocated
|
|
* and NULL will be returned.
|
|
*
|
|
* The returned text will NOT necessarily be NULL terminated.
|
|
*/
|
|
HRESULT CTxtEdit::TxGetText(
|
|
BSTR *pbstrText ) //@parm where to return an allocated BSTR
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetText");
|
|
|
|
CTxtPtr tp(this, 0);
|
|
CCallMgr callmgr(this);
|
|
|
|
START_PROFILING
|
|
|
|
if(!pbstrText)
|
|
return E_INVALIDARG;
|
|
|
|
const LONG cch = GetTextLength();
|
|
|
|
if(cch <= 0)
|
|
{
|
|
*pbstrText = 0;
|
|
return S_OK;
|
|
}
|
|
*pbstrText = SysAllocStringLen(NULL, cch);
|
|
if(!*pbstrText)
|
|
{
|
|
GetCallMgr()->SetOutOfMemory();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
tp.GetText(cch, *pbstrText);
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxSetText (pszText)
|
|
*
|
|
* @mfunc Sets all of the text in the control
|
|
*
|
|
* @rdesc
|
|
* S_OK - text was successfully set <nl>
|
|
* E_FAIL - text could not be updated. <nl>
|
|
*
|
|
* @comm
|
|
* This method should be used with care; it essentially re-initializes
|
|
* the text engine with some new data; any previous data and formatting
|
|
* information will be LOST, including undo information.
|
|
*
|
|
* If previous data has been copied to the clipboard, that data will be
|
|
* rendered completely to the clipboard (via OleFlushClipboard) before
|
|
* it is discarded.
|
|
*
|
|
* This method is NOT undo-able.
|
|
*
|
|
* Two alternate approaches to setting text are WM_SETTEXT and TOM
|
|
* ITextRange::SetText.
|
|
*
|
|
* @xref
|
|
* <mf CTxtRange::SetText>
|
|
*/
|
|
HRESULT CTxtEdit::TxSetText(
|
|
LPCTSTR pszText) //@parm String to replace the current text with
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxSetText");
|
|
|
|
START_PROFILING
|
|
|
|
return SetText(pszText, ST_CHECKPROTECTION, 1200);
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetCurTargetX (px)
|
|
*
|
|
* @mfunc
|
|
* Get the target x position of the caret
|
|
*
|
|
* @rdesc
|
|
* HRESULT with possible values:
|
|
*
|
|
* S_OK - x position of the caret returned <nl>
|
|
* E_FAIL - There is no selection <nl>
|
|
* E_INVALIDARG - Input argument is invalid <nl>
|
|
*
|
|
* @comm
|
|
* This method is useful for implementing up-down cursoring
|
|
* through a conceptual vertical line. To illustrate this feature,
|
|
* consider setting the insertion point at, say, column 20 in a
|
|
* text editor. Now cursor up and down--notice that wherever possible,
|
|
* the editor tries to put the insertion point as close to column 20
|
|
* as it can for the current line. Column 20 is thus the "target" column
|
|
* for the insertion point.
|
|
*
|
|
* Users would like to have this same capability when cursoring through
|
|
* Forms; however, as other controls don't necessarily share the same
|
|
* notion of column position, the target caret position is expressed simply
|
|
* as an x-coordinate on the display (in *client* coordinates).
|
|
*/
|
|
HRESULT CTxtEdit::TxGetCurTargetX(
|
|
LONG *px) //@parm the x location in client coordinates
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetCurTargetX");
|
|
|
|
START_PROFILING
|
|
CTxtSelection *psel = GetSel();
|
|
|
|
if(!psel)
|
|
return E_FAIL;
|
|
|
|
if(!px)
|
|
return E_INVALIDARG;
|
|
|
|
*px = psel->GetUpCaretReally();
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetBaseLinePos(pBaseLinePos)
|
|
*
|
|
* @mfunc Get the base line position of the first visible line, in pixels,
|
|
* relative the TS client rectangle. Needed for aligning controls on their
|
|
* baselines.
|
|
*
|
|
* @rdesc HRESULT = E_NOTIMPL
|
|
*/
|
|
HRESULT CTxtEdit::TxGetBaseLinePos(
|
|
LONG *pBaseLinePos) //@parm Where to return baseline position
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetBaseLinePos");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetNaturalSize (dwAspect, hdcDraw, hicTargetDev, ptd, dwMode,
|
|
* psizelExtent, pwidth, pheight)
|
|
*
|
|
* @mfunc Allow control to be resized so it fits content appropriately
|
|
*
|
|
* @rdesc S_OK <nl>
|
|
* E_INVALIDARG <nl>
|
|
* E_FAIL Unable to determine correct size <nl>
|
|
* E_OUTOFMEMORY <nl>
|
|
*
|
|
* @comm
|
|
*
|
|
* The first 4 parameters are similar to equivalent parameters in
|
|
* TxDraw and give the same information. In the case TS needs to
|
|
* recalc lines, it should use these values the same ways as in
|
|
* TxDraw.
|
|
*
|
|
* <p pWidth> and <p pHeight> are IN/OUT parameters. The host passes the
|
|
* "tentative" width and height of the client rectangle for the text object
|
|
* and Text Services will compare these values against its current cached
|
|
* state, and if different should recalc lines. Then it will compute
|
|
* and return the natural size. As spec-ed currently, the host can ask for 2
|
|
* different kinds of natural sizes:
|
|
*
|
|
* TXTNS_FITTOCONTENT: the entire text should be formatted to the
|
|
* width that is passed in.Then Text Services return the height of
|
|
* the entire text and the width of the widest line. Note that this
|
|
* option ignores any paragraph formatting such as centering and
|
|
* only returns the raw size for the text.
|
|
*
|
|
* TXTNS_ROUNDTOLINE: returns the integral height of the number of lines that
|
|
* will fit in the input height rounded to the next full line boundary.
|
|
*
|
|
* Note that passed and returned width and height correspond to
|
|
* the *client* rectangle in client units.
|
|
*
|
|
*
|
|
* BACKGROUND
|
|
* Here is a quick description of the features mentioned above:
|
|
*
|
|
* FITTOCONTEXT: Normally happens when the user double clicks one
|
|
* of the control grab handles. Sizes the control to the "optimal"
|
|
* size to fit the entire content. Should accomodate the height of
|
|
* the entire text and the width of the widest line.
|
|
*
|
|
* ROUNDTOLINE (Integral height): if this property is set, when the
|
|
* user resizes the control, it snaps to heights that allow an
|
|
* integral number of lines to be displayed (no line will be clipped).
|
|
*/
|
|
HRESULT CTxtEdit::TxGetNaturalSize(
|
|
DWORD dwAspect, //@parm Aspect for drawing. Values taken from OLE's
|
|
// DVASPECT enumeration
|
|
HDC hdcDraw, //@parm DC into which drawing would occur
|
|
HDC hicTargetDev, //@parm DC for which text should be formatted, i.e.,
|
|
// for WYSIWYG
|
|
DVTARGETDEVICE *ptd,//@parm More info on the target device
|
|
DWORD dwMode, //@parm Type of fitting requested. Either
|
|
// TXTNS_FITTOCONTENT or TXTNS_ROUNDTOLINE
|
|
const SIZEL *psizelExtent,//@parm Size of extent to use for zooming
|
|
LONG * pwidth, //@parm Width for such a fitting [in,out]
|
|
LONG * pheight) //@parm Height for such a fitting [in,out]
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetNaturalSize");
|
|
|
|
HRESULT hr;
|
|
CCallMgr callmgr(this);
|
|
|
|
START_PROFILING
|
|
|
|
if(dwAspect != DVASPECT_CONTENT && dwAspect != DVASPECT_DOCPRINT)
|
|
{
|
|
// We don't support the aspect requested
|
|
return DV_E_DVASPECT;
|
|
}
|
|
|
|
if (hicTargetDev && !ptd || !pwidth || !pheight ||
|
|
!IN_RANGE(TXTNS_FITTOCONTENT2, dwMode, TXTNS_ROUNDTOLINE))
|
|
{
|
|
// Either and information context is provided without the device
|
|
// target or the mode is not valid or the width was not provided
|
|
// or the height was not provided. In short, the input parameters
|
|
// are not valid so tell the caller.
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if(!psizelExtent->cy)
|
|
{
|
|
// No extent for control, so just return 0
|
|
*pwidth = 0;
|
|
*pheight = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HDC hicLocal = NULL;
|
|
|
|
// Did they give us a ptd without a hic?
|
|
if(!hicTargetDev && ptd)
|
|
{
|
|
// Create and information context for the device information
|
|
// since it wasn't supplied.
|
|
hicLocal = CreateIC(
|
|
(TCHAR *)((BYTE *) ptd + ptd->tdDriverNameOffset),
|
|
(TCHAR *)((BYTE *) ptd + ptd->tdDeviceNameOffset),
|
|
(TCHAR *)((BYTE *) ptd + ptd->tdPortNameOffset),
|
|
(DEVMODE *)((BYTE *) ptd + ptd->tdExtDevmodeOffset));
|
|
|
|
if(!hicLocal)
|
|
return E_FAIL; // Couldn't create it
|
|
|
|
hicTargetDev = hicLocal;
|
|
}
|
|
|
|
// Convenient place to put height & width for converting them to
|
|
// device units.
|
|
POINT pt;
|
|
pt.x = *pwidth;
|
|
pt.y = *pheight;
|
|
|
|
AssertSz(GetMapMode(hdcDraw) == MM_TEXT || GetDeviceCaps(hdcDraw, TECHNOLOGY) == DT_METAFILE,
|
|
"RichEdit requires MM_TEXT."); // REVIEW (keithcu) Clients do (and should) use MM_TEXT
|
|
|
|
// Set the extent information needed for zooming
|
|
_pdp->SetTempZoomDenominator(psizelExtent->cy);
|
|
|
|
if(TXTNS_ROUNDTOLINE == dwMode)
|
|
{
|
|
// Round to fit simply calculates the
|
|
hr = _pdp->RoundToLine(hdcDraw, pt.x, &pt.y);
|
|
}
|
|
else
|
|
{
|
|
// Get natural size for entire presentation
|
|
// Allocate memory for the draw information
|
|
CDrawInfo di(this);
|
|
|
|
// Set up the drawing parameters
|
|
_pdp->SetDrawInfo(
|
|
&di,
|
|
dwAspect,
|
|
-1,
|
|
NULL,
|
|
ptd,
|
|
hicTargetDev);
|
|
|
|
// Set the Draw DC
|
|
_pdp->SetDC(hdcDraw);
|
|
|
|
// Tell display to figure size needed for this display
|
|
hr = _pdp->GetNaturalSize(hdcDraw, hicTargetDev, dwMode, &pt.x, &pt.y);
|
|
|
|
_pdp->ResetDC(); // Restore state
|
|
_pdp->ReleaseDrawInfo();
|
|
}
|
|
|
|
if(SUCCEEDED(hr)) // Set return values if this worked
|
|
{
|
|
*pwidth = pt.x; // Update return values
|
|
*pheight = pt.y;
|
|
}
|
|
|
|
if(hicLocal) // Clean up info context
|
|
DeleteDC(hicLocal); // if we created one
|
|
|
|
_pdp->ResetTempZoomDenominator(); // Reset temporary zoom factor
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetDropTarget (ppDropTarget)
|
|
*
|
|
* @mfunc Get the drop target for the text control
|
|
*
|
|
* @rdesc
|
|
* S_OK - Got drop target successfully <nl>
|
|
* E_OUTOFMEMORY - Could not create drop target <nl>
|
|
*
|
|
* @comm
|
|
* The caller (host) is responsible for calling Register/Revoke
|
|
* DragDrop and for calling IUnknown::Release on the returned
|
|
* drop target when done.
|
|
*/
|
|
HRESULT CTxtEdit::TxGetDropTarget(
|
|
IDropTarget **ppDropTarget) //@parm Where to put pointer to drop target
|
|
{
|
|
#ifndef NODRAGDROP
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetDropTarget");
|
|
|
|
HRESULT hr;
|
|
CCallMgr callmgr(this);
|
|
|
|
START_PROFILING
|
|
|
|
hr = _ldte.GetDropTarget(ppDropTarget);
|
|
if(hr == NOERROR)
|
|
(*ppDropTarget)->AddRef();
|
|
return hr;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::OnTxPropertyBitsChange (dwMask, dwBits)
|
|
*
|
|
* @mfunc Set properties that can be represented by bits.
|
|
*
|
|
* @rdesc HRESULT
|
|
*
|
|
* @comm The following property bits are understood: <nl>
|
|
*
|
|
* TXTBIT_RICHTEXT <nl>
|
|
* TXTBIT_MULTILINE <nl>
|
|
* TXTBIT_READONLY <nl>
|
|
* TXTBIT_SHOWACCELERATOR <nl>
|
|
* TXTBIT_USEPASSWORD <nl>
|
|
* TXTBIT_HIDESELECTION <nl>
|
|
* TXTBIT_SAVESELECTION <nl>
|
|
* TXTBIT_AUTOWORDSEL <nl>
|
|
* TXTBIT_AUTOSIZE <nl>
|
|
* TXTBIT_VERTICAL <nl>
|
|
* TXTBIT_SELECTIONBAR <nl>
|
|
* TXTBIT_WORDWRAP <nl>
|
|
*
|
|
* TXTBIT_CLIENTRECTCHANGE <nl>
|
|
* TXTBIT_VIEWINSETCHANGE <nl>
|
|
* TXTBIT_BACKSTYLECHANGE <nl>
|
|
* TXTBIT_MAXLENGTHCHANGE <nl>
|
|
* TXTBIT_SCROLLBARCHANGE <nl>
|
|
* TXTBIT_CHARFORMATCHANGE <nl>
|
|
* TXTBIT_PARAFORMATCHANGE <nl>
|
|
* TXTBIT_ALLOWBEEP <nl>
|
|
* TXTBIT_EXTENTCHANGE <nl>
|
|
*
|
|
* A brief description of each property follows:
|
|
*
|
|
*
|
|
* Client rectangle (TXTBIT_CLIENTRECTCHANGE):
|
|
*
|
|
* The rectangle the Text Services are responsible for painting
|
|
* and managing. The host will rely on the Text Services for painting
|
|
* that area. Text Services must not paint or invalidate areas outside of
|
|
* that rectangle.
|
|
*
|
|
* The host will forward mouse messages to the Text Services whenever the
|
|
* cursor is over this rectangle.
|
|
*
|
|
* This rectangle is expressed in client coordinates of the containing window.
|
|
*
|
|
* IMPORTANT: this property cannot be queried from the host when it is
|
|
* inactive. The TxGetClientRect method will fail if called at inactive time.
|
|
*
|
|
*
|
|
* View inset (TXTBIT_VIEWINSETCHANGE):
|
|
*
|
|
* This is the amount of space on each side between the client rectangle and
|
|
* the view rectangle. The view rectangle (also called Formating rectangle)
|
|
* is the rectangle the text should be formatted in.
|
|
*
|
|
* The view insets are is passed in a RECT structure but this is not really
|
|
* a rectangle. It should be treated as 4 independent values to substract
|
|
* on each side of the client rectangle to figure the view rectangle.
|
|
*
|
|
* The view insets are passed in himetrics so that they do not depend on
|
|
* the client rectangle and the rendering DC.
|
|
*
|
|
* View insets can be negative on either side of the client rectangle,
|
|
* leading to a bigger view rectangle than the client rectangle. The text
|
|
* should then be clipped to the client rectangle. If the view rectangle
|
|
* is wider than the client rectangle, then the host may add a horizontal
|
|
* scrollbar to the control.
|
|
*
|
|
* Single line Text Services ignore the right boundary of the view rectangle
|
|
* when formatting text.
|
|
*
|
|
* The view inset is available from the host at all times, active or
|
|
* inactive.
|
|
*
|
|
*
|
|
* Backstyle (TXTBIT_BACKSTYLECHANGE):
|
|
*
|
|
* The style of the background of the client rectangle. Can be either of
|
|
* the following values: <nl>
|
|
* #define TXTBACK_TRANSPARENT 0 <nl>
|
|
* #define TXTBACK_SOLID 1 <nl>
|
|
*
|
|
* Values for this property are similar to VB4 values for the same property.
|
|
*
|
|
*
|
|
* MaxLength (TXTBIT_MAXLENGTHCHANGE):
|
|
*
|
|
* The maximum length the text can have. Text Services should reject
|
|
* character insertion and pasted text when this maximum is reached.
|
|
* TxSetText however should still accept (and set) text longer than the
|
|
* maximum length. This is because this method is used for binding and
|
|
* it is critical to maintain the integrity of the data the control
|
|
* is bound to.
|
|
*
|
|
*
|
|
* Scrollbar (TXTBIT_SCROLLBARCHANGE):
|
|
*
|
|
* This property indicates which scollbar is present and whether scollbars
|
|
* are hidden or disabled when scrolling is impossible. It also controls
|
|
* auto-scrolling when the insertion point gets off the client rectangle.
|
|
*
|
|
* This is a DWORD where bits are layed out as in the system window style.
|
|
* Possible bits are:
|
|
* WS_HSCROLL // control has horizontal scrollbar <nl>
|
|
* WS_VSCROLL // control has vertical scrollbar <nl>
|
|
* ES_AUTOVSCROLL // auto-scroll horizontally <nl>
|
|
* ES_AUTOVSCROLL // auto-scroll vertically <nl>
|
|
* ES_DISABLENOSCROLL // scrollbar should be disabled when scrolling
|
|
* impossible <nl>
|
|
*
|
|
* Default CHARFORMAT (TXTBIT_CHARFORMATCHANGE):
|
|
*
|
|
* The CHARFORMAT or CHARFORMAT2 used for default character-format runs,
|
|
* i.e., those not explicitly formatted via the selection or TOM methods.
|
|
|
|
*
|
|
* Default PARAFORMAT (TXTBIT_PARAFORMATCHANGE):
|
|
*
|
|
* The PARAFORMAT or PARAFORMAT2 used for default paragraph-format runs,
|
|
* i.e., those not explicitly formatted via the selection or TOM methods.
|
|
*
|
|
*
|
|
* TXTBIT_ALLOWBEEP:
|
|
*
|
|
* TXTBIT_EXTENTCHANGE:
|
|
*
|
|
*
|
|
* TXTBIT_RICHTEXT:
|
|
*
|
|
* Whether the Text Services should be in Rich-Text mode or not. This
|
|
* principally affects how editing commands are applied. For example,
|
|
* applying bold to some text in a plain edit control makes all of the
|
|
* text bold, rather than just the selected text in a rich text control.
|
|
*
|
|
* Note that if there is either undo state or the object has any text,
|
|
* the attempt to change this bit will be ignored.
|
|
*
|
|
*
|
|
* TXTBIT_MULTILINE:
|
|
*
|
|
* If this property is FALSE, Text Services should not process the CR
|
|
* key and truncate any incoming text containing hard line breaks just
|
|
* before the first line break. It is OK to also truncate text set via
|
|
* TxSetText (meaning, it is the responsibility of the host to not use a s
|
|
* single line control when bound to a multi-line field).
|
|
*
|
|
* If this property is TRUE, Text Services should work in multiline mode.
|
|
* The TXTBIT_WORDWRAP can be used to know whether to wrap the lines to
|
|
* the view rectangle or clip them.
|
|
*
|
|
*
|
|
* TXTBIT_READONLY:
|
|
*
|
|
* If this property is TRUE, Text Services should not accept any editing
|
|
* change via the user interface. However, they should still accept
|
|
* programmatic changes via EM_SETTEXT, EM_REPLACETEXT and TxSetText.
|
|
*
|
|
* In read only mode, the user should still be able to move the
|
|
* insertion point, select text and carry out other non content modifying
|
|
* operations such as Copy.
|
|
*
|
|
*
|
|
* TXTBIT_SHOWACCELERATOR:
|
|
*
|
|
* Refer to the "Accelerator" section for details about this property.
|
|
*
|
|
*
|
|
* TXTBIT_USEPASSWORD:
|
|
*
|
|
* If this property is TRUE, the Text Services should show the entire
|
|
* text using the character obtained by TxGetPasswordChar.
|
|
*
|
|
* The notification on this property may mean two different things:
|
|
* · The password character changed,
|
|
* · The password character was not used before and is used now
|
|
* (or vice versa).
|
|
*
|
|
*
|
|
* TXTBIT_HIDESELECTION:
|
|
*
|
|
* If this property is TRUE, Text Services should hide the selection
|
|
* when the control is inactive. If it is FALSE, the selection, if any,
|
|
* should still be displayed when the control is inactive.
|
|
*
|
|
* If TRUE, this property implies TXTBIT_SAVESELECTION = TRUE.
|
|
*
|
|
*
|
|
* TXTBIT_SAVESELECTION:
|
|
*
|
|
* If this property is TRUE, Text Services should remember the
|
|
* boundaries of the selection when the control goes inactive. If FALSE,
|
|
* it is not necessary to remember the selection when the control goes
|
|
* inactive. It can be reset to start = 0, length = 0 when the control
|
|
* goes active again.
|
|
*
|
|
* This property is used by hosts for which it is not necessary to
|
|
* remember the selection when inactive.
|
|
*
|
|
*
|
|
* TXTBIT_AUTOWORDSEL:
|
|
*
|
|
* This property turns the AutoWordSelect feature on or off.
|
|
*
|
|
*
|
|
* TXTBIT_AUTOSIZE:
|
|
*
|
|
* This property turns the AutoSize feature on or off. Refer to the
|
|
* "AutoSize" section for more details.
|
|
*
|
|
*
|
|
* TXTBIT_VERTICAL:
|
|
*
|
|
* This property turns on vertical writing. Used for FE support.
|
|
* Details TBD.
|
|
*
|
|
*
|
|
* TXTBIT_WORDWRAP:
|
|
*
|
|
* If this property is TRUE and MultiLine is also TRUE, then Text Services
|
|
* should wrap the line to the view rectangle. If this property is FALSE,
|
|
* the lines should not be wrapped but clipped. The right side of the
|
|
* view rectangle should be ignored.
|
|
*
|
|
* If the MultiLine property is off, this property has no effect.
|
|
*
|
|
*
|
|
* TXTBIT_LINESELECTION:
|
|
*
|
|
* This property turns on or off the Line Selection feature. This feature
|
|
* enable the user to select lines or paragraph by placing the mouse cursor
|
|
* over a "line selection" area on the left of the control. The cursor is
|
|
* displayed as a NE arrow in that area. If the Line Selection feature is
|
|
* off, that area should not be shown.
|
|
*
|
|
*/
|
|
HRESULT CTxtEdit::OnTxPropertyBitsChange(
|
|
DWORD dwMask, //@parm Bits representing properties to be changed
|
|
DWORD dwBits) //@parm New values for bit properties
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::OnTxPropertyBitsChange");
|
|
|
|
HRESULT hr = E_FAIL;
|
|
DWORD dwLoopMask = dwMask;
|
|
CCallMgr callmgr(this);
|
|
|
|
START_PROFILING
|
|
|
|
for (int i = 0; (i < MAX_PROPERTY_BITS) && (dwLoopMask != 0);
|
|
i++, dwLoopMask >>= 1)
|
|
{
|
|
if (dwLoopMask & 1)
|
|
{
|
|
hr = (this->*_fnpPropChg[i])((dwBits & (1 << i)) != 0);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*
|
|
* CTxtEdit::TxGetCachedSize (pdup, pdvp)
|
|
*
|
|
* @mfunc
|
|
* Returns the cached drawing size (if any) that text services
|
|
* is using. Typically, this will be the size of the last client
|
|
* rect used in TxDraw, TxSetCursor, etc., although it is not
|
|
* guaranteed to be.
|
|
*
|
|
* @rdesc
|
|
* HRESULT
|
|
*
|
|
* @comm
|
|
* This information is provided to allow the host to potentially
|
|
* perform various optimizations, amongst other things.
|
|
*/
|
|
HRESULT CTxtEdit::TxGetCachedSize(
|
|
DWORD *pdupClient, //@parm Where to put width (in client coords)
|
|
DWORD *pdvpClient) //@parm Where to put height (in client coords)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CTxtEdit::TxGetCachedSize");
|
|
|
|
return _pdp->GetCachedSize((long*) pdupClient, (long*) pdvpClient);
|
|
}
|
|
|
|
// Forward declaration of initialization helper.
|
|
// These really should be in some header file. But which?
|
|
// Currently the definitions are in dxfrobj.cpp and font.cpp
|
|
void RegisterFETCs();
|
|
void InitFontCache();
|
|
|
|
/*
|
|
* CreateTextServices (punkOuter, phost, ppserv)
|
|
*
|
|
* @func
|
|
* Create an instance of the RichEdit Engine. This engine supports the
|
|
* ITextServices and Microsoft Text Object Model (TOM) interfaces.
|
|
*
|
|
* @rdesc
|
|
* S_OK - New text services instance created successfully. <nl>
|
|
* E_INVALIDARG - An invalid argument was passed in. <nl>
|
|
* E_OUTOFMEMORY - Memory for text services object could not be allocated. <nl>
|
|
* E_FAIL - Text services could not be initialized
|
|
*
|
|
* @comm
|
|
* Text Services may be created as a standard OLE aggregated object.
|
|
* Callers should follow standard OLE32 rules for dealing with
|
|
* aggregated objects and caching interface pointers obtained via
|
|
* QueryInterface from the private IUnknown.
|
|
*/
|
|
STDAPI CreateTextServices(
|
|
IUnknown *punkOuter, //@parm Outer unknown, may be NULL
|
|
ITextHost *phost, //@parm Client's ITextHost implementation; must be
|
|
// valid
|
|
IUnknown **ppUnk) //@parm Private IUnknown of text services engine
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEEXTERN, "CreateTextServices");
|
|
|
|
static bool fOnce = FALSE;
|
|
|
|
if (!fOnce) {
|
|
CLock lock;
|
|
fOnce = TRUE;
|
|
W32->InitSysParams();
|
|
W32->InitPreferredFontInfo();
|
|
RegisterFETCs(); // Register new clipboard formats
|
|
CreateFormatCaches(); // Create global format caches
|
|
if ( !InitKinsokuClassify() )
|
|
{
|
|
// Init tables for classifying Unicode chars.
|
|
return E_FAIL;
|
|
}
|
|
InitFontCache();
|
|
}
|
|
|
|
if(!ppUnk)
|
|
return E_INVALIDARG;
|
|
|
|
CTxtEdit *ped = new CTxtEdit((ITextHost2*)phost, punkOuter);
|
|
if(!ped)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if(ped->Init(NULL))
|
|
{
|
|
*ppUnk = ped->GetPrivateIUnknown();
|
|
return S_OK;
|
|
}
|
|
delete ped;
|
|
return E_FAIL;
|
|
}
|