|
|
/*
* @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; }
|