📄 textviewpaint.cpp
字号:
//
// MODULE: TextViewPaint.cpp
//
// PURPOSE: Painting and display for the TextView control
//
// NOTES: www.catch22.net
//
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
#include "TextView.h"
#include "TextViewInternal.h"
void PaintRect(HDC hdc, int x, int y, int width, int height, COLORREF fill);
void PaintRect(HDC hdc, RECT *rect, COLORREF fill);
void DrawCheckedRect(HDC hdc, RECT *rect, COLORREF fg, COLORREF bg);
extern "C" COLORREF MixRGB(COLORREF, COLORREF);
//
// Perform a full redraw of the entire window
//
VOID TextView::RefreshWindow()
{
InvalidateRect(m_hWnd, NULL, FALSE);
}
USPCACHE *TextView::GetUspCache(HDC hdc, ULONG nLineNo, ULONG *nOffset/*=0*/)
{
TCHAR buff[TEXTBUFSIZE];
ATTR attr[TEXTBUFSIZE];
ULONG colno = 0;
ULONG off_chars = 0;
int len;
HDC hdcTemp;
USPDATA *uspData;
ULONG lru_usage = -1;
int lru_index = 0;
//
// Search the cache to see if we've already analyzed the requested line
//
for(int i = 0; i < USP_CACHE_SIZE; i++)
{
// remember the least-recently used
if(m_uspCache[i].usage < lru_usage)
{
lru_index = i;
lru_usage = m_uspCache[i].usage;
}
// match the line#
if(m_uspCache[i].usage > 0 && m_uspCache[i].lineno == nLineNo)
{
if(nOffset)
*nOffset = m_uspCache[i].offset;
m_uspCache[i].usage++;
return &m_uspCache[i];
}
}
//
// not found? overwrite the "least-recently-used" entry
//
m_uspCache[lru_index].lineno = nLineNo;
m_uspCache[lru_index].usage = 1;
uspData = m_uspCache[lru_index].uspData;
if(hdc == 0) hdcTemp = GetDC(m_hWnd);
else hdcTemp = hdc;
//
// get the text for the entire line and apply style attributes
//
len = m_pTextDoc->getline(nLineNo, buff, TEXTBUFSIZE, &off_chars);
// cache the line's offset and length information
m_uspCache[lru_index].offset = off_chars;
m_uspCache[lru_index].length = len;
m_uspCache[lru_index].length_CRLF = len - CRLF_size(buff, len);
len = ApplyTextAttributes(nLineNo, off_chars, colno, buff, len, attr);
//
// setup the tabs + itemization states
//
int tablist[] = { m_nTabWidthChars };
SCRIPT_TABDEF tabdef = { 1, 0, tablist, 0 };
SCRIPT_CONTROL scriptControl = { 0 };
SCRIPT_STATE scriptState = { 0 };
//SCRIPT_DIGITSUBSTITUTE scriptDigitSub;
//ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &scriptDigitSub);
//ScriptApplyDigitSubstitution(&scriptDigitSub, &scriptControl, &scriptState);
//
// go!
//
UspAnalyze(
uspData,
hdcTemp,
buff,
len,
attr,
0,
m_uspFontList,
&scriptControl,
&scriptState,
&tabdef
);
//
// Modify CR/LF so cursor cannot traverse into them
//
//MarkCRLF(uspData, buff, len, attr);
//
// Apply the selection
//
ApplySelection(uspData, nLineNo, off_chars, len);
if(hdc == 0)
ReleaseDC(m_hWnd, hdcTemp);
if(nOffset)
*nOffset = off_chars;
return &m_uspCache[lru_index];
}
//
// Return a fully-analyzed USPDATA object for the specified line
//
USPDATA *TextView::GetUspData(HDC hdc, ULONG nLineNo, ULONG *nOffset/*=0*/)
{
USPCACHE *uspCache = GetUspCache(hdc, nLineNo, nOffset);
if(uspCache)
return uspCache->uspData;
else
return 0;
}
//
// Invalidate every entry in the cache so we can start afresh
//
void TextView::ResetLineCache()
{
for(int i = 0; i < USP_CACHE_SIZE; i++)
{
m_uspCache[i].usage = 0;
}
}
//
// Painting procedure for TextView objects
//
LONG TextView::OnPaint()
{
PAINTSTRUCT ps;
ULONG i;
ULONG first;
ULONG last;
HRGN hrgnUpdate;
HDC hdcMem;
HBITMAP hbmMem;
RECT rect;
//
// get update region *before* BeginPaint validates the window
//
hrgnUpdate = CreateRectRgn(0,0,1,1);
GetUpdateRgn(m_hWnd, hrgnUpdate, FALSE);
//
// create a memoryDC the same size a single line, for double-buffering
//
BeginPaint(m_hWnd, &ps);
GetClientRect(m_hWnd, &rect);
hdcMem = CreateCompatibleDC(ps.hdc);
hbmMem = CreateCompatibleBitmap(ps.hdc, rect.right-rect.left, m_nLineHeight);
SelectObject(hdcMem, hbmMem);
//
// figure out which lines to redraw
//
first = m_nVScrollPos + ps.rcPaint.top / m_nLineHeight;
last = m_nVScrollPos + ps.rcPaint.bottom / m_nLineHeight;
// make sure we never wrap around the 4gb boundary
if(last < first)
last = -1;
//
// draw the display line-by-line
//
for(i = first; i <= last; i++)
{
int sx = 0;
int sy = (i - m_nVScrollPos) * m_nLineHeight;
int width = rect.right-rect.left;
// prep the background
PaintRect(hdcMem, 0, 0, width, m_nLineHeight, LineColour(i));
//PaintRect(hdcMem, m_cpBlockStart.xpos+LeftMarginWidth(), 0, m_cpBlockEnd.xpos-m_cpBlockStart.xpos, m_nLineHeight,GetColour(TXC_HIGHLIGHT));
// draw each line into the offscreen buffer
PaintLine(hdcMem, i, -m_nHScrollPos * m_nFontWidth, 0, hrgnUpdate);
// transfer to screen
BitBlt( ps.hdc, sx, sy, width, m_nLineHeight, hdcMem, 0, 0, SRCCOPY);
}
//
// Cleanup
//
EndPaint(m_hWnd, &ps);
DeleteDC(hdcMem);
DeleteObject(hbmMem);
DeleteObject(hrgnUpdate);
return 0;
}
//
// Draw the specified line (including margins etc) to the specified location
//
void TextView::PaintLine(HDC hdc, ULONG nLineNo, int xpos, int ypos, HRGN hrgnUpdate)
{
RECT bounds;
HRGN hrgnBounds;
GetClientRect(m_hWnd, &bounds);
SelectClipRgn(hdc, NULL);
// no point in drawing outside the window-update-region
if(hrgnUpdate != NULL)
{
// work out where the line would have been on-screen
bounds.left = (long)(-m_nHScrollPos * m_nFontWidth + LeftMarginWidth());
bounds.top = (long)((nLineNo - m_nVScrollPos) * m_nLineHeight);
bounds.right = (long)(bounds.right);
bounds.bottom = (long)(bounds.top + m_nLineHeight);
// clip the window update-region with the line's bounding rectangle
hrgnBounds = CreateRectRgnIndirect(&bounds);
CombineRgn(hrgnBounds, hrgnUpdate, hrgnBounds, RGN_AND);
// work out the bounding-rectangle of this intersection
GetRgnBox(hrgnBounds, &bounds);
bounds.top = 0;
bounds.bottom = m_nLineHeight;
}
PaintText(hdc, nLineNo, xpos + LeftMarginWidth(), ypos, &bounds);
DeleteObject(hrgnBounds);
SelectClipRgn(hdc, NULL);
//
// draw the margin straight over the top
//
if(LeftMarginWidth() > 0)
{
PaintMargin(hdc, nLineNo, 0, 0);
}
}
//
// Return width of margin
//
int TextView::LeftMarginWidth()
{
int width = 0;
int cx = 0;
int cy = 0;
// get dimensions of imagelist icons
if(m_hImageList)
ImageList_GetIconSize(m_hImageList, &cx, &cy);
if(CheckStyle(TXS_LINENUMBERS))
{
width += m_nLinenoWidth;
if(CheckStyle(TXS_SELMARGIN) && cx > 0)
width += cx + 4;
else
width += 20;
if(1) width += 1;
if(0) width += 5;
return width;
}
// selection margin by itself
else if(CheckStyle(TXS_SELMARGIN))
{
width += cx + 4;
if(0) width += 1;
if(0) width += 5;
return width;
}
return 0;
}
//
// This must be called whenever the number of lines changes
// (probably easier to call it when the file-size changes)
//
void TextView::UpdateMarginWidth()
{
HDC hdc = GetDC(m_hWnd);
HANDLE hOldFont = SelectObject(hdc, m_uspFontList[0].hFont);
TCHAR buf[32];
int len = wsprintf(buf, LINENO_FMT, m_nLineCount);
m_nLinenoWidth = TextWidth(hdc, buf, len);
SelectObject(hdc, hOldFont);
ReleaseDC(m_hWnd, hdc);
}
//
// Draw the specified line's margin into the area described by *margin*
//
int TextView::PaintMargin(HDC hdc, ULONG nLineNo, int xpos, int ypos)
{
RECT rect = { xpos, ypos, xpos + LeftMarginWidth(), ypos + m_nLineHeight };
int imgWidth;
int imgHeight;
int imgX;
int imgY;
int selwidth = CheckStyle(TXS_SELMARGIN) ? 20 : 0;
TCHAR ach[32];
//int nummaxwidth = 60;
if(m_hImageList && selwidth > 0)
{
// selection margin must include imagelists
ImageList_GetIconSize(m_hImageList, &imgWidth, &imgHeight);
imgX = xpos + (selwidth - imgWidth) / 2;
imgY = ypos + (m_nLineHeight - imgHeight) / 2;
}
if(CheckStyle(TXS_LINENUMBERS))
{
HANDLE hOldFont = SelectObject(hdc, m_uspFontList[0].hFont);
int len = wsprintf(ach, LINENO_FMT, nLineNo + 1);
int width = TextWidth(hdc, ach, len);
// only draw line number if in-range
if(nLineNo >= m_nLineCount)
len = 0;
rect.right = rect.left + m_nLinenoWidth;
if(CheckStyle(TXS_SELMARGIN) && m_hImageList)
{
imgX = rect.right;
rect.right += imgWidth + 4;
}
else
{
rect.right += 20;
}
SetTextColor(hdc, GetColour(TXC_LINENUMBERTEXT));
SetBkColor(hdc, GetColour(TXC_LINENUMBER));
ExtTextOut( hdc,
rect.left + m_nLinenoWidth - width,
rect.top + NeatTextYOffset(&m_uspFontList[0]),
ETO_OPAQUE | ETO_CLIPPED,
&rect,
ach,
len,
0);
// vertical line
rect.left = rect.right;
rect.right += 1;
//PaintRect(hdc, &rect, MixRGB(GetSysColor(COLOR_3DFACE), 0xffffff));
PaintRect(hdc, &rect, GetColour(TXC_BACKGROUND));
// bleed area - use this to draw "folding" arrows
/*rect.left = rect.right;
rect.right += 5;
PaintRect(hdc, &rect, GetColour(TXC_BACKGROUND));*/
SelectObject(hdc, hOldFont);
}
else
{
DrawCheckedRect(hdc, &rect, GetColour(TXC_SELMARGIN1), GetColour(TXC_SELMARGIN2));
}
//
// Retrieve information about this specific line
//
LINEINFO *linfo = GetLineInfo(nLineNo);
if(m_hImageList && linfo && nLineNo < m_nLineCount)
{
ImageList_DrawEx(
m_hImageList,
linfo->nImageIdx,
hdc,
imgX,
imgY,
imgWidth,
imgHeight,
CLR_NONE,
CLR_NONE,
ILD_TRANSPARENT
);
}
return rect.right-rect.left;
}
//
// Draw a line of text into the specified device-context
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -