// 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; }