📄 textviewmouse.cpp
字号:
//
// 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 + -