📄 ccrystaltextview.cpp
字号:
int nSelBegin = 0, nSelEnd = 0;
if (m_ptDrawSelStart.y > ptTextPos.y)
{
nSelBegin = nCount;
}
else
if (m_ptDrawSelStart.y == ptTextPos.y)
{
nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
if (nSelBegin < 0)
nSelBegin = 0;
if (nSelBegin > nCount)
nSelBegin = nCount;
}
if (m_ptDrawSelEnd.y > ptTextPos.y)
{
nSelEnd = nCount;
}
else
if (m_ptDrawSelEnd.y == ptTextPos.y)
{
nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
if (nSelEnd < 0)
nSelEnd = 0;
if (nSelEnd > nCount)
nSelEnd = nCount;
}
ASSERT(nSelBegin >= 0 && nSelBegin <= nCount);
ASSERT(nSelEnd >= 0 && nSelEnd <= nCount);
ASSERT(nSelBegin <= nSelEnd);
// Draw part of the text before selection
if (nSelBegin > 0)
{
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset, nSelBegin);
}
if (nSelBegin < nSelEnd)
{
COLORREF crOldBk = pdc->SetBkColor(GetColor(COLORINDEX_SELBKGND));
// COLORREF crOldText = pdc->SetTextColor(GetColor(COLORINDEX_SELTEXT));
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset + nSelBegin, nSelEnd - nSelBegin);
pdc->SetBkColor(crOldBk);
// pdc->SetTextColor(crOldText);
}
if (nSelEnd < nCount)
{
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset + nSelEnd, nCount - nSelEnd);
}
}
else
{
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset, nCount);
}
}
}
void CCrystalTextView::GetLineColors(int nLineIndex, COLORREF &crBkgnd,
COLORREF &crText, BOOL &bDrawWhitespace)
{
DWORD dwLineFlags = GetLineFlags(nLineIndex);
bDrawWhitespace = TRUE;
crText = RGB(255, 255, 255);
if (dwLineFlags & LF_EXECUTION)
{
crBkgnd = RGB(0, 128, 0);
return;
}
if (dwLineFlags & LF_BREAKPOINT)
{
crBkgnd = RGB(255, 0, 0);
return;
}
if (dwLineFlags & LF_INVALID_BREAKPOINT)
{
crBkgnd = RGB(128, 128, 0);
return;
}
crBkgnd = CLR_NONE;
crText = CLR_NONE;
bDrawWhitespace = FALSE;
}
DWORD CCrystalTextView::GetParseCookie(int nLineIndex)
{
int nLineCount = GetLineCount();
if (m_pdwParseCookies == NULL)
{
m_nParseArraySize = nLineCount;
m_pdwParseCookies = new DWORD[nLineCount];
memset(m_pdwParseCookies, 0xff, nLineCount * sizeof(DWORD));
}
if (nLineIndex < 0)
return 0;
if (m_pdwParseCookies[nLineIndex] != (DWORD) -1)
return m_pdwParseCookies[nLineIndex];
int L = nLineIndex;
while (L >= 0 && m_pdwParseCookies[L] == (DWORD) -1)
L --;
L ++;
int nBlocks;
while (L <= nLineIndex)
{
DWORD dwCookie = 0;
if (L > 0)
dwCookie = m_pdwParseCookies[L - 1];
ASSERT(dwCookie != (DWORD) -1);
m_pdwParseCookies[L] = ParseLine(dwCookie, L, NULL, nBlocks);
ASSERT(m_pdwParseCookies[L] != (DWORD) -1);
L ++;
}
return m_pdwParseCookies[nLineIndex];
}
void CCrystalTextView::DrawSingleLine(CDC *pdc, const CRect &rc, int nLineIndex)
{
ASSERT(nLineIndex >= -1 && nLineIndex < GetLineCount());
if (nLineIndex == -1)
{
// Draw line beyond the text
pdc->FillSolidRect(rc, GetColor(COLORINDEX_WHITESPACE));
return;
}
/* else if (IsFirstLineOfCollapsedBlock(nLineIndex))
{
// first line of collapsed block
DrawEllipsis(pdc, rc);
return;
} */
// Acquire the background color for the current line
BOOL bDrawWhitespace = FALSE;
COLORREF crBkgnd, crText;
GetLineColors(nLineIndex, crBkgnd, crText, bDrawWhitespace);
if (crBkgnd == CLR_NONE)
crBkgnd = GetColor(COLORINDEX_BKGND);
int nLength = GetLineLength(nLineIndex);
if (nLength == 0)
{
// MiK
if (IsFirstLineOfCollapsedBlock(nLineIndex))
{
DrawEllipsis(pdc, rc);
}
else
{
// Draw the empty line
CRect rect = rc;
if ((m_bFocused || m_bShowInactiveSelection) && IsInsideSelBlock(CPoint(0, nLineIndex)))
{
pdc->FillSolidRect(rect.left, rect.top, GetCharWidth(), rect.Height(), GetColor(COLORINDEX_SELBKGND));
rect.left += GetCharWidth();
}
pdc->FillSolidRect(rect, bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
}
// parse line: give it a chance to mark collapsible blocks
GetParseCookie(nLineIndex);
return;
}
// Parse the line
LPCTSTR pszChars = GetLineChars(nLineIndex);
DWORD dwCookie = GetParseCookie(nLineIndex - 1);
TEXTBLOCK *pBuf = (TEXTBLOCK *) _alloca(sizeof(TEXTBLOCK) * nLength * 3);
int nBlocks = 0;
m_pdwParseCookies[nLineIndex] = ParseLine(dwCookie, nLineIndex, pBuf, nBlocks);
ASSERT(m_pdwParseCookies[nLineIndex] != (DWORD) -1);
// Draw the line text
CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
pdc->SetBkColor(crBkgnd);
if (crText != CLR_NONE)
pdc->SetTextColor(crText);
BOOL bColorSet = FALSE;
if (nBlocks > 0)
{
ASSERT(pBuf[0].m_nCharPos >= 0 && pBuf[0].m_nCharPos <= nLength);
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
pdc->SelectObject(GetFont(GetItalic(COLORINDEX_NORMALTEXT), GetBold(COLORINDEX_NORMALTEXT)));
DrawLineHelper(pdc, origin, rc, COLORINDEX_NORMALTEXT, pszChars, 0, pBuf[0].m_nCharPos, CPoint(0, nLineIndex));
for (int I = 0; I < nBlocks - 1; I ++)
{
ASSERT(pBuf[I].m_nCharPos >= 0 && pBuf[I].m_nCharPos <= nLength);
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(pBuf[I].m_nColorIndex));
pdc->SelectObject(GetFont(GetItalic(pBuf[I].m_nColorIndex), GetBold(pBuf[I].m_nColorIndex)));
DrawLineHelper(pdc, origin, rc, pBuf[I].m_nColorIndex, pszChars,
pBuf[I].m_nCharPos, pBuf[I + 1].m_nCharPos - pBuf[I].m_nCharPos,
CPoint(pBuf[I].m_nCharPos, nLineIndex));
}
ASSERT(pBuf[nBlocks - 1].m_nCharPos >= 0 && pBuf[nBlocks - 1].m_nCharPos <= nLength);
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(pBuf[nBlocks - 1].m_nColorIndex));
pdc->SelectObject(GetFont(GetItalic(pBuf[nBlocks - 1].m_nColorIndex),
GetBold(pBuf[nBlocks - 1].m_nColorIndex)));
DrawLineHelper(pdc, origin, rc, pBuf[nBlocks - 1].m_nColorIndex, pszChars,
pBuf[nBlocks - 1].m_nCharPos, nLength - pBuf[nBlocks - 1].m_nCharPos,
CPoint(pBuf[nBlocks - 1].m_nCharPos, nLineIndex));
}
else
{
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
pdc->SelectObject(GetFont(GetItalic(COLORINDEX_NORMALTEXT), GetBold(COLORINDEX_NORMALTEXT)));
DrawLineHelper(pdc, origin, rc, COLORINDEX_NORMALTEXT, pszChars, 0, nLength, CPoint(0, nLineIndex));
}
// Draw whitespaces to the left of the text
CRect frect = rc;
if (origin.x > frect.left)
frect.left = origin.x;
if (frect.right > frect.left)
{
if ((m_bFocused || m_bShowInactiveSelection) && IsInsideSelBlock(CPoint(nLength, nLineIndex)))
{
pdc->FillSolidRect(frect.left, frect.top, GetCharWidth(), frect.Height(),
GetColor(COLORINDEX_SELBKGND));
frect.left += GetCharWidth();
}
if (frect.right > frect.left)
pdc->FillSolidRect(frect, bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
// MiK
if (IsFirstLineOfCollapsedBlock(nLineIndex))
{
DrawEllipsis(pdc, frect);
}
}
}
COLORREF CCrystalTextView::GetColor(int nColorIndex)
{
switch (nColorIndex)
{
case COLORINDEX_WHITESPACE:
case COLORINDEX_BKGND:
return ::GetSysColor(COLOR_WINDOW);
case COLORINDEX_NORMALTEXT:
return ::GetSysColor(COLOR_WINDOWTEXT);
case COLORINDEX_SELMARGIN:
{
COLORREF rgbGray= ::GetSysColor(COLOR_3DFACE);
return RGB(0x80 + GetRValue(rgbGray) / 2, 0x80 + GetGValue(rgbGray) / 2, 0x80 + GetBValue(rgbGray) / 2);
}
// return RGB(240,240,240); //::GetSysColor(COLOR_SCROLLBAR);
case COLORINDEX_PREPROCESSOR:
return RGB(128, 0, 128);
case COLORINDEX_COMMENT:
return RGB(128, 128, 128);
// [JRT]: Enabled Support For Numbers...
case COLORINDEX_NUMBER:
return RGB(0, 0, 255);
// [JRT]: Support For C/C++ Operators
case COLORINDEX_OPERATOR:
return RGB(128, 0, 0);
case COLORINDEX_KEYWORD:
return RGB(0, 0, 160);
case COLORINDEX_SELBKGND:
return RGB(192, 192, 224);
case COLORINDEX_SELTEXT:
return RGB(255, 255, 255);
case COLORINDEX_STRING:
return RGB(0, 128, 128);
case COLORINDEX_ELLIPSIS:
return RGB(128, 128, 128);
}
return RGB(0, 0, 0);
}
DWORD CCrystalTextView::GetLineFlags(int nLineIndex)
{
if (m_pTextBuffer == NULL)
return 0;
return m_pTextBuffer->GetLineFlags(nLineIndex);
}
void CCrystalTextView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
{
if (!m_bSelMargin)
{
pdc->FillSolidRect(rect, GetColor(COLORINDEX_BKGND));
return;
}
pdc->FillSolidRect(rect, GetColor(COLORINDEX_SELMARGIN));
if (nLineIndex < 0)
return;
int nImageIndex= -1;
DWORD dwLineFlags= GetLineFlags(nLineIndex);
static const DWORD adwFlags[] =
{
LF_EXECUTION,
LF_BREAKPOINT,
LF_COMPILATION_ERROR,
LF_BOOKMARK(1),
LF_BOOKMARK(2),
LF_BOOKMARK(3),
LF_BOOKMARK(4),
LF_BOOKMARK(5),
LF_BOOKMARK(6),
LF_BOOKMARK(7),
LF_BOOKMARK(8),
LF_BOOKMARK(9),
LF_BOOKMARK(0),
LF_BOOKMARKS,
LF_INVALID_BREAKPOINT
};
for (int I = 0; I <= sizeof(adwFlags) / sizeof(adwFlags[0]); I ++)
{
if ((dwLineFlags & adwFlags[I]) != 0)
{
nImageIndex = I;
break;
}
}
if (nImageIndex >= 0)
DrawMarginIcon(pdc, rect, nImageIndex);
// MiK: draw expand/collapse mark on top of other marks
int nIndex= nLineIndex - m_nTopLine;
bool bInsideBlock= nIndex >= 0 && nIndex < m_vLineInsideBlock.size() ? m_vLineInsideBlock[nIndex] : false;
nImageIndex = sizeof(adwFlags) / sizeof(adwFlags[0]);
if (bInsideBlock)
DrawMarginIcon(pdc, rect, nImageIndex + 3);
if (dwLineFlags & (LF_COLLAPSIBLE_BLOCK_START | LF_COLLAPSIBLE_BLOCK_END))
{
int nImg= 0;
if (dwLineFlags & LF_COLLAPSIBLE_BLOCK_START && dwLineFlags & LF_COLLAPSED_BLOCK)
nImg = 2;
else if (dwLineFlags & LF_COLLAPSIBLE_BLOCK_END)
nImg = 1;
DrawMarginIcon(pdc, rect, nImageIndex + nImg);
}
DrawMarginMarker(nLineIndex, pdc, rect);
}
void CCrystalTextView::DrawMarginMarker(int nLine, CDC* pDC, const CRect &rect)
{}
void CCrystalTextView::DrawMarginIcon(CDC* pDC, const CRect &rect, int nImageIndex)
{
if (nImageIndex >= 0)
{
if (m_pIcons == NULL)
{
m_pIcons = new CImageList;
VERIFY(m_pIcons->Create(IDR_MARGIN_ICONS, 16, 16, RGB(255, 0, 255)));
}
CPoint pt(rect.left + 2, rect.top + (rect.Height() - 16) / 2);
VERIFY(m_pIcons->Draw(pDC, nImageIndex, pt, ILD_TRANSPARENT));
}
}
BOOL CCrystalTextView::IsInsideSelBlock(CPoint ptTextPos)
{
ASSERT_VALIDTEXTPOS(ptTextPos);
if (ptTextPos.y < m_ptDrawSelStart.y)
return FALSE;
if (ptTextPos.y > m_ptDrawSelEnd.y)
return FALSE;
if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
return TRUE;
if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
{
if (ptTextPos.y == m_ptDrawSelEnd.y)
return ptTextPos.x < m_ptDrawSelEnd.x;
ASSERT(ptTextPos.y == m_ptDrawSelStart.y);
return ptTextPos.x >= m_ptDrawSelStart.x;
}
ASSERT(m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
}
BOOL CCrystalTextView::IsInsideSelection(const CPoint &ptTextPos)
{
PrepareSelBounds();
return IsInsideSelBlock(ptTextPos);
}
void CCrystalTextView::PrepareSelBounds()
{
if (m_ptSelStart.y < m_ptSelEnd.y ||
(m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
{
m_ptDrawSelStart = m_ptSelStart;
m_ptDrawSelEnd = m_ptSelEnd;
}
else
{
m_ptDrawSelStart = m_ptSelEnd;
m_ptDrawSelEnd = m_ptSelStart;
}
}
void CCrystalTextView::OnDraw(CDC* pdc)
{
CRect rcClient;
GetClientRect(rcClient);
int nLineCount = GetLineCount();
int nLineHeight = GetLineHeight();
PrepareSelBounds();
CDC cacheDC;
cacheDC.CreateCompatibleDC(pdc);
if (cacheDC.m_hDC == 0)
return;
if (m_pCacheBitmap == NULL)
{
m_pCacheBitmap = new CBitmap;
VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), nLineHeight));
}
CBitmap* pOldBitmap= cacheDC.SelectObject(m_pCacheBitmap);
CRect rcLine;
rcLine = rcClient;
rcLine.bottom = rcLine.top + nLineHeight;
CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
// block vertical lines support
FindBlockBoundaries(m_nTopLine, rcClient.Height() / nLineHeight + 1);
for (int nCurrentLine = m_nTopLine; rcLine.top < rcClient.bottom; ++nCurrentLine)
{
int nLineIndex= nCurrentLine < nLineCount ? nCurrentLine : -1;
if (IsLineHidden(nCurrentLine))
{
// hidden lines inside collapsed block
continue; // do not draw hidden lines
}
else // normal (visible) lines
{
if (pdc->RectVisible(rcLine))
{
DrawMargin(&cacheDC, rcCacheMargin, nLineIndex);
DrawSingleLine(&cacheDC, rcCacheLine, nLineIndex);
VERIFY(pdc->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
}
}
rcLine.OffsetRect(0, nLineHeight);
}
cacheDC.SelectObject(pOldBitmap);
cacheDC.DeleteDC();
}
// This fn tries to determine if lines displayed lie inside collapsible text blocks
//
// (it's sensitive to spurious block ends; just like whole collapsing code; try
// a few closing brackets '}' without corresponding '{' ones to see what happens
// with lines below)
//
void CCrystalTextView::FindBlockBoundaries(int nTopLine, int nLines)
{
// this vector will tell me whether line N lies inside text block
m_vLineInsideBlock.clear();
if (nLines < 1 || m_pTextBuffer == NULL)
return;
int nLineCount= m_pTextBuffer->GetLineCount();
if (nTopLine >= nLineCount)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -