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

696 lines
16 KiB
C++

/*
* @doc INTERNAL
*
* @module PROPCHG.CPP -- Property Change Notification Routines |
*
* Original Author: <nl>
* Rick Sailor
*
* History: <nl>
* 9/5/95 ricksa Created and documented
*
* Documentation is generated straight from the code. The following
* date/time stamp indicates the version of code from which the
* the documentation was generated.
*
* Copyright (c) 1995-1997 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 "_urlsup.h"
ASSERTDATA
CTxtEdit::FNPPROPCHG CTxtEdit::_fnpPropChg[MAX_PROPERTY_BITS];
/*
* CTxtEdit::UpdateAccelerator()
*
* @mfunc
* Get accelerator cp from host
*
* @rdesc
* HRESULT
*
* @devnote:
* The point of this is to leave the accelerator offset unchanged
* in the face of an error from the host.
*/
HRESULT CTxtEdit::UpdateAccelerator()
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::UpdateAccelerator");
LONG cpAccel;
HRESULT hr = _phost->TxGetAcceleratorPos(&cpAccel);
if(SUCCEEDED(hr))
{
// It worked so reset our value
AssertSz(cpAccel < 32768,
"CTxtEdit::UpdateAccelerator: cp too large");
_cpAccelerator = cpAccel;
}
return hr;
}
/*
* CTxtEdit::HandleRichToPlainConversion()
*
* @mfunc
* Convert a rich text object to a plain text object
*/
void CTxtEdit::HandleRichToPlainConversion()
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::HandleRichToPlainConversion");
// Notify every interested party that they should dump their formatting
_nm.NotifyPreReplaceRange(NULL, CONVERT_TO_PLAIN, 0, 0, 0, 0);
// Set _fRich to false so we can delete the final CRLF.
_fRich = 0;
_fSelChangeCharFormat = 0;
_fPageView = 0;
// if(_pdetecturl)
// {
// delete _pdetecturl;
// _pdetecturl = NULL;
// }
// Tell document to dump its format runs
_story.DeleteFormatRuns();
// Clear out the ending CRLF
CRchTxtPtr rtp(this, 0);
rtp.ReplaceRange(GetTextLength(), 0, NULL, NULL, -1);
}
/*
* CTxtEdit::OnRichEditChange (fPropertyFlag)
*
* @mfunc
* Notify text services that rich-text property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnRichEditChange(
BOOL fPropertyFlag) //@parm New state of richedit flag
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnRichEditChange");
// Calculate length of empty document. Remember that multiline rich text
// controls always have and end of paragraph marker.
LONG cchEmptyDoc = cchCR;
CFreezeDisplay fd(_pdp); // defer screen update until we finish the change.
if(!_fRich)
cchEmptyDoc = 0;
else if(_f10Mode)
cchEmptyDoc = cchCRLF;
// This can only be changed if there is no text and nothing to undo.
// It makes no sense to change when there is already text. This is
// particularly true of going from rich to plain. Further, what would
// you do with the undo state?
if(GetTextLength() == cchEmptyDoc && (!_pundo || !_pundo->CanUndo()))
{
#ifdef DEBUG
// Make sure that document is in a sensible state.
if(_fRich)
{
CTxtPtr tp(this, 0);
WCHAR szBuf[cchCRLF];
tp.GetText(cchCRLF, &szBuf[0]);
AssertSz(szBuf[0] == CR && (!_f10Mode || szBuf[1] == LF),
"CTxtEdit::OnRichEditChange: invalid document terminator");
}
#endif // DEBUG
if(_fRich && !fPropertyFlag)
{
// Going from rich text to plain text. Need to dump
// format runs.
HandleRichToPlainConversion();
_fAutoFontSizeAdjust = TRUE;
}
else if (!_fRich && fPropertyFlag)
{
// Going from plain text to rich text. Need to add the
// appropriate EOP at the end of the document.
SetRichDocEndEOP(0);
_fAutoFontSizeAdjust = FALSE;
}
_fRich = fPropertyFlag;
return S_OK;
}
return E_FAIL; // Flag was not updated
}
/*
* CTxtEdit::OnTxMultiLineChange (fMultiline)
*
* @mfunc
* Notify text services that the display changed.
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnTxMultiLineChange(
BOOL fMultiLine)
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnTxMultiLineChange");
BOOL fHadSelection = (_psel != NULL);
CDisplay * pSavedDisplay;
BOOL fOldShowSelection = FALSE;
// Remember the old value for show selection
if (fHadSelection)
fOldShowSelection = _psel->GetShowSelection();
// Save the current display away and null it out
pSavedDisplay = _pdp;
_pdp = NULL;
// Attempt to create the new display
if (fMultiLine)
_pdp = new CDisplayML(this);
else
_pdp = new CDisplaySL(this);
Assert(_pdp);
if(!_pdp)
{
Assert(pSavedDisplay);
_pdp = pSavedDisplay;
return E_OUTOFMEMORY;
}
// Attempt to init the new display
if(pSavedDisplay)
_pdp->InitFromDisplay(pSavedDisplay);
if(!_pdp->Init())
{
delete _pdp;
Assert(pSavedDisplay);
_pdp = pSavedDisplay;
return E_FAIL;
}
// Ok to now kill the old display
delete pSavedDisplay;
// Is there are selection?
if(_psel)
{
// Need to tell it there is a new display to talk to.
_psel->SetDisplay(_pdp);
}
// Is this a switch to Single Line? If this is we need to
// make sure we truncate the text to the first EOP. We wait
// till this point to do this check to make sure that everything
// is in sync before doing something which potentially affects
// the display and the selection.
if(!fMultiLine)
{
// Set up for finding an EOP
CTxtPtr tp(this, 0);
tp.FindEOP(tomForward);
// Is there any EOP and text beyond?
if (tp.GetCp() < GetAdjustedTextLength())
{
// FindEOP places the text after the EOP if there
// is one. Since we want to delete the EOP as well
// we need to back up to the EOP.
tp.BackupCRLF();
// Sync up the cp's of all the ranges before deleting
// the text.
CRchTxtPtr rtp(this, tp.GetCp());
// Truncate from the EOP to the end of the document
rtp.ReplaceRange(GetAdjustedTextLength() - tp.GetCp(), 0, NULL, NULL, -1);
}
}
_pdp->UpdateView();
if(fHadSelection && _fFocus && fOldShowSelection)
_psel->ShowSelection(TRUE);
return S_OK;
}
/*
* CTxtEdit::OnTxReadOnlyChange (fReadOnly)
*
* @mfunc
* Notify text services that read-only property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnTxReadOnlyChange(
BOOL fReadOnly) //@parm TRUE = read only, FALSE = not read only
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnTxReadOnlyChange");
if (fReadOnly)
_ldte.ReleaseDropTarget();
_fReadOnly = fReadOnly; // Cache bit
return S_OK;
}
/*
* CTxtEdit::OnShowAccelerator (fPropertyFlag)
*
* @mfunc
* Update accelerator based on change
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnShowAccelerator(
BOOL fPropertyFlag) //@parm TRUE = show accelerator
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnShowAccelerator");
// Get the new accelerator character
HRESULT hr = UpdateAccelerator();
// Update the view - we update even in the face of an error return.
// The point is that errors will be rare (non-existent?) and the update
// will work even in the face of the error so why bother conditionalizing
// the execution.
NeedViewUpdate(TRUE);
return hr;
}
/*
* CTxtEdit::OnUsePassword (fPropertyFlag)
*
* @mfunc
* Update use-password property
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnUsePassword(
BOOL fPropertyFlag) //@parm TRUE = use password character
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnUsePassword");
Assert((DWORD)fPropertyFlag <= 1); // Be sure it's C boolean
_fUsePassword = fPropertyFlag;
_pdp->UpdateView(); // State changed so update view
return S_OK;
}
/*
* CTxtEdit::OnTxHideSelectionChange (fHideSelection)
*
* @mfunc
* Notify text services that hide-selection property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnTxHideSelectionChange(
BOOL fHideSelection) //@parm TRUE = hide selection
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnTxHideSelectionChange");
// update internal flag if selection is to be hidden
_fHideSelection = fHideSelection;
if (!_fFocus)
OnHideSelectionChange(fHideSelection);
return S_OK;
}
/*
* CTxtEdit::OnHideSelectionChange (fHideSelection)
*
* @mfunc
* Performs the actual hide selection. Helper to OnTxHideSelectionChange
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnHideSelectionChange(
BOOL fHideSelection) //@parm TRUE = hide selection
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnHideSelectionChange");
_fHideSelection = fHideSelection;
CTxtSelection * psel = GetSel();
if(psel)
{
psel->ShowSelection(!fHideSelection);
// In the case where we don't have focus we don't want to allow the user to display the caret but it's okay
// to hide the caret.
if (_fFocus || fHideSelection)
psel->ShowCaret(!fHideSelection);
}
if(!_fInPlaceActive)
{
TxInvalidate(); // Since _fInPlaceActive = FALSE,
TxUpdateWindow(); // this only tells user.exe to
} // send a WM_PAINT message
return S_OK;
}
/*
* CTxtEdit::OnSaveSelection (fPropertyFlag)
*
* @mfunc
* Notify text services that save-selection property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnSaveSelection(
BOOL fPropertyFlag) //@parm TRUE = save selection when inactive
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnSaveSelection");
return S_OK;
}
/*
* CTxtEdit::OnAutoWordSel (fPropertyFlag)
*
* @mfunc
* Notify text services that auto-word-selection property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnAutoWordSel(
BOOL fPropertyFlag) //@parm TRUE = auto word selection on
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnAutoWordSel");
// We call back to the host when we need to know, so we don't bother doing
// anything in response to this notification.
return S_OK;
}
/*
* CTxtEdit::OnTxVerticalChange (fVertical)
*
* @mfunc
* Notify text services that vertical property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnTxVerticalChange(
BOOL fVertical) //@parm TRUE - text vertically oriented.
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnTxVerticalChange");
_fUseAtFont = fVertical;
HandleSetTextFlow(fVertical ? tflowSW : tflowES);
return S_OK;
}
/*
* CTxtEdit::OnClientRectChange (fPropertyFlag)
*
* @mfunc
* Notify text services that client rectangle changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnClientRectChange(
BOOL fPropertyFlag) //@parm Ignored for this property
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnClientRectChange");
// It is unclear whether we need to actually do anything for this
// notification. Logically, the change of this property is followed
// closely by some kind of operation which will cause the display
// cache to be updated anyway. The old code is left here as an
// example of what might be done if it turns out we need to do
// anything. For now, we will simply return S_OK to this notification.
#if 0
if (_fInPlaceActive)
{
RECT rc;
if(_phost->TxGetClientRect(&rc) == NOERROR)
_pdp->OnClientRectChange(rc);
return S_OK;
}
return NeedViewUpdate(fPropertyFlag);
#endif // 0
if(IsInPageView())
_pdp->Paginate(0, TRUE);
// With a client rect change we do need to update the caret when
// we get redrawn even if the basic information did not change.
_pdp->SetUpdateCaret();
if (_fInPlaceActive && _pMsgFilter && _pMsgCallBack)
_pMsgCallBack->NotifyEvents(NE_LAYOUTCHANGE);
return S_OK;
}
/*
* CTxtEdit::OnCharFormatChange (fPropertyFlag)
*
* @mfunc
* Update default CCharFormat
*
* @rdesc
* S_OK - update successfully processed.
*/
HRESULT CTxtEdit::OnCharFormatChange(
BOOL fPropertyFlag) //@parm Not used
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnCharFormatChange");
CCharFormat CF;
DWORD dwMask;
HRESULT hr = TxGetDefaultCharFormat(&CF, dwMask);
if(hr == NOERROR)
{
DWORD dwMask2 = CFM2_CHARFORMAT;
WPARAM wparam = SCF_ALL;
if(!GetAdjustedTextLength())
{
dwMask2 = CFM2_CHARFORMAT | CFM2_NOCHARSETCHECK;
wparam = 0;
}
// OnSetCharFormat handles updating the view.
hr = OnSetCharFormat(wparam, &CF, NULL, dwMask, dwMask2) ? NOERROR : E_FAIL;
}
return hr;
}
/*
* CTxtEdit::OnParaFormatChange (fPropertyFlag)
*
* @mfunc
* Update default CParaFormat
*
* @rdesc
* S_OK - update successfully processed
*/
HRESULT CTxtEdit::OnParaFormatChange(
BOOL fPropertyFlag) //@parm Not used
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnParaFormatChange");
CParaFormat PF;
HRESULT hr = TxGetDefaultParaFormat(&PF);
if(hr == NOERROR)
{
// OnSetParaFormat handles updating the view.
hr = OnSetParaFormat(SPF_SETDEFAULT, &PF, NULL, PFM_ALL2, 0)
? NOERROR : E_FAIL;
}
#ifdef TABS
GetTabsCache()->Release(PF._iTabs);
#endif
return hr;
}
/*
* CTxtEdit::NeedViewUpdate (fPropertyFlag)
*
* @mfunc
* Notify text services that view of data changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::NeedViewUpdate(
BOOL fPropertyFlag)
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::NeedViewUpdate");
_pdp->UpdateView();
return S_OK;
}
/*
* CTxtEdit::OnTxBackStyleChange (fPropertyFlag)
*
* @mfunc
* Notify text services that background style changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnTxBackStyleChange(
BOOL fPropertyFlag) //@parm Ignored for this property
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnTxBackStyleChange");
_fTransparent = (TxGetBackStyle() == TXTBACK_TRANSPARENT);
TxInvalidate();
return S_OK;
}
/*
* CTxtEdit::OnAllowBeep (fPropertyFlag)
*
* @mfunc
* Notify text services that beep property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnAllowBeep(
BOOL fPropertyFlag) //@parm New state of property
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnAllowBeep");
_fAllowBeep = fPropertyFlag;
return S_OK;
}
/*
* CTxtEdit::OnMaxLengthChange (fPropertyFlag)
*
* @mfunc
* Notify text services that max-length property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnMaxLengthChange(
BOOL fPropertyFlag) //@parm New state of property
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnMaxLengthChange");
// Query host for max text length
DWORD length = CP_INFINITE;
_phost->TxGetMaxLength(&length);
_cchTextMost = length;
return S_OK;
}
/*
* CTxtEdit::OnWordWrapChange (fPropertyFlag)
*
* @mfunc
* Notify text services that word-wrap property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnWordWrapChange(
BOOL fPropertyFlag) //@parm TRUE = do word wrap
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnWordWrapChange");
_pdp->SetWordWrap(fPropertyFlag);
// Update was successful so we need the screen updated at some point
_pdp->UpdateView();
return S_OK;
}
/*
* CTxtEdit::OnDisableDrag (fPropertyFlag)
*
* @mfunc
* Notify text services that disable drag property changed
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnDisableDrag(
BOOL fPropertyFlag) //@parm New state of property
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnDisableDrag");
_fDisableDrag = fPropertyFlag;
return S_OK;
}
/*
* CTxtEdit::OnScrollChange (fPropertyFlag)
*
* @mfunc
* Notify text services scroll property change
*
* @rdesc
* S_OK - Notification successfully processed.
*/
HRESULT CTxtEdit::OnScrollChange(
BOOL fPropertyFlag) //@parm New state of property
{
TRACEBEGIN(TRCSUBSYSTS, TRCSCOPEINTERN, "CTxtEdit::OnScrollChange");
// Tell the display that scroll bars for sure need to be updated
_pdp->SetViewChanged();
// Tell the display to update itself.
_pdp->UpdateView();
return S_OK;
}