📄 caret.cpp
字号:
// caret.cpp : Caret handling helper functions.
//
#include "stdafx.h"
#include "gettext.h"
#include "mainfrm.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
//-----------------------------------------------------------------------------
// [GETTEXT] MoveCaret moves the caret to a particular line and character.
void DMainFrame::MoveCaret(int iLine, int iChar)
{
// Ignore if we don't have any lines of text
if (d_cLines == 0)
return;
// Check that line and character indices are in range.
ASSERT (iLine >= 0 && iLine <= d_cLines);
ASSERT (iChar >= 0 && iChar <= (d_saTextInfo[iLine].ccLen));
// Get pointer to text string and count of characters.
LPTSTR lp = d_saTextInfo[iLine].pText;
int cc = d_saTextInfo[iLine].ccLen;
// Figuring which line is simple math.
CPoint ptCaret;
ptCaret.y = (iLine - d_iTopLine) * d_cyLineHeight;
// Figuring which character requires a loop and some simple math.
ptCaret.x = d_cxLeftMargin;
for (int ich = 0; ich < iChar; ich++)
{
// If a tab character...
if (lp[ich] == '\t')
{
// Tabs enabled...
if (d_bTabs)
{
ptCaret.x += d_cxTabStop;
}
else // ... Tabs disabled.
{
ptCaret.x += d_cxAveCharWidth;
}
}
else // ...not a tab character.
{
ptCaret.x += d_piCharWidth[lp[ich] - d_bchFirstChar];
}
}
SetCaretPos(ptCaret);
d_ptCaret = ptCaret;
}
//-----------------------------------------------------------------------------
// [GETTEXT] MoveCaretChar moves caret by a relative amount.
void DMainFrame::MoveCaretChar(int nCharCount)
{
// We're not equipped to handle moves greater than ABS(1).
ASSERT ((nCharCount == -1) || (nCharCount == 1));
// Handle empty buffer situation.
if (d_cLines == 0)
return;
// Query caret indices: (line offset, character offset)
int iLine = QueryClickLine(d_ptCaret);
int iChar = QueryClickChar(iLine, d_ptCaret);
// Query length of current line.
int ccLine = d_saTextInfo[iLine].ccLen;
// [LeftArrow] at start of line.
if ((nCharCount <= 0) && (iChar == 0))
{
// Cannot move before first line.
if (iLine == 0)
{
::MessageBeep(0);
return;
}
iLine -= 1;
iChar = d_saTextInfo[iLine].ccLen;
// Scroll if we need to.
if (iLine < d_iTopLine)
OnVScroll(SB_LINEUP, 0, 0);
}
// [RightArrow] at end of line.
else if ((nCharCount >= 0) && (iChar == ccLine))
{
// Cannot move beyond last line
if (iLine == (d_cLines-1))
{
::MessageBeep(0);
return;
}
iLine += 1;
iChar = 0;
// Scroll if we need to.
int yCaretBottom = d_ptCaret.y + (d_cyLineHeight * 2);
CRect rClient;
GetClientRect(&rClient);
if (rClient.bottom < yCaretBottom)
OnVScroll(SB_LINEDOWN, 0, 0);
}
// Adjust caret character index.
else
{
iChar += nCharCount;
}
MoveCaret(iLine, iChar);
}
//-----------------------------------------------------------------------------
// [GETTEXT] MoveCaretClick simulates a mouse click to move the caret.
void DMainFrame::MoveCaretClick(CPoint ptHit)
{
CRect rClient;
GetClientRect(&rClient);
// Scroll UP if top of caret is above top of client area.
if (ptHit.y < 0)
{
// Prevent scrolling above top line.
if (d_iTopLine <= 0)
{
::MessageBeep(0);
return;
}
// Scroll requested line into view.
OnVScroll(SB_LINEUP, 0, 0);
// Adjust hit point to take scrolling into account.
ptHit.y += d_cyLineHeight;
}
// Scroll DOWN if bottom of caret is below bottom of client area.
if ((ptHit.y + d_cyLineHeight) > rClient.bottom)
{
// Prevent scrolling below last line.
if (d_iTopLine >= (d_cLines - d_clHeight))
{
::MessageBeep(0);
return;
}
// Scroll requested line into view.
OnVScroll(SB_LINEDOWN, 0, 0);
// Adjust hit point to take scrolling into account.
ptHit.y -= d_cyLineHeight;
}
// Give ourselves a mouse click.
OnLButtonDown(0, ptHit);
}
//-----------------------------------------------------------------------------
// [GETTEXT] QueryClickChar fetches index to character where caret goes.
int DMainFrame::QueryClickChar(int iLine, CPoint point)
{
// Easy special case: left margin click means start of line.
if (point.x <= d_cxLeftMargin || d_cLines == 0)
return 0;
// Get pointer to text string and count of characters.
LPTSTR lp = d_saTextInfo[iLine].pText;
int cc = d_saTextInfo[iLine].ccLen;
// Loop until a hit.
int ich;
int x = d_cxLeftMargin;
int xPrev = x;
for (ich = 0; ich <= cc; ich++)
{
// Check for passing the character cell.
if (x >= point.x)
{
// Compare point to middle of previous character cell.
if ((xPrev + x) / 2 >= point.x)
{
ich--;
}
// Character to right of caret location.
return ich;
}
// Store previous char cell for 1/2 cell calculation.
xPrev = x;
// If a tab character...
if (lp[ich] == '\t')
{
// Tabs enabled?
int xTabWidth = (d_bTabs) ? d_cxTabStop : d_cxAveCharWidth;
x += xTabWidth;
}
else // ...not a tab character.
{
// Avoid reading past end of array.
if (ich < cc)
{
// Increment x-value by last character width.
x += d_piCharWidth[lp[ich] - d_bchFirstChar];
}
}
}
// Beyond end of line means caret goes after last character.
return cc;
}
//-----------------------------------------------------------------------------
// [GETTEXT] QueryClickLine converts client area point to line number.
int DMainFrame::QueryClickLine(CPoint point)
{
// Figure out range of lines to check.
int iLine = d_iTopLine;
int cLastLine = d_iTopLine + d_clHeight + 1;
cLastLine = min (cLastLine, d_cLines);
// Initialize hit test rectangle to client area size.
CRect rTextLine;
GetClientRect(&rTextLine);
// Can't deal outside client area.
ASSERT(rTextLine.PtInRect(point));
rTextLine.bottom = d_cyLineHeight;
// Loop through lines in window doing hit testing.
// NOTE: Since every line is the same height,
// we could easily optimize this loop.
for (int i=iLine; i < cLastLine; i++)
{
// Test for a hit.
if (rTextLine.PtInRect(point))
{
return i;
}
// If no hit, increment test rectangle and try again.
rTextLine.top += d_cyLineHeight;
rTextLine.bottom += d_cyLineHeight;
}
// User clicked below bottom line. Set pick to last line.
return cLastLine - 1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -