645 lines
20 KiB
C++
645 lines
20 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
#include <textdoc.hxx>
|
|
|
|
#include <stdlib.h>
|
|
|
|
SV_IMPL_PTRARR( TextCharAttribs, TextCharAttribPtr );
|
|
|
|
|
|
|
|
// Vergleichmethode wird von QuickSort gerufen...
|
|
|
|
extern "C" {
|
|
int SAL_CALL CompareStart( const void* pFirst, const void* pSecond )
|
|
{
|
|
if ( (*((TextCharAttrib**)pFirst))->GetStart() < (*((TextCharAttrib**)pSecond))->GetStart() )
|
|
return (-1);
|
|
else if ( (*((TextCharAttrib**)pFirst))->GetStart() > (*((TextCharAttrib**)pSecond))->GetStart() )
|
|
return (1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// (+) class TextCharAttrib
|
|
// -------------------------------------------------------------------------
|
|
TextCharAttrib::TextCharAttrib( const TextAttrib& rAttr, sal_uInt16 nStart, sal_uInt16 nEnd )
|
|
{
|
|
mpAttr = rAttr.Clone();
|
|
mnStart = nStart,
|
|
mnEnd = nEnd;
|
|
}
|
|
|
|
TextCharAttrib::TextCharAttrib( const TextCharAttrib& rTextCharAttrib )
|
|
{
|
|
mpAttr = rTextCharAttrib.GetAttr().Clone();
|
|
mnStart = rTextCharAttrib.mnStart;
|
|
mnEnd = rTextCharAttrib.mnEnd;
|
|
}
|
|
|
|
TextCharAttrib::~TextCharAttrib()
|
|
{
|
|
delete mpAttr;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// (+) class TextCharAttribList
|
|
// -------------------------------------------------------------------------
|
|
|
|
TextCharAttribList::TextCharAttribList()
|
|
{
|
|
mbHasEmptyAttribs = sal_False;
|
|
}
|
|
|
|
TextCharAttribList::~TextCharAttribList()
|
|
{
|
|
// PTRARR_DEL
|
|
}
|
|
|
|
void TextCharAttribList::Clear( sal_Bool bDestroyAttribs )
|
|
{
|
|
if ( bDestroyAttribs )
|
|
TextCharAttribs::DeleteAndDestroy( 0, Count() );
|
|
else
|
|
TextCharAttribs::Remove( 0, Count() );
|
|
}
|
|
|
|
|
|
void TextCharAttribList::InsertAttrib( TextCharAttrib* pAttrib )
|
|
{
|
|
if ( pAttrib->IsEmpty() )
|
|
mbHasEmptyAttribs = sal_True;
|
|
|
|
const sal_uInt16 nCount = Count();
|
|
const sal_uInt16 nStart = pAttrib->GetStart(); // vielleicht besser fuer Comp.Opt.
|
|
sal_Bool bInserted = sal_False;
|
|
for ( sal_uInt16 x = 0; x < nCount; x++ )
|
|
{
|
|
TextCharAttrib* pCurAttrib = GetObject( x );
|
|
if ( pCurAttrib->GetStart() > nStart )
|
|
{
|
|
Insert( pAttrib, x );
|
|
bInserted = sal_True;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bInserted )
|
|
Insert( pAttrib, nCount );
|
|
}
|
|
|
|
void TextCharAttribList::ResortAttribs()
|
|
{
|
|
if ( Count() )
|
|
qsort( (void*)GetData(), Count(), sizeof( TextCharAttrib* ), CompareStart );
|
|
}
|
|
|
|
TextCharAttrib* TextCharAttribList::FindAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
|
|
{
|
|
// Rueckwaerts, falls eins dort endet, das naechste startet.
|
|
// => Das startende gilt...
|
|
|
|
for ( sal_uInt16 nAttr = Count(); nAttr; )
|
|
{
|
|
TextCharAttrib* pAttr = GetObject( --nAttr );
|
|
|
|
if ( pAttr->GetEnd() < nPos )
|
|
return 0;
|
|
|
|
if ( ( pAttr->Which() == nWhich ) && pAttr->IsIn(nPos) )
|
|
return pAttr;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
TextCharAttrib* TextCharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_uInt16 nFromPos, sal_uInt16 nMaxPos ) const
|
|
{
|
|
DBG_ASSERT( nWhich, "FindNextAttrib: Which?" );
|
|
const sal_uInt16 nAttribs = Count();
|
|
for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
|
|
{
|
|
TextCharAttrib* pAttr = GetObject( nAttr );
|
|
if ( ( pAttr->GetStart() >= nFromPos ) &&
|
|
( pAttr->GetEnd() <= nMaxPos ) &&
|
|
( pAttr->Which() == nWhich ) )
|
|
return pAttr;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
sal_Bool TextCharAttribList::HasAttrib( sal_uInt16 nWhich ) const
|
|
{
|
|
for ( sal_uInt16 nAttr = Count(); nAttr; )
|
|
{
|
|
const TextCharAttrib* pAttr = GetObject( --nAttr );
|
|
if ( pAttr->Which() == nWhich )
|
|
return sal_True;
|
|
}
|
|
return sal_False;
|
|
}
|
|
|
|
sal_Bool TextCharAttribList::HasBoundingAttrib( sal_uInt16 nBound )
|
|
{
|
|
// Rueckwaerts, falls eins dort endet, das naechste startet.
|
|
// => Das startende gilt...
|
|
for ( sal_uInt16 nAttr = Count(); nAttr; )
|
|
{
|
|
TextCharAttrib* pAttr = GetObject( --nAttr );
|
|
|
|
if ( pAttr->GetEnd() < nBound )
|
|
return sal_False;
|
|
|
|
if ( ( pAttr->GetStart() == nBound ) || ( pAttr->GetEnd() == nBound ) )
|
|
return sal_True;
|
|
}
|
|
return sal_False;
|
|
}
|
|
|
|
TextCharAttrib* TextCharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
|
|
{
|
|
if ( !mbHasEmptyAttribs )
|
|
return 0;
|
|
|
|
const sal_uInt16 nAttribs = Count();
|
|
for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
|
|
{
|
|
TextCharAttrib* pAttr = GetObject( nAttr );
|
|
if ( pAttr->GetStart() > nPos )
|
|
return 0;
|
|
|
|
if ( ( pAttr->GetStart() == nPos ) && ( pAttr->GetEnd() == nPos ) && ( pAttr->Which() == nWhich ) )
|
|
return pAttr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void TextCharAttribList::DeleteEmptyAttribs()
|
|
{
|
|
for ( sal_uInt16 nAttr = 0; nAttr < Count(); nAttr++ )
|
|
{
|
|
TextCharAttrib* pAttr = GetObject( nAttr );
|
|
if ( pAttr->IsEmpty() )
|
|
{
|
|
Remove( nAttr );
|
|
delete pAttr;
|
|
nAttr--;
|
|
}
|
|
}
|
|
mbHasEmptyAttribs = sal_False;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// (+) class TextNode
|
|
// -------------------------------------------------------------------------
|
|
|
|
TextNode::TextNode( const String& rText ) :
|
|
maText( rText )
|
|
{
|
|
}
|
|
|
|
void TextNode::ExpandAttribs( sal_uInt16 nIndex, sal_uInt16 nNew )
|
|
{
|
|
if ( !nNew )
|
|
return;
|
|
|
|
sal_Bool bResort = sal_False;
|
|
sal_uInt16 nAttribs = maCharAttribs.Count();
|
|
for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
|
|
{
|
|
TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
|
|
if ( pAttrib->GetEnd() >= nIndex )
|
|
{
|
|
// Alle Attribute hinter der Einfuegeposition verschieben...
|
|
if ( pAttrib->GetStart() > nIndex )
|
|
{
|
|
pAttrib->MoveForward( nNew );
|
|
}
|
|
// 0: Leeres Attribut expandieren, wenn an Einfuegestelle
|
|
else if ( pAttrib->IsEmpty() )
|
|
{
|
|
// Index nicht pruefen, leeres durfte nur dort liegen.
|
|
// Wenn spaeter doch Ueberpruefung:
|
|
// Spezialfall: Start == 0; AbsLen == 1, nNew = 1 => Expand, weil durch Absatzumbruch!
|
|
// Start <= nIndex, End >= nIndex => Start=End=nIndex!
|
|
// if ( pAttrib->GetStart() == nIndex )
|
|
pAttrib->Expand( nNew );
|
|
}
|
|
// 1: Attribut startet davor, geht bis Index...
|
|
else if ( pAttrib->GetEnd() == nIndex ) // Start muss davor liegen
|
|
{
|
|
// Nur expandieren, wenn kein Feature,
|
|
// und wenn nicht in ExcludeListe!
|
|
// Sonst geht z.B. ein UL bis zum neuen ULDB, beide expandieren
|
|
if ( !maCharAttribs.FindEmptyAttrib( pAttrib->Which(), nIndex ) )
|
|
{
|
|
pAttrib->Expand( nNew );
|
|
}
|
|
else
|
|
bResort = sal_True;
|
|
}
|
|
// 2: Attribut startet davor, geht hinter Index...
|
|
else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
|
|
{
|
|
pAttrib->Expand( nNew );
|
|
}
|
|
// 3: Attribut startet auf Index...
|
|
else if ( pAttrib->GetStart() == nIndex )
|
|
{
|
|
if ( nIndex == 0 )
|
|
{
|
|
pAttrib->Expand( nNew );
|
|
// bResort = sal_True; // es gibt ja keine Features mehr...
|
|
}
|
|
else
|
|
pAttrib->MoveForward( nNew );
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribut verdreht!" );
|
|
DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len() ), "Expand: Attrib groesser als Absatz!" );
|
|
DBG_ASSERT( !pAttrib->IsEmpty(), "Leeres Attribut nach ExpandAttribs?" );
|
|
}
|
|
|
|
if ( bResort )
|
|
maCharAttribs.ResortAttribs();
|
|
}
|
|
|
|
void TextNode::CollapsAttribs( sal_uInt16 nIndex, sal_uInt16 nDeleted )
|
|
{
|
|
if ( !nDeleted )
|
|
return;
|
|
|
|
sal_Bool bResort = sal_False;
|
|
sal_uInt16 nEndChanges = nIndex+nDeleted;
|
|
|
|
for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
|
|
{
|
|
TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
|
|
sal_Bool bDelAttr = sal_False;
|
|
if ( pAttrib->GetEnd() >= nIndex )
|
|
{
|
|
// Alles Attribute hinter der Einfuegeposition verschieben...
|
|
if ( pAttrib->GetStart() >= nEndChanges )
|
|
{
|
|
pAttrib->MoveBackward( nDeleted );
|
|
}
|
|
// 1. Innenliegende Attribute loeschen...
|
|
else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
|
|
{
|
|
// Spezialfall: Attrubt deckt genau den Bereich ab
|
|
// => als leeres Attribut behalten.
|
|
if ( ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
|
|
pAttrib->GetEnd() = nIndex; // leer
|
|
else
|
|
bDelAttr = sal_True;
|
|
}
|
|
// 2. Attribut beginnt davor, endet drinnen oder dahinter...
|
|
else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
|
|
{
|
|
if ( pAttrib->GetEnd() <= nEndChanges ) // endet drinnen
|
|
pAttrib->GetEnd() = nIndex;
|
|
else
|
|
pAttrib->Collaps( nDeleted ); // endet dahinter
|
|
}
|
|
// 3. Attribut beginnt drinnen, endet dahinter...
|
|
else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
|
|
{
|
|
// Features duerfen nicht expandieren!
|
|
pAttrib->GetStart() = nEndChanges;
|
|
pAttrib->MoveBackward( nDeleted );
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collaps: Attribut verdreht!" );
|
|
DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len()) || bDelAttr, "Collaps: Attrib groesser als Absatz!" );
|
|
if ( bDelAttr /* || pAttrib->IsEmpty() */ )
|
|
{
|
|
bResort = sal_True;
|
|
maCharAttribs.RemoveAttrib( nAttr );
|
|
delete pAttrib;
|
|
nAttr--;
|
|
}
|
|
else if ( pAttrib->IsEmpty() )
|
|
maCharAttribs.HasEmptyAttribs() = sal_True;
|
|
}
|
|
|
|
if ( bResort )
|
|
maCharAttribs.ResortAttribs();
|
|
}
|
|
|
|
void TextNode::InsertText( sal_uInt16 nPos, const String& rText )
|
|
{
|
|
maText.Insert( rText, nPos );
|
|
ExpandAttribs( nPos, rText.Len() );
|
|
}
|
|
|
|
void TextNode::InsertText( sal_uInt16 nPos, sal_Unicode c )
|
|
{
|
|
maText.Insert( c, nPos );
|
|
ExpandAttribs( nPos, 1 );
|
|
}
|
|
|
|
void TextNode::RemoveText( sal_uInt16 nPos, sal_uInt16 nChars )
|
|
{
|
|
maText.Erase( nPos, nChars );
|
|
CollapsAttribs( nPos, nChars );
|
|
}
|
|
|
|
TextNode* TextNode::Split( sal_uInt16 nPos, sal_Bool bKeepEndingAttribs )
|
|
{
|
|
String aNewText;
|
|
if ( nPos < maText.Len() )
|
|
{
|
|
aNewText = maText.Copy( nPos );
|
|
maText.Erase( nPos );
|
|
}
|
|
TextNode* pNew = new TextNode( aNewText );
|
|
|
|
for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
|
|
{
|
|
TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
|
|
if ( pAttrib->GetEnd() < nPos )
|
|
{
|
|
// bleiben unveraendert....
|
|
;
|
|
}
|
|
else if ( pAttrib->GetEnd() == nPos )
|
|
{
|
|
// muessen als leeres Attribut kopiert werden.
|
|
// !FindAttrib nur sinnvoll, wenn Rueckwaerts durch Liste!
|
|
if ( bKeepEndingAttribs && !pNew->maCharAttribs.FindAttrib( pAttrib->Which(), 0 ) )
|
|
{
|
|
TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
|
|
pNewAttrib->GetStart() = 0;
|
|
pNewAttrib->GetEnd() = 0;
|
|
pNew->maCharAttribs.InsertAttrib( pNewAttrib );
|
|
}
|
|
}
|
|
else if ( pAttrib->IsInside( nPos ) || ( !nPos && !pAttrib->GetStart() ) )
|
|
{
|
|
// Wenn ganz vorne gecuttet wird, muss das Attribut erhalten bleiben!
|
|
// muessen kopiert und geaendert werden
|
|
TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
|
|
pNewAttrib->GetStart() = 0;
|
|
pNewAttrib->GetEnd() = pAttrib->GetEnd()-nPos;
|
|
pNew->maCharAttribs.InsertAttrib( pNewAttrib );
|
|
// stutzen:
|
|
pAttrib->GetEnd() = nPos;
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( pAttrib->GetStart() >= nPos, "Start < nPos!" );
|
|
DBG_ASSERT( pAttrib->GetEnd() >= nPos, "End < nPos!" );
|
|
// alle dahinter verschieben in den neuen Node (this)
|
|
maCharAttribs.RemoveAttrib( nAttr );
|
|
pNew->maCharAttribs.InsertAttrib( pAttrib );
|
|
pAttrib->GetStart() = pAttrib->GetStart() - nPos;
|
|
pAttrib->GetEnd() = pAttrib->GetEnd() - nPos;
|
|
nAttr--;
|
|
}
|
|
}
|
|
return pNew;
|
|
}
|
|
|
|
void TextNode::Append( const TextNode& rNode )
|
|
{
|
|
sal_uInt16 nOldLen = maText.Len();
|
|
|
|
maText += rNode.GetText();
|
|
|
|
const sal_uInt16 nAttribs = rNode.GetCharAttribs().Count();
|
|
for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
|
|
{
|
|
TextCharAttrib* pAttrib = rNode.GetCharAttribs().GetAttrib( nAttr );
|
|
sal_Bool bMelted = sal_False;
|
|
if ( pAttrib->GetStart() == 0 )
|
|
{
|
|
// Evtl koennen Attribute zusammengefasst werden:
|
|
sal_uInt16 nTmpAttribs = maCharAttribs.Count();
|
|
for ( sal_uInt16 nTmpAttr = 0; nTmpAttr < nTmpAttribs; nTmpAttr++ )
|
|
{
|
|
TextCharAttrib* pTmpAttrib = maCharAttribs.GetAttrib( nTmpAttr );
|
|
|
|
if ( pTmpAttrib->GetEnd() == nOldLen )
|
|
{
|
|
if ( ( pTmpAttrib->Which() == pAttrib->Which() ) &&
|
|
( pTmpAttrib->GetAttr() == pAttrib->GetAttr() ) )
|
|
{
|
|
pTmpAttrib->GetEnd() =
|
|
pTmpAttrib->GetEnd() + pAttrib->GetLen();
|
|
bMelted = sal_True;
|
|
break; // es kann nur eins von der Sorte an der Stelle geben
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bMelted )
|
|
{
|
|
TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
|
|
pNewAttrib->GetStart() = pNewAttrib->GetStart() + nOldLen;
|
|
pNewAttrib->GetEnd() = pNewAttrib->GetEnd() + nOldLen;
|
|
maCharAttribs.InsertAttrib( pNewAttrib );
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// (+) class TextDoc
|
|
// -------------------------------------------------------------------------
|
|
|
|
TextDoc::TextDoc()
|
|
{
|
|
mnLeftMargin = 0;
|
|
};
|
|
|
|
TextDoc::~TextDoc()
|
|
{
|
|
DestroyTextNodes();
|
|
}
|
|
|
|
void TextDoc::Clear()
|
|
{
|
|
DestroyTextNodes();
|
|
}
|
|
|
|
void TextDoc::DestroyTextNodes()
|
|
{
|
|
for ( sal_uLong nNode = 0; nNode < maTextNodes.Count(); nNode++ )
|
|
delete maTextNodes.GetObject( nNode );
|
|
maTextNodes.clear();
|
|
}
|
|
|
|
String TextDoc::GetText( const sal_Unicode* pSep ) const
|
|
{
|
|
sal_uLong nLen = GetTextLen( pSep );
|
|
sal_uLong nNodes = maTextNodes.Count();
|
|
|
|
if ( nLen > STRING_MAXLEN )
|
|
{
|
|
OSL_FAIL( "Text zu gross fuer String" );
|
|
return String();
|
|
}
|
|
|
|
String aASCIIText;
|
|
sal_uLong nLastNode = nNodes-1;
|
|
for ( sal_uLong nNode = 0; nNode < nNodes; nNode++ )
|
|
{
|
|
TextNode* pNode = maTextNodes.GetObject( nNode );
|
|
String aTmp( pNode->GetText() );
|
|
aASCIIText += aTmp;
|
|
if ( pSep && ( nNode != nLastNode ) )
|
|
aASCIIText += pSep;
|
|
}
|
|
|
|
return aASCIIText;
|
|
}
|
|
|
|
XubString TextDoc::GetText( sal_uLong nPara ) const
|
|
{
|
|
XubString aText;
|
|
TextNode* pNode = ( nPara < maTextNodes.Count() ) ? maTextNodes.GetObject( nPara ) : 0;
|
|
if ( pNode )
|
|
aText = pNode->GetText();
|
|
|
|
return aText;
|
|
}
|
|
|
|
|
|
sal_uLong TextDoc::GetTextLen( const xub_Unicode* pSep, const TextSelection* pSel ) const
|
|
{
|
|
sal_uLong nLen = 0;
|
|
sal_uLong nNodes = maTextNodes.Count();
|
|
if ( nNodes )
|
|
{
|
|
sal_uLong nStartNode = 0;
|
|
sal_uLong nEndNode = nNodes-1;
|
|
if ( pSel )
|
|
{
|
|
nStartNode = pSel->GetStart().GetPara();
|
|
nEndNode = pSel->GetEnd().GetPara();
|
|
}
|
|
|
|
for ( sal_uLong nNode = nStartNode; nNode <= nEndNode; nNode++ )
|
|
{
|
|
TextNode* pNode = maTextNodes.GetObject( nNode );
|
|
|
|
sal_uInt16 nS = 0;
|
|
sal_uLong nE = pNode->GetText().Len();
|
|
if ( pSel && ( nNode == pSel->GetStart().GetPara() ) )
|
|
nS = pSel->GetStart().GetIndex();
|
|
if ( pSel && ( nNode == pSel->GetEnd().GetPara() ) )
|
|
nE = pSel->GetEnd().GetIndex();
|
|
|
|
nLen += ( nE - nS );
|
|
}
|
|
|
|
if ( pSep )
|
|
nLen += (nEndNode-nStartNode) * String( pSep ).Len();
|
|
}
|
|
|
|
return nLen;
|
|
}
|
|
|
|
TextPaM TextDoc::InsertText( const TextPaM& rPaM, xub_Unicode c )
|
|
{
|
|
DBG_ASSERT( c != 0x0A, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
|
|
DBG_ASSERT( c != 0x0D, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
|
|
|
|
TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
|
|
pNode->InsertText( rPaM.GetIndex(), c );
|
|
|
|
TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+1 );
|
|
return aPaM;
|
|
}
|
|
|
|
TextPaM TextDoc::InsertText( const TextPaM& rPaM, const XubString& rStr )
|
|
{
|
|
DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
|
|
DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
|
|
|
|
TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
|
|
pNode->InsertText( rPaM.GetIndex(), rStr );
|
|
|
|
TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+rStr.Len() );
|
|
return aPaM;
|
|
}
|
|
|
|
TextPaM TextDoc::InsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs )
|
|
{
|
|
TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
|
|
TextNode* pNew = pNode->Split( rPaM.GetIndex(), bKeepEndingAttribs );
|
|
|
|
maTextNodes.Insert( pNew, rPaM.GetPara()+1 );
|
|
|
|
TextPaM aPaM( rPaM.GetPara()+1, 0 );
|
|
return aPaM;
|
|
}
|
|
|
|
TextPaM TextDoc::ConnectParagraphs( TextNode* pLeft, TextNode* pRight )
|
|
{
|
|
sal_uInt16 nPrevLen = pLeft->GetText().Len();
|
|
pLeft->Append( *pRight );
|
|
|
|
// der rechte verschwindet.
|
|
sal_uLong nRight = maTextNodes.GetPos( pRight );
|
|
maTextNodes.Remove( nRight );
|
|
delete pRight;
|
|
|
|
sal_uLong nLeft = maTextNodes.GetPos( pLeft );
|
|
TextPaM aPaM( nLeft, nPrevLen );
|
|
return aPaM;
|
|
}
|
|
|
|
TextPaM TextDoc::RemoveChars( const TextPaM& rPaM, sal_uInt16 nChars )
|
|
{
|
|
TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
|
|
pNode->RemoveText( rPaM.GetIndex(), nChars );
|
|
|
|
return rPaM;
|
|
}
|
|
|
|
sal_Bool TextDoc::IsValidPaM( const TextPaM& rPaM )
|
|
{
|
|
if ( rPaM.GetPara() >= maTextNodes.Count() )
|
|
{
|
|
OSL_FAIL( "PaM: Para out of range" );
|
|
return sal_False;
|
|
}
|
|
TextNode * pNode = maTextNodes.GetObject( rPaM.GetPara() );
|
|
if ( rPaM.GetIndex() > pNode->GetText().Len() )
|
|
{
|
|
OSL_FAIL( "PaM: Index out of range" );
|
|
return sal_False;
|
|
}
|
|
return sal_True;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|