/* * @doc INTERNAL * * @module PROPCHG.CPP -- Property Change Notification Routines | * * Original Author: * Rick Sailor * * History: * 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; }