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

📄 textviewkeynav.cpp

📁 支持Unicode及Uniscribe的多语言输入的文本编辑器源码。
💻 CPP
字号:
//
//	MODULE:		TextViewKeyNav.cpp
//
//	PURPOSE:	Keyboard navigation for TextView
//
//	NOTES:		www.catch22.net
//

#define STRICT
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <tchar.h>
#include "TextView.h"
#include "TextViewInternal.h"

/*struct SCRIPT_LOGATTR
{ 
  BYTE fSoftBreak	:1; 
  BYTE fWhiteSpace	:1; 
  BYTE fCharStop	:1; 
  BYTE fWordStop	:1; 
  BYTE fInvalid		:1; 
  BYTE fReserved	:3; 
};*/


bool IsKeyPressed(UINT nVirtKey)
{
	return GetKeyState(nVirtKey) < 0 ? true : false;
}

//
//	Get the UspCache and logical attributes for specified line
//
bool TextView::GetLogAttr(ULONG nLineNo, USPCACHE **puspCache, CSCRIPT_LOGATTR **plogAttr, ULONG *pnOffset)
{
	if((*puspCache = GetUspCache(0, nLineNo, pnOffset)) == 0)
		return false;

	if(plogAttr && (*plogAttr = UspGetLogAttr((*puspCache)->uspData)) == 0)
		return false;

	return true;
}

//
//	Move caret up specified number of lines
//
VOID TextView::MoveLineUp(int numLines)
{
	USPDATA			* uspData;
	ULONG			  lineOffset;
	
	int				  charPos;
	BOOL			  trailing;

	m_nCurrentLine -= min(m_nCurrentLine, (unsigned)numLines);

	// get Uniscribe data for prev line
	uspData = GetUspData(0, m_nCurrentLine, &lineOffset);

	// move up to character nearest the caret-anchor positions
	UspXToOffset(uspData, m_nAnchorPosX, &charPos, &trailing, 0);

	m_nCursorOffset = lineOffset + charPos + trailing;
}

//
//	Move caret down specified number of lines
//
VOID TextView::MoveLineDown(int numLines)
{
	USPDATA			* uspData;
	ULONG			  lineOffset;
	
	int				  charPos;
	BOOL			  trailing;

	m_nCurrentLine += min(m_nLineCount-m_nCurrentLine-1, (unsigned)numLines);

	// get Uniscribe data for prev line
	uspData = GetUspData(0, m_nCurrentLine, &lineOffset);

	// move down to character nearest the caret-anchor position
	UspXToOffset(uspData, m_nAnchorPosX, &charPos, &trailing, 0);

	m_nCursorOffset = lineOffset + charPos + trailing;
}

//
//	Move to start of previous word (to the left)
//
VOID TextView::MoveWordPrev()
{
	USPCACHE		* uspCache;
	CSCRIPT_LOGATTR * logAttr;
	ULONG			  lineOffset;
	int				  charPos;

	// get Uniscribe data for current line
	if(!GetLogAttr(m_nCurrentLine, &uspCache, &logAttr, &lineOffset))
		return;

	// move 1 character to left
	charPos = m_nCursorOffset - lineOffset - 1; 

	// skip to end of *previous* line if necessary
	if(charPos < 0)
	{
		charPos = 0;
		
		if(m_nCurrentLine > 0)
		{
			MoveLineEnd(m_nCurrentLine-1);		
			return;
		}
	}

	// skip preceding whitespace
	while(charPos > 0 && logAttr[charPos].fWhiteSpace)
		charPos--;

	// skip whole characters until we hit a word-break/more whitespace
	for( ; charPos > 0 ; charPos--)
	{
		if(logAttr[charPos].fWordStop || logAttr[charPos-1].fWhiteSpace)
			break;
	}

	m_nCursorOffset = lineOffset + charPos;
}

//
//	Move to start of next word
//
VOID TextView::MoveWordNext()
{
	USPCACHE		* uspCache;
	CSCRIPT_LOGATTR * logAttr;
	ULONG			  lineOffset;
	int				  charPos;

	// get Uniscribe data for current line
	if(!GetLogAttr(m_nCurrentLine, &uspCache, &logAttr, &lineOffset))
		return;

	charPos = m_nCursorOffset - lineOffset;

	// if already at end-of-line, skip to next line
	if(charPos == uspCache->length_CRLF)
	{
		if(m_nCurrentLine + 1 < m_nLineCount)
			MoveLineStart(m_nCurrentLine+1);

		return;
	}

	// if already on a word-break, go to next char
	if(logAttr[charPos].fWordStop)
		charPos++;

	// skip whole characters until we hit a word-break/more whitespace
	for( ; charPos < uspCache->length_CRLF; charPos++)
	{
		if(logAttr[charPos].fWordStop || logAttr[charPos].fWhiteSpace)
			break;
	}

	// skip trailing whitespace
	while(charPos < uspCache->length_CRLF && logAttr[charPos].fWhiteSpace)
		charPos++;

	m_nCursorOffset = lineOffset + charPos;
}

//
//	Move to start of current word
//
VOID TextView::MoveWordStart()
{
	USPCACHE		* uspCache;
	CSCRIPT_LOGATTR * logAttr;
	ULONG			  lineOffset;
	int				  charPos;

	// get Uniscribe data for current line
	if(!GetLogAttr(m_nCurrentLine, &uspCache, &logAttr, &lineOffset))
		return;

	charPos  = m_nCursorOffset - lineOffset;

	while(charPos > 0 && !logAttr[charPos-1].fWhiteSpace)
		charPos--;

	m_nCursorOffset = lineOffset + charPos;
}

//
//	Move to end of current word
//
VOID TextView::MoveWordEnd()
{
	USPCACHE		* uspCache;
	CSCRIPT_LOGATTR * logAttr;
	ULONG			  lineOffset;
	int				  charPos;

	// get Uniscribe data for current line
	if(!GetLogAttr(m_nCurrentLine, &uspCache, &logAttr, &lineOffset))
		return;

	charPos  = m_nCursorOffset - lineOffset;

	while(charPos < uspCache->length_CRLF && !logAttr[charPos].fWhiteSpace)
		charPos++;

	m_nCursorOffset = lineOffset + charPos;
}

//
//	Move to previous character
//
VOID TextView::MoveCharPrev()
{
	USPCACHE		* uspCache;
	CSCRIPT_LOGATTR * logAttr;
	ULONG			  lineOffset;
	int				  charPos;

	// get Uniscribe data for current line
	if(!GetLogAttr(m_nCurrentLine, &uspCache, &logAttr, &lineOffset))
		return;

	charPos = m_nCursorOffset - lineOffset;

	// find the previous valid character-position
	for( --charPos; charPos >= 0; charPos--)
	{
		if(logAttr[charPos].fCharStop)
			break;
	}

	// move up to end-of-last line if necessary
	if(charPos < 0)
	{
		charPos  = 0;

		if(m_nCurrentLine > 0)
		{
			MoveLineEnd(m_nCurrentLine-1);
			return;
		}
	}

	// update cursor position
	m_nCursorOffset = lineOffset + charPos;
}

//
//	Move to next character
//
VOID TextView::MoveCharNext()
{
	USPCACHE		* uspCache;
	CSCRIPT_LOGATTR * logAttr;
	ULONG			  lineOffset;
	int				  charPos;

	// get Uniscribe data for specified line
	if(!GetLogAttr(m_nCurrentLine, &uspCache, &logAttr, &lineOffset))
		return;

	charPos = m_nCursorOffset - lineOffset;

	// find the next valid character-position
	for( ++charPos; charPos <= uspCache->length_CRLF; charPos++)
	{
		if(logAttr[charPos].fCharStop)
			break;
	}

	// skip to beginning of next line if we hit the CR/LF
	if(charPos > uspCache->length_CRLF)
	{
		if(m_nCurrentLine + 1 < m_nLineCount)
			MoveLineStart(m_nCurrentLine+1);
	}
	// otherwise advance the character-position
	else
	{
		m_nCursorOffset = lineOffset + charPos;
	}
}

//
//	Move to start of specified line
//
VOID TextView::MoveLineStart(ULONG lineNo)
{
	ULONG			  lineOffset;
	USPCACHE		* uspCache;
	CSCRIPT_LOGATTR * logAttr;
	int				  charPos;
	
	// get Uniscribe data for current line
	if(!GetLogAttr(lineNo, &uspCache, &logAttr, &lineOffset))
		return;

	charPos  = m_nCursorOffset - lineOffset;
	
	// if already at start of line, skip *forwards* past any whitespace
	if(m_nCursorOffset == lineOffset)
	{
		// skip whitespace
		while(logAttr[m_nCursorOffset - lineOffset].fWhiteSpace)
			m_nCursorOffset++;
	}
	// if not at start, goto start
	else
	{
		m_nCursorOffset = lineOffset;
	}
}

//
//	Move to end of specified line
//
VOID TextView::MoveLineEnd(ULONG lineNo)
{
	USPCACHE *uspCache;
	
	if((uspCache = GetUspCache(0, lineNo)) == 0)
		return;

	m_nCursorOffset = uspCache->offset + uspCache->length_CRLF;
}

