You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

787 lines
18 KiB
C++

2 years ago
// XTPSyntaxEditDrawTextProcessor.cpp: implementation of the CXTPSyntaxEditDrawTextProcessor class.
//
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME SYNTAX EDIT LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
// common includes
#include "Common/XTPDrawHelpers.h"
#include "Common/XTPSmartPtrInternalT.h"
#include "XTPSyntaxEditDrawTextProcessor.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CXTPSyntaxEditDrawTextProcessor::CXTPSyntaxEditDrawTextProcessor()
{
m_nTabSize = 4;
ZeroMemory(&m_tmText, sizeof(m_tmText));
m_nSpaceWidth = 6; // 6 px
m_rcTextRect.SetRect(0, 0, 0, 0);
m_nRowHeight = 16;
m_nScrollXOffset = 0;
m_nDrawingRow = -1;
m_nNextRowPosX = 0;
m_nPrintingRow = -1;
m_ptNextPrintPos = CPoint(0, 0);
m_bUseOutputDC = FALSE;
m_arExpandCharsBuffer.SetDataSize(0, 1024, 1024);
}
int CXTPSyntaxEditDrawTextProcessor::RecalcRowHeight(CDC* pDC, CFont* pFont)
{
ASSERT(pDC && pFont);
if (!pDC || !pFont)
return m_nRowHeight;
m_bUseOutputDC = FALSE;
CXTPFontDC fontDC(pDC, pFont);
BOOL bRes = pDC->GetTextMetrics(&m_tmText);
ASSERT(bRes);
if (bRes)
m_nRowHeight = m_tmText.tmHeight;
if (pDC->IsPrinting())
{
TEXTMETRIC tmText;
bRes = pDC->GetOutputTextMetrics(&tmText);
ASSERT(bRes);
if (bRes)
{
if (tmText.tmHeight > m_nRowHeight)
{
m_nRowHeight = tmText.tmHeight;
}
m_bUseOutputDC = //tmText.tmHeight > m_tmText.tmHeight ||
tmText.tmAveCharWidth > m_tmText.tmAveCharWidth ||
tmText.tmMaxCharWidth > m_tmText.tmMaxCharWidth;
if (m_bUseOutputDC)
m_tmText = tmText;
}
}
if (m_bUseOutputDC)
m_nSpaceWidth = pDC->GetOutputTextExtent(_T(" "), 1).cx;
else
m_nSpaceWidth = pDC->GetTextExtent(_T(" "), 1).cx;
return m_nRowHeight;
}
CXTPSyntaxEditDrawTextProcessor::CXTPRowInfo* CXTPSyntaxEditDrawTextProcessor::GetRowInfo(int nRow) const
{
if (nRow < 0)
return NULL;
if (nRow > GetRowsCount(TRUE))
nRow = GetRowsCount(TRUE) + 10;
if (nRow >= m_arRows.GetSize())
m_arRows.SetSize(nRow + 100);
CXTPRowInfo* pRI = m_arRows.GetAt(nRow, FALSE);
if (!pRI)
{
pRI = new CXTPRowInfo();
CXTPRowsInfoArray::TObjectPtr ptrRowI(pRI, FALSE);
m_arRows.SetAt(nRow, ptrRowI);
}
return pRI;
}
void CXTPSyntaxEditDrawTextProcessor::ResetRowInfo(int nRow)
{
m_nDrawingRow = nRow;
m_nNextRowPosX = 0;
m_nPrintingRow = -1;
m_ptNextPrintPos = CPoint(0, 0);
if (nRow < 0 || nRow >= m_arRows.GetSize())
{
return;
}
CXTPRowInfo* pRI = m_arRows.GetAt(nRow, FALSE);
if (pRI)
pRI->Reset();
}
int CXTPSyntaxEditDrawTextProcessor::AlignColIdxToTabs(int nRow, int nCol, BOOL bVirtualSpace)
{
CXTPRowInfo* pRI = GetRowInfo(nRow);
ASSERT(pRI);
if (!pRI)
return nCol;
if (nCol >= 0 && nCol < pRI->arTabs.GetDataSize())
{
nCol += pRI->arTabs[nCol];
}
if (!bVirtualSpace && nCol > pRI->arCharsEnds.GetDataSize())
{
nCol = pRI->arCharsEnds.GetDataSize();
}
return nCol;
}
void CXTPSyntaxEditDrawTextProcessor::SetRowTabPositions(int nRow, LPCTSTR pcszOrigRowText)
{
CXTPRowInfo* pRowI = GetRowInfo(nRow);
ASSERT(pRowI);
if (!pRowI)
return;
pRowI->arTabs.RemoveAll();
pRowI->arDispCol2StrPos.RemoveAll();
pRowI->arStrPos2DispCol.RemoveAll();
int nDispPos = 0;
int nStrPos = 0;
for (LPCTSTR p = pcszOrigRowText; p && *p != 0; p = _tcsinc(p), nStrPos++)
{
pRowI->arStrPos2DispCol.AddData(nDispPos);
if (*p == _T('\t'))
{
int nSpaces = m_nTabSize - (nDispPos % m_nTabSize);
for (int i = 0; i < nSpaces; i++)
{
if (i == 0)
{
pRowI->arTabs.AddData(0);
pRowI->arDispCol2StrPos.AddData(nStrPos);
}
else
{
pRowI->arTabs.AddData((BYTE)(nSpaces - i));
//pRowI->arDispCol2StrPos.AddData(nStrPos + 1);
//Corrected, only with the next real character we have the next nStrPos.
pRowI->arDispCol2StrPos.AddData(nStrPos);
}
}
nDispPos += nSpaces;
}
else
{
#ifdef XTP_FIXED
// multi-byte character incorrect.
for (int index = 0; index < ( isleadbyte(*p) != 0 ? 2 : 1 ); index ++)
{
pRowI->arTabs.AddData(0);
pRowI->arDispCol2StrPos.AddData(nStrPos);
nDispPos++;
}
#else
pRowI->arTabs.AddData(0);
pRowI->arDispCol2StrPos.AddData(nStrPos);
nDispPos++;
#endif
}
}
pRowI->arStrPos2DispCol.AddData(nDispPos);
pRowI->arDispCol2StrPos.AddData(nStrPos);
}
int CXTPSyntaxEditDrawTextProcessor::DispPosToStrPos(int nRow, int nDispPos, BOOL bVirtualSpace) const
{
CXTPRowInfo* pRowI = GetRowInfo(nRow);
ASSERT(pRowI);
if (!pRowI)
return 0;
ASSERT(nDispPos >= 0);
if (nDispPos < 0)
return 0;
int nCount = pRowI->arDispCol2StrPos.GetDataSize();
if (nDispPos >= nCount)
{
int nStrPos = nCount > 0 ? pRowI->arDispCol2StrPos[nCount - 1] : 0;
if (bVirtualSpace)
{
int nCount2 = pRowI->arStrPos2DispCol.GetDataSize();
ASSERT(nStrPos <= nCount2);
int nStrPos2 = min(nStrPos, nCount2 - 1);
int nLastDispPos = (nStrPos2 >= 0) ? (pRowI->arStrPos2DispCol[nStrPos2]) : nCount;
ASSERT(nDispPos >= nLastDispPos);
nStrPos += nDispPos - nLastDispPos /* + 1*/;
}
else
{
nStrPos++;
}
return nStrPos;
}
return pRowI->arDispCol2StrPos[nDispPos];
}
int CXTPSyntaxEditDrawTextProcessor::StrPosToDispPos(int nRow, int nStrPos, BOOL bVirtualSpace) const
{
CXTPRowInfo* pRowI = GetRowInfo(nRow);
ASSERT(pRowI);
if (!pRowI)
return 0;
ASSERT(nStrPos >= 0);
if (nStrPos < 0)
return 0;
int nCount = pRowI->arStrPos2DispCol.GetDataSize();
if (nStrPos >= nCount)
{
int nDispPos = nCount > 0 ? pRowI->arStrPos2DispCol[nCount - 1] + 1 : 0;
if (bVirtualSpace)
{
nDispPos += nStrPos - nCount + 1;
}
return nDispPos;
}
return pRowI->arStrPos2DispCol[nStrPos];
}
int CXTPSyntaxEditDrawTextProcessor::ExpandChars(LPCTSTR pszChars, CString& strBuffer,
int nDispPos, BOOL bEnableWhiteSpace)
{
m_arExpandCharsBuffer.RemoveAll();
for (LPCTSTR p = pszChars; p && *p != 0; p = _tcsinc(p))
{
if (*p == _T('\t'))
{
int nSpaces = m_nTabSize - (nDispPos % m_nTabSize);
BOOL bFirstWhiteSpaceChar = bEnableWhiteSpace;
for (int i = 0; i < nSpaces; i++)
{
if (bFirstWhiteSpaceChar)
{
//strBuffer += (TCHAR)(unsigned char)0xBB;
m_arExpandCharsBuffer.AddData((TCHAR)(unsigned char)0xBB);
bFirstWhiteSpaceChar = FALSE;
}
else
{
//strBuffer += _T(' ');
m_arExpandCharsBuffer.AddData(_T(' '));
}
}
nDispPos += nSpaces;
}
else
{
if (bEnableWhiteSpace && *p == _T(' '))
m_arExpandCharsBuffer.AddData((TCHAR)(unsigned char)0xB7); //strBuffer += (TCHAR)(unsigned char)0xB7;
else
{
m_arExpandCharsBuffer.AddData(*p); //strBuffer += *p;
#ifndef _UNICODE
if (_tcsinc(p) > p + 1)
{
ASSERT(_tcsinc(p) == p + 2);
m_arExpandCharsBuffer.AddData(*(p+1));
}
#endif
}
#ifdef XTP_FIXED
// multi-byte character : display length 2
// single-byte character : display length 1
if (isleadbyte( *p ))
nDispPos += 2;
else
nDispPos++;
#else
nDispPos++;
#endif
}
}
m_arExpandCharsBuffer.AddData(_T('\0'));
strBuffer = m_arExpandCharsBuffer.GetData();
return nDispPos;
}
int CXTPSyntaxEditDrawTextProcessor::DrawRowPart(CDC* pDC, int nRow, LPCTSTR pcszText, int nchCount)
{
if (m_nDrawingRow != nRow)
{
if (nRow >= 0) // the new row started
ResetRowInfo(nRow);
m_nDrawingRow = nRow;
m_nNextRowPosX = 0;
}
if (nRow == -1)
return 0;
//--------------------------------------------------------
int nTextLen = 0;
if (nchCount < 0)
{
nchCount = (int) _tcsclen(pcszText);
nTextLen = (int) _tcslen(pcszText);
}
else
{
#ifdef XTP_FIXED
// "nchCount" meen byte length.
// so, need not translate form TextLength to ByteLength.
nTextLen = nchCount;
#else
nTextLen = (int) _tcsnbcnt(pcszText, nchCount);
#endif
}
// ASSERT(nchCount <= (int) _tcsclen(pcszText));
//--------------------------------------------------------
int nY = m_rcTextRect.top + nRow * m_nRowHeight;
int nX = m_rcTextRect.left - m_nScrollXOffset + m_nNextRowPosX;
CRect rcText = m_rcTextRect;
rcText.left = max(rcText.left, nX); // ENSURE THE TEXT IS CLIPPED APPROPRIATELY
pDC->ExtTextOut(nX, nY, ETO_CLIPPED, &rcText, pcszText, nTextLen, NULL);
//--------------------------------------------------------
if (m_arBuf_aDx.GetSize() < nTextLen)
m_arBuf_aDx.SetSize(nTextLen + 100);
int nMaxExtent = m_rcTextRect.Width() + m_nScrollXOffset - m_nNextRowPosX + 30; // 30 is a gap to be sure
nMaxExtent = max(nMaxExtent, 30);
int nFit = 0;
LPINT alpDx = (int*)m_arBuf_aDx.GetData();
CSize szText(0, 0);
BOOL bResExt = GetTextExtentExPoint(pDC->m_hAttribDC, pcszText, nTextLen,
0, // nMaxExtent,
NULL, // &nFit,
alpDx, // array of partial string widths
&szText // string dimensions
);
nFit = nTextLen;
VERIFY(bResExt);
CXTPRowInfo* pRI = GetRowInfo(nRow);
if (pRI)
{
if (nFit >= 0 && nFit <= nTextLen)
{
int i;
LPCTSTR p = pcszText;
LPCTSTR p_prev = pcszText;
for (i = 0; i < nFit; i++)
{
pRI->arCharsEnds.AddData(m_nNextRowPosX + alpDx[i]);
p_prev = p;
p = _tcsinc(p);
int nCharLen = int(p - p_prev);
#ifdef XTP_FIXED
if (nCharLen > 1)
pRI->arCharsEnds.AddData(m_nNextRowPosX + alpDx[i]);
// ASSERT( nCharLen > 2 );
#endif
i += nCharLen - 1; // skip second byte for MBCS chars.
}
}
else
{
ASSERT(FALSE);
}
pRI->nMaxWidth += szText.cx;
}
m_nNextRowPosX += szText.cx;
return m_nNextRowPosX;
}
int CXTPSyntaxEditDrawTextProcessor::PrintRowPart(CDC* pDC, int nRow, int nPosY, UINT nFlags,
LPCTSTR pcszText, int nchCount, int *pnPrintedTextLen)
{
if (pnPrintedTextLen)
*pnPrintedTextLen = 0;
if (m_nPrintingRow != nRow)
{
m_nPrintingRow = nRow;
m_ptNextPrintPos = CPoint(0, 0);
}
if (nRow == -1)
return 0;
//--------------------------------------------------------
int nTextLen = 0;
if (nchCount < 0)
{
nchCount = (int)_tcsclen(pcszText);
nTextLen = (int)_tcslen(pcszText);
}
else
{
nTextLen = (int)_tcsnbcnt(pcszText, nchCount);
}
ASSERT(nchCount <= (int)_tcsclen(pcszText));
//--------------------------------------------------------
if (m_arBuf_aDx.GetSize() < nTextLen)
m_arBuf_aDx.SetSize(nTextLen + 100);
LPINT alpDx = (int*)m_arBuf_aDx.GetData();
//--------------------------------------------------------
CRect rcText = m_rcTextRect;
int nTextLen_rest = nTextLen;
LPCTSTR pTxt = NULL;
for (pTxt = pcszText; nTextLen_rest;)
{
int nY = m_rcTextRect.top + nPosY + m_ptNextPrintPos.y;
int nX = m_rcTextRect.left + m_ptNextPrintPos.x;
if (nY + GetRowHeight() > m_rcTextRect.bottom)
break;
//--------------------------------------------------------
int nMaxExtent = max(0, rcText.right - nX - 20);
int nFit = 0;
CSize szText(0, 0);
BOOL bResExt = GetTextExtentExPoint(
m_bUseOutputDC ? pDC->m_hDC : pDC->m_hAttribDC, // HDC hdc, // handle to DC
pTxt, // LPCTSTR lpString, // character string
nTextLen_rest, // int nCount, // number of characters
nMaxExtent, // maximum extent for string
&nFit, // LPINT lpnFit, // maximum number of characters
alpDx, // array of partial string widths
&szText //LPSIZE lpSize // string dimensions
);
VERIFY(bResExt);
//nFit = min(nFit, nTextLen_rest);
LPCTSTR pTextToDtaw = pTxt;
BOOL bAllTextDrawn = (nFit == nTextLen_rest);
if (nFit > 0 && nFit <= nTextLen_rest)
{
CString strSeps = _T(" .,!?)-+=;\\"); //_T(" .,!?)-+=*&^%$#@~`:;\\|/");
int nFit_wb = 0;
int nTextLen_wb = nTextLen_rest;
LPCTSTR pTxt_wb = pTxt;
LPCTSTR p_prev = NULL;
int i = 0;
for (i = 0; i < nFit; i++)
{
p_prev = pTxt;
pTxt = _tcsinc(pTxt);
int nCharLen = int(pTxt - p_prev);
i += nCharLen - 1; // skip second byte for MBCS chars.
nTextLen_rest--;
if ((nFlags & DT_WORDBREAK) && strSeps.Find(*p_prev, 0) >= 0)
{
nFit_wb = i+1;
nTextLen_wb = nTextLen_rest;
pTxt_wb = pTxt;
}
}
if (!bAllTextDrawn && (nFlags & DT_WORDBREAK))
{
if (nFit_wb >= 0)
{
nFit = nFit_wb;
nTextLen_rest = nTextLen_wb;
pTxt = pTxt_wb;
}
}
if (nFit)
{
m_ptNextPrintPos.x += alpDx[nFit - 1] + 1;
}
}
// Draw text (if need)
if (nFit && (nFlags & DT_CALCRECT) == 0)
{
rcText.left = max(rcText.left, nX); // ENSURE THE TEXT IS CLIPPED APPROPRIATELY
pDC->ExtTextOut(nX, nY, ETO_CLIPPED, &rcText, pTextToDtaw, nFit, NULL);
}
CXTPRowInfo* pRI = GetRowInfo(nRow);
if (pRI)
pRI->nMaxWidth = m_ptNextPrintPos.x;
// Move point to the next line (if need)
if (!bAllTextDrawn && (nFlags & DT_SINGLELINE) == 0)
{
int nIconX = m_rcTextRect.left + m_ptNextPrintPos.x + 3;
int nIconY = m_rcTextRect.top + nPosY + m_ptNextPrintPos.y;
int nDelta = GetRowHeight() - GetRowHeight()/4;
CPen pen1(PS_SOLID, 1, RGB(0, 0, 0));
CPen pen2(PS_SOLID, 2, RGB(0, 0, 0));
CPen* pPen_old = pDC->SelectObject(&pen1);
pDC->MoveTo(nIconX, nIconY + nDelta/2);
pDC->LineTo(nIconX, nIconY + nDelta);
pDC->LineTo(nIconX + nDelta/2 - 1, nIconY + nDelta);
pDC->SelectObject(&pen2);
pDC->MoveTo(nIconX + 1, nIconY + nDelta - 1);
pDC->LineTo(nIconX + nDelta/2, nIconY + nDelta/2);
pDC->SelectObject(pPen_old);
int nSublineOffsetX = m_tmText.tmAveCharWidth * 3;
m_ptNextPrintPos.y += GetRowHeight();
m_ptNextPrintPos.x = nSublineOffsetX;
if (pRI)
pRI->nMaxWidth = m_ptNextPrintPos.x;
}
else
{
break;
}
}
if (pnPrintedTextLen)
{
int nLen0 = int(pTxt - pcszText);
*pnPrintedTextLen = (int)_tcsnccnt(pcszText, nLen0);
}
return m_ptNextPrintPos.y + GetRowHeight();
}
int CXTPSyntaxEditDrawTextProcessor::GetColPosX(int nRow, int nCol, int* pnChawWidth,
BOOL bVirtualSpace) const
{
CXTPRowInfo* pRI = GetRowInfo(nRow);
if (!pRI)
return 0;
int nPosX = 0;
if (pnChawWidth)
*pnChawWidth = 0;
int nColsCount = pRI->arCharsEnds.GetDataSize();
if (nCol <= 0)
{
nPosX = 0;
}
else if (nCol < nColsCount)
{
nPosX = pRI->arCharsEnds[nCol - 1];
if (pnChawWidth)
*pnChawWidth = pRI->arCharsEnds[nCol] - pRI->arCharsEnds[nCol - 1];
}
else
{
if (nColsCount)
nPosX = pRI->arCharsEnds[nColsCount - 1];
if (bVirtualSpace)
{
ASSERT(m_nSpaceWidth);
nPosX += m_nSpaceWidth * (nCol - nColsCount);
}
if (pnChawWidth)
*pnChawWidth = m_nSpaceWidth;
}
nPosX = m_rcTextRect.left - m_nScrollXOffset + nPosX;
return nPosX;
}
BOOL CXTPSyntaxEditDrawTextProcessor::ColFromXPos(int nRow, int nX, int& rnCol, BOOL bVirtualSpace) const
{
rnCol = 0;
CXTPRowInfo* pRI = GetRowInfo(nRow);
ASSERT(pRI);
if (!pRI)
return FALSE;
int nX2 = nX - m_rcTextRect.left + m_nScrollXOffset;
if (nX2 < 0)
return FALSE;
int nCharsCount = pRI->arCharsEnds.GetDataSize();
int nPrevCharEnd = 0;
for (int i = 0; i < nCharsCount; i++)
{
int nCharEnd = pRI->arCharsEnds[i];
if (nX2 >= nPrevCharEnd && nX2 <= nCharEnd)
{
int nMiddle = nPrevCharEnd + (nCharEnd - nPrevCharEnd) / 2;
rnCol = i + ((nX2 < nMiddle) ? 0 : 1);
return TRUE;
}
nPrevCharEnd = nCharEnd;
}
rnCol = nCharsCount;
ASSERT(m_nSpaceWidth);
ASSERT(nX2 >= nPrevCharEnd);
int nFreeWidth = nX2 - nPrevCharEnd;
if (bVirtualSpace)
{
rnCol = nCharsCount + nFreeWidth / max(1, m_nSpaceWidth);
}
return FALSE;
}
BOOL CXTPSyntaxEditDrawTextProcessor::HitTest(const CPoint& pt, int& rnRow, int& rnCol, BOOL bVirtualSpace) const
{
if (!m_rcTextRect.PtInRect(pt))
return FALSE;
VERIFY(HitTestRow(pt.y, rnRow));
ColFromXPos(rnRow, pt.x, rnCol, bVirtualSpace);
return TRUE;
}
BOOL CXTPSyntaxEditDrawTextProcessor::HitTestRow(int nY, int& rnRow) const
{
if (nY < m_rcTextRect.top || nY >= m_rcTextRect.bottom)
return FALSE;
int nY2 = nY - m_rcTextRect.top;
rnRow = nY2 / max(1, m_nRowHeight);
return TRUE;
}
CPoint CXTPSyntaxEditDrawTextProcessor::SetCaretByPoint(CWnd* pWnd, const CPoint& pt, const CSize& szSize,
int& rnRow, int& rnCol, BOOL bVirtualSpace)
{
BOOL bHitRes = HitTest(pt, rnRow, rnCol, bVirtualSpace);
VERIFY(bHitRes);
return SetCaretPos(pWnd, szSize, rnRow, rnCol);
}
CPoint CXTPSyntaxEditDrawTextProcessor::SetCaretPos(CWnd* pWnd, const CSize& szSize,
int nRow, int& rnCol, BOOL bHideCaret, BOOL bVirtualSpace)
{
rnCol = AlignColIdxToTabs(nRow, rnCol, bVirtualSpace);
int nRowY = m_rcTextRect.top + nRow * m_nRowHeight;
int nCaretX = GetColPosX(nRow, rnCol, NULL, bVirtualSpace);
CPoint ptCaret(nCaretX, nRowY);
if (m_rcTextRect.PtInRect(ptCaret) && !bHideCaret)
{
pWnd->CreateSolidCaret(szSize.cx, szSize.cy);
pWnd->SetCaretPos(CPoint(nCaretX, nRowY));
pWnd->ShowCaret();
}
else
{
pWnd->HideCaret();
}
return ptCaret;
}
void CXTPSyntaxEditDrawTextProcessor::SetScrollXOffset(int nOffsetX)
{
m_nScrollXOffset = nOffsetX;
}
int CXTPSyntaxEditDrawTextProcessor::GetRowWidth(int nRow) const
{
CXTPRowInfo* pRI = GetRowInfo(nRow);
ASSERT(pRI);
if (pRI)
return pRI->nMaxWidth;
return 0;
}
int CXTPSyntaxEditDrawTextProcessor::GetRowsMaxWidth() const
{
int nMaxWidth = 0;
int nRowsCount = GetRowsCount(TRUE);
for (int i = 0; i < nRowsCount; i++)
{
CXTPRowInfo* pRI = GetRowInfo(i);
if (pRI && pRI->nMaxWidth > nMaxWidth)
{
nMaxWidth = pRI->nMaxWidth;
}
}
return nMaxWidth;
}