📄 edit.cpp
字号:
if (GetKeyState(VK_SHIFT) & 0x8000)
iKeyState |= SHFT_ON;
if (HIWORD(dwFlags) & KF_ALTDOWN)
iKeyState |= ALT_ON;
if (GetKeyState(VK_CONTROL) & 0x8000)
iKeyState |= CTL_ON;
CTxtSelection * const psel = GetSel();
AssertSz(psel,"CTxtEdit::OnTxOutlineKeyDown() - No selection object !");
CTxtRange rg(*psel);
if ((iKeyState & (ALT_ON | CTL_ON)) == (ALT_ON | CTL_ON))
{
switch(vkey)
{
case 'N': // Alt-Ctrl-N => Normal View
SetViewKind(VM_NORMAL);
break;
case 'O': // Alt-Ctrl-O => Outline View
SetViewKind(VM_OUTLINE);
break;
default :
hr = S_FALSE;
break;
}
}
else if ((iKeyState & (ALT_ON | SHFT_ON)) == (ALT_ON | SHFT_ON))
{
switch(vkey)
{
case VK_LEFT: // Left arrow
case VK_RIGHT: // Right arrow
hr = rg.Promote(vkey == VK_LEFT ? 1 : -1, publdr);
psel->Update_iFormat(-1);
psel->Update(FALSE);
break;
case VK_UP: // Up arrow
case VK_DOWN: // Down arrow
hr = MoveSelection(vkey == VK_UP ? -1 : 1, publdr);
psel->Update(TRUE);
break;
case VK_ADD:
case VK_SUBTRACT:
level = vkey == VK_ADD ? 1 : -1;
fWholeDoc = FALSE;
/* Fall through */
case 'A':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (!level)
level = vkey == 'A' ? 9 : vkey - '0';
#ifdef PWD_JUPITER // GuyBark 81387: Allow undo of expand/collapse operation
hr = rg.ExpandOutline(level, fWholeDoc, publdr);
#else
hr = rg.ExpandOutline(level, fWholeDoc);
#endif // PWD_JUPITER
break;
default :
hr = S_FALSE;
}
}
else if ((iKeyState & (SHFT_ON | CTL_ON)) == (SHFT_ON | CTL_ON))
{
if (vkey == 'N') // Demote to Body
hr = rg.Promote(0, publdr);
else if (vkey == 'L') // Fiddle bullet style
{
CParaFormat PF; // view, use Normal style
psel->GetParaFormat(&PF);
PF.dwMask = PFM_NUMBERING;
PF.wNumbering++;
PF.wNumbering %= tomListNumberAsUCRoman + 1;
if (PF.wNumbering)
{
PF.dwMask |= PFM_NUMBERINGSTYLE | PFM_NUMBERINGSTART;
PF.wNumberingStyle = PFNS_PERIOD;
PF.wNumberingStart = 1;
}
hr = psel->SetParaFormat(&PF, publdr);
}
else
hr = S_FALSE;
if (hr != S_FALSE)
{
psel->Update_iFormat(-1);
psel->Update(FALSE);
}
}
else
hr = S_FALSE;
return hr;
}
/*
* CTxtEdit::OnTxChar (vkey, dwFlags, publdr)
*
* @mfunc
* Handle WM_CHAR message
*
* @rdesc
* HRESULT with the following values:
*
* S_OK if key was understood and consumed
* S_MSG_KEY_IGNORED if key was understood, but not consumed
* S_FALSE if key was not understood (and not consumed)
*/
HRESULT CTxtEdit::OnTxChar(
WORD vkey, //@parm Translated key code
DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxChar");
if (_fMouseDown || vkey == VK_ESCAPE || // Ctrl-Backspace generates VK_F16
vkey == VK_BACK || vkey==VK_F16) // Eat it since we process it
{ // in WM_KEYDOWN
return S_OK;
}
CTxtSelection * const psel = GetSel();
AssertSz(psel,
"CTxtEdit::OnChar() - No selection object !");
psel->SetExtend(FALSE); // Shift doesn't mean extend for
// WM_CHAR
if(_fReadOnly && vkey != 3) // Don't allow input if read only,
{ // but allow copy (Ctrl-C)
if(vkey >= ' ')
Sound();
return S_MSG_KEY_IGNORED;
}
if(vkey >= ' ' || vkey == VK_TAB)
{
SetCursor(0);
if(IsntProtectedOrReadOnly(WM_CHAR, vkey, dwFlags))
{
LONG nDeadKey = _bDeadKey;
if(nDeadKey)
{
LONG ch = vkey | 0x20; // Convert to lower case
BOOL fShift = vkey != ch; // (if ASCII letter)
// a b c d e f g h i j
static chOff[] = {0xDF, 0, 0xE7, 0, 0xE7, 0, 0, 0, 0xEB, 0,
// k l m n o p q r s t u
0, 0, 0, 0xF1, 0xF1, 0, 0, 0, 0, 0, 0xF8};
_bDeadKey = 0;
if(!IN_RANGE('a', ch, 'u')) // Not relevant ASCII
return S_OK; // letter
vkey = chOff[ch - 'a']; // Translate to base char
if(!vkey) // No accents available
return S_OK; // in current approach
if(ch == 'n')
{
if(nDeadKey != ACCENT_TILDE)
return S_OK;
}
else if(nDeadKey == ACCENT_CEDILLA)
{
if(ch != 'c')
return S_OK;
}
else // aeiou
{
vkey += nDeadKey;
if (nDeadKey >= ACCENT_TILDE && // eiu with ~ or :
(vkey == 0xF0 || vkey & 8))
{
if(nDeadKey != ACCENT_UMLAUT)// Only have umlauts
return S_OK;
vkey--;
}
}
if(fShift)
vkey &= ~0x20;
}
// If there are conpotition string, then determin it.
if(IsIMEComposition()){
HIMC hIMC = TxImmGetContext();
if(hIMC){
pImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
TxImmReleaseContext(hIMC);
}
}
psel->PutChar((TCHAR)vkey, _fOverstrike, publdr);
}
}
return S_OK;
}
HRESULT CTxtEdit::OnTxSysKeyDown(
WORD vkey, //@parm Virtual key code
DWORD dwFlags, //@parm lparam of WM_KEYDOWN msg
IUndoBuilder *publdr) //@parm Undobuilder to receive antievents
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnTxSysKeyDown");
HRESULT hr = OnTxSpecialKeyDown(vkey, dwFlags, publdr);
if (hr == S_OK)
return hr;
hr = S_FALSE;
if(vkey == VK_BACK && (dwFlags & SYS_ALTERNATE))
{
if( _pundo && _pundo->CanUndo())
{
if( PopAndExecuteAntiEvent(_pundo, 0) != NOERROR )
{
hr = S_MSG_KEY_IGNORED;
}
}
else
Sound();
}
else if( (vkey == VK_F10 &&// F10
!(dwFlags & SYS_PREVKEYSTATE) && // Key previously up
(GetKeyState(VK_SHIFT) & 0x8000))) // Shift is down
{
HandleKbdContextMenu();
}
return hr;
}
/////////////////////////////// Other system events //////////////////////////////
HRESULT CTxtEdit::OnContextMenu(LPARAM lparam)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnContextMenu");
POINT pt;
pt.x = LOWORD(lparam);
pt.y = HIWORD(lparam);
if( TxScreenToClient(&pt) )
{
return OnTxRButtonUp(pt.x, pt.y, 0);
}
return S_FALSE;
}
/*
* CTxtEdit::HandleKbdContextMenu
*
* @mfunc decides where to put the context menu on the basis of where the
* the selection is. Useful for shift-F10 and VK_APPS, where
* we aren't given a location.
*
* @rdesc void
*/
void CTxtEdit::HandleKbdContextMenu(void)
{
POINT pt;
RECT rc;
const CTxtSelection * const psel = GetSel();
// Figure out where selection ends and put context menu near it
if(_pdp->PointFromTp(*psel, NULL, FALSE, pt, NULL, TA_TOP) < 0)
return;
// Make sure point is still within bounds of edit control
_pdp->GetViewRect(rc);
if (pt.x < rc.left)
pt.x = rc.left;
if (pt.x > rc.right - 2)
pt.x = rc.right - 2;
if (pt.y < rc.top)
pt.y = rc.top;
if (pt.y > rc.bottom - 2)
pt.y = rc.bottom - 2;
OnTxRButtonUp(pt.x, pt.y, 0);
}
/////////////////////////////// Format Range Commands //////////////////////////////
LONG CTxtEdit::OnFormatRange(
FORMATRANGE *pfr,
SPrintControl prtcon,
HDC hdcMeasure,
LONG xMeasurePerInch,
LONG yMeasurePerInch)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnFormatRange");
// Even if there is 0 text, we want to print the control so that it will
// fill the control with background color.
// Use Adjusted Text Length. Embedded objects using RichEdit will get the empty
// document they expect and will create a default size document.
if(!pfr ||
((pfr->chrg.cpMin >= (LONG)GetAdjustedTextLength()) && !prtcon._fPrintFromDraw))
{ // we're done formatting, get rid of our printer's display context.
delete _pdpPrinter;
_pdpPrinter = NULL;
return GetAdjustedTextLength();
}
LONG lRetVal = -1;
BOOL fSetDCWorked = FALSE;
HDC hdcLocal = pfr->hdc;
// first time in with this printer, set up a new display context.
// IMPORTANT: proper completion of the printing process is required
// to dispose of this context and begin a new context.
// This is implicitly done by printing the last character, or
// sending an EM_FORMATRANGE message with pfr equal to NULL.
if ( NULL == _pdpPrinter )
{
_pdpPrinter = new CDisplayPrinter (this, hdcLocal,
pfr->rc.right - pfr->rc.left /* x width max */,
pfr->rc.bottom - pfr->rc.top, /* y height max */
prtcon);
if ( _pdpPrinter ){
_pdpPrinter->Init();
// Future: (ricksa) This is a really yucky way to pass the draw info
// to the printer but it was quick. We want to make this better.
_pdpPrinter->ResetDrawInfo(_pdp);
// Set the temporary zoom factor (if there is one).
_pdpPrinter->SetTempZoomDenominator(
_pdp->GetTempZoomDenominator());
}
}
else
{
_pdpPrinter->SetPrintDimensions(&pfr->rc);
}
// We set the DC everytime because it could have changed.
if (_pdpPrinter && GetDeviceCaps(hdcLocal, TECHNOLOGY) != DT_METAFILE)
{
// This is not a metafile so do the normal thing
fSetDCWorked = _pdpPrinter->SetDC(hdcLocal);
}
else
{
// Is the measure DC already set up?
if (NULL == hdcMeasure)
{
// We need to set up a metafile with an HDC we can use for
// measuring. It is important to note the hack going on here.
// Richedit 1.0 assumed that it could map metafiles using TWIPS.
// Therefore, we pass the TRUE flag into CreateMeasureDC which
// causes a measure DC to be created which is based on TWIPS.
hdcMeasure = CreateMeasureDC(hdcLocal, NULL, TRUE, pfr->rcPage.left,
pfr->rcPage.top, pfr->rcPage.right - pfr->rcPage.left,
pfr->rcPage.bottom - pfr->rcPage.top, &xMeasurePerInch,
&yMeasurePerInch);
}
// Were we able to set up a measure DC?
if (hdcMeasure != NULL)
{
if ( _pdpPrinter ){
_pdpPrinter->SetMetafileDC(hdcLocal, hdcMeasure,
xMeasurePerInch, yMeasurePerInch);
}
fSetDCWorked = TRUE;
}
}
if (fSetDCWorked)
{
// we set this everytime because it could have changed.
if ( _pdpPrinter && _pdpPrinter->SetTargetDC( pfr->hdcTarget ) )
{
LONG cpReturn;
// Format another, single page worth of text.
cpReturn = _pdpPrinter->FormatRange( pfr->chrg.cpMin, pfr->chrg.cpMost);
if (!prtcon._fPrintFromDraw)
{
// after formatting, we know where the bottom is. But we only
// want to set this if we are writing a page rather than
// displaying a control on the printer.
pfr->rc.bottom = INT
(pfr->rc.top + _pdpPrinter->DYtoLY( _pdpPrinter->GetHeight()) );
}
// remember this in case the host wishes to do its own banding.
_pdpPrinter->SetPrintView( pfr->rc ); // we need to save this for OnDisplayBand.
_pdpPrinter->SetPrintPage( pfr->rcPage );
// if we're asked to render, then render the entire page in one go.
if(prtcon._fDoPrint && ((cpReturn > 0) || prtcon._fPrintFromDraw))
{
OnDisplayBand( &pfr->rc, prtcon._fPrintFromDraw );
// note: we can no longer call OnDisplayBand without reformating.
_pdpPrinter->Clear(AF_DELETEMEM);
}
lRetVal = cpReturn;
}
}
if (hdcMeasure)
{
TxReleaseMeasureDC(hdcMeasure);
hdcMeasure = NULL;
}
return lRetVal;
}
// Keep the compiler quiet about xPhys and yPhys below.
// The compiler complains incorrectly about possible use of uninitialized variable.
#pragma warning (disable : 4701)
BOOL CTxtEdit::OnDisplayBand(const RECT *prc, BOOL fPrintFromDraw)
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::OnDisplayBand");
RECT rc, rcPrint;
HDC hdcPrinter;
// Make sure OnFormatRange was called and that it actually rendered something.
if(!_pdpPrinter || !_pdpPrinter->Count())
return FALSE;
// proportionally map to printers extents.
rc.left = (INT) _pdpPrinter->LXtoDX(prc->left);
rc.right = (INT) _pdpPrinter->LXtoDX(prc->right);
rc.top = (INT) _pdpPrinter->LYtoDY(prc->top);
rc.bottom = (INT) _pdpPrinter->LYtoDY(prc->bottom);
rcPrint = _pdpPrinter->GetPrintView();
rcPrint.left = (INT) _pdpPrinter->LXtoDX(rcPrint.left);
rcPrint.right = (INT) _pdpPrinter->LXtoDX(rcPrint.right);
rcPrint.top = (INT) _pdpPrinter->LYtoDY(rcPrint.top);
rcPrint.bottom = (INT) _pdpPrinter->LYtoDY(rcPrint.bottom);
// Get the DC for the printer because we use it below.
hdcPrinter = _pdpPrinter->GetDC();
if (fPrintFromDraw)
{
// We need to take the view inset into account
_pdpPrinter->GetViewRect(rcPrint, &rcPrint);
}
#ifdef PWD_JUPITER
// GuyBark 34191 Jupiter: We must extent the right edge of the view,
// otherwise letters with overhangs get cut off. This may look rather
// hard coded, but this is the same hard coded value used by PWord's
// doc.c. Ideally we'd not hard code the value, but there doesn't seem
// to be an easy way of finding the required inset here. We take
// similar action when the user scrolls the RichEdit window.
rcPrint.right += 8;
#endif // PWD_JUPITER
// Render this band.
_pdpPrinter->Render(rcPrint, rc);
return TRUE;
}
#pragma warning (default : 4701)
//////////////////////////////// Protected ranges //////////////////////////////////
/*
* CTxtEdit::IsProtected (msg, wparam, lparam)
*
* @mfunc
* Find out if selection is protected
*
* @rdesc
* TRUE iff 1) control is read-only or 2) selection is protected and
* parent query says to protect
*/
BOOL CTxtEdit::IsProtected(
UINT msg, //@parm Message id
WPARAM wparam, //@parm WPARAM from window's message
LPARAM lparam) //@parm LPARAM from window's message
{
TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CTxtEdit::IsProtected");
LONG iDirection = 0;
CTxtSelection *psel = GetSel();
// there are a few special cases to consider, namely
// backspacing into a protected range, 'del'eting into
// a protected range, and type with overstrike into a protected
// range.
if( (msg == WM_KEYDOWN && (wparam == VK_BACK || wparam == VK_F16)) )
{
// check for the format behind the selection, if we are trying to
// backspace an insertion point.
iDirection = -1;
}
else if( (msg == WM_KEYDOWN && wparam == VK_DELETE) ||
(_fOverstrike && msg == WM_CHAR) )
{
iDirection = 1;
}
int iProt = 0;
// HACK ALERT: we don't do fIsDBCS protection checking for EM_REPLACESEL,
// EM_SETCHARFORMAT, or EM_SETPARAFORMAT. Outlook uses
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -