summaryrefslogtreecommitdiff
path: root/svtools/source/edit/textdoc.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'svtools/source/edit/textdoc.cxx')
-rw-r--r--svtools/source/edit/textdoc.cxx1073
1 files changed, 1073 insertions, 0 deletions
diff --git a/svtools/source/edit/textdoc.cxx b/svtools/source/edit/textdoc.cxx
new file mode 100644
index 000000000000..f9e651808011
--- /dev/null
+++ b/svtools/source/edit/textdoc.cxx
@@ -0,0 +1,1073 @@
+/*************************************************************************
+ *
+ * $RCSfile: textdoc.cxx,v $
+ *
+ * $Revision: 1.1.1.1 $
+ *
+ * last change: $Author: hr $ $Date: 2000-09-18 16:58:58 $
+ *
+ * The Contents of this file are made available subject to the terms of
+ * either of the following licenses
+ *
+ * - GNU Lesser General Public License Version 2.1
+ * - Sun Industry Standards Source License Version 1.1
+ *
+ * Sun Microsystems Inc., October, 2000
+ *
+ * GNU Lesser General Public License Version 2.1
+ * =============================================
+ * Copyright 2000 by Sun Microsystems, Inc.
+ * 901 San Antonio Road, Palo Alto, CA 94303, USA
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software Foundation.
+ *
+ * This library 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 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ *
+ * Sun Industry Standards Source License Version 1.1
+ * =================================================
+ * The contents of this file are subject to the Sun Industry Standards
+ * Source License Version 1.1 (the "License"); You may not use this file
+ * except in compliance with the License. You may obtain a copy of the
+ * License at http://www.openoffice.org/license.html.
+ *
+ * Software provided under this License is provided on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+ * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+ * See the License for the specific provisions governing your rights and
+ * obligations concerning the Software.
+ *
+ * The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+ *
+ * Copyright: 2000 by Sun Microsystems, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * Contributor(s): _______________________________________
+ *
+ *
+ ************************************************************************/
+
+#include <textdoc.hxx>
+
+#include <stdlib.h>
+
+SV_IMPL_PTRARR( TextCharAttribs, TextCharAttribPtr );
+
+
+
+// Vergleichmethode wird von QuickSort gerufen...
+
+EXTERN_C
+#if defined( PM2 ) && (!defined( CSET ) && !defined ( MTW ) && !defined( WTC ))
+int _stdcall
+#else
+#ifdef WNT
+#if _MSC_VER >= 1200
+int __cdecl
+#else
+int _cdecl
+#endif
+#else
+int
+#endif
+#endif
+
+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, USHORT nStart, USHORT 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 = FALSE;
+}
+
+TextCharAttribList::~TextCharAttribList()
+{
+ // PTRARR_DEL
+}
+
+void TextCharAttribList::Clear( BOOL bDestroyAttribs )
+{
+ if ( bDestroyAttribs )
+ TextCharAttribs::DeleteAndDestroy( 0, Count() );
+ else
+ TextCharAttribs::Remove( 0, Count() );
+}
+
+
+void TextCharAttribList::InsertAttrib( TextCharAttrib* pAttrib )
+{
+ if ( pAttrib->IsEmpty() )
+ mbHasEmptyAttribs = TRUE;
+
+ const USHORT nCount = Count();
+ const USHORT nStart = pAttrib->GetStart(); // vielleicht besser fuer Comp.Opt.
+ BOOL bInserted = FALSE;
+ for ( USHORT x = 0; x < nCount; x++ )
+ {
+ TextCharAttrib* pCurAttrib = GetObject( x );
+ if ( pCurAttrib->GetStart() > nStart )
+ {
+ Insert( pAttrib, x );
+ bInserted = TRUE;
+ break;
+ }
+ }
+ if ( !bInserted )
+ Insert( pAttrib, nCount );
+}
+
+void TextCharAttribList::ResortAttribs()
+{
+ if ( Count() )
+ qsort( (void*)GetData(), Count(), sizeof( TextCharAttrib* ), CompareStart );
+}
+
+TextCharAttrib* TextCharAttribList::FindAttrib( USHORT nWhich, USHORT nPos )
+{
+ // Rueckwaerts, falls eins dort endet, das naechste startet.
+ // => Das startende gilt...
+
+ for ( USHORT 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( USHORT nWhich, USHORT nFromPos, USHORT nMaxPos ) const
+{
+ DBG_ASSERT( nWhich, "FindNextAttrib: Which?" );
+ const USHORT nAttribs = Count();
+ for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ )
+ {
+ TextCharAttrib* pAttr = GetObject( nAttr );
+ if ( ( pAttr->GetStart() >= nFromPos ) &&
+ ( pAttr->GetEnd() <= nMaxPos ) &&
+ ( pAttr->Which() == nWhich ) )
+ return pAttr;
+ }
+ return NULL;
+}
+
+BOOL TextCharAttribList::HasAttrib( USHORT nWhich ) const
+{
+ for ( USHORT nAttr = Count(); nAttr; )
+ {
+ const TextCharAttrib* pAttr = GetObject( --nAttr );
+ if ( pAttr->Which() == nWhich )
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL TextCharAttribList::HasBoundingAttrib( USHORT nBound )
+{
+ // Rueckwaerts, falls eins dort endet, das naechste startet.
+ // => Das startende gilt...
+ for ( USHORT nAttr = Count(); nAttr; )
+ {
+ TextCharAttrib* pAttr = GetObject( --nAttr );
+
+ if ( pAttr->GetEnd() < nBound )
+ return FALSE;
+
+ if ( ( pAttr->GetStart() == nBound ) || ( pAttr->GetEnd() == nBound ) )
+ return TRUE;
+ }
+ return FALSE;
+}
+
+TextCharAttrib* TextCharAttribList::FindEmptyAttrib( USHORT nWhich, USHORT nPos )
+{
+ if ( !mbHasEmptyAttribs )
+ return 0;
+
+ const USHORT nAttribs = Count();
+ for ( USHORT 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 ( USHORT nAttr = 0; nAttr < Count(); nAttr++ )
+ {
+ TextCharAttrib* pAttr = GetObject( nAttr );
+ if ( pAttr->IsEmpty() )
+ {
+ Remove( nAttr );
+ delete pAttr;
+ nAttr--;
+ }
+ }
+ mbHasEmptyAttribs = FALSE;
+}
+
+#ifdef DBG_UTIL
+BOOL TextCharAttribList::DbgCheckAttribs()
+{
+ BOOL bOK = TRUE;
+ for ( USHORT nAttr = 0; nAttr < Count(); nAttr++ )
+ {
+ TextCharAttrib* pAttr = GetObject( nAttr );
+ if ( pAttr->GetStart() > pAttr->GetEnd() )
+ {
+ bOK = FALSE;
+ DBG_ERROR( "Attr verdreht" );
+ }
+ }
+ return bOK;
+}
+#endif
+
+ // -------------------------------------------------------------------------
+// (+) class TextNode
+// -------------------------------------------------------------------------
+
+TextNode::TextNode( const String& rText ) :
+ maText( rText )
+{
+}
+
+void TextNode::ExpandAttribs( USHORT nIndex, USHORT nNew )
+{
+ if ( !nNew )
+ return;
+
+ BOOL bResort = FALSE;
+ USHORT nAttribs = maCharAttribs.Count();
+ for ( USHORT 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 = 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 = 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();
+
+#ifdef EDITDEBUG
+ DBG_ASSERT( CheckOrderedList( (TextCharAttribs*)&maCharAttribs ), "Expand: Start-Liste verdreht" );
+#endif
+}
+
+void TextNode::CollapsAttribs( USHORT nIndex, USHORT nDeleted )
+{
+ if ( !nDeleted )
+ return;
+
+ BOOL bResort = FALSE;
+ USHORT nEndChanges = nIndex+nDeleted;
+
+ for ( USHORT nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
+ {
+ TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
+ BOOL bDelAttr = 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 = 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 = TRUE;
+ maCharAttribs.RemoveAttrib( nAttr );
+ delete pAttrib;
+ nAttr--;
+ }
+ else if ( pAttrib->IsEmpty() )
+ maCharAttribs.HasEmptyAttribs() = TRUE;
+ }
+
+ if ( bResort )
+ maCharAttribs.ResortAttribs();
+
+#ifdef EDITDEBUG
+ DBG_ASSERT( CheckOrderedList( (TextCharAttribs)&maCharAttribs ), "Collaps: Start-Liste verdreht" );
+#endif
+}
+
+void TextNode::InsertText( USHORT nPos, const String& rText )
+{
+ maText.Insert( rText, nPos );
+ ExpandAttribs( nPos, rText.Len() );
+}
+
+void TextNode::InsertText( USHORT nPos, sal_Unicode c )
+{
+ maText.Insert( c, nPos );
+ ExpandAttribs( nPos, 1 );
+}
+
+void TextNode::RemoveText( USHORT nPos, USHORT nChars )
+{
+ maText.Erase( nPos, nChars );
+ CollapsAttribs( nPos, nChars );
+}
+
+TextNode* TextNode::Split( USHORT nPos, BOOL bKeepEndingAttribs )
+{
+ String aNewText = maText.Copy( nPos );
+ maText.Erase( nPos );
+ TextNode* pNew = new TextNode( aNewText );
+
+ for ( USHORT 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() -= nPos;
+ pAttrib->GetEnd() -= nPos;
+ nAttr--;
+ }
+ }
+ return pNew;
+}
+
+void TextNode::Append( const TextNode& rNode )
+{
+ USHORT nOldLen = maText.Len();
+
+ maText += rNode.GetText();
+
+#ifdef EDITDEBUG
+ DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute VOR AppendAttribs kaputt" );
+#endif
+
+ const USHORT nAttribs = rNode.GetCharAttribs().Count();
+ for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ )
+ {
+ TextCharAttrib* pAttrib = rNode.GetCharAttribs().GetAttrib( nAttr );
+ BOOL bMelted = FALSE;
+ if ( pAttrib->GetStart() == 0 )
+ {
+ // Evtl koennen Attribute zusammengefasst werden:
+ USHORT nTmpAttribs = maCharAttribs.Count();
+ for ( USHORT 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() += pAttrib->GetLen();
+ bMelted = TRUE;
+ break; // es kann nur eins von der Sorte an der Stelle geben
+ }
+ }
+ }
+ }
+
+ if ( !bMelted )
+ {
+ TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
+ pNewAttrib->GetStart() += nOldLen;
+ pNewAttrib->GetEnd() += nOldLen;
+ maCharAttribs.InsertAttrib( pNewAttrib );
+ }
+ }
+
+#ifdef EDITDEBUG
+ DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute NACH AppendAttribs kaputt" );
+#endif
+}
+
+ // -------------------------------------------------------------------------
+// (+) class TextDoc
+// -------------------------------------------------------------------------
+
+TextDoc::TextDoc()
+{
+ mnLeftMargin = 0;
+};
+
+TextDoc::~TextDoc()
+{
+ DestroyTextNodes();
+}
+
+void TextDoc::Clear()
+{
+ DestroyTextNodes();
+}
+
+void TextDoc::DestroyTextNodes()
+{
+ for ( ULONG nNode = 0; nNode < maTextNodes.Count(); nNode++ )
+ delete maTextNodes.GetObject( nNode );
+ maTextNodes.Clear();
+}
+
+String TextDoc::GetText( const sal_Unicode* pSep ) const
+{
+ ULONG nLen = GetTextLen( pSep );
+ ULONG nNodes = maTextNodes.Count();
+
+ if ( nLen > STRING_MAXLEN )
+ {
+ DBG_ERROR( "Text zu gross fuer String" );
+ return String();
+ }
+
+ String aASCIIText;
+ ULONG nLastNode = nNodes-1;
+ for ( 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( ULONG nPara ) const
+{
+ XubString aText;
+ TextNode* pNode = ( nPara < maTextNodes.Count() ) ? maTextNodes.GetObject( nPara ) : 0;
+ if ( pNode )
+ aText = pNode->GetText();
+
+ return aText;
+}
+
+
+ULONG TextDoc::GetTextLen( const xub_Unicode* pSep, const TextSelection* pSel ) const
+{
+ ULONG nLen = 0;
+ ULONG nNodes = maTextNodes.Count();
+ if ( nNodes )
+ {
+ ULONG nStartNode = 0;
+ ULONG nEndNode = nNodes-1;
+ if ( pSel )
+ {
+ nStartNode = pSel->GetStart().GetPara();
+ nEndNode = pSel->GetEnd().GetPara();
+ }
+
+ for ( ULONG nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ TextNode* pNode = maTextNodes.GetObject( nNode );
+
+ USHORT nS = 0;
+ 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, 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 )
+{
+ USHORT nPrevLen = pLeft->GetText().Len();
+ pLeft->Append( *pRight );
+
+ // der rechte verschwindet.
+ ULONG nRight = maTextNodes.GetPos( pRight );
+ maTextNodes.Remove( nRight );
+ delete pRight;
+
+ ULONG nLeft = maTextNodes.GetPos( pLeft );
+ TextPaM aPaM( nLeft, nPrevLen );
+ return aPaM;
+}
+
+TextPaM TextDoc::RemoveChars( const TextPaM& rPaM, USHORT nChars )
+{
+ TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
+ pNode->RemoveText( rPaM.GetIndex(), nChars );
+
+ return rPaM;
+}
+
+BOOL TextDoc::IsValidPaM( const TextPaM& rPaM )
+{
+ if ( rPaM.GetPara() >= maTextNodes.Count() )
+ {
+ DBG_ERROR( "PaM: Para out of range" );
+ return FALSE;
+ }
+ TextNode * pNode = maTextNodes.GetObject( rPaM.GetPara() );
+ if ( rPaM.GetIndex() > pNode->GetText().Len() )
+ {
+ DBG_ERROR( "PaM: Index out of range" );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+
+void TextDoc::InsertAttribInSelection( TextNode* pNode, USHORT nStart, USHORT nEnd, const SfxPoolItem& rPoolItem )
+{
+ DBG_ASSERT( pNode, "Wohin mit dem Attribut?" );
+ DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" );
+
+ // fuer Optimierung:
+ // dieses endet am Anfang der Selektion => kann erweitert werden
+ TextCharAttrib* pEndingAttrib = 0;
+ // dieses startet am Ende der Selektion => kann erweitert werden
+ TextCharAttrib* pStartingAttrib = 0;
+
+ DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" );
+
+ RemoveAttribs( pNode, nStart, nEnd, pStartingAttrib, pEndingAttrib, rPoolItem.Which() );
+
+ if ( pStartingAttrib && pEndingAttrib &&
+ ( *(pStartingAttrib->GetItem()) == rPoolItem ) &&
+ ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
+ {
+ // wird ein groesses Attribut.
+ pEndingAttrib->GetEnd() = pStartingAttrib->GetEnd();
+ pCurPool->Remove( *(pStartingAttrib->GetItem()) );
+ pNode->GetCharAttribs().GetAttribs().Remove( pNode->GetCharAttribs().GetAttribs().GetPos( pStartingAttrib ) );
+ delete pStartingAttrib;
+ }
+ else if ( pStartingAttrib && ( *(pStartingAttrib->GetItem()) == rPoolItem ) )
+ pStartingAttrib->GetStart() = nStart;
+ else if ( pEndingAttrib && ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
+ pEndingAttrib->GetEnd() = nEnd;
+ else
+ InsertAttrib( rPoolItem, pNode, nStart, nEnd );
+
+ if ( pStartingAttrib )
+ pNode->GetCharAttribs().ResortAttribs();
+}
+
+BOOL TextDoc::RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd, USHORT nWhich )
+{
+ TextCharAttrib* pStarting;
+ TextCharAttrib* pEnding;
+ return RemoveAttribs( pNode, nStart, nEnd, pStarting, pEnding, nWhich );
+}
+
+BOOL TextDoc::RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd, TextCharAttrib*& rpStarting, TextCharAttrib*& rpEnding, USHORT nWhich )
+{
+ DBG_ASSERT( pNode, "Wohin mit dem Attribut?" );
+ DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" );
+
+ // dieses endet am Anfang der Selektion => kann erweitert werden
+ rpEnding = 0;
+ // dieses startet am Ende der Selektion => kann erweitert werden
+ rpStarting = 0;
+
+ BOOL bChanged = FALSE;
+
+ DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" );
+
+ // ueber die Attribute iterieren...
+ USHORT nAttr = 0;
+ TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ while ( pAttr )
+ {
+ BOOL bRemoveAttrib = FALSE;
+ if ( !nWhich || ( pAttr->Which() == nWhich ) )
+ {
+ // Attribut beginnt in Selection
+ if ( ( pAttr->GetStart() >= nStart ) && ( pAttr->GetStart() <= nEnd ) )
+ {
+ bChanged = TRUE;
+ if ( pAttr->GetEnd() > nEnd )
+ {
+ pAttr->GetStart() = nEnd; // dann faengt es dahinter an
+ rpStarting = pAttr;
+ break; // es kann kein weiteres Attrib hier liegen
+ }
+ else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
+ {
+ // Feature nur loeschen, wenn genau an der Stelle
+ bRemoveAttrib = TRUE;
+ }
+ }
+
+ // Attribut endet in Selection
+ else if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetEnd() <= nEnd ) )
+ {
+ bChanged = TRUE;
+ if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() )
+ {
+ pAttr->GetEnd() = nStart; // dann hoert es hier auf
+ rpEnding = pAttr;
+ }
+ else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
+ {
+ // Feature nur loeschen, wenn genau an der Stelle
+ bRemoveAttrib = TRUE;
+ }
+ }
+ // Attribut ueberlappt die Selektion
+ else if ( ( pAttr->GetStart() <= nStart ) && ( pAttr->GetEnd() >= nEnd ) )
+ {
+ bChanged = TRUE;
+ if ( pAttr->GetStart() == nStart )
+ {
+ pAttr->GetStart() = nEnd;
+ rpStarting = pAttr;
+ break; // es kann weitere Attribute geben!
+ }
+ else if ( pAttr->GetEnd() == nEnd )
+ {
+ pAttr->GetEnd() = nStart;
+ rpEnding = pAttr;
+ break; // es kann weitere Attribute geben!
+ }
+ else // Attribut muss gesplittet werden...
+ {
+ USHORT nOldEnd = pAttr->GetEnd();
+ pAttr->GetEnd() = nStart;
+ rpEnding = pAttr;
+// ULONG nSavePos = pNode->GetCharAttribs().GetStartList().GetCurPos();
+ InsertAttrib( *pAttr->GetItem(), pNode, nEnd, nOldEnd );
+// pNode->GetCharAttribs().GetStartList().Seek( nSavePos );
+ break; // es kann weitere Attribute geben!
+ }
+ }
+ }
+ if ( bRemoveAttrib )
+ {
+ DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Loeschen und behalten des gleichen Attributs ?" );
+ pNode->GetCharAttribs().GetAttribs().Remove(nAttr);
+ pCurPool->Remove( *pAttr->GetItem() );
+ delete pAttr;
+ nAttr--;
+ }
+ nAttr++;
+ pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+ return bChanged;
+}
+
+#pragma SEG_FUNCDEF(editdoc_3f)
+
+void TextDoc::InsertAttrib( const SfxPoolItem& rPoolItem, TextNode* pNode, USHORT nStart, USHORT nEnd )
+{
+ // Diese Methode prueft nicht mehr, ob ein entspr. Attribut
+ // schon an der Stelle existiert!
+
+ // pruefen, ob neues Attrib oder einfach nur Ende eines Attribs...
+// const SfxPoolItem& rDefItem = pNode->GetContentAttribs().GetItem( rPoolItem.Which() );
+// BOOL bCreateAttrib = ( rDefItem != rPoolItem );
+
+ // Durch den Verlust der Exclude-Liste geht es nicht mehr, dass ich
+ // kein neues Attribut benoetige und nur das alte nicht expandiere...
+// if ( !bCreateAttrib )
+ {
+ // => Wenn schon Default-Item, dann wenigstens nur dann einstellen,
+ // wenn davor wirklich ein entsprechendes Attribut.
+// if ( pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart ) )
+// bCreateAttrib = TRUE;
+ // Aber kleiner Trost:
+ // Die wenigsten schreiben, aendern das Attr, schreiben, und
+ // stellen dann wieder das Default-Attr ein.
+ }
+
+ // 22.9.95:
+ // Die Uberlegung, einfach das andere Attribut nicht zu expandieren, war
+ // sowieso falsch, da das DefAttr aus einer Vorlage kommen kann,
+ // die irgendwann verschwindet!
+// if ( bCreateAttrib )
+// {
+ TextCharAttrib* pAttrib = MakeCharAttrib( *pCurPool, rPoolItem, nStart, nEnd );
+ DBG_ASSERT( pAttrib, "MakeCharAttrib fehlgeschlagen!" );
+ pNode->GetCharAttribs().InsertAttrib( pAttrib );
+// }
+// else
+// {
+// TextCharAttrib* pTmpAttrib =
+// pNode->GetCharAttribs().FindAnyAttrib( rPoolItem.Which() );
+// if ( pTmpAttrib ) // sonst benoetige ich es sowieso nicht....
+// {
+// aExcludeList.Insert( pTmpAttrib->GetItem() );
+// }
+// }
+}
+
+#pragma SEG_FUNCDEF(editdoc_40)
+
+void TextDoc::InsertAttrib( TextNode* pNode, USHORT nStart, USHORT nEnd, const SfxPoolItem& rPoolItem )
+{
+ if ( nStart != nEnd )
+ {
+ InsertAttribInSelection( pNode, nStart, nEnd, rPoolItem );
+ }
+ else
+ {
+ // Pruefen, ob schon ein neues Attribut mit der WhichId an der Stelle:
+ TextCharAttrib* pAttr = pNode->GetCharAttribs().FindEmptyAttrib( rPoolItem.Which(), nStart );
+ if ( pAttr )
+ {
+ // Attribut entfernen....
+ pNode->GetCharAttribs().GetAttribs().Remove(
+ pNode->GetCharAttribs().GetAttribs().GetPos( pAttr ) );
+ }
+
+ // pruefen, ob ein 'gleiches' Attribut an der Stelle liegt.
+ pAttr = pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart );
+ if ( pAttr )
+ {
+ if ( pAttr->IsInside( nStart ) ) // splitten
+ {
+ // ???????????????????????????????
+ // eigentlich noch pruefen, ob wirklich splittet, oder return !
+ // ???????????????????????????????
+ USHORT nOldEnd = pAttr->GetEnd();
+ pAttr->GetEnd() = nStart;
+ pAttr = MakeCharAttrib( *pCurPool, *(pAttr->GetItem()), nStart, nOldEnd );
+ pNode->GetCharAttribs().InsertAttrib( pAttr );
+ }
+ else if ( pAttr->GetEnd() == nStart )
+ {
+ DBG_ASSERT( !pAttr->IsEmpty(), "Doch noch ein leeres Attribut?" );
+ // pruefen, ob genau das gleiche Attribut
+ if ( *(pAttr->GetItem()) == rPoolItem )
+ return;
+ }
+ }
+ InsertAttrib( rPoolItem, pNode, nStart, nStart );
+ }
+}
+
+#pragma SEG_FUNCDEF(editdoc_41)
+
+void TextDoc::FindAttribs( TextNode* pNode, USHORT nStartPos, USHORT nEndPos, SfxItemSet& rCurSet )
+{
+ DBG_ASSERT( pNode, "Wo soll ich suchen ?" );
+ DBG_ASSERT( nStartPos <= nEndPos, "Ungueltiger Bereich!" );
+
+ USHORT nAttr = 0;
+ TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ // keine Selection...
+ if ( nStartPos == nEndPos )
+ {
+ while ( pAttr && ( pAttr->GetStart() <= nEndPos) )
+ {
+ const SfxPoolItem* pItem = 0;
+ // Attribut liegt dadrueber...
+ if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
+ pItem = pAttr->GetItem();
+ // Attribut endet hier, ist nicht leer
+ else if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
+ {
+ if ( !pNode->GetCharAttribs().FindEmptyAttrib( pAttr->GetItem()->Which(), nStartPos ) )
+ pItem = pAttr->GetItem();
+ }
+ // Attribut endet hier, ist leer
+ else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
+ {
+// if ( aExcludeList.FindAttrib( pAttr->GetItem()->Which() ) )
+ pItem = pAttr->GetItem();
+// else if ( pNode->Len() == 0 ) // Sonderfall
+// pItem = pAttr->GetItem();
+ }
+ // Attribut beginnt hier
+ else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
+ {
+ if ( nStartPos == 0 ) // Sonderfall
+ pItem = pAttr->GetItem();
+ }
+
+ if ( pItem )
+ {
+ USHORT nWhich = pItem->Which();
+ if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF )
+ {
+ rCurSet.Put( *pItem );
+ }
+ else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON )
+ {
+ const SfxPoolItem& rItem = rCurSet.Get( nWhich );
+ if ( rItem != *pItem )
+ {
+ rCurSet.InvalidateItem( nWhich );
+ }
+ }
+ }
+ nAttr++;
+ pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+ }
+ else // Selektion
+ {
+ while ( pAttr && ( pAttr->GetStart() < nEndPos) )
+ {
+ const SfxPoolItem* pItem = 0;
+ // Attribut liegt dadrueber...
+ if ( ( pAttr->GetStart() <= nStartPos ) && ( pAttr->GetEnd() >= nEndPos ) )
+ pItem = pAttr->GetItem();
+ // Attribut startet mitten drin...
+ else if ( pAttr->GetStart() >= nStartPos )
+ {
+ // !!! pItem = pAttr->GetItem();
+ // einfach nur pItem reicht nicht, da ich z.B. bei Shadow
+ // niemals ein ungleiches Item finden wuerde, da ein solche
+ // seine Anwesenheit durch Abwesenheit repraesentiert!
+ // if ( ... )
+ // Es muesste geprueft werden, on genau das gleiche Attribut
+ // an der Bruchstelle aufsetzt, was recht aufwendig ist.
+ // Da ich beim Einfuegen von Attributen aber etwas optimiere
+ // tritt der Fall nicht so schnell auf...
+ // Also aus Geschwindigkeitsgruenden:
+ rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
+
+ }
+ // Attribut endet mitten drin...
+ else if ( pAttr->GetEnd() > nStartPos )
+ {
+ // pItem = pAttr->GetItem();
+ // s.o.
+
+ // -----------------31.05.95 16:01-------------------
+ // Ist falsch, wenn das gleiche Attribut sofort wieder
+ // eingestellt wird!
+ // => Sollte am besten nicht vorkommen, also gleich beim
+ // Setzen von Attributen richtig machen!
+ // --------------------------------------------------
+ rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
+ }
+
+ if ( pItem )
+ {
+ USHORT nWhich = pItem->Which();
+ if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF )
+ {
+ rCurSet.Put( *pItem );
+ }
+ else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON )
+ {
+ const SfxPoolItem& rItem = rCurSet.Get( nWhich );
+ if ( rItem != *pItem )
+ {
+ rCurSet.InvalidateItem( nWhich );
+ }
+ }
+ }
+ nAttr++;
+ pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
+ }
+ }
+}
+
+
+*/
+
+