📄 markup.cpp
字号:
int nDocLength = m_strDoc.GetLength();
int nInsLength = strInsert.GetLength();
int nNewLength = nInsLength + nDocLength - nReplace;
// When creating a document, reduce reallocs by reserving string space
// Allow for 1.5 times the current allocation
int nBufferLen = nNewLength;
int nAllocLen = ((CStringData*)((LPCTSTR)m_strDoc)-1)->nAllocLength;
if ( nNewLength > nAllocLen )
{
nBufferLen += nBufferLen/2 + 128;
if ( nBufferLen < nNewLength )
nBufferLen = nNewLength;
}
_TCHAR* pDoc = m_strDoc.GetBuffer( nBufferLen );
// Move part of old doc that goes after insert, then copy insert into it
if ( nLeft+nReplace < nDocLength )
memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(_TCHAR) );
memcpy( &pDoc[nLeft], strInsert, nInsLength*sizeof(_TCHAR) );
m_strDoc.ReleaseBuffer( nNewLength );
}
void CMarkup::x_Adjust( int iPos, int nShift, bool bAfterPos /*=false*/ )
{
// Loop through affected elements and adjust indexes
// Algorithm:
// 1. update children unless bAfterPos
// (if no children or bAfterPos is true, length of iPos not affected)
// 2. update starts of next siblings and their children
// 3. go up until there is a next sibling of a parent and update starts
// 4. step 2
int iPosTop = m_aPos[iPos].iElemParent;
bool bPosFirst = bAfterPos; // mark as first to skip its children
// Stop when we've reached the virtual parent (which has no tags)
while ( m_aPos[iPos].StartTagLen() )
{
// Were we at containing parent of affected position?
bool bPosTop = false;
if ( iPos == iPosTop )
{
// Move iPosTop up one towards root
iPosTop = m_aPos[iPos].iElemParent;
bPosTop = true;
}
// Traverse to the next update position
if ( ! bPosTop && ! bPosFirst && m_aPos[iPos].iElemChild )
{
// Depth first
iPos = m_aPos[iPos].iElemChild;
}
else if ( m_aPos[iPos].iElemNext )
{
iPos = m_aPos[iPos].iElemNext;
}
else
{
// Look for next sibling of a parent of iPos
// When going back up, parents have already been done except iPosTop
while ( 1 )
{
iPos = m_aPos[iPos].iElemParent;
if ( iPos == iPosTop )
break;
if ( m_aPos[iPos].iElemNext )
{
iPos = m_aPos[iPos].iElemNext;
break;
}
}
}
bPosFirst = false;
// Shift indexes at iPos
if ( iPos != iPosTop )
m_aPos[iPos].nStart += nShift;
else
m_aPos[iPos].nLength += nShift;
}
}
int CMarkup::x_InsertNew( int iPosParent, int& iPosRel, CMarkup::NodePos& node )
{
// Parent empty tag or tags with no content?
bool bEmptyParentTag = iPosParent && m_aPos[iPosParent].IsEmptyElement();
bool bNoContentParentTags = iPosParent && ! m_aPos[iPosParent].ContentLen();
if ( node.nLength )
{
// Located at a non-element node
if ( ! (node.nFlags & MNF_INSERT) )
node.nStart += node.nLength;
}
else if ( iPosRel )
{
// Located at an element
node.nStart = m_aPos[iPosRel].nStart;
if ( ! (node.nFlags & MNF_INSERT) ) // follow iPosRel
node.nStart += m_aPos[iPosRel].nLength;
}
else if ( bEmptyParentTag )
{
// Parent has no separate end tag, so split empty element
if ( m_aPos[iPosParent].nFlags & MNF_NONENDED )
node.nStart = m_aPos[iPosParent].StartContent();
else
node.nStart = m_aPos[iPosParent].StartContent() - 1;
}
else
{
if ( node.nFlags & (MNF_INSERT|MNF_REPLACE) )
node.nStart = m_aPos[iPosParent].StartContent();
else // before end tag
node.nStart = m_aPos[iPosParent].StartAfter() - m_aPos[iPosParent].EndTagLen();
}
// Go up to start of next node, unless its splitting an empty element
if ( ! (node.nFlags&(MNF_WITHNOLINES|MNF_REPLACE)) && ! bEmptyParentTag )
{
LPCTSTR szDoc = (LPCTSTR)m_strDoc;
int nChar = node.nStart;
if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
node.nStart = nChar;
}
// Is insert relative to element position? (i.e. not other kind of node)
if ( ! node.nLength )
{
// Modify iPosRel to reflect position before
if ( iPosRel )
{
if ( node.nFlags & MNF_INSERT )
{
if ( ! (m_aPos[iPosRel].nFlags & MNF_FIRST) )
iPosRel = m_aPos[iPosRel].iElemPrev;
else
iPosRel = 0;
}
}
else if ( ! (node.nFlags & MNF_INSERT) )
{
// If parent has a child, add after last child
if ( m_aPos[iPosParent].iElemChild )
iPosRel = m_aPos[m_aPos[iPosParent].iElemChild].iElemPrev;
}
}
// Get node length (used only by x_AddNode)
node.nLength = node.strMeta.GetLength();
// Prepare end of lines
if ( (! (node.nFlags & MNF_WITHNOLINES)) && (bEmptyParentTag || bNoContentParentTags) )
node.nStart += x_EOLLEN;
if ( ! (node.nFlags & MNF_WITHNOLINES) )
node.strMeta += x_EOL;
// Calculate insert offset and replace length
int nReplace = 0;
int nInsertAt = node.nStart;
if ( bEmptyParentTag )
{
CString strTagName = x_GetTagName( iPosParent );
CString strFormat;
if ( node.nFlags & MNF_WITHNOLINES )
strFormat = _T(">");
else
strFormat = _T(">") x_EOL;
strFormat += node.strMeta;
strFormat += _T("</");
strFormat += strTagName;
node.strMeta = strFormat;
if ( m_aPos[iPosParent].nFlags & MNF_NONENDED )
{
nInsertAt = m_aPos[iPosParent].StartAfter() - 1;
nReplace = 0;
m_aPos[iPosParent].nFlags ^= MNF_NONENDED;
}
else
{
nInsertAt = m_aPos[iPosParent].StartAfter() - 2;
nReplace = 1;
m_aPos[iPosParent].AdjustStartTagLen( -1 );
}
m_aPos[iPosParent].SetEndTagLen( 3 + strTagName.GetLength() );
}
else
{
if ( node.nFlags & MNF_REPLACE )
{
nInsertAt = m_aPos[iPosParent].StartContent();
nReplace = m_aPos[iPosParent].ContentLen();
}
else if ( bNoContentParentTags )
{
node.strMeta = x_EOL + node.strMeta;
nInsertAt = m_aPos[iPosParent].StartContent();
}
}
x_DocChange( nInsertAt, nReplace, node.strMeta );
return nReplace;
}
bool CMarkup::x_AddElem( LPCTSTR szName, int nValue, int nFlags )
{
// Convert integer to string
_TCHAR szVal[25];
_stprintf( szVal, _T("%d"), nValue );
return x_AddElem( szName, szVal, nFlags );
}
bool CMarkup::x_AddElem( LPCTSTR szName, LPCTSTR szValue, int nFlags )
{
if ( nFlags & MNF_CHILD )
{
// Adding a child element under main position
if ( ! m_iPos )
return false;
}
// Locate where to add element relative to current node
NodePos node( nFlags );
int iPosParent, iPosBefore;
if ( nFlags & MNF_CHILD )
{
iPosParent = m_iPos;
iPosBefore = m_iPosChild;
}
else
{
iPosParent = m_iPosParent;
iPosBefore = m_iPos;
node.nStart = m_nNodeOffset;
node.nLength = m_nNodeLength;
}
// Cannot have data in non-ended element
if ( (nFlags&MNF_WITHNOEND) && szValue && szValue[0] )
return false;
// Allocate ElemPos structure for this element
int iPos = x_GetFreePos();
// Create string for insert
// If no szValue is specified, an empty element is created
// i.e. either <NAME>value</NAME> or <NAME/>
//
ElemPos* pElem = &m_aPos[iPos];
int nLenName = (int)_tcslen(szName);
if ( ! szValue || ! szValue[0] )
{
// <NAME/> empty element
node.strMeta = _T("<");
node.strMeta += szName;
if ( nFlags & MNF_WITHNOEND )
{
node.strMeta += _T(">");
pElem->SetStartTagLen( nLenName + 2 );
pElem->nLength = nLenName + 2;
}
else
{
if ( nFlags & MNF_WITHXHTMLSPACE )
{
node.strMeta += _T(" />");
pElem->SetStartTagLen( nLenName + 4 );
pElem->nLength = nLenName + 4;
}
else
{
node.strMeta += _T("/>");
pElem->SetStartTagLen( nLenName + 3 );
pElem->nLength = nLenName + 3;
}
}
pElem->SetEndTagLen( 0 );
}
else
{
// <NAME>value</NAME>
CString strValue;
if ( nFlags & MNF_WITHCDATA )
strValue = x_EncodeCDATASection( szValue );
else
strValue = EscapeText( szValue, nFlags );
int nLenValue = strValue.GetLength();
node.strMeta = _T("<");
node.strMeta += szName;
node.strMeta += _T(">");
node.strMeta += strValue;
node.strMeta += _T("</");
node.strMeta += szName;
node.strMeta += _T(">");
pElem->SetEndTagLen( nLenName + 3 );
pElem->nLength = nLenName * 2 + nLenValue + 5;
pElem->SetStartTagLen( nLenName + 2 );
}
// Insert
int nReplace = x_InsertNew( iPosParent, iPosBefore, node );
pElem->nStart = node.nStart;
pElem->iElemChild = 0;
if ( nFlags & MNF_WITHNOEND )
pElem->nFlags = MNF_NONENDED;
else
pElem->nFlags = 0;
x_LinkElem( iPosParent, iPosBefore, iPos );
x_Adjust( iPos, node.strMeta.GetLength() - nReplace );
if ( nFlags & MNF_CHILD )
x_SetPos( m_iPosParent, iPosParent, iPos );
else
x_SetPos( iPosParent, iPos, 0 );
return true;
}
CString CMarkup::x_GetSubDoc( int iPos ) const
{
if ( iPos )
{
int nStart = m_aPos[iPos].nStart;
int nNext = nStart + m_aPos[iPos].nLength;
LPCTSTR szDoc = (LPCTSTR)m_strDoc;
int nChar = nNext;
if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
nNext = nChar;
return m_strDoc.Mid( nStart, nNext - nStart );
}
return _T("");
}
bool CMarkup::x_AddSubDoc( LPCTSTR szSubDoc, int nFlags )
{
// Add subdocument, parse, and modify positions of affected elements
//
NodePos node( nFlags );
int iPosParent, iPosBefore;
if ( nFlags & MNF_CHILD )
{
// Add a subdocument under main position, before or after child
if ( ! m_iPos )
return false;
iPosParent = m_iPos;
iPosBefore = m_iPosChild;
}
else
{
// Add a subdocument under parent position, before or after main
iPosParent = m_iPosParent;
iPosBefore = m_iPos;
node.nStart = m_nNodeOffset;
node.nLength = m_nNodeLength;
}
// Parse subdocument
bool bWellFormed = true;
TokenPos token( szSubDoc, m_nFlags );
int iPosVirtual = x_GetFreePos();
m_aPos[iPosVirtual].ClearVirtualParent();
m_aPos[iPosVirtual].SetLevel( m_aPos[iPosParent].Level() + 1 );
int iPos = x_ParseElem( iPosVirtual, token );
if ( (!iPos) || m_aPos[iPosVirtual].nFlags & MNF_ILLFORMED )
bWellFormed = false;
if ( m_aPos[iPosVirtual].nFlags & MNF_ILLDATA )
m_aPos[iPosParent].nFlags |= MNF_ILLDATA;
// Extract subdocument without leading/trailing nodes
int nExtractStart = 0;
int iPosLast = m_aPos[iPos].iElemPrev;
if ( bWellFormed )
{
nExtractStart = m_aPos[iPos].nStart;
int nExtractLength = m_aPos[iPos].nLength;
if ( iPos != iPosLast )
{
nExtractLength = m_aPos[iPosLast].nStart - nExtractStart + m_aPos[iPosLast].nLength;
bWellFormed = false; // treat as subdoc here, but return not well-formed
}
memcpy( node.strMeta.GetBuffer(nExtractLength+x_EOLLEN), &szSubDoc[nExtractStart], nExtractLength*sizeof(_TCHAR) );
node.strMeta.ReleaseBuffer( nExtractLength );
}
else
{
node.strMeta = szSubDoc;
node.nFlags |= MNF_WITHNOLINES;
}
// Insert
int nReplace = x_InsertNew( iPosParent, iPosBefore, node );
// Adjust and link in the inserted elements
// iPosVirtual will stop it from affecting rest of document
int nAdjust = node.nStart - nExtractStart;
if ( iPos && nAdjust )
{
x_Adjust( iPos, nAdjust );
m_aPos[iPos].nStart += nAdjust;
}
int iPosChild = iPos;
while ( iPosChild )
{
int iPosNext = m_aPos[iPosChild].iElemNext;
x_LinkElem( iPosParent, iPosBefore, iPosChild );
iPosBefore = iPosChild;
iPosChild = iPosNext;
}
x_ReleasePos( iPosVirtual );
// Now adjust remainder of document
x_Adjust( iPosLast, node.strMeta.GetLength() - nReplace, true );
// Set position to top element of subdocument
if ( nFlags & MNF_CHILD )
x_SetPos( m_iPosParent, iPosParent, iPos );
else // Main
x_SetPos( m_iPosParent, iPos, 0 );
return bWellFormed;
}
int CMarkup::x_RemoveElem( int iPos )
{
// Remove element and all contained elements
// Return new position
//
if ( ! iPos )
return 0;
// Determine whether any whitespace up to next tag
int nAfterEnd = m_aPos[iPos].StartAfter();
LPCTSTR szDoc = (LPCTSTR)m_strDoc;
int nChar = nAfterEnd;
if ( ! x_FindAny(szDoc,nChar) || szDoc[nChar] == _T('<') )
nAfterEnd = nChar;
// Remove from document, adjust affected indexes, and unlink
int nLen = nAfterEnd - m_aPos[iPos].nStart;
x_DocChange( m_aPos[iPos].nStart, nLen, CString() );
x_Adjust( iPos, - nLen, true );
int iPosPrev = x_UnlinkElem( iPos );
x_CheckSavedPos();
return iPosPrev;
}
void CMarkup::x_LinkElem( int iPosParent, int iPosBefore, int iPos )
{
// Link in element, and initialize nFlags, and iElem indexes
ElemPos* pElem = &m_aPos[iPos];
pElem->iElemParent = iPosParent;
if ( iPosBefore )
{
// Link in after iPosBefore
pElem->nFlags &= ~MNF_FIRST;
pElem->iElemNext = m_aPos[iPosBefore].iElemNext;
if ( pElem->iElemNext )
m_aPos[pElem->iElemNext].iElemPrev = iPos;
else
m_aPos[m_aPos[iPosParent].iElemChild].iElemPrev = iPos;
m_aPos[iPosBefore].iElemNext = iPos;
pElem->iElemPrev = iPosBefore;
}
else
{
// Link in as first child
pElem->nFlags |= MNF_FIRST;
if ( m_aPos[iPosParent].iElemChild )
{
pElem->iElem
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -