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

📄 textviewmouse.cpp

📁 支持Unicode及Uniscribe的多语言输入的文本编辑器源码。
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//
//	MODULE:		TextViewMouse.cpp
//
//	PURPOSE:	Mouse and caret support 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"

int ScrollDir(int counter, int dir);
bool IsKeyPressed(UINT nVirtKey);

//
//	WM_MOUSEACTIVATE
//
//	Grab the keyboard input focus 
//	
LONG TextView::OnMouseActivate(HWND hwndTop, UINT nHitTest, UINT nMessage)
{
	SetFocus(m_hWnd);
	return MA_ACTIVATE;
}

HMENU TextView::CreateContextMenu()
{
	HMENU hMenu = CreatePopupMenu();

	// do we have a selection?
	UINT fSelection = (m_nSelectionStart == m_nSelectionEnd) ?
		MF_DISABLED| MF_GRAYED : MF_ENABLED;

	// is there text on the clipboard?
	UINT fClipboard = (IsClipboardFormatAvailable(CF_TEXT) || IsClipboardFormatAvailable(CF_UNICODETEXT)) ?
		MF_ENABLED : MF_GRAYED | MF_DISABLED;

	UINT fCanUndo = CanUndo() ? MF_ENABLED : MF_GRAYED | MF_DISABLED;
	UINT fCanRedo = CanRedo() ? MF_ENABLED : MF_GRAYED | MF_DISABLED;

	AppendMenu(hMenu, MF_STRING|fCanUndo,				WM_UNDO, _T("&Undo"));
	AppendMenu(hMenu, MF_STRING|fCanRedo,				TXM_REDO, _T("&Redo"));
	AppendMenu(hMenu, MF_SEPARATOR,						0, 0);
	AppendMenu(hMenu, MF_STRING|fSelection,				WM_CUT,    _T("Cu&t"));
	AppendMenu(hMenu, MF_STRING|fSelection,				WM_COPY,   _T("&Copy"));
	AppendMenu(hMenu, MF_STRING|fClipboard,				WM_PASTE,  _T("&Paste"));
	AppendMenu(hMenu, MF_STRING|fSelection,				WM_CLEAR, _T("&Delete"));
	AppendMenu(hMenu, MF_SEPARATOR,						0, 0);
	AppendMenu(hMenu, MF_STRING|MF_ENABLED,				TXM_SETSELALL, _T("&Select All"));
	AppendMenu(hMenu, MF_SEPARATOR,						0, 0);
	AppendMenu(hMenu, MF_STRING|MF_ENABLED,				WM_USER+7, _T("&Right to left Reading order"));
	AppendMenu(hMenu, MF_STRING|MF_ENABLED,				WM_USER+8, _T("&Show Unicode control characters"));

	return hMenu;
}

//
//	WM_CONTEXTMENU
//
//	Respond to right-click message
//
LONG TextView::OnContextMenu(HWND hwndParam, int x, int y)
{
	if(m_hUserMenu == 0)
	{
		HMENU hMenu = CreateContextMenu();
		UINT  uCmd  = TrackPopupMenu(hMenu, TPM_RETURNCMD, x, y, 0, m_hWnd, 0);

		if(uCmd != 0)
			PostMessage(m_hWnd, uCmd, 0, 0);

		return 0;
	}
	else
	{
		UINT uCmd = TrackPopupMenu(m_hUserMenu, TPM_RETURNCMD, x, y, 0, m_hWnd, 0);

		if(uCmd != 0)
			PostMessage(GetParent(m_hWnd), WM_COMMAND, MAKEWPARAM(uCmd, 0), (LPARAM)GetParent(m_hWnd));

		return 0;
	}
	//PostMessage(m_hWnd, WM_COMMAND, MAKEWPARAM(uCmd, 0), (LPARAM)m_hWnd);
	
	return DefWindowProc(m_hWnd, WM_CONTEXTMENU, (WPARAM)hwndParam, MAKELONG(x,y));
}


//
//	WM_LBUTTONDOWN
//
//  Position caret to nearest text character under mouse
//
LONG TextView::OnLButtonDown(UINT nFlags, int mx, int my)
{
	ULONG nLineNo;
	ULONG nFileOff;
	
	// regular mouse input - mouse is within 
	if(mx >= LeftMarginWidth())
	{
		// map the mouse-coordinates to a real file-offset-coordinate
		MouseCoordToFilePos(mx, my, &nLineNo, &nFileOff, &m_nCaretPosX);
		m_nAnchorPosX = m_nCaretPosX;

		UpdateCaretXY(m_nCaretPosX, nLineNo);

		// Any key but <shift>
		if(IsKeyPressed(VK_SHIFT) == false)
		{
			// remove any existing selection
			InvalidateRange(m_nSelectionStart, m_nSelectionEnd);

			// reset cursor and selection offsets to the same location
			m_nSelectionStart	= nFileOff;
			m_nSelectionEnd		= nFileOff;
			m_nCursorOffset		= nFileOff;
		}
		else
		{
			// redraw to cursor
			InvalidateRange(m_nSelectionEnd, nFileOff);
			
			// extend selection to cursor
			m_nSelectionEnd		= nFileOff;
			m_nCursorOffset		= nFileOff;
		}

		if(IsKeyPressed(VK_MENU))
		{
			m_cpBlockStart.line = nLineNo;
			m_cpBlockStart.xpos = m_nCaretPosX;
			m_nSelectionType	= SEL_BLOCK;
		}
		else
		{
			m_nSelectionType	= SEL_NORMAL;
		}

		// set capture for mouse-move selections
		m_nSelectionMode = IsKeyPressed(VK_MENU) ? SEL_BLOCK : SEL_NORMAL;
	}
	// mouse clicked within margin 
	else
	{
		// remove any existing selection
		InvalidateRange(m_nSelectionStart, m_nSelectionEnd);

		nLineNo = (my / m_nLineHeight) + m_nVScrollPos;

		//
		// if we click in the margin then jump back to start of line
		//
		if(m_nHScrollPos != 0)
		{
			m_nHScrollPos = 0;
			SetupScrollbars();
			RefreshWindow();
		}

		m_pTextDoc->lineinfo_from_lineno(nLineNo, &m_nSelectionStart, &m_nSelectionEnd, 0, 0);
		m_nSelectionEnd    += m_nSelectionStart;
		m_nCursorOffset	    = m_nSelectionStart;
		
		m_nSelMarginOffset1 = m_nSelectionStart;
		m_nSelMarginOffset2 = m_nSelectionEnd;

		InvalidateRange(m_nSelectionStart, m_nSelectionEnd);
		
		UpdateCaretOffset(m_nCursorOffset, FALSE, &m_nCaretPosX, &m_nCurrentLine);
		m_nAnchorPosX = m_nCaretPosX;

		// set capture for mouse-move selections
		m_nSelectionMode = SEL_MARGIN;
	}

	UpdateLine(nLineNo);

	SetCapture(m_hWnd);

	TVNCURSORINFO ci = { { 0 }, nLineNo, 0, m_nCursorOffset };
	NotifyParent(TVN_CURSOR_CHANGE, (NMHDR *)&ci);
	return 0;
}

//
//	WM_LBUTTONUP 
//
//	Release capture and cancel any mouse-scrolling
//
LONG TextView::OnLButtonUp(UINT nFlags, int mx, int my)
{
	// shift cursor to end of selection
	if(m_nSelectionMode == SEL_MARGIN)
	{
		m_nCursorOffset = m_nSelectionEnd;
		UpdateCaretOffset(m_nCursorOffset, FALSE, &m_nCaretPosX, &m_nCurrentLine);
	}

	if(m_nSelectionMode)
	{
		// cancel the scroll-timer if it is still running
		if(m_nScrollTimer != 0)
		{
			KillTimer(m_hWnd, m_nScrollTimer);
			m_nScrollTimer = 0;
		}

		m_nSelectionMode = SEL_NONE;
		ReleaseCapture();
	}

	return 0;
}

//
//	WM_LBUTTONDBKCLK
//
//	Select the word under the mouse
//
LONG TextView::OnLButtonDblClick(UINT nFlags, int mx, int my)
{
	// remove any existing selection
	InvalidateRange(m_nSelectionStart, m_nSelectionEnd);

	// regular mouse input - mouse is within scrolling viewport
	if(mx >= LeftMarginWidth())
	{
		ULONG lineno, fileoff;
		int   xpos;

		// map the mouse-coordinates to a real file-offset-coordinate
		MouseCoordToFilePos(mx, my, &lineno, &fileoff, &xpos);
		m_nAnchorPosX = m_nCaretPosX;

		// move selection-start to start of word
		MoveWordStart();
		m_nSelectionStart = m_nCursorOffset;

		// move selection-end to end of word
		MoveWordEnd();
		m_nSelectionEnd = m_nCursorOffset;

		// update caret position
		InvalidateRange(m_nSelectionStart, m_nSelectionEnd);
		UpdateCaretOffset(m_nCursorOffset, TRUE, &m_nCaretPosX, &m_nCurrentLine);
		m_nAnchorPosX = m_nCaretPosX;

		NotifyParent(TVN_CURSOR_CHANGE);
	}

	return 0;
}

//
//	WM_MOUSEMOVE
//
//	Set the selection end-point if we are dragging the mouse
//
LONG TextView::OnMouseMove(UINT nFlags, int mx, int my)
{
	if(m_nSelectionMode)
	{
		ULONG	nLineNo, nFileOff;
		BOOL	fCurChanged = FALSE;

		RECT	rect;
		POINT	pt = { mx, my };

		//
		//	First thing we must do is switch from margin-mode to normal-mode 
		//	if the mouse strays into the main document area
		//
		if(m_nSelectionMode == SEL_MARGIN && mx > LeftMarginWidth())
		{
			m_nSelectionMode = SEL_NORMAL;
			SetCursor(LoadCursor(0, IDC_IBEAM));
		}

		//
		//	Mouse-scrolling: detect if the mouse
		//	is inside/outside of the TextView scrolling area
		//  and stop/start a scrolling timer appropriately
		//
		GetClientRect(m_hWnd, &rect);
		
		// build the scrolling area
		rect.bottom -= rect.bottom % m_nLineHeight;
		rect.left   += LeftMarginWidth();

		// If mouse is within this area, we don't need to scroll
		if(PtInRect(&rect, pt))
		{
			if(m_nScrollTimer != 0)
			{
				KillTimer(m_hWnd, m_nScrollTimer);
				m_nScrollTimer = 0;
			}
		}
		// If mouse is outside window, start a timer in
		// order to generate regular scrolling intervals
		else 
		{
			if(m_nScrollTimer == 0)
			{
				m_nScrollCounter = 0;
				m_nScrollTimer   = SetTimer(m_hWnd, 1, 30, 0);
			}
		}

		// get new cursor offset+coordinates
		MouseCoordToFilePos(mx, my, &nLineNo, &nFileOff, &m_nCaretPosX);
		m_nAnchorPosX = m_nCaretPosX;

		m_cpBlockEnd.line = nLineNo;
		m_cpBlockEnd.xpos = mx + m_nHScrollPos * m_nFontWidth - LeftMarginWidth();//m_nCaretPosX;


		// redraw the old and new lines if they are different
		UpdateLine(nLineNo);

		// update the region of text that has changed selection state
		fCurChanged = m_nSelectionEnd == nFileOff ? FALSE : TRUE;
		//if(m_nSelectionEnd != nFileOff)
		{
			ULONG linelen;
			m_pTextDoc->lineinfo_from_lineno(nLineNo, 0, &linelen, 0, 0);

			m_nCursorOffset	= nFileOff;

			if(m_nSelectionMode == SEL_MARGIN)
			{
				if(nFileOff >= m_nSelectionStart)
				{
					nFileOff += linelen;
					m_nSelectionStart = m_nSelMarginOffset1;
				}
				else
				{
					m_nSelectionStart = m_nSelMarginOffset2;
				}
			}

			// redraw from old selection-pos to new position
			InvalidateRange(m_nSelectionEnd, nFileOff);

			// adjust the cursor + selection to the new offset
			m_nSelectionEnd = nFileOff;
		}

		if(m_nSelectionMode == SEL_BLOCK)
			RefreshWindow();

		//m_nCaretPosX = mx+m_nHScrollPos*m_nFontWidth-LeftMarginWidth();
		// always set the caret position because we might be scrolling
		UpdateCaretXY(m_nCaretPosX, m_nCurrentLine);

		if(fCurChanged)
		{
			NotifyParent(TVN_CURSOR_CHANGE);
		}
	}
	// mouse isn't being used for a selection, so set the cursor instead
	else
	{
		if(mx < LeftMarginWidth())
		{
			SetCursor(m_hMarginCursor);
		}
		else
		{
			//OnLButtonDown(0, mx, my);
			SetCursor(LoadCursor(0, IDC_IBEAM));
		}

	}

	return 0;
}

//
//	WM_TIMER handler
//
//	Used to create regular scrolling 
//
LONG TextView::OnTimer(UINT nTimerId)
{
	int	  dx = 0, dy = 0;	// scrolling vectors
	RECT  rect;
	POINT pt;
	
	// find client area, but make it an even no. of lines
	GetClientRect(m_hWnd, &rect);
	rect.bottom -= rect.bottom % m_nLineHeight;
	rect.left   += LeftMarginWidth();

⌨️ 快捷键说明

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