📄 textdiagramctrl.cpp
字号:
// TextDiagramCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "TextDiagramCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define CHARSIZE_X 8
#define CHARSIZE_Y 12
#define ARROWSIZE 6
#define IDC_EDITTEXT 101
enum
{
ID_RECT_DELETE = 32771,
ID_RECT_EDITTEXT,
ID_RECT_NEW,
ID_CONN_NEW,
ID_CONN_DELETE,
ID_CONN_MODIFY,
ID_RESET,
};
/////////////////////////////////////////////////////////////////////////////
// CTextDiagramCtrl
CTextDiagramCtrl::CTextDiagramCtrl() :
m_nSelRect(-1), m_ptScrollOffset(0, 0), m_bMoving(FALSE), m_nSizing(HTNOWHERE),
m_ptDragStart(0, 0), m_ptDragPrev(0, 0), m_editText("-|#*+", TRUE), m_bDblClick(FALSE), m_ptContextMenu(0, 0)
{
}
CTextDiagramCtrl::~CTextDiagramCtrl()
{
}
BEGIN_MESSAGE_MAP(CTextDiagramCtrl, CWnd)
//{{AFX_MSG_MAP(CTextDiagramCtrl)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
ON_WM_LBUTTONDOWN()
ON_WM_CREATE()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_SIZE()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_CAPTURECHANGED()
ON_WM_NCHITTEST()
ON_WM_SETCURSOR()
ON_WM_LBUTTONDBLCLK()
ON_WM_GETDLGCODE()
ON_WM_KEYDOWN()
ON_WM_CONTEXTMENU()
ON_COMMAND(ID_RECT_DELETE, OnRectDelete)
ON_COMMAND(ID_RECT_EDITTEXT, OnRectEditText)
ON_COMMAND(ID_RECT_NEW, OnRectNew)
ON_COMMAND(ID_CONN_NEW, OnConnNew)
ON_COMMAND(ID_CONN_DELETE, OnConnDelete)
ON_COMMAND(ID_CONN_MODIFY, OnConnModify)
ON_COMMAND(ID_RESET, OnReset)
//}}AFX_MSG_MAP
ON_EN_KILLFOCUS(IDC_EDITTEXT, OnEndTextEdit)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTextDiagramCtrl message handlers
BOOL CTextDiagramCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParent, UINT nID)
{
return CWnd::Create(NULL, NULL, dwStyle | WS_HSCROLL | WS_VSCROLL, rect, pParent, nID);
}
void CTextDiagramCtrl::SetDiagram(const CTextDiagram& diagram)
{
CTextDiagram::SetDiagram(diagram);
m_nSelRect = -1;
SendNotification(TDN_SELCHANGE);
UpdateScrollbars();
Invalidate();
}
void CTextDiagramCtrl::SetDiagram(LPCTSTR szDiagram)
{
CTextDiagram::SetDiagram(szDiagram);
m_nSelRect = -1;
SendNotification(TDN_SELCHANGE);
UpdateScrollbars();
Invalidate();
}
void CTextDiagramCtrl::SetDiagram(const CStringArray& diagram)
{
CTextDiagram::SetDiagram(diagram);
m_nSelRect = -1;
SendNotification(TDN_SELCHANGE);
UpdateScrollbars();
Invalidate();
}
void CTextDiagramCtrl::ResetDiagram()
{
CTextDiagram::ResetDiagram();
m_nSelRect = -1;
SendNotification(TDN_SELCHANGE);
UpdateScrollbars();
Invalidate();
}
int CTextDiagramCtrl::AddRect(LPCRECT pRect, LPCTSTR szText)
{
int nRect = CTextDiagram::AddRect(pRect, szText);
if (nRect >= 0)
{
UpdateScrollbars();
Invalidate();
m_nSelRect = nRect;
SendNotification(TDN_SELCHANGE);
}
return nRect;
}
int CTextDiagramCtrl::AddConnection(int nRectFrom, int nRectTo, int nSideFrom)
{
int nConn = CTextDiagram::AddConnection(nRectFrom, nRectTo, nSideFrom);
if (nConn >= 0)
{
UpdateScrollbars();
Invalidate();
}
return nConn;
}
BOOL CTextDiagramCtrl::DeleteRect(int nRect)
{
if (CTextDiagram::DeleteRect(nRect))
{
UpdateScrollbars();
Invalidate();
if (m_nSelRect == nRect)
{
m_nSelRect = -1;
SendNotification(TDN_SELCHANGE);
}
return TRUE;
}
return FALSE;
}
BOOL CTextDiagramCtrl::DeleteConnection(int nConn)
{
if (CTextDiagram::DeleteConnection(nConn))
{
UpdateScrollbars();
Invalidate();
return TRUE;
}
return FALSE;
}
BOOL CTextDiagramCtrl::SetRect(int nRect, const CTDRect& rect)
{
if (CTextDiagram::SetRect(nRect, rect))
{
UpdateScrollbars();
Invalidate();
return TRUE;
}
return FALSE;
}
BOOL CTextDiagramCtrl::SetText(int nRect, LPCTSTR szText)
{
if (CTextDiagram::SetText(nRect, szText))
{
Invalidate();
return TRUE;
}
return FALSE;
}
BOOL CTextDiagramCtrl::SetConnection(int nConn, const CTDConnection& conn)
{
if (CTextDiagram::SetConnection(nConn, conn))
{
UpdateScrollbars();
Invalidate();
return TRUE;
}
return FALSE;
}
void CTextDiagramCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rClient;
GetClientRect(rClient);
dc.IntersectClipRect(rClient);
// draw rects
dc.SelectStockObject(ANSI_FIXED_FONT);
int nRect = 0;
CTDRect rect;
while (GetRect(nRect, rect))
{
DrawRect(&dc, rect, nRect == m_nSelRect);
nRect++;
}
// draw connections
int nConn = 0;
CTDConnection conn;
while (GetConnection(nConn, conn))
{
DrawConn(&dc, conn);
nConn++;
}
}
void CTextDiagramCtrl::DrawRect(CDC* pDC, const CTDRect& rect, BOOL bSelected)
{
CRect rDraw(rect);
LogicalToClient(rDraw);
if (!rDraw.IsRectEmpty())
{
if (bSelected)
pDC->FillSolidRect(rDraw, GetSysColor(COLOR_HIGHLIGHT));
pDC->Draw3dRect(rDraw, 0, 0);
// text
rDraw.DeflateRect(CHARSIZE_X / 2, CHARSIZE_Y / 2);
pDC->SetTextColor(GetSysColor(bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
pDC->SetBkMode(TRANSPARENT);
RenderText(pDC, rDraw, rect.GetText());
}
}
void CTextDiagramCtrl::RenderText(CDC* pDC, const CRect& rect, const CString& sText)
{
int nLen = sText.GetLength(), nX = rect.left, nY = rect.top;
for (int nChar = 0; nChar < nLen && (nY + CHARSIZE_Y) < rect.bottom; nChar++)
{
#ifdef _VER_CHINESE
char c[3];
c[0] = sText[nChar];
// new line?
if (c[0] == _T('\r'))
{
nX = rect.left;
nY += CHARSIZE_Y;
nChar++; // to account for the trailing '\n'
}
else if (
(((c[0] & 0x80) != 0) && ((nX + 2 * CHARSIZE_X) >= rect.right)) || //it is a Chinese character,it need two bytes space
(((c[0] & 0x80) == 0) && ((nX + CHARSIZE_X) >= rect.right))
)
{
nX = rect.left;
nY += CHARSIZE_Y;
nChar--; // try again
}
else
{
if((c[0] & 0x80) != 0)//it is the first byte of a Chinese character
{
nChar++;
c[1] = sText[nChar];
c[2] = 0;
pDC->ExtTextOut(nX, nY, 0, NULL, CString(c), NULL);
nX += 2 * CHARSIZE_X;
}
else
{
c[1] = 0;
pDC->ExtTextOut(nX, nY, 0, NULL, CString(c), NULL);
nX += CHARSIZE_X;
}
}
#else
char c = sText[nChar];
// new line?
if (c == '\r')
{
nX = rect.left;
nY += CHARSIZE_Y;
nChar++; // to account for the trailing '\n'
}
else if ((nX + CHARSIZE_X) > rect.right)
{
nX = rect.left;
nY += CHARSIZE_Y;
nChar--; // try again
}
else
{
pDC->ExtTextOut(nX, nY, 0, NULL, CString(c), NULL);
nX += CHARSIZE_X;
}
#endif
}
}
void CTextDiagramCtrl::DrawConn(CDC* pDC, const CTDConnection& conn)
{
CPoint ptSegStart = conn.GetStartPos(), ptSegEnd;
CPoint ptFrom(ptSegStart);
ptFrom.x = ptFrom.x * CHARSIZE_X + CHARSIZE_X / 2;
ptFrom.y = ptFrom.y * CHARSIZE_Y + CHARSIZE_Y / 2;
CRect rStart(ptFrom.x - 1, ptFrom.y - 1, ptFrom.x + 2, ptFrom.y + 2);
rStart.OffsetRect(-m_ptScrollOffset);
ptFrom.Offset(-m_ptScrollOffset);
pDC->FillSolidRect(rStart, 0);
pDC->MoveTo(ptFrom);
for (int nSeg = 0; nSeg < conn.NumSegments(); nSeg++)
{
CPoint ptTo, ptSegEnd = conn.GetSegmentPos(nSeg);
ptTo.x = ptSegEnd.x * CHARSIZE_X + CHARSIZE_X / 2;
ptTo.y = ptSegEnd.y * CHARSIZE_Y + CHARSIZE_Y / 2;
ptTo.Offset(-m_ptScrollOffset);
pDC->LineTo(ptTo);
ptFrom = ptTo; // for drawing the arrow
}
DrawArrow(pDC, ptFrom, conn.GetEndDirection());
}
void CTextDiagramCtrl::DrawArrow(CDC* pDC, CPoint point, int nDirection)
{
switch (nDirection)
{
case CONN_UP:
pDC->MoveTo(point.x - ARROWSIZE, point.y + ARROWSIZE);
pDC->LineTo(point);
pDC->LineTo(point.x + (ARROWSIZE + 1), point.y + (ARROWSIZE + 1));
break;
case CONN_DOWN:
pDC->MoveTo(point.x - ARROWSIZE, point.y - ARROWSIZE);
pDC->LineTo(point);
pDC->LineTo(point.x + (ARROWSIZE + 1), point.y - (ARROWSIZE + 1));
break;
case CONN_LEFT:
pDC->MoveTo(point.x + ARROWSIZE, point.y - ARROWSIZE);
pDC->LineTo(point);
pDC->LineTo(point.x + (ARROWSIZE + 1), point.y + (ARROWSIZE + 1));
break;
case CONN_RIGHT:
pDC->MoveTo(point.x - ARROWSIZE, point.y - ARROWSIZE);
pDC->LineTo(point);
pDC->LineTo(point.x - (ARROWSIZE + 1), point.y + (ARROWSIZE + 1));
break;
}
}
BOOL CTextDiagramCtrl::OnEraseBkgnd(CDC* pDC)
{
CRect rClient;
GetClientRect(rClient);
// clip out the selected item
int nSaveDC = pDC->SaveDC();
if (m_nSelRect != -1)
{
CTDRect rect;
GetRect(m_nSelRect, rect);
CRect rDraw(rect);
LogicalToClient(rDraw);
pDC->ExcludeClipRect(rDraw);
}
pDC->FillSolidRect(rClient, GetSysColor(COLOR_WINDOW));
DrawGrid(pDC);
pDC->RestoreDC(nSaveDC);
return TRUE;
}
void CTextDiagramCtrl::DrawGrid(CDC* pDC)
{
CRect rClient;
GetClientRect(rClient);
rClient |= GetBoundingRect();
for (int nY = CHARSIZE_Y; nY < rClient.bottom; nY += CHARSIZE_Y)
{
if ((nY / CHARSIZE_Y) % 10)
pDC->FillSolidRect(0, nY - m_ptScrollOffset.y, rClient.right, 1, RGB(192, 192, 192));
else
pDC->FillSolidRect(0, nY - m_ptScrollOffset.y, rClient.right, 1, RGB(160, 160, 160));
}
for (int nX = CHARSIZE_X; nX < rClient.right; nX += CHARSIZE_X)
{
if ((nX / CHARSIZE_X) % 10)
pDC->FillSolidRect(nX - m_ptScrollOffset.x, 0, 1, rClient.bottom, RGB(192, 192, 192));
else
pDC->FillSolidRect(nX - m_ptScrollOffset.x, 0, 1, rClient.bottom, RGB(160, 160, 160));
}
}
void CTextDiagramCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnLButtonDown(nFlags, point);
// always make sure existing edit is finished
OnEndTextEdit();
m_bDblClick = FALSE;
SetFocus();
// update selection
point.Offset(m_ptScrollOffset);
int nSel = IntersectRect(point.x / CHARSIZE_X, point.y / CHARSIZE_Y);
if (nSel != m_nSelRect)
{
m_nSelRect = nSel;
Invalidate();
SendNotification(TDN_SELCHANGE);
}
if (nSel != -1)
{
// test for whether we are moving or sizing
CTDRect rect;
GetRect(m_nSelRect, rect);
// deflate this and then check we're still inside
CRect rDraw(rect);
LogicalToClient(rDraw);
rDraw.DeflateRect(2, 2);
rDraw.OffsetRect(m_ptScrollOffset);
if (!rDraw.PtInRect(point))
{
if (point.x <= rDraw.left)
m_nSizing = HTLEFT;
else if (point.x >= rDraw.right)
m_nSizing = HTRIGHT;
else if (point.y <= rDraw.top)
m_nSizing = HTTOP;
else if (point.y >= rDraw.bottom)
m_nSizing = HTBOTTOM;
else // ??
ASSERT (0);
}
else
m_bMoving = TRUE;
m_ptDragStart = m_ptDragPrev = point;
SetCapture();
}
}
int CTextDiagramCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
UpdateScrollbars();
return 0;
}
void CTextDiagramCtrl::UpdateScrollbars()
{
CRect rClient, rDiagram(GetBoundingRect());
GetClientRect(rClient);
CPoint ptOffset(m_ptScrollOffset);
ShowScrollBar(SB_VERT, rClient.bottom < rDiagram.bottom);
ShowScrollBar(SB_HORZ, rClient.right < rDiagram.right);
if (rClient.bottom >= rDiagram.bottom)
m_ptScrollOffset.y = 0;
else
{
m_ptScrollOffset.y = min(m_ptScrollOffset.y, (rDiagram.bottom - rClient.bottom));
// set proportional scrollbar
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL & ~SIF_TRACKPOS;
si.nMin = 0;
si.nMax = rDiagram.bottom;
si.nPage = rClient.bottom;
si.nPos = m_ptScrollOffset.y;
si.nTrackPos = 0;
SetScrollInfo(SB_VERT, &si);
}
if (rClient.right >= rDiagram.right)
m_ptScrollOffset.x = 0;
else
{
m_ptScrollOffset.x = min(m_ptScrollOffset.x, (rDiagram.right - rClient.right));
// set proportional scrollbar
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL & ~SIF_TRACKPOS;
si.nMin = 0;
si.nMax = rDiagram.right;
si.nPage = rClient.right;
si.nPos = m_ptScrollOffset.x;
si.nTrackPos = 0;
SetScrollInfo(SB_HORZ, &si);
}
if (m_ptScrollOffset != ptOffset)
Invalidate();
}
CRect CTextDiagramCtrl::GetBoundingRect()
{
return CRect(0, 0, (m_size.cx + 1) * CHARSIZE_X, (m_size.cy + 1) * CHARSIZE_Y);
}
void CTextDiagramCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CRect rClient, rDiagram(GetBoundingRect());
GetClientRect(rClient);
CPoint ptOffset(m_ptScrollOffset);
switch (nSBCode)
{
case SB_LEFT:
m_ptScrollOffset.x = 0;
break;
case SB_ENDSCROLL:
break;
case SB_LINELEFT:
m_ptScrollOffset.x = max(m_ptScrollOffset.x - CHARSIZE_X, 0);
break;
case SB_LINERIGHT:
m_ptScrollOffset.x = min(rDiagram.right - rClient.right, m_ptScrollOffset.x + CHARSIZE_X);
break;
case SB_PAGELEFT:
m_ptScrollOffset.x = max(m_ptScrollOffset.x - CHARSIZE_X, 0);
break;
case SB_PAGERIGHT:
m_ptScrollOffset.x = min(rDiagram.right - rClient.right, m_ptScrollOffset.x + CHARSIZE_X);
break;
case SB_RIGHT:
m_ptScrollOffset.x = (rDiagram.right - rClient.right);
break;
case SB_THUMBPOSITION:
m_ptScrollOffset.x = (nPos / CHARSIZE_X) * CHARSIZE_X;
break;
case SB_THUMBTRACK:
m_ptScrollOffset.x = (nPos / CHARSIZE_X) * CHARSIZE_X;
break;
}
if (m_ptScrollOffset != ptOffset)
{
Invalidate();
UpdateScrollbars();
}
CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}
void CTextDiagramCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CRect rClient, rDiagram(GetBoundingRect());
GetClientRect(rClient);
CPoint ptOffset(m_ptScrollOffset);
switch (nSBCode)
{
case SB_TOP:
m_ptScrollOffset.y = 0;
break;
case SB_ENDSCROLL:
break;
case SB_LINEUP:
m_ptScrollOffset.y = max(m_ptScrollOffset.y - CHARSIZE_Y, 0);
break;
case SB_LINEDOWN:
m_ptScrollOffset.y = min(rDiagram.bottom - rClient.bottom, m_ptScrollOffset.y + CHARSIZE_Y);
break;
case SB_PAGEUP:
m_ptScrollOffset.y = max(m_ptScrollOffset.y - CHARSIZE_Y, 0);
break;
case SB_PAGEDOWN:
m_ptScrollOffset.y = min(rDiagram.bottom - rClient.bottom, m_ptScrollOffset.y + CHARSIZE_Y);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -