⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 textviewpaint.cpp

📁 支持Unicode及Uniscribe的多语言输入的文本编辑器源码。
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//
//	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 + -