//
//	Move to start of file
//
VOID TextView::MoveFileStart()
{
	m_nCursorOffset = 0;
}

//
//	Move to end of file
//
VOID TextView::MoveFileEnd()
{
	m_nCursorOffset = m_pTextDoc->size();
}


//
//	Process keyboard-navigation keys
//
LONG TextView::OnKeyDown(UINT nKeyCode, UINT nFlags)
{
	bool fCtrlDown	= IsKeyPressed(VK_CONTROL);
	bool fShiftDown	= IsKeyPressed(VK_SHIFT);
	BOOL fAdvancing = FALSE;

	//
	//	Process the key-press. Cursor movement is different depending
	//	on if <ctrl> is held down or not, so act accordingly
	//
	switch(nKeyCode)
	{
	case VK_SHIFT: case VK_CONTROL:
		return 0;

	// CTRL+Z undo
	case 'z': case 'Z':
		
		if(fCtrlDown && Undo())
			NotifyParent(TVN_CHANGED);

		return 0;

	// CTRL+Y redo
	case 'y': case 'Y':
		
		if(fCtrlDown && Redo()) 
			NotifyParent(TVN_CHANGED);

		return 0;

	// Change insert mode / clipboard copy&paste
	case VK_INSERT:

		if(fCtrlDown)
		{
			OnCopy();
			NotifyParent(TVN_CHANGED);
		}
		else if(fShiftDown)
		{
			OnPaste();
			NotifyParent(TVN_CHANGED);
		}
		else
		{
			if(m_nEditMode == MODE_INSERT)
				m_nEditMode = MODE_OVERWRITE;

			else if(m_nEditMode == MODE_OVERWRITE)
				m_nEditMode = MODE_INSERT;

			NotifyParent(TVN_EDITMODE_CHANGE);
		}

		return 0;

	case VK_DELETE:

		if(m_nEditMode != MODE_READONLY)
		{
			if(fShiftDown)
				OnCut();
			else
				ForwardDelete();

			NotifyParent(TVN_CHANGED);
		}
		return 0;

	case VK_BACK:

		if(m_nEditMode != MODE_READONLY)
		{
			BackDelete();
			fAdvancing = FALSE;

			NotifyParent(TVN_CHANGED);
		}
		return 0;

	case VK_LEFT:

		if(fCtrlDown)	MoveWordPrev();
		else			MoveCharPrev();

		fAdvancing = FALSE;
		break;

	case VK_RIGHT:
		
		if(fCtrlDown)	MoveWordNext();
		else			MoveCharNext();
			
		fAdvancing = TRUE;
		break;

	case VK_UP:
		if(fCtrlDown)	Scroll(0, -1);
		else			MoveLineUp(1);
		break;

	case VK_DOWN:
		if(fCtrlDown)	Scroll(0, 1);
		else			MoveLineDown(1);
		break;

	case VK_PRIOR:
		if(!fCtrlDown)	MoveLineUp(m_nWindowLines);
		break;

	case VK_NEXT:
		if(!fCtrlDown)	MoveLineDown(m_nWindowLines);
		break;

	case VK_HOME:
		if(fCtrlDown)	MoveFileStart();
		else			MoveLineStart(m_nCurrentLine);
		break;

	case VK_END:
		if(fCtrlDown)	MoveFileEnd();
		else			MoveLineEnd(m_nCurrentLine);
		break;

	default:
		return 0;
	}

	// Extend selection if <shift> is down
	if(fShiftDown)
	{		
		InvalidateRange(m_nSelectionEnd, m_nCursorOffset);
		m_nSelectionEnd	= m_nCursorOffset;
	}
	// Otherwise clear the selection
	else
	{
		if(m_nSelectionStart != m_nSelectionEnd)
			InvalidateRange(m_nSelectionStart, m_nSelectionEnd);

		m_nSelectionEnd		= m_nCursorOffset;
		m_nSelectionStart	= m_nCursorOffset;
	}

	// update caret-location (xpos, line#) from the offset
	UpdateCaretOffset(m_nCursorOffset, fAdvancing, &m_nCaretPosX, &m_nCurrentLine);
	
	// maintain the caret 'anchor' position *except* for up/down actions
	if(nKeyCode != VK_UP && nKeyCode != VK_DOWN)
	{
		m_nAnchorPosX = m_nCaretPosX;

		// scroll as necessary to keep caret within viewport
		ScrollToPosition(m_nCaretPosX, m_nCurrentLine);
	}
	else
	{
		// scroll as necessary to keep caret within viewport
		if(!fCtrlDown)
			ScrollToPosition(m_nCaretPosX, m_nCurrentLine);
	}

	NotifyParent(TVN_CURSOR_CHANGE);

	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -