////////////////////////////////////////////////////////////////////////////
// File: CCrystalTextView.cpp
// Version: 1.0.0.0
// Created: 29-Dec-1998
//
// Author: Stcherbatchenko Andrei
// E-mail: windfall@gmx.de
//
// Implementation of the CCrystalTextView class, a part of Crystal Edit -
// syntax coloring text editor.
//
// You are free to use or modify this code to the following restrictions:
// - Acknowledge me somewhere in your about box, simple "Parts of code by.."
// will be enough. If you can't (or don't want to), contact me personally.
// - LEAVE THIS HEADER INTACT
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// 17-Feb-99
// FIX: missing UpdateCaret() in CCrystalTextView::SetFont
// FIX: missing UpdateCaret() in CCrystalTextView::RecalcVertScrollBar
// FIX: mistype in CCrystalTextView::RecalcPageLayouts + instead of +=
// FIX: removed condition 'm_nLineHeight < 20' in
// CCrystalTextView::CalcLineCharDim(). This caused painting defects
// when using very small fonts.
//
// FEATURE: Some experiments with smooth scrolling, controlled by
// m_bSmoothScroll member variable, by default turned off.
// See ScrollToLine function for implementation details.
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// 21-Feb-99
// Paul Selormey, James R. Twine
// + FEATURE: description for Undo/Redo actions
// + FEATURE: multiple MSVC-like bookmarks
// + FEATURE: 'Disable backspace at beginning of line' option
// + FEATURE: 'Disable drag-n-drop editing' option
//
// + FIX: ResetView() now virtual
// + FEATURE: Added OnEditOperation() virtual: base for auto-indent,
// smart indent etc.
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <malloc.h>
#include "editcmd.h"
#include "editreg.h"
#include "CCrystalTextView.h"
#include "CCrystalTextBuffer.h"
#include "CFindTextDlg.h"
#ifndef __AFXPRIV_H__
#pragma message("Include <afxpriv.h> in your stdafx.h to avoid this message")
#include <afxpriv.h>
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define TAB_CHARACTER _T('\xBB')
#define SPACE_CHARACTER _T('\x95')
#define DEFAULT_PRINT_MARGIN 1000 // 10 millimeters
#define SMOOTH_SCROLL_FACTOR 6
////////////////////////////////////////////////////////////////////////////
// CCrystalTextView
IMPLEMENT_DYNCREATE(CCrystalTextView, CView)
HINSTANCE CCrystalTextView::s_hResourceInst = NULL;
BEGIN_MESSAGE_MAP(CCrystalTextView, CView)
//{{AFX_MSG_MAP(CCrystalTextView)
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
ON_WM_SIZE()
ON_WM_VSCROLL()
ON_WM_SETCURSOR()
ON_WM_LBUTTONDOWN()
ON_WM_SETFOCUS()
ON_WM_HSCROLL()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_WM_KILLFOCUS()
ON_WM_LBUTTONDBLCLK()
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
ON_WM_RBUTTONDOWN()
ON_WM_SYSCOLORCHANGE()
ON_WM_CREATE()
ON_COMMAND(ID_EDIT_FIND, OnEditFind)
ON_COMMAND(ID_EDIT_REPEAT, OnEditRepeat)
ON_UPDATE_COMMAND_UI(ID_EDIT_REPEAT, OnUpdateEditRepeat)
ON_COMMAND(ID_EDIT_FIND_PREVIOUS, OnEditFindPrevious)
ON_UPDATE_COMMAND_UI(ID_EDIT_FIND_PREVIOUS, OnUpdateEditFindPrevious)
//}}AFX_MSG_MAP
ON_COMMAND(ID_EDIT_CHAR_LEFT, OnCharLeft)
ON_COMMAND(ID_EDIT_EXT_CHAR_LEFT, OnExtCharLeft)
ON_COMMAND(ID_EDIT_CHAR_RIGHT, OnCharRight)
ON_COMMAND(ID_EDIT_EXT_CHAR_RIGHT, OnExtCharRight)
ON_COMMAND(ID_EDIT_WORD_LEFT, OnWordLeft)
ON_COMMAND(ID_EDIT_EXT_WORD_LEFT, OnExtWordLeft)
ON_COMMAND(ID_EDIT_WORD_RIGHT, OnWordRight)
ON_COMMAND(ID_EDIT_EXT_WORD_RIGHT, OnExtWordRight)
ON_COMMAND(ID_EDIT_LINE_UP, OnLineUp)
ON_COMMAND(ID_EDIT_EXT_LINE_UP, OnExtLineUp)
ON_COMMAND(ID_EDIT_LINE_DOWN, OnLineDown)
ON_COMMAND(ID_EDIT_EXT_LINE_DOWN, OnExtLineDown)
ON_COMMAND(ID_EDIT_SCROLL_UP, ScrollUp)
ON_COMMAND(ID_EDIT_SCROLL_DOWN, ScrollDown)
ON_COMMAND(ID_EDIT_PAGE_UP, OnPageUp)
ON_COMMAND(ID_EDIT_EXT_PAGE_UP, OnExtPageUp)
ON_COMMAND(ID_EDIT_PAGE_DOWN, OnPageDown)
ON_COMMAND(ID_EDIT_EXT_PAGE_DOWN, OnExtPageDown)
ON_COMMAND(ID_EDIT_LINE_END, OnLineEnd)
ON_COMMAND(ID_EDIT_EXT_LINE_END, OnExtLineEnd)
ON_COMMAND(ID_EDIT_HOME, OnHome)
ON_COMMAND(ID_EDIT_EXT_HOME, OnExtHome)
ON_COMMAND(ID_EDIT_TEXT_BEGIN, OnTextBegin)
ON_COMMAND(ID_EDIT_EXT_TEXT_BEGIN, OnExtTextBegin)
ON_COMMAND(ID_EDIT_TEXT_END, OnTextEnd)
ON_COMMAND(ID_EDIT_EXT_TEXT_END, OnExtTextEnd)
// Standard printing commands
ON_COMMAND(ID_FILE_PAGE_SETUP, OnFilePageSetup)
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
// Status
ON_UPDATE_COMMAND_UI(ID_EDIT_INDICATOR_CRLF, OnUpdateIndicatorCRLF)
ON_UPDATE_COMMAND_UI(ID_EDIT_INDICATOR_POSITION, OnUpdateIndicatorPosition)
// Bookmarks
ON_COMMAND_RANGE(ID_EDIT_TOGGLE_BOOKMARK0, ID_EDIT_TOGGLE_BOOKMARK9, OnToggleBookmark)
ON_COMMAND_RANGE(ID_EDIT_GO_BOOKMARK0, ID_EDIT_GO_BOOKMARK9, OnGoBookmark)
ON_COMMAND(ID_EDIT_CLEAR_BOOKMARKS, OnClearBookmarks)
// More Bookmarks
ON_COMMAND(ID_EDIT_TOGGLE_BOOKMARK, OnToggleBookmark)
ON_COMMAND(ID_EDIT_GOTO_NEXT_BOOKMARK, OnNextBookmark)
ON_COMMAND(ID_EDIT_GOTO_PREV_BOOKMARK, OnPrevBookmark)
ON_COMMAND(ID_EDIT_CLEAR_ALL_BOOKMARKS, OnClearAllBookmarks)
ON_UPDATE_COMMAND_UI(ID_EDIT_GOTO_NEXT_BOOKMARK, OnUpdateNextBookmark)
ON_UPDATE_COMMAND_UI(ID_EDIT_GOTO_PREV_BOOKMARK, OnUpdatePrevBookmark)
ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR_ALL_BOOKMARKS, OnUpdateClearAllBookmarks)
END_MESSAGE_MAP()
#define EXPAND_PRIMITIVE(impl, func) \
void CCrystalTextView::On##func() { impl(FALSE); } \
void CCrystalTextView::OnExt##func() { impl(TRUE); }
EXPAND_PRIMITIVE(MoveLeft, CharLeft)
EXPAND_PRIMITIVE(MoveRight, CharRight)
EXPAND_PRIMITIVE(MoveWordLeft, WordLeft)
EXPAND_PRIMITIVE(MoveWordRight, WordRight)
EXPAND_PRIMITIVE(MoveUp, LineUp)
EXPAND_PRIMITIVE(MoveDown, LineDown)
EXPAND_PRIMITIVE(MovePgUp, PageUp)
EXPAND_PRIMITIVE(MovePgDn, PageDown)
EXPAND_PRIMITIVE(MoveHome, Home)
EXPAND_PRIMITIVE(MoveEnd, LineEnd)
EXPAND_PRIMITIVE(MoveCtrlHome, TextBegin)
EXPAND_PRIMITIVE(MoveCtrlEnd, TextEnd)
#undef EXPAND_PRIMITIVE
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextView construction/destruction
CCrystalTextView::CCrystalTextView()
{
AFX_ZERO_INIT_OBJECT(CView);
m_bSelMargin = TRUE;
ResetView();
}
CCrystalTextView::~CCrystalTextView()
{
ASSERT(m_hAccel == NULL);
ASSERT(m_pCacheBitmap == NULL);
ASSERT(m_pTextBuffer == NULL); // Must be correctly detached
if (m_pszLastFindWhat != NULL)
free(m_pszLastFindWhat);
if (m_pdwParseCookies != NULL)
delete m_pdwParseCookies;
if (m_pnActualLineLength != NULL)
delete m_pnActualLineLength;
}
BOOL CCrystalTextView::PreCreateWindow(CREATESTRUCT& cs)
{
CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
{
// View must always create its own scrollbars,
// if only it's not used within splitter
cs.style |= (WS_HSCROLL | WS_VSCROLL);
}
cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
return CView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextView drawing
void CCrystalTextView::GetSelection(CPoint &ptStart, CPoint &ptEnd)
{
PrepareSelBounds();
ptStart = m_ptDrawSelStart;
ptEnd = m_ptDrawSelEnd;
}
CCrystalTextBuffer *CCrystalTextView::LocateTextBuffer()
{
return NULL;
}
int CCrystalTextView::GetLineActualLength(int nLineIndex)
{
int nLineCount = GetLineCount();
ASSERT(nLineCount > 0);
ASSERT(nLineIndex >= 0 && nLineIndex < nLineCount);
if (m_pnActualLineLength == NULL)
{
m_pnActualLineLength = new int[nLineCount];
memset(m_pnActualLineLength, 0xff, sizeof(int) * nLineCount);
m_nActualLengthArraySize = nLineCount;
}
if (m_pnActualLineLength[nLineIndex] >= 0)
return m_pnActualLineLength[nLineIndex];
// Actual line length is not determined yet, let's calculate a little
int nActualLength = 0;
int nLength = GetLineLength(nLineIndex);
if (nLength > 0)
{
LPCTSTR pszLine = GetLineChars(nLineIndex);
LPTSTR pszChars = (LPTSTR) _alloca(sizeof(TCHAR) * (nLength + 1));
memcpy(pszChars, pszLine, sizeof(TCHAR) * nLength);
pszChars[nLength] = 0;
LPTSTR pszCurrent = pszChars;
int nTabSize = GetTabSize();
for (;;)
{
#ifdef _UNICODE
LPTSTR psz = wcschr(pszCurrent, L'\t');
#else
LPTSTR psz = strchr(pszCurrent, '\t');
#endif
if (psz == NULL)
{
nActualLength += (pszChars + nLength - pszCurrent);
break;
}
nActualLength += (psz - pszCurrent);
nActualLength += (nTabSize - nActualLength % nTabSize);
pszCurrent = psz + 1;
}
}
m_pnActualLineLength[nLineIndex] = nActualLength;
return nActualLength;
}
void CCrystalTextView::ScrollToChar(int nNewOffsetChar, BOOL bNoSmoothScroll /*= FALSE*/, BOOL bTrackScrollBar /*= TRUE*/)
{
// For now, ignoring bNoSmoothScroll and m_bSmoothScroll
if (m_nOffsetChar != nNewOffsetChar)
{
int nScrollChars = m_nOffsetChar - nNewOffsetChar;
m_nOffsetChar = nNewOffsetChar;
CRect rcScroll;
GetClientRect(&rcScroll);
rcScroll.left += GetMarginWidth();
ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
UpdateWindow();
if (bTrackScrollBar)
RecalcHorzScrollBar(TRUE);
}
}
void CCrystalTextView::ScrollToLine(int nNewTopLine, BOOL bNoSmoothScroll /*= FALSE*/, BOOL bTrackScrollBar /*= TRUE*/)
{
if (m_nTopLine != nNewTopLine)
{
if (bNoSmoothScroll || ! m_bSmoothScroll)
{
int nScrollLines = m_nTopLine - nNewTopLine;
m_nTopLine = nNewTopLine;
ScrollWindow(0, nScrollLines * GetLineHeight());
UpdateWindow();
if (bTrackScrollBar)
RecalcVertScrollBar(TRUE);
}
else
{
// Do smooth scrolling
int nLineHeight = GetLineHeight();
if (m_nTopLine > nNewTopLine)
{
int nIncrement = (m_nTopLine - nNewTopLine) / SMOOTH_SCROLL_FACTOR + 1;
while (m_nTopLine != nNewTopLine)
{
int nTopLine = m_nTopLine - nIncrement;
if (nTopLine < nNewTopLine)
nTopLine = nNewTopLine;
int nScrollLines = nTopLine - m_nTopLine;
m_nTopLine = nTopLine;
ScrollWindow(0, - nLineHeight * nScrollLines);
UpdateWindow();
if (bTrackScrollBar)
RecalcVertScrollBar(TRUE);
}
}
else
{
int nIncrement = (nNewTopLine - m_nTopLine) / SMOOTH_SCROLL_FACTOR + 1;
while (m_nTopLine != nNewTopLine)
{
int nTopLine = m_nTopLine + nIncrement;
if (nTopLine > nNewTopLine)
nTopLine = nNewTopLine;
int nScrollLines = nTopLine - m_nTopLine;
m_nTopLine = nTopLine;
ScrollWindow(0, - nLineHeight * nScrollLines);
UpdateWindow();
if (bTrackScrollBar)
RecalcVertScrollBar(TRUE);
}
}
}
}
}
void CCrystalTextView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)
{
if (nCount <= 0)
{
line = _T("");
return;
}
int nTabSize = GetTabSize();
int nActualOffset = 0;
for (int I = 0; I < nOffset; I ++)
{
if (pszChars[I] == _T('\t'))
nActualOffset += (nTabSize - nActualOffset % nTabSize);
else
nActualOffset ++;
}
pszChars += nOffset;
int nLength = nCount;
int nTabCount = 0;
for (I = 0; I < nLength; I ++)
{
if (pszChars[I] == _T('\t'))
nTabCount ++;
}
LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
int nCurPos = 0;
if (nTabCount > 0 || m_bViewTabs)
{
for (I = 0; I < nLength; I ++)
{
if (pszChars[I] == _T('\t'))
{
int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
if (m_bViewTabs)
{
pszBuf[nCurPos ++] = TAB_CHARACTER;
nSpaces --;
}
while (nSpaces > 0)
{
pszBuf[nCurPos ++] = _T(' ');
nSpaces --;
}
}
else
{
if (pszChars[I] == _T(' ') && m_bViewTabs)
pszBuf[nCurPos] = SPACE_CHARACTER;
else
pszBuf[nCurPos] = pszChars[I];
nCurPos ++;
}
}
}
else
{
memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
nCurPos = nLength;
}
pszBuf[nCurPos] = 0;
line.ReleaseBuffer();
}
void CCrystalTextView::DrawLineHelperImpl(CDC *pdc, CPoint &ptOrigin, const CRect &rcClip,
LPCTSTR pszChars, int nOffset, int nCount)
{
ASSERT(nCount >= 0);
if (nCount > 0)
{
CString line;
ExpandChars(pszChars, nOffset, nCount, line);
int nWidth = rcClip.right - ptOrigin.x;
if (nWidth > 0)
{
int nCharWidth = GetCharWidth();
int nCount = line.GetLength();
int nCountFit = nWidth / nCharWidth + 1;
if (nCount > nCountFit)
nCount = nCountFit;
#ifdef _DEBUG
//CSize sz = pdc->GetTextExtent(line, nCount);
//ASSERT(sz.cx == m_nCharWidth * nCount);
#endif
/*
CRect rcBounds = rcClip;
rcBounds.left = ptOrigin.x;
rcBounds.right = rcBounds.left + GetCharWidth() * nCount;
pdc->ExtTextOut(rcBounds.left, rcBounds.top, ETO_OPAQUE, &rcBounds, NULL, 0, NULL);
*/
VERIFY(pdc->ExtTextOut(ptOrigin.x, ptOrigin.y, ETO_CLIPPED, &rcClip, line, nCount, NULL));
}
ptOrigin.x += GetCharWidth() * line.GetLength();
}
}
void CCrystalTextView::DrawLineHelper(CDC *pdc, CPoint &ptOrigin, const CRect &rcClip, int nColorIndex,
LPCTSTR pszChars, int nOffset, int nCount, CPoint ptTextPos)
{
if (nCount > 0)
{
if (m_bFocused || m_bShowInactiveSelection)
{
int nSelBegin = 0, nSelEnd = 0;
if (m_ptDrawSelStart.y > ptTextPos.y)
{
nSelBegin = nCount;
}
else
if (m_ptDrawSelStart.y == ptTextPos.y)
{
nSelBegin = m_ptDrawSelStart.x - ptTextPos.x;
if (nSelBegin < 0)
nSelBegin = 0;
if (nSelBegin > nCount)
nSelBegin = nCount;
}
if (m_ptDrawSelEnd.y > ptTextPos.y)
{
nSelEnd = nCount;
}
else
if (m_ptDrawSelEnd.y == ptTextPos.y)
{
nSelEnd = m_ptDrawSelEnd.x - ptTextPos.x;
if (nSelEnd < 0)
nSelEnd = 0;
if (nSelEnd > nCount)
nSelEnd = nCount;
}
ASSERT(nSelBegin >= 0 && nSelBegin <= nCount);
ASSERT(nSelEnd >= 0 && nSelEnd <= nCount);
ASSERT(nSelBegin <= nSelEnd);
// Draw part of the text before selection
if (nSelBegin > 0)
{
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset, nSelBegin);
}
if (nSelBegin < nSelEnd)
{
COLORREF crOldBk = pdc->SetBkColor(GetColor(COLORINDEX_SELBKGND));
COLORREF crOldText = pdc->SetTextColor(GetColor(COLORINDEX_SELTEXT));
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset + nSelBegin, nSelEnd - nSelBegin);
pdc->SetBkColor(crOldBk);
pdc->SetTextColor(crOldText);
}
if (nSelEnd < nCount)
{
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset + nSelEnd, nCount - nSelEnd);
}
}
else
{
DrawLineHelperImpl(pdc, ptOrigin, rcClip, pszChars, nOffset, nCount);
}
}
}
void CCrystalTextView::GetLineColors(int nLineIndex, COLORREF &crBkgnd,
COLORREF &crText, BOOL &bDrawWhitespace)
{
DWORD dwLineFlags = GetLineFlags(nLineIndex);
bDrawWhitespace = TRUE;
crText = RGB(255, 255, 255);
if (dwLineFlags & LF_EXECUTION)
{
crBkgnd = RGB(0, 128, 0);
return;
}
if (dwLineFlags & LF_BREAKPOINT)
{
crBkgnd = RGB(255, 0, 0);
return;
}
if (dwLineFlags & LF_INVALID_BREAKPOINT)
{
crBkgnd = RGB(128, 128, 0);
return;
}
crBkgnd = CLR_NONE;
crText = CLR_NONE;
bDrawWhitespace = FALSE;
}
DWORD CCrystalTextView::GetParseCookie(int nLineIndex)
{
int nLineCount = GetLineCount();
if (m_pdwParseCookies == NULL)
{
m_nParseArraySize = nLineCount;
m_pdwParseCookies = new DWORD[nLineCount];
memset(m_pdwParseCookies, 0xff, nLineCount * sizeof(DWORD));
}
if (nLineIndex < 0)
return 0;
if (m_pdwParseCookies[nLineIndex] != (DWORD) -1)
return m_pdwParseCookies[nLineIndex];
int L = nLineIndex;
while (L >= 0 && m_pdwParseCookies[L] == (DWORD) -1)
L --;
L ++;
int nBlocks;
while (L <= nLineIndex)
{
DWORD dwCookie = 0;
if (L > 0)
dwCookie = m_pdwParseCookies[L - 1];
ASSERT(dwCookie != (DWORD) -1);
m_pdwParseCookies[L] = ParseLine(dwCookie, L, NULL, nBlocks);
ASSERT(m_pdwParseCookies[L] != (DWORD) -1);
L ++;
}
return m_pdwParseCookies[nLineIndex];
}
void CCrystalTextView::DrawSingleLine(CDC *pdc, const CRect &rc, int nLineIndex)
{
ASSERT(nLineIndex >= -1 && nLineIndex < GetLineCount());
if (nLineIndex == -1)
{
// Draw line beyond the text
pdc->FillSolidRect(rc, GetColor(COLORINDEX_WHITESPACE));
return;
}
// Acquire the background color for the current line
BOOL bDrawWhitespace = FALSE;
COLORREF crBkgnd, crText;
GetLineColors(nLineIndex, crBkgnd, crText, bDrawWhitespace);
if (crBkgnd == CLR_NONE)
crBkgnd = GetColor(COLORINDEX_BKGND);
int nLength = GetLineLength(nLineIndex);
if (nLength == 0)
{
// Draw the empty line
CRect rect = rc;
if ((m_bFocused || m_bShowInactiveSelection) && IsInsideSelBlock(CPoint(0, nLineIndex)))
{
pdc->FillSolidRect(rect.left, rect.top, GetCharWidth(), rect.Height(), GetColor(COLORINDEX_SELBKGND));
rect.left += GetCharWidth();
}
pdc->FillSolidRect(rect, bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
return;
}
// Parse the line
LPCTSTR pszChars = GetLineChars(nLineIndex);
DWORD dwCookie = GetParseCookie(nLineIndex - 1);
TEXTBLOCK *pBuf = (TEXTBLOCK *) _alloca(sizeof(TEXTBLOCK) * nLength * 3);
int nBlocks = 0;
m_pdwParseCookies[nLineIndex] = ParseLine(dwCookie, nLineIndex, pBuf, nBlocks);
ASSERT(m_pdwParseCookies[nLineIndex] != (DWORD) -1);
// Draw the line text
CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
pdc->SetBkColor(crBkgnd);
if (crText != CLR_NONE)
pdc->SetTextColor(crText);
BOOL bColorSet = FALSE;
if (nBlocks > 0)
{
ASSERT(pBuf[0].m_nCharPos >= 0 && pBuf[0].m_nCharPos <= nLength);
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
pdc->SelectObject(GetFont(GetItalic(COLORINDEX_NORMALTEXT), GetBold(COLORINDEX_NORMALTEXT)));
DrawLineHelper(pdc, origin, rc, COLORINDEX_NORMALTEXT, pszChars, 0, pBuf[0].m_nCharPos, CPoint(0, nLineIndex));
for (int I = 0; I < nBlocks - 1; I ++)
{
ASSERT(pBuf[I].m_nCharPos >= 0 && pBuf[I].m_nCharPos <= nLength);
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(pBuf[I].m_nColorIndex));
pdc->SelectObject(GetFont(GetItalic(pBuf[I].m_nColorIndex), GetBold(pBuf[I].m_nColorIndex)));
DrawLineHelper(pdc, origin, rc, pBuf[I].m_nColorIndex, pszChars,
pBuf[I].m_nCharPos, pBuf[I + 1].m_nCharPos - pBuf[I].m_nCharPos,
CPoint(pBuf[I].m_nCharPos, nLineIndex));
}
ASSERT(pBuf[nBlocks - 1].m_nCharPos >= 0 && pBuf[nBlocks - 1].m_nCharPos <= nLength);
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(pBuf[nBlocks - 1].m_nColorIndex));
pdc->SelectObject(GetFont(GetItalic(pBuf[nBlocks - 1].m_nColorIndex),
GetBold(pBuf[nBlocks - 1].m_nColorIndex)));
DrawLineHelper(pdc, origin, rc, pBuf[nBlocks - 1].m_nColorIndex, pszChars,
pBuf[nBlocks - 1].m_nCharPos, nLength - pBuf[nBlocks - 1].m_nCharPos,
CPoint(pBuf[nBlocks - 1].m_nCharPos, nLineIndex));
}
else
{
if (crText == CLR_NONE)
pdc->SetTextColor(GetColor(COLORINDEX_NORMALTEXT));
pdc->SelectObject(GetFont(GetItalic(COLORINDEX_NORMALTEXT), GetBold(COLORINDEX_NORMALTEXT)));
DrawLineHelper(pdc, origin, rc, COLORINDEX_NORMALTEXT, pszChars, 0, nLength, CPoint(0, nLineIndex));
}
// Draw whitespaces to the left of the text
CRect frect = rc;
if (origin.x > frect.left)
frect.left = origin.x;
if (frect.right > frect.left)
{
if ((m_bFocused || m_bShowInactiveSelection) && IsInsideSelBlock(CPoint(nLength, nLineIndex)))
{
pdc->FillSolidRect(frect.left, frect.top, GetCharWidth(), frect.Height(),
GetColor(COLORINDEX_SELBKGND));
frect.left += GetCharWidth();
}
if (frect.right > frect.left)
pdc->FillSolidRect(frect, bDrawWhitespace ? crBkgnd : GetColor(COLORINDEX_WHITESPACE));
}
}
COLORREF CCrystalTextView::GetColor(int nColorIndex)
{
switch (nColorIndex)
{
case COLORINDEX_WHITESPACE:
case COLORINDEX_BKGND:
return ::GetSysColor(COLOR_WINDOW);
case COLORINDEX_NORMALTEXT:
return ::GetSysColor(COLOR_WINDOWTEXT);
case COLORINDEX_SELMARGIN:
return ::GetSysColor(COLOR_SCROLLBAR);
case COLORINDEX_PREPROCESSOR:
return RGB(0, 128, 192);
case COLORINDEX_COMMENT:
return RGB(128, 128, 128);
// [JRT]: Enabled Support For Numbers...
case COLORINDEX_NUMBER:
return RGB(0x80, 0x00, 0x00);
// [JRT]: Support For C/C++ Operators
case COLORINDEX_OPERATOR:
return RGB(0x00, 0x00, 0x00);
case COLORINDEX_KEYWORD:
return RGB(0, 0, 255);
case COLORINDEX_SELBKGND:
return RGB(0, 0, 0);
case COLORINDEX_SELTEXT:
return RGB(255, 255, 255);
}
return RGB(255, 0, 0);
}
DWORD CCrystalTextView::GetLineFlags(int nLineIndex)
{
if (m_pTextBuffer == NULL)
return 0;
return m_pTextBuffer->GetLineFlags(nLineIndex);
}
void CCrystalTextView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
{
if (! m_bSelMargin)
{
pdc->FillSolidRect(rect, GetColor(COLORINDEX_BKGND));
return;
}
pdc->FillSolidRect(rect, GetColor(COLORINDEX_SELMARGIN));
int nImageIndex = -1;
if (nLineIndex >= 0)
{
DWORD dwLineFlags = GetLineFlags(nLineIndex);
static const DWORD adwFlags[] =
{
LF_EXECUTION,
LF_BREAKPOINT,
LF_COMPILATION_ERROR,
LF_BOOKMARK(1),
LF_BOOKMARK(2),
LF_BOOKMARK(3),
LF_BOOKMARK(4),
LF_BOOKMARK(5),
LF_BOOKMARK(6),
LF_BOOKMARK(7),
LF_BOOKMARK(8),
LF_BOOKMARK(9),
LF_BOOKMARK(0),
LF_BOOKMARKS,
LF_INVALID_BREAKPOINT
};
for (int I = 0; I <= sizeof(adwFlags) / sizeof(adwFlags[0]); I ++)
{
if ((dwLineFlags & adwFlags[I]) != 0)
{
nImageIndex = I;
break;
}
}
}
if (nImageIndex >= 0)
{
if (m_pIcons == NULL)
{
m_pIcons = new CImageList;
VERIFY(m_pIcons->Create(IDR_MARGIN_ICONS, 16, 16, RGB(255, 255, 255)));
}
CPoint pt(rect.left + 2, rect.top + (rect.Height() - 16) / 2);
VERIFY(m_pIcons->Draw(pdc, nImageIndex, pt, ILD_TRANSPARENT));
}
}
BOOL CCrystalTextView::IsInsideSelBlock(CPoint ptTextPos)
{
ASSERT_VALIDTEXTPOS(ptTextPos);
if (ptTextPos.y < m_ptDrawSelStart.y)
return FALSE;
if (ptTextPos.y > m_ptDrawSelEnd.y)
return FALSE;
if (ptTextPos.y < m_ptDrawSelEnd.y && ptTextPos.y > m_ptDrawSelStart.y)
return TRUE;
if (m_ptDrawSelStart.y < m_ptDrawSelEnd.y)
{
if (ptTextPos.y == m_ptDrawSelEnd.y)
return ptTextPos.x < m_ptDrawSelEnd.x;
ASSERT(ptTextPos.y == m_ptDrawSelStart.y);
return ptTextPos.x >= m_ptDrawSelStart.x;
}
ASSERT(m_ptDrawSelStart.y == m_ptDrawSelEnd.y);
return ptTextPos.x >= m_ptDrawSelStart.x && ptTextPos.x < m_ptDrawSelEnd.x;
}
BOOL CCrystalTextView::IsInsideSelection(const CPoint &ptTextPos)
{
PrepareSelBounds();
return IsInsideSelBlock(ptTextPos);
}
void CCrystalTextView::PrepareSelBounds()
{
if (m_ptSelStart.y < m_ptSelEnd.y ||
(m_ptSelStart.y == m_ptSelEnd.y && m_ptSelStart.x < m_ptSelEnd.x))
{
m_ptDrawSelStart = m_ptSelStart;
m_ptDrawSelEnd = m_ptSelEnd;
}
else
{
m_ptDrawSelStart = m_ptSelEnd;
m_ptDrawSelEnd = m_ptSelStart;
}
}
void CCrystalTextView::OnDraw(CDC *pdc)
{
CRect rcClient;
GetClientRect(rcClient);
int nLineCount = GetLineCount();
int nLineHeight = GetLineHeight();
PrepareSelBounds();
CDC cacheDC;
VERIFY(cacheDC.CreateCompatibleDC(pdc));
if (m_pCacheBitmap == NULL)
{
m_pCacheBitmap = new CBitmap;
VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pdc, rcClient.Width(), nLineHeight));
}
CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
CRect rcLine;
rcLine = rcClient;
rcLine.bottom = rcLine.top + nLineHeight;
CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
int nCurrentLine = m_nTopLine;
while (rcLine.top < rcClient.bottom)
{
if (nCurrentLine < nLineCount)
{
DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
}
else
{
DrawMargin(&cacheDC, rcCacheMargin, -1);
DrawSingleLine(&cacheDC, rcCacheLine, -1);
}
VERIFY(pdc->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
nCurrentLine ++;
rcLine.OffsetRect(0, nLineHeight);
}
cacheDC.SelectObject(pOldBitmap);
cacheDC.DeleteDC();
}
void CCrystalTextView::ResetView()
{
m_nTopLine = 0;
m_nOffsetChar = 0;
m_nLineHeight = -1;
m_nCharWidth = -1;
m_nTabSize = 4;
m_nMaxLineLength = -1;
m_nScreenLines = -1;
m_nScreenChars = -1;
m_nIdealCharPos = -1;
m_ptAnchor.x = 0;
m_ptAnchor.y = 0;
if (m_pIcons != NULL)
{
delete m_pIcons;
m_pIcons = NULL;
}
for (int I = 0; I < 4; I ++)
{
if (m_apFonts[I] != NULL)
{
m_apFonts[I]->DeleteObject();
delete m_apFonts[I];
m_apFonts[I] = NULL;
}
}
if (m_pdwParseCookies != NULL)
{
delete m_pdwParseCookies;
m_pdwParseCookies = NULL;
}
if (m_pnActualLineLength != NULL)
{
delete m_pnActualLineLength;
m_pnActualLineLength = NULL;
}
m_nParseArraySize = 0;
m_nActualLengthArraySize = 0;
m_ptCursorPos.x = 0;
m_ptCursorPos.y = 0;
m_ptSelStart = m_ptSelEnd = m_ptCursorPos;
m_bDragSelection = FALSE;
m_bVertScrollBarLocked = FALSE;
m_bHorzScrollBarLocked = FALSE;
if (::IsWindow(m_hWnd))
UpdateCaret();
m_bLastSearch = FALSE;
m_bShowInactiveSelection = FALSE;
m_bPrintHeader = FALSE;
m_bPrintFooter = TRUE;
m_bBookmarkExist = FALSE; // More bookmarks
m_bMultipleSearch = FALSE; // More search
}
void CCrystalTextView::UpdateCaret()
{
ASSERT_VALIDTEXTPOS(m_ptCursorPos);
if (m_bFocused && ! m_bCursorHidden &&
CalculateActualOffset(m_ptCursorPos.y, m_ptCursorPos.x) >= m_nOffsetChar)
{
CreateSolidCaret(2, GetLineHeight());
SetCaretPos(TextToClient(m_ptCursorPos));
ShowCaret();
}
else
{
HideCaret();
}
}
int CCrystalTextView::GetTabSize()
{
ASSERT(m_nTabSize >= 0 && m_nTabSize <= 64);
return m_nTabSize;
}
void CCrystalTextView::SetTabSize(int nTabSize)
{
ASSERT(nTabSize >= 0 && nTabSize <= 64);
if (m_nTabSize != nTabSize)
{
m_nTabSize = nTabSize;
if (m_pnActualLineLength != NULL)
{
delete m_pnActualLineLength;
m_pnActualLineLength = NULL;
}
m_nActualLengthArraySize = 0;
m_nMaxLineLength = -1;
RecalcHorzScrollBar();
Invalidate();
UpdateCaret();
}
}
CFont *CCrystalTextView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
{
int nIndex = 0;
if (bBold)
nIndex |= 1;
if (bItalic)
nIndex |= 2;
if (m_apFonts[nIndex] == NULL)
{
m_apFonts[nIndex] = new CFont;
m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
m_lfBaseFont.lfItalic = (BYTE) bItalic;
if (! m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
{
delete m_apFonts[nIndex];
m_apFonts[nIndex] = NULL;
return CView::GetFont();
}
}
return m_apFonts[nIndex];
}
void CCrystalTextView::CalcLineCharDim()
{
CDC *pdc = GetDC();
CFont *pOldFont = pdc->SelectObject(GetFont());
CSize szCharExt = pdc->GetTextExtent(_T("X"));
m_nLineHeight = szCharExt.cy;
if (m_nLineHeight < 1)
m_nLineHeight = 1;
m_nCharWidth = szCharExt.cx;
/*
TEXTMETRIC tm;
if (pdc->GetTextMetrics(&tm))
m_nCharWidth -= tm.tmOverhang;
*/
pdc->SelectObject(pOldFont);
ReleaseDC(pdc);
}
int CCrystalTextView::GetLineHeight()
{
if (m_nLineHeight == -1)
CalcLineCharDim();
return m_nLineHeight;
}
int CCrystalTextView::GetCharWidth()
{
if (m_nCharWidth == -1)
CalcLineCharDim();
return m_nCharWidth;
}
int CCrystalTextView::GetMaxLineLength()
{
if (m_nMaxLineLength == -1)
{
m_nMaxLineLength = 0;
int nLineCount = GetLineCount();
for (int I = 0; I < nLineCount; I ++)
{
int nActualLength = GetLineActualLength(I);
if (m_nMaxLineLength < nActualLength)
m_nMaxLineLength = nActualLength;
}
}
return m_nMaxLineLength;
}
CCrystalTextView *CCrystalTextView::GetSiblingView(int nRow, int nCol)
{
CSplitterWnd *pSplitter = GetParentSplitter(this, FALSE);
if (pSplitter == NULL)
return NULL;
CWnd *pWnd = CWnd::FromHandlePermanent(
::GetDlgItem(pSplitter->m_hWnd, pSplitter->IdFromRowCol(nRow, nCol)));
if (pWnd == NULL || ! pWnd->IsKindOf(RUNTIME_CLASS(CCrystalTextView)))
return NULL;
return (CCrystalTextView *) pWnd;
}
void CCrystalTextView::OnInitialUpdate()
{
CView::OnInitialUpdate();
AttachToBuffer(NULL);
CSplitterWnd *pSplitter = GetParentSplitter(this, FALSE);
if (pSplitter != NULL)
{
// See CSplitterWnd::IdFromRowCol() implementation
int nRow = (GetDlgCtrlID() - AFX_IDW_PANE_FIRST) / 16;
int nCol = (GetDlgCtrlID() - AFX_IDW_PANE_FIRST) % 16;
ASSERT(nRow >= 0 && nRow < pSplitter->GetRowCount());
ASSERT(nCol >= 0 && nCol < pSplitter->GetColumnCount());
if (nRow > 0)
{
CCrystalTextView *pSiblingView = GetSiblingView(0, nCol);
if (pSiblingView != NULL && pSiblingView != this)
{
m_nOffsetChar = pSiblingView->m_nOffsetChar;
ASSERT(m_nOffsetChar >= 0 && m_nOffsetChar <= GetMaxLineLength());
}
}
if (nCol > 0)
{
CCrystalTextView *pSiblingView = GetSiblingView(nRow, 0);
if (pSiblingView != NULL && pSiblingView != this)
{
m_nTopLine = pSiblingView->m_nTopLine;
ASSERT(m_nTopLine >= 0 && m_nTopLine < GetLineCount());
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextView printing
void CCrystalTextView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
CView::OnPrepareDC(pDC, pInfo);
if (pInfo != NULL)
{
pInfo->m_bContinuePrinting = TRUE;
if (m_pnPages != NULL && (int) pInfo->m_nCurPage > m_nPrintPages)
pInfo->m_bContinuePrinting = FALSE;
}
}
BOOL CCrystalTextView::OnPreparePrinting(CPrintInfo* pInfo)
{
return DoPreparePrinting(pInfo);
}
int CCrystalTextView::PrintLineHeight(CDC *pdc, int nLine)
{
ASSERT(nLine >= 0 && nLine < GetLineCount());
ASSERT(m_nPrintLineHeight > 0);
int nLength = GetLineLength(nLine);
if (nLength == 0)
return m_nPrintLineHeight;
CString line;
LPCTSTR pszChars = GetLineChars(nLine);
ExpandChars(pszChars, 0, nLength, line);
CRect rcPrintArea = m_rcPrintArea;
pdc->DrawText(line, &rcPrintArea, DT_LEFT | DT_NOPREFIX | DT_TOP | DT_WORDBREAK | DT_CALCRECT);
return rcPrintArea.Height();
}
void CCrystalTextView::GetPrintHeaderText(int nPageNum, CString &text)
{
ASSERT(m_bPrintHeader);
text = _T("");
}
void CCrystalTextView::GetPrintFooterText(int nPageNum, CString &text)
{
ASSERT(m_bPrintFooter);
text.Format(_T("Page %d/%d"), nPageNum, m_nPrintPages);
}
void CCrystalTextView::PrintHeader(CDC *pdc, int nPageNum)
{
CRect rcHeader = m_rcPrintArea;
rcHeader.bottom = rcHeader.top;
rcHeader.top -= (m_nPrintLineHeight + m_nPrintLineHeight / 2);
CString text;
GetPrintHeaderText(nPageNum, text);
if (! text.IsEmpty())
pdc->DrawText(text, &rcHeader, DT_CENTER | DT_NOPREFIX | DT_TOP | DT_SINGLELINE);
}
void CCrystalTextView::PrintFooter(CDC *pdc, int nPageNum)
{
CRect rcFooter = m_rcPrintArea;
rcFooter.top = rcFooter.bottom;
rcFooter.bottom += (m_nPrintLineHeight + m_nPrintLineHeight / 2);
CString text;
GetPrintFooterText(nPageNum, text);
if (! text.IsEmpty())
pdc->DrawText(text, &rcFooter, DT_CENTER | DT_NOPREFIX | DT_BOTTOM | DT_SINGLELINE);
}
void CCrystalTextView::RecalcPageLayouts(CDC *pdc, CPrintInfo *pInfo)
{
m_ptPageArea = pInfo->m_rectDraw;
m_ptPageArea.NormalizeRect();
m_nPrintLineHeight = pdc->GetTextExtent(_T("X")).cy;
m_rcPrintArea = m_ptPageArea;
CSize szTopLeft, szBottomRight;
CWinApp *pApp = AfxGetApp();
ASSERT(pApp != NULL);
szTopLeft.cx = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_LEFT, DEFAULT_PRINT_MARGIN);
szBottomRight.cx = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_RIGHT, DEFAULT_PRINT_MARGIN);
szTopLeft.cy = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_TOP, DEFAULT_PRINT_MARGIN);
szBottomRight.cy = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_BOTTOM, DEFAULT_PRINT_MARGIN);
pdc->HIMETRICtoLP(&szTopLeft);
pdc->HIMETRICtoLP(&szBottomRight);
m_rcPrintArea.left += szTopLeft.cx;
m_rcPrintArea.right -= szBottomRight.cx;
m_rcPrintArea.top += szTopLeft.cy;
m_rcPrintArea.bottom -= szBottomRight.cy;
if (m_bPrintHeader)
m_rcPrintArea.top += m_nPrintLineHeight + m_nPrintLineHeight / 2;
if (m_bPrintFooter)
m_rcPrintArea.bottom += m_nPrintLineHeight + m_nPrintLineHeight / 2;
int nLimit = 32;
m_nPrintPages = 1;
m_pnPages = new int[nLimit];
m_pnPages[0] = 0;
int nLineCount = GetLineCount();
int nLine = 1;
int y = m_rcPrintArea.top + PrintLineHeight(pdc, 0);
while (nLine < nLineCount)
{
int nHeight = PrintLineHeight(pdc, nLine);
if (y + nHeight <= m_rcPrintArea.bottom)
{
y += nHeight;
}
else
{
ASSERT(nLimit >= m_nPrintPages);
if (nLimit <= m_nPrintPages)
{
nLimit += 32;
int *pnNewPages = new int[nLimit];
memcpy(pnNewPages, m_pnPages, sizeof(int) * m_nPrintPages);
delete m_pnPages;
m_pnPages = pnNewPages;
}
ASSERT(nLimit > m_nPrintPages);
m_pnPages[m_nPrintPages ++] = nLine;
y = m_rcPrintArea.top + nHeight;
}
nLine ++;
}
}
void CCrystalTextView::OnBeginPrinting(CDC *pdc, CPrintInfo *pInfo)
{
ASSERT(m_pnPages == NULL);
ASSERT(m_pPrintFont == NULL);
CFont *pDisplayFont = GetFont();
LOGFONT lf;
pDisplayFont->GetLogFont(&lf);
CDC *pDisplayDC = GetDC();
lf.lfHeight = MulDiv(lf.lfHeight, pdc->GetDeviceCaps(LOGPIXELSY), pDisplayDC->GetDeviceCaps(LOGPIXELSY) * 2);
lf.lfWidth = MulDiv(lf.lfWidth, pdc->GetDeviceCaps(LOGPIXELSX), pDisplayDC->GetDeviceCaps(LOGPIXELSX) * 2);
ReleaseDC(pDisplayDC);
m_pPrintFont = new CFont;
if (! m_pPrintFont->CreateFontIndirect(&lf))
{
delete m_pPrintFont;
m_pPrintFont = NULL;
return;
}
pdc->SelectObject(m_pPrintFont);
}
void CCrystalTextView::OnEndPrinting(CDC *pdc, CPrintInfo *pInfo)
{
if (m_pPrintFont != NULL)
{
delete m_pPrintFont;
m_pPrintFont = NULL;
}
if (m_pnPages != NULL)
{
delete m_pnPages;
m_pnPages = NULL;
}
m_nPrintPages = 0;
m_nPrintLineHeight = 0;
}
void CCrystalTextView::OnPrint(CDC* pdc, CPrintInfo* pInfo)
{
if (m_pnPages == NULL)
{
RecalcPageLayouts(pdc, pInfo);
ASSERT(m_pnPages != NULL);
}
ASSERT(pInfo->m_nCurPage >= 1 && (int) pInfo->m_nCurPage <= m_nPrintPages);
int nLine = m_pnPages[pInfo->m_nCurPage - 1];
int nEndLine = GetLineCount();
if ((int) pInfo->m_nCurPage < m_nPrintPages)
nEndLine = m_pnPages[pInfo->m_nCurPage];
TRACE(_T("Printing page %d of %d, lines %d - %d\n"), pInfo->m_nCurPage, m_nPrintPages,
nLine, nEndLine - 1);
if (m_bPrintHeader)
PrintHeader(pdc, pInfo->m_nCurPage);
if (m_bPrintFooter)
PrintFooter(pdc, pInfo->m_nCurPage);
int y = m_rcPrintArea.top;
for (; nLine < nEndLine; nLine ++)
{
int nLineLength = GetLineLength(nLine);
if (nLineLength == 0)
{
y += m_nPrintLineHeight;
continue;
}
CRect rcPrintRect = m_rcPrintArea;
rcPrintRect.top = y;
LPCTSTR pszChars = GetLineChars(nLine);
CString line;
ExpandChars(pszChars, 0, nLineLength, line);
y += pdc->DrawText(line, &rcPrintRect, DT_LEFT | DT_NOPREFIX | DT_TOP | DT_WORDBREAK);
}
}
/////////////////////////////////////////////////////////////////////////////
// CCrystalTextView message handlers
int CCrystalTextView::GetLineCount()
{
if (m_pTextBuffer == NULL)
return 1; // Single empty line
int nLineCount = m_pTextBuffer->GetLineCount();
ASSERT(nLineCount > 0);
return nLineCount;
}
int CCrystalTextView::GetLineLength(int nLineIndex)
{
if (m_pTextBuffer == NULL)
return 0;
return m_pTextBuffer->GetLineLength(nLineIndex);
}
LPCTSTR CCrystalTextView::GetLineChars(int nLineIndex)
{
if (m_pTextBuffer == NULL)
return NULL;
return m_pTextBuffer->GetLineChars(nLineIndex);
}
void CCrystalTextView::AttachToBuffer(CCrystalTextBuffer *pBuf /*= NULL*/)
{
if (m_pTextBuffer != NULL)
m_pTextBuffer->RemoveView(this);
if (pBuf == NULL)
{
pBuf = LocateTextBuffer();
// ...
}
m_pTextBuffer = pBuf;
if (m_pTextBuffer != NULL)
m_pTextBuffer->AddView(this);
ResetView();
// Init scrollbars
CScrollBar *pVertScrollBarCtrl = GetScrollBarCtrl(SB_VERT);
if (pVertScrollBarCtrl != NULL)
pVertScrollBarCtrl->EnableScrollBar(GetScreenLines() >= GetLineCount() ?
ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
CScrollBar *pHorzScrollBarCtrl = GetScrollBarCtrl(SB_HORZ);
if (pHorzScrollBarCtrl != NULL)
pHorzScrollBarCtrl->EnableScrollBar(GetScreenChars() >= GetMaxLineLength() ?
ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
// Update scrollbars
RecalcVertScrollBar();
RecalcHorzScrollBar();
}
void CCrystalTextView::DetachFromBuffer()
{
if (m_pTextBuffer != NULL)
{
m_pTextBuffer->RemoveView(this);
m_pTextBuffer = NULL;
ResetView();
}
}
int CCrystalTextView::GetScreenLines()
{
if (m_nScreenLines == -1)
{
CRect rect;
GetClientRect(&rect);
m_nScreenLines = rect.Height() / GetLineHeight();
}
return m_nScreenLines;
}
BOOL CCrystalTextView::GetItalic(int nColorIndex)
{
return FALSE;
}
BOOL CCrystalTextView::GetBold(int nColorIndex)
{
return FALSE;
}
int CCrystalTextView::GetScreenChars()
{
if (m_nScreenChars == -1)
{
CRect rect;
GetClientRect(&rect);
m_nScreenChars = (rect.Width() - GetMarginWidth()) / GetCharWidth();
}
return m_nScreenChars;
}
void CCrystalTextView::OnDestroy()
{
DetachFromBuffer();
m_hAccel = NULL;
CView::OnDestroy();
for (int I = 0; I < 4; I ++)
{
if (m_apFonts[I] != NULL)
{
m_apFonts[I]->DeleteObject();
delete m_apFonts[I];
m_apFonts[I] = NULL;
}
}
if (m_pCacheBitmap != NULL)
{
delete m_pCacheBitmap;
m_pCacheBitmap = NULL;
}
}
BOOL CCrystalTextView::OnEraseBkgnd(CDC *pdc)
{
return TRUE;
}
void CCrystalTextView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if (m_pCacheBitmap != NULL)
{
m_pCacheBitmap->DeleteObject();
delete m_pCacheBitmap;
m_pCacheBitmap = NULL;
}
m_nScreenLines = -1;
m_nScreenChars = -1;
RecalcVertScrollBar();
RecalcHorzScrollBar();
}
void CCrystalTextView::UpdateSiblingScrollPos(BOOL bHorz)
{
CSplitterWnd *pSplitterWnd = GetParentSplitter(this, FALSE);
if (pSplitterWnd != NULL)
{
// See CSplitterWnd::IdFromRowCol() implementation for details
int nCurrentRow = (GetDlgCtrlID() - AFX_IDW_PANE_FIRST) / 16;
int nCurrentCol = (GetDlgCtrlID() - AFX_IDW_PANE_FIRST) % 16;
ASSERT(nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount());
ASSERT(nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount());
if (bHorz)
{
int nCols = pSplitterWnd->GetColumnCount();
for (int nCol = 0; nCol < nCols; nCol ++)
{
if (nCol != nCurrentCol) // We don't need to update ourselves
{
CCrystalTextView *pSiblingView = GetSiblingView(nCurrentRow, nCol);
if (pSiblingView != NULL)
pSiblingView->OnUpdateSibling(this, TRUE);
}
}
}
else
{
int nRows = pSplitterWnd->GetRowCount();
for (int nRow = 0; nRow < nRows; nRow ++)
{
if (nRow != nCurrentRow) // We don't need to update ourselves
{
CCrystalTextView *pSiblingView = GetSiblingView(nRow, nCurrentCol);
if (pSiblingView != NULL)
pSiblingView->OnUpdateSibling(this, FALSE);
}
}
}
}
}
void CCrystalTextView::OnUpdateSibling(CCrystalTextView *pUpdateSource, BOOL bHorz)
{
if (pUpdateSource != this)
{
ASSERT(pUpdateSource != NULL);
ASSERT_KINDOF(CCrystalTextView, pUpdateSource);
if (bHorz)
{
ASSERT(pUpdateSource->m_nTopLine >= 0);
ASSERT(pUpdateSource->m_nTopLine < GetLineCount());
if (pUpdateSource->m_nTopLine != m_nTopLine)
{
ScrollToLine(pUpdateSource->m_nTopLine, TRUE, FALSE);
UpdateCaret();
}
}
else
{
ASSERT(pUpdateSource->m_nOffsetChar >= 0);
ASSERT(pUpdateSource->m_nOffsetChar < GetMaxLineLength());
if (pUpdateSource->m_nOffsetChar != m_nOffsetChar)
{
ScrollToChar(pUpdateSource->m_nOffsetChar, TRUE, FALSE);
UpdateCaret();
}
}
}
}
void CCrystalTextView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
{
SCROLLINFO si;
si.cbSize = sizeof(si);
if (bPositionOnly)
{
si.fMask = SIF_POS;
si.nPos = m_nTopLine;
}
else
{
if (GetScreenLines() >= GetLineCount() && m_nTopLine > 0)
{
m_nTopLine = 0;
Invalidate();
UpdateCaret();
}
si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
si.nMin = 0;
si.nMax = GetLineCount() - 1;
si.nPage = GetScreenLines();
si.nPos = m_nTopLine;
}
VERIFY(SetScrollInfo(SB_VERT, &si));
}
void CCrystalTextView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CView::OnVScroll(nSBCode, nPos, pScrollBar);
// Note we cannot use nPos because of its 16-bit nature
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
VERIFY(GetScrollInfo(SB_VERT, &si));
int nPageLines = GetScreenLines();
int nLineCount = GetLineCount();
int nNewTopLine;
BOOL bDisableSmooth = TRUE;
switch (nSBCode)
{
case SB_TOP:
nNewTopLine = 0;
bDisableSmooth = FALSE;
break;
case SB_BOTTOM:
nNewTopLine = nLineCount - nPageLines + 1;
bDisableSmooth = FALSE;
break;
case SB_LINEUP:
nNewTopLine = m_nTopLine - 1;
break;
case SB_LINEDOWN:
nNewTopLine = m_nTopLine + 1;
break;
case SB_PAGEUP:
nNewTopLine = m_nTopLine - si.nPage + 1;
bDisableSmooth = FALSE;
break;
case SB_PAGEDOWN:
nNewTopLine = m_nTopLine + si.nPage - 1;
bDisableSmooth = FALSE;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
nNewTopLine = si.nTrackPos;
break;
default:
return;
}
if (nNewTopLine < 0)
nNewTopLine = 0;
if (nNewTopLine >= nLineCount)
nNewTopLine = nLineCount - 1;
ScrollToLine(nNewTopLine, bDisableSmooth);
}
void CCrystalTextView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
{
// Again, we cannot use nPos because it's 16-bit
SCROLLINFO si;
si.cbSize = sizeof(si);
if (bPositionOnly)
{
si.fMask = SIF_POS;
si.nPos = m_nOffsetChar;
}
else
{
if (GetScreenChars() >= GetMaxLineLength() && m_nOffsetChar > 0)
{
m_nOffsetChar = 0;
Invalidate();
UpdateCaret();
}
si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
si.nMin = 0;
si.nMax = GetMaxLineLength() - 1;
si.nPage = GetScreenChars();
si.nPos = m_nOffsetChar;
}
VERIFY(SetScrollInfo(SB_HORZ, &si));
}
void CCrystalTextView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CView::OnHScroll(nSBCode, nPos, pScrollBar);
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
VERIFY(GetScrollInfo(SB_HORZ, &si));
int nPageChars = GetScreenChars();
int nMaxLineLength = GetMaxLineLength();
int nNewOffset;
switch (nSBCode)
{
case SB_LEFT:
nNewOffset = 0;
break;
case SB_BOTTOM:
nNewOffset = nMaxLineLength - nPageChars + 1;
break;
case SB_LINEUP:
nNewOffset = m_nOffsetChar - 1;
break;
case SB_LINEDOWN:
nNewOffset = m_nOffsetChar + 1;
break;
case SB_PAGEUP:
nNewOffset = m_nOffsetChar - si.nPage + 1;
break;
case SB_PAGEDOWN:
nNewOffset = m_nOffsetChar + si.nPage - 1;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
nNewOffset = si.nTrackPos;
break;
default:
return;
}
if (nNewOffset >= nMaxLineLength)
nNewOffset = nMaxLineLength - 1;
if (nNewOffset < 0)
nNewOffset = 0;
ScrollToChar(nNewOffset, TRUE);
UpdateCaret();
}
BOOL CCrystalTextView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (nHitTest == HTCLIENT)
{
CPoint pt;
::GetCursorPos(&pt);
ScreenToClient(&pt);
if (pt.x < GetMarginWidth())
{
::SetCursor(::LoadCursor(GetResourceHandle(), MAKEINTRESOURCE(IDR_MARGIN_CURSOR)));
}
else
{
CPoint ptText = ClientToText(pt);
PrepareSelBounds();
if (IsInsideSelBlock(ptText))
{
// [JRT]: Support For Disabling Drag and Drop...
if (!m_bDisableDragAndDrop) // If Drag And Drop Not Disabled
::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
}
else
::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM)));
}
return TRUE;
}
return CView::OnSetCursor(pWnd, nHitTest, message);
}
CPoint CCrystalTextView::ClientToText(const CPoint &point)
{
int nLineCount = GetLineCount();
CPoint pt;
pt.y = m_nTopLine + point.y / GetLineHeight();
if (pt.y >= nLineCount)
pt.y = nLineCount - 1;
if (pt.y < 0)
pt.y = 0;
int nLength = 0;
LPCTSTR pszLine = NULL;
if (pt.y >= 0 && pt.y < nLineCount)
{
nLength = GetLineLength(pt.y);
pszLine = GetLineChars(pt.y);
}
int nPos = m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth();
if (nPos < 0)
nPos = 0;
int nIndex = 0, nCurPos = 0;
int nTabSize = GetTabSize();
while (nIndex < nLength)
{
if (pszLine[nIndex] == _T('\t'))
nCurPos += (nTabSize - nCurPos % nTabSize);
else
nCurPos ++;
if (nCurPos > nPos)
break;
nIndex ++;
}
ASSERT(nIndex >= 0 && nIndex <= nLength);
pt.x = nIndex;
return pt;
}
#ifdef _DEBUG
void CCrystalTextView::AssertValidTextPos(const CPoint &point)
{
if (GetLineCount() > 0)
{
ASSERT(m_nTopLine >= 0 && m_nOffsetChar >= 0);
ASSERT(point.y >= 0 && point.y < GetLineCount());
ASSERT(point.x >= 0 && point.x <= GetLineLength(point.y));
}
}
#endif
CPoint CCrystalTextView::TextToClient(const CPoint &point)
{
ASSERT_VALIDTEXTPOS(point);
int nLength = GetLineLength(point.y);
LPCTSTR pszLine = GetLineChars(point.y);
CPoint pt;
pt.y = (point.y - m_nTopLine) * GetLineHeight();
pt.x = 0;
int nTabSize = GetTabSize();
for (int nIndex = 0; nIndex < point.x; nIndex ++)
{
if (pszLine[nIndex] == _T('\t'))
pt.x += (nTabSize - pt.x % nTabSize);
else
pt.x ++;
}
pt.x = (pt.x - m_nOffsetChar) * GetCharWidth() + GetMarginWidth();
return pt;
}
void CCrystalTextView::InvalidateLines(int nLine1, int nLine2, BOOL bInvalidateMargin /*= FALSE*/)
{
bInvalidateMargin = TRUE;
if (nLine2 == -1)
{
CRect rcInvalid;
GetClientRect(&rcInvalid);
if (! bInvalidateMargin)
rcInvalid.left += GetMarginWidth();
rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
InvalidateRect(&rcInvalid, FALSE);
}
else
{
if (nLine2 < nLine1)
{
int nTemp = nLine1;
nLine1 = nLine2;
nLine2 = nTemp;
}
CRect rcInvalid;
GetClientRect(&rcInvalid);
if (! bInvalidateMargin)
rcInvalid.left += GetMarginWidth();
rcInvalid.top = (nLine1 - m_nTopLine) * GetLineHeight();
rcInvalid.bottom = (nLine2 - m_nTopLine + 1) * GetLineHeight();
InvalidateRect(&rcInvalid, FALSE);
}
}
void CCrystalTextView::SetSelection(const CPoint &ptStart, const CPoint &ptEnd)
{
ASSERT_VALIDTEXTPOS(ptStart);
ASSERT_VALIDTEXTPOS(ptEnd);
if (m_ptSelStart == ptStart)
{
if (m_ptSelEnd != ptEnd)
InvalidateLines(ptEnd.y, m_ptSelEnd.y);
}
else
{
InvalidateLines(ptStart.y, ptEnd.y);
InvalidateLines(m_ptSelStart.y, m_ptSelEnd.y);
}
m_ptSelStart = ptStart;
m_ptSelEnd = ptEnd;
}
void CCrystalTextView::AdjustTextPoint(CPoint &point)
{
point.x += GetCharWidth() / 2; //todo
}
void CCrystalTextView::OnSetFocus(CWnd* pOldWnd)
{
CView::OnSetFocus(pOldWnd);
m_bFocused = TRUE;
if (m_ptSelStart != m_ptSelEnd)
InvalidateLines(m_ptSelStart.y, m_ptSelEnd.y);
UpdateCaret();
}
DWORD CCrystalTextView::ParseLine(DWORD dwCookie, int nLineIndex, TEXTBLOCK *pBuf, int &nActualItems)
{
return 0;
}
int CCrystalTextView::CalculateActualOffset(int nLineIndex, int nCharIndex)
{
int nLength = GetLineLength(nLineIndex);
ASSERT(nCharIndex >= 0 && nCharIndex <= nLength);
LPCTSTR pszChars = GetLineChars(nLineIndex);
int nOffset = 0;
int nTabSize = GetTabSize();
for (int I = 0; I < nCharIndex; I ++)
{
if (pszChars[I] == _T('\t'))
nOffset += (nTabSize - nOffset % nTabSize);
else
nOffset ++;
}
return nOffset;
}
int CCrystalTextView::ApproxActualOffset(int nLineIndex, int nOffset)
{
if (nOffset == 0)
return 0;
int nLength = GetLineLength(nLineIndex);
LPCTSTR pszChars = GetLineChars(nLineIndex);
int nCurrentOffset = 0;
int nTabSize = GetTabSize();
for (int I = 0; I < nLength; I ++)
{
if (pszChars[I] == _T('\t'))
nCurrentOffset += (nTabSize - nCurrentOffset % nTabSize);
else
nCurrentOffset ++;
if (nCurrentOffset >= nOffset)
{
if (nOffset <= nCurrentOffset - nTabSize / 2)
return I;
return I + 1;
}
}
return nLength;
}
void CCrystalTextView::EnsureVisible(CPoint pt)
{
// Scroll vertically
int nLineCount = GetLineCount();
int nNewTopLine = m_nTopLine;
if (pt.y >= nNewTopLine + GetScreenLines())
{
nNewTopLine = pt.y - GetScreenLines() + 1;
}
if (pt.y < nNewTopLine)
{
nNewTopLine = pt.y;
}
if (nNewTopLine < 0)
nNewTopLine = 0;
if (nNewTopLine >= nLineCount)
nNewTopLine = nLineCount - 1;
if (m_nTopLine != nNewTopLine)
{
ScrollToLine(nNewTopLine);
UpdateSiblingScrollPos(TRUE);
}
// Scroll horizontally
int nActualPos = CalculateActualOffset(pt.y, pt.x);
int nNewOffset = m_nOffsetChar;
if (nActualPos > nNewOffset + GetScreenChars())
{
nNewOffset = nActualPos - GetScreenChars();
}
if (nActualPos < nNewOffset)
{
nNewOffset = nActualPos;
}
if (nNewOffset >= GetMaxLineLength())
nNewOffset = GetMaxLineLength() - 1;
if (nNewOffset < 0)
nNewOffset = 0;
if (m_nOffsetChar != nNewOffset)
{
ScrollToChar(nNewOffset);
UpdateCaret();
UpdateSiblingScrollPos(FALSE);
}
}
void CCrystalTextView::OnKillFocus(CWnd* pNewWnd)
{
CView::OnKillFocus(pNewWnd);
m_bFocused = FALSE;
UpdateCaret();
if (m_ptSelStart != m_ptSelEnd)
InvalidateLines(m_ptSelStart.y, m_ptSelEnd.y);
if (m_bDragSelection)
{
ReleaseCapture();
KillTimer(m_nDragSelTimer);
m_bDragSelection = FALSE;
}
}
void CCrystalTextView::OnSysColorChange()
{
CView::OnSysColorChange();
Invalidate();
}
void CCrystalTextView::GetText(const CPoint &ptStart, const CPoint &ptEnd, CString &text)
{
if (m_pTextBuffer != NULL)
m_pTextBuffer->GetText(ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, text);
else
text = _T("");
}
void CCrystalTextView::UpdateView(CCrystalTextView *pSource, CUpdateContext *pContext,
DWORD dwFlags, int nLineIndex /*= -1*/)
{
if (dwFlags & UPDATE_RESET)
{
ResetView();
RecalcVertScrollBar();
RecalcHorzScrollBar();
return;
}
int nLineCount = GetLineCount();
ASSERT(nLineCount > 0);
ASSERT(nLineIndex >= -1 && nLineIndex < nLineCount);
if ((dwFlags & UPDATE_SINGLELINE) != 0)
{
ASSERT(nLineIndex != -1);
// All text below this line should be reparsed
if (m_pdwParseCookies != NULL)
{
ASSERT(m_nParseArraySize == nLineCount);
memset(m_pdwParseCookies + nLineIndex, 0xff, sizeof(DWORD) * (m_nParseArraySize - nLineIndex));
}
// This line'th actual length must be recalculated
if (m_pnActualLineLength != NULL)
{
ASSERT(m_nActualLengthArraySize == nLineCount);
m_pnActualLineLength[nLineIndex] = -1;
}
// Repaint the lines
InvalidateLines(nLineIndex, -1, TRUE);
}
else
{
if (nLineIndex == -1)
nLineIndex = 0; // Refresh all text
// All text below this line should be reparsed
if (m_pdwParseCookies != NULL)
{
if (m_nParseArraySize != nLineCount)
{
// Reallocate cookies array
DWORD *pdwNewArray = new DWORD[nLineCount];
if (nLineIndex > 0)
memcpy(pdwNewArray, m_pdwParseCookies, sizeof(DWORD) * nLineIndex);
delete m_pdwParseCookies;
m_nParseArraySize = nLineCount;
m_pdwParseCookies = pdwNewArray;
}
memset(m_pdwParseCookies + nLineIndex, 0xff, sizeof(DWORD) * (m_nParseArraySize - nLineIndex));
}
// Recalculate actual length for all lines below this
if (m_pnActualLineLength != NULL)
{
if (m_nActualLengthArraySize != nLineCount)
{
// Reallocate actual length array
int *pnNewArray = new int[nLineCount];
if (nLineIndex > 0)
memcpy(pnNewArray, m_pnActualLineLength, sizeof(int) * nLineIndex);
delete m_pnActualLineLength;
m_nActualLengthArraySize = nLineCount;
m_pnActualLineLength = pnNewArray;
}
memset(m_pnActualLineLength + nLineIndex, 0xff, sizeof(DWORD) * (m_nActualLengthArraySize - nLineIndex));
}
// Repaint the lines
InvalidateLines(nLineIndex, -1, TRUE);
}
// All those points must be recalculated and validated
if (pContext != NULL)
{
pContext->RecalcPoint(m_ptCursorPos);
pContext->RecalcPoint(m_ptSelStart);
pContext->RecalcPoint(m_ptSelEnd);
pContext->RecalcPoint(m_ptAnchor);
ASSERT_VALIDTEXTPOS(m_ptCursorPos);
ASSERT_VALIDTEXTPOS(m_ptSelStart);
ASSERT_VALIDTEXTPOS(m_ptSelEnd);
ASSERT_VALIDTEXTPOS(m_ptAnchor);
if (m_bDraggingText)
{
pContext->RecalcPoint(m_ptDraggedTextBegin);
pContext->RecalcPoint(m_ptDraggedTextEnd);
ASSERT_VALIDTEXTPOS(m_ptDraggedTextBegin);
ASSERT_VALIDTEXTPOS(m_ptDraggedTextEnd);
}
CPoint ptTopLine(0, m_nTopLine);
pContext->RecalcPoint(ptTopLine);
ASSERT_VALIDTEXTPOS(ptTopLine);
m_nTopLine = ptTopLine.y;
UpdateCaret();
}
// Recalculate vertical scrollbar, if needed
if ((dwFlags & UPDATE_VERTRANGE) != 0)
{
if (! m_bVertScrollBarLocked)
RecalcVertScrollBar();
}
// Recalculate horizontal scrollbar, if needed
if ((dwFlags & UPDATE_HORZRANGE) != 0)
{
m_nMaxLineLength = -1;
if (! m_bHorzScrollBarLocked)
RecalcHorzScrollBar();
}
}
HINSTANCE CCrystalTextView::GetResourceHandle()
{
#ifdef CRYSEDIT_RES_HANDLE
return CRYSEDIT_RES_HANDLE;
#else
if (s_hResourceInst != NULL)
return s_hResourceInst;
return AfxGetResourceHandle();
#endif
}
int CCrystalTextView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
m_lfBaseFont.lfHeight = 0;
m_lfBaseFont.lfWeight = FW_NORMAL;
m_lfBaseFont.lfItalic = FALSE;
m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
ASSERT(m_hAccel == NULL);
m_hAccel = ::LoadAccelerators(GetResourceHandle(), MAKEINTRESOURCE(IDR_DEFAULT_ACCEL));
ASSERT(m_hAccel != NULL);
return 0;
}
void CCrystalTextView::SetAnchor(const CPoint &ptNewAnchor)
{
ASSERT_VALIDTEXTPOS(ptNewAnchor);
m_ptAnchor = ptNewAnchor;
}
void CCrystalTextView::OnEditOperation(int nAction, LPCTSTR pszText)
{
}
BOOL CCrystalTextView::PreTranslateMessage(MSG *pMsg)
{
if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
{
if (m_hAccel != NULL)
{
if (::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
return TRUE;
}
}
return CView::PreTranslateMessage(pMsg);
}
CPoint CCrystalTextView::GetCursorPos()
{
return m_ptCursorPos;
}
void CCrystalTextView::SetCursorPos(const CPoint &ptCursorPos)
{
ASSERT_VALIDTEXTPOS(ptCursorPos);
m_ptCursorPos = ptCursorPos;
m_nIdealCharPos = CalculateActualOffset(m_ptCursorPos.y, m_ptCursorPos.x);
UpdateCaret();
}
void CCrystalTextView::SetSelectionMargin(BOOL bSelMargin)
{
if (m_bSelMargin != bSelMargin)
{
m_bSelMargin = bSelMargin;
if (::IsWindow(m_hWnd))
{
m_nScreenChars = -1;
Invalidate();
RecalcHorzScrollBar();
}
}
}
void CCrystalTextView::GetFont(LOGFONT &lf)
{
lf = m_lfBaseFont;
}
void CCrystalTextView::SetFont(const LOGFONT &lf)
{
m_lfBaseFont = lf;
m_nScreenLines = -1;
m_nScreenChars = -1;
m_nCharWidth = -1;
m_nLineHeight = -1;
if (m_pCacheBitmap != NULL)
{
m_pCacheBitmap->DeleteObject();
delete m_pCacheBitmap;
m_pCacheBitmap = NULL;
}
for (int I = 0; I < 4; I ++)
{
if (m_apFonts[I] != NULL)
{
m_apFonts[I]->DeleteObject();
delete m_apFonts[I];
m_apFonts[I] = NULL;
}
}
if (::IsWindow(m_hWnd))
{
RecalcVertScrollBar();
RecalcHorzScrollBar();
UpdateCaret();
Invalidate();
}
}
void CCrystalTextView::OnUpdateIndicatorPosition(CCmdUI* pCmdUI)
{
ASSERT_VALIDTEXTPOS(m_ptCursorPos);
CString stat;
stat.Format(_T("Ln %d, Col %d"), m_ptCursorPos.y + 1, m_ptCursorPos.x + 1);
pCmdUI->SetText(stat);
}
void CCrystalTextView::OnUpdateIndicatorCRLF(CCmdUI* pCmdUI)
{
if (m_pTextBuffer != NULL)
{
int crlfMode = m_pTextBuffer->GetCRLFMode();
switch (crlfMode)
{
case CRLF_STYLE_DOS:
pCmdUI->SetText(_T("DOS"));
pCmdUI->Enable(TRUE);
break;
case CRLF_STYLE_UNIX:
pCmdUI->SetText(_T("UNIX"));
pCmdUI->Enable(TRUE);
break;
case CRLF_STYLE_MAC:
pCmdUI->SetText(_T("MAC"));
pCmdUI->Enable(TRUE);
break;
default:
pCmdUI->SetText(NULL);
pCmdUI->Enable(FALSE);
}
}
else
{
pCmdUI->SetText(NULL);
pCmdUI->Enable(FALSE);
}
}
void CCrystalTextView::OnToggleBookmark(UINT nCmdID)
{
int nBookmarkID = nCmdID - ID_EDIT_TOGGLE_BOOKMARK0;
ASSERT(nBookmarkID >= 0 && nBookmarkID <= 9);
if (m_pTextBuffer != NULL)
{
DWORD dwFlags = GetLineFlags(m_ptCursorPos.y);
DWORD dwMask = LF_BOOKMARK(nBookmarkID);
m_pTextBuffer->SetLineFlag(m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0);
}
}
void CCrystalTextView::OnGoBookmark(UINT nCmdID)
{
int nBookmarkID = nCmdID - ID_EDIT_GO_BOOKMARK0;
ASSERT(nBookmarkID >= 0 && nBookmarkID <= 9);
if (m_pTextBuffer != NULL)
{
int nLine = m_pTextBuffer->GetLineWithFlag(LF_BOOKMARK(nBookmarkID));
if (nLine >= 0)
{
CPoint pt(0, nLine);
ASSERT_VALIDTEXTPOS(pt);
SetCursorPos(pt);
SetSelection(pt, pt);
SetAnchor(pt);
EnsureVisible(pt);
}
}
}
void CCrystalTextView::OnClearBookmarks()
{
if (m_pTextBuffer != NULL)
{
for (int nBookmarkID = 0; nBookmarkID <= 9; nBookmarkID++)
{
int nLine = m_pTextBuffer->GetLineWithFlag(LF_BOOKMARK(nBookmarkID));
if (nLine >= 0)
{
m_pTextBuffer->SetLineFlag(nLine, LF_BOOKMARK(nBookmarkID), FALSE);
}
}
}
}
void CCrystalTextView::ShowCursor()
{
m_bCursorHidden = FALSE;
UpdateCaret();
}
void CCrystalTextView::HideCursor()
{
m_bCursorHidden = TRUE;
UpdateCaret();
}
DROPEFFECT CCrystalTextView::GetDropEffect()
{
return DROPEFFECT_COPY;
}
void CCrystalTextView::OnDropSource(DROPEFFECT de)
{
ASSERT(de == DROPEFFECT_COPY);
}
HGLOBAL CCrystalTextView::PrepareDragData()
{
PrepareSelBounds();
if (m_ptDrawSelStart == m_ptDrawSelEnd)
return NULL;
CString text;
GetText(m_ptDrawSelStart, m_ptDrawSelEnd, text);
HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, lstrlen(text) + 1);
if (hData == NULL)
return NULL;
LPSTR pszData = (LPSTR) ::GlobalLock(hData);
USES_CONVERSION;
strcpy(pszData, T2A(text.GetBuffer(0)));
text.ReleaseBuffer();
::GlobalUnlock(hData);
m_ptDraggedTextBegin = m_ptDrawSelStart;
m_ptDraggedTextEnd = m_ptDrawSelEnd;
return hData;
}
static int FindStringHelper(LPCTSTR pszFindWhere, LPCTSTR pszFindWhat, BOOL bWholeWord)
{
ASSERT(pszFindWhere != NULL);
ASSERT(pszFindWhat != NULL);
int nCur = 0;
int nLength = lstrlen(pszFindWhat);
for (;;)
{
#ifdef _UNICODE
LPCTSTR pszPos = wcsstr(pszFindWhere, pszFindWhat);
#else
LPCTSTR pszPos = strstr(pszFindWhere, pszFindWhat);
#endif
if (pszPos == NULL)
return -1;
if (! bWholeWord)
return nCur + (pszPos - pszFindWhere);
if (pszPos > pszFindWhere && (isalnum(pszPos[-1]) || pszPos[-1] == _T('_')))
{
nCur += (pszPos - pszFindWhere);
pszFindWhere = pszPos + 1;
continue;
}
if (isalnum(pszPos[nLength]) || pszPos[nLength] == _T('_'))
{
nCur += (pszPos - pszFindWhere + 1);
pszFindWhere = pszPos + 1;
continue;
}
return nCur + (pszPos - pszFindWhere);
}
ASSERT(FALSE); // Unreachable
return -1;
}
BOOL CCrystalTextView::HighlightText(const CPoint &ptStartPos, int nLength)
{
ASSERT_VALIDTEXTPOS(ptStartPos);
m_ptCursorPos = ptStartPos;
m_ptCursorPos.x += nLength;
ASSERT_VALIDTEXTPOS(m_ptCursorPos); // Probably 'nLength' is bigger than expected...
m_ptAnchor = m_ptCursorPos;
SetSelection(ptStartPos, m_ptCursorPos);
UpdateCaret();
EnsureVisible(m_ptCursorPos);
return TRUE;
}
BOOL CCrystalTextView::FindText(LPCTSTR pszText, const CPoint &ptStartPos, DWORD dwFlags,
BOOL bWrapSearch, CPoint *pptFoundPos)
{
int nLineCount = GetLineCount();
return FindTextInBlock(pszText, ptStartPos, CPoint(0, 0),
CPoint(GetLineLength(nLineCount - 1), nLineCount - 1),
dwFlags, bWrapSearch, pptFoundPos);
}
BOOL CCrystalTextView::FindTextInBlock(LPCTSTR pszText, const CPoint &ptStartPosition,
const CPoint &ptBlockBegin, const CPoint &ptBlockEnd,
DWORD dwFlags, BOOL bWrapSearch, CPoint *pptFoundPos)
{
CPoint ptCurrentPos = ptStartPosition;
ASSERT(pszText != NULL && lstrlen(pszText) > 0);
ASSERT_VALIDTEXTPOS(ptCurrentPos);
ASSERT_VALIDTEXTPOS(ptBlockBegin);
ASSERT_VALIDTEXTPOS(ptBlockEnd);
ASSERT(ptBlockBegin.y < ptBlockEnd.y || ptBlockBegin.y == ptBlockEnd.y &&
ptBlockBegin.x <= ptBlockEnd.x);
if (ptBlockBegin == ptBlockEnd)
return FALSE;
if (ptCurrentPos.y < ptBlockBegin.y || ptCurrentPos.y == ptBlockBegin.y &&
ptCurrentPos.x < ptBlockBegin.x)
ptCurrentPos = ptBlockBegin;
CString what = pszText;
if ((dwFlags & FIND_MATCH_CASE) == 0)
what.MakeUpper();
if (dwFlags & FIND_DIRECTION_UP)
{
// Let's check if we deal with whole text.
// At this point, we cannot search *up* in selection
ASSERT(ptBlockBegin.x == 0 && ptBlockBegin.y == 0);
ASSERT(ptBlockEnd.x == GetLineLength(GetLineCount() - 1) &&
ptBlockEnd.y == GetLineCount() - 1);
// Proceed as if we have whole text search.
for (;;)
{
while (ptCurrentPos.y >= 0)
{
int nLineLength = GetLineLength(ptCurrentPos.y);
nLineLength -= ptCurrentPos.x;
if (nLineLength <= 0)
{
ptCurrentPos.x = 0;
ptCurrentPos.y --;
continue;
}
LPCTSTR pszChars = GetLineChars(ptCurrentPos.y);
pszChars += ptCurrentPos.x;
CString line;
lstrcpyn(line.GetBuffer(nLineLength + 1), pszChars, nLineLength + 1);
line.ReleaseBuffer();
if ((dwFlags & FIND_MATCH_CASE) == 0)
line.MakeUpper();
int nPos = ::FindStringHelper(line, what, (dwFlags & FIND_WHOLE_WORD) != 0);
if (nPos >= 0) // Found text!
{
ptCurrentPos.x += nPos;
*pptFoundPos = ptCurrentPos;
return TRUE;
}
ptCurrentPos.x = 0;
ptCurrentPos.y --;
}
// Beginning of text reached
if (! bWrapSearch)
return FALSE;
// Start again from the end of text
bWrapSearch = FALSE;
ptCurrentPos = CPoint(0, GetLineCount() - 1);
}
}
else
{
for (;;)
{
while (ptCurrentPos.y <= ptBlockEnd.y)
{
int nLineLength = GetLineLength(ptCurrentPos.y);
nLineLength -= ptCurrentPos.x;
if (nLineLength <= 0)
{
ptCurrentPos.x = 0;
ptCurrentPos.y ++;
continue;
}
LPCTSTR pszChars = GetLineChars(ptCurrentPos.y);
pszChars += ptCurrentPos.x;
// Prepare necessary part of line
CString line;
lstrcpyn(line.GetBuffer(nLineLength + 1), pszChars, nLineLength + 1);
line.ReleaseBuffer();
if ((dwFlags & FIND_MATCH_CASE) == 0)
line.MakeUpper();
// Perform search in the line
int nPos = ::FindStringHelper(line, what, (dwFlags & FIND_WHOLE_WORD) != 0);
if (nPos >= 0)
{
ptCurrentPos.x += nPos;
// Check of the text found is outside the block.
if (ptCurrentPos.y == ptBlockEnd.y && ptCurrentPos.x >= ptBlockEnd.x)
break;
*pptFoundPos = ptCurrentPos;
return TRUE;
}
// Go further, text was not found
ptCurrentPos.x = 0;
ptCurrentPos.y ++;
}
// End of text reached
if (! bWrapSearch)
return FALSE;
// Start from the beginning
bWrapSearch = FALSE;
ptCurrentPos = ptBlockBegin;
}
}
ASSERT(FALSE); // Unreachable
return FALSE;
}
void CCrystalTextView::OnEditFind()
{
CWinApp *pApp = AfxGetApp();
ASSERT(pApp != NULL);
CFindTextDlg dlg(this);
if (m_bLastSearch)
{
// Get the latest search parameters
dlg.m_bMatchCase = (m_dwLastSearchFlags & FIND_MATCH_CASE) != 0;
dlg.m_bWholeWord = (m_dwLastSearchFlags & FIND_WHOLE_WORD) != 0;
dlg.m_nDirection = (m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0 ? 0 : 1;
if (m_pszLastFindWhat != NULL)
dlg.m_sText = m_pszLastFindWhat;
}
else
{
// Take search parameters from registry
dlg.m_bMatchCase = pApp->GetProfileInt(REG_FIND_SUBKEY, REG_MATCH_CASE, FALSE);
dlg.m_bWholeWord = pApp->GetProfileInt(REG_FIND_SUBKEY, REG_WHOLE_WORD, FALSE);
dlg.m_nDirection = 1; // Search down
dlg.m_sText = pApp->GetProfileString(REG_FIND_SUBKEY, REG_FIND_WHAT, _T(""));
}
// Take the current selection, if any
if (IsSelection())
{
CPoint ptSelStart, ptSelEnd;
GetSelection(ptSelStart, ptSelEnd); if (ptSelStart.y == ptSelEnd.y)
{
LPCTSTR pszChars = GetLineChars(ptSelStart.y);
int nChars = ptSelEnd.x - ptSelStart.x;
lstrcpyn(dlg.m_sText.GetBuffer(nChars + 1), pszChars + ptSelStart.x, nChars + 1);
dlg.m_sText.ReleaseBuffer();
}
}
// Execute Find dialog
dlg.m_ptCurrentPos = m_ptCursorPos; // Search from cursor position
m_bShowInactiveSelection = TRUE;
dlg.DoModal();
m_bShowInactiveSelection = FALSE;
// Save search parameters for 'F3' command
m_bLastSearch = TRUE;
if (m_pszLastFindWhat != NULL)
free(m_pszLastFindWhat);
#ifdef _UNICODE
m_pszLastFindWhat = _wcsdup(dlg.m_sText);
#else
m_pszLastFindWhat = strdup(dlg.m_sText);
#endif
m_dwLastSearchFlags = 0;
if (dlg.m_bMatchCase)
m_dwLastSearchFlags |= FIND_MATCH_CASE;
if (dlg.m_bWholeWord)
m_dwLastSearchFlags |= FIND_WHOLE_WORD;
if (dlg.m_nDirection == 0)
m_dwLastSearchFlags |= FIND_DIRECTION_UP;
// Save search parameters to registry
pApp->WriteProfileInt(REG_FIND_SUBKEY, REG_MATCH_CASE, dlg.m_bMatchCase);
pApp->WriteProfileInt(REG_FIND_SUBKEY, REG_WHOLE_WORD, dlg.m_bWholeWord);
pApp->WriteProfileString(REG_FIND_SUBKEY, REG_FIND_WHAT, dlg.m_sText);
}
void CCrystalTextView::OnEditRepeat()
{
if (m_bLastSearch)
{
CPoint ptFoundPos;
if (! FindText(m_pszLastFindWhat, m_ptCursorPos, m_dwLastSearchFlags, TRUE, &ptFoundPos))
{
CString prompt;
prompt.Format(IDS_EDIT_TEXT_NOT_FOUND, m_pszLastFindWhat);
AfxMessageBox(prompt);
return;
}
HighlightText(ptFoundPos, lstrlen(m_pszLastFindWhat));
m_bMultipleSearch = TRUE; // More search
}
}
void CCrystalTextView::OnUpdateEditRepeat(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bLastSearch);
}
void CCrystalTextView::OnEditFindPrevious()
{
DWORD dwSaveSearchFlags = m_dwLastSearchFlags;
if ((m_dwLastSearchFlags & FIND_DIRECTION_UP) != 0)
m_dwLastSearchFlags &= ~FIND_DIRECTION_UP;
else
m_dwLastSearchFlags |= FIND_DIRECTION_UP;
OnEditRepeat();
m_dwLastSearchFlags = dwSaveSearchFlags;
}
void CCrystalTextView::OnUpdateEditFindPrevious(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bLastSearch);
}
void CCrystalTextView::OnFilePageSetup()
{
CWinApp *pApp = AfxGetApp();
ASSERT(pApp != NULL);
CPageSetupDialog dlg;
dlg.m_psd.Flags &= ~PSD_INTHOUSANDTHSOFINCHES;
dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS | PSD_DISABLEORIENTATION | PSD_DISABLEPAPER;
dlg.m_psd.rtMargin.left = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_LEFT, DEFAULT_PRINT_MARGIN);
dlg.m_psd.rtMargin.right = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_RIGHT, DEFAULT_PRINT_MARGIN);
dlg.m_psd.rtMargin.top = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_TOP, DEFAULT_PRINT_MARGIN);
dlg.m_psd.rtMargin.bottom = pApp->GetProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_BOTTOM, DEFAULT_PRINT_MARGIN);
if (dlg.DoModal() == IDOK)
{
pApp->WriteProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_LEFT, dlg.m_psd.rtMargin.left);
pApp->WriteProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_RIGHT, dlg.m_psd.rtMargin.right);
pApp->WriteProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_TOP, dlg.m_psd.rtMargin.top);
pApp->WriteProfileInt(REG_PAGE_SUBKEY, REG_MARGIN_BOTTOM, dlg.m_psd.rtMargin.bottom);
}
}
void CCrystalTextView::OnToggleBookmark()
{
if (m_pTextBuffer != NULL)
{
DWORD dwFlags = GetLineFlags(m_ptCursorPos.y);
DWORD dwMask = LF_BOOKMARKS;
m_pTextBuffer->SetLineFlag(m_ptCursorPos.y, dwMask, (dwFlags & dwMask) == 0, FALSE);
}
int nLine = m_pTextBuffer->GetLineWithFlag(LF_BOOKMARKS);
if (nLine >= 0)
m_bBookmarkExist = TRUE;
else
m_bBookmarkExist = FALSE;
}
void CCrystalTextView::OnNextBookmark()
{
if (m_pTextBuffer != NULL)
{
int nLine = m_pTextBuffer->FindNextBookmarkLine(m_ptCursorPos.y);
if (nLine >= 0)
{
CPoint pt(0, nLine);
ASSERT_VALIDTEXTPOS(pt);
SetCursorPos(pt);
SetSelection(pt, pt);
SetAnchor(pt);
EnsureVisible(pt);
}
}
}
void CCrystalTextView::OnPrevBookmark()
{
if (m_pTextBuffer != NULL)
{
int nLine = m_pTextBuffer->FindPrevBookmarkLine(m_ptCursorPos.y);
if (nLine >= 0)
{
CPoint pt(0, nLine);
ASSERT_VALIDTEXTPOS(pt);
SetCursorPos(pt);
SetSelection(pt, pt);
SetAnchor(pt);
EnsureVisible(pt);
}
}
}
void CCrystalTextView::OnClearAllBookmarks()
{
if (m_pTextBuffer != NULL)
{
int nLineCount = GetLineCount();
for (int I = 0; I < nLineCount; I ++)
{
if (m_pTextBuffer->GetLineFlags(I) & LF_BOOKMARKS)
m_pTextBuffer->SetLineFlag(I, LF_BOOKMARKS, FALSE);
}
m_bBookmarkExist = FALSE;
}
}
void CCrystalTextView::OnUpdateNextBookmark(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bBookmarkExist);
}
void CCrystalTextView::OnUpdatePrevBookmark(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bBookmarkExist);
}
void CCrystalTextView::OnUpdateClearAllBookmarks(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bBookmarkExist);
}
BOOL CCrystalTextView::GetViewTabs()
{
return m_bViewTabs;
}
void CCrystalTextView::SetViewTabs(BOOL bViewTabs)
{
if (bViewTabs != m_bViewTabs)
{
m_bViewTabs = bViewTabs;
if (::IsWindow(m_hWnd))
Invalidate();
}
}
BOOL CCrystalTextView::GetSelectionMargin()
{
return m_bSelMargin;
}
int CCrystalTextView::GetMarginWidth()
{
return m_bSelMargin ? 20 : 1;
}
BOOL CCrystalTextView::GetSmoothScroll() const
{
return m_bSmoothScroll;
}
void CCrystalTextView::SetSmoothScroll(BOOL bSmoothScroll)
{
m_bSmoothScroll = bSmoothScroll;
}
// [JRT]
BOOL CCrystalTextView::GetDisableDragAndDrop() const
{
return m_bDisableDragAndDrop;
}
// [JRT]
void CCrystalTextView::SetDisableDragAndDrop(BOOL bDDAD)
{
m_bDisableDragAndDrop = bDDAD;
